- Fixed some issues with WMBrowser and the file panel that were
[wmaker-crm.git] / WINGs / wlist.c
blob27cbcb0c9680791594f8d2a4d53d0edb47eb7b3c
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;
141 void
142 WMSetListAllowEmptySelection(WMList *lPtr, Bool flag)
144 lPtr->flags.allowEmptySelection = flag ? 1 : 0;
148 static int
149 comparator(const void *a, const void *b)
151 return (strcmp((*(WMListItem**)a)->text, (*(WMListItem**)b)->text));
155 void
156 WMSortListItems(WMList *lPtr)
158 WMSortArray(lPtr->items, comparator);
160 paintList(lPtr);
165 void
166 WMSortListItemsWithComparer(WMList *lPtr, WMCompareDataProc *func)
168 WMSortArray(lPtr->items, func);
170 paintList(lPtr);
175 WMListItem*
176 WMInsertListItem(WMList *lPtr, int row, char *text)
178 WMListItem *item;
180 CHECK_CLASS(lPtr, WC_List);
182 item = wmalloc(sizeof(WMListItem));
183 memset(item, 0, sizeof(WMListItem));
184 item->text = wstrdup(text);
186 row = WMIN(row, WMGetArrayItemCount(lPtr->items));
188 if (row < 0)
189 WMAddToArray(lPtr->items, item);
190 else
191 WMInsertInArray(lPtr->items, row, item);
193 /* update the scroller when idle, so that we don't waste time
194 * updating it when another item is going to be added later */
195 if (!lPtr->idleID) {
196 lPtr->idleID = WMAddIdleHandler((WMCallback*)updateScroller, lPtr);
199 return item;
203 void
204 WMRemoveListItem(WMList *lPtr, int row)
206 WMListItem *item;
207 int topItem = lPtr->topItem;
208 int selNotify = 0;
210 CHECK_CLASS(lPtr, WC_List);
212 /*wassertr(row>=0 && row<WMGetArrayItemCount(lPtr->items));*/
213 if (row<0 || row>=WMGetArrayItemCount(lPtr->items))
214 return;
216 item = WMGetFromArray(lPtr->items, row);
217 if (item->selected) {
218 WMRemoveFromArray(lPtr->selectedItems, item);
219 selNotify = 1;
222 if (row <= lPtr->topItem+lPtr->fullFitLines+lPtr->flags.dontFitAll)
223 lPtr->topItem--;
224 if (lPtr->topItem < 0)
225 lPtr->topItem = 0;
227 WMDeleteFromArray(lPtr->items, row);
229 if (!lPtr->idleID) {
230 lPtr->idleID = WMAddIdleHandler((WMCallback*)updateScroller, lPtr);
232 if (lPtr->topItem != topItem) {
233 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
235 if (selNotify) {
236 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
241 WMListItem*
242 WMGetListItem(WMList *lPtr, int row)
244 return WMGetFromArray(lPtr->items, row);
248 WMArray*
249 WMGetListItems(WMList *lPtr)
251 return lPtr->items;
255 void
256 WMSetListUserDrawProc(WMList *lPtr, WMListDrawProc *proc)
258 lPtr->flags.userDrawn = 1;
259 lPtr->draw = proc;
263 void
264 WMSetListUserDrawItemHeight(WMList *lPtr, unsigned short height)
266 assert(height > 0);
268 lPtr->flags.userItemHeight = 1;
269 lPtr->itemHeight = height;
271 updateGeometry(lPtr);
275 void
276 WMClearList(WMList *lPtr)
278 int selNo = WMGetArrayItemCount(lPtr->selectedItems);
280 WMEmptyArray(lPtr->selectedItems);
281 WMEmptyArray(lPtr->items);
283 lPtr->topItem = 0;
285 if (!lPtr->idleID) {
286 WMDeleteIdleHandler(lPtr->idleID);
287 lPtr->idleID = NULL;
289 if (lPtr->view->flags.realized) {
290 updateScroller(lPtr);
292 if (selNo > 0) {
293 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
298 void
299 WMSetListAction(WMList *lPtr, WMAction *action, void *clientData)
301 lPtr->action = action;
302 lPtr->clientData = clientData;
306 void
307 WMSetListDoubleAction(WMList *lPtr, WMAction *action, void *clientData)
309 lPtr->doubleAction = action;
310 lPtr->doubleClientData = clientData;
314 WMArray*
315 WMGetListSelectedItems(WMList *lPtr)
317 return lPtr->selectedItems;
321 WMListItem*
322 WMGetListSelectedItem(WMList *lPtr)
324 return WMGetFromArray(lPtr->selectedItems, 0);
329 WMGetListSelectedItemRow(WMList *lPtr)
331 WMListItem *item = WMGetFromArray(lPtr->selectedItems, 0);
333 return (item!=NULL ? WMGetFirstInArray(lPtr->items, item) : WLNotFound);
338 WMGetListItemHeight(WMList *lPtr)
340 return lPtr->itemHeight;
344 void
345 WMSetListPosition(WMList *lPtr, int row)
347 lPtr->topItem = row;
348 if (lPtr->topItem + lPtr->fullFitLines > WMGetArrayItemCount(lPtr->items))
349 lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines;
351 if (lPtr->topItem < 0)
352 lPtr->topItem = 0;
354 if (lPtr->view->flags.realized)
355 updateScroller(lPtr);
359 void
360 WMSetListBottomPosition(WMList *lPtr, int row)
362 if (WMGetArrayItemCount(lPtr->items) > lPtr->fullFitLines) {
363 lPtr->topItem = row - lPtr->fullFitLines;
364 if (lPtr->topItem < 0)
365 lPtr->topItem = 0;
366 if (lPtr->view->flags.realized)
367 updateScroller(lPtr);
373 WMGetListNumberOfRows(WMList *lPtr)
375 return WMGetArrayItemCount(lPtr->items);
380 WMGetListPosition(WMList *lPtr)
382 return lPtr->topItem;
386 Bool
387 WMListAllowsMultipleSelection(WMList *lPtr)
389 return lPtr->flags.allowMultipleSelection;
393 Bool
394 WMListAllowsEmptySelection(WMList *lPtr)
396 return lPtr->flags.allowEmptySelection;
400 static void
401 scrollByAmount(WMList *lPtr, int amount)
403 int itemCount = WMGetArrayItemCount(lPtr->items);
405 if ((amount < 0 && lPtr->topItem > 0) ||
406 (amount > 0 && (lPtr->topItem + lPtr->fullFitLines < itemCount))) {
408 lPtr->topItem += amount;
409 if (lPtr->topItem < 0)
410 lPtr->topItem = 0;
411 if (lPtr->topItem + lPtr->fullFitLines > itemCount)
412 lPtr->topItem = itemCount - lPtr->fullFitLines;
414 updateScroller(lPtr);
419 static void
420 vScrollCallBack(WMWidget *scroller, void *self)
422 WMList *lPtr = (WMList*)self;
423 int height;
424 int oldTopItem = lPtr->topItem;
425 int itemCount = WMGetArrayItemCount(lPtr->items);
427 height = lPtr->view->size.height - 4;
429 switch (WMGetScrollerHitPart((WMScroller*)scroller)) {
430 case WSDecrementLine:
431 scrollByAmount(lPtr, -1);
432 break;
434 case WSIncrementLine:
435 scrollByAmount(lPtr, 1);
436 break;
438 case WSDecrementPage:
439 scrollByAmount(lPtr, -lPtr->fullFitLines+(1-lPtr->flags.dontFitAll)+1);
440 break;
442 case WSIncrementPage:
443 scrollByAmount(lPtr, lPtr->fullFitLines-(1-lPtr->flags.dontFitAll)-1);
444 break;
446 case WSDecrementWheel:
447 scrollByAmount(lPtr, -lPtr->fullFitLines / 3);
448 break;
450 case WSIncrementWheel:
451 scrollByAmount(lPtr, lPtr->fullFitLines / 3);
452 break;
454 case WSKnob:
455 lPtr->topItem = WMGetScrollerValue(lPtr->vScroller) *
456 (float)(itemCount - lPtr->fullFitLines);
458 if (oldTopItem != lPtr->topItem)
459 paintList(lPtr); // use updateScroller(lPtr) here?
460 break;
462 case WSKnobSlot:
463 case WSNoPart:
464 default:
465 /* do nothing */
466 break;
469 if (lPtr->topItem != oldTopItem)
470 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
474 static void
475 paintItem(List *lPtr, int index)
477 WMView *view = lPtr->view;
478 W_Screen *scr = view->screen;
479 int width, height, x, y;
480 WMListItem *itemPtr;
482 itemPtr = WMGetFromArray(lPtr->items, index);
484 width = lPtr->view->size.width - 2 - 19;
485 height = lPtr->itemHeight;
486 x = 19;
487 y = 2 + (index-lPtr->topItem) * lPtr->itemHeight + 1;
489 if (lPtr->flags.userDrawn) {
490 WMRect rect;
491 int flags;
493 rect.size.width = width;
494 rect.size.height = height;
495 rect.pos.x = x;
496 rect.pos.y = y;
498 flags = itemPtr->uflags;
499 if (itemPtr->disabled)
500 flags |= WLDSDisabled;
501 if (itemPtr->selected)
502 flags |= WLDSSelected;
503 if (itemPtr->isBranch)
504 flags |= WLDSIsBranch;
506 if (lPtr->draw)
507 (*lPtr->draw)(lPtr, index, view->window, itemPtr->text, flags,
508 &rect);
509 } else {
510 if (itemPtr->selected) {
511 XFillRectangle(scr->display, view->window, WMColorGC(scr->white),
512 x, y, width, height);
513 } else {
514 XClearArea(scr->display, view->window, x, y, width, height, False);
517 W_PaintText(view, view->window, scr->normalFont, x+4, y, width,
518 WALeft, WMColorGC(scr->black), False,
519 itemPtr->text, strlen(itemPtr->text));
522 if ((index-lPtr->topItem+lPtr->fullFitLines)*lPtr->itemHeight >
523 lPtr->view->size.height-2) {
524 W_DrawRelief(lPtr->view->screen, lPtr->view->window, 0, 0,
525 lPtr->view->size.width, lPtr->view->size.height,
526 WRSunken);
532 static void
533 paintList(List *lPtr)
535 W_Screen *scrPtr = lPtr->view->screen;
536 int i, lim;
538 if (!lPtr->view->flags.mapped)
539 return;
541 if (WMGetArrayItemCount(lPtr->items) > 0) {
542 if (lPtr->topItem+lPtr->fullFitLines+lPtr->flags.dontFitAll
543 > WMGetArrayItemCount(lPtr->items)) {
545 lim = WMGetArrayItemCount(lPtr->items) - lPtr->topItem;
546 XClearArea(scrPtr->display, lPtr->view->window, 19,
547 2+lim*lPtr->itemHeight, lPtr->view->size.width-21,
548 lPtr->view->size.height-lim*lPtr->itemHeight-3, False);
549 } else {
550 lim = lPtr->fullFitLines + lPtr->flags.dontFitAll;
552 for (i = lPtr->topItem; i < lPtr->topItem + lim; i++) {
553 paintItem(lPtr, i);
555 } else {
556 XClearWindow(scrPtr->display, lPtr->view->window);
558 W_DrawRelief(scrPtr, lPtr->view->window, 0, 0, lPtr->view->size.width,
559 lPtr->view->size.height, WRSunken);
562 #if 0
563 static void
564 scrollTo(List *lPtr, int newTop)
568 #endif
570 static void
571 updateScroller(List *lPtr)
573 float knobProportion, floatValue, tmp;
574 int count = WMGetArrayItemCount(lPtr->items);
576 if (lPtr->idleID)
577 WMDeleteIdleHandler(lPtr->idleID);
578 lPtr->idleID = NULL;
580 paintList(lPtr);
582 if (count == 0 || count <= lPtr->fullFitLines)
583 WMSetScrollerParameters(lPtr->vScroller, 0, 1);
584 else {
585 tmp = lPtr->fullFitLines;
586 knobProportion = tmp/(float)count;
588 floatValue = (float)lPtr->topItem/(float)(count - lPtr->fullFitLines);
590 WMSetScrollerParameters(lPtr->vScroller, floatValue, knobProportion);
595 static void
596 handleEvents(XEvent *event, void *data)
598 List *lPtr = (List*)data;
600 CHECK_CLASS(data, WC_List);
603 switch (event->type) {
604 case Expose:
605 if (event->xexpose.count!=0)
606 break;
607 paintList(lPtr);
608 break;
610 case DestroyNotify:
611 destroyList(lPtr);
612 break;
618 static int
619 matchTitle(void *item, void *title)
621 return (strcmp(((WMListItem*)item)->text, (char*)title)==0 ? 1 : 0);
626 WMFindRowOfListItemWithTitle(WMList *lPtr, char *title)
628 return WMFindInArray(lPtr->items, matchTitle, title);
632 void
633 WMSelectListItem(WMList *lPtr, int row)
635 WMListItem *item;
637 if (row >= WMGetArrayItemCount(lPtr->items))
638 return;
640 if (row < 0) {
641 /* row = -1 will deselects all for backward compatibility.
642 * will be removed later. -Dan */
643 WMUnselectAllListItems(lPtr);
644 return;
647 item = WMGetFromArray(lPtr->items, row);
648 if (item->selected)
649 return; /* Return if already selected */
651 if (!lPtr->flags.allowMultipleSelection) {
652 /* unselect previous selected items */
653 int foo = lPtr->flags.allowEmptySelection;
655 lPtr->flags.allowEmptySelection = 1;
656 WMUnselectAllListItems(lPtr);
657 lPtr->flags.allowEmptySelection = foo;
660 /* select item */
661 item->selected = 1;
662 WMAddToArray(lPtr->selectedItems, item);
664 if (lPtr->view->flags.mapped && row>=lPtr->topItem
665 && row<=lPtr->topItem+lPtr->fullFitLines) {
666 paintItem(lPtr, row);
669 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
673 void
674 WMUnselectListItem(WMList *lPtr, int row)
676 WMListItem *item = WMGetFromArray(lPtr->items, row);
678 if (!item || !item->selected)
679 return;
681 if (!lPtr->flags.allowEmptySelection &&
682 WMGetArrayItemCount(lPtr->selectedItems) <= 1) {
683 return;
686 item->selected = 0;
687 WMRemoveFromArray(lPtr->selectedItems, item);
689 if (lPtr->view->flags.mapped && row>=lPtr->topItem
690 && row<=lPtr->topItem+lPtr->fullFitLines) {
691 paintItem(lPtr, row);
694 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
698 void
699 WMSelectListItemsInRange(WMList *lPtr, WMRange range)
701 WMListItem *item;
702 int position = range.position, k = 1, notify = 0;
703 int total = WMGetArrayItemCount(lPtr->items);
705 if (!lPtr->flags.allowMultipleSelection)
706 return;
707 if (range.count==0)
708 return; /* Nothing to select */
710 if (range.count < 0) {
711 range.count = -range.count;
712 k = -1;
715 for (; range.count>0 && position>=0 && position<total; range.count--) {
716 item = WMGetFromArray(lPtr->items, position);
717 if (!item->selected) {
718 item->selected = 1;
719 WMAddToArray(lPtr->selectedItems, item);
720 if (lPtr->view->flags.mapped && position>=lPtr->topItem
721 && position<=lPtr->topItem+lPtr->fullFitLines) {
722 paintItem(lPtr, position);
724 notify = 1;
726 position += k;
729 if (notify) {
730 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
735 void
736 WMSetListSelectionToRange(WMList *lPtr, WMRange range)
738 WMListItem *item;
739 int mark1, mark2, i, k;
740 int position = range.position, notify = 0;
741 int total = WMGetArrayItemCount(lPtr->items);
743 if (!lPtr->flags.allowMultipleSelection)
744 return;
746 if (range.count==0) {
747 WMUnselectAllListItems(lPtr);
748 return;
751 if (range.count < 0) {
752 mark1 = range.position + range.count + 1;
753 mark2 = range.position + 1;
754 range.count = -range.count;
755 k = -1;
756 } else {
757 mark1 = range.position;
758 mark2 = range.position + range.count;
759 k = 1;
761 if (mark1 > total)
762 mark1 = total;
763 if (mark2 < 0)
764 mark2 = 0;
766 WMEmptyArray(lPtr->selectedItems);
768 for (i=0; i<mark1; i++) {
769 item = WMGetFromArray(lPtr->items, i);
770 if (item->selected) {
771 item->selected = 0;
772 if (lPtr->view->flags.mapped && i>=lPtr->topItem
773 && i<=lPtr->topItem+lPtr->fullFitLines) {
774 paintItem(lPtr, i);
776 notify = 1;
779 for (; range.count>0 && position>=0 && position<total; range.count--) {
780 item = WMGetFromArray(lPtr->items, position);
781 if (!item->selected) {
782 item->selected = 1;
783 if (lPtr->view->flags.mapped && position>=lPtr->topItem
784 && position<=lPtr->topItem+lPtr->fullFitLines) {
785 paintItem(lPtr, position);
787 notify = 1;
789 WMAddToArray(lPtr->selectedItems, item);
790 position += k;
792 for (i=mark2; i<total; i++) {
793 item = WMGetFromArray(lPtr->items, i);
794 if (item->selected) {
795 item->selected = 0;
796 if (lPtr->view->flags.mapped && i>=lPtr->topItem
797 && i<=lPtr->topItem+lPtr->fullFitLines) {
798 paintItem(lPtr, i);
800 notify = 1;
804 if (notify) {
805 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
810 void
811 WMSelectAllListItems(WMList *lPtr)
813 int i;
814 WMListItem *item;
816 if (!lPtr->flags.allowMultipleSelection)
817 return;
819 if (WMGetArrayItemCount(lPtr->items) ==
820 WMGetArrayItemCount(lPtr->selectedItems)) {
821 return; /* All items are selected already */
824 WMFreeArray(lPtr->selectedItems);
825 lPtr->selectedItems = WMCreateArrayWithArray(lPtr->items);
827 for (i=0; i<WMGetArrayItemCount(lPtr->items); i++) {
828 item = WMGetFromArray(lPtr->items, i);
829 if (!item->selected) {
830 item->selected = 1;
831 if (lPtr->view->flags.mapped && i>=lPtr->topItem
832 && i<=lPtr->topItem+lPtr->fullFitLines) {
833 paintItem(lPtr, i);
838 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
842 void
843 WMUnselectAllListItems(WMList *lPtr)
845 int i, keep;
846 WMListItem *item, *keepItem;
848 keep = lPtr->flags.allowEmptySelection ? 0 : 1;
850 if (WMGetArrayItemCount(lPtr->selectedItems) == keep)
851 return;
853 keepItem = (keep==1 ? WMGetFromArray(lPtr->selectedItems, 0) : NULL);
855 for (i=0; i<WMGetArrayItemCount(lPtr->items); i++) {
856 item = WMGetFromArray(lPtr->items, i);
857 if (item!=keepItem && item->selected) {
858 item->selected = 0;
859 if (lPtr->view->flags.mapped && i>=lPtr->topItem
860 && i<=lPtr->topItem+lPtr->fullFitLines) {
861 paintItem(lPtr, i);
866 WMEmptyArray(lPtr->selectedItems);
867 if (keepItem!=NULL)
868 WMAddToArray(lPtr->selectedItems, keepItem);
870 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
874 static int
875 getItemIndexAt(List *lPtr, int clickY)
877 int index;
879 index = (clickY - 2) / lPtr->itemHeight + lPtr->topItem;
881 if (index < 0 || index >= WMGetArrayItemCount(lPtr->items))
882 return -1;
884 return index;
888 static void
889 toggleItemSelection(WMList *lPtr, int index)
891 WMListItem *item = WMGetFromArray(lPtr->items, index);
893 if (item && item->selected) {
894 WMUnselectListItem(lPtr, index);
895 } else {
896 WMSelectListItem(lPtr, index);
901 static void
902 handleActionEvents(XEvent *event, void *data)
904 List *lPtr = (List*)data;
905 int tmp;
906 int topItem = lPtr->topItem;
907 static int lastClicked = -1, prevItem = -1;
909 CHECK_CLASS(data, WC_List);
911 switch (event->type) {
912 case ButtonRelease:
913 /* Ignore mouse wheel events, they're not "real" button events */
914 if (event->xbutton.button == WINGsConfiguration.mouseWheelUp ||
915 event->xbutton.button == WINGsConfiguration.mouseWheelDown) {
916 break;
919 lPtr->flags.buttonPressed = 0;
920 tmp = getItemIndexAt(lPtr, event->xbutton.y);
922 if (tmp >= 0) {
923 if (lPtr->action)
924 (*lPtr->action)(lPtr, lPtr->clientData);
927 if (!(event->xbutton.state & ShiftMask))
928 lastClicked = prevItem = tmp;
930 break;
932 case EnterNotify:
933 lPtr->flags.buttonPressed = lPtr->flags.buttonWasPressed;
934 lPtr->flags.buttonWasPressed = 0;
935 break;
937 case LeaveNotify:
938 lPtr->flags.buttonWasPressed = lPtr->flags.buttonPressed;
939 lPtr->flags.buttonPressed = 0;
940 break;
942 case ButtonPress:
943 if (event->xbutton.x <= WMWidgetWidth(lPtr->vScroller))
944 break;
945 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown ||
946 event->xbutton.button == WINGsConfiguration.mouseWheelUp) {
947 int amount = 0;
949 if (event->xbutton.state & ControlMask) {
950 amount = lPtr->fullFitLines-(1-lPtr->flags.dontFitAll)-1;
951 } else if (event->xbutton.state & ShiftMask) {
952 amount = 1;
953 } else {
954 amount = lPtr->fullFitLines / 3;
955 if (amount == 0)
956 amount++;
958 if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
959 amount = -amount;
961 scrollByAmount(lPtr, amount);
962 break;
965 tmp = getItemIndexAt(lPtr, event->xbutton.y);
966 lPtr->flags.buttonPressed = 1;
968 if (tmp >= 0) {
969 if (tmp == lastClicked && WMIsDoubleClick(event)) {
970 WMSelectListItem(lPtr, tmp);
971 if (lPtr->doubleAction)
972 (*lPtr->doubleAction)(lPtr, lPtr->doubleClientData);
973 } else {
974 if (!lPtr->flags.allowMultipleSelection) {
975 if (event->xbutton.state & ControlMask) {
976 toggleItemSelection(lPtr, tmp);
977 } else {
978 WMSelectListItem(lPtr, tmp);
980 } else {
981 WMRange range;
982 WMListItem *lastSel;
984 if (event->xbutton.state & ControlMask) {
985 toggleItemSelection(lPtr, tmp);
986 } else if (event->xbutton.state & ShiftMask) {
987 if (WMGetArrayItemCount(lPtr->selectedItems) == 0) {
988 WMSelectListItem(lPtr, tmp);
989 } else {
990 lastSel = WMGetFromArray(lPtr->items, lastClicked);
991 range.position = WMGetFirstInArray(lPtr->items,
992 lastSel);
993 if (tmp >= range.position)
994 range.count = tmp - range.position + 1;
995 else
996 range.count = tmp - range.position - 1;
998 WMSetListSelectionToRange(lPtr, range);
1000 } else {
1001 range.position = tmp;
1002 range.count = 1;
1003 WMSetListSelectionToRange(lPtr, range);
1009 if (!(event->xbutton.state & ShiftMask))
1010 lastClicked = prevItem = tmp;
1012 break;
1014 case MotionNotify:
1015 if (lPtr->flags.buttonPressed) {
1016 tmp = getItemIndexAt(lPtr, event->xmotion.y);
1017 if (tmp>=0 && tmp!=prevItem) {
1018 if (lPtr->flags.allowMultipleSelection) {
1019 WMRange range;
1021 range.position = lastClicked;
1022 if (tmp >= lastClicked)
1023 range.count = tmp - lastClicked + 1;
1024 else
1025 range.count = tmp - lastClicked - 1;
1026 WMSetListSelectionToRange(lPtr, range);
1027 } else {
1028 WMSelectListItem(lPtr, tmp);
1031 prevItem = tmp;
1033 break;
1035 if (lPtr->topItem != topItem)
1036 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
1040 static void
1041 updateGeometry(WMList *lPtr)
1043 lPtr->fullFitLines = (lPtr->view->size.height - 4) / lPtr->itemHeight;
1044 if (lPtr->fullFitLines * lPtr->itemHeight < lPtr->view->size.height - 4) {
1045 lPtr->flags.dontFitAll = 1;
1046 } else {
1047 lPtr->flags.dontFitAll = 0;
1050 if (WMGetArrayItemCount(lPtr->items) - lPtr->topItem <= lPtr->fullFitLines) {
1051 lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines;
1052 if (lPtr->topItem < 0)
1053 lPtr->topItem = 0;
1056 updateScroller(lPtr);
1060 static void
1061 didResizeList(W_ViewDelegate *self, WMView *view)
1063 WMList *lPtr = (WMList*)view->self;
1065 WMResizeWidget(lPtr->vScroller, 1, view->size.height-2);
1067 updateGeometry(lPtr);
1071 static void
1072 destroyList(List *lPtr)
1074 if (lPtr->idleID)
1075 WMDeleteIdleHandler(lPtr->idleID);
1076 lPtr->idleID = NULL;
1078 WMFreeArray(lPtr->items);
1080 wfree(lPtr);