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
;
37 unsigned int allowMultipleSelection
:1;
38 unsigned int allowEmptySelection
:1;
39 unsigned int userDrawn
:1;
40 unsigned int userItemHeight
:1;
41 unsigned int dontFitAll
:1; /* 1 = last item won't be fully visible */
42 unsigned int redrawPending
:1;
43 unsigned int buttonPressed
:1;
44 unsigned int buttonWasPressed
:1;
50 #define DEFAULT_WIDTH 150
51 #define DEFAULT_HEIGHT 150
53 #define SCROLL_DELAY 100
56 static void destroyList(List
*lPtr
);
57 static void paintList(List
*lPtr
);
60 static void handleEvents(XEvent
*event
, void *data
);
61 static void handleActionEvents(XEvent
*event
, void *data
);
63 static void updateScroller(void *data
);
64 static void scrollForwardSelecting(void *data
);
65 static void scrollBackwardSelecting(void *data
);
67 static void vScrollCallBack(WMWidget
*scroller
, void *self
);
69 static void toggleItemSelection(WMList
*lPtr
, int index
);
71 static void updateGeometry(WMList
*lPtr
);
72 static void didResizeList();
74 static void unselectAllListItems(WMList
*lPtr
, WMListItem
*exceptThis
);
77 W_ViewDelegate _ListViewDelegate
= {
87 releaseItem(void *data
)
89 WMListItem
*item
= (WMListItem
*)data
;
98 WMCreateList(WMWidget
*parent
)
101 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
103 lPtr
= wmalloc(sizeof(List
));
104 memset(lPtr
, 0, sizeof(List
));
106 lPtr
->widgetClass
= WC_List
;
108 lPtr
->view
= W_CreateView(W_VIEW(parent
));
113 lPtr
->view
->self
= lPtr
;
115 lPtr
->view
->delegate
= &_ListViewDelegate
;
117 WMCreateEventHandler(lPtr
->view
, ExposureMask
|StructureNotifyMask
118 |ClientMessageMask
, handleEvents
, lPtr
);
120 WMCreateEventHandler(lPtr
->view
, ButtonPressMask
|ButtonReleaseMask
121 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
,
122 handleActionEvents
, lPtr
);
124 lPtr
->itemHeight
= WMFontHeight(scrPtr
->normalFont
) + 1;
126 lPtr
->items
= WMCreateArrayWithDestructor(4, releaseItem
);
127 lPtr
->selectedItems
= WMCreateArray(4);
129 /* create the vertical scroller */
130 lPtr
->vScroller
= WMCreateScroller(lPtr
);
131 WMMoveWidget(lPtr
->vScroller
, 1, 1);
132 WMSetScrollerArrowsPosition(lPtr
->vScroller
, WSAMaxEnd
);
134 WMSetScrollerAction(lPtr
->vScroller
, vScrollCallBack
, lPtr
);
136 /* make the scroller map itself when it's realized */
137 WMMapWidget(lPtr
->vScroller
);
139 W_ResizeView(lPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
146 WMSetListAllowMultipleSelection(WMList
*lPtr
, Bool flag
)
148 lPtr
->flags
.allowMultipleSelection
= ((flag
==0) ? 0 : 1);
153 WMSetListAllowEmptySelection(WMList
*lPtr
, Bool flag
)
155 lPtr
->flags
.allowEmptySelection
= ((flag
==0) ? 0 : 1);
160 comparator(const void *a
, const void *b
)
162 return (strcmp((*(WMListItem
**)a
)->text
, (*(WMListItem
**)b
)->text
));
167 WMSortListItems(WMList
*lPtr
)
169 WMSortArray(lPtr
->items
, comparator
);
177 WMSortListItemsWithComparer(WMList
*lPtr
, WMCompareDataProc
*func
)
179 WMSortArray(lPtr
->items
, func
);
187 WMInsertListItem(WMList
*lPtr
, int row
, char *text
)
191 CHECK_CLASS(lPtr
, WC_List
);
193 item
= wmalloc(sizeof(WMListItem
));
194 memset(item
, 0, sizeof(WMListItem
));
195 item
->text
= wstrdup(text
);
197 row
= WMIN(row
, WMGetArrayItemCount(lPtr
->items
));
200 WMAddToArray(lPtr
->items
, item
);
202 WMInsertInArray(lPtr
->items
, row
, item
);
204 /* update the scroller when idle, so that we don't waste time
205 * updating it when another item is going to be added later */
207 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
215 WMRemoveListItem(WMList
*lPtr
, int row
)
218 int topItem
= lPtr
->topItem
;
221 CHECK_CLASS(lPtr
, WC_List
);
223 /*wassertr(row>=0 && row<WMGetArrayItemCount(lPtr->items));*/
224 if (row
<0 || row
>=WMGetArrayItemCount(lPtr
->items
))
227 item
= WMGetFromArray(lPtr
->items
, row
);
228 if (item
->selected
) {
229 WMRemoveFromArray(lPtr
->selectedItems
, item
);
233 if (row
<= lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
)
235 if (lPtr
->topItem
< 0)
238 WMDeleteFromArray(lPtr
->items
, row
);
241 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
243 if (lPtr
->topItem
!= topItem
) {
244 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
247 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
253 WMGetListItem(WMList
*lPtr
, int row
)
255 return WMGetFromArray(lPtr
->items
, row
);
260 WMGetListItems(WMList
*lPtr
)
267 WMSetListUserDrawProc(WMList
*lPtr
, WMListDrawProc
*proc
)
269 lPtr
->flags
.userDrawn
= 1;
275 WMSetListUserDrawItemHeight(WMList
*lPtr
, unsigned short height
)
279 lPtr
->flags
.userItemHeight
= 1;
280 lPtr
->itemHeight
= height
;
282 updateGeometry(lPtr
);
287 WMClearList(WMList
*lPtr
)
289 int selNo
= WMGetArrayItemCount(lPtr
->selectedItems
);
291 WMEmptyArray(lPtr
->selectedItems
);
292 WMEmptyArray(lPtr
->items
);
297 WMDeleteIdleHandler(lPtr
->idleID
);
300 if (lPtr
->selectID
) {
301 WMDeleteTimerHandler(lPtr
->selectID
);
302 lPtr
->selectID
= NULL
;
304 if (lPtr
->view
->flags
.realized
) {
305 updateScroller(lPtr
);
308 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
314 WMSetListAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
316 lPtr
->action
= action
;
317 lPtr
->clientData
= clientData
;
322 WMSetListDoubleAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
324 lPtr
->doubleAction
= action
;
325 lPtr
->doubleClientData
= clientData
;
330 WMGetListSelectedItems(WMList
*lPtr
)
332 return lPtr
->selectedItems
;
337 WMGetListSelectedItem(WMList
*lPtr
)
339 return WMGetFromArray(lPtr
->selectedItems
, 0);
344 WMGetListSelectedItemRow(WMList
*lPtr
)
346 WMListItem
*item
= WMGetFromArray(lPtr
->selectedItems
, 0);
348 return (item
!=NULL
? WMGetFirstInArray(lPtr
->items
, item
) : WLNotFound
);
353 WMGetListItemHeight(WMList
*lPtr
)
355 return lPtr
->itemHeight
;
360 WMSetListPosition(WMList
*lPtr
, int row
)
363 if (lPtr
->topItem
+ lPtr
->fullFitLines
> WMGetArrayItemCount(lPtr
->items
))
364 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
366 if (lPtr
->topItem
< 0)
369 if (lPtr
->view
->flags
.realized
)
370 updateScroller(lPtr
);
375 WMSetListBottomPosition(WMList
*lPtr
, int row
)
377 if (WMGetArrayItemCount(lPtr
->items
) > lPtr
->fullFitLines
) {
378 lPtr
->topItem
= row
- lPtr
->fullFitLines
;
379 if (lPtr
->topItem
< 0)
381 if (lPtr
->view
->flags
.realized
)
382 updateScroller(lPtr
);
388 WMGetListNumberOfRows(WMList
*lPtr
)
390 return WMGetArrayItemCount(lPtr
->items
);
395 WMGetListPosition(WMList
*lPtr
)
397 return lPtr
->topItem
;
402 WMListAllowsMultipleSelection(WMList
*lPtr
)
404 return lPtr
->flags
.allowMultipleSelection
;
409 WMListAllowsEmptySelection(WMList
*lPtr
)
411 return lPtr
->flags
.allowEmptySelection
;
416 scrollByAmount(WMList
*lPtr
, int amount
)
418 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
420 if ((amount
< 0 && lPtr
->topItem
> 0) ||
421 (amount
> 0 && (lPtr
->topItem
+ lPtr
->fullFitLines
< itemCount
))) {
423 lPtr
->topItem
+= amount
;
424 if (lPtr
->topItem
< 0)
426 if (lPtr
->topItem
+ lPtr
->fullFitLines
> itemCount
)
427 lPtr
->topItem
= itemCount
- lPtr
->fullFitLines
;
429 updateScroller(lPtr
);
435 vScrollCallBack(WMWidget
*scroller
, void *self
)
437 WMList
*lPtr
= (WMList
*)self
;
439 int oldTopItem
= lPtr
->topItem
;
440 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
442 height
= lPtr
->view
->size
.height
- 4;
444 switch (WMGetScrollerHitPart((WMScroller
*)scroller
)) {
445 case WSDecrementLine
:
446 scrollByAmount(lPtr
, -1);
449 case WSIncrementLine
:
450 scrollByAmount(lPtr
, 1);
453 case WSDecrementPage
:
454 scrollByAmount(lPtr
, -lPtr
->fullFitLines
+(1-lPtr
->flags
.dontFitAll
)+1);
457 case WSIncrementPage
:
458 scrollByAmount(lPtr
, lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1);
461 case WSDecrementWheel
:
462 scrollByAmount(lPtr
, -lPtr
->fullFitLines
/ 3);
465 case WSIncrementWheel
:
466 scrollByAmount(lPtr
, lPtr
->fullFitLines
/ 3);
470 lPtr
->topItem
= WMGetScrollerValue(lPtr
->vScroller
) *
471 (float)(itemCount
- lPtr
->fullFitLines
);
473 if (oldTopItem
!= lPtr
->topItem
)
474 paintList(lPtr
); /* use updateScroller(lPtr) here? -Dan */
484 if (lPtr
->topItem
!= oldTopItem
)
485 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
490 paintItem(List
*lPtr
, int index
)
492 WMView
*view
= lPtr
->view
;
493 W_Screen
*scr
= view
->screen
;
494 Display
*display
= scr
->display
;
495 int width
, height
, x
, y
, tlen
;
498 itemPtr
= WMGetFromArray(lPtr
->items
, index
);
500 width
= lPtr
->view
->size
.width
- 2 - 19;
501 height
= lPtr
->itemHeight
;
503 y
= 2 + (index
-lPtr
->topItem
) * lPtr
->itemHeight
+ 1;
504 tlen
= strlen(itemPtr
->text
);
506 if (lPtr
->flags
.userDrawn
) {
508 Drawable d
= view
->window
;
512 rect
.size
.width
= width
;
513 rect
.size
.height
= height
;
517 flags
= itemPtr
->uflags
;
518 if (itemPtr
->disabled
)
519 flags
|= WLDSDisabled
;
520 if (itemPtr
->selected
)
521 flags
|= WLDSSelected
;
522 if (itemPtr
->isBranch
)
523 flags
|= WLDSIsBranch
;
525 #ifdef DOUBLE_BUFFER_no
526 d
= XCreatePixmap(display
, view
->window
, view
->size
.width
,
527 view
->size
.height
, scr
->depth
);
531 (*lPtr
->draw
)(lPtr
, index
, d
, itemPtr
->text
, flags
, &rect
);
533 #ifdef DOUBLE_BUFFER_no
534 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, x
, y
, width
, height
, x
, y
);
535 XFreePixmap(display
, d
);
539 WMColor
*back
= (itemPtr
->selected
? scr
->white
: view
->backColor
);
542 d
= XCreatePixmap(display
, view
->window
, width
, height
, scr
->depth
);
543 XFillRectangle(display
, d
, WMColorGC(back
), 0, 0, width
, height
);
545 W_PaintText(view
, d
, scr
->normalFont
, 4, 0, width
, WALeft
, scr
->black
,
546 False
, itemPtr
->text
, tlen
);
547 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, 0, 0, width
, height
, x
, y
);
548 XFreePixmap(display
, d
);
550 if (itemPtr
->selected
) {
551 XFillRectangle(display
, view
->window
, WMColorGC(scr
->white
),
552 x
, y
, width
, height
);
554 XClearArea(display
, view
->window
, x
, y
, width
, height
, False
);
557 W_PaintText(view
, view
->window
, scr
->normalFont
, x
+4, y
, width
,
558 WALeft
, scr
->black
, False
, itemPtr
->text
, tlen
);
562 if ((index
-lPtr
->topItem
+lPtr
->fullFitLines
)*lPtr
->itemHeight
>
563 lPtr
->view
->size
.height
-2) {
564 W_DrawRelief(lPtr
->view
->screen
, lPtr
->view
->window
, 0, 0,
565 lPtr
->view
->size
.width
, lPtr
->view
->size
.height
,
573 paintList(List
*lPtr
)
575 W_Screen
*scrPtr
= lPtr
->view
->screen
;
578 if (!lPtr
->view
->flags
.mapped
)
581 if (WMGetArrayItemCount(lPtr
->items
) > 0) {
582 if (lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
583 > WMGetArrayItemCount(lPtr
->items
)) {
585 lim
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
;
586 XClearArea(scrPtr
->display
, lPtr
->view
->window
, 19,
587 2+lim
*lPtr
->itemHeight
, lPtr
->view
->size
.width
-21,
588 lPtr
->view
->size
.height
-lim
*lPtr
->itemHeight
-3, False
);
590 lim
= lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
;
592 for (i
= lPtr
->topItem
; i
< lPtr
->topItem
+ lim
; i
++) {
596 XClearWindow(scrPtr
->display
, lPtr
->view
->window
);
598 W_DrawRelief(scrPtr
, lPtr
->view
->window
, 0, 0, lPtr
->view
->size
.width
,
599 lPtr
->view
->size
.height
, WRSunken
);
604 scrollTo(List
*lPtr
, int newTop
)
611 updateScroller(void *data
)
613 List
*lPtr
= (List
*)data
;
615 float knobProportion
, floatValue
, tmp
;
616 int count
= WMGetArrayItemCount(lPtr
->items
);
619 WMDeleteIdleHandler(lPtr
->idleID
);
624 if (count
== 0 || count
<= lPtr
->fullFitLines
)
625 WMSetScrollerParameters(lPtr
->vScroller
, 0, 1);
627 tmp
= lPtr
->fullFitLines
;
628 knobProportion
= tmp
/(float)count
;
630 floatValue
= (float)lPtr
->topItem
/(float)(count
- lPtr
->fullFitLines
);
632 WMSetScrollerParameters(lPtr
->vScroller
, floatValue
, knobProportion
);
638 scrollForwardSelecting(void *data
)
640 List
*lPtr
= (List
*)data
;
643 lastSelected
= lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
-1;
645 if (lastSelected
>= WMGetArrayItemCount(lPtr
->items
)-1) {
646 lPtr
->selectID
= NULL
;
647 if (lPtr
->flags
.dontFitAll
)
648 scrollByAmount(lPtr
, 1);
652 /* selecting NEEDS to be done before scrolling to avoid flickering */
653 if (lPtr
->flags
.allowMultipleSelection
) {
657 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
658 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
659 if (lastSelected
+1 >= range
.position
) {
660 range
.count
= lastSelected
- range
.position
+ 2;
662 range
.count
= lastSelected
- range
.position
;
664 WMSetListSelectionToRange(lPtr
, range
);
666 WMSelectListItem(lPtr
, lastSelected
+1);
668 scrollByAmount(lPtr
, 1);
670 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
,
676 scrollBackwardSelecting(void *data
)
678 List
*lPtr
= (List
*)data
;
680 if (lPtr
->topItem
< 1) {
681 lPtr
->selectID
= NULL
;
685 /* selecting NEEDS to be done before scrolling to avoid flickering */
686 if (lPtr
->flags
.allowMultipleSelection
) {
690 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
691 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
692 if (lPtr
->topItem
-1 >= range
.position
) {
693 range
.count
= lPtr
->topItem
- range
.position
;
695 range
.count
= lPtr
->topItem
- range
.position
- 2;
697 WMSetListSelectionToRange(lPtr
, range
);
699 WMSelectListItem(lPtr
, lPtr
->topItem
-1);
701 scrollByAmount(lPtr
, -1);
703 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
,
709 handleEvents(XEvent
*event
, void *data
)
711 List
*lPtr
= (List
*)data
;
713 CHECK_CLASS(data
, WC_List
);
716 switch (event
->type
) {
718 if (event
->xexpose
.count
!=0)
732 matchTitle(void *item
, void *title
)
734 return (strcmp(((WMListItem
*)item
)->text
, (char*)title
)==0 ? 1 : 0);
739 WMFindRowOfListItemWithTitle(WMList
*lPtr
, char *title
)
741 return WMFindInArray(lPtr
->items
, matchTitle
, title
);
746 WMSelectListItem(WMList
*lPtr
, int row
)
750 if (row
>= WMGetArrayItemCount(lPtr
->items
))
754 /* row = -1 will deselects all for backward compatibility.
755 * will be removed later. -Dan */
756 WMUnselectAllListItems(lPtr
);
760 item
= WMGetFromArray(lPtr
->items
, row
);
762 return; /* Return if already selected */
764 if (!lPtr
->flags
.allowMultipleSelection
) {
765 /* unselect previous selected items */
766 unselectAllListItems(lPtr
, NULL
);
771 WMAddToArray(lPtr
->selectedItems
, item
);
773 if (lPtr
->view
->flags
.mapped
&& row
>=lPtr
->topItem
774 && row
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
775 paintItem(lPtr
, row
);
778 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
783 WMUnselectListItem(WMList
*lPtr
, int row
)
785 WMListItem
*item
= WMGetFromArray(lPtr
->items
, row
);
787 if (!item
|| !item
->selected
)
790 if (!lPtr
->flags
.allowEmptySelection
&&
791 WMGetArrayItemCount(lPtr
->selectedItems
) <= 1) {
796 WMRemoveFromArray(lPtr
->selectedItems
, item
);
798 if (lPtr
->view
->flags
.mapped
&& row
>=lPtr
->topItem
799 && row
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
800 paintItem(lPtr
, row
);
803 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
808 WMSelectListItemsInRange(WMList
*lPtr
, WMRange range
)
811 int position
= range
.position
, k
= 1, notify
= 0;
812 int total
= WMGetArrayItemCount(lPtr
->items
);
814 if (!lPtr
->flags
.allowMultipleSelection
)
817 return; /* Nothing to select */
819 if (range
.count
< 0) {
820 range
.count
= -range
.count
;
824 for (; range
.count
>0 && position
>=0 && position
<total
; range
.count
--) {
825 item
= WMGetFromArray(lPtr
->items
, position
);
826 if (!item
->selected
) {
828 WMAddToArray(lPtr
->selectedItems
, item
);
829 if (lPtr
->view
->flags
.mapped
&& position
>=lPtr
->topItem
830 && position
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
831 paintItem(lPtr
, position
);
839 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
845 WMSetListSelectionToRange(WMList
*lPtr
, WMRange range
)
848 int mark1
, mark2
, i
, k
;
849 int position
= range
.position
, notify
= 0;
850 int total
= WMGetArrayItemCount(lPtr
->items
);
852 if (!lPtr
->flags
.allowMultipleSelection
)
855 if (range
.count
==0) {
856 WMUnselectAllListItems(lPtr
);
860 if (range
.count
< 0) {
861 mark1
= range
.position
+ range
.count
+ 1;
862 mark2
= range
.position
+ 1;
863 range
.count
= -range
.count
;
866 mark1
= range
.position
;
867 mark2
= range
.position
+ range
.count
;
875 WMEmptyArray(lPtr
->selectedItems
);
877 for (i
=0; i
<mark1
; i
++) {
878 item
= WMGetFromArray(lPtr
->items
, i
);
879 if (item
->selected
) {
881 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
882 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
888 for (; range
.count
>0 && position
>=0 && position
<total
; range
.count
--) {
889 item
= WMGetFromArray(lPtr
->items
, position
);
890 if (!item
->selected
) {
892 if (lPtr
->view
->flags
.mapped
&& position
>=lPtr
->topItem
893 && position
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
894 paintItem(lPtr
, position
);
898 WMAddToArray(lPtr
->selectedItems
, item
);
901 for (i
=mark2
; i
<total
; i
++) {
902 item
= WMGetFromArray(lPtr
->items
, i
);
903 if (item
->selected
) {
905 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
906 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
914 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
920 WMSelectAllListItems(WMList
*lPtr
)
925 if (!lPtr
->flags
.allowMultipleSelection
)
928 if (WMGetArrayItemCount(lPtr
->items
) ==
929 WMGetArrayItemCount(lPtr
->selectedItems
)) {
930 return; /* All items are selected already */
933 WMFreeArray(lPtr
->selectedItems
);
934 lPtr
->selectedItems
= WMCreateArrayWithArray(lPtr
->items
);
936 for (i
=0; i
<WMGetArrayItemCount(lPtr
->items
); i
++) {
937 item
= WMGetFromArray(lPtr
->items
, i
);
938 if (!item
->selected
) {
940 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
941 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
947 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
951 * Be careful from where you call this function! It doesn't honor the
952 * allowEmptySelection flag and doesn't send a notification about selection
953 * change! You need to manage these in the functions from where you call it.
955 * This will unselect all items if exceptThis is NULL, else will keep
956 * exceptThis selected.
957 * Make sure that exceptThis is one of the already selected items if not NULL!
961 unselectAllListItems(WMList
*lPtr
, WMListItem
*exceptThis
)
966 for (i
=0; i
<WMGetArrayItemCount(lPtr
->items
); i
++) {
967 item
= WMGetFromArray(lPtr
->items
, i
);
968 if (item
!=exceptThis
&& item
->selected
) {
970 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
971 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
) {
977 WMEmptyArray(lPtr
->selectedItems
);
978 if (exceptThis
!=NULL
) {
979 exceptThis
->selected
= 1;
980 WMAddToArray(lPtr
->selectedItems
, exceptThis
);
986 WMUnselectAllListItems(WMList
*lPtr
)
989 WMListItem
*keepItem
;
991 keep
= lPtr
->flags
.allowEmptySelection
? 0 : 1;
993 if (WMGetArrayItemCount(lPtr
->selectedItems
) == keep
)
996 keepItem
= (keep
==1 ? WMGetFromArray(lPtr
->selectedItems
, 0) : NULL
);
998 unselectAllListItems(lPtr
, keepItem
);
1000 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
1005 getItemIndexAt(List
*lPtr
, int clickY
)
1009 index
= (clickY
- 2) / lPtr
->itemHeight
+ lPtr
->topItem
;
1011 if (index
< 0 || index
>= WMGetArrayItemCount(lPtr
->items
))
1019 toggleItemSelection(WMList
*lPtr
, int index
)
1021 WMListItem
*item
= WMGetFromArray(lPtr
->items
, index
);
1023 if (item
&& item
->selected
) {
1024 WMUnselectListItem(lPtr
, index
);
1026 WMSelectListItem(lPtr
, index
);
1032 handleActionEvents(XEvent
*event
, void *data
)
1034 List
*lPtr
= (List
*)data
;
1036 int topItem
= lPtr
->topItem
;
1037 static int lastClicked
= -1, prevItem
= -1;
1039 CHECK_CLASS(data
, WC_List
);
1041 switch (event
->type
) {
1043 /* Ignore mouse wheel events, they're not "real" button events */
1044 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
||
1045 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
) {
1049 lPtr
->flags
.buttonPressed
= 0;
1050 if (lPtr
->selectID
) {
1051 WMDeleteTimerHandler(lPtr
->selectID
);
1052 lPtr
->selectID
= NULL
;
1054 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
1058 (*lPtr
->action
)(lPtr
, lPtr
->clientData
);
1061 if (!(event
->xbutton
.state
& ShiftMask
))
1062 lastClicked
= prevItem
= tmp
;
1067 if (lPtr
->selectID
) {
1068 WMDeleteTimerHandler(lPtr
->selectID
);
1069 lPtr
->selectID
= NULL
;
1074 height
= WMWidgetHeight(lPtr
);
1075 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
1076 if (event
->xcrossing
.y
>= height
) {
1077 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1078 scrollForwardSelecting
,
1080 } else if (event
->xcrossing
.y
<= 0) {
1081 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1082 scrollBackwardSelecting
,
1089 if (event
->xbutton
.x
<= WMWidgetWidth(lPtr
->vScroller
))
1091 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
||
1092 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
) {
1095 if (event
->xbutton
.state
& ControlMask
) {
1096 amount
= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
1097 } else if (event
->xbutton
.state
& ShiftMask
) {
1100 amount
= lPtr
->fullFitLines
/ 3;
1104 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
)
1107 scrollByAmount(lPtr
, amount
);
1111 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
1112 lPtr
->flags
.buttonPressed
= 1;
1115 if (tmp
== lastClicked
&& WMIsDoubleClick(event
)) {
1116 WMSelectListItem(lPtr
, tmp
);
1117 if (lPtr
->doubleAction
)
1118 (*lPtr
->doubleAction
)(lPtr
, lPtr
->doubleClientData
);
1120 if (!lPtr
->flags
.allowMultipleSelection
) {
1121 if (event
->xbutton
.state
& ControlMask
) {
1122 toggleItemSelection(lPtr
, tmp
);
1124 WMSelectListItem(lPtr
, tmp
);
1128 WMListItem
*lastSel
;
1130 if (event
->xbutton
.state
& ControlMask
) {
1131 toggleItemSelection(lPtr
, tmp
);
1132 } else if (event
->xbutton
.state
& ShiftMask
) {
1133 if (WMGetArrayItemCount(lPtr
->selectedItems
) == 0) {
1134 WMSelectListItem(lPtr
, tmp
);
1136 lastSel
= WMGetFromArray(lPtr
->items
, lastClicked
);
1137 range
.position
= WMGetFirstInArray(lPtr
->items
,
1139 if (tmp
>= range
.position
)
1140 range
.count
= tmp
- range
.position
+ 1;
1142 range
.count
= tmp
- range
.position
- 1;
1144 WMSetListSelectionToRange(lPtr
, range
);
1147 range
.position
= tmp
;
1149 WMSetListSelectionToRange(lPtr
, range
);
1155 if (!(event
->xbutton
.state
& ShiftMask
))
1156 lastClicked
= prevItem
= tmp
;
1161 height
= WMWidgetHeight(lPtr
);
1162 if (lPtr
->selectID
&& event
->xmotion
.y
>0 && event
->xmotion
.y
<height
) {
1163 WMDeleteTimerHandler(lPtr
->selectID
);
1164 lPtr
->selectID
= NULL
;
1166 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
1167 if (event
->xmotion
.y
<= 0) {
1168 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1169 scrollBackwardSelecting
,
1172 } else if (event
->xmotion
.y
>= height
) {
1173 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
,
1174 scrollForwardSelecting
,
1179 tmp
= getItemIndexAt(lPtr
, event
->xmotion
.y
);
1180 if (tmp
>=0 && tmp
!=prevItem
) {
1181 if (lPtr
->flags
.allowMultipleSelection
) {
1184 range
.position
= lastClicked
;
1185 if (tmp
>= lastClicked
)
1186 range
.count
= tmp
- lastClicked
+ 1;
1188 range
.count
= tmp
- lastClicked
- 1;
1189 WMSetListSelectionToRange(lPtr
, range
);
1191 WMSelectListItem(lPtr
, tmp
);
1198 if (lPtr
->topItem
!= topItem
)
1199 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
1204 updateGeometry(WMList
*lPtr
)
1206 lPtr
->fullFitLines
= (lPtr
->view
->size
.height
- 4) / lPtr
->itemHeight
;
1207 if (lPtr
->fullFitLines
* lPtr
->itemHeight
< lPtr
->view
->size
.height
- 4) {
1208 lPtr
->flags
.dontFitAll
= 1;
1210 lPtr
->flags
.dontFitAll
= 0;
1213 if (WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
<= lPtr
->fullFitLines
) {
1214 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
1215 if (lPtr
->topItem
< 0)
1219 updateScroller(lPtr
);
1224 didResizeList(W_ViewDelegate
*self
, WMView
*view
)
1226 WMList
*lPtr
= (WMList
*)view
->self
;
1228 WMResizeWidget(lPtr
->vScroller
, 1, view
->size
.height
-2);
1230 updateGeometry(lPtr
);
1235 destroyList(List
*lPtr
)
1238 WMDeleteIdleHandler(lPtr
->idleID
);
1239 lPtr
->idleID
= NULL
;
1242 WMDeleteTimerHandler(lPtr
->selectID
);
1243 lPtr
->selectID
= NULL
;
1245 if (lPtr
->selectedItems
)
1246 WMFreeArray(lPtr
->selectedItems
);
1249 WMFreeArray(lPtr
->items
);