User:CBM-c-/VS-List Browsers part 4: Difference between revisions

From Vectorworks Developer
Jump to navigation Jump to search
(Create Subpage for List Browser 4)
 
(→‎Events: expand)
 
(8 intermediate revisions by the same user not shown)
Line 4: Line 4:


For better understanding please use the comprehensive example at [[User:CBM-c-/Dialog with List Browser| Dialog with List Browser]].
For better understanding please use the comprehensive example at [[User:CBM-c-/Dialog with List Browser| Dialog with List Browser]].
By --[[User:CBM-c-|_c_]] ([[User talk:CBM-c-|talk]]) 03:08, 31 December 2020 (EST) (previously Orso B. Schmid)
 
By --[[User:CBM-c-|_c_]] ([[User talk:CBM-c-|talk]]) 03:08, 31 December 2020 (EST), previously Orso B. Schmid.




Line 19: Line 20:




A List Browser is capable of returning an event on user's click. Using events offers an easy way to store the last selected row and column. It is always advisable to use events since this reduces your code. The user's selection is parsable through '''GetLBEventInfo'''. It returns a set of three informations:
List Browsers can return an event on user's click or mouse activity through [[VS:GetLBEventInfo]], see examples below. The list of list browser's events is to be found in the SDK in the file ''SDKLib/Include/Kernel/MiniCadCallbacks.X.h''. On the present Developer wiki there are a few notes about events at [[SDK:Layout_Manager:_List_Browser]].
* event triggered
 
[[VS:GetLBEventInfo]] returns a set of three informations:
 
* row selected
* row selected
* column selected
* column selected
There is a subtle difference in the type of event triggered, depending on the control type of the column involved. Documented is the event "-4", upon row selection, but undocumented is that the event returned can also be "-2" if the cell clicked belongs to a column whose [[User:CBM-c-/VS-List_Browsers_part_2#Control_Type| Control Type]] uses [[User:CBM-c-/VS-List_Browsers_part_2#Column_Data_Items_.28list.29| Column Data Items]].
* event triggered
Mind please that the row returned might be "-1" if the user clicks on white space within the List Browser. This can happen if your List Browser is higher than the rows displayed. After the last row some white space displays and clicks on this space cause event "-1".
It is *highly* recommendable to parse for List Browsers events exclusively in the dialog events CASE statement, straight where you code the response to user's action on the listBrowserID item. The event returned will be "-1" if the List Browsers hasn't been clicked at least once (for example just at dialog start). Or even more misleading values. Read this attentively.
[EDIT Dieter Geerts 2014-03-15] : I could not reproduce the '-1' event type. I guess it is solved in the current version of VW, 2014.


<code lang="pas">
<code lang="pas">
GetLBEventInfo( dialogID:LONGINT; componentID:LONGINT;  
{ list browser events }
VAR eventType:INTEGER; VAR rowIndex:INTEGER; VAR columIndex:INTEGER):BOOLEAN;
kLBrowColDataSel = -2; { user changed column data items, this occurs also without click }
kLBDataChangeAllClick = -3; {}
kLBrowSel = -4; { user clicked on row }
kLBDoubleClick = -5; { I can't ever get this to show, must be SDK only }
kLBDeleteKeyPressed = -6;
kLBUpKeyPressed = -7;
kLBDownKeyPressed = -8; { user scrolled a row using arrow keys: no rows and cols are set by GetLBEventInfo }
kLBAlphaNumericKeyPressed = -9; { user typed a key and the LB scrolls down: no rows/cols are set by GetLBEventInfo }
kLBSortOccurred = -10;
kLBSelectionMultChangeClick = -11;
kLBEnterKeyPressed = -12;
 
{ from the SDK, I never saw them in the reality: }
kLBDataChangeRecursiveClick = -13;
kLBDoubleAllClick = -14;
kLBDoubleRecursiveClick = -15;
 
{ LB's direct edit, introduced by VW 2020, not accessibile yet (VW 2021) with VS }
kLBQueryInteractionType = -16;
kLBQueryItemValue = -17;
kLBQueryItemListRetrieval = -18;
kLBItemEditCompletionData = -19;  
</code>
</code>


Sets variables with the last event raised, the last selected row and last selected column.
* You should always use this routine in an IF condition for the case that it returns FALSE.
* You shall pay a lot of care if you call ''GetLBEventInfo'' outside the CASE statement for the involved List Browser item ID. See below. ''GetLBEventInfo'' should be used exclusively in the CASE statement for your List Browser. The events returned are not reliable otherwise.
* If you store selection indexes in global variables, you'll do well to initialize them to -1 and code accordingly. If for some reason ''GetLBEventInfo'' returns false, you risk misleading values otherwise (for example "0" which would lead to believe a first column or row has selected).
* Setting a sorting column somewhere in your script raises an event. I believe it shouldn't. This will record as last user event and will mislead you. Be careful if you change sorting column after column creation.
;  Event flags:  
;  Event flags:  
* no event: row selection for on-click-toggle control types but no Data Items List is available
* no event: row selection for on-click-toggle control types but no Data Items List is available
* -1 if the user clicks on white space within the List Browser. Can happen if your List Browser is higher than the rows displayed. After the last row some white space displays and clicks on this space cause event "-1".
* event = -2: row selection for on-click-toggle control types when items are available
* event = -2: row selection for on-click-toggle control types when items are available
* event = -4: row selection for control types Static and Number
* event = -4: row selection for control types Static and Number
:* event = -8: the user scrolls the LB using the arrow keys. This event sets no vars for the rows or cols: rowIndex is always -1, columIndex is always -1. The involved row will highlight nevertheless. But you have no way to find out what is selected until the user actually clicks on a row.
* event = -8: the user scrolls the LB using the arrow keys. This event sets no vars for the rows or cols: rowIndex is always -1, columIndex is always -1. The involved row will highlight nevertheless. But you have no way to find out what is selected until the user actually clicks on a row.
* event = -9: the user types a string while the focus is on the LB. This behaves the same as event -8. From Julian Carr on the V-list.
* event = -9: the user types a string while the focus is on the LB. This behaves the same as event -8. From Julian Carr on the V-list.
* event = -10: column selection  
* event = -10: column selection  
;  Events on drag-drop: [DG - 2014-03-16]
 
<code lang="pas">
GetLBEventInfo( dialogID: LONGINT; componentID: LONGINT;
VAR eventType:INTEGER; VAR rowIndex:INTEGER; VAR columIndex:INTEGER):BOOLEAN;
</code>
 
[[VS:GetLBEventInfo]] Sets variables with the last event raised, the last selected row and last selected column. Starting with VW 2021, on Windows you can use ''GetLBEventInfo'' only at the very moment where the user click occurred. Trying to retrieve last user clicks/events after other dialog events will lead to no informations set. This used to be different in previous versions, where you could parse for events any time.
; Important: you should store as globals last selected row, column, and event.
* If you store selection indexes in global variables, you'll do well to initialize them to -1 and code accordingly. If for some reason ''GetLBEventInfo'' returns false, you risk misleading values otherwise (for example "0" which would lead to believe a first column or row has selected).
* Setting a sorting column somewhere in your script raises an event. I believe it shouldn't. This will record as last user event. Be careful if you change sorting column after column creation.
* You should always use this routine in an IF condition for the case that it returns FALSE.
 
;  Events on [[User:CBM-c-/VS-List_Browsers_part_2#Column Drag and drop| Drag and Drop]]: [DG - 2014-03-16]
You need to use the data that comes with the dialog event handler here to catch the drag-drop events!
You need to use the data that comes with the dialog event handler here to catch the drag-drop events!
:* event = -4; data = rowIndex: When starting, a normal event is thrown, so just a select event.
* event = -4; data = rowIndex: When starting, a normal event is thrown, so just a select event.
* event = -4; data = -50: The first event on releasing the item(s).
* event = -4; data = -50: The first event on releasing the item(s).
* event = -4; data = 43703408: The second event on releasing the item(s).
* event = -4; data = 43703408: The second event on releasing the item(s).
* event = -4; data = -51: The third and last event on releasing the item(s).
* event = -4; data = -51: The third and last event on releasing the item(s).
So check for the -51 data to react on drag-drop of items.
So check for the -51 data to react on drag-drop of items.
And here I go. Orso's usual dissection:
 
; Beastly misleading:  
; Beastly misleading:  
* event = 0: you called GetLBEventInfo outside the CASE block AND didn't set a sorting column somewhere in your code  
* event = 0: you called GetLBEventInfo outside the CASE block AND didn't set a sorting column somewhere in your code  
* eventRow = 0: see above
* eventRow = 0: see above
* eventCol = 0: see above
* eventCol = 0: see above
:* event = -10: you called GetLBEventInfo outside the CASE block AND set a sorting column somewhere in your code!
* event = -10: you called GetLBEventInfo outside the CASE block AND set a sorting column somewhere in your code!
* eventRow = -1: see above
* eventRow = -1: see above
* eventCol = 0: see above
* eventCol = 0: see above
;  Probable hint of crashy VW:  
;  Probable hint of crashy VW:  
* event = -&lt;long integer value&gt;: you called GetLBEventInfo outside the CASE block and you got some garbage value from previous codes. I saw this happen but cannot reproduce.
* event = -&lt;long integer value&gt;: you called GetLBEventInfo outside the CASE block and you got some garbage value from previous codes. I saw this happen but cannot reproduce.
* eventRow = -&lt;long integer value&gt;: see above
* eventRow = -&lt;long integer value&gt;: see above
* eventCol = -&lt;long integer value&gt;: see above
* eventCol = -&lt;long integer value&gt;: see above
;  Warning:  
;  Warning:  
* eventRow = -1: clicking on white space below the last row (for example when your row count doesn't fill the whole available List Browser space).
* eventRow = -1: clicking on white space below the last row (for example when your row count doesn't fill the whole available List Browser space).
Line 69: Line 100:
* eventCol = 0: default value, before first user's click on the List Browser (for example just after dialog launch). this might be regarded as a bug.
* eventCol = 0: default value, before first user's click on the List Browser (for example just after dialog launch). this might be regarded as a bug.
* Resizing columns doesn't return an event parsable with GetLBEventInfo.
* Resizing columns doesn't return an event parsable with GetLBEventInfo.
;  Furthermore:  it can be observed that image based control types behave differently:
;  Furthermore:  it can be observed that image based control types behave differently:
* event = -2: row selection for control types: Static Icon, Multiple Icons
* event = -2: row selection for control types: Static Icon, Multiple Icons
* event = -2: row selection for control types: Radio, Multi State, Single Instance when Column Data Items are defined.
* event = -2: row selection for control types: Radio, Multi State, Single Instance when Column Data Items are defined.
Must have something to do with the Column Data Items wanting to change or these controls expecting them.
Must have something to do with the Column Data Items wanting to change or these controls expecting them.
 
;  Returns FALSE:  
;  Returns FALSE:  
* on row selection for control types: Radio, Multi State, Single Instance when Column Data Items are missing (this is a hint that you forgot to set up Column Data Items).
* on row selection for control types: Radio, Multi State, Single Instance when Column Data Items are missing (this is a hint that you forgot to set up Column Data Items).
;  Unexpectedly returns TRUE:  
;  Unexpectedly returns TRUE:  
* calling the routine before first user's click on the List Browser (for example just after dialog launch).... and you called it outside the CASE statement for your List Browser ID.
* calling the routine before first user's click on the List Browser (for example just after dialog launch).... and you called it outside the CASE statement for your List Browser ID.
<code lang="pas">
{put in SetupDialogC case }
CASE item OF
SetupDialogC:
BEGIN
{ ... }
END;
listBrowserID: IF GetLBEventInfo(dlog, listBrowserID, event, eventRow, eventCol) THEN
alrtDialog(concat('event: ', event, ' last sel row: ', eventRow, ' last sel col: ', eventCol));
END;
{ ... }
</code>
=== Drag and drop ===
Drag and drop permits to the user to change order of the rows. This feature is disabled by default. Upon selecting a row and beginning a drag, the List Browser will wait for the user to drop it at some other position: it triggers user interaction. This feature can only be set on columns with control [[User:CBM-c-/VS-List_Browsers_part_2#Number| Number]]. Other columns will simply behave weird. An example can be observed on the "Design Layers" tab of NNA's Organizations dialog, when the option "Details" is activated: the stacking order is dealt with ''Drag and Drop''.
This feature can be made even more interesting when combined with [[#List_Browsers_Events| List Browsers Events]]. At this point you can pass rows of data between two List Browsers. If you code such functionality you must pay great attention to reset the last selected indexes of both List Browsers. There is to my knowledge no NNA dialog using drag and drop between two (or more) List Browsers so user's of your dialog won't recognize the possibility on their own. They are not used to it. Abundant alerts should inform the user that a drop on another List Browser is awaited. Then it works like a charm. Among my favorites, but it takes some heavy coding to set up properly.
;  For this to work you need:
* [[User:CBM-c-/VS-List_Browsers_part_2#Number| Number]]
;  BUG:  ''on Windows during the user interaction (when the user is dragging a row) the whole screen in all monitors will flicker mercilessly, excluding the Vectorworks document window, which stay put and quiet. This is a long standing issue, but after all we don't look at anything else but our List Browser, while we are about to drop rows. (Fixed by VW 14 SP3)''
<code lang="pas">
EnableLBDragAndDrop(dlog: LONGINT; LB: LONGINT;
enable: BOOLEAN) : BOOLEAN;
</code>
Enables/disables drag and drop on a List Browser. It only works for columns of type ''Number''.
;  result:  always TRUE, like ''SetLBDragDropColumn''. Simply pass the result to a temporary boolean variable that you can trash.
<code lang="pas">
SetLBDragDropColumn( dlog : LONGINT; LB : LONGINT;
columnIndex: INTEGER) : BOOLEAN;
</code>
Declare the drag and drop column.
;  result:  it doesn't look capable of returning false. Even if the List Browser doesn't have a single column, it will happily return TRUE.
See also: [[#List_Browsers_Events| Using List Browsers Events]]
<code lang="pas">
EnableLBDropOnIndices(dlog, LB : LONGINT;
iStartIndex, iEndIndex : INTEGER; bEnable : BOOLEAN) : BOOLEAN;
</code>
By default all rows accept dropping. With this call is possible to disable, or re-enable, some rows from dropping. It shall be noticed that this is targeting rows, not columns.
;  iStartIndex, iEndIndex:  range of rows where dropping is disabled.
=== Sorting ===
By default sorting is activated upon creating a List Browser, but it's possible to enable, disable and set sorting order (ascending or descending) during dialog execution. A few observations:
* Sorting image cells is meaningless, you can disable it since it will only upset your user.
* Sorting text cells on Mac behaves differently than on PC: this I wish to explore further.
* Sorting numeric cells sort them alphabetically, see the cell ''Owner'' [[User:CBM-c-/VS-List_Browsers_part_2#Number| Number]].
;  NOTE:  setting a sorting column by script raises a List Browser event, which is a bug. This should only happen on user interaction. See [[#List_Browsers_Events| List Browsers Events]].
<code lang="pas">
EnableLBSorting(dialogID: LONGINT; componentI LONGINT;
enableSorting: BOOLEAN);
</code>
Enables/disables sorting for a whole List Browser. Sorting is TRUE by default.
<code lang="pas">
GetLBColumnSortState( dialogID:LONGINT; componentID:LONGINT;
columnIndex:INTEGER):INTEGER;
</code>
Returns sorting state of a column.
<code lang="pas">
GetLBSortColumn( dialogID:LONGINT; componentID:LONGINT):INTEGER;
SetLBSortColumn( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
isAscending:BOOLEAN);
</code>
Returns or set the current sort column in a List Browser whose sorting is enabled.




Line 159: Line 120:
SetLBSelection(dialogID: LONGINT; componentID: LONGINT;  
SetLBSelection(dialogID: LONGINT; componentID: LONGINT;  
firstItemIndex: INTEGER; lastItemIndex: INTEGER; select: BOOLEAN): BOOLEAN;
firstItemIndex: INTEGER; lastItemIndex: INTEGER; select: BOOLEAN): BOOLEAN;
</code>
Select/deselects a range of rows from row "firstItemIndex" to row "lastItemIndex". If the selection is outside the List Browser's scroll window, the window will automatically scroll to show the selection. See [[#Visibility| EnsureLBItemIsVisible]] for a similar behavior, but without selection.
<code lang="pas">
GetNumSelectedLBItems(dialogID: LONGINT; componentID: LONGINT): INTEGER;
GetNumSelectedLBItems(dialogID: LONGINT; componentID: LONGINT): INTEGER;
</code>
Count of currently selected rows. Among the most used routines, allows to loop down all selected rows for processing. Using [[#List_Browsers_Events| List Browsers Events]] the last selected row and column will be collected. But this still doesn't inform you on other rows eventually selected. This check is not needed if '''EnableLBSingleLineSelection''' is active: the user cannot select more than one line.
<code lang="pas">
IsLBItemSelected(dialogID: LONGINT; componentID: LONGINT;  
IsLBItemSelected(dialogID: LONGINT; componentID: LONGINT;  
itemIndex: INTEGER): BOOLEAN;
itemIndex: INTEGER): BOOLEAN;
</code>
Checks if row "itemIndex" is currently selected (itemIndex = rowIndex). Like the previous routine, this check is not needed if ''EnableLBSingleLineSelection''' is active and the script parses for [[#List_Browsers_Events| List Browsers Events]].
 
<code lang="pas">
EnableLBSingleLineSelection(dialogID: LONGINT; componentID: LONGINT;  
EnableLBSingleLineSelection(dialogID: LONGINT; componentID: LONGINT;  
enable: BOOLEAN): BOOLEAN;
enable: BOOLEAN): BOOLEAN;
</code>
Enable/disable selection of multiple rows. By default multiple selection is enabled. This is affects the whole list browser.
<code lang="pas">
EnableLBClickAllDataChange(dialogID: LONGINT; componentID: LONGINT;  
EnableLBClickAllDataChange(dialogID: LONGINT; componentID: LONGINT;  
enable: BOOLEAN): BOOLEAN;
enable: BOOLEAN): BOOLEAN;
</code>
</code>
Enable/disable changing all cells' values in a column when a modifier key (ctrl/alt on Mac/PC) is pressed. An example of this feature is the Navigation palette: the visibility column. Upon clicking on a row and while multiple rows are selected, the value displayed in the clicked row will apply to all selected rows. Is a good feature that can save some code, if your script allows for this functionality.


<div style="margin: 1em; border: dotted 1px #aaa; padding: 1em; background-color: #F2F2F2;">
[[VS:SetLBSelection]] Selects/deselects a range of rows from row "firstItemIndex" to row "lastItemIndex". If the selection is outside the List Browser's scroll window, the window will automatically scroll to show the selection. See [[VS:EnsureLBItemIsVisible]] for a similar behavior, but without selection. This check is not needed if [[VS:EnableLBSingleLineSelection]] is active, where the user cannot select more than one line.
Examples:
 
[[VS:EnableLBSingleLineSelection]] Enable/disable selection of multiple rows. By default multiple selection is enabled. This affects the whole list browser.


<code lang="pas">
[[VS:EnableLBClickAllDataChange]] Enable/disable changing all cells' values in a column when a modifier key (ctrl/alt on Mac/PC) is pressed. An example of this feature is the Navigation palette: the visibility column. Upon clicking on a row and while multiple rows are selected, the value displayed in the clicked row will apply to all selected rows.
PROCEDURE LB_SetSelAllRows(d, LB: LONGINT; select: BOOLEAN);
VAR
temp_b : BOOLEAN;
BEGIN
IF GetNumLBItems(d, LB) &gt; 0 THEN
temp_b := SetLBSelection(d, LB, 0, GetNumLBItems(d, LB)-1, select);
END;
PROCEDURE LB_DeleteSelRows(d, LB: LONGINT);
VAR
i : INTEGER;
temp_b : BOOLEAN;
BEGIN
i := 0;
WHILE i &lt; GetNumLBItems(d, LB) DO
IF IsLBItemSelected(d, LB, i) THEN
temp_b := DeleteLBItem(d, LB, i)
ELSE
i := i + 1;
END;
</code>
</div>




=== Destroy data ===
=== Destroy data ===


Sometimes what you need is to destroy data. Below the routine allowing for this to be achieved:
Sometimes what you need is to destroy rows or columns:


<code lang="pas">
<code lang="pas">
DeleteLBItem(dialogID: LONGINT; componentID: LONGINT;
rowIndex: INTEGER): BOOLEAN; { removes row }
DeleteLBColumn(dialogID: LONGINT; componentID: LONGINT;  
DeleteLBColumn(dialogID: LONGINT; componentID: LONGINT;  
columnIndex: INTEGER): BOOLEAN;
columnIndex: INTEGER): BOOLEAN; { removes col }
RemoveLBColumnDataItem( dialogID: LONGINT; componentID: LONGINT;
columnIndex:INTEGER; columnDataItemIndex:INTEGER):BOOLEAN; { removes col data item }
DeleteAllLBItems(dialogID: LONGINT; componentID: LONGINT): BOOLEAN;
RemoveAllLBColumnDataItems( dialogID: LONGINT; componentID: LONGINT;
columnIndex:INTEGER);
</code>
</code>
Remove the column at "columnIndex" from the List Browser. Is seldom, but it can happen that you need to rebuild the List Browser's columns anew. One such circumstance is that you need to re-write the columns titles.
: ''BUG: If you pass the index of a column (NOT Column Data Item) for deleting it, and this doesn't exists, the application will crash (VW 13 and 14).''


<code lang="pas">
RemoveAllLBColumnDataItems( dialogID:LONGINT; componentID:LONGINT;
columnIndex:INTEGER);
</code>
Remove all items in the [[User:CBM-c-/VS-List_Browsers_part_2#Column_Data_Items_.28list.29| List of Column Data Items]] defined for the column at "columnIndex".


<code lang="pas">
[[VS:DeleteLBItem]] Remove the row at "rowIndex" from the List Browser.
RemoveLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT;
 
columnIndex:INTEGER; columnDataItemIndex:INTEGER):BOOLEAN;
[[VS:DeleteLBColumn]] Remove the column at "columnIndex" from the List Browser. If you need to re-write the column headers, you must delete all columns.
</code>
Remove the chosen item at "columnDataItemIndex" from the [[User:CBM-c-/VS-List_Browsers_part_2#Column_Data_Items_.28list.29| List of Column Data Items]] defined for the column at "columnIndex".


<code lang="pas">
[[VS:RemoveLBColumnDataItem]] Remove the chosen item at "columnDataItemIndex" from the [[User:CBM-c-/VS-List_Browsers_part_2#Column Data Items List| Column Data Items List]] defined for the column at "columnIndex".
DeleteAllLBItems(dialogID: LONGINT; componentID: LONGINT): BOOLEAN;
</code>
Remove all rows in List Browser.


<code lang="pas">
[[VS:DeleteAllLBItems]], [[VS:RemoveAllLBColumnDataItems]].
DeleteLBItem(dialogID: LONGINT; componentID: LONGINT;
itemIndex: INTEGER): BOOLEAN;
</code>
Remove the row at "itemIndex".




=== Update and Refresh ===
=== Update and Refresh ===


When the entire content of your List Browser shall be rebuilt destroying or modifying rows in a loop, you should always remember to disable the List Browser updates using '''EnableLBUpdates''' before the loop and reactivate it after the loop has finished executing. If you don't, you might bog down VW, which will recalculate the List Browser at each row, each cell inserted. Don't forget to re-enable updates otherwise you get a white list browser according to the event sent on user's click.
Disable the List Browser updates with [[VS:EnableLBUpdates]] before looping through a list browser for any operation, reactivate it after the loop has finished executing. If you don't, VW will recalculate the List Browser at each row, each cell inserted.  
To be noted that enabling doesn't really work well on PC (!expand!).
 
A List Browser refresh might be needed after changing row's data. This applies specially for resource images which might be changing during your script execution. The routine '''RefreshLB''' coerces the refresh. If you change data in a loop please remember to keep "RefreshLB" out of it. This call should always be the last thing you do after renewing data in any fashion. This is among the most important settings when you are dealing with heavy data.
Don't forget to re-enable updates and refresh the List Browser using [[VS:RefreshLB]].
This call should always be the last thing you do after renewing data.


<code lang="pas">
<code lang="pas">
EnableLBUpdates(liDialogID, liComponentID :LONGINT;  
EnableLBUpdates(liDialogID, liComponentID : LONGINT;  
bEnableUpdates :BOOLEAN);
bEnableUpdates :BOOLEAN);
RefreshLB( dialogID: LONGINT; componentID: LONGINT):BOOLEAN;
</code>
</code>
Enable/disable updating cells in a List Browser. Disable ''always'' before looping rows with content modification and re-enable after the loop is terminated.


<code lang="pas">
[[VS:EnableLBUpdates]] Enable/disable updating cells in a List Browser. Disable ''always'' before looping rows with content modification and re-enable after the loop is terminated.
RefreshLB( dialogID:LONGINT; componentID:LONGINT):BOOLEAN;
 
</code>
[[VS:RefreshLB]] Redraws the whole List Browser. Keep this outside content modifications involving loops.
Redraws the whole List Browser. Keep this outside content modifications involving loops.




Line 271: Line 187:


<code lang="pas">
<code lang="pas">
SetFocusOnLB( dialogID:LONGINT; componentID:LONGINT):BOOLEAN;
SetFocusOnLB(dialogID: LONGINT; componentID: LONGINT):BOOLEAN;
EnsureLBItemIsVisible( dialogID: LONGINT; componentID: LONGINT;
index:INTEGER):BOOLEAN;
</code>
</code>


<code lang="pas">
[[VS:SetFocusOnLB]] Focus on the chosen List Browser.
EnsureLBItemIsVisible( dialogID:LONGINT; componentID:LONGINT;
 
index:INTEGER):BOOLEAN;
[[VS:EnsureLBItemIsVisible]] Make the List Browser window display a row even if outside the scroll bounds of the list browser's window. Upon refreshing a List Browser the displayed rows always start at row "0". You don't need this call if you coerce a selection with [[VS:SetLBSelection]], the selection will scroll automatically the window to fit.
</code>
Make the List Browser window display a row even if outside the scroll bounds of the list browser's window. Upon refreshing a List Browser the displayed rows always start at row "0". You don't need this call if you coerce a selection with '''SetLBSelection''': the selection will scroll automatically the window to fit.




=== Enabling ===
=== Enabling ===


Sometimes you need to make your List Browser not accessible to the user, this can be obtained with '''EnableLB'''. The List Browser displays grayed and any user interaction is disabled ([[#Drag_and_drop| Drag and Drop]], on-click events). The List Browser is nevertheless fully enabled internally: you can still change content and display by script and it will update as expected.
Sometimes you need to make your List Browser not accessible to the user, use [[VS:EnableLB]]. The List Browser displays grayed and any user interaction is disabled ([[User:CBM-c-/VS-List_Browsers_part_2#Column Drag and drop| Drag and Drop]], on-click events). The List Browser is nevertheless fully enabled internally: you can still change content and display by script and it will update as expected.
See [[#Drag_and_drop| Drag and Drop]] for disabling dropping only on a range of rows (instead of disabling the whole list browser).
 
See [[User:CBM-c-/VS-List_Browsers_part_2#Column Drag and drop| Drag and Drop]] for disabling dropping only on a range of rows (instead of disabling the whole list browser).


<code lang="pas">
<code lang="pas">
Line 290: Line 207:
enable: BOOLEAN): BOOLEAN;
enable: BOOLEAN): BOOLEAN;
</code>
</code>
Enable/disable a List Browser. Disabled: not clickable and grayed.
 
[[VS:EnableLB]] Enable/disable a List Browser. Disabled: not clickable and grayed.




Line 296: Line 214:


Elsewhere called "itemData". Never figured this out.  
Elsewhere called "itemData". Never figured this out.  
One could think that it expects a resource index, similar to '''CreateControl''', but it doesn't. Since it demands a Longint, I also tipped on indexed resources (records, symbols, images...) or a list created with BuildResourceList, but this must be a wrong concept. Another possibility is that it expects a handle to some sub-object, but we cannot pass it with Vectorscript. For example user data objects of type 76 accompanying some parametric objects or records of certain kinds (textures, sketches...). Last possibility: too much fantasy and is just unsupported.
One could think that it expects a resource index, similar to '''CreateControl''', but it doesn't. Since it demands a Longint,  
 
I also tipped on indexed resources (records, symbols, images...) or a list created with BuildResourceList, but this must be a wrong concept. Another possibility is that it expects a handle to some sub-object, but we cannot pass it with Vectorscript. For example user data objects of type 76 accompanying some parametric objects or records of certain kinds (textures, sketches...). Last possibility: too much fantasy and is just unsupported.
Or does it perhaps await data from another dialog?
Or does it perhaps await data from another dialog?
I have no idea if this should be initialized to "0" or "-1". And I still didn't encounter a situation where this matters. Intuitively I'd say that init on "0" is well.
I have no idea if this should be initialized to "0" or "-1". And I still didn't encounter a situation where this matters. Intuitively I'd say that init on "0" is well.
You will find a reference to this mysterious type of data in the routines below:
You will find a reference to this mysterious type of data in the routines below:
;  InsertLBColumnDataItem:  targets a column and add something to the particular column data item using the parameter "itemData"? What is that we can add here?
 
;  GetLBColumnDataItemInfo:  securely get infos of the mysterious thing added at the particular column data item. Whatever that might be.
[[VS:InsertLBColumnDataItem]]:  targets a column and add something to the particular column data item using the parameter "itemData"? What is that we can add here?
;  SetLBItemData:  ''broken in VW 2017'', either this or GetLBItemData doesn't work any longer, but it did. It targets a cell. Changing it to any possible value didn't ever produce a result by me, but the value set with it could be fetched using GetLBItemData.
 
;  GetLBItemData:  ''broken in VW 2017'', either this or SetLBItemData doesn't work any longer, but it did, returning what was set with SetLBItemData.  
[[VS:GetLBColumnDataItemInfo]]:  securely get infos of the mysterious thing added at the particular column data item. Whatever that might be.
 
[[VS:SetLBItemData]]:  ''broken in VW 2017'', either this or '''GetLBItemData''' doesn't work any longer, but it did. It targets a cell. Changing it to any possible value didn't ever produce a result by me, but the value set with it could be fetched using GetLBItemData.
 
[[VS:GetLBItemData]]:  ''broken in VW 2017'', either this or '''SetLBItemData''' doesn't work any longer, but it did, returning what was set with '''SetLBItemData'''.  




Line 313: Line 237:
! [[User:CBM-c-/VS-List_Browsers_part_4| Part 4: Events]]
! [[User:CBM-c-/VS-List_Browsers_part_4| Part 4: Events]]
|}
|}
=== Examples ===
==== Example: fetch event, row and column ====
<div style="margin: 1em; border: dotted 1px #aaa; padding: 1em; background-color: #F2F2F2;">
<code lang="pas">
{put in SetupDialogC case }
CASE item OF
SetupDialogC:
BEGIN
{ set up the list browser listBrowserID }
{ ... }
END;
{ on some event in the list browser, parse event, rowand column }
listBrowserID:
IF GetLBEventInfo(dlog, listBrowserID, event, eventRow, eventCol) THEN
alrtDialog(concat('event: ', event, ' last sel row: ', eventRow, ' last sel col: ', eventCol));
END;
{ ... }
</code>
==== Example: select all rows ====
<code lang="pas">
{ _c_ ********************************************* }
{ Selects / Deselects all LB rows }
PROCEDURE LB_SetSelAllRows(d, LB: LONGINT; select: BOOLEAN);
VAR
temp_b : BOOLEAN;
BEGIN
IF GetNumLBItems(d, LB) > 0 THEN
temp_b := SetLBSelection(d, LB, 0, GetNumLBItems(d, LB)-1, select);
END;
</code>
==== Example: deselect all rows ====
<code lang="pas">
{ _c_ ******************************************** }
{ Deselects all rows in a LB }
PROCEDURE LB_DeselectAllLBrows(d, LB: LONGINT);
VAR
temp_b : BOOLEAN;
BEGIN
temp_b := SetLBSelection(d, LB, 0, GetNumLBItems(d, LB) -1, FALSE);
END;
</code>
==== Example: deselect selected rows ====
<code lang="pas">
{ _c_ ********************************************* }
{ Delete selected LB items }
PROCEDURE LB_DeleteSelRows(d, LB: LONGINT);
VAR
i : INTEGER;
temp_b : BOOLEAN;
BEGIN
i := 0;
WHILE i < GetNumLBItems(d, LB) DO
IF IsLBItemSelected(d, LB, i) THEN
temp_b := DeleteLBItem(d, LB, i)
ELSE
i := i + 1;
END;
</code>
==== Example: delete all columns ====
<code lang="pas">
{ _c_ ******************************************** }
{ Deletes all columns in a LB }
PROCEDURE LB_DeleteAllLBcols(d, LB: LONGINT);
VAR
temp_b : BOOLEAN;
BEGIN
temp_b := DeleteAllLBItems(d, LB); { remove all rows }
WHILE GetNumLBColumns(d, LB) > 0 DO BEGIN
RemoveAllLBColumnDataItems(d, LB, 0); { remove all data items }
temp_b := DeleteLBColumn(d, LB, 0);
END;
END;
</code>
</div>

Latest revision as of 07:07, 31 January 2021

For better understanding please use the comprehensive example at Dialog with List Browser.

By --_c_ (talk) 03:08, 31 December 2020 (EST), previously Orso B. Schmid.


Part 1: Creation Part 2: Columns Part 3: Rows and Cells Part 4: Events


Events

List Browsers can return an event on user's click or mouse activity through VS:GetLBEventInfo, see examples below. The list of list browser's events is to be found in the SDK in the file SDKLib/Include/Kernel/MiniCadCallbacks.X.h. On the present Developer wiki there are a few notes about events at SDK:Layout_Manager:_List_Browser.

VS:GetLBEventInfo returns a set of three informations:

  • row selected
  • column selected
  • event triggered
{ list browser events }
kLBrowColDataSel = -2;	{ user changed column data items, this occurs also without click }
kLBDataChangeAllClick	= -3; {}
kLBrowSel = -4;	{ user clicked on row }
kLBDoubleClick = -5; { I can't ever get this to show, must be SDK only }
kLBDeleteKeyPressed	= -6;
kLBUpKeyPressed	 = -7;
kLBDownKeyPressed = -8; { user scrolled a row using arrow keys: no rows and cols are set by GetLBEventInfo }
kLBAlphaNumericKeyPressed = -9; { user typed a key and the LB scrolls down: no rows/cols are set by GetLBEventInfo }
kLBSortOccurred = -10;
kLBSelectionMultChangeClick = -11;
kLBEnterKeyPressed = -12;

{ from the SDK, I never saw them in the reality: }
kLBDataChangeRecursiveClick = -13;
kLBDoubleAllClick = -14;
kLBDoubleRecursiveClick = -15;

{ LB's direct edit, introduced by VW 2020, not accessibile yet (VW 2021) with VS }
kLBQueryInteractionType = -16;
kLBQueryItemValue = -17;
kLBQueryItemListRetrieval = -18;
kLBItemEditCompletionData = -19; 
Event flags
  • no event: row selection for on-click-toggle control types but no Data Items List is available
  • -1 if the user clicks on white space within the List Browser. Can happen if your List Browser is higher than the rows displayed. After the last row some white space displays and clicks on this space cause event "-1".
  • event = -2: row selection for on-click-toggle control types when items are available
  • event = -4: row selection for control types Static and Number
  • event = -8: the user scrolls the LB using the arrow keys. This event sets no vars for the rows or cols: rowIndex is always -1, columIndex is always -1. The involved row will highlight nevertheless. But you have no way to find out what is selected until the user actually clicks on a row.
  • event = -9: the user types a string while the focus is on the LB. This behaves the same as event -8. From Julian Carr on the V-list.
  • event = -10: column selection
GetLBEventInfo( dialogID: LONGINT; componentID: LONGINT; 
	VAR eventType:INTEGER; VAR rowIndex:INTEGER; VAR columIndex:INTEGER):BOOLEAN;

VS:GetLBEventInfo Sets variables with the last event raised, the last selected row and last selected column. Starting with VW 2021, on Windows you can use GetLBEventInfo only at the very moment where the user click occurred. Trying to retrieve last user clicks/events after other dialog events will lead to no informations set. This used to be different in previous versions, where you could parse for events any time.

Important
you should store as globals last selected row, column, and event.
  • If you store selection indexes in global variables, you'll do well to initialize them to -1 and code accordingly. If for some reason GetLBEventInfo returns false, you risk misleading values otherwise (for example "0" which would lead to believe a first column or row has selected).
  • Setting a sorting column somewhere in your script raises an event. I believe it shouldn't. This will record as last user event. Be careful if you change sorting column after column creation.
  • You should always use this routine in an IF condition for the case that it returns FALSE.
Events on Drag and Drop
[DG - 2014-03-16]

You need to use the data that comes with the dialog event handler here to catch the drag-drop events!

  • event = -4; data = rowIndex: When starting, a normal event is thrown, so just a select event.
  • event = -4; data = -50: The first event on releasing the item(s).
  • event = -4; data = 43703408: The second event on releasing the item(s).
  • event = -4; data = -51: The third and last event on releasing the item(s).

So check for the -51 data to react on drag-drop of items.

Beastly misleading
  • event = 0: you called GetLBEventInfo outside the CASE block AND didn't set a sorting column somewhere in your code
  • eventRow = 0: see above
  • eventCol = 0: see above
  • event = -10: you called GetLBEventInfo outside the CASE block AND set a sorting column somewhere in your code!
  • eventRow = -1: see above
  • eventCol = 0: see above
Probable hint of crashy VW
  • event = -<long integer value>: you called GetLBEventInfo outside the CASE block and you got some garbage value from previous codes. I saw this happen but cannot reproduce.
  • eventRow = -<long integer value>: see above
  • eventCol = -<long integer value>: see above
Warning
  • eventRow = -1: clicking on white space below the last row (for example when your row count doesn't fill the whole available List Browser space).
  • eventCol = -1: see above.
  • eventCol = 0: default value, before first user's click on the List Browser (for example just after dialog launch). this might be regarded as a bug.
  • Resizing columns doesn't return an event parsable with GetLBEventInfo.
Furthermore
it can be observed that image based control types behave differently:
  • event = -2: row selection for control types: Static Icon, Multiple Icons
  • event = -2: row selection for control types: Radio, Multi State, Single Instance when Column Data Items are defined.

Must have something to do with the Column Data Items wanting to change or these controls expecting them.

Returns FALSE
  • on row selection for control types: Radio, Multi State, Single Instance when Column Data Items are missing (this is a hint that you forgot to set up Column Data Items).
Unexpectedly returns TRUE
  • calling the routine before first user's click on the List Browser (for example just after dialog launch).... and you called it outside the CASE statement for your List Browser ID.


Select

Script-side selection is very well combined with List Browsers Events, which allows to keep easy track of the last user's selection, if any.

SetLBSelection(dialogID: LONGINT; componentID: LONGINT; 
	firstItemIndex: INTEGER; lastItemIndex: INTEGER; select: BOOLEAN): BOOLEAN;
GetNumSelectedLBItems(dialogID: LONGINT; componentID: LONGINT): INTEGER;
IsLBItemSelected(dialogID: LONGINT; componentID: LONGINT; 
	itemIndex: INTEGER): BOOLEAN;
	
EnableLBSingleLineSelection(dialogID: LONGINT; componentID: LONGINT; 
	enable: BOOLEAN): BOOLEAN;
EnableLBClickAllDataChange(dialogID: LONGINT; componentID: LONGINT; 
	enable: BOOLEAN): BOOLEAN;

VS:SetLBSelection Selects/deselects a range of rows from row "firstItemIndex" to row "lastItemIndex". If the selection is outside the List Browser's scroll window, the window will automatically scroll to show the selection. See VS:EnsureLBItemIsVisible for a similar behavior, but without selection. This check is not needed if VS:EnableLBSingleLineSelection is active, where the user cannot select more than one line.

VS:EnableLBSingleLineSelection Enable/disable selection of multiple rows. By default multiple selection is enabled. This affects the whole list browser.

VS:EnableLBClickAllDataChange Enable/disable changing all cells' values in a column when a modifier key (ctrl/alt on Mac/PC) is pressed. An example of this feature is the Navigation palette: the visibility column. Upon clicking on a row and while multiple rows are selected, the value displayed in the clicked row will apply to all selected rows.


Destroy data

Sometimes what you need is to destroy rows or columns:

DeleteLBItem(dialogID: LONGINT; componentID: LONGINT; 
	rowIndex: INTEGER): BOOLEAN; { removes row }
DeleteLBColumn(dialogID: LONGINT; componentID: LONGINT; 
	columnIndex: INTEGER): BOOLEAN; { removes col }
RemoveLBColumnDataItem( dialogID: LONGINT; componentID: LONGINT; 
	columnIndex:INTEGER; columnDataItemIndex:INTEGER):BOOLEAN; { removes col data item }
	
DeleteAllLBItems(dialogID: LONGINT; componentID: LONGINT): BOOLEAN;
RemoveAllLBColumnDataItems( dialogID: LONGINT; componentID: LONGINT; 
	columnIndex:INTEGER);


VS:DeleteLBItem Remove the row at "rowIndex" from the List Browser.

VS:DeleteLBColumn Remove the column at "columnIndex" from the List Browser. If you need to re-write the column headers, you must delete all columns.

VS:RemoveLBColumnDataItem Remove the chosen item at "columnDataItemIndex" from the Column Data Items List defined for the column at "columnIndex".

VS:DeleteAllLBItems, VS:RemoveAllLBColumnDataItems.


Update and Refresh

Disable the List Browser updates with VS:EnableLBUpdates before looping through a list browser for any operation, reactivate it after the loop has finished executing. If you don't, VW will recalculate the List Browser at each row, each cell inserted.

Don't forget to re-enable updates and refresh the List Browser using VS:RefreshLB. This call should always be the last thing you do after renewing data.

EnableLBUpdates(liDialogID, liComponentID : LONGINT; 
	bEnableUpdates :BOOLEAN);
RefreshLB( dialogID: LONGINT; componentID: LONGINT):BOOLEAN;

VS:EnableLBUpdates Enable/disable updating cells in a List Browser. Disable always before looping rows with content modification and re-enable after the loop is terminated.

VS:RefreshLB Redraws the whole List Browser. Keep this outside content modifications involving loops.


Visibility

Useful routines for ensuring that List Browsers are visible:

SetFocusOnLB(dialogID: LONGINT; componentID: LONGINT):BOOLEAN;
EnsureLBItemIsVisible( dialogID: LONGINT; componentID: LONGINT; 
	index:INTEGER):BOOLEAN;

VS:SetFocusOnLB Focus on the chosen List Browser.

VS:EnsureLBItemIsVisible Make the List Browser window display a row even if outside the scroll bounds of the list browser's window. Upon refreshing a List Browser the displayed rows always start at row "0". You don't need this call if you coerce a selection with VS:SetLBSelection, the selection will scroll automatically the window to fit.


Enabling

Sometimes you need to make your List Browser not accessible to the user, use VS:EnableLB. The List Browser displays grayed and any user interaction is disabled ( Drag and Drop, on-click events). The List Browser is nevertheless fully enabled internally: you can still change content and display by script and it will update as expected.

See Drag and Drop for disabling dropping only on a range of rows (instead of disabling the whole list browser).

EnableLB(dialogID: LONGINT; componentID: LONGINT; 
	enable: BOOLEAN): BOOLEAN;

VS:EnableLB Enable/disable a List Browser. Disabled: not clickable and grayed.


User Data

Elsewhere called "itemData". Never figured this out. One could think that it expects a resource index, similar to CreateControl, but it doesn't. Since it demands a Longint,

I also tipped on indexed resources (records, symbols, images...) or a list created with BuildResourceList, but this must be a wrong concept. Another possibility is that it expects a handle to some sub-object, but we cannot pass it with Vectorscript. For example user data objects of type 76 accompanying some parametric objects or records of certain kinds (textures, sketches...). Last possibility: too much fantasy and is just unsupported. Or does it perhaps await data from another dialog? I have no idea if this should be initialized to "0" or "-1". And I still didn't encounter a situation where this matters. Intuitively I'd say that init on "0" is well. You will find a reference to this mysterious type of data in the routines below:

VS:InsertLBColumnDataItem
targets a column and add something to the particular column data item using the parameter "itemData"? What is that we can add here?
VS:GetLBColumnDataItemInfo
securely get infos of the mysterious thing added at the particular column data item. Whatever that might be.
VS:SetLBItemData
broken in VW 2017, either this or GetLBItemData doesn't work any longer, but it did. It targets a cell. Changing it to any possible value didn't ever produce a result by me, but the value set with it could be fetched using GetLBItemData.
VS:GetLBItemData
broken in VW 2017, either this or SetLBItemData doesn't work any longer, but it did, returning what was set with SetLBItemData.


Part 1: Creation Part 2: Columns Part 3: Rows and Cells Part 4: Events


Examples

Example: fetch event, row and column

{put in SetupDialogC case }
CASE item OF
SetupDialogC:
	BEGIN
		{ set up the list browser listBrowserID }
		{ ... }
	END;

{ on some event in the list browser, parse event, rowand column }
listBrowserID:
	IF GetLBEventInfo(dlog, listBrowserID, event, eventRow, eventCol) THEN
		alrtDialog(concat('event: ', event, ' last sel row: ', eventRow, ' last sel col: ', eventCol));
END;
{ ... }


Example: select all rows

{ _c_ ********************************************* }
{ Selects / Deselects all LB rows }
PROCEDURE LB_SetSelAllRows(d, LB: LONGINT; select: BOOLEAN);
	VAR
		temp_b : BOOLEAN;
	BEGIN
		IF GetNumLBItems(d, LB) > 0 THEN
			temp_b := SetLBSelection(d, LB, 0, GetNumLBItems(d, LB)-1, select);
	END;


Example: deselect all rows

{ _c_ ******************************************** }
{ Deselects all rows in a LB }
PROCEDURE LB_DeselectAllLBrows(d, LB: LONGINT);
	VAR
		temp_b : BOOLEAN;
	BEGIN
		temp_b := SetLBSelection(d, LB, 0, GetNumLBItems(d, LB) -1, FALSE);
	END;


Example: deselect selected rows

{ _c_ ********************************************* }
{ Delete selected LB items }
PROCEDURE LB_DeleteSelRows(d, LB: LONGINT);
	VAR
		i : INTEGER;
		temp_b : BOOLEAN;
	BEGIN
		i := 0;
		WHILE i < GetNumLBItems(d, LB) DO
			IF IsLBItemSelected(d, LB, i) THEN
				temp_b := DeleteLBItem(d, LB, i)
			ELSE
				i := i + 1;
	END;


Example: delete all columns

{ _c_ ******************************************** }
{ Deletes all columns in a LB }
PROCEDURE LB_DeleteAllLBcols(d, LB: LONGINT);
	VAR
		temp_b : BOOLEAN;
	BEGIN
		temp_b := DeleteAllLBItems(d, LB); { remove all rows }
		
		WHILE GetNumLBColumns(d, LB) > 0 DO BEGIN
			RemoveAllLBColumnDataItems(d, LB, 0); { remove all data items }
			temp_b := DeleteLBColumn(d, LB, 0);
		END;
	END;