7 char *WMListDidScrollNotification
= "WMListDidScrollNotification";
8 char *WMListSelectionDidChangeNotification
= "WMListSelectionDidChangeNotification";
10 typedef struct W_List
{
14 WMArray
*items
; /* list of WMListItem */
15 WMArray
*selectedItems
; /* list of selected WMListItems */
19 int topItem
; /* index of first visible item */
21 short fullFitLines
; /* no of lines that fit entirely */
25 void *doubleClientData
;
26 WMAction
*doubleAction
;
30 WMHandlerID
*idleID
; /* for updating the scroller after adding elements */
32 WMHandlerID
*selectID
; /* for selecting items in list while scrolling */
34 WMScroller
*vScroller
;
39 unsigned int allowMultipleSelection
:1;
40 unsigned int allowEmptySelection
:1;
41 unsigned int userDrawn
:1;
42 unsigned int userItemHeight
:1;
43 unsigned int dontFitAll
:1; /* 1 = last item won't be fully visible */
44 unsigned int redrawPending
:1;
45 unsigned int buttonPressed
:1;
46 unsigned int buttonWasPressed
:1;
52 #define DEFAULT_WIDTH 150
53 #define DEFAULT_HEIGHT 150
55 #define SCROLL_DELAY 100
58 static void destroyList(List
*lPtr
);
59 static void paintList(List
*lPtr
);
62 static void handleEvents(XEvent
*event
, void *data
);
63 static void handleActionEvents(XEvent
*event
, void *data
);
65 static void updateScroller(void *data
);
66 static void scrollForwardSelecting(void *data
);
67 static void scrollBackwardSelecting(void *data
);
69 static void vScrollCallBack(WMWidget
*scroller
, void *self
);
71 static void toggleItemSelection(WMList
*lPtr
, int index
);
73 static void updateGeometry(WMList
*lPtr
);
74 static void didResizeList();
76 static void unselectAllListItems(WMList
*lPtr
, WMListItem
*exceptThis
);
79 W_ViewDelegate _ListViewDelegate
= {
89 updateDoubleBufferPixmap(WMList
*lPtr
)
91 WMView
*view
= lPtr
->view
;
92 WMScreen
*scr
= view
->screen
;
94 if (!view
->flags
.realized
)
97 if (lPtr
->doubleBuffer
)
98 XFreePixmap(scr
->display
, lPtr
->doubleBuffer
);
100 XCreatePixmap(scr
->display
, view
->window
, view
->size
.width
,
101 lPtr
->itemHeight
, scr
->depth
);
106 realizeObserver(void *self
, WMNotification
*not)
108 updateDoubleBufferPixmap(self
);
113 releaseItem(void *data
)
115 WMListItem
*item
= (WMListItem
*)data
;
124 WMCreateList(WMWidget
*parent
)
127 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
129 lPtr
= wmalloc(sizeof(List
));
130 memset(lPtr
, 0, sizeof(List
));
132 lPtr
->widgetClass
= WC_List
;
134 lPtr
->view
= W_CreateView(W_VIEW(parent
));
139 lPtr
->view
->self
= lPtr
;
141 lPtr
->view
->delegate
= &_ListViewDelegate
;
143 WMCreateEventHandler(lPtr
->view
, ExposureMask
|StructureNotifyMask
144 |ClientMessageMask
, handleEvents
, lPtr
);
146 WMCreateEventHandler(lPtr
->view
, ButtonPressMask
|ButtonReleaseMask
147 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
,
148 handleActionEvents
, lPtr
);
150 lPtr
->itemHeight
= WMFontHeight(scrPtr
->normalFont
) + 1;
152 lPtr
->items
= WMCreateArrayWithDestructor(4, releaseItem
);
153 lPtr
->selectedItems
= WMCreateArray(4);
155 /* create the vertical scroller */
156 lPtr
->vScroller
= WMCreateScroller(lPtr
);
157 WMMoveWidget(lPtr
->vScroller
, 1, 1);
158 WMSetScrollerArrowsPosition(lPtr
->vScroller
, WSAMaxEnd
);
160 WMSetScrollerAction(lPtr
->vScroller
, vScrollCallBack
, lPtr
);
162 /* make the scroller map itself when it's realized */
163 WMMapWidget(lPtr
->vScroller
);
165 W_ResizeView(lPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
167 WMAddNotificationObserver(realizeObserver
, lPtr
,
168 WMViewRealizedNotification
, lPtr
->view
);
175 WMSetListAllowMultipleSelection(WMList
*lPtr
, Bool flag
)
177 lPtr
->flags
.allowMultipleSelection
= ((flag
==0) ? 0 : 1);
182 WMSetListAllowEmptySelection(WMList
*lPtr
, Bool flag
)
184 lPtr
->flags
.allowEmptySelection
= ((flag
==0) ? 0 : 1);
189 comparator(const void *a
, const void *b
)
191 return (strcmp((*(WMListItem
**)a
)->text
, (*(WMListItem
**)b
)->text
));
196 WMSortListItems(WMList
*lPtr
)
198 WMSortArray(lPtr
->items
, comparator
);
206 WMSortListItemsWithComparer(WMList
*lPtr
, WMCompareDataProc
*func
)
208 WMSortArray(lPtr
->items
, func
);
216 WMInsertListItem(WMList
*lPtr
, int row
, char *text
)
220 CHECK_CLASS(lPtr
, WC_List
);
222 item
= wmalloc(sizeof(WMListItem
));
223 memset(item
, 0, sizeof(WMListItem
));
224 item
->text
= wstrdup(text
);
226 row
= WMIN(row
, WMGetArrayItemCount(lPtr
->items
));
229 WMAddToArray(lPtr
->items
, item
);
231 WMInsertInArray(lPtr
->items
, row
, item
);
233 /* update the scroller when idle, so that we don't waste time
234 * updating it when another item is going to be added later */
236 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
244 WMRemoveListItem(WMList
*lPtr
, int row
)
247 int topItem
= lPtr
->topItem
;
250 CHECK_CLASS(lPtr
, WC_List
);
252 /*wassertr(row>=0 && row<WMGetArrayItemCount(lPtr->items));*/
253 if (row
<0 || row
>=WMGetArrayItemCount(lPtr
->items
))
256 item
= WMGetFromArray(lPtr
->items
, row
);
257 if (item
->selected
) {
258 WMRemoveFromArray(lPtr
->selectedItems
, item
);
262 if (row
<= lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
)
264 if (lPtr
->topItem
< 0)
267 WMDeleteFromArray(lPtr
->items
, row
);
270 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
272 if (lPtr
->topItem
!= topItem
) {
273 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
276 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
282 WMGetListItem(WMList
*lPtr
, int row
)
284 return WMGetFromArray(lPtr
->items
, row
);
289 WMGetListItems(WMList
*lPtr
)
296 WMSetListUserDrawProc(WMList
*lPtr
, WMListDrawProc
*proc
)
298 lPtr
->flags
.userDrawn
= 1;
304 WMSetListUserDrawItemHeight(WMList
*lPtr
, unsigned short height
)
306 W_Screen
*scr
= lPtr
->view
->screen
;
310 lPtr
->flags
.userItemHeight
= 1;
311 lPtr
->itemHeight
= height
;
313 updateDoubleBufferPixmap(lPtr
);
315 updateGeometry(lPtr
);
320 WMClearList(WMList
*lPtr
)
322 int selNo
= WMGetArrayItemCount(lPtr
->selectedItems
);
324 WMEmptyArray(lPtr
->selectedItems
);
325 WMEmptyArray(lPtr
->items
);
330 WMDeleteIdleHandler(lPtr
->idleID
);
333 if (lPtr
->selectID
) {
334 WMDeleteTimerHandler(lPtr
->selectID
);
335 lPtr
->selectID
= NULL
;
337 if (lPtr
->view
->flags
.realized
) {
338 updateScroller(lPtr
);
341 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
347 WMSetListAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
349 lPtr
->action
= action
;
350 lPtr
->clientData
= clientData
;
355 WMSetListDoubleAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
357 lPtr
->doubleAction
= action
;
358 lPtr
->doubleClientData
= clientData
;
363 WMGetListSelectedItems(WMList
*lPtr
)
365 return lPtr
->selectedItems
;
370 WMGetListSelectedItem(WMList
*lPtr
)
372 return WMGetFromArray(lPtr
->selectedItems
, 0);
377 WMGetListSelectedItemRow(WMList
*lPtr
)
379 WMListItem
*item
= WMGetFromArray(lPtr
->selectedItems
, 0);
381 return (item
!=NULL
? WMGetFirstInArray(lPtr
->items
, item
) : WLNotFound
);
386 WMGetListItemHeight(WMList
*lPtr
)
388 return lPtr
->itemHeight
;
393 WMSetListPosition(WMList
*lPtr
, int row
)
396 if (lPtr
->topItem
+ lPtr
->fullFitLines
> WMGetArrayItemCount(lPtr
->items
))
397 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
399 if (lPtr
->topItem
< 0)
402 if (lPtr
->view
->flags
.realized
)
403 updateScroller(lPtr
);
408 WMSetListBottomPosition(WMList
*lPtr
, int row
)
410 if (WMGetArrayItemCount(lPtr
->items
) > lPtr
->fullFitLines
) {
411 lPtr
->topItem
= row
- lPtr
->fullFitLines
;
412 if (lPtr
->topItem
< 0)
414 if (lPtr
->view
->flags
.realized
)
415 updateScroller(lPtr
);
421 WMGetListNumberOfRows(WMList
*lPtr
)
423 return WMGetArrayItemCount(lPtr
->items
);
428 WMGetListPosition(WMList
*lPtr
)
430 return lPtr
->topItem
;
435 WMListAllowsMultipleSelection(WMList
*lPtr
)
437 return lPtr
->flags
.allowMultipleSelection
;
442 WMListAllowsEmptySelection(WMList
*lPtr
)
444 return lPtr
->flags
.allowEmptySelection
;
449 scrollByAmount(WMList
*lPtr
, int amount
)
451 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
453 if ((amount
< 0 && lPtr
->topItem
> 0) ||
454 (amount
> 0 && (lPtr
->topItem
+ lPtr
->fullFitLines
< itemCount
))) {
456 lPtr
->topItem
+= amount
;
457 if (lPtr
->topItem
< 0)
459 if (lPtr
->topItem
+ lPtr
->fullFitLines
> itemCount
)
460 lPtr
->topItem
= itemCount
- lPtr
->fullFitLines
;
462 updateScroller(lPtr
);
468 vScrollCallBack(WMWidget
*scroller
, void *self
)
470 WMList
*lPtr
= (WMList
*)self
;
472 int oldTopItem
= lPtr
->topItem
;
473 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
475 height
= lPtr
->view
->size
.height
- 4;
477 switch (WMGetScrollerHitPart((WMScroller
*)scroller
)) {
478 case WSDecrementLine
:
479 scrollByAmount(lPtr
, -1);
482 case WSIncrementLine
:
483 scrollByAmount(lPtr
, 1);
486 case WSDecrementPage
:
487 scrollByAmount(lPtr
, -lPtr
->fullFitLines
+(1-lPtr
->flags
.dontFitAll
)+1);
490 case WSIncrementPage
:
491 scrollByAmount(lPtr
, lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1);
494 case WSDecrementWheel
:
495 scrollByAmount(lPtr
, -lPtr
->fullFitLines
/ 3);
498 case WSIncrementWheel
:
499 scrollByAmount(lPtr
, lPtr
->fullFitLines
/ 3);
503 lPtr
->topItem
= WMGetScrollerValue(lPtr
->vScroller
) *
504 (float)(itemCount
- lPtr
->fullFitLines
);
506 if (oldTopItem
!= lPtr
->topItem
)
507 paintList(lPtr
); /* use updateScroller(lPtr) here? -Dan */
517 if (lPtr
->topItem
!= oldTopItem
)
518 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
523 paintItem(List
*lPtr
, int index
)
525 WMView
*view
= lPtr
->view
;
526 W_Screen
*scr
= view
->screen
;
527 Display
*display
= scr
->display
;
528 int width
, height
, x
, y
, tlen
;
530 Drawable d
= lPtr
->doubleBuffer
;
532 itemPtr
= WMGetFromArray(lPtr
->items
, index
);
534 width
= lPtr
->view
->size
.width
- 2 - 19;
535 height
= lPtr
->itemHeight
;
537 y
= 2 + (index
-lPtr
->topItem
) * lPtr
->itemHeight
+ 1;
538 tlen
= strlen(itemPtr
->text
);
540 if (lPtr
->flags
.userDrawn
) {
545 rect
.size
.width
= width
;
546 rect
.size
.height
= height
;
550 flags
= itemPtr
->uflags
;
551 if (itemPtr
->disabled
)
552 flags
|= WLDSDisabled
;
553 if (itemPtr
->selected
)
554 flags
|= WLDSSelected
;
555 if (itemPtr
->isBranch
)
556 flags
|= WLDSIsBranch
;
559 (*lPtr
->draw
)(lPtr
, index
, d
, itemPtr
->text
, flags
, &rect
);
561 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, 0, 0, width
, height
, x
, y
);
563 WMColor
*back
= (itemPtr
->selected
? scr
->white
: view
->backColor
);
565 XFillRectangle(display
, d
, WMColorGC(back
), 0, 0, width
, height
);
567 W_PaintText(view
, d
, scr
->normalFont
, 4, 0, width
, WALeft
, scr
->black
,
568 False
, itemPtr
->text
, tlen
);
569 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, 0, 0, width
, height
, x
, y
);
572 if ((index
-lPtr
->topItem
+lPtr
->fullFitLines
)*lPtr
->itemHeight
>
573 lPtr
->view
->size
.height
-2) {
574 W_DrawRelief(lPtr
->view
->screen
, lPtr
->view
->window
, 0, 0,
575 lPtr
->view
->size
.width
, lPtr
->view
->size
.height
,
583 paintList(List
*lPtr
)
585 W_Screen
*scrPtr
= lPtr
->view
->screen
;
588 if (!lPtr
->view
->flags
.mapped
)
591 if (WMGetArrayItemCount(lPtr
->items
) > 0) {
592 if (lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
593 > WMGetArrayItemCount(lPtr
->items
)) {
595 lim
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
;
596 XClearArea(scrPtr
->display
, lPtr
->view
->window
, 19,
597 2+lim
*lPtr
->itemHeight
, lPtr
->view
->size
.width
-21,
598 lPtr
->view
->size
.height
-lim
*lPtr
->itemHeight
-3, False
);
600 lim
= lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
;
602 for (i
= lPtr
->topItem
; i
< lPtr
->topItem
+ lim
; i
++) {
606 XClearWindow(scrPtr
->display
, lPtr
->view
->window
);
608 W_DrawRelief(scrPtr
, lPtr
->view
->window
, 0, 0, lPtr
->view
->size
.width
,
609 lPtr
->view
->size
.height
, WRSunken
);
614 scrollTo(List
*lPtr
, int newTop
)
621 updateScroller(void *data
)
623 List
*lPtr
= (List
*)data
;
625 float knobProportion
, floatValue
, tmp
;
626 int count
= WMGetArrayItemCount(lPtr
->items
);
629 WMDeleteIdleHandler(lPtr
->idleID
);
634 if (count
== 0 || count
<= lPtr
->fullFitLines
)
635 WMSetScrollerParameters(lPtr
->vScroller
, 0, 1);
637 tmp
= lPtr
->fullFitLines
;
638 knobProportion
= tmp
/(float)count
;
640 floatValue
= (float)lPtr
->topItem
/(float)(count
- lPtr
->fullFitLines
);
642 WMSetScrollerParameters(lPtr
->vScroller
, floatValue
, knobProportion
);
648 scrollForwardSelecting(void *data
)
650 List
*lPtr
= (List
*)data
;
653 lastSelected
= lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
-1;
655 if (lastSelected
>= WMGetArrayItemCount(lPtr
->items
)-1) {
656 lPtr
->selectID
= NULL
;
657 if (lPtr
->flags
.dontFitAll
)
658 scrollByAmount(lPtr
, 1);
662 /* selecting NEEDS to be done before scrolling to avoid flickering */
663 if (lPtr
->flags
.allowMultipleSelection
) {
667 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
668 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
669 if (lastSelected
+1 >= range
.position
) {
670 range
.count
= lastSelected
- range
.position
+ 2;
672 range
.count
= lastSelected
- range
.position
;
674 WMSetListSelectionToRange(lPtr
, range
);
676 WMSelectListItem(lPtr
, lastSelected
+1);
678 scrollByAmount(lPtr
, 1);
680 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
,
686 scrollBackwardSelecting(void *data
)
688 List
*lPtr
= (List
*)data
;
690 if (lPtr
->topItem
< 1) {
691 lPtr
->selectID
= NULL
;
695 /* selecting NEEDS to be done before scrolling to avoid flickering */
696 if (lPtr
->flags
.allowMultipleSelection
) {
700 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
701 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
702 if (lPtr
->topItem
-1 >= range
.position
) {
703 range
.count
= lPtr
->topItem
- range
.position
;
705 range
.count
= lPtr
->topItem
- range
.position
- 2;
707 WMSetListSelectionToRange(lPtr
, range
);
709 WMSelectListItem(lPtr
, lPtr
->topItem
-1);
711 scrollByAmount(lPtr
, -1);
713 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
,
719 handleEvents(XEvent
*event
, void *data
)
721 List
*lPtr
= (List
*)data
;
723 CHECK_CLASS(data
, WC_List
);
726 switch (event
->type
) {
728 if (event
->xexpose
.count
!=0)
742 matchTitle(void *item
, void *title
)
744 return (strcmp(((WMListItem
*)item
)->text
, (char*)title
)==0 ? 1 : 0);
749 WMFindRowOfListItemWithTitle(WMList
*lPtr
, char *title
)
751 return WMFindInArray(lPtr
->items
, matchTitle
, title
);
756 WMSelectListItem(WMList
*lPtr
, int row
)
760 if (row
>= WMGetArrayItemCount(lPtr
->items
))
764 /* row = -1 will deselects all for backward compatibility.
765 * will be removed later. -Dan */
766 WMUnselectAllListItems(lPtr
);
770 item
= WMGetFromArray(lPtr
->items
, row
);
772 return; /* Return if already selected */
774 if (!lPtr
->flags
.allowMultipleSelection
) {
775 /* unselect previous selected items */
776 unselectAllListItems(lPtr
, NULL
);
781 WMAddToArray(lPtr
->selectedItems
, item
);
783 if (lPtr
->view
->flags
.mapped
&& row
>=lPtr
->topItem
784 && row
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
785 paintItem(lPtr
, row
);
788 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
793 WMUnselectListItem(WMList
*lPtr
, int row
)
795 WMListItem
*item
= WMGetFromArray(lPtr
->items
, row
);
797 if (!item
|| !item
->selected
)
800 if (!lPtr
->flags
.allowEmptySelection
&&
801 WMGetArrayItemCount(lPtr
->selectedItems
) <= 1) {
806 WMRemoveFromArray(lPtr
->selectedItems
, item
);
808 if (lPtr
->view
->flags
.mapped
&& row
>=lPtr
->topItem
809 && row
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
810 paintItem(lPtr
, row
);
813 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
818 WMSelectListItemsInRange(WMList
*lPtr
, WMRange range
)
821 int position
= range
.position
, k
= 1, notify
= 0;
822 int total
= WMGetArrayItemCount(lPtr
->items
);
824 if (!lPtr
->flags
.allowMultipleSelection
)
827 return; /* Nothing to select */
829 if (range
.count
< 0) {
830 range
.count
= -range
.count
;
834 for (; range
.count
>0 && position
>=0 && position
<total
; range
.count
--) {
835 item
= WMGetFromArray(lPtr
->items
, position
);
836 if (!item
->selected
) {
838 WMAddToArray(lPtr
->selectedItems
, item
);
839 if (lPtr
->view
->flags
.mapped
&& position
>=lPtr
->topItem
840 && position
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
841 paintItem(lPtr
, position
);
849 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
855 WMSetListSelectionToRange(WMList
*lPtr
, WMRange range
)
858 int mark1
, mark2
, i
, k
;
859 int position
= range
.position
, notify
= 0;
860 int total
= WMGetArrayItemCount(lPtr
->items
);
862 if (!lPtr
->flags
.allowMultipleSelection
)
865 if (range
.count
==0) {
866 WMUnselectAllListItems(lPtr
);
870 if (range
.count
< 0) {
871 mark1
= range
.position
+ range
.count
+ 1;
872 mark2
= range
.position
+ 1;
873 range
.count
= -range
.count
;
876 mark1
= range
.position
;
877 mark2
= range
.position
+ range
.count
;
885 WMEmptyArray(lPtr
->selectedItems
);
887 for (i
=0; i
<mark1
; i
++) {
888 item
= WMGetFromArray(lPtr
->items
, i
);
889 if (item
->selected
) {
891 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
892 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
898 for (; range
.count
>0 && position
>=0 && position
<total
; range
.count
--) {
899 item
= WMGetFromArray(lPtr
->items
, position
);
900 if (!item
->selected
) {
902 if (lPtr
->view
->flags
.mapped
&& position
>=lPtr
->topItem
903 && position
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
904 paintItem(lPtr
, position
);
908 WMAddToArray(lPtr
->selectedItems
, item
);
911 for (i
=mark2
; i
<total
; i
++) {
912 item
= WMGetFromArray(lPtr
->items
, i
);
913 if (item
->selected
) {
915 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
916 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
924 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
930 WMSelectAllListItems(WMList
*lPtr
)
935 if (!lPtr
->flags
.allowMultipleSelection
)
938 if (WMGetArrayItemCount(lPtr
->items
) ==
939 WMGetArrayItemCount(lPtr
->selectedItems
)) {
940 return; /* All items are selected already */
943 WMFreeArray(lPtr
->selectedItems
);
944 lPtr
->selectedItems
= WMCreateArrayWithArray(lPtr
->items
);
946 for (i
=0; i
<WMGetArrayItemCount(lPtr
->items
); i
++) {
947 item
= WMGetFromArray(lPtr
->items
, i
);
948 if (!item
->selected
) {
950 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
951 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
957 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
961 * Be careful from where you call this function! It doesn't honor the
962 * allowEmptySelection flag and doesn't send a notification about selection
963 * change! You need to manage these in the functions from where you call it.
965 * This will unselect all items if exceptThis is NULL, else will keep
966 * exceptThis selected.
967 * Make sure that exceptThis is one of the already selected items if not NULL!
971 unselectAllListItems(WMList
*lPtr
, WMListItem
*exceptThis
)
976 for (i
=0; i
<WMGetArrayItemCount(lPtr
->items
); i
++) {
977 item
= WMGetFromArray(lPtr
->items
, i
);
978 if (item
!=exceptThis
&& item
->selected
) {
980 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
981 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
987 WMEmptyArray(lPtr
->selectedItems
);
988 if (exceptThis
!=NULL
) {
989 exceptThis
->selected
= 1;
990 WMAddToArray(lPtr
->selectedItems
, exceptThis
);
996 WMUnselectAllListItems(WMList
*lPtr
)
999 WMListItem
*keepItem
;
1001 keep
= lPtr
->flags
.allowEmptySelection
? 0 : 1;
1003 if (WMGetArrayItemCount(lPtr
->selectedItems
) == keep
)
1006 keepItem
= (keep
==1 ? WMGetFromArray(lPtr
->selectedItems
, 0) : NULL
);
1008 unselectAllListItems(lPtr
, keepItem
);
1010 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
1015 getItemIndexAt(List
*lPtr
, int clickY
)
1019 index
= (clickY
- 2) / lPtr
->itemHeight
+ lPtr
->topItem
;
1021 if (index
< 0 || index
>= WMGetArrayItemCount(lPtr
->items
))
1029 toggleItemSelection(WMList
*lPtr
, int index
)
1031 WMListItem
*item
= WMGetFromArray(lPtr
->items
, index
);
1033 if (item
&& item
->selected
) {
1034 WMUnselectListItem(lPtr
, index
);
1036 WMSelectListItem(lPtr
, index
);
1042 handleActionEvents(XEvent
*event
, void *data
)
1044 List
*lPtr
= (List
*)data
;
1046 int topItem
= lPtr
->topItem
;
1047 static int lastClicked
= -1, prevItem
= -1;
1049 CHECK_CLASS(data
, WC_List
);
1051 switch (event
->type
) {
1053 /* Ignore mouse wheel events, they're not "real" button events */
1054 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
||
1055 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
) {
1059 lPtr
->flags
.buttonPressed
= 0;
1060 if (lPtr
->selectID
) {
1061 WMDeleteTimerHandler(lPtr
->selectID
);
1062 lPtr
->selectID
= NULL
;
1064 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
1068 (*lPtr
->action
)(lPtr
, lPtr
->clientData
);
1071 if (!(event
->xbutton
.state
& ShiftMask
))
1072 lastClicked
= prevItem
= tmp
;
1077 if (lPtr
->selectID
) {
1078 WMDeleteTimerHandler(lPtr
->selectID
);
1079 lPtr
->selectID
= NULL
;
1084 height
= WMWidgetHeight(lPtr
);
1085 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
1086 if (event
->xcrossing
.y
>= height
) {
1087 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1088 scrollForwardSelecting
,
1090 } else if (event
->xcrossing
.y
<= 0) {
1091 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1092 scrollBackwardSelecting
,
1099 if (event
->xbutton
.x
<= WMWidgetWidth(lPtr
->vScroller
))
1101 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
||
1102 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
) {
1105 if (event
->xbutton
.state
& ControlMask
) {
1106 amount
= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
1107 } else if (event
->xbutton
.state
& ShiftMask
) {
1110 amount
= lPtr
->fullFitLines
/ 3;
1114 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
)
1117 scrollByAmount(lPtr
, amount
);
1121 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
1122 lPtr
->flags
.buttonPressed
= 1;
1125 if (tmp
== lastClicked
&& WMIsDoubleClick(event
)) {
1126 WMSelectListItem(lPtr
, tmp
);
1127 if (lPtr
->doubleAction
)
1128 (*lPtr
->doubleAction
)(lPtr
, lPtr
->doubleClientData
);
1130 if (!lPtr
->flags
.allowMultipleSelection
) {
1131 if (event
->xbutton
.state
& ControlMask
) {
1132 toggleItemSelection(lPtr
, tmp
);
1134 WMSelectListItem(lPtr
, tmp
);
1138 WMListItem
*lastSel
;
1140 if (event
->xbutton
.state
& ControlMask
) {
1141 toggleItemSelection(lPtr
, tmp
);
1142 } else if (event
->xbutton
.state
& ShiftMask
) {
1143 if (WMGetArrayItemCount(lPtr
->selectedItems
) == 0) {
1144 WMSelectListItem(lPtr
, tmp
);
1146 lastSel
= WMGetFromArray(lPtr
->items
, lastClicked
);
1147 range
.position
= WMGetFirstInArray(lPtr
->items
,
1149 if (tmp
>= range
.position
)
1150 range
.count
= tmp
- range
.position
+ 1;
1152 range
.count
= tmp
- range
.position
- 1;
1154 WMSetListSelectionToRange(lPtr
, range
);
1157 range
.position
= tmp
;
1159 WMSetListSelectionToRange(lPtr
, range
);
1165 if (!(event
->xbutton
.state
& ShiftMask
))
1166 lastClicked
= prevItem
= tmp
;
1171 height
= WMWidgetHeight(lPtr
);
1172 if (lPtr
->selectID
&& event
->xmotion
.y
>0 && event
->xmotion
.y
<height
) {
1173 WMDeleteTimerHandler(lPtr
->selectID
);
1174 lPtr
->selectID
= NULL
;
1176 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
1177 if (event
->xmotion
.y
<= 0) {
1178 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1179 scrollBackwardSelecting
,
1182 } else if (event
->xmotion
.y
>= height
) {
1183 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1184 scrollForwardSelecting
,
1189 tmp
= getItemIndexAt(lPtr
, event
->xmotion
.y
);
1190 if (tmp
>=0 && tmp
!=prevItem
) {
1191 if (lPtr
->flags
.allowMultipleSelection
) {
1194 range
.position
= lastClicked
;
1195 if (tmp
>= lastClicked
)
1196 range
.count
= tmp
- lastClicked
+ 1;
1198 range
.count
= tmp
- lastClicked
- 1;
1199 WMSetListSelectionToRange(lPtr
, range
);
1201 WMSelectListItem(lPtr
, tmp
);
1208 if (lPtr
->topItem
!= topItem
)
1209 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
1214 updateGeometry(WMList
*lPtr
)
1216 lPtr
->fullFitLines
= (lPtr
->view
->size
.height
- 4) / lPtr
->itemHeight
;
1217 if (lPtr
->fullFitLines
* lPtr
->itemHeight
< lPtr
->view
->size
.height
- 4) {
1218 lPtr
->flags
.dontFitAll
= 1;
1220 lPtr
->flags
.dontFitAll
= 0;
1223 if (WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
<= lPtr
->fullFitLines
) {
1224 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
1225 if (lPtr
->topItem
< 0)
1229 updateScroller(lPtr
);
1234 didResizeList(W_ViewDelegate
*self
, WMView
*view
)
1236 WMList
*lPtr
= (WMList
*)view
->self
;
1237 W_Screen
*scr
= view
->screen
;
1239 WMResizeWidget(lPtr
->vScroller
, 1, view
->size
.height
-2);
1241 updateDoubleBufferPixmap(lPtr
);
1243 updateGeometry(lPtr
);
1248 destroyList(List
*lPtr
)
1251 WMDeleteIdleHandler(lPtr
->idleID
);
1252 lPtr
->idleID
= NULL
;
1255 WMDeleteTimerHandler(lPtr
->selectID
);
1256 lPtr
->selectID
= NULL
;
1258 if (lPtr
->selectedItems
)
1259 WMFreeArray(lPtr
->selectedItems
);
1262 WMFreeArray(lPtr
->items
);
1264 if (lPtr
->doubleBuffer
)
1265 XFreePixmap(lPtr
->view
->screen
->display
, lPtr
->doubleBuffer
);
1267 WMRemoveNotificationObserver(lPtr
);