- the list multiple selection code is working now. it still needs some
[wmaker-crm.git] / WINGs / wlist.c
blob9fed3aeae4e2b637ad2330192081341c0fe7fb68
5 #include "WINGsP.h"
7 char *WMListDidScrollNotification = "WMListDidScrollNotification";
8 char *WMListSelectionDidChangeNotification = "WMListSelectionDidChangeNotification";
10 typedef struct W_List {
11 W_Class widgetClass;
12 W_View *view;
14 WMArray *items; /* list of WMListItem */
15 WMArray *selectedItems; /* list of selected WMListItems */
17 short itemHeight;
19 short topItem; /* index of first visible item */
21 short fullFitLines; /* no of lines that fit entirely */
23 void *clientData;
24 WMAction *action;
25 void *doubleClientData;
26 WMAction *doubleAction;
28 WMListDrawProc *draw;
30 WMHandlerID *idleID; /* for updating the scroller after adding elements */
32 WMScroller *vScroller;
34 struct {
35 unsigned int allowMultipleSelection:1;
36 unsigned int allowEmptySelection:1;
37 unsigned int userDrawn:1;
38 unsigned int userItemHeight:1;
39 unsigned int dontFitAll:1; /* 1 = last item won't be fully visible */
40 unsigned int redrawPending:1;
41 unsigned int buttonPressed:1;
42 unsigned int buttonWasPressed:1;
43 } flags;
44 } List;
48 #define DEFAULT_WIDTH 150
49 #define DEFAULT_HEIGHT 150
52 static void destroyList(List *lPtr);
53 static void paintList(List *lPtr);
56 static void handleEvents(XEvent *event, void *data);
57 static void handleActionEvents(XEvent *event, void *data);
58 static void updateScroller(List *lPtr);
60 static void vScrollCallBack(WMWidget *scroller, void *self);
62 static void updateGeometry(WMList *lPtr);
63 static void didResizeList();
66 W_ViewDelegate _ListViewDelegate = {
67 NULL,
68 NULL,
69 didResizeList,
70 NULL,
71 NULL
75 static void
76 releaseItem(void *data)
78 WMListItem *item = (WMListItem*)data;
80 if (item->text)
81 wfree(item->text);
82 wfree(item);
86 WMList*
87 WMCreateList(WMWidget *parent)
89 List *lPtr;
90 W_Screen *scrPtr = W_VIEW(parent)->screen;
92 lPtr = wmalloc(sizeof(List));
93 memset(lPtr, 0, sizeof(List));
95 lPtr->widgetClass = WC_List;
97 lPtr->view = W_CreateView(W_VIEW(parent));
98 if (!lPtr->view) {
99 wfree(lPtr);
100 return NULL;
102 lPtr->view->self = lPtr;
104 lPtr->view->delegate = &_ListViewDelegate;
106 WMCreateEventHandler(lPtr->view, ExposureMask|StructureNotifyMask
107 |ClientMessageMask, handleEvents, lPtr);
109 WMCreateEventHandler(lPtr->view, ButtonPressMask|ButtonReleaseMask
110 |EnterWindowMask|LeaveWindowMask|ButtonMotionMask,
111 handleActionEvents, lPtr);
113 lPtr->itemHeight = WMFontHeight(scrPtr->normalFont) + 1;
115 lPtr->items = WMCreateArrayWithDestructor(4, releaseItem);
116 lPtr->selectedItems = WMCreateArray(4);
118 /* create the vertical scroller */
119 lPtr->vScroller = WMCreateScroller(lPtr);
120 WMMoveWidget(lPtr->vScroller, 1, 1);
121 WMSetScrollerArrowsPosition(lPtr->vScroller, WSAMaxEnd);
123 WMSetScrollerAction(lPtr->vScroller, vScrollCallBack, lPtr);
125 /* make the scroller map itself when it's realized */
126 WMMapWidget(lPtr->vScroller);
128 W_ResizeView(lPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
130 return lPtr;
134 void
135 WMSetListAllowMultipleSelection(WMList *lPtr, Bool flag)
137 lPtr->flags.allowMultipleSelection = flag ? 1 : 0;
142 void
143 WMSetListAllowEmptySelection(WMList *lPtr, Bool flag)
145 lPtr->flags.allowEmptySelection = flag ? 1 : 0;
149 static int
150 comparator(const void *a, const void *b)
152 return (strcmp((*(WMListItem**)a)->text, (*(WMListItem**)b)->text));
156 void
157 WMSortListItems(WMList *lPtr)
159 WMSortArray(lPtr->items, comparator);
161 paintList(lPtr);
166 void
167 WMSortListItemsWithComparer(WMList *lPtr, WMCompareDataProc *func)
169 WMSortArray(lPtr->items, func);
171 paintList(lPtr);
176 WMListItem*
177 WMInsertListItem(WMList *lPtr, int row, char *text)
179 WMListItem *item;
181 CHECK_CLASS(lPtr, WC_List);
183 item = wmalloc(sizeof(WMListItem));
184 memset(item, 0, sizeof(WMListItem));
185 item->text = wstrdup(text);
187 row = WMIN(row, WMGetArrayItemCount(lPtr->items));
189 if (row < 0)
190 WMAddToArray(lPtr->items, item);
191 else
192 WMInsertInArray(lPtr->items, row, item);
194 /* update the scroller when idle, so that we don't waste time
195 * updating it when another item is going to be added later */
196 if (!lPtr->idleID) {
197 lPtr->idleID = WMAddIdleHandler((WMCallback*)updateScroller, lPtr);
200 return item;
204 void
205 WMRemoveListItem(WMList *lPtr, int row)
207 WMListItem *item;
208 int topItem = lPtr->topItem;
209 int selNotify = 0;
211 CHECK_CLASS(lPtr, WC_List);
213 /*wassertr(row>=0 && row<WMGetArrayItemCount(lPtr->items));*/
214 if (row<0 || row>=WMGetArrayItemCount(lPtr->items))
215 return;
217 item = WMGetFromArray(lPtr->items, row);
218 if (item->selected) {
219 WMRemoveFromArray(lPtr->selectedItems, item);
220 selNotify = 1;
223 if (row <= lPtr->topItem+lPtr->fullFitLines+lPtr->flags.dontFitAll)
224 lPtr->topItem--;
225 if (lPtr->topItem < 0)
226 lPtr->topItem = 0;
228 WMDeleteFromArray(lPtr->items, row);
230 if (!lPtr->idleID) {
231 lPtr->idleID = WMAddIdleHandler((WMCallback*)updateScroller, lPtr);
233 if (lPtr->topItem != topItem) {
234 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
236 if (selNotify) {
237 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
242 WMListItem*
243 WMGetListItem(WMList *lPtr, int row)
245 return WMGetFromArray(lPtr->items, row);
249 WMArray*
250 WMGetListItems(WMList *lPtr)
252 return lPtr->items;
256 void
257 WMSetListUserDrawProc(WMList *lPtr, WMListDrawProc *proc)
259 lPtr->flags.userDrawn = 1;
260 lPtr->draw = proc;
264 void
265 WMSetListUserDrawItemHeight(WMList *lPtr, unsigned short height)
267 assert(height > 0);
269 lPtr->flags.userItemHeight = 1;
270 lPtr->itemHeight = height;
272 updateGeometry(lPtr);
276 void
277 WMClearList(WMList *lPtr)
279 int selNo = WMGetArrayItemCount(lPtr->selectedItems);
281 WMEmptyArray(lPtr->selectedItems);
282 WMEmptyArray(lPtr->items);
284 lPtr->topItem = 0;
286 if (!lPtr->idleID) {
287 WMDeleteIdleHandler(lPtr->idleID);
288 lPtr->idleID = NULL;
290 if (lPtr->view->flags.realized) {
291 updateScroller(lPtr);
293 if (selNo > 0) {
294 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
299 void
300 WMSetListAction(WMList *lPtr, WMAction *action, void *clientData)
302 lPtr->action = action;
303 lPtr->clientData = clientData;
307 void
308 WMSetListDoubleAction(WMList *lPtr, WMAction *action, void *clientData)
310 lPtr->doubleAction = action;
311 lPtr->doubleClientData = clientData;
315 WMArray*
316 WMGetListSelectedItems(WMList *lPtr)
318 return lPtr->selectedItems;
322 WMListItem*
323 WMGetListSelectedItem(WMList *lPtr)
325 return WMGetFromArray(lPtr->selectedItems, 0);
330 WMGetListSelectedItemRow(WMList *lPtr)
332 WMListItem *item = WMGetFromArray(lPtr->selectedItems, 0);
334 return (item!=NULL ? WMGetFirstInArray(lPtr->items, item) : WLNotFound);
339 WMGetListItemHeight(WMList *lPtr)
341 return lPtr->itemHeight;
345 void
346 WMSetListPosition(WMList *lPtr, int row)
348 lPtr->topItem = row;
349 if (lPtr->topItem + lPtr->fullFitLines > WMGetArrayItemCount(lPtr->items))
350 lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines;
352 if (lPtr->topItem < 0)
353 lPtr->topItem = 0;
355 if (lPtr->view->flags.realized)
356 updateScroller(lPtr);
360 void
361 WMSetListBottomPosition(WMList *lPtr, int row)
363 if (WMGetArrayItemCount(lPtr->items) > lPtr->fullFitLines) {
364 lPtr->topItem = row - lPtr->fullFitLines;
365 if (lPtr->topItem < 0)
366 lPtr->topItem = 0;
367 if (lPtr->view->flags.realized)
368 updateScroller(lPtr);
374 WMGetListNumberOfRows(WMList *lPtr)
376 return WMGetArrayItemCount(lPtr->items);
381 WMGetListPosition(WMList *lPtr)
383 return lPtr->topItem;
387 Bool
388 WMListAllowsMultipleSelection(WMList *lPtr)
390 return lPtr->flags.allowMultipleSelection;
394 Bool
395 WMListAllowsEmptySelection(WMList *lPtr)
397 return lPtr->flags.allowEmptySelection;
401 static void
402 scrollByAmount(WMList *lPtr, int amount)
404 int itemCount = WMGetArrayItemCount(lPtr->items);
406 if ((amount < 0 && lPtr->topItem > 0) ||
407 (amount > 0 && (lPtr->topItem + lPtr->fullFitLines < itemCount))) {
409 lPtr->topItem += amount;
410 if (lPtr->topItem < 0)
411 lPtr->topItem = 0;
412 if (lPtr->topItem + lPtr->fullFitLines > itemCount)
413 lPtr->topItem = itemCount - lPtr->fullFitLines;
415 updateScroller(lPtr);
420 static void
421 vScrollCallBack(WMWidget *scroller, void *self)
423 WMList *lPtr = (WMList*)self;
424 int height;
425 int oldTopItem = lPtr->topItem;
426 int itemCount = WMGetArrayItemCount(lPtr->items);
428 height = lPtr->view->size.height - 4;
430 switch (WMGetScrollerHitPart((WMScroller*)scroller)) {
431 case WSDecrementLine:
432 scrollByAmount(lPtr, -1);
433 break;
435 case WSIncrementLine:
436 scrollByAmount(lPtr, 1);
437 break;
439 case WSDecrementPage:
440 scrollByAmount(lPtr, -lPtr->fullFitLines+(1-lPtr->flags.dontFitAll)+1);
441 break;
443 case WSIncrementPage:
444 scrollByAmount(lPtr, lPtr->fullFitLines-(1-lPtr->flags.dontFitAll)-1);
445 break;
447 case WSDecrementWheel:
448 scrollByAmount(lPtr, -lPtr->fullFitLines / 3);
449 break;
451 case WSIncrementWheel:
452 scrollByAmount(lPtr, lPtr->fullFitLines / 3);
453 break;
455 case WSKnob:
456 lPtr->topItem = WMGetScrollerValue(lPtr->vScroller) *
457 (float)(itemCount - lPtr->fullFitLines);
459 if (oldTopItem != lPtr->topItem)
460 paintList(lPtr); // use updateScroller(lPtr) here?
461 break;
463 case WSKnobSlot:
464 case WSNoPart:
465 default:
466 /* do nothing */
467 break;
470 if (lPtr->topItem != oldTopItem)
471 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
475 static void
476 paintItem(List *lPtr, int index)
478 WMView *view = lPtr->view;
479 W_Screen *scr = view->screen;
480 int width, height, x, y;
481 WMListItem *itemPtr;
483 itemPtr = WMGetFromArray(lPtr->items, index);
485 width = lPtr->view->size.width - 2 - 19;
486 height = lPtr->itemHeight;
487 x = 19;
488 y = 2 + (index-lPtr->topItem) * lPtr->itemHeight + 1;
490 if (lPtr->flags.userDrawn) {
491 WMRect rect;
492 int flags;
494 rect.size.width = width;
495 rect.size.height = height;
496 rect.pos.x = x;
497 rect.pos.y = y;
499 flags = itemPtr->uflags;
500 if (itemPtr->disabled)
501 flags |= WLDSDisabled;
502 if (itemPtr->selected)
503 flags |= WLDSSelected;
504 if (itemPtr->isBranch)
505 flags |= WLDSIsBranch;
507 if (lPtr->draw)
508 (*lPtr->draw)(lPtr, index, view->window, itemPtr->text, flags,
509 &rect);
510 } else {
511 if (itemPtr->selected) {
512 XFillRectangle(scr->display, view->window, WMColorGC(scr->white),
513 x, y, width, height);
514 } else {
515 XClearArea(scr->display, view->window, x, y, width, height, False);
518 W_PaintText(view, view->window, scr->normalFont, x+4, y, width,
519 WALeft, WMColorGC(scr->black), False,
520 itemPtr->text, strlen(itemPtr->text));
523 if ((index-lPtr->topItem+lPtr->fullFitLines)*lPtr->itemHeight >
524 lPtr->view->size.height-2) {
525 W_DrawRelief(lPtr->view->screen, lPtr->view->window, 0, 0,
526 lPtr->view->size.width, lPtr->view->size.height,
527 WRSunken);
533 static void
534 paintList(List *lPtr)
536 W_Screen *scrPtr = lPtr->view->screen;
537 int i, lim;
539 if (!lPtr->view->flags.mapped)
540 return;
542 if (WMGetArrayItemCount(lPtr->items) > 0) {
543 if (lPtr->topItem+lPtr->fullFitLines+lPtr->flags.dontFitAll
544 > WMGetArrayItemCount(lPtr->items)) {
546 lim = WMGetArrayItemCount(lPtr->items) - lPtr->topItem;
547 XClearArea(scrPtr->display, lPtr->view->window, 19,
548 2+lim*lPtr->itemHeight, lPtr->view->size.width-21,
549 lPtr->view->size.height-lim*lPtr->itemHeight-3, False);
550 } else {
551 lim = lPtr->fullFitLines + lPtr->flags.dontFitAll;
553 for (i = lPtr->topItem; i < lPtr->topItem + lim; i++) {
554 paintItem(lPtr, i);
556 } else {
557 XClearWindow(scrPtr->display, lPtr->view->window);
559 W_DrawRelief(scrPtr, lPtr->view->window, 0, 0, lPtr->view->size.width,
560 lPtr->view->size.height, WRSunken);
563 #if 0
564 static void
565 scrollTo(List *lPtr, int newTop)
569 #endif
571 static void
572 updateScroller(List *lPtr)
574 float knobProportion, floatValue, tmp;
575 int count = WMGetArrayItemCount(lPtr->items);
577 if (lPtr->idleID)
578 WMDeleteIdleHandler(lPtr->idleID);
579 lPtr->idleID = NULL;
581 paintList(lPtr);
583 if (count == 0 || count <= lPtr->fullFitLines)
584 WMSetScrollerParameters(lPtr->vScroller, 0, 1);
585 else {
586 tmp = lPtr->fullFitLines;
587 knobProportion = tmp/(float)count;
589 floatValue = (float)lPtr->topItem/(float)(count - lPtr->fullFitLines);
591 WMSetScrollerParameters(lPtr->vScroller, floatValue, knobProportion);
596 static void
597 handleEvents(XEvent *event, void *data)
599 List *lPtr = (List*)data;
601 CHECK_CLASS(data, WC_List);
604 switch (event->type) {
605 case Expose:
606 if (event->xexpose.count!=0)
607 break;
608 paintList(lPtr);
609 break;
611 case DestroyNotify:
612 destroyList(lPtr);
613 break;
619 static int
620 matchTitle(void *item, void *title)
622 return (strcmp(((WMListItem*)item)->text, (char*)title)==0 ? 1 : 0);
627 WMFindRowOfListItemWithTitle(WMList *lPtr, char *title)
629 return WMFindInArray(lPtr->items, matchTitle, title);
633 void
634 WMSelectListItem(WMList *lPtr, int row)
636 WMListItem *item;
638 if (row >= WMGetArrayItemCount(lPtr->items))
639 return;
641 if (row < 0) {
642 /* row = -1 will deselects all for backward compatibility.
643 * will be removed later. -Dan */
644 WMUnselectAllListItems(lPtr);
645 return;
648 item = WMGetFromArray(lPtr->items, row);
649 if (item->selected)
650 return; /* Return if already selected */
652 if (!lPtr->flags.allowMultipleSelection) {
653 /* unselect previous selected items */
654 WMUnselectAllListItems(lPtr);
657 /* select item */
658 item->selected = 1;
659 WMAddToArray(lPtr->selectedItems, item);
661 if (lPtr->view->flags.mapped && row>=lPtr->topItem
662 && row<=lPtr->topItem+lPtr->fullFitLines) {
663 paintItem(lPtr, row);
666 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
670 void
671 WMUnselectListItem(WMList *lPtr, int row)
673 WMListItem *item = WMGetFromArray(lPtr->items, row);
675 if (!item || !item->selected)
676 return;
678 if (!lPtr->flags.allowEmptySelection &&
679 WMGetArrayItemCount(lPtr->selectedItems) <= 1) {
680 return;
683 item->selected = 0;
684 WMRemoveFromArray(lPtr->selectedItems, item);
686 if (lPtr->view->flags.mapped && row>=lPtr->topItem
687 && row<=lPtr->topItem+lPtr->fullFitLines) {
688 paintItem(lPtr, row);
691 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
695 void
696 WMSelectListItemsInRange(WMList *lPtr, WMRange range)
698 WMListItem *item;
699 int position = range.position, k = 1, notify = 0;
700 int total = WMGetArrayItemCount(lPtr->items);
702 if (!lPtr->flags.allowMultipleSelection)
703 return;
704 if (range.count==0)
705 return; /* Nothing to select */
707 if (range.count < 0) {
708 range.count = -range.count;
709 k = -1;
712 for (; range.count>0 && position>=0 && position<total; range.count--) {
713 item = WMGetFromArray(lPtr->items, position);
714 if (!item->selected) {
715 item->selected = 1;
716 WMAddToArray(lPtr->selectedItems, item);
717 if (lPtr->view->flags.mapped && position>=lPtr->topItem
718 && position<=lPtr->topItem+lPtr->fullFitLines) {
719 paintItem(lPtr, position);
721 notify = 1;
723 position += k;
726 if (notify) {
727 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
732 void
733 WMSetListSelectionToRange(WMList *lPtr, WMRange range)
735 WMListItem *item;
736 int mark1, mark2, i, k;
737 int position = range.position, notify = 0;
738 int total = WMGetArrayItemCount(lPtr->items);
740 if (!lPtr->flags.allowMultipleSelection)
741 return;
743 if (range.count==0) {
744 WMUnselectAllListItems(lPtr);
745 return;
748 if (range.count < 0) {
749 mark1 = range.position + range.count + 1;
750 mark2 = range.position + 1;
751 range.count = -range.count;
752 k = -1;
753 } else {
754 mark1 = range.position;
755 mark2 = range.position + range.count;
756 k = 1;
758 if (mark1 > total)
759 mark1 = total;
760 if (mark2 < 0)
761 mark2 = 0;
763 WMEmptyArray(lPtr->selectedItems);
765 for (i=0; i<mark1; i++) {
766 item = WMGetFromArray(lPtr->items, i);
767 if (item->selected) {
768 item->selected = 0;
769 if (lPtr->view->flags.mapped && i>=lPtr->topItem
770 && i<=lPtr->topItem+lPtr->fullFitLines) {
771 paintItem(lPtr, i);
773 notify = 1;
776 for (; range.count>0 && position>=0 && position<total; range.count--) {
777 item = WMGetFromArray(lPtr->items, position);
778 if (!item->selected) {
779 item->selected = 1;
780 if (lPtr->view->flags.mapped && position>=lPtr->topItem
781 && position<=lPtr->topItem+lPtr->fullFitLines) {
782 paintItem(lPtr, position);
784 notify = 1;
786 WMAddToArray(lPtr->selectedItems, item);
787 position += k;
789 for (i=mark2; i<total; i++) {
790 item = WMGetFromArray(lPtr->items, i);
791 if (item->selected) {
792 item->selected = 0;
793 if (lPtr->view->flags.mapped && i>=lPtr->topItem
794 && i<=lPtr->topItem+lPtr->fullFitLines) {
795 paintItem(lPtr, i);
797 notify = 1;
801 if (notify) {
802 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
807 void
808 WMSelectAllListItems(WMList *lPtr)
810 int i;
811 WMListItem *item;
813 if (!lPtr->flags.allowMultipleSelection)
814 return;
816 if (WMGetArrayItemCount(lPtr->items) ==
817 WMGetArrayItemCount(lPtr->selectedItems)) {
818 return; /* All items are selected already */
821 WMFreeArray(lPtr->selectedItems);
822 lPtr->selectedItems = WMCreateArrayWithArray(lPtr->items);
824 for (i=0; i<WMGetArrayItemCount(lPtr->items); i++) {
825 item = WMGetFromArray(lPtr->items, i);
826 if (!item->selected) {
827 item->selected = 1;
828 if (lPtr->view->flags.mapped && i>=lPtr->topItem
829 && i<=lPtr->topItem+lPtr->fullFitLines) {
830 paintItem(lPtr, i);
835 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
839 void
840 WMUnselectAllListItems(WMList *lPtr)
842 int i;//, keep;
843 WMListItem *item;//, *keepItem;
845 // FIXME: check for allowEmptySelection
847 //keep = lPtr->flags.allowEmptySelection ? 0 : 1;
849 //if (WMGetArrayItemCount(lPtr->selectedItems) == keep)
850 // return 1; /* Nothing selected so return */
852 //keepItem = (keep==1 ? WMGetFromArray(lPtr->selectedItems, 0) : NULL);
854 for (i=0; i<WMGetArrayItemCount(lPtr->items); i++) {
855 item = WMGetFromArray(lPtr->items, i);
856 if (item->selected) {
857 item->selected = 0;
858 if (lPtr->view->flags.mapped && i>=lPtr->topItem
859 && i<=lPtr->topItem+lPtr->fullFitLines) {
860 paintItem(lPtr, i);
865 WMEmptyArray(lPtr->selectedItems);
866 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
870 static int
871 getItemIndexAt(List *lPtr, int clickY)
873 int index;
875 index = (clickY - 2) / lPtr->itemHeight + lPtr->topItem;
877 if (index < 0 || index >= WMGetArrayItemCount(lPtr->items))
878 return -1;
880 return index;
884 static void
885 handleActionEvents(XEvent *event, void *data)
887 List *lPtr = (List*)data;
888 int tmp;
889 int topItem = lPtr->topItem;
890 static int lastClicked = -1, prevItem = -1;
892 CHECK_CLASS(data, WC_List);
894 switch (event->type) {
895 case ButtonRelease:
896 /* Ignore mouse wheel events, they're not "real" button events */
897 if (event->xbutton.button == WINGsConfiguration.mouseWheelUp ||
898 event->xbutton.button == WINGsConfiguration.mouseWheelDown) {
899 break;
902 lPtr->flags.buttonPressed = 0;
903 tmp = getItemIndexAt(lPtr, event->xbutton.y);
905 if (tmp >= 0) {
906 if (lPtr->action)
907 (*lPtr->action)(lPtr, lPtr->clientData);
910 if (!(event->xbutton.state & ShiftMask))
911 lastClicked = prevItem = tmp;
913 break;
915 case EnterNotify:
916 lPtr->flags.buttonPressed = lPtr->flags.buttonWasPressed;
917 lPtr->flags.buttonWasPressed = 0;
918 break;
920 case LeaveNotify:
921 lPtr->flags.buttonWasPressed = lPtr->flags.buttonPressed;
922 lPtr->flags.buttonPressed = 0;
923 break;
925 case ButtonPress:
926 if (event->xbutton.x <= WMWidgetWidth(lPtr->vScroller))
927 break;
928 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown ||
929 event->xbutton.button == WINGsConfiguration.mouseWheelUp) {
930 int amount = 0;
932 if (event->xbutton.state & ControlMask) {
933 amount = lPtr->fullFitLines-(1-lPtr->flags.dontFitAll)-1;
934 } else if (event->xbutton.state & ShiftMask) {
935 amount = 1;
936 } else {
937 amount = lPtr->fullFitLines / 3;
938 if (amount == 0)
939 amount++;
941 if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
942 amount = -amount;
944 scrollByAmount(lPtr, amount);
945 break;
948 tmp = getItemIndexAt(lPtr, event->xbutton.y);
949 lPtr->flags.buttonPressed = 1;
951 if (tmp >= 0) {
952 if (tmp == lastClicked && WMIsDoubleClick(event)) {
953 WMSelectListItem(lPtr, tmp);
954 if (lPtr->doubleAction)
955 (*lPtr->doubleAction)(lPtr, lPtr->doubleClientData);
956 } else {
957 if (!lPtr->flags.allowMultipleSelection) {
958 WMSelectListItem(lPtr, tmp);
959 } else {
960 WMRange range;
961 WMListItem *item, *lastSel;
963 if (event->xbutton.state & ControlMask) {
964 item = WMGetFromArray(lPtr->items, tmp);
965 if (item && item->selected) {
966 WMUnselectListItem(lPtr, tmp);
967 } else {
968 WMSelectListItem(lPtr, tmp);
970 } else if (event->xbutton.state & ShiftMask) {
971 if (WMGetArrayItemCount(lPtr->selectedItems) == 0) {
972 WMSelectListItem(lPtr, tmp);
973 } else {
974 lastSel = WMGetFromArray(lPtr->items, lastClicked);
975 range.position = WMGetFirstInArray(lPtr->items,
976 lastSel);
977 if (tmp >= range.position)
978 range.count = tmp - range.position + 1;
979 else
980 range.count = tmp - range.position - 1;
982 WMSetListSelectionToRange(lPtr, range);
984 } else {
985 range.position = tmp;
986 range.count = 1;
987 WMSetListSelectionToRange(lPtr, range);
993 if (!(event->xbutton.state & ShiftMask))
994 lastClicked = prevItem = tmp;
996 break;
998 case MotionNotify:
999 if (lPtr->flags.buttonPressed) {
1000 tmp = getItemIndexAt(lPtr, event->xmotion.y);
1001 if (tmp>=0 && tmp!=prevItem) {
1002 if (lPtr->flags.allowMultipleSelection) {
1003 WMRange range;
1005 range.position = lastClicked;
1006 if (tmp >= lastClicked)
1007 range.count = tmp - lastClicked + 1;
1008 else
1009 range.count = tmp - lastClicked - 1;
1010 WMSetListSelectionToRange(lPtr, range);
1011 } else {
1012 WMSelectListItem(lPtr, tmp);
1015 prevItem = tmp;
1017 break;
1019 if (lPtr->topItem != topItem)
1020 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
1024 static void
1025 updateGeometry(WMList *lPtr)
1027 lPtr->fullFitLines = (lPtr->view->size.height - 4) / lPtr->itemHeight;
1028 if (lPtr->fullFitLines * lPtr->itemHeight < lPtr->view->size.height - 4) {
1029 lPtr->flags.dontFitAll = 1;
1030 } else {
1031 lPtr->flags.dontFitAll = 0;
1034 if (WMGetArrayItemCount(lPtr->items) - lPtr->topItem <= lPtr->fullFitLines) {
1035 lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines;
1036 if (lPtr->topItem < 0)
1037 lPtr->topItem = 0;
1040 updateScroller(lPtr);
1044 static void
1045 didResizeList(W_ViewDelegate *self, WMView *view)
1047 WMList *lPtr = (WMList*)view->self;
1049 WMResizeWidget(lPtr->vScroller, 1, view->size.height-2);
1051 updateGeometry(lPtr);
1055 static void
1056 destroyList(List *lPtr)
1058 if (lPtr->idleID)
1059 WMDeleteIdleHandler(lPtr->idleID);
1060 lPtr->idleID = NULL;
1062 WMFreeArray(lPtr->items);
1064 wfree(lPtr);