2015-04-12 02:16:11 -05:00
// 12 april 2015
# include "uipriv_windows.h"
2015-05-04 13:05:36 -05:00
// TODO enable/disable
2015-04-12 02:16:11 -05:00
struct tab {
2015-04-16 08:32:34 -05:00
uiTab t ;
2015-04-17 10:18:45 -05:00
HWND hwnd ;
2015-04-29 00:53:39 -05:00
uiContainer * * pages ;
2015-04-30 14:50:03 -05:00
int * margined ;
2015-04-12 02:16:11 -05:00
uintmax_t len ;
uintmax_t cap ;
2015-05-04 13:42:23 -05:00
void ( * baseEnable ) ( uiControl * ) ;
void ( * baseDisable ) ( uiControl * ) ;
void ( * baseSysFunc ) ( uiControl * , uiControlSysFuncParams * ) ;
2015-04-12 02:16:11 -05:00
} ;
static BOOL onWM_COMMAND ( uiControl * c , WORD code , LRESULT * lResult )
{
return FALSE ;
}
// we have to handle hiding and showing of tab pages ourselves
static BOOL onWM_NOTIFY ( uiControl * c , NMHDR * nm , LRESULT * lResult )
{
2015-04-16 08:32:34 -05:00
struct tab * t = ( struct tab * ) c ;
2015-04-12 02:16:11 -05:00
LRESULT n ;
switch ( nm - > code ) {
case TCN_SELCHANGING :
2015-04-17 10:18:45 -05:00
n = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
2015-04-12 02:16:11 -05:00
if ( n ! = ( LRESULT ) ( - 1 ) ) // if we're changing to a real tab
2015-04-29 00:53:39 -05:00
uiControlHide ( uiControl ( t - > pages [ n ] ) ) ;
2015-04-12 02:16:11 -05:00
* lResult = FALSE ; // and allow the change
return TRUE ;
case TCN_SELCHANGE :
2015-04-17 10:18:45 -05:00
n = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
2015-04-12 02:16:11 -05:00
if ( n ! = ( LRESULT ) ( - 1 ) ) { // if we're changing to a real tab
2015-04-29 22:05:15 -05:00
uiControlShow ( uiControl ( t - > pages [ n ] ) ) ;
2015-04-12 02:16:11 -05:00
// because we only resize the current child on resize, we'll need to trigger an update here
2015-04-13 09:15:36 -05:00
// don't call uiParentUpdate(); doing that won't size the content area (so we'll still have a 0x0 content area, for instance)
2015-04-17 10:18:45 -05:00
SendMessageW ( t - > hwnd , msgUpdateChild , 0 , 0 ) ;
2015-04-12 02:16:11 -05:00
}
* lResult = 0 ;
return TRUE ;
}
return FALSE ;
}
2015-04-18 17:02:16 -05:00
static void onDestroy ( void * data )
2015-04-12 02:16:11 -05:00
{
2015-04-18 17:02:16 -05:00
struct tab * t = ( struct tab * ) data ;
2015-04-18 13:19:42 -05:00
uintmax_t i ;
2015-04-12 02:16:11 -05:00
2015-04-29 00:53:39 -05:00
// first, hide the widget to avoid flicker
ShowWindow ( t - > hwnd , SW_HIDE ) ;
// because the pages don't have by a libui paent, we can simply destroy them
// we don't have to worry about the Windows tab control holding a reference to our bin; there is no reference holding anyway
2015-04-18 13:19:42 -05:00
for ( i = 0 ; i < t - > len ; i + + )
2015-04-29 00:53:39 -05:00
uiControlDestroy ( uiControl ( t - > pages [ i ] ) ) ;
// and finally destroy ourselves
2015-04-12 02:16:11 -05:00
uiFree ( t - > pages ) ;
2015-04-30 14:50:03 -05:00
uiFree ( t - > margined ) ;
2015-04-12 02:16:11 -05:00
uiFree ( t ) ;
}
2015-05-04 13:42:23 -05:00
// TODO rename all the other preferredSize overloads to this form
static void tabPreferredSize ( uiControl * c , uiSizing * d , intmax_t * width , intmax_t * height )
2015-04-12 02:16:11 -05:00
{
2015-05-03 16:13:40 -05:00
struct tab * t = ( struct tab * ) c ;
LRESULT current ;
uiContainer * curpage ;
intmax_t curwid , curht ;
RECT r ;
r . left = 0 ;
r . top = 0 ;
r . right = 0 ;
r . bottom = 0 ;
if ( t - > len ! = 0 ) {
current = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
if ( current ! = ( LRESULT ) ( - 1 ) ) {
curpage = t - > pages [ current ] ;
uiControlPreferredSize ( uiControl ( curpage ) , d , & curwid , & curht ) ;
r . right = curwid ;
r . bottom = curht ;
}
}
// otherwise just use the rect [0 0 0 0]
2015-05-03 16:21:48 -05:00
// the following will take the tabs themselves into account
2015-05-03 16:13:40 -05:00
SendMessageW ( t - > hwnd , TCM_ADJUSTRECT , ( WPARAM ) TRUE , ( LPARAM ) ( & r ) ) ;
* width = r . right - r . left ;
* height = r . bottom - r . top ;
2015-04-12 02:16:11 -05:00
}
2015-05-04 13:42:23 -05:00
static void tabEnable ( uiControl * c )
{
struct tab * t = ( struct tab * ) c ;
2015-05-04 13:45:18 -05:00
uintmax_t i ;
2015-05-04 13:42:23 -05:00
( * ( t - > baseEnable ) ) ( uiControl ( t ) ) ;
2015-05-04 13:45:18 -05:00
for ( i = 0 ; i < t - > len ; i + + )
uiControlEnable ( uiControl ( t - > pages [ i ] ) ) ;
2015-05-04 13:42:23 -05:00
}
static void tabDisable ( uiControl * c )
{
struct tab * t = ( struct tab * ) c ;
2015-05-04 13:45:18 -05:00
uintmax_t i ;
2015-05-04 13:42:23 -05:00
( * ( t - > baseDisable ) ) ( uiControl ( t ) ) ;
2015-05-04 13:45:18 -05:00
for ( i = 0 ; i < t - > len ; i + + )
uiControlDisable ( uiControl ( t - > pages [ i ] ) ) ;
2015-05-04 13:42:23 -05:00
}
static void tabSysFunc ( uiControl * c , uiControlSysFuncParams * p )
{
struct tab * t = ( struct tab * ) c ;
2015-05-04 13:45:18 -05:00
uintmax_t i ;
2015-05-04 13:42:23 -05:00
( * ( t - > baseSysFunc ) ) ( uiControl ( t ) , p ) ;
2015-05-04 13:45:18 -05:00
for ( i = 0 ; i < t - > len ; i + + )
uiControlSysFunc ( uiControl ( t - > pages [ i ] ) , p ) ;
2015-05-04 13:42:23 -05:00
}
2015-04-12 02:16:11 -05:00
// common code for resizes
2015-04-17 10:18:45 -05:00
static void resizeTab ( struct tab * t , LONG width , LONG height )
2015-04-12 02:16:11 -05:00
{
LRESULT n ;
2015-04-12 21:39:36 -05:00
RECT r ;
2015-04-29 01:22:15 -05:00
HWND binHWND ;
2015-04-12 02:16:11 -05:00
2015-04-17 10:18:45 -05:00
n = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
2015-04-12 02:16:11 -05:00
if ( n = = ( LRESULT ) ( - 1 ) ) // no child selected; do nothing
return ;
// make a rect at (0, 0) of the given window size
// this should give us the correct client coordinates
r . left = 0 ;
r . top = 0 ;
r . right = width ;
r . bottom = height ;
// convert to the display rectangle
2015-04-17 10:18:45 -05:00
SendMessageW ( t - > hwnd , TCM_ADJUSTRECT , FALSE , ( LPARAM ) ( & r ) ) ;
2015-04-12 02:16:11 -05:00
2015-04-29 01:22:15 -05:00
binHWND = ( HWND ) uiControlHandle ( uiControl ( t - > pages [ n ] ) ) ;
if ( MoveWindow ( binHWND , r . left , r . top , r . right - r . left , r . bottom - r . top , TRUE ) = = 0 )
logLastError ( " error resizing uiTab page in resizeTab() " ) ;
2015-04-12 02:16:11 -05:00
}
2015-04-12 21:39:36 -05:00
// and finally, because we have to resize parents, we have to handle resizes and updates
2015-04-29 00:53:39 -05:00
// TODO see if this approach is /really/ necessary and we can get by with an alteration to the above function and overriding uiControlResize()
2015-04-12 02:16:11 -05:00
static LRESULT CALLBACK tabSubProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam , UINT_PTR uIdSubclass , DWORD_PTR dwRefData )
{
2015-04-17 10:18:45 -05:00
struct tab * t = ( struct tab * ) dwRefData ;
2015-04-12 02:16:11 -05:00
WINDOWPOS * wp = ( WINDOWPOS * ) lParam ;
2015-04-12 21:57:05 -05:00
LRESULT lResult ;
2015-04-13 09:15:36 -05:00
RECT r ;
2015-04-12 02:16:11 -05:00
switch ( uMsg ) {
case WM_WINDOWPOSCHANGED :
if ( ( wp - > flags & SWP_NOSIZE ) ! = 0 )
break ;
// first, let the tab control handle it
lResult = ( * fv_DefSubclassProc ) ( hwnd , uMsg , wParam , lParam ) ;
// we have the window rect width as part of the WINDOWPOS; resize
2015-04-17 10:18:45 -05:00
resizeTab ( t , wp - > cx , wp - > cy ) ;
2015-04-12 02:16:11 -05:00
return lResult ;
2015-04-13 09:15:36 -05:00
case msgUpdateChild :
2015-04-17 10:18:45 -05:00
if ( GetWindowRect ( t - > hwnd , & r ) = = 0 )
2015-04-13 09:15:36 -05:00
logLastError ( " error getting Tab window rect for synthesized resize message in tabSubProc() " ) ;
2015-05-03 15:56:58 -05:00
// these are in screen coordinates, which match what WM_WINDOWPOSCHANGED gave us (see http://stackoverflow.com/questions/29598334/are-the-coordinates-in-windowpos-on-wm-windowposchanged-in-parent-coordinates-or)
2015-04-17 10:18:45 -05:00
resizeTab ( t , r . right - r . left , r . bottom - r . top ) ;
2015-04-13 09:15:36 -05:00
return 0 ;
2015-04-12 02:16:11 -05:00
case WM_NCDESTROY :
if ( ( * fv_RemoveWindowSubclass ) ( hwnd , tabSubProc , uIdSubclass ) = = FALSE )
logLastError ( " error removing Tab resize handling subclass in tabSubProc() " ) ;
break ;
}
return ( * fv_DefSubclassProc ) ( hwnd , uMsg , wParam , lParam ) ;
}
# define tabCapGrow 32
2015-04-29 00:53:39 -05:00
static void tabAppendPage ( uiTab * tt , const char * name , uiControl * child )
2015-04-12 02:16:11 -05:00
{
2015-04-16 08:32:34 -05:00
struct tab * t = ( struct tab * ) tt ;
2015-04-12 02:16:11 -05:00
TCITEMW item ;
LRESULT n ;
2015-04-29 00:53:39 -05:00
uiContainer * page ;
2015-04-12 02:16:11 -05:00
WCHAR * wname ;
if ( t - > len > = t - > cap ) {
t - > cap + = tabCapGrow ;
2015-05-03 18:52:24 -05:00
t - > pages = ( uiContainer * * ) uiRealloc ( t - > pages , t - > cap * sizeof ( uiContainer * ) ) ;
t - > margined = ( int * ) uiRealloc ( t - > margined , t - > cap * sizeof ( int ) ) ;
2015-04-12 02:16:11 -05:00
}
2015-04-17 10:18:45 -05:00
n = SendMessageW ( t - > hwnd , TCM_GETITEMCOUNT , 0 , 0 ) ;
2015-04-12 02:16:11 -05:00
2015-04-29 00:53:39 -05:00
page = newBin ( ) ;
binSetMainControl ( page , child ) ;
binSetParent ( page , ( uintptr_t ) ( t - > hwnd ) ) ;
2015-04-12 02:16:11 -05:00
if ( n ! = 0 ) // if this isn't the first page, we have to hide the other controls
2015-04-29 00:53:39 -05:00
uiControlHide ( uiControl ( page ) ) ;
t - > pages [ t - > len ] = page ;
2015-04-30 14:50:03 -05:00
t - > margined [ t - > len ] = 0 ; // TODO should not be necessary but blah blah blah realloc
2015-04-12 02:16:11 -05:00
t - > len + + ;
ZeroMemory ( & item , sizeof ( TCITEMW ) ) ;
item . mask = TCIF_TEXT ;
wname = toUTF16 ( name ) ;
item . pszText = wname ;
// MSDN's example code uses the first invalid index directly for this
2015-04-17 10:18:45 -05:00
if ( SendMessageW ( t - > hwnd , TCM_INSERTITEM , ( WPARAM ) n , ( LPARAM ) ( & item ) ) = = ( LRESULT ) - 1 )
2015-04-12 02:16:11 -05:00
logLastError ( " error adding tab to Tab in uiTabAddPage() " ) ;
uiFree ( wname ) ;
2015-04-13 09:15:36 -05:00
// if this is the first tab, Windows will automatically show it /without/ sending a TCN_SELCHANGE notification
// so we need to manually resize the tab ourselves
// don't use uiUpdateParent() for the same reason as in the TCN_SELCHANGE handler
2015-04-17 10:18:45 -05:00
SendMessageW ( t - > hwnd , msgUpdateChild , 0 , 0 ) ;
2015-04-12 02:16:11 -05:00
}
2015-04-16 08:32:34 -05:00
2015-04-18 13:41:28 -05:00
static void tabDeletePage ( uiTab * tt , uintmax_t n )
{
struct tab * t = ( struct tab * ) tt ;
2015-04-29 00:53:39 -05:00
uiContainer * page ;
2015-04-18 13:41:28 -05:00
uintmax_t i ;
// first delete the tab from the tab control
// if this is the current tab, no tab will be selected, which is good
if ( SendMessageW ( t - > hwnd , TCM_DELETEITEM , ( WPARAM ) n , 0 ) = = FALSE )
logLastError ( " error deleting Tab page in tabDeletePage() " ) ;
// now delete the page itself
2015-04-29 00:53:39 -05:00
page = t - > pages [ n ] ;
2015-04-30 14:50:03 -05:00
for ( i = n ; i < t - > len - 1 ; i + + ) {
2015-04-18 13:41:28 -05:00
t - > pages [ i ] = t - > pages [ i + 1 ] ;
2015-04-30 14:50:03 -05:00
t - > margined [ i ] = t - > margined [ i + 1 ] ;
}
2015-04-18 13:41:28 -05:00
t - > pages [ i ] = NULL ;
2015-04-30 14:50:03 -05:00
t - > margined [ i ] = 0 ;
2015-04-18 13:41:28 -05:00
t - > len - - ;
// make sure the page's control isn't destroyed
2015-04-29 00:53:39 -05:00
binSetMainControl ( page , NULL ) ;
// see tabDestroy() above for details
uiControlDestroy ( uiControl ( page ) ) ;
2015-04-18 13:41:28 -05:00
}
2015-04-30 14:50:03 -05:00
static uintmax_t tabNumPages ( uiTab * tt )
{
struct tab * t = ( struct tab * ) tt ;
return t - > len ;
}
static int tabMargined ( uiTab * tt , uintmax_t n )
{
struct tab * t = ( struct tab * ) tt ;
return t - > margined [ n ] ;
}
// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
# define tabMargin 7
static void tabSetMargined ( uiTab * tt , uintmax_t n , int margined )
{
struct tab * t = ( struct tab * ) tt ;
t - > margined [ n ] = margined ;
if ( t - > margined [ n ] )
binSetMargins ( t - > pages [ n ] , tabMargin , tabMargin , tabMargin , tabMargin ) ;
else
binSetMargins ( t - > pages [ n ] , 0 , 0 , 0 , 0 ) ;
}
2015-04-16 08:32:34 -05:00
uiTab * uiNewTab ( void )
{
struct tab * t ;
2015-05-02 19:51:00 -05:00
uiWindowsMakeControlParams p ;
2015-04-16 08:32:34 -05:00
t = uiNew ( struct tab ) ;
p . dwExStyle = 0 ; // don't set WS_EX_CONTROLPARENT yet; we do that dynamically in the message loop (see main_windows.c)
p . lpClassName = WC_TABCONTROLW ;
p . lpWindowName = L " " ;
p . dwStyle = TCS_TOOLTIPS | WS_TABSTOP ;
p . hInstance = hInstance ;
p . useStandardControlFont = TRUE ;
p . onWM_COMMAND = onWM_COMMAND ;
p . onWM_NOTIFY = onWM_NOTIFY ;
2015-04-18 17:02:16 -05:00
p . onDestroy = onDestroy ;
p . onDestroyData = t ;
2015-05-02 19:51:00 -05:00
uiWindowsMakeControl ( uiControl ( t ) , & p ) ;
2015-04-16 08:32:34 -05:00
2015-05-01 09:16:02 -05:00
t - > hwnd = ( HWND ) uiControlHandle ( uiControl ( t ) ) ;
2015-04-17 10:18:45 -05:00
if ( ( * fv_SetWindowSubclass ) ( t - > hwnd , tabSubProc , 0 , ( DWORD_PTR ) t ) = = FALSE )
2015-04-16 08:32:34 -05:00
logLastError ( " error subclassing Tab to give it its own resize handler in uiNewTab() " ) ;
2015-05-04 13:42:23 -05:00
uiControl ( t ) - > PreferredSize = tabPreferredSize ;
t - > baseEnable = uiControl ( t ) - > Enable ;
uiControl ( t ) - > Enable = tabEnable ;
t - > baseDisable = uiControl ( t ) - > Disable ;
uiControl ( t ) - > Disable = tabDisable ;
t - > baseSysFunc = uiControl ( t ) - > SysFunc ;
uiControl ( t ) - > SysFunc = tabSysFunc ;
2015-04-16 08:32:34 -05:00
2015-04-29 00:53:39 -05:00
uiTab ( t ) - > AppendPage = tabAppendPage ;
2015-04-18 13:41:28 -05:00
uiTab ( t ) - > DeletePage = tabDeletePage ;
2015-04-30 14:50:03 -05:00
uiTab ( t ) - > NumPages = tabNumPages ;
uiTab ( t ) - > Margined = tabMargined ;
uiTab ( t ) - > SetMargined = tabSetMargined ;
2015-04-16 08:32:34 -05:00
return uiTab ( t ) ;
}