#include "WINGsP.h" char *WMListDidScrollNotification = "WMListDidScrollNotification"; char *WMListSelectionDidChangeNotification = "WMListSelectionDidChangeNotification"; typedef struct W_List { W_Class widgetClass; W_View *view; WMArray *items; /* list of WMListItem */ WMArray *selectedItems; /* list of selected WMListItems */ short itemHeight; int topItem; /* index of first visible item */ short fullFitLines; /* no of lines that fit entirely */ void *clientData; WMAction *action; void *doubleClientData; WMAction *doubleAction; WMListDrawProc *draw; WMHandlerID *idleID; /* for updating the scroller after adding elements */ WMHandlerID *selectID; /* for selecting items in list while scrolling */ WMScroller *vScroller; struct { unsigned int allowMultipleSelection:1; unsigned int allowEmptySelection:1; unsigned int userDrawn:1; unsigned int userItemHeight:1; unsigned int dontFitAll:1; /* 1 = last item won't be fully visible */ unsigned int redrawPending:1; unsigned int buttonPressed:1; unsigned int buttonWasPressed:1; } flags; } List; #define DEFAULT_WIDTH 150 #define DEFAULT_HEIGHT 150 #define SCROLL_DELAY 100 static void destroyList(List *lPtr); static void paintList(List *lPtr); static void handleEvents(XEvent *event, void *data); static void handleActionEvents(XEvent *event, void *data); static void updateScroller(void *data); static void scrollForwardSelecting(void *data); static void scrollBackwardSelecting(void *data); static void vScrollCallBack(WMWidget *scroller, void *self); static void toggleItemSelection(WMList *lPtr, int index); static void updateGeometry(WMList *lPtr); static void didResizeList(); static void unselectAllListItems(WMList *lPtr, WMListItem *exceptThis); W_ViewDelegate _ListViewDelegate = { NULL, NULL, didResizeList, NULL, NULL }; static void releaseItem(void *data) { WMListItem *item = (WMListItem*)data; if (item->text) wfree(item->text); wfree(item); } WMList* WMCreateList(WMWidget *parent) { List *lPtr; W_Screen *scrPtr = W_VIEW(parent)->screen; lPtr = wmalloc(sizeof(List)); memset(lPtr, 0, sizeof(List)); lPtr->widgetClass = WC_List; lPtr->view = W_CreateView(W_VIEW(parent)); if (!lPtr->view) { wfree(lPtr); return NULL; } lPtr->view->self = lPtr; lPtr->view->delegate = &_ListViewDelegate; WMCreateEventHandler(lPtr->view, ExposureMask|StructureNotifyMask |ClientMessageMask, handleEvents, lPtr); WMCreateEventHandler(lPtr->view, ButtonPressMask|ButtonReleaseMask |EnterWindowMask|LeaveWindowMask|ButtonMotionMask, handleActionEvents, lPtr); lPtr->itemHeight = WMFontHeight(scrPtr->normalFont) + 1; lPtr->items = WMCreateArrayWithDestructor(4, releaseItem); lPtr->selectedItems = WMCreateArray(4); /* create the vertical scroller */ lPtr->vScroller = WMCreateScroller(lPtr); WMMoveWidget(lPtr->vScroller, 1, 1); WMSetScrollerArrowsPosition(lPtr->vScroller, WSAMaxEnd); WMSetScrollerAction(lPtr->vScroller, vScrollCallBack, lPtr); /* make the scroller map itself when it's realized */ WMMapWidget(lPtr->vScroller); W_ResizeView(lPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT); return lPtr; } void WMSetListAllowMultipleSelection(WMList *lPtr, Bool flag) { lPtr->flags.allowMultipleSelection = flag ? 1 : 0; } void WMSetListAllowEmptySelection(WMList *lPtr, Bool flag) { lPtr->flags.allowEmptySelection = flag ? 1 : 0; } static int comparator(const void *a, const void *b) { return (strcmp((*(WMListItem**)a)->text, (*(WMListItem**)b)->text)); } void WMSortListItems(WMList *lPtr) { WMSortArray(lPtr->items, comparator); paintList(lPtr); } void WMSortListItemsWithComparer(WMList *lPtr, WMCompareDataProc *func) { WMSortArray(lPtr->items, func); paintList(lPtr); } WMListItem* WMInsertListItem(WMList *lPtr, int row, char *text) { WMListItem *item; CHECK_CLASS(lPtr, WC_List); item = wmalloc(sizeof(WMListItem)); memset(item, 0, sizeof(WMListItem)); item->text = wstrdup(text); row = WMIN(row, WMGetArrayItemCount(lPtr->items)); if (row < 0) WMAddToArray(lPtr->items, item); else WMInsertInArray(lPtr->items, row, item); /* update the scroller when idle, so that we don't waste time * updating it when another item is going to be added later */ if (!lPtr->idleID) { lPtr->idleID = WMAddIdleHandler((WMCallback*)updateScroller, lPtr); } return item; } void WMRemoveListItem(WMList *lPtr, int row) { WMListItem *item; int topItem = lPtr->topItem; int selNotify = 0; CHECK_CLASS(lPtr, WC_List); /*wassertr(row>=0 && rowitems));*/ if (row<0 || row>=WMGetArrayItemCount(lPtr->items)) return; item = WMGetFromArray(lPtr->items, row); if (item->selected) { WMRemoveFromArray(lPtr->selectedItems, item); selNotify = 1; } if (row <= lPtr->topItem+lPtr->fullFitLines+lPtr->flags.dontFitAll) lPtr->topItem--; if (lPtr->topItem < 0) lPtr->topItem = 0; WMDeleteFromArray(lPtr->items, row); if (!lPtr->idleID) { lPtr->idleID = WMAddIdleHandler((WMCallback*)updateScroller, lPtr); } if (lPtr->topItem != topItem) { WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL); } if (selNotify) { WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } } WMListItem* WMGetListItem(WMList *lPtr, int row) { return WMGetFromArray(lPtr->items, row); } WMArray* WMGetListItems(WMList *lPtr) { return lPtr->items; } void WMSetListUserDrawProc(WMList *lPtr, WMListDrawProc *proc) { lPtr->flags.userDrawn = 1; lPtr->draw = proc; } void WMSetListUserDrawItemHeight(WMList *lPtr, unsigned short height) { assert(height > 0); lPtr->flags.userItemHeight = 1; lPtr->itemHeight = height; updateGeometry(lPtr); } void WMClearList(WMList *lPtr) { int selNo = WMGetArrayItemCount(lPtr->selectedItems); WMEmptyArray(lPtr->selectedItems); WMEmptyArray(lPtr->items); lPtr->topItem = 0; if (!lPtr->idleID) { WMDeleteIdleHandler(lPtr->idleID); lPtr->idleID = NULL; } if (lPtr->selectID) { WMDeleteTimerHandler(lPtr->selectID); lPtr->selectID = NULL; } if (lPtr->view->flags.realized) { updateScroller(lPtr); } if (selNo > 0) { WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } } void WMSetListAction(WMList *lPtr, WMAction *action, void *clientData) { lPtr->action = action; lPtr->clientData = clientData; } void WMSetListDoubleAction(WMList *lPtr, WMAction *action, void *clientData) { lPtr->doubleAction = action; lPtr->doubleClientData = clientData; } WMArray* WMGetListSelectedItems(WMList *lPtr) { return lPtr->selectedItems; } WMListItem* WMGetListSelectedItem(WMList *lPtr) { return WMGetFromArray(lPtr->selectedItems, 0); } int WMGetListSelectedItemRow(WMList *lPtr) { WMListItem *item = WMGetFromArray(lPtr->selectedItems, 0); return (item!=NULL ? WMGetFirstInArray(lPtr->items, item) : WLNotFound); } int WMGetListItemHeight(WMList *lPtr) { return lPtr->itemHeight; } void WMSetListPosition(WMList *lPtr, int row) { lPtr->topItem = row; if (lPtr->topItem + lPtr->fullFitLines > WMGetArrayItemCount(lPtr->items)) lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines; if (lPtr->topItem < 0) lPtr->topItem = 0; if (lPtr->view->flags.realized) updateScroller(lPtr); } void WMSetListBottomPosition(WMList *lPtr, int row) { if (WMGetArrayItemCount(lPtr->items) > lPtr->fullFitLines) { lPtr->topItem = row - lPtr->fullFitLines; if (lPtr->topItem < 0) lPtr->topItem = 0; if (lPtr->view->flags.realized) updateScroller(lPtr); } } int WMGetListNumberOfRows(WMList *lPtr) { return WMGetArrayItemCount(lPtr->items); } int WMGetListPosition(WMList *lPtr) { return lPtr->topItem; } Bool WMListAllowsMultipleSelection(WMList *lPtr) { return lPtr->flags.allowMultipleSelection; } Bool WMListAllowsEmptySelection(WMList *lPtr) { return lPtr->flags.allowEmptySelection; } static void scrollByAmount(WMList *lPtr, int amount) { int itemCount = WMGetArrayItemCount(lPtr->items); if ((amount < 0 && lPtr->topItem > 0) || (amount > 0 && (lPtr->topItem + lPtr->fullFitLines < itemCount))) { lPtr->topItem += amount; if (lPtr->topItem < 0) lPtr->topItem = 0; if (lPtr->topItem + lPtr->fullFitLines > itemCount) lPtr->topItem = itemCount - lPtr->fullFitLines; updateScroller(lPtr); } } static void vScrollCallBack(WMWidget *scroller, void *self) { WMList *lPtr = (WMList*)self; int height; int oldTopItem = lPtr->topItem; int itemCount = WMGetArrayItemCount(lPtr->items); height = lPtr->view->size.height - 4; switch (WMGetScrollerHitPart((WMScroller*)scroller)) { case WSDecrementLine: scrollByAmount(lPtr, -1); break; case WSIncrementLine: scrollByAmount(lPtr, 1); break; case WSDecrementPage: scrollByAmount(lPtr, -lPtr->fullFitLines+(1-lPtr->flags.dontFitAll)+1); break; case WSIncrementPage: scrollByAmount(lPtr, lPtr->fullFitLines-(1-lPtr->flags.dontFitAll)-1); break; case WSDecrementWheel: scrollByAmount(lPtr, -lPtr->fullFitLines / 3); break; case WSIncrementWheel: scrollByAmount(lPtr, lPtr->fullFitLines / 3); break; case WSKnob: lPtr->topItem = WMGetScrollerValue(lPtr->vScroller) * (float)(itemCount - lPtr->fullFitLines); if (oldTopItem != lPtr->topItem) paintList(lPtr); /* use updateScroller(lPtr) here? -Dan */ break; case WSKnobSlot: case WSNoPart: default: /* do nothing */ break; } if (lPtr->topItem != oldTopItem) WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL); } static void paintItem(List *lPtr, int index) { WMView *view = lPtr->view; W_Screen *scr = view->screen; int width, height, x, y; WMListItem *itemPtr; itemPtr = WMGetFromArray(lPtr->items, index); width = lPtr->view->size.width - 2 - 19; height = lPtr->itemHeight; x = 19; y = 2 + (index-lPtr->topItem) * lPtr->itemHeight + 1; if (lPtr->flags.userDrawn) { WMRect rect; int flags; rect.size.width = width; rect.size.height = height; rect.pos.x = x; rect.pos.y = y; flags = itemPtr->uflags; if (itemPtr->disabled) flags |= WLDSDisabled; if (itemPtr->selected) flags |= WLDSSelected; if (itemPtr->isBranch) flags |= WLDSIsBranch; if (lPtr->draw) (*lPtr->draw)(lPtr, index, view->window, itemPtr->text, flags, &rect); } else { if (itemPtr->selected) { XFillRectangle(scr->display, view->window, WMColorGC(scr->white), x, y, width, height); } else { XClearArea(scr->display, view->window, x, y, width, height, False); } W_PaintText(view, view->window, scr->normalFont, x+4, y, width, WALeft, WMColorGC(scr->black), False, itemPtr->text, strlen(itemPtr->text)); } if ((index-lPtr->topItem+lPtr->fullFitLines)*lPtr->itemHeight > lPtr->view->size.height-2) { W_DrawRelief(lPtr->view->screen, lPtr->view->window, 0, 0, lPtr->view->size.width, lPtr->view->size.height, WRSunken); } } static void paintList(List *lPtr) { W_Screen *scrPtr = lPtr->view->screen; int i, lim; if (!lPtr->view->flags.mapped) return; if (WMGetArrayItemCount(lPtr->items) > 0) { if (lPtr->topItem+lPtr->fullFitLines+lPtr->flags.dontFitAll > WMGetArrayItemCount(lPtr->items)) { lim = WMGetArrayItemCount(lPtr->items) - lPtr->topItem; XClearArea(scrPtr->display, lPtr->view->window, 19, 2+lim*lPtr->itemHeight, lPtr->view->size.width-21, lPtr->view->size.height-lim*lPtr->itemHeight-3, False); } else { lim = lPtr->fullFitLines + lPtr->flags.dontFitAll; } for (i = lPtr->topItem; i < lPtr->topItem + lim; i++) { paintItem(lPtr, i); } } else { XClearWindow(scrPtr->display, lPtr->view->window); } W_DrawRelief(scrPtr, lPtr->view->window, 0, 0, lPtr->view->size.width, lPtr->view->size.height, WRSunken); } #if 0 static void scrollTo(List *lPtr, int newTop) { } #endif static void updateScroller(void *data) { List *lPtr = (List*)data; float knobProportion, floatValue, tmp; int count = WMGetArrayItemCount(lPtr->items); if (lPtr->idleID) WMDeleteIdleHandler(lPtr->idleID); lPtr->idleID = NULL; paintList(lPtr); if (count == 0 || count <= lPtr->fullFitLines) WMSetScrollerParameters(lPtr->vScroller, 0, 1); else { tmp = lPtr->fullFitLines; knobProportion = tmp/(float)count; floatValue = (float)lPtr->topItem/(float)(count - lPtr->fullFitLines); WMSetScrollerParameters(lPtr->vScroller, floatValue, knobProportion); } } static void scrollForwardSelecting(void *data) { List *lPtr = (List*)data; int lastSelected; lastSelected = lPtr->topItem+lPtr->fullFitLines+lPtr->flags.dontFitAll-1; if (lastSelected >= WMGetArrayItemCount(lPtr->items)-1) { lPtr->selectID = NULL; if (lPtr->flags.dontFitAll) scrollByAmount(lPtr, 1); return; } /* selecting NEEDS to be done before scrolling to avoid flickering */ if (lPtr->flags.allowMultipleSelection) { WMListItem *item; WMRange range; item = WMGetFromArray(lPtr->selectedItems, 0); range.position = WMGetFirstInArray(lPtr->items, item); if (lastSelected+1 >= range.position) { range.count = lastSelected - range.position + 2; } else { range.count = lastSelected - range.position; } WMSetListSelectionToRange(lPtr, range); } else { WMSelectListItem(lPtr, lastSelected+1); } scrollByAmount(lPtr, 1); lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollForwardSelecting, lPtr); } static void scrollBackwardSelecting(void *data) { List *lPtr = (List*)data; if (lPtr->topItem < 1) { lPtr->selectID = NULL; return; } /* selecting NEEDS to be done before scrolling to avoid flickering */ if (lPtr->flags.allowMultipleSelection) { WMListItem *item; WMRange range; item = WMGetFromArray(lPtr->selectedItems, 0); range.position = WMGetFirstInArray(lPtr->items, item); if (lPtr->topItem-1 >= range.position) { range.count = lPtr->topItem - range.position; } else { range.count = lPtr->topItem - range.position - 2; } WMSetListSelectionToRange(lPtr, range); } else { WMSelectListItem(lPtr, lPtr->topItem-1); } scrollByAmount(lPtr, -1); lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollBackwardSelecting, lPtr); } static void handleEvents(XEvent *event, void *data) { List *lPtr = (List*)data; CHECK_CLASS(data, WC_List); switch (event->type) { case Expose: if (event->xexpose.count!=0) break; paintList(lPtr); break; case DestroyNotify: destroyList(lPtr); break; } } static int matchTitle(void *item, void *title) { return (strcmp(((WMListItem*)item)->text, (char*)title)==0 ? 1 : 0); } int WMFindRowOfListItemWithTitle(WMList *lPtr, char *title) { return WMFindInArray(lPtr->items, matchTitle, title); } void WMSelectListItem(WMList *lPtr, int row) { WMListItem *item; if (row >= WMGetArrayItemCount(lPtr->items)) return; if (row < 0) { /* row = -1 will deselects all for backward compatibility. * will be removed later. -Dan */ WMUnselectAllListItems(lPtr); return; } item = WMGetFromArray(lPtr->items, row); if (item->selected) return; /* Return if already selected */ if (!lPtr->flags.allowMultipleSelection) { /* unselect previous selected items */ unselectAllListItems(lPtr, NULL); } /* select item */ item->selected = 1; WMAddToArray(lPtr->selectedItems, item); if (lPtr->view->flags.mapped && row>=lPtr->topItem && row<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, row); } WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } void WMUnselectListItem(WMList *lPtr, int row) { WMListItem *item = WMGetFromArray(lPtr->items, row); if (!item || !item->selected) return; if (!lPtr->flags.allowEmptySelection && WMGetArrayItemCount(lPtr->selectedItems) <= 1) { return; } item->selected = 0; WMRemoveFromArray(lPtr->selectedItems, item); if (lPtr->view->flags.mapped && row>=lPtr->topItem && row<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, row); } WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } void WMSelectListItemsInRange(WMList *lPtr, WMRange range) { WMListItem *item; int position = range.position, k = 1, notify = 0; int total = WMGetArrayItemCount(lPtr->items); if (!lPtr->flags.allowMultipleSelection) return; if (range.count==0) return; /* Nothing to select */ if (range.count < 0) { range.count = -range.count; k = -1; } for (; range.count>0 && position>=0 && positionitems, position); if (!item->selected) { item->selected = 1; WMAddToArray(lPtr->selectedItems, item); if (lPtr->view->flags.mapped && position>=lPtr->topItem && position<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, position); } notify = 1; } position += k; } if (notify) { WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } } void WMSetListSelectionToRange(WMList *lPtr, WMRange range) { WMListItem *item; int mark1, mark2, i, k; int position = range.position, notify = 0; int total = WMGetArrayItemCount(lPtr->items); if (!lPtr->flags.allowMultipleSelection) return; if (range.count==0) { WMUnselectAllListItems(lPtr); return; } if (range.count < 0) { mark1 = range.position + range.count + 1; mark2 = range.position + 1; range.count = -range.count; k = -1; } else { mark1 = range.position; mark2 = range.position + range.count; k = 1; } if (mark1 > total) mark1 = total; if (mark2 < 0) mark2 = 0; WMEmptyArray(lPtr->selectedItems); for (i=0; iitems, i); if (item->selected) { item->selected = 0; if (lPtr->view->flags.mapped && i>=lPtr->topItem && i<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, i); } notify = 1; } } for (; range.count>0 && position>=0 && positionitems, position); if (!item->selected) { item->selected = 1; if (lPtr->view->flags.mapped && position>=lPtr->topItem && position<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, position); } notify = 1; } WMAddToArray(lPtr->selectedItems, item); position += k; } for (i=mark2; iitems, i); if (item->selected) { item->selected = 0; if (lPtr->view->flags.mapped && i>=lPtr->topItem && i<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, i); } notify = 1; } } if (notify) { WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } } void WMSelectAllListItems(WMList *lPtr) { int i; WMListItem *item; if (!lPtr->flags.allowMultipleSelection) return; if (WMGetArrayItemCount(lPtr->items) == WMGetArrayItemCount(lPtr->selectedItems)) { return; /* All items are selected already */ } WMFreeArray(lPtr->selectedItems); lPtr->selectedItems = WMCreateArrayWithArray(lPtr->items); for (i=0; iitems); i++) { item = WMGetFromArray(lPtr->items, i); if (!item->selected) { item->selected = 1; if (lPtr->view->flags.mapped && i>=lPtr->topItem && i<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, i); } } } WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } /* * Be careful from where you call this function! It doesn't honor the * allowEmptySelection flag and doesn't send a notification about selection * change! You need to manage these in the functions from where you call it. * * This will unselect all items if exceptThis is NULL, else will keep * exceptThis selected. * Make sure that exceptThis is one of the already selected items if not NULL! * */ static void unselectAllListItems(WMList *lPtr, WMListItem *exceptThis) { int i; WMListItem *item; for (i=0; iitems); i++) { item = WMGetFromArray(lPtr->items, i); if (item!=exceptThis && item->selected) { item->selected = 0; if (lPtr->view->flags.mapped && i>=lPtr->topItem && i<=lPtr->topItem+lPtr->fullFitLines) { paintItem(lPtr, i); } } } WMEmptyArray(lPtr->selectedItems); if (exceptThis!=NULL) { exceptThis->selected = 1; WMAddToArray(lPtr->selectedItems, exceptThis); } } void WMUnselectAllListItems(WMList *lPtr) { int keep; WMListItem *keepItem; keep = lPtr->flags.allowEmptySelection ? 0 : 1; if (WMGetArrayItemCount(lPtr->selectedItems) == keep) return; keepItem = (keep==1 ? WMGetFromArray(lPtr->selectedItems, 0) : NULL); unselectAllListItems(lPtr, keepItem); WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL); } static int getItemIndexAt(List *lPtr, int clickY) { int index; index = (clickY - 2) / lPtr->itemHeight + lPtr->topItem; if (index < 0 || index >= WMGetArrayItemCount(lPtr->items)) return -1; return index; } static void toggleItemSelection(WMList *lPtr, int index) { WMListItem *item = WMGetFromArray(lPtr->items, index); if (item && item->selected) { WMUnselectListItem(lPtr, index); } else { WMSelectListItem(lPtr, index); } } static void handleActionEvents(XEvent *event, void *data) { List *lPtr = (List*)data; int tmp, height; int topItem = lPtr->topItem; static int lastClicked = -1, prevItem = -1; CHECK_CLASS(data, WC_List); switch (event->type) { case ButtonRelease: /* Ignore mouse wheel events, they're not "real" button events */ if (event->xbutton.button == WINGsConfiguration.mouseWheelUp || event->xbutton.button == WINGsConfiguration.mouseWheelDown) { break; } lPtr->flags.buttonPressed = 0; if (lPtr->selectID) { WMDeleteTimerHandler(lPtr->selectID); lPtr->selectID = NULL; } tmp = getItemIndexAt(lPtr, event->xbutton.y); if (tmp >= 0) { if (lPtr->action) (*lPtr->action)(lPtr, lPtr->clientData); } if (!(event->xbutton.state & ShiftMask)) lastClicked = prevItem = tmp; break; case EnterNotify: if (lPtr->selectID) { WMDeleteTimerHandler(lPtr->selectID); lPtr->selectID = NULL; } break; case LeaveNotify: height = WMWidgetHeight(lPtr); if (lPtr->flags.buttonPressed && !lPtr->selectID) { if (event->xcrossing.y >= height) { lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollForwardSelecting, lPtr); } else if (event->xcrossing.y <= 0) { lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollBackwardSelecting, lPtr); } } break; case ButtonPress: if (event->xbutton.x <= WMWidgetWidth(lPtr->vScroller)) break; if (event->xbutton.button == WINGsConfiguration.mouseWheelDown || event->xbutton.button == WINGsConfiguration.mouseWheelUp) { int amount = 0; if (event->xbutton.state & ControlMask) { amount = lPtr->fullFitLines-(1-lPtr->flags.dontFitAll)-1; } else if (event->xbutton.state & ShiftMask) { amount = 1; } else { amount = lPtr->fullFitLines / 3; if (amount == 0) amount++; } if (event->xbutton.button == WINGsConfiguration.mouseWheelUp) amount = -amount; scrollByAmount(lPtr, amount); break; } tmp = getItemIndexAt(lPtr, event->xbutton.y); lPtr->flags.buttonPressed = 1; if (tmp >= 0) { if (tmp == lastClicked && WMIsDoubleClick(event)) { WMSelectListItem(lPtr, tmp); if (lPtr->doubleAction) (*lPtr->doubleAction)(lPtr, lPtr->doubleClientData); } else { if (!lPtr->flags.allowMultipleSelection) { if (event->xbutton.state & ControlMask) { toggleItemSelection(lPtr, tmp); } else { WMSelectListItem(lPtr, tmp); } } else { WMRange range; WMListItem *lastSel; if (event->xbutton.state & ControlMask) { toggleItemSelection(lPtr, tmp); } else if (event->xbutton.state & ShiftMask) { if (WMGetArrayItemCount(lPtr->selectedItems) == 0) { WMSelectListItem(lPtr, tmp); } else { lastSel = WMGetFromArray(lPtr->items, lastClicked); range.position = WMGetFirstInArray(lPtr->items, lastSel); if (tmp >= range.position) range.count = tmp - range.position + 1; else range.count = tmp - range.position - 1; WMSetListSelectionToRange(lPtr, range); } } else { range.position = tmp; range.count = 1; WMSetListSelectionToRange(lPtr, range); } } } } if (!(event->xbutton.state & ShiftMask)) lastClicked = prevItem = tmp; break; case MotionNotify: height = WMWidgetHeight(lPtr); if (lPtr->selectID && event->xmotion.y>0 && event->xmotion.yselectID); lPtr->selectID = NULL; } if (lPtr->flags.buttonPressed && !lPtr->selectID) { if (event->xmotion.y <= 0) { lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollBackwardSelecting, lPtr); break; } else if (event->xmotion.y >= height) { lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollForwardSelecting, lPtr); break; } tmp = getItemIndexAt(lPtr, event->xmotion.y); if (tmp>=0 && tmp!=prevItem) { if (lPtr->flags.allowMultipleSelection) { WMRange range; range.position = lastClicked; if (tmp >= lastClicked) range.count = tmp - lastClicked + 1; else range.count = tmp - lastClicked - 1; WMSetListSelectionToRange(lPtr, range); } else { WMSelectListItem(lPtr, tmp); } } prevItem = tmp; } break; } if (lPtr->topItem != topItem) WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL); } static void updateGeometry(WMList *lPtr) { lPtr->fullFitLines = (lPtr->view->size.height - 4) / lPtr->itemHeight; if (lPtr->fullFitLines * lPtr->itemHeight < lPtr->view->size.height - 4) { lPtr->flags.dontFitAll = 1; } else { lPtr->flags.dontFitAll = 0; } if (WMGetArrayItemCount(lPtr->items) - lPtr->topItem <= lPtr->fullFitLines) { lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines; if (lPtr->topItem < 0) lPtr->topItem = 0; } updateScroller(lPtr); } static void didResizeList(W_ViewDelegate *self, WMView *view) { WMList *lPtr = (WMList*)view->self; WMResizeWidget(lPtr->vScroller, 1, view->size.height-2); updateGeometry(lPtr); } static void destroyList(List *lPtr) { if (lPtr->idleID) WMDeleteIdleHandler(lPtr->idleID); lPtr->idleID = NULL; if (lPtr->selectID) WMDeleteTimerHandler(lPtr->selectID); lPtr->selectID = NULL; if (lPtr->selectedItems) WMFreeArray(lPtr->selectedItems); if (lPtr->items) WMFreeArray(lPtr->items); wfree(lPtr); }