4 char *WMListDidScrollNotification
= "WMListDidScrollNotification";
5 char *WMListSelectionDidChangeNotification
= "WMListSelectionDidChangeNotification";
7 typedef struct W_List
{
11 WMArray
*items
; /* list of WMListItem */
12 WMArray
*selectedItems
; /* list of selected WMListItems */
16 int topItem
; /* index of first visible item */
18 short fullFitLines
; /* no of lines that fit entirely */
22 void *doubleClientData
;
23 WMAction
*doubleAction
;
27 WMHandlerID
*idleID
; /* for updating the scroller after adding elements */
29 WMHandlerID
*selectID
; /* for selecting items in list while scrolling */
31 WMScroller
*vScroller
;
36 unsigned int allowMultipleSelection
:1;
37 unsigned int allowEmptySelection
:1;
38 unsigned int userDrawn
:1;
39 unsigned int userItemHeight
:1;
40 unsigned int dontFitAll
:1; /* 1 = last item won't be fully visible */
41 unsigned int redrawPending
:1;
42 unsigned int buttonPressed
:1;
43 unsigned int buttonWasPressed
:1;
47 #define DEFAULT_WIDTH 150
48 #define DEFAULT_HEIGHT 150
50 #define SCROLL_DELAY 100
52 static void destroyList(List
* lPtr
);
53 static void paintList(List
* lPtr
);
55 static void handleEvents(XEvent
* event
, void *data
);
56 static void handleActionEvents(XEvent
* event
, void *data
);
58 static void updateScroller(void *data
);
59 static void scrollForwardSelecting(void *data
);
60 static void scrollBackwardSelecting(void *data
);
62 static void vScrollCallBack(WMWidget
* scroller
, void *self
);
64 static void toggleItemSelection(WMList
* lPtr
, int index
);
66 static void updateGeometry(WMList
* lPtr
);
67 static void didResizeList();
69 static void unselectAllListItems(WMList
* lPtr
, WMListItem
* exceptThis
);
71 W_ViewDelegate _ListViewDelegate
= {
79 static void updateDoubleBufferPixmap(WMList
* lPtr
)
81 WMView
*view
= lPtr
->view
;
82 WMScreen
*scr
= view
->screen
;
84 if (!view
->flags
.realized
)
87 if (lPtr
->doubleBuffer
)
88 XFreePixmap(scr
->display
, lPtr
->doubleBuffer
);
90 XCreatePixmap(scr
->display
, view
->window
, view
->size
.width
, lPtr
->itemHeight
, scr
->depth
);
93 static void realizeObserver(void *self
, WMNotification
* not)
95 updateDoubleBufferPixmap(self
);
98 static void releaseItem(void *data
)
100 WMListItem
*item
= (WMListItem
*) data
;
107 WMList
*WMCreateList(WMWidget
* parent
)
110 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
112 lPtr
= wmalloc(sizeof(List
));
113 memset(lPtr
, 0, sizeof(List
));
115 lPtr
->widgetClass
= WC_List
;
117 lPtr
->view
= W_CreateView(W_VIEW(parent
));
122 lPtr
->view
->self
= lPtr
;
124 lPtr
->view
->delegate
= &_ListViewDelegate
;
126 WMCreateEventHandler(lPtr
->view
, ExposureMask
| StructureNotifyMask
127 | ClientMessageMask
, handleEvents
, lPtr
);
129 WMCreateEventHandler(lPtr
->view
, ButtonPressMask
| ButtonReleaseMask
130 | EnterWindowMask
| LeaveWindowMask
| ButtonMotionMask
, handleActionEvents
, lPtr
);
132 lPtr
->itemHeight
= WMFontHeight(scrPtr
->normalFont
) + 1;
134 lPtr
->items
= WMCreateArrayWithDestructor(4, releaseItem
);
135 lPtr
->selectedItems
= WMCreateArray(4);
137 /* create the vertical scroller */
138 lPtr
->vScroller
= WMCreateScroller(lPtr
);
139 WMMoveWidget(lPtr
->vScroller
, 1, 1);
140 WMSetScrollerArrowsPosition(lPtr
->vScroller
, WSAMaxEnd
);
142 WMSetScrollerAction(lPtr
->vScroller
, vScrollCallBack
, lPtr
);
144 /* make the scroller map itself when it's realized */
145 WMMapWidget(lPtr
->vScroller
);
147 W_ResizeView(lPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
149 WMAddNotificationObserver(realizeObserver
, lPtr
, WMViewRealizedNotification
, lPtr
->view
);
154 void WMSetListAllowMultipleSelection(WMList
* lPtr
, Bool flag
)
156 lPtr
->flags
.allowMultipleSelection
= ((flag
== 0) ? 0 : 1);
159 void WMSetListAllowEmptySelection(WMList
* lPtr
, Bool flag
)
161 lPtr
->flags
.allowEmptySelection
= ((flag
== 0) ? 0 : 1);
164 static int comparator(const void *a
, const void *b
)
166 return (strcmp((*(WMListItem
**) a
)->text
, (*(WMListItem
**) b
)->text
));
169 void WMSortListItems(WMList
* lPtr
)
171 WMSortArray(lPtr
->items
, comparator
);
176 void WMSortListItemsWithComparer(WMList
* lPtr
, WMCompareDataProc
* func
)
178 WMSortArray(lPtr
->items
, func
);
183 WMListItem
*WMInsertListItem(WMList
* lPtr
, int row
, char *text
)
187 CHECK_CLASS(lPtr
, WC_List
);
189 item
= wmalloc(sizeof(WMListItem
));
190 memset(item
, 0, sizeof(WMListItem
));
191 item
->text
= wstrdup(text
);
193 row
= WMIN(row
, WMGetArrayItemCount(lPtr
->items
));
196 WMAddToArray(lPtr
->items
, item
);
198 WMInsertInArray(lPtr
->items
, row
, item
);
200 /* update the scroller when idle, so that we don't waste time
201 * updating it when another item is going to be added later */
203 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*) updateScroller
, lPtr
);
209 void WMRemoveListItem(WMList
* lPtr
, int row
)
212 int topItem
= lPtr
->topItem
;
215 CHECK_CLASS(lPtr
, WC_List
);
217 /*wassertr(row>=0 && row<WMGetArrayItemCount(lPtr->items)); */
218 if (row
< 0 || row
>= WMGetArrayItemCount(lPtr
->items
))
221 item
= WMGetFromArray(lPtr
->items
, row
);
222 if (item
->selected
) {
223 WMRemoveFromArray(lPtr
->selectedItems
, item
);
227 if (row
<= lPtr
->topItem
+ lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
)
229 if (lPtr
->topItem
< 0)
232 WMDeleteFromArray(lPtr
->items
, row
);
235 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*) updateScroller
, lPtr
);
237 if (lPtr
->topItem
!= topItem
) {
238 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
241 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
245 WMListItem
*WMGetListItem(WMList
* lPtr
, int row
)
247 return WMGetFromArray(lPtr
->items
, row
);
250 WMArray
*WMGetListItems(WMList
* lPtr
)
255 void WMSetListUserDrawProc(WMList
* lPtr
, WMListDrawProc
* proc
)
257 lPtr
->flags
.userDrawn
= 1;
261 void WMSetListUserDrawItemHeight(WMList
* lPtr
, unsigned short height
)
265 lPtr
->flags
.userItemHeight
= 1;
266 lPtr
->itemHeight
= height
;
268 updateDoubleBufferPixmap(lPtr
);
270 updateGeometry(lPtr
);
273 void WMClearList(WMList
* lPtr
)
275 int selNo
= WMGetArrayItemCount(lPtr
->selectedItems
);
277 WMEmptyArray(lPtr
->selectedItems
);
278 WMEmptyArray(lPtr
->items
);
283 WMDeleteIdleHandler(lPtr
->idleID
);
286 if (lPtr
->selectID
) {
287 WMDeleteTimerHandler(lPtr
->selectID
);
288 lPtr
->selectID
= NULL
;
290 if (lPtr
->view
->flags
.realized
) {
291 updateScroller(lPtr
);
294 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
298 void WMSetListAction(WMList
* lPtr
, WMAction
* action
, void *clientData
)
300 lPtr
->action
= action
;
301 lPtr
->clientData
= clientData
;
304 void WMSetListDoubleAction(WMList
* lPtr
, WMAction
* action
, void *clientData
)
306 lPtr
->doubleAction
= action
;
307 lPtr
->doubleClientData
= clientData
;
310 WMArray
*WMGetListSelectedItems(WMList
* lPtr
)
312 return lPtr
->selectedItems
;
315 WMListItem
*WMGetListSelectedItem(WMList
* lPtr
)
317 return WMGetFromArray(lPtr
->selectedItems
, 0);
320 int WMGetListSelectedItemRow(WMList
* lPtr
)
322 WMListItem
*item
= WMGetFromArray(lPtr
->selectedItems
, 0);
324 return (item
!= NULL
? WMGetFirstInArray(lPtr
->items
, item
) : WLNotFound
);
327 int WMGetListItemHeight(WMList
* lPtr
)
329 return lPtr
->itemHeight
;
332 void WMSetListPosition(WMList
* lPtr
, int row
)
335 if (lPtr
->topItem
+ lPtr
->fullFitLines
> WMGetArrayItemCount(lPtr
->items
))
336 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
338 if (lPtr
->topItem
< 0)
341 if (lPtr
->view
->flags
.realized
)
342 updateScroller(lPtr
);
345 void WMSetListBottomPosition(WMList
* lPtr
, int row
)
347 if (WMGetArrayItemCount(lPtr
->items
) > lPtr
->fullFitLines
) {
348 lPtr
->topItem
= row
- lPtr
->fullFitLines
;
349 if (lPtr
->topItem
< 0)
351 if (lPtr
->view
->flags
.realized
)
352 updateScroller(lPtr
);
356 int WMGetListNumberOfRows(WMList
* lPtr
)
358 return WMGetArrayItemCount(lPtr
->items
);
361 int WMGetListPosition(WMList
* lPtr
)
363 return lPtr
->topItem
;
366 Bool
WMListAllowsMultipleSelection(WMList
* lPtr
)
368 return lPtr
->flags
.allowMultipleSelection
;
371 Bool
WMListAllowsEmptySelection(WMList
* lPtr
)
373 return lPtr
->flags
.allowEmptySelection
;
376 static void scrollByAmount(WMList
* lPtr
, int amount
)
378 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
380 if ((amount
< 0 && lPtr
->topItem
> 0) || (amount
> 0 && (lPtr
->topItem
+ lPtr
->fullFitLines
< itemCount
))) {
382 lPtr
->topItem
+= amount
;
383 if (lPtr
->topItem
< 0)
385 if (lPtr
->topItem
+ lPtr
->fullFitLines
> itemCount
)
386 lPtr
->topItem
= itemCount
- lPtr
->fullFitLines
;
388 updateScroller(lPtr
);
392 static void vScrollCallBack(WMWidget
* scroller
, void *self
)
394 WMList
*lPtr
= (WMList
*) self
;
396 int oldTopItem
= lPtr
->topItem
;
397 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
399 height
= lPtr
->view
->size
.height
- 4;
401 switch (WMGetScrollerHitPart((WMScroller
*) scroller
)) {
402 case WSDecrementLine
:
403 scrollByAmount(lPtr
, -1);
406 case WSIncrementLine
:
407 scrollByAmount(lPtr
, 1);
410 case WSDecrementPage
:
411 scrollByAmount(lPtr
, -lPtr
->fullFitLines
+ (1 - lPtr
->flags
.dontFitAll
) + 1);
414 case WSIncrementPage
:
415 scrollByAmount(lPtr
, lPtr
->fullFitLines
- (1 - lPtr
->flags
.dontFitAll
) - 1);
418 case WSDecrementWheel
:
419 scrollByAmount(lPtr
, -lPtr
->fullFitLines
/ 3);
422 case WSIncrementWheel
:
423 scrollByAmount(lPtr
, lPtr
->fullFitLines
/ 3);
427 lPtr
->topItem
= WMGetScrollerValue(lPtr
->vScroller
) * (float)(itemCount
- lPtr
->fullFitLines
);
429 if (oldTopItem
!= lPtr
->topItem
)
430 paintList(lPtr
); /* use updateScroller(lPtr) here? -Dan */
440 if (lPtr
->topItem
!= oldTopItem
)
441 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
444 static void paintItem(List
* lPtr
, int index
)
446 WMView
*view
= lPtr
->view
;
447 W_Screen
*scr
= view
->screen
;
448 Display
*display
= scr
->display
;
449 int width
, height
, x
, y
, tlen
;
451 Drawable d
= lPtr
->doubleBuffer
;
453 itemPtr
= WMGetFromArray(lPtr
->items
, index
);
455 width
= lPtr
->view
->size
.width
- 2 - 19;
456 height
= lPtr
->itemHeight
;
458 y
= 2 + (index
- lPtr
->topItem
) * lPtr
->itemHeight
+ 1;
459 tlen
= strlen(itemPtr
->text
);
461 if (lPtr
->flags
.userDrawn
) {
465 rect
.size
.width
= width
;
466 rect
.size
.height
= height
;
470 flags
= itemPtr
->uflags
;
471 if (itemPtr
->disabled
)
472 flags
|= WLDSDisabled
;
473 if (itemPtr
->selected
)
474 flags
|= WLDSSelected
;
475 if (itemPtr
->isBranch
)
476 flags
|= WLDSIsBranch
;
479 (*lPtr
->draw
) (lPtr
, index
, d
, itemPtr
->text
, flags
, &rect
);
481 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, 0, 0, width
, height
, x
, y
);
483 WMColor
*back
= (itemPtr
->selected
? scr
->white
: view
->backColor
);
485 XFillRectangle(display
, d
, WMColorGC(back
), 0, 0, width
, height
);
487 W_PaintText(view
, d
, scr
->normalFont
, 4, 0, width
, WALeft
, scr
->black
, False
, itemPtr
->text
, tlen
);
488 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, 0, 0, width
, height
, x
, y
);
491 if ((index
- lPtr
->topItem
+ lPtr
->fullFitLines
) * lPtr
->itemHeight
> lPtr
->view
->size
.height
- 2) {
492 W_DrawRelief(lPtr
->view
->screen
, lPtr
->view
->window
, 0, 0,
493 lPtr
->view
->size
.width
, lPtr
->view
->size
.height
, WRSunken
);
497 static void paintList(List
* lPtr
)
499 W_Screen
*scrPtr
= lPtr
->view
->screen
;
502 if (!lPtr
->view
->flags
.mapped
)
505 if (WMGetArrayItemCount(lPtr
->items
) > 0) {
506 if (lPtr
->topItem
+ lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
> WMGetArrayItemCount(lPtr
->items
)) {
508 lim
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
;
509 XClearArea(scrPtr
->display
, lPtr
->view
->window
, 19,
510 2 + lim
* lPtr
->itemHeight
, lPtr
->view
->size
.width
- 21,
511 lPtr
->view
->size
.height
- lim
* lPtr
->itemHeight
- 3, False
);
513 lim
= lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
;
515 for (i
= lPtr
->topItem
; i
< lPtr
->topItem
+ lim
; i
++) {
519 XClearWindow(scrPtr
->display
, lPtr
->view
->window
);
521 W_DrawRelief(scrPtr
, lPtr
->view
->window
, 0, 0, lPtr
->view
->size
.width
, lPtr
->view
->size
.height
, WRSunken
);
525 static void scrollTo(List
* lPtr
, int newTop
)
531 static void updateScroller(void *data
)
533 List
*lPtr
= (List
*) data
;
535 float knobProportion
, floatValue
, tmp
;
536 int count
= WMGetArrayItemCount(lPtr
->items
);
539 WMDeleteIdleHandler(lPtr
->idleID
);
544 if (count
== 0 || count
<= lPtr
->fullFitLines
)
545 WMSetScrollerParameters(lPtr
->vScroller
, 0, 1);
547 tmp
= lPtr
->fullFitLines
;
548 knobProportion
= tmp
/ (float)count
;
550 floatValue
= (float)lPtr
->topItem
/ (float)(count
- lPtr
->fullFitLines
);
552 WMSetScrollerParameters(lPtr
->vScroller
, floatValue
, knobProportion
);
556 static void scrollForwardSelecting(void *data
)
558 List
*lPtr
= (List
*) data
;
561 lastSelected
= lPtr
->topItem
+ lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
- 1;
563 if (lastSelected
>= WMGetArrayItemCount(lPtr
->items
) - 1) {
564 lPtr
->selectID
= NULL
;
565 if (lPtr
->flags
.dontFitAll
)
566 scrollByAmount(lPtr
, 1);
570 /* selecting NEEDS to be done before scrolling to avoid flickering */
571 if (lPtr
->flags
.allowMultipleSelection
) {
575 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
576 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
577 if (lastSelected
+ 1 >= range
.position
) {
578 range
.count
= lastSelected
- range
.position
+ 2;
580 range
.count
= lastSelected
- range
.position
;
582 WMSetListSelectionToRange(lPtr
, range
);
584 WMSelectListItem(lPtr
, lastSelected
+ 1);
586 scrollByAmount(lPtr
, 1);
588 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
, lPtr
);
591 static void scrollBackwardSelecting(void *data
)
593 List
*lPtr
= (List
*) data
;
595 if (lPtr
->topItem
< 1) {
596 lPtr
->selectID
= NULL
;
600 /* selecting NEEDS to be done before scrolling to avoid flickering */
601 if (lPtr
->flags
.allowMultipleSelection
) {
605 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
606 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
607 if (lPtr
->topItem
- 1 >= range
.position
) {
608 range
.count
= lPtr
->topItem
- range
.position
;
610 range
.count
= lPtr
->topItem
- range
.position
- 2;
612 WMSetListSelectionToRange(lPtr
, range
);
614 WMSelectListItem(lPtr
, lPtr
->topItem
- 1);
616 scrollByAmount(lPtr
, -1);
618 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
, lPtr
);
621 static void handleEvents(XEvent
* event
, void *data
)
623 List
*lPtr
= (List
*) data
;
625 CHECK_CLASS(data
, WC_List
);
627 switch (event
->type
) {
629 if (event
->xexpose
.count
!= 0)
641 static int matchTitle(void *item
, void *title
)
643 return (strcmp(((WMListItem
*) item
)->text
, (char *)title
) == 0 ? 1 : 0);
646 int WMFindRowOfListItemWithTitle(WMList
* lPtr
, char *title
)
648 return WMFindInArray(lPtr
->items
, matchTitle
, title
);
651 void WMSelectListItem(WMList
* lPtr
, int row
)
655 if (row
>= WMGetArrayItemCount(lPtr
->items
))
659 /* row = -1 will deselects all for backward compatibility.
660 * will be removed later. -Dan */
661 WMUnselectAllListItems(lPtr
);
665 item
= WMGetFromArray(lPtr
->items
, row
);
667 return; /* Return if already selected */
669 if (!lPtr
->flags
.allowMultipleSelection
) {
670 /* unselect previous selected items */
671 unselectAllListItems(lPtr
, NULL
);
676 WMAddToArray(lPtr
->selectedItems
, item
);
678 if (lPtr
->view
->flags
.mapped
&& row
>= lPtr
->topItem
&& row
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
679 paintItem(lPtr
, row
);
682 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
685 void WMUnselectListItem(WMList
* lPtr
, int row
)
687 WMListItem
*item
= WMGetFromArray(lPtr
->items
, row
);
689 if (!item
|| !item
->selected
)
692 if (!lPtr
->flags
.allowEmptySelection
&& WMGetArrayItemCount(lPtr
->selectedItems
) <= 1) {
697 WMRemoveFromArray(lPtr
->selectedItems
, item
);
699 if (lPtr
->view
->flags
.mapped
&& row
>= lPtr
->topItem
&& row
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
700 paintItem(lPtr
, row
);
703 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
706 void WMSelectListItemsInRange(WMList
* lPtr
, WMRange range
)
709 int position
= range
.position
, k
= 1, notify
= 0;
710 int total
= WMGetArrayItemCount(lPtr
->items
);
712 if (!lPtr
->flags
.allowMultipleSelection
)
714 if (range
.count
== 0)
715 return; /* Nothing to select */
717 if (range
.count
< 0) {
718 range
.count
= -range
.count
;
722 for (; range
.count
> 0 && position
>= 0 && position
< total
; range
.count
--) {
723 item
= WMGetFromArray(lPtr
->items
, position
);
724 if (!item
->selected
) {
726 WMAddToArray(lPtr
->selectedItems
, item
);
727 if (lPtr
->view
->flags
.mapped
&& position
>= lPtr
->topItem
728 && position
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
729 paintItem(lPtr
, position
);
737 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
741 void WMSetListSelectionToRange(WMList
* lPtr
, WMRange range
)
744 int mark1
, mark2
, i
, k
;
745 int position
= range
.position
, notify
= 0;
746 int total
= WMGetArrayItemCount(lPtr
->items
);
748 if (!lPtr
->flags
.allowMultipleSelection
)
751 if (range
.count
== 0) {
752 WMUnselectAllListItems(lPtr
);
756 if (range
.count
< 0) {
757 mark1
= range
.position
+ range
.count
+ 1;
758 mark2
= range
.position
+ 1;
759 range
.count
= -range
.count
;
762 mark1
= range
.position
;
763 mark2
= range
.position
+ range
.count
;
771 WMEmptyArray(lPtr
->selectedItems
);
773 for (i
= 0; i
< mark1
; i
++) {
774 item
= WMGetFromArray(lPtr
->items
, i
);
775 if (item
->selected
) {
777 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
778 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
784 for (; range
.count
> 0 && position
>= 0 && position
< total
; range
.count
--) {
785 item
= WMGetFromArray(lPtr
->items
, position
);
786 if (!item
->selected
) {
788 if (lPtr
->view
->flags
.mapped
&& position
>= lPtr
->topItem
789 && position
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
790 paintItem(lPtr
, position
);
794 WMAddToArray(lPtr
->selectedItems
, item
);
797 for (i
= mark2
; i
< total
; i
++) {
798 item
= WMGetFromArray(lPtr
->items
, i
);
799 if (item
->selected
) {
801 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
802 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
810 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
814 void WMSelectAllListItems(WMList
* lPtr
)
819 if (!lPtr
->flags
.allowMultipleSelection
)
822 if (WMGetArrayItemCount(lPtr
->items
) == WMGetArrayItemCount(lPtr
->selectedItems
)) {
823 return; /* All items are selected already */
826 WMFreeArray(lPtr
->selectedItems
);
827 lPtr
->selectedItems
= WMCreateArrayWithArray(lPtr
->items
);
829 for (i
= 0; i
< WMGetArrayItemCount(lPtr
->items
); i
++) {
830 item
= WMGetFromArray(lPtr
->items
, i
);
831 if (!item
->selected
) {
833 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
834 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
840 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
844 * Be careful from where you call this function! It doesn't honor the
845 * allowEmptySelection flag and doesn't send a notification about selection
846 * change! You need to manage these in the functions from where you call it.
848 * This will unselect all items if exceptThis is NULL, else will keep
849 * exceptThis selected.
850 * Make sure that exceptThis is one of the already selected items if not NULL!
853 static void unselectAllListItems(WMList
* lPtr
, WMListItem
* exceptThis
)
858 for (i
= 0; i
< WMGetArrayItemCount(lPtr
->items
); i
++) {
859 item
= WMGetFromArray(lPtr
->items
, i
);
860 if (item
!= exceptThis
&& item
->selected
) {
862 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
863 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
869 WMEmptyArray(lPtr
->selectedItems
);
870 if (exceptThis
!= NULL
) {
871 exceptThis
->selected
= 1;
872 WMAddToArray(lPtr
->selectedItems
, exceptThis
);
876 void WMUnselectAllListItems(WMList
* lPtr
)
879 WMListItem
*keepItem
;
881 keep
= lPtr
->flags
.allowEmptySelection
? 0 : 1;
883 if (WMGetArrayItemCount(lPtr
->selectedItems
) == keep
)
886 keepItem
= (keep
== 1 ? WMGetFromArray(lPtr
->selectedItems
, 0) : NULL
);
888 unselectAllListItems(lPtr
, keepItem
);
890 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
893 static int getItemIndexAt(List
* lPtr
, int clickY
)
897 index
= (clickY
- 2) / lPtr
->itemHeight
+ lPtr
->topItem
;
899 if (index
< 0 || index
>= WMGetArrayItemCount(lPtr
->items
))
905 static void toggleItemSelection(WMList
* lPtr
, int index
)
907 WMListItem
*item
= WMGetFromArray(lPtr
->items
, index
);
909 if (item
&& item
->selected
) {
910 WMUnselectListItem(lPtr
, index
);
912 WMSelectListItem(lPtr
, index
);
916 static void handleActionEvents(XEvent
* event
, void *data
)
918 List
*lPtr
= (List
*) data
;
920 int topItem
= lPtr
->topItem
;
921 static int lastClicked
= -1, prevItem
= -1;
923 CHECK_CLASS(data
, WC_List
);
925 switch (event
->type
) {
927 /* Ignore mouse wheel events, they're not "real" button events */
928 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
||
929 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
) {
933 lPtr
->flags
.buttonPressed
= 0;
934 if (lPtr
->selectID
) {
935 WMDeleteTimerHandler(lPtr
->selectID
);
936 lPtr
->selectID
= NULL
;
938 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
942 (*lPtr
->action
) (lPtr
, lPtr
->clientData
);
945 if (!(event
->xbutton
.state
& ShiftMask
))
946 lastClicked
= prevItem
= tmp
;
951 if (lPtr
->selectID
) {
952 WMDeleteTimerHandler(lPtr
->selectID
);
953 lPtr
->selectID
= NULL
;
958 height
= WMWidgetHeight(lPtr
);
959 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
960 if (event
->xcrossing
.y
>= height
) {
961 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
, lPtr
);
962 } else if (event
->xcrossing
.y
<= 0) {
963 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
, lPtr
);
969 if (event
->xbutton
.x
<= WMWidgetWidth(lPtr
->vScroller
))
971 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
||
972 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
) {
975 if (event
->xbutton
.state
& ControlMask
) {
976 amount
= lPtr
->fullFitLines
- (1 - lPtr
->flags
.dontFitAll
) - 1;
977 } else if (event
->xbutton
.state
& ShiftMask
) {
980 amount
= lPtr
->fullFitLines
/ 3;
984 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
)
987 scrollByAmount(lPtr
, amount
);
991 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
992 lPtr
->flags
.buttonPressed
= 1;
995 if (tmp
== lastClicked
&& WMIsDoubleClick(event
)) {
996 WMSelectListItem(lPtr
, tmp
);
997 if (lPtr
->doubleAction
)
998 (*lPtr
->doubleAction
) (lPtr
, lPtr
->doubleClientData
);
1000 if (!lPtr
->flags
.allowMultipleSelection
) {
1001 if (event
->xbutton
.state
& ControlMask
) {
1002 toggleItemSelection(lPtr
, tmp
);
1004 WMSelectListItem(lPtr
, tmp
);
1008 WMListItem
*lastSel
;
1010 if (event
->xbutton
.state
& ControlMask
) {
1011 toggleItemSelection(lPtr
, tmp
);
1012 } else if (event
->xbutton
.state
& ShiftMask
) {
1013 if (WMGetArrayItemCount(lPtr
->selectedItems
) == 0) {
1014 WMSelectListItem(lPtr
, tmp
);
1016 lastSel
= WMGetFromArray(lPtr
->items
, lastClicked
);
1017 range
.position
= WMGetFirstInArray(lPtr
->items
, lastSel
);
1018 if (tmp
>= range
.position
)
1019 range
.count
= tmp
- range
.position
+ 1;
1021 range
.count
= tmp
- range
.position
- 1;
1023 WMSetListSelectionToRange(lPtr
, range
);
1026 range
.position
= tmp
;
1028 WMSetListSelectionToRange(lPtr
, range
);
1034 if (!(event
->xbutton
.state
& ShiftMask
))
1035 lastClicked
= prevItem
= tmp
;
1040 height
= WMWidgetHeight(lPtr
);
1041 if (lPtr
->selectID
&& event
->xmotion
.y
> 0 && event
->xmotion
.y
< height
) {
1042 WMDeleteTimerHandler(lPtr
->selectID
);
1043 lPtr
->selectID
= NULL
;
1045 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
1046 if (event
->xmotion
.y
<= 0) {
1047 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
, lPtr
);
1049 } else if (event
->xmotion
.y
>= height
) {
1050 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
, lPtr
);
1054 tmp
= getItemIndexAt(lPtr
, event
->xmotion
.y
);
1055 if (tmp
>= 0 && tmp
!= prevItem
) {
1056 if (lPtr
->flags
.allowMultipleSelection
) {
1059 range
.position
= lastClicked
;
1060 if (tmp
>= lastClicked
)
1061 range
.count
= tmp
- lastClicked
+ 1;
1063 range
.count
= tmp
- lastClicked
- 1;
1064 WMSetListSelectionToRange(lPtr
, range
);
1066 WMSelectListItem(lPtr
, tmp
);
1073 if (lPtr
->topItem
!= topItem
)
1074 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
1077 static void updateGeometry(WMList
* lPtr
)
1079 lPtr
->fullFitLines
= (lPtr
->view
->size
.height
- 4) / lPtr
->itemHeight
;
1080 if (lPtr
->fullFitLines
* lPtr
->itemHeight
< lPtr
->view
->size
.height
- 4) {
1081 lPtr
->flags
.dontFitAll
= 1;
1083 lPtr
->flags
.dontFitAll
= 0;
1086 if (WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
<= lPtr
->fullFitLines
) {
1087 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
1088 if (lPtr
->topItem
< 0)
1092 updateScroller(lPtr
);
1095 static void didResizeList(W_ViewDelegate
* self
, WMView
* view
)
1097 WMList
*lPtr
= (WMList
*) view
->self
;
1099 WMResizeWidget(lPtr
->vScroller
, 1, view
->size
.height
- 2);
1101 updateDoubleBufferPixmap(lPtr
);
1103 updateGeometry(lPtr
);
1106 static void destroyList(List
* lPtr
)
1109 WMDeleteIdleHandler(lPtr
->idleID
);
1110 lPtr
->idleID
= NULL
;
1113 WMDeleteTimerHandler(lPtr
->selectID
);
1114 lPtr
->selectID
= NULL
;
1116 if (lPtr
->selectedItems
)
1117 WMFreeArray(lPtr
->selectedItems
);
1120 WMFreeArray(lPtr
->items
);
1122 if (lPtr
->doubleBuffer
)
1123 XFreePixmap(lPtr
->view
->screen
->display
, lPtr
->doubleBuffer
);
1125 WMRemoveNotificationObserver(lPtr
);