2018-05-29 19:26:48 -05:00
# include "uipriv_windows.hpp"
2018-06-09 23:49:44 -05:00
# include "table.hpp"
2018-05-29 19:26:48 -05:00
2018-06-17 09:50:24 -05:00
// general TODOs:
// - tooltips don't work properly on columns with icons (the listview always thinks there's enough room for a short label because it's not taking the icon into account); is this a bug in our LVN_GETDISPINFO handler or something else?
2018-06-19 22:07:24 -05:00
// - should clicking on some other column of the same row, even one that doesn't edit, cancel editing?
2018-06-23 10:27:02 -05:00
// - implement keyboard accessibility
// - implement accessibility in general (Dynamic Annotations maybe?)
2018-07-28 12:57:20 -05:00
// - if I didn't handle these already: "drawing focus rects here, subitem navigation and activation with the keyboard"
2018-06-17 09:50:24 -05:00
2018-05-29 19:26:48 -05:00
uiTableModel * uiNewTableModel ( uiTableModelHandler * mh )
{
uiTableModel * m ;
2018-06-07 21:54:01 -05:00
m = uiprivNew ( uiTableModel ) ;
2018-05-29 19:26:48 -05:00
m - > mh = mh ;
2018-06-07 21:54:01 -05:00
m - > tables = new std : : vector < uiTable * > ;
2018-05-29 19:26:48 -05:00
return m ;
}
void uiFreeTableModel ( uiTableModel * m )
{
2018-06-07 21:54:01 -05:00
delete m - > tables ;
uiprivFree ( m ) ;
2018-05-29 19:26:48 -05:00
}
2018-06-08 00:35:23 -05:00
// TODO document that when this is called, the model must return the new row count when asked
2018-05-29 19:26:48 -05:00
void uiTableModelRowInserted ( uiTableModel * m , int newIndex )
{
LVITEMW item ;
2018-06-08 00:35:23 -05:00
int newCount ;
2018-05-29 19:26:48 -05:00
2018-06-23 22:35:42 -05:00
newCount = uiprivTableModelNumRows ( m ) ;
2018-05-29 19:26:48 -05:00
ZeroMemory ( & item , sizeof ( LVITEMW ) ) ;
item . mask = 0 ;
item . iItem = newIndex ;
item . iSubItem = 0 ;
2018-06-08 00:35:23 -05:00
for ( auto t : * ( m - > tables ) ) {
// actually insert the rows
if ( SendMessageW ( t - > hwnd , LVM_SETITEMCOUNT , ( WPARAM ) newCount , LVSICF_NOINVALIDATEALL ) = = 0 )
logLastError ( L " error calling LVM_SETITEMCOUNT in uiTableModelRowInserted() " ) ;
// and redraw every row from the new row down to simulate adding it
if ( SendMessageW ( t - > hwnd , LVM_REDRAWITEMS , ( WPARAM ) newIndex , ( LPARAM ) ( newCount - 1 ) ) = = FALSE )
logLastError ( L " error calling LVM_REDRAWITEMS in uiTableModelRowInserted() " ) ;
// update selection state
2018-05-29 19:26:48 -05:00
if ( SendMessageW ( t - > hwnd , LVM_INSERTITEM , 0 , ( LPARAM ) ( & item ) ) = = ( LRESULT ) ( - 1 ) )
2018-06-08 00:35:23 -05:00
logLastError ( L " error calling LVM_INSERTITEM in uiTableModelRowInserted() to update selection state " ) ;
}
2018-05-29 19:26:48 -05:00
}
2018-06-08 00:35:23 -05:00
// TODO compare LVM_UPDATE and LVM_REDRAWITEMS
2018-05-29 19:26:48 -05:00
void uiTableModelRowChanged ( uiTableModel * m , int index )
{
2018-06-07 21:54:01 -05:00
for ( auto t : * ( m - > tables ) )
2018-05-29 19:26:48 -05:00
if ( SendMessageW ( t - > hwnd , LVM_UPDATE , ( WPARAM ) index , 0 ) = = ( LRESULT ) ( - 1 ) )
logLastError ( L " error calling LVM_UPDATE in uiTableModelRowChanged() " ) ;
}
2018-06-08 00:35:23 -05:00
// TODO document that when this is called, the model must return the OLD row count when asked
// TODO for this and the above, see what GTK+ requires and adjust accordingly
2018-05-29 19:26:48 -05:00
void uiTableModelRowDeleted ( uiTableModel * m , int oldIndex )
{
2018-06-08 00:35:23 -05:00
int newCount ;
2018-06-23 22:35:42 -05:00
newCount = uiprivTableModelNumRows ( m ) ;
2018-06-08 00:35:23 -05:00
newCount - - ;
for ( auto t : * ( m - > tables ) ) {
// update selection state
2018-05-29 19:26:48 -05:00
if ( SendMessageW ( t - > hwnd , LVM_DELETEITEM , ( WPARAM ) oldIndex , 0 ) = = ( LRESULT ) ( - 1 ) )
2018-06-08 00:35:23 -05:00
logLastError ( L " error calling LVM_DELETEITEM in uiTableModelRowDeleted() to update selection state " ) ;
// actually delete the rows
if ( SendMessageW ( t - > hwnd , LVM_SETITEMCOUNT , ( WPARAM ) newCount , LVSICF_NOINVALIDATEALL ) = = 0 )
logLastError ( L " error calling LVM_SETITEMCOUNT in uiTableModelRowDeleted() " ) ;
// and redraw every row from the new nth row down to simulate removing the old nth row
if ( SendMessageW ( t - > hwnd , LVM_REDRAWITEMS , ( WPARAM ) oldIndex , ( LPARAM ) ( newCount - 1 ) ) = = FALSE )
logLastError ( L " error calling LVM_REDRAWITEMS in uiTableModelRowDeleted() " ) ;
}
2018-05-29 19:26:48 -05:00
}
2018-06-23 22:35:42 -05:00
uiTableModelHandler * uiprivTableModelHandler ( uiTableModel * m )
{
return m - > mh ;
}
2018-06-16 18:40:06 -05:00
// TODO explain all this
2018-06-16 18:05:36 -05:00
static LRESULT CALLBACK tableSubProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam , UINT_PTR uIDSubclass , DWORD_PTR dwRefData )
{
uiTable * t = ( uiTable * ) dwRefData ;
2018-06-19 22:07:24 -05:00
NMHDR * nmhdr = ( NMHDR * ) lParam ;
bool finishEdit , abortEdit ;
HWND header ;
2018-06-17 14:06:45 -05:00
LRESULT lResult ;
2018-06-19 22:07:24 -05:00
HRESULT hr ;
2018-06-16 18:05:36 -05:00
2018-06-19 22:07:24 -05:00
finishEdit = false ;
abortEdit = false ;
2018-06-16 18:05:36 -05:00
switch ( uMsg ) {
case WM_TIMER :
2018-06-17 14:06:45 -05:00
if ( wParam = = ( WPARAM ) ( & ( t - > inDoubleClickTimer ) ) ) {
t - > inDoubleClickTimer = FALSE ;
// TODO check errors
KillTimer ( hwnd , wParam ) ;
return 0 ;
}
2018-06-16 18:05:36 -05:00
if ( wParam ! = ( WPARAM ) t )
break ;
2018-06-16 18:40:06 -05:00
// TODO only increment and update if visible?
for ( auto & i : * ( t - > indeterminatePositions ) ) {
i . second + + ;
2018-06-16 18:05:36 -05:00
// TODO check errors
2018-06-16 18:40:06 -05:00
SendMessageW ( hwnd , LVM_UPDATE , ( WPARAM ) ( i . first . first ) , 0 ) ;
2018-06-16 18:05:36 -05:00
}
return 0 ;
2018-06-17 14:06:45 -05:00
case WM_LBUTTONDOWN :
t - > inLButtonDown = TRUE ;
lResult = DefSubclassProc ( hwnd , uMsg , wParam , lParam ) ;
t - > inLButtonDown = FALSE ;
return lResult ;
2018-06-19 22:07:24 -05:00
case WM_COMMAND :
if ( HIWORD ( wParam ) = = EN_UPDATE ) {
// the real list view resizes the edit control on this notification specifically
2018-06-20 17:47:55 -05:00
hr = uiprivTableResizeWhileEditing ( t ) ;
if ( hr ! = S_OK ) {
// TODO
}
break ;
2018-06-19 22:07:24 -05:00
}
// the real list view accepts changes in this case
if ( HIWORD ( wParam ) = = EN_KILLFOCUS )
finishEdit = true ;
break ; // don't override default handling
case WM_NOTIFY :
// list view accepts changes on column resize, but does not provide such notifications :/
header = ( HWND ) SendMessageW ( t - > hwnd , LVM_GETHEADER , 0 , 0 ) ;
if ( nmhdr - > hwndFrom = = header ) {
NMHEADERW * nm = ( NMHEADERW * ) nmhdr ;
switch ( nmhdr - > code ) {
case HDN_ITEMCHANGED :
if ( ( nm - > pitem - > mask & HDI_WIDTH ) = = 0 )
break ;
// fall through
case HDN_DIVIDERDBLCLICK :
case HDN_TRACK :
case HDN_ENDTRACK :
finishEdit = true ;
}
}
// I think this mirrors the WM_COMMAND one above... TODO
if ( nmhdr - > code = = NM_KILLFOCUS )
finishEdit = true ;
break ; // don't override default handling
case LVM_CANCELEDITLABEL :
finishEdit = true ;
// TODO properly imitate notifiactions
break ; // don't override default handling
// TODO finish edit on WM_WINDOWPOSCHANGING and WM_SIZE?
// for the next three: this item is about to go away; don't bother keeping changes
case LVM_SETITEMCOUNT :
if ( wParam < = t - > editedItem )
abortEdit = true ;
break ; // don't override default handling
case LVM_DELETEITEM :
if ( wParam = = t - > editedItem )
abortEdit = true ;
break ; // don't override default handling
case LVM_DELETEALLITEMS :
abortEdit = true ;
break ; // don't override default handling
2018-06-16 18:05:36 -05:00
case WM_NCDESTROY :
if ( RemoveWindowSubclass ( hwnd , tableSubProc , uIDSubclass ) = = FALSE )
logLastError ( L " RemoveWindowSubclass() " ) ;
// fall through
}
2018-06-19 22:07:24 -05:00
if ( finishEdit ) {
hr = uiprivTableFinishEditingText ( t ) ;
if ( hr ! = S_OK ) {
// TODO
}
} else if ( abortEdit ) {
hr = uiprivTableAbortEditingText ( t ) ;
if ( hr ! = S_OK ) {
// TODO
}
}
2018-06-16 18:05:36 -05:00
return DefSubclassProc ( hwnd , uMsg , wParam , lParam ) ;
}
int uiprivTableProgress ( uiTable * t , int item , int subitem , int modelColumn , LONG * pos )
{
2018-06-23 19:19:30 -05:00
uiTableValue * value ;
2018-06-16 18:05:36 -05:00
int progress ;
std : : pair < int , int > p ;
std : : map < std : : pair < int , int > , LONG > : : iterator iter ;
bool startTimer = false ;
bool stopTimer = false ;
2018-06-23 22:35:42 -05:00
value = uiprivTableModelCellValue ( t - > model , item , modelColumn ) ;
2018-06-23 19:19:30 -05:00
progress = uiTableValueInt ( value ) ;
uiFreeTableValue ( value ) ;
2018-06-16 18:05:36 -05:00
p . first = item ;
p . second = subitem ;
iter = t - > indeterminatePositions - > find ( p ) ;
if ( iter = = t - > indeterminatePositions - > end ( ) ) {
if ( progress = = - 1 ) {
startTimer = t - > indeterminatePositions - > size ( ) = = 0 ;
( * ( t - > indeterminatePositions ) ) [ p ] = 0 ;
if ( pos ! = NULL )
* pos = 0 ;
}
} else
if ( progress ! = - 1 ) {
t - > indeterminatePositions - > erase ( p ) ;
stopTimer = t - > indeterminatePositions - > size ( ) = = 0 ;
} else if ( pos ! = NULL )
* pos = iter - > second ;
if ( startTimer )
// the interval shown here is PBM_SETMARQUEE's default
// TODO should we pass a function here instead? it seems to be called by DispatchMessage(), not DefWindowProc(), but I'm still unsure
2018-06-16 18:40:06 -05:00
if ( SetTimer ( t - > hwnd , ( UINT_PTR ) t , 30 , NULL ) = = 0 )
2018-06-16 18:05:36 -05:00
logLastError ( L " SetTimer() " ) ;
if ( stopTimer )
if ( KillTimer ( t - > hwnd , ( UINT_PTR ) ( & t ) ) = = 0 )
logLastError ( L " KillTimer() " ) ;
2018-06-16 18:40:06 -05:00
return progress ;
2018-06-16 18:05:36 -05:00
}
2018-06-19 22:07:24 -05:00
// TODO properly integrate compound statements
2018-06-08 20:16:06 -05:00
static BOOL onWM_NOTIFY ( uiControl * c , HWND hwnd , NMHDR * nmhdr , LRESULT * lResult )
2018-05-29 19:26:48 -05:00
{
2018-06-08 20:16:06 -05:00
uiTable * t = uiTable ( c ) ;
2018-06-15 09:04:32 -05:00
HRESULT hr ;
2018-05-29 19:26:48 -05:00
2018-06-08 20:16:06 -05:00
switch ( nmhdr - > code ) {
case LVN_GETDISPINFO :
2018-06-16 11:33:16 -05:00
hr = uiprivTableHandleLVN_GETDISPINFO ( t , ( NMLVDISPINFOW * ) nmhdr , lResult ) ;
if ( hr ! = S_OK ) {
// TODO
return FALSE ;
}
2018-06-08 20:16:06 -05:00
return TRUE ;
2018-06-09 09:40:42 -05:00
case NM_CUSTOMDRAW :
2018-06-15 09:04:32 -05:00
hr = uiprivTableHandleNM_CUSTOMDRAW ( t , ( NMLVCUSTOMDRAW * ) nmhdr , lResult ) ;
if ( hr ! = S_OK ) {
// TODO
return FALSE ;
}
2018-06-09 09:40:42 -05:00
return TRUE ;
2018-06-17 08:29:29 -05:00
case NM_CLICK :
2018-06-17 10:48:39 -05:00
#if 0
2018-06-17 08:29:29 -05:00
{
NMITEMACTIVATE * nm = ( NMITEMACTIVATE * ) nmhdr ;
LVHITTESTINFO ht ;
WCHAR buf [ 256 ] ;
ZeroMemory ( & ht , sizeof ( LVHITTESTINFO ) ) ;
ht . pt = nm - > ptAction ;
if ( SendMessageW ( t - > hwnd , LVM_SUBITEMHITTEST , 0 , ( LPARAM ) ( & ht ) ) = = ( LRESULT ) ( - 1 ) )
MessageBoxW ( GetAncestor ( t - > hwnd , GA_ROOT ) , L " No hit " , L " No hit " , MB_OK ) ;
else {
wsprintf ( buf , L " item %d subitem %d htflags 0x%I32X " ,
ht . iItem , ht . iSubItem , ht . flags ) ;
MessageBoxW ( GetAncestor ( t - > hwnd , GA_ROOT ) , buf , buf , MB_OK ) ;
}
}
* lResult = 0 ;
return TRUE ;
2018-06-17 10:48:39 -05:00
# else
hr = uiprivTableHandleNM_CLICK ( t , ( NMITEMACTIVATE * ) nmhdr , lResult ) ;
if ( hr ! = S_OK ) {
// TODO
return FALSE ;
}
return TRUE ;
# endif
2018-06-17 14:06:45 -05:00
case LVN_ITEMCHANGED :
{
NMLISTVIEW * nm = ( NMLISTVIEW * ) nmhdr ;
UINT oldSelected , newSelected ;
2018-06-19 22:07:24 -05:00
HRESULT hr ;
2018-06-17 14:06:45 -05:00
2018-06-19 22:07:24 -05:00
// TODO clean up these if cases
if ( ! t - > inLButtonDown & & t - > edit = = NULL )
2018-06-17 14:06:45 -05:00
return FALSE ;
oldSelected = nm - > uOldState & LVIS_SELECTED ;
newSelected = nm - > uNewState & LVIS_SELECTED ;
2018-06-19 22:07:24 -05:00
if ( t - > inLButtonDown & & oldSelected = = 0 & & newSelected ! = 0 ) {
2018-06-17 14:06:45 -05:00
t - > inDoubleClickTimer = TRUE ;
// TODO check error
SetTimer ( t - > hwnd , ( UINT_PTR ) ( & ( t - > inDoubleClickTimer ) ) ,
GetDoubleClickTime ( ) , NULL ) ;
* lResult = 0 ;
return TRUE ;
}
2018-06-19 22:07:24 -05:00
// the nm->iItem == -1 case is because that is used if "the change has been applied to all items in the list view"
if ( t - > edit ! = NULL & & oldSelected ! = 0 & & newSelected = = 0 & & ( t - > editedItem = = nm - > iItem | | nm - > iItem = = - 1 ) ) {
// TODO see if the real list view accepts or rejects changes here; Windows Explorer accepts
hr = uiprivTableFinishEditingText ( t ) ;
if ( hr ! = S_OK ) {
// TODO
return FALSE ;
}
* lResult = 0 ;
return TRUE ;
}
return FALSE ;
}
// the real list view accepts changes when scrolling or clicking column headers
case LVN_BEGINSCROLL :
case LVN_COLUMNCLICK :
hr = uiprivTableFinishEditingText ( t ) ;
if ( hr ! = S_OK ) {
// TODO
2018-06-17 14:06:45 -05:00
return FALSE ;
}
2018-06-19 22:07:24 -05:00
* lResult = 0 ;
return TRUE ;
2018-06-08 20:16:06 -05:00
}
return FALSE ;
2018-05-29 19:26:48 -05:00
}
static void uiTableDestroy ( uiControl * c )
{
uiTable * t = uiTable ( c ) ;
uiTableModel * model = t - > model ;
std : : vector < uiTable * > : : iterator it ;
2018-06-19 22:07:24 -05:00
HRESULT hr ;
2018-05-29 19:26:48 -05:00
2018-06-19 22:07:24 -05:00
hr = uiprivTableAbortEditingText ( t ) ;
if ( hr ! = S_OK ) {
// TODO
}
2018-05-29 19:26:48 -05:00
uiWindowsUnregisterWM_NOTIFYHandler ( t - > hwnd ) ;
uiWindowsEnsureDestroyWindow ( t - > hwnd ) ;
// detach table from model
2018-06-08 21:05:11 -05:00
for ( it = model - > tables - > begin ( ) ; it ! = model - > tables - > end ( ) ; it + + ) {
2018-05-29 19:26:48 -05:00
if ( * it = = t ) {
2018-06-08 20:44:55 -05:00
model - > tables - > erase ( it ) ;
2018-05-29 19:26:48 -05:00
break ;
}
}
// free the columns
2018-06-08 20:44:55 -05:00
for ( auto col : * ( t - > columns ) )
2018-05-29 19:26:48 -05:00
uiprivFree ( col ) ;
2018-06-07 21:54:01 -05:00
delete t - > columns ;
2018-06-16 18:05:36 -05:00
// t->imagelist will be automatically destroyed
delete t - > indeterminatePositions ;
2018-05-29 19:26:48 -05:00
uiFreeControl ( uiControl ( t ) ) ;
}
2018-06-08 20:16:06 -05:00
uiWindowsControlAllDefaultsExceptDestroy ( uiTable )
2018-05-29 19:26:48 -05:00
// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing:
// "columns widths that avoid truncated data x an integral number of items"
// Don't think that'll cut it when some cells have overlong data (eg
// stupidly long URLs). So for now, just hardcode a minimum.
2018-06-11 07:01:18 -05:00
// TODO Investigate using LVM_GETHEADER/HDM_LAYOUT here
// TODO investigate using LVM_APPROXIMATEVIEWRECT here
2018-05-29 19:26:48 -05:00
# define tableMinWidth 107 /* in line with other controls */
2018-06-08 20:44:55 -05:00
# define tableMinHeight (14 * 3) /* header + 2 lines (roughly) */
2018-05-29 19:26:48 -05:00
static void uiTableMinimumSize ( uiWindowsControl * c , int * width , int * height )
{
uiTable * t = uiTable ( c ) ;
uiWindowsSizing sizing ;
int x , y ;
x = tableMinWidth ;
y = tableMinHeight ;
uiWindowsGetSizing ( t - > hwnd , & sizing ) ;
uiWindowsSizingDlgUnitsToPixels ( & sizing , & x , & y ) ;
* width = x ;
* height = y ;
2018-06-08 20:45:30 -05:00
}
2018-05-29 19:26:48 -05:00
2018-06-09 23:49:44 -05:00
static uiprivTableColumnParams * appendColumn ( uiTable * t , const char * name , int colfmt )
2018-05-29 19:26:48 -05:00
{
2018-06-08 20:44:55 -05:00
WCHAR * wstr ;
2018-06-08 20:16:06 -05:00
LVCOLUMNW lvc ;
2018-06-09 23:49:44 -05:00
uiprivTableColumnParams * p ;
2018-05-29 19:26:48 -05:00
2018-06-08 20:44:55 -05:00
ZeroMemory ( & lvc , sizeof ( LVCOLUMNW ) ) ;
lvc . mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT ;
lvc . fmt = colfmt ;
lvc . cx = 120 ; // TODO
wstr = toUTF16 ( name ) ;
lvc . pszText = wstr ;
if ( SendMessageW ( t - > hwnd , LVM_INSERTCOLUMNW , t - > nColumns , ( LPARAM ) ( & lvc ) ) = = ( LRESULT ) ( - 1 ) )
logLastError ( L " error calling LVM_INSERTCOLUMNW in appendColumn() " ) ;
uiprivFree ( wstr ) ;
t - > nColumns + + ;
2018-06-09 23:49:44 -05:00
p = uiprivNew ( uiprivTableColumnParams ) ;
2018-06-08 20:44:55 -05:00
p - > textModelColumn = - 1 ;
2018-06-24 08:52:01 -05:00
p - > textEditableModelColumn = - 1 ;
2018-06-23 23:45:54 -05:00
p - > textParams = uiprivDefaultTextColumnOptionalParams ;
2018-06-08 20:44:55 -05:00
p - > imageModelColumn = - 1 ;
p - > checkboxModelColumn = - 1 ;
2018-06-24 08:52:01 -05:00
p - > checkboxEditableModelColumn = - 1 ;
2018-06-08 20:44:55 -05:00
p - > progressBarModelColumn = - 1 ;
p - > buttonModelColumn = - 1 ;
t - > columns - > push_back ( p ) ;
return p ;
}
2018-06-08 20:16:06 -05:00
2018-06-24 08:52:01 -05:00
void uiTableAppendTextColumn ( uiTable * t , const char * name , int textModelColumn , int textEditableModelColumn , uiTableTextColumnOptionalParams * textParams )
2018-06-08 20:44:55 -05:00
{
2018-06-09 23:49:44 -05:00
uiprivTableColumnParams * p ;
2018-06-08 20:16:06 -05:00
2018-06-08 20:44:55 -05:00
p = appendColumn ( t , name , LVCFMT_LEFT ) ;
p - > textModelColumn = textModelColumn ;
2018-06-24 08:52:01 -05:00
p - > textEditableModelColumn = textEditableModelColumn ;
if ( textParams ! = NULL )
p - > textParams = * textParams ;
2018-06-08 20:44:55 -05:00
}
void uiTableAppendImageColumn ( uiTable * t , const char * name , int imageModelColumn )
{
2018-06-16 21:12:54 -05:00
uiprivTableColumnParams * p ;
p = appendColumn ( t , name , LVCFMT_LEFT ) ;
p - > imageModelColumn = imageModelColumn ;
2018-06-08 20:44:55 -05:00
}
void uiTableAppendImageTextColumn ( uiTable * t , const char * name , int imageModelColumn , int textModelColumn , int textEditableModelColumn , uiTableTextColumnOptionalParams * textParams )
{
2018-06-09 23:49:44 -05:00
uiprivTableColumnParams * p ;
2018-06-08 20:44:55 -05:00
2018-06-09 11:15:50 -05:00
p = appendColumn ( t , name , LVCFMT_LEFT ) ;
2018-06-08 20:44:55 -05:00
p - > textModelColumn = textModelColumn ;
2018-06-24 08:52:01 -05:00
p - > textEditableModelColumn = textEditableModelColumn ;
2018-06-08 21:05:11 -05:00
if ( textParams ! = NULL )
p - > textParams = * textParams ;
2018-06-08 20:44:55 -05:00
p - > imageModelColumn = imageModelColumn ;
2018-06-08 20:16:06 -05:00
}
2018-06-08 20:44:55 -05:00
void uiTableAppendCheckboxColumn ( uiTable * t , const char * name , int checkboxModelColumn , int checkboxEditableModelColumn )
2018-06-08 20:16:06 -05:00
{
2018-06-09 23:49:44 -05:00
uiprivTableColumnParams * p ;
2018-06-09 22:02:37 -05:00
p = appendColumn ( t , name , LVCFMT_LEFT ) ;
p - > checkboxModelColumn = checkboxModelColumn ;
2018-06-24 08:52:01 -05:00
p - > checkboxEditableModelColumn = checkboxEditableModelColumn ;
2018-06-08 20:16:06 -05:00
}
2018-06-08 20:44:55 -05:00
void uiTableAppendCheckboxTextColumn ( uiTable * t , const char * name , int checkboxModelColumn , int checkboxEditableModelColumn , int textModelColumn , int textEditableModelColumn , uiTableTextColumnOptionalParams * textParams )
2018-06-08 20:16:06 -05:00
{
2018-06-10 09:43:29 -05:00
uiprivTableColumnParams * p ;
2018-06-08 20:44:55 -05:00
p = appendColumn ( t , name , LVCFMT_LEFT ) ;
p - > textModelColumn = textModelColumn ;
2018-06-24 08:52:01 -05:00
p - > textEditableModelColumn = textEditableModelColumn ;
2018-06-08 21:05:11 -05:00
if ( textParams ! = NULL )
p - > textParams = * textParams ;
2018-06-08 20:44:55 -05:00
p - > checkboxModelColumn = checkboxModelColumn ;
2018-06-24 08:52:01 -05:00
p - > checkboxEditableModelColumn = checkboxEditableModelColumn ;
2018-06-08 20:44:55 -05:00
}
void uiTableAppendProgressBarColumn ( uiTable * t , const char * name , int progressModelColumn )
{
2018-06-16 12:06:20 -05:00
uiprivTableColumnParams * p ;
p = appendColumn ( t , name , LVCFMT_LEFT ) ;
p - > progressBarModelColumn = progressModelColumn ;
2018-06-08 20:44:55 -05:00
}
2018-06-24 08:52:01 -05:00
void uiTableAppendButtonColumn ( uiTable * t , const char * name , int buttonModelColumn , int buttonClickableModelColumn )
2018-06-08 20:44:55 -05:00
{
2018-06-16 21:12:54 -05:00
uiprivTableColumnParams * p ;
// TODO see if we can get rid of this parameter
p = appendColumn ( t , name , LVCFMT_LEFT ) ;
2018-06-24 08:52:01 -05:00
p - > buttonModelColumn = buttonModelColumn ;
2018-06-16 21:12:54 -05:00
p - > buttonClickableModelColumn = buttonClickableModelColumn ;
2018-06-08 20:16:06 -05:00
}
2018-06-24 09:28:41 -05:00
uiTable * uiNewTable ( uiTableParams * p )
2018-05-29 19:26:48 -05:00
{
uiTable * t ;
int n ;
2018-06-10 09:43:29 -05:00
HRESULT hr ;
2018-05-29 19:26:48 -05:00
uiWindowsNewControl ( uiTable , t ) ;
2018-06-07 21:54:01 -05:00
2018-06-09 23:49:44 -05:00
t - > columns = new std : : vector < uiprivTableColumnParams * > ;
2018-06-24 09:28:41 -05:00
t - > model = p - > Model ;
t - > backgroundColumn = p - > RowBackgroundColorModelColumn ;
2018-06-19 06:51:34 -05:00
// WS_CLIPCHILDREN is here to prevent drawing over the edit box used for editing text
2018-05-29 19:26:48 -05:00
t - > hwnd = uiWindowsEnsureCreateControlHWND ( WS_EX_CLIENTEDGE ,
WC_LISTVIEW , L " " ,
2018-06-19 06:51:34 -05:00
LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_CLIPCHILDREN | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL ,
2018-05-29 19:26:48 -05:00
hInstance , NULL ,
TRUE ) ;
2018-06-24 09:28:41 -05:00
t - > model - > tables - > push_back ( t ) ;
2018-05-29 19:26:48 -05:00
uiWindowsRegisterWM_NOTIFYHandler ( t - > hwnd , onWM_NOTIFY , uiControl ( t ) ) ;
// TODO: try LVS_EX_AUTOSIZECOLUMNS
2018-06-10 16:38:51 -05:00
// TODO check error
2018-05-29 19:26:48 -05:00
SendMessageW ( t - > hwnd , LVM_SETEXTENDEDLISTVIEWSTYLE ,
2018-06-09 11:15:50 -05:00
( WPARAM ) ( LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES ) ,
( LPARAM ) ( LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES ) ) ;
2018-06-24 09:28:41 -05:00
n = uiprivTableModelNumRows ( t - > model ) ;
2018-05-29 19:26:48 -05:00
if ( SendMessageW ( t - > hwnd , LVM_SETITEMCOUNT , ( WPARAM ) n , 0 ) = = 0 )
logLastError ( L " error calling LVM_SETITEMCOUNT in uiNewTable() " ) ;
2018-06-08 21:05:11 -05:00
2018-06-15 21:50:19 -05:00
hr = uiprivUpdateImageListSize ( t ) ;
2018-06-10 09:43:29 -05:00
if ( hr ! = S_OK ) {
// TODO
}
2018-06-09 22:02:37 -05:00
2018-06-16 18:05:36 -05:00
t - > indeterminatePositions = new std : : map < std : : pair < int , int > , LONG > ;
if ( SetWindowSubclass ( t - > hwnd , tableSubProc , 0 , ( DWORD_PTR ) t ) = = FALSE )
logLastError ( L " SetWindowSubclass() " ) ;
2018-05-29 19:26:48 -05:00
return t ;
}