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(W_ViewDelegate
* self
, WMView
* view
);
69 static void unselectAllListItems(WMList
* lPtr
, WMListItem
* exceptThis
);
71 static 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 /* Parameter not used, but tell the compiler that it is ok */
98 updateDoubleBufferPixmap(self
);
101 static void releaseItem(void *data
)
103 WMListItem
*item
= (WMListItem
*) data
;
110 WMList
*WMCreateList(WMWidget
* parent
)
113 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
115 lPtr
= wmalloc(sizeof(List
));
117 lPtr
->widgetClass
= WC_List
;
119 lPtr
->view
= W_CreateView(W_VIEW(parent
));
124 lPtr
->view
->self
= lPtr
;
126 lPtr
->view
->delegate
= &_ListViewDelegate
;
128 WMCreateEventHandler(lPtr
->view
, ExposureMask
| StructureNotifyMask
129 | ClientMessageMask
, handleEvents
, lPtr
);
131 WMCreateEventHandler(lPtr
->view
, ButtonPressMask
| ButtonReleaseMask
132 | EnterWindowMask
| LeaveWindowMask
| ButtonMotionMask
, handleActionEvents
, lPtr
);
134 lPtr
->itemHeight
= WMFontHeight(scrPtr
->normalFont
) + 1;
136 lPtr
->items
= WMCreateArrayWithDestructor(4, releaseItem
);
137 lPtr
->selectedItems
= WMCreateArray(4);
139 /* create the vertical scroller */
140 lPtr
->vScroller
= WMCreateScroller(lPtr
);
141 WMMoveWidget(lPtr
->vScroller
, 1, 1);
142 WMSetScrollerArrowsPosition(lPtr
->vScroller
, WSAMaxEnd
);
144 WMSetScrollerAction(lPtr
->vScroller
, vScrollCallBack
, lPtr
);
146 /* make the scroller map itself when it's realized */
147 WMMapWidget(lPtr
->vScroller
);
149 W_ResizeView(lPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
151 WMAddNotificationObserver(realizeObserver
, lPtr
, WMViewRealizedNotification
, lPtr
->view
);
156 void WMSetListAllowMultipleSelection(WMList
* lPtr
, Bool flag
)
158 lPtr
->flags
.allowMultipleSelection
= ((flag
== 0) ? 0 : 1);
161 void WMSetListAllowEmptySelection(WMList
* lPtr
, Bool flag
)
163 lPtr
->flags
.allowEmptySelection
= ((flag
== 0) ? 0 : 1);
166 static int comparator(const void *a
, const void *b
)
168 return (strcmp((*(WMListItem
**) a
)->text
, (*(WMListItem
**) b
)->text
));
171 void WMSortListItems(WMList
* lPtr
)
173 WMSortArray(lPtr
->items
, comparator
);
178 void WMSortListItemsWithComparer(WMList
* lPtr
, WMCompareDataProc
* func
)
180 WMSortArray(lPtr
->items
, func
);
185 WMListItem
*WMInsertListItem(WMList
* lPtr
, int row
, const char *text
)
189 CHECK_CLASS(lPtr
, WC_List
);
191 item
= wmalloc(sizeof(WMListItem
));
192 item
->text
= wstrdup(text
);
194 row
= WMIN(row
, WMGetArrayItemCount(lPtr
->items
));
197 WMAddToArray(lPtr
->items
, item
);
199 WMInsertInArray(lPtr
->items
, row
, item
);
201 /* update the scroller when idle, so that we don't waste time
202 * updating it when another item is going to be added later */
204 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*) updateScroller
, lPtr
);
210 void WMRemoveListItem(WMList
* lPtr
, int row
)
213 int topItem
= lPtr
->topItem
;
216 CHECK_CLASS(lPtr
, WC_List
);
218 /*wassertr(row>=0 && row<WMGetArrayItemCount(lPtr->items)); */
219 if (row
< 0 || row
>= WMGetArrayItemCount(lPtr
->items
))
222 item
= WMGetFromArray(lPtr
->items
, row
);
223 if (item
->selected
) {
224 WMRemoveFromArray(lPtr
->selectedItems
, item
);
228 if (row
<= lPtr
->topItem
+ lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
)
230 if (lPtr
->topItem
< 0)
233 WMDeleteFromArray(lPtr
->items
, row
);
236 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*) updateScroller
, lPtr
);
238 if (lPtr
->topItem
!= topItem
) {
239 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
242 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
246 WMListItem
*WMGetListItem(WMList
* lPtr
, int row
)
248 return WMGetFromArray(lPtr
->items
, row
);
251 WMArray
*WMGetListItems(WMList
* lPtr
)
256 void WMSetListUserDrawProc(WMList
* lPtr
, WMListDrawProc
* proc
)
258 lPtr
->flags
.userDrawn
= 1;
262 void WMSetListUserDrawItemHeight(WMList
* lPtr
, unsigned short height
)
266 lPtr
->flags
.userItemHeight
= 1;
267 lPtr
->itemHeight
= height
;
269 updateDoubleBufferPixmap(lPtr
);
271 updateGeometry(lPtr
);
274 void WMClearList(WMList
* lPtr
)
276 int selNo
= WMGetArrayItemCount(lPtr
->selectedItems
);
278 WMEmptyArray(lPtr
->selectedItems
);
279 WMEmptyArray(lPtr
->items
);
284 WMDeleteIdleHandler(lPtr
->idleID
);
287 if (lPtr
->selectID
) {
288 WMDeleteTimerHandler(lPtr
->selectID
);
289 lPtr
->selectID
= NULL
;
291 if (lPtr
->view
->flags
.realized
) {
292 updateScroller(lPtr
);
295 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
299 void WMSetListAction(WMList
* lPtr
, WMAction
* action
, void *clientData
)
301 lPtr
->action
= action
;
302 lPtr
->clientData
= clientData
;
305 void WMSetListDoubleAction(WMList
* lPtr
, WMAction
* action
, void *clientData
)
307 lPtr
->doubleAction
= action
;
308 lPtr
->doubleClientData
= clientData
;
311 WMArray
*WMGetListSelectedItems(WMList
* lPtr
)
313 return lPtr
->selectedItems
;
316 WMListItem
*WMGetListSelectedItem(WMList
* lPtr
)
318 return WMGetFromArray(lPtr
->selectedItems
, 0);
321 int WMGetListSelectedItemRow(WMList
* lPtr
)
323 WMListItem
*item
= WMGetFromArray(lPtr
->selectedItems
, 0);
325 return (item
!= NULL
? WMGetFirstInArray(lPtr
->items
, item
) : WLNotFound
);
328 int WMGetListItemHeight(WMList
* lPtr
)
330 return lPtr
->itemHeight
;
333 void WMSetListPosition(WMList
* lPtr
, int row
)
336 if (lPtr
->topItem
+ lPtr
->fullFitLines
> WMGetArrayItemCount(lPtr
->items
))
337 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
339 if (lPtr
->topItem
< 0)
342 if (lPtr
->view
->flags
.realized
)
343 updateScroller(lPtr
);
346 void WMSetListBottomPosition(WMList
* lPtr
, int row
)
348 if (WMGetArrayItemCount(lPtr
->items
) > lPtr
->fullFitLines
) {
349 lPtr
->topItem
= row
- lPtr
->fullFitLines
;
350 if (lPtr
->topItem
< 0)
352 if (lPtr
->view
->flags
.realized
)
353 updateScroller(lPtr
);
357 int WMGetListNumberOfRows(WMList
* lPtr
)
359 return WMGetArrayItemCount(lPtr
->items
);
362 int WMGetListPosition(WMList
* lPtr
)
364 return lPtr
->topItem
;
367 Bool
WMListAllowsMultipleSelection(WMList
* lPtr
)
369 return lPtr
->flags
.allowMultipleSelection
;
372 Bool
WMListAllowsEmptySelection(WMList
* lPtr
)
374 return lPtr
->flags
.allowEmptySelection
;
377 static void scrollByAmount(WMList
* lPtr
, int amount
)
379 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
381 if ((amount
< 0 && lPtr
->topItem
> 0) || (amount
> 0 && (lPtr
->topItem
+ lPtr
->fullFitLines
< itemCount
))) {
383 lPtr
->topItem
+= amount
;
384 if (lPtr
->topItem
< 0)
386 if (lPtr
->topItem
+ lPtr
->fullFitLines
> itemCount
)
387 lPtr
->topItem
= itemCount
- lPtr
->fullFitLines
;
389 updateScroller(lPtr
);
393 static void vScrollCallBack(WMWidget
* scroller
, void *self
)
395 WMList
*lPtr
= (WMList
*) self
;
396 int oldTopItem
= lPtr
->topItem
;
397 int itemCount
= WMGetArrayItemCount(lPtr
->items
);
399 switch (WMGetScrollerHitPart((WMScroller
*) scroller
)) {
400 case WSDecrementLine
:
401 scrollByAmount(lPtr
, -1);
404 case WSIncrementLine
:
405 scrollByAmount(lPtr
, 1);
408 case WSDecrementPage
:
409 scrollByAmount(lPtr
, -lPtr
->fullFitLines
+ (1 - lPtr
->flags
.dontFitAll
) + 1);
412 case WSIncrementPage
:
413 scrollByAmount(lPtr
, lPtr
->fullFitLines
- (1 - lPtr
->flags
.dontFitAll
) - 1);
416 case WSDecrementWheel
:
417 scrollByAmount(lPtr
, -lPtr
->fullFitLines
/ 3);
420 case WSIncrementWheel
:
421 scrollByAmount(lPtr
, lPtr
->fullFitLines
/ 3);
425 lPtr
->topItem
= WMGetScrollerValue(lPtr
->vScroller
) * (float)(itemCount
- lPtr
->fullFitLines
);
427 if (oldTopItem
!= lPtr
->topItem
)
428 paintList(lPtr
); /* use updateScroller(lPtr) here? -Dan */
438 if (lPtr
->topItem
!= oldTopItem
)
439 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
442 static void paintItem(List
* lPtr
, int index
)
444 WMView
*view
= lPtr
->view
;
445 W_Screen
*scr
= view
->screen
;
446 Display
*display
= scr
->display
;
447 int width
, height
, x
, y
, tlen
;
449 Drawable d
= lPtr
->doubleBuffer
;
451 itemPtr
= WMGetFromArray(lPtr
->items
, index
);
453 width
= lPtr
->view
->size
.width
- 2 - 19;
454 height
= lPtr
->itemHeight
;
456 y
= 2 + (index
- lPtr
->topItem
) * lPtr
->itemHeight
+ 1;
457 tlen
= strlen(itemPtr
->text
);
459 if (lPtr
->flags
.userDrawn
) {
463 rect
.size
.width
= width
;
464 rect
.size
.height
= height
;
468 flags
= itemPtr
->uflags
;
469 if (itemPtr
->disabled
)
470 flags
|= WLDSDisabled
;
471 if (itemPtr
->selected
)
472 flags
|= WLDSSelected
;
473 if (itemPtr
->isBranch
)
474 flags
|= WLDSIsBranch
;
477 (*lPtr
->draw
) (lPtr
, index
, d
, itemPtr
->text
, flags
, &rect
);
479 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, 0, 0, width
, height
, x
, y
);
481 WMColor
*back
= (itemPtr
->selected
? scr
->white
: view
->backColor
);
483 XFillRectangle(display
, d
, WMColorGC(back
), 0, 0, width
, height
);
485 W_PaintText(view
, d
, scr
->normalFont
, 4, 0, width
, WALeft
, scr
->black
, False
, itemPtr
->text
, tlen
);
486 XCopyArea(display
, d
, view
->window
, scr
->copyGC
, 0, 0, width
, height
, x
, y
);
489 if ((index
- lPtr
->topItem
+ lPtr
->fullFitLines
) * lPtr
->itemHeight
> lPtr
->view
->size
.height
- 2) {
490 W_DrawRelief(lPtr
->view
->screen
, lPtr
->view
->window
, 0, 0,
491 lPtr
->view
->size
.width
, lPtr
->view
->size
.height
, WRSunken
);
495 static void paintList(List
* lPtr
)
497 W_Screen
*scrPtr
= lPtr
->view
->screen
;
500 if (!lPtr
->view
->flags
.mapped
)
503 if (WMGetArrayItemCount(lPtr
->items
) > 0) {
504 if (lPtr
->topItem
+ lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
> WMGetArrayItemCount(lPtr
->items
)) {
506 lim
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
;
507 XClearArea(scrPtr
->display
, lPtr
->view
->window
, 19,
508 2 + lim
* lPtr
->itemHeight
, lPtr
->view
->size
.width
- 21,
509 lPtr
->view
->size
.height
- lim
* lPtr
->itemHeight
- 3, False
);
511 lim
= lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
;
513 for (i
= lPtr
->topItem
; i
< lPtr
->topItem
+ lim
; i
++) {
517 XClearWindow(scrPtr
->display
, lPtr
->view
->window
);
519 W_DrawRelief(scrPtr
, lPtr
->view
->window
, 0, 0, lPtr
->view
->size
.width
, lPtr
->view
->size
.height
, WRSunken
);
523 static void scrollTo(List
* lPtr
, int newTop
)
529 static void updateScroller(void *data
)
531 List
*lPtr
= (List
*) data
;
533 float knobProportion
, floatValue
, tmp
;
534 int count
= WMGetArrayItemCount(lPtr
->items
);
537 WMDeleteIdleHandler(lPtr
->idleID
);
542 if (count
== 0 || count
<= lPtr
->fullFitLines
)
543 WMSetScrollerParameters(lPtr
->vScroller
, 0, 1);
545 tmp
= lPtr
->fullFitLines
;
546 knobProportion
= tmp
/ (float)count
;
548 floatValue
= (float)lPtr
->topItem
/ (float)(count
- lPtr
->fullFitLines
);
550 WMSetScrollerParameters(lPtr
->vScroller
, floatValue
, knobProportion
);
554 static void scrollForwardSelecting(void *data
)
556 List
*lPtr
= (List
*) data
;
559 lastSelected
= lPtr
->topItem
+ lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
- 1;
561 if (lastSelected
>= WMGetArrayItemCount(lPtr
->items
) - 1) {
562 lPtr
->selectID
= NULL
;
563 if (lPtr
->flags
.dontFitAll
)
564 scrollByAmount(lPtr
, 1);
568 /* selecting NEEDS to be done before scrolling to avoid flickering */
569 if (lPtr
->flags
.allowMultipleSelection
) {
573 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
574 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
575 if (lastSelected
+ 1 >= range
.position
) {
576 range
.count
= lastSelected
- range
.position
+ 2;
578 range
.count
= lastSelected
- range
.position
;
580 WMSetListSelectionToRange(lPtr
, range
);
582 WMSelectListItem(lPtr
, lastSelected
+ 1);
584 scrollByAmount(lPtr
, 1);
586 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
, lPtr
);
589 static void scrollBackwardSelecting(void *data
)
591 List
*lPtr
= (List
*) data
;
593 if (lPtr
->topItem
< 1) {
594 lPtr
->selectID
= NULL
;
598 /* selecting NEEDS to be done before scrolling to avoid flickering */
599 if (lPtr
->flags
.allowMultipleSelection
) {
603 item
= WMGetFromArray(lPtr
->selectedItems
, 0);
604 range
.position
= WMGetFirstInArray(lPtr
->items
, item
);
605 if (lPtr
->topItem
- 1 >= range
.position
) {
606 range
.count
= lPtr
->topItem
- range
.position
;
608 range
.count
= lPtr
->topItem
- range
.position
- 2;
610 WMSetListSelectionToRange(lPtr
, range
);
612 WMSelectListItem(lPtr
, lPtr
->topItem
- 1);
614 scrollByAmount(lPtr
, -1);
616 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
, lPtr
);
619 static void handleEvents(XEvent
* event
, void *data
)
621 List
*lPtr
= (List
*) data
;
623 CHECK_CLASS(data
, WC_List
);
625 switch (event
->type
) {
627 if (event
->xexpose
.count
!= 0)
639 static int matchTitle(const void *item
, const void *title
)
641 const WMListItem
*wl_item
= item
;
642 const char *s_title
= title
;
644 return (strcmp(wl_item
->text
, s_title
) == 0 ? 1 : 0);
647 int WMFindRowOfListItemWithTitle(WMList
* lPtr
, const char *title
)
650 * We explicitely discard the 'const' attribute here because the
651 * call-back function handler must not be made with a const
652 * attribute, but our local call-back function (above) does have
653 * it properly set, so we're consistent
655 return WMFindInArray(lPtr
->items
, matchTitle
, (char *) title
);
658 void WMSelectListItem(WMList
* lPtr
, int row
)
662 if (row
>= WMGetArrayItemCount(lPtr
->items
))
666 /* row = -1 will deselects all for backward compatibility.
667 * will be removed later. -Dan */
668 WMUnselectAllListItems(lPtr
);
672 item
= WMGetFromArray(lPtr
->items
, row
);
674 return; /* Return if already selected */
676 if (!lPtr
->flags
.allowMultipleSelection
) {
677 /* unselect previous selected items */
678 unselectAllListItems(lPtr
, NULL
);
683 WMAddToArray(lPtr
->selectedItems
, item
);
685 if (lPtr
->view
->flags
.mapped
&& row
>= lPtr
->topItem
&& row
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
686 paintItem(lPtr
, row
);
689 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
692 void WMUnselectListItem(WMList
* lPtr
, int row
)
694 WMListItem
*item
= WMGetFromArray(lPtr
->items
, row
);
696 if (!item
|| !item
->selected
)
699 if (!lPtr
->flags
.allowEmptySelection
&& WMGetArrayItemCount(lPtr
->selectedItems
) <= 1) {
704 WMRemoveFromArray(lPtr
->selectedItems
, item
);
706 if (lPtr
->view
->flags
.mapped
&& row
>= lPtr
->topItem
&& row
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
707 paintItem(lPtr
, row
);
710 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
713 void WMSelectListItemsInRange(WMList
* lPtr
, WMRange range
)
716 int position
= range
.position
, k
= 1, notify
= 0;
717 int total
= WMGetArrayItemCount(lPtr
->items
);
719 if (!lPtr
->flags
.allowMultipleSelection
)
721 if (range
.count
== 0)
722 return; /* Nothing to select */
724 if (range
.count
< 0) {
725 range
.count
= -range
.count
;
729 for (; range
.count
> 0 && position
>= 0 && position
< total
; range
.count
--) {
730 item
= WMGetFromArray(lPtr
->items
, position
);
731 if (!item
->selected
) {
733 WMAddToArray(lPtr
->selectedItems
, item
);
734 if (lPtr
->view
->flags
.mapped
&& position
>= lPtr
->topItem
735 && position
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
736 paintItem(lPtr
, position
);
744 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
748 void WMSetListSelectionToRange(WMList
* lPtr
, WMRange range
)
751 int mark1
, mark2
, i
, k
;
752 int position
= range
.position
, notify
= 0;
753 int total
= WMGetArrayItemCount(lPtr
->items
);
755 if (!lPtr
->flags
.allowMultipleSelection
)
758 if (range
.count
== 0) {
759 WMUnselectAllListItems(lPtr
);
763 if (range
.count
< 0) {
764 mark1
= range
.position
+ range
.count
+ 1;
765 mark2
= range
.position
+ 1;
766 range
.count
= -range
.count
;
769 mark1
= range
.position
;
770 mark2
= range
.position
+ range
.count
;
778 WMEmptyArray(lPtr
->selectedItems
);
780 for (i
= 0; i
< mark1
; i
++) {
781 item
= WMGetFromArray(lPtr
->items
, i
);
782 if (item
->selected
) {
784 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
785 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
791 for (; range
.count
> 0 && position
>= 0 && position
< total
; range
.count
--) {
792 item
= WMGetFromArray(lPtr
->items
, position
);
793 if (!item
->selected
) {
795 if (lPtr
->view
->flags
.mapped
&& position
>= lPtr
->topItem
796 && position
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
797 paintItem(lPtr
, position
);
801 WMAddToArray(lPtr
->selectedItems
, item
);
804 for (i
= mark2
; i
< total
; i
++) {
805 item
= WMGetFromArray(lPtr
->items
, i
);
806 if (item
->selected
) {
808 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
809 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
817 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
821 void WMSelectAllListItems(WMList
* lPtr
)
826 if (!lPtr
->flags
.allowMultipleSelection
)
829 if (WMGetArrayItemCount(lPtr
->items
) == WMGetArrayItemCount(lPtr
->selectedItems
)) {
830 return; /* All items are selected already */
833 WMFreeArray(lPtr
->selectedItems
);
834 lPtr
->selectedItems
= WMCreateArrayWithArray(lPtr
->items
);
836 for (i
= 0; i
< WMGetArrayItemCount(lPtr
->items
); i
++) {
837 item
= WMGetFromArray(lPtr
->items
, i
);
838 if (!item
->selected
) {
840 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
841 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
847 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
851 * Be careful from where you call this function! It doesn't honor the
852 * allowEmptySelection flag and doesn't send a notification about selection
853 * change! You need to manage these in the functions from where you call it.
855 * This will unselect all items if exceptThis is NULL, else will keep
856 * exceptThis selected.
857 * Make sure that exceptThis is one of the already selected items if not NULL!
860 static void unselectAllListItems(WMList
* lPtr
, WMListItem
* exceptThis
)
865 for (i
= 0; i
< WMGetArrayItemCount(lPtr
->items
); i
++) {
866 item
= WMGetFromArray(lPtr
->items
, i
);
867 if (item
!= exceptThis
&& item
->selected
) {
869 if (lPtr
->view
->flags
.mapped
&& i
>= lPtr
->topItem
870 && i
<= lPtr
->topItem
+ lPtr
->fullFitLines
) {
876 WMEmptyArray(lPtr
->selectedItems
);
877 if (exceptThis
!= NULL
) {
878 exceptThis
->selected
= 1;
879 WMAddToArray(lPtr
->selectedItems
, exceptThis
);
883 void WMUnselectAllListItems(WMList
* lPtr
)
886 WMListItem
*keepItem
;
888 keep
= lPtr
->flags
.allowEmptySelection
? 0 : 1;
890 if (WMGetArrayItemCount(lPtr
->selectedItems
) == keep
)
893 keepItem
= (keep
== 1 ? WMGetFromArray(lPtr
->selectedItems
, 0) : NULL
);
895 unselectAllListItems(lPtr
, keepItem
);
897 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
, NULL
);
900 static int getItemIndexAt(List
* lPtr
, int clickY
)
904 index
= (clickY
- 2) / lPtr
->itemHeight
+ lPtr
->topItem
;
906 if (index
< 0 || index
>= WMGetArrayItemCount(lPtr
->items
))
912 static void toggleItemSelection(WMList
* lPtr
, int index
)
914 WMListItem
*item
= WMGetFromArray(lPtr
->items
, index
);
916 if (item
&& item
->selected
) {
917 WMUnselectListItem(lPtr
, index
);
919 WMSelectListItem(lPtr
, index
);
923 static void handleActionEvents(XEvent
* event
, void *data
)
925 List
*lPtr
= (List
*) data
;
927 int topItem
= lPtr
->topItem
;
928 static int lastClicked
= -1, prevItem
= -1;
930 CHECK_CLASS(data
, WC_List
);
932 switch (event
->type
) {
934 /* Ignore mouse wheel events, they're not "real" button events */
935 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
||
936 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
) {
940 lPtr
->flags
.buttonPressed
= 0;
941 if (lPtr
->selectID
) {
942 WMDeleteTimerHandler(lPtr
->selectID
);
943 lPtr
->selectID
= NULL
;
945 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
949 (*lPtr
->action
) (lPtr
, lPtr
->clientData
);
952 if (!(event
->xbutton
.state
& ShiftMask
))
953 lastClicked
= prevItem
= tmp
;
958 if (lPtr
->selectID
) {
959 WMDeleteTimerHandler(lPtr
->selectID
);
960 lPtr
->selectID
= NULL
;
965 height
= WMWidgetHeight(lPtr
);
966 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
967 if (event
->xcrossing
.y
>= height
) {
968 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
, lPtr
);
969 } else if (event
->xcrossing
.y
<= 0) {
970 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
, lPtr
);
976 if (event
->xbutton
.x
<= WMWidgetWidth(lPtr
->vScroller
))
978 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
||
979 event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
) {
982 if (event
->xbutton
.state
& ControlMask
) {
983 amount
= lPtr
->fullFitLines
- (1 - lPtr
->flags
.dontFitAll
) - 1;
984 } else if (event
->xbutton
.state
& ShiftMask
) {
987 amount
= lPtr
->fullFitLines
/ 3;
991 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
)
994 scrollByAmount(lPtr
, amount
);
998 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
999 lPtr
->flags
.buttonPressed
= 1;
1002 if (tmp
== lastClicked
&& WMIsDoubleClick(event
)) {
1003 WMSelectListItem(lPtr
, tmp
);
1004 if (lPtr
->doubleAction
)
1005 (*lPtr
->doubleAction
) (lPtr
, lPtr
->doubleClientData
);
1007 if (!lPtr
->flags
.allowMultipleSelection
) {
1008 if (event
->xbutton
.state
& ControlMask
) {
1009 toggleItemSelection(lPtr
, tmp
);
1011 WMSelectListItem(lPtr
, tmp
);
1015 WMListItem
*lastSel
;
1017 if (event
->xbutton
.state
& ControlMask
) {
1018 toggleItemSelection(lPtr
, tmp
);
1019 } else if (event
->xbutton
.state
& ShiftMask
) {
1020 if (WMGetArrayItemCount(lPtr
->selectedItems
) == 0) {
1021 WMSelectListItem(lPtr
, tmp
);
1023 lastSel
= WMGetFromArray(lPtr
->items
, lastClicked
);
1024 range
.position
= WMGetFirstInArray(lPtr
->items
, lastSel
);
1025 if (tmp
>= range
.position
)
1026 range
.count
= tmp
- range
.position
+ 1;
1028 range
.count
= tmp
- range
.position
- 1;
1030 WMSetListSelectionToRange(lPtr
, range
);
1033 range
.position
= tmp
;
1035 WMSetListSelectionToRange(lPtr
, range
);
1041 if (!(event
->xbutton
.state
& ShiftMask
))
1042 lastClicked
= prevItem
= tmp
;
1047 height
= WMWidgetHeight(lPtr
);
1048 if (lPtr
->selectID
&& event
->xmotion
.y
> 0 && event
->xmotion
.y
< height
) {
1049 WMDeleteTimerHandler(lPtr
->selectID
);
1050 lPtr
->selectID
= NULL
;
1052 if (lPtr
->flags
.buttonPressed
&& !lPtr
->selectID
) {
1053 if (event
->xmotion
.y
<= 0) {
1054 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollBackwardSelecting
, lPtr
);
1056 } else if (event
->xmotion
.y
>= height
) {
1057 lPtr
->selectID
= WMAddTimerHandler(SCROLL_DELAY
, scrollForwardSelecting
, lPtr
);
1061 tmp
= getItemIndexAt(lPtr
, event
->xmotion
.y
);
1062 if (tmp
>= 0 && tmp
!= prevItem
) {
1063 if (lPtr
->flags
.allowMultipleSelection
) {
1066 range
.position
= lastClicked
;
1067 if (tmp
>= lastClicked
)
1068 range
.count
= tmp
- lastClicked
+ 1;
1070 range
.count
= tmp
- lastClicked
- 1;
1071 WMSetListSelectionToRange(lPtr
, range
);
1073 WMSelectListItem(lPtr
, tmp
);
1080 if (lPtr
->topItem
!= topItem
)
1081 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
1084 static void updateGeometry(WMList
* lPtr
)
1086 lPtr
->fullFitLines
= (lPtr
->view
->size
.height
- 4) / lPtr
->itemHeight
;
1087 if (lPtr
->fullFitLines
* lPtr
->itemHeight
< lPtr
->view
->size
.height
- 4) {
1088 lPtr
->flags
.dontFitAll
= 1;
1090 lPtr
->flags
.dontFitAll
= 0;
1093 if (WMGetArrayItemCount(lPtr
->items
) - lPtr
->topItem
<= lPtr
->fullFitLines
) {
1094 lPtr
->topItem
= WMGetArrayItemCount(lPtr
->items
) - lPtr
->fullFitLines
;
1095 if (lPtr
->topItem
< 0)
1099 updateScroller(lPtr
);
1102 static void didResizeList(W_ViewDelegate
* self
, WMView
* view
)
1104 WMList
*lPtr
= (WMList
*) view
->self
;
1106 /* Parameter not used, but tell the compiler that it is ok */
1109 WMResizeWidget(lPtr
->vScroller
, 1, view
->size
.height
- 2);
1111 updateDoubleBufferPixmap(lPtr
);
1113 updateGeometry(lPtr
);
1116 static void destroyList(List
* lPtr
)
1119 WMDeleteIdleHandler(lPtr
->idleID
);
1120 lPtr
->idleID
= NULL
;
1123 WMDeleteTimerHandler(lPtr
->selectID
);
1124 lPtr
->selectID
= NULL
;
1126 if (lPtr
->selectedItems
)
1127 WMFreeArray(lPtr
->selectedItems
);
1130 WMFreeArray(lPtr
->items
);
1132 if (lPtr
->doubleBuffer
)
1133 XFreePixmap(lPtr
->view
->screen
->display
, lPtr
->doubleBuffer
);
1135 WMRemoveNotificationObserver(lPtr
);