Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / wlist.c
Commit [+]AuthorDateLineData
9d2e6ef9 scottc1998-09-29 22:36:29 +00001
9d2e6ef9 scottc1998-09-29 22:36:29 +00002#include "WINGsP.h"
3
0261c326 dan1999-01-06 15:22:33 +00004char *WMListDidScrollNotification = "WMListDidScrollNotification";
e7495baf dan1999-02-17 11:06:40 +00005char *WMListSelectionDidChangeNotification = "WMListSelectionDidChangeNotification";
9d2e6ef9 scottc1998-09-29 22:36:29 +00006
7typedef struct W_List {
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02008 W_Class widgetClass;
9 W_View *view;
9d2e6ef9 scottc1998-09-29 22:36:29 +000010
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020011 WMArray *items; /* list of WMListItem */
12 WMArray *selectedItems; /* list of selected WMListItems */
41ff1274 dan2000-09-23 03:49:58 +000013
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020014 short itemHeight;
41ff1274 dan2000-09-23 03:49:58 +000015
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020016 int topItem; /* index of first visible item */
41ff1274 dan2000-09-23 03:49:58 +000017
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020018 short fullFitLines; /* no of lines that fit entirely */
9d2e6ef9 scottc1998-09-29 22:36:29 +000019
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020020 void *clientData;
21 WMAction *action;
22 void *doubleClientData;
23 WMAction *doubleAction;
41ff1274 dan2000-09-23 03:49:58 +000024
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020025 WMListDrawProc *draw;
41ff1274 dan2000-09-23 03:49:58 +000026
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020027 WMHandlerID *idleID; /* for updating the scroller after adding elements */
9d2e6ef9 scottc1998-09-29 22:36:29 +000028
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020029 WMHandlerID *selectID; /* for selecting items in list while scrolling */
eeda795d dan2000-10-03 12:38:23 +000030
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020031 WMScroller *vScroller;
9d2e6ef9 scottc1998-09-29 22:36:29 +000032
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020033 Pixmap doubleBuffer;
1e922744 dan2002-10-16 04:05:45 +000034
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020035 struct {
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;
44 } flags;
9d2e6ef9 scottc1998-09-29 22:36:29 +000045} List;
46
9d2e6ef9 scottc1998-09-29 22:36:29 +000047#define DEFAULT_WIDTH 150
48#define DEFAULT_HEIGHT 150
49
eeda795d dan2000-10-03 12:38:23 +000050#define SCROLL_DELAY 100
51
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020052static void destroyList(List * lPtr);
53static void paintList(List * lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +000054
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020055static void handleEvents(XEvent * event, void *data);
56static void handleActionEvents(XEvent * event, void *data);
eeda795d dan2000-10-03 12:38:23 +000057
58static void updateScroller(void *data);
59static void scrollForwardSelecting(void *data);
60static void scrollBackwardSelecting(void *data);
9d2e6ef9 scottc1998-09-29 22:36:29 +000061
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020062static void vScrollCallBack(WMWidget * scroller, void *self);
9d2e6ef9 scottc1998-09-29 22:36:29 +000063
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020064static void toggleItemSelection(WMList * lPtr, int index);
eeda795d dan2000-10-03 12:38:23 +000065
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020066static void updateGeometry(WMList * lPtr);
5e4625da kojima1999-05-29 21:41:25 +000067static void didResizeList();
9d2e6ef9 scottc1998-09-29 22:36:29 +000068
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020069static void unselectAllListItems(WMList * lPtr, WMListItem * exceptThis);
9d2e6ef9 scottc1998-09-29 22:36:29 +000070
5e4625da kojima1999-05-29 21:41:25 +000071W_ViewDelegate _ListViewDelegate = {
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020072 NULL,
73 NULL,
74 didResizeList,
75 NULL,
76 NULL
9d2e6ef9 scottc1998-09-29 22:36:29 +000077};
78
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020079static void updateDoubleBufferPixmap(WMList * lPtr)
1e922744 dan2002-10-16 04:05:45 +000080{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020081 WMView *view = lPtr->view;
82 WMScreen *scr = view->screen;
1e922744 dan2002-10-16 04:05:45 +000083
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020084 if (!view->flags.realized)
85 return;
1e922744 dan2002-10-16 04:05:45 +000086
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020087 if (lPtr->doubleBuffer)
88 XFreePixmap(scr->display, lPtr->doubleBuffer);
89 lPtr->doubleBuffer =
90 XCreatePixmap(scr->display, view->window, view->size.width, lPtr->itemHeight, scr->depth);
1e922744 dan2002-10-16 04:05:45 +000091}
92
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020093static void realizeObserver(void *self, WMNotification * not)
1e922744 dan2002-10-16 04:05:45 +000094{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020095 updateDoubleBufferPixmap(self);
1e922744 dan2002-10-16 04:05:45 +000096}
97
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020098static void releaseItem(void *data)
41ff1274 dan2000-09-23 03:49:58 +000099{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200100 WMListItem *item = (WMListItem *) data;
41ff1274 dan2000-09-23 03:49:58 +0000101
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200102 if (item->text)
103 wfree(item->text);
104 wfree(item);
41ff1274 dan2000-09-23 03:49:58 +0000105}
106
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200107WMList *WMCreateList(WMWidget * parent)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000108{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200109 List *lPtr;
110 W_Screen *scrPtr = W_VIEW(parent)->screen;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000111
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200112 lPtr = wmalloc(sizeof(List));
113 memset(lPtr, 0, sizeof(List));
9d2e6ef9 scottc1998-09-29 22:36:29 +0000114
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200115 lPtr->widgetClass = WC_List;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000116
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200117 lPtr->view = W_CreateView(W_VIEW(parent));
118 if (!lPtr->view) {
119 wfree(lPtr);
120 return NULL;
121 }
122 lPtr->view->self = lPtr;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000123
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200124 lPtr->view->delegate = &_ListViewDelegate;
5e4625da kojima1999-05-29 21:41:25 +0000125
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200126 WMCreateEventHandler(lPtr->view, ExposureMask | StructureNotifyMask
127 | ClientMessageMask, handleEvents, lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000128
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200129 WMCreateEventHandler(lPtr->view, ButtonPressMask | ButtonReleaseMask
130 | EnterWindowMask | LeaveWindowMask | ButtonMotionMask, handleActionEvents, lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000131
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200132 lPtr->itemHeight = WMFontHeight(scrPtr->normalFont) + 1;
e82c30b2 kojima1999-10-03 03:47:21 +0000133
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200134 lPtr->items = WMCreateArrayWithDestructor(4, releaseItem);
135 lPtr->selectedItems = WMCreateArray(4);
e82c30b2 kojima1999-10-03 03:47:21 +0000136
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200137 /* create the vertical scroller */
138 lPtr->vScroller = WMCreateScroller(lPtr);
139 WMMoveWidget(lPtr->vScroller, 1, 1);
140 WMSetScrollerArrowsPosition(lPtr->vScroller, WSAMaxEnd);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000141
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200142 WMSetScrollerAction(lPtr->vScroller, vScrollCallBack, lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000143
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200144 /* make the scroller map itself when it's realized */
145 WMMapWidget(lPtr->vScroller);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000146
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200147 W_ResizeView(lPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
41ff1274 dan2000-09-23 03:49:58 +0000148
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200149 WMAddNotificationObserver(realizeObserver, lPtr, WMViewRealizedNotification, lPtr->view);
1e922744 dan2002-10-16 04:05:45 +0000150
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200151 return lPtr;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000152}
153
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200154void WMSetListAllowMultipleSelection(WMList * lPtr, Bool flag)
6973f07f dan2000-09-28 03:09:57 +0000155{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200156 lPtr->flags.allowMultipleSelection = ((flag == 0) ? 0 : 1);
6973f07f dan2000-09-28 03:09:57 +0000157}
158
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200159void WMSetListAllowEmptySelection(WMList * lPtr, Bool flag)
6973f07f dan2000-09-28 03:09:57 +0000160{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200161 lPtr->flags.allowEmptySelection = ((flag == 0) ? 0 : 1);
6973f07f dan2000-09-28 03:09:57 +0000162}
163
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200164static int comparator(const void *a, const void *b)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000165{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200166 return (strcmp((*(WMListItem **) a)->text, (*(WMListItem **) b)->text));
e82c30b2 kojima1999-10-03 03:47:21 +0000167}
9d2e6ef9 scottc1998-09-29 22:36:29 +0000168
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200169void WMSortListItems(WMList * lPtr)
e82c30b2 kojima1999-10-03 03:47:21 +0000170{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200171 WMSortArray(lPtr->items, comparator);
e82c30b2 kojima1999-10-03 03:47:21 +0000172
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200173 paintList(lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000174}
175
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200176void WMSortListItemsWithComparer(WMList * lPtr, WMCompareDataProc * func)
eb87c409 kojima1999-10-20 03:25:06 +0000177{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200178 WMSortArray(lPtr->items, func);
eb87c409 kojima1999-10-20 03:25:06 +0000179
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200180 paintList(lPtr);
eb87c409 kojima1999-10-20 03:25:06 +0000181}
182
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200183WMListItem *WMInsertListItem(WMList * lPtr, int row, char *text)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000184{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200185 WMListItem *item;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000186
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200187 CHECK_CLASS(lPtr, WC_List);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000188
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200189 item = wmalloc(sizeof(WMListItem));
190 memset(item, 0, sizeof(WMListItem));
191 item->text = wstrdup(text);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000192
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200193 row = WMIN(row, WMGetArrayItemCount(lPtr->items));
41ff1274 dan2000-09-23 03:49:58 +0000194
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200195 if (row < 0)
196 WMAddToArray(lPtr->items, item);
197 else
198 WMInsertInArray(lPtr->items, row, item);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000199
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200200 /* update the scroller when idle, so that we don't waste time
201 * updating it when another item is going to be added later */
202 if (!lPtr->idleID) {
203 lPtr->idleID = WMAddIdleHandler((WMCallback *) updateScroller, lPtr);
204 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000205
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200206 return item;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000207}
208
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200209void WMRemoveListItem(WMList * lPtr, int row)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000210{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200211 WMListItem *item;
212 int topItem = lPtr->topItem;
213 int selNotify = 0;
214
215 CHECK_CLASS(lPtr, WC_List);
216
217 /*wassertr(row>=0 && row<WMGetArrayItemCount(lPtr->items)); */
218 if (row < 0 || row >= WMGetArrayItemCount(lPtr->items))
219 return;
220
221 item = WMGetFromArray(lPtr->items, row);
222 if (item->selected) {
223 WMRemoveFromArray(lPtr->selectedItems, item);
224 selNotify = 1;
225 }
226
227 if (row <= lPtr->topItem + lPtr->fullFitLines + lPtr->flags.dontFitAll)
228 lPtr->topItem--;
229 if (lPtr->topItem < 0)
230 lPtr->topItem = 0;
231
232 WMDeleteFromArray(lPtr->items, row);
233
234 if (!lPtr->idleID) {
235 lPtr->idleID = WMAddIdleHandler((WMCallback *) updateScroller, lPtr);
236 }
237 if (lPtr->topItem != topItem) {
238 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
239 }
240 if (selNotify) {
241 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
242 }
6973f07f dan2000-09-28 03:09:57 +0000243}
9d2e6ef9 scottc1998-09-29 22:36:29 +0000244
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200245WMListItem *WMGetListItem(WMList * lPtr, int row)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000246{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200247 return WMGetFromArray(lPtr->items, row);
e82c30b2 kojima1999-10-03 03:47:21 +0000248}
9d2e6ef9 scottc1998-09-29 22:36:29 +0000249
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200250WMArray *WMGetListItems(WMList * lPtr)
e82c30b2 kojima1999-10-03 03:47:21 +0000251{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200252 return lPtr->items;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000253}
254
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200255void WMSetListUserDrawProc(WMList * lPtr, WMListDrawProc * proc)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000256{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200257 lPtr->flags.userDrawn = 1;
258 lPtr->draw = proc;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000259}
260
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200261void WMSetListUserDrawItemHeight(WMList * lPtr, unsigned short height)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000262{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200263 assert(height > 0);
41ff1274 dan2000-09-23 03:49:58 +0000264
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200265 lPtr->flags.userItemHeight = 1;
266 lPtr->itemHeight = height;
41ff1274 dan2000-09-23 03:49:58 +0000267
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200268 updateDoubleBufferPixmap(lPtr);
1e922744 dan2002-10-16 04:05:45 +0000269
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200270 updateGeometry(lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000271}
272
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200273void WMClearList(WMList * lPtr)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000274{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200275 int selNo = WMGetArrayItemCount(lPtr->selectedItems);
276
277 WMEmptyArray(lPtr->selectedItems);
278 WMEmptyArray(lPtr->items);
279
280 lPtr->topItem = 0;
281
282 if (!lPtr->idleID) {
283 WMDeleteIdleHandler(lPtr->idleID);
284 lPtr->idleID = NULL;
285 }
286 if (lPtr->selectID) {
287 WMDeleteTimerHandler(lPtr->selectID);
288 lPtr->selectID = NULL;
289 }
290 if (lPtr->view->flags.realized) {
291 updateScroller(lPtr);
292 }
293 if (selNo > 0) {
294 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
295 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000296}
297
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200298void WMSetListAction(WMList * lPtr, WMAction * action, void *clientData)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000299{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200300 lPtr->action = action;
301 lPtr->clientData = clientData;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000302}
303
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200304void WMSetListDoubleAction(WMList * lPtr, WMAction * action, void *clientData)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000305{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200306 lPtr->doubleAction = action;
307 lPtr->doubleClientData = clientData;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000308}
309
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200310WMArray *WMGetListSelectedItems(WMList * lPtr)
6973f07f dan2000-09-28 03:09:57 +0000311{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200312 return lPtr->selectedItems;
6973f07f dan2000-09-28 03:09:57 +0000313}
314
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200315WMListItem *WMGetListSelectedItem(WMList * lPtr)
e82c30b2 kojima1999-10-03 03:47:21 +0000316{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200317 return WMGetFromArray(lPtr->selectedItems, 0);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000318}
319
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200320int WMGetListSelectedItemRow(WMList * lPtr)
41ff1274 dan2000-09-23 03:49:58 +0000321{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200322 WMListItem *item = WMGetFromArray(lPtr->selectedItems, 0);
6973f07f dan2000-09-28 03:09:57 +0000323
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200324 return (item != NULL ? WMGetFirstInArray(lPtr->items, item) : WLNotFound);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000325}
326
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200327int WMGetListItemHeight(WMList * lPtr)
41ff1274 dan2000-09-23 03:49:58 +0000328{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200329 return lPtr->itemHeight;
0261c326 dan1999-01-06 15:22:33 +0000330}
331
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200332void WMSetListPosition(WMList * lPtr, int row)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000333{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200334 lPtr->topItem = row;
335 if (lPtr->topItem + lPtr->fullFitLines > WMGetArrayItemCount(lPtr->items))
336 lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000337
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200338 if (lPtr->topItem < 0)
339 lPtr->topItem = 0;
41ff1274 dan2000-09-23 03:49:58 +0000340
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200341 if (lPtr->view->flags.realized)
342 updateScroller(lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000343}
344
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200345void WMSetListBottomPosition(WMList * lPtr, int row)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000346{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200347 if (WMGetArrayItemCount(lPtr->items) > lPtr->fullFitLines) {
348 lPtr->topItem = row - lPtr->fullFitLines;
349 if (lPtr->topItem < 0)
350 lPtr->topItem = 0;
351 if (lPtr->view->flags.realized)
352 updateScroller(lPtr);
353 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000354}
355
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200356int WMGetListNumberOfRows(WMList * lPtr)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000357{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200358 return WMGetArrayItemCount(lPtr->items);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000359}
360
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200361int WMGetListPosition(WMList * lPtr)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000362{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200363 return lPtr->topItem;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000364}
365
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200366Bool WMListAllowsMultipleSelection(WMList * lPtr)
6973f07f dan2000-09-28 03:09:57 +0000367{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200368 return lPtr->flags.allowMultipleSelection;
6973f07f dan2000-09-28 03:09:57 +0000369}
370
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200371Bool WMListAllowsEmptySelection(WMList * lPtr)
6973f07f dan2000-09-28 03:09:57 +0000372{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200373 return lPtr->flags.allowEmptySelection;
6973f07f dan2000-09-28 03:09:57 +0000374}
375
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200376static void scrollByAmount(WMList * lPtr, int amount)
60a01b0f dan2000-09-28 14:06:50 +0000377{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200378 int itemCount = WMGetArrayItemCount(lPtr->items);
60a01b0f dan2000-09-28 14:06:50 +0000379
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200380 if ((amount < 0 && lPtr->topItem > 0) || (amount > 0 && (lPtr->topItem + lPtr->fullFitLines < itemCount))) {
60a01b0f dan2000-09-28 14:06:50 +0000381
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200382 lPtr->topItem += amount;
383 if (lPtr->topItem < 0)
384 lPtr->topItem = 0;
385 if (lPtr->topItem + lPtr->fullFitLines > itemCount)
386 lPtr->topItem = itemCount - lPtr->fullFitLines;
60a01b0f dan2000-09-28 14:06:50 +0000387
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200388 updateScroller(lPtr);
389 }
60a01b0f dan2000-09-28 14:06:50 +0000390}
391
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200392static void vScrollCallBack(WMWidget * scroller, void *self)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000393{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200394 WMList *lPtr = (WMList *) self;
395 int height;
396 int oldTopItem = lPtr->topItem;
397 int itemCount = WMGetArrayItemCount(lPtr->items);
398
399 height = lPtr->view->size.height - 4;
400
401 switch (WMGetScrollerHitPart((WMScroller *) scroller)) {
402 case WSDecrementLine:
403 scrollByAmount(lPtr, -1);
404 break;
405
406 case WSIncrementLine:
407 scrollByAmount(lPtr, 1);
408 break;
409
410 case WSDecrementPage:
411 scrollByAmount(lPtr, -lPtr->fullFitLines + (1 - lPtr->flags.dontFitAll) + 1);
412 break;
413
414 case WSIncrementPage:
415 scrollByAmount(lPtr, lPtr->fullFitLines - (1 - lPtr->flags.dontFitAll) - 1);
416 break;
417
418 case WSDecrementWheel:
419 scrollByAmount(lPtr, -lPtr->fullFitLines / 3);
420 break;
421
422 case WSIncrementWheel:
423 scrollByAmount(lPtr, lPtr->fullFitLines / 3);
424 break;
425
426 case WSKnob:
427 lPtr->topItem = WMGetScrollerValue(lPtr->vScroller) * (float)(itemCount - lPtr->fullFitLines);
428
429 if (oldTopItem != lPtr->topItem)
430 paintList(lPtr); /* use updateScroller(lPtr) here? -Dan */
431 break;
432
433 case WSKnobSlot:
434 case WSNoPart:
435 default:
436 /* do nothing */
437 break;
438 }
439
440 if (lPtr->topItem != oldTopItem)
441 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000442}
443
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200444static void paintItem(List * lPtr, int index)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000445{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200446 WMView *view = lPtr->view;
447 W_Screen *scr = view->screen;
448 Display *display = scr->display;
449 int width, height, x, y, tlen;
450 WMListItem *itemPtr;
451 Drawable d = lPtr->doubleBuffer;
452
453 itemPtr = WMGetFromArray(lPtr->items, index);
454
455 width = lPtr->view->size.width - 2 - 19;
456 height = lPtr->itemHeight;
457 x = 19;
458 y = 2 + (index - lPtr->topItem) * lPtr->itemHeight + 1;
459 tlen = strlen(itemPtr->text);
460
461 if (lPtr->flags.userDrawn) {
462 WMRect rect;
463 int flags;
464
465 rect.size.width = width;
466 rect.size.height = height;
467 rect.pos.x = 0;
468 rect.pos.y = 0;
469
470 flags = itemPtr->uflags;
471 if (itemPtr->disabled)
472 flags |= WLDSDisabled;
473 if (itemPtr->selected)
474 flags |= WLDSSelected;
475 if (itemPtr->isBranch)
476 flags |= WLDSIsBranch;
477
478 if (lPtr->draw)
479 (*lPtr->draw) (lPtr, index, d, itemPtr->text, flags, &rect);
480
481 XCopyArea(display, d, view->window, scr->copyGC, 0, 0, width, height, x, y);
482 } else {
483 WMColor *back = (itemPtr->selected ? scr->white : view->backColor);
484
485 XFillRectangle(display, d, WMColorGC(back), 0, 0, width, height);
486
487 W_PaintText(view, d, scr->normalFont, 4, 0, width, WALeft, scr->black, False, itemPtr->text, tlen);
488 XCopyArea(display, d, view->window, scr->copyGC, 0, 0, width, height, x, y);
489 }
490
491 if ((index - lPtr->topItem + lPtr->fullFitLines) * lPtr->itemHeight > lPtr->view->size.height - 2) {
492 W_DrawRelief(lPtr->view->screen, lPtr->view->window, 0, 0,
493 lPtr->view->size.width, lPtr->view->size.height, WRSunken);
494 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000495}
496
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200497static void paintList(List * lPtr)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000498{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200499 W_Screen *scrPtr = lPtr->view->screen;
500 int i, lim;
501
502 if (!lPtr->view->flags.mapped)
503 return;
504
505 if (WMGetArrayItemCount(lPtr->items) > 0) {
506 if (lPtr->topItem + lPtr->fullFitLines + lPtr->flags.dontFitAll > WMGetArrayItemCount(lPtr->items)) {
507
508 lim = WMGetArrayItemCount(lPtr->items) - lPtr->topItem;
509 XClearArea(scrPtr->display, lPtr->view->window, 19,
510 2 + lim * lPtr->itemHeight, lPtr->view->size.width - 21,
511 lPtr->view->size.height - lim * lPtr->itemHeight - 3, False);
512 } else {
513 lim = lPtr->fullFitLines + lPtr->flags.dontFitAll;
514 }
515 for (i = lPtr->topItem; i < lPtr->topItem + lim; i++) {
516 paintItem(lPtr, i);
517 }
518 } else {
519 XClearWindow(scrPtr->display, lPtr->view->window);
520 }
521 W_DrawRelief(scrPtr, lPtr->view->window, 0, 0, lPtr->view->size.width, lPtr->view->size.height, WRSunken);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000522}
523
524#if 0
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200525static void scrollTo(List * lPtr, int newTop)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000526{
41ff1274 dan2000-09-23 03:49:58 +0000527
9d2e6ef9 scottc1998-09-29 22:36:29 +0000528}
529#endif
530
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200531static void updateScroller(void *data)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000532{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200533 List *lPtr = (List *) data;
eeda795d dan2000-10-03 12:38:23 +0000534
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200535 float knobProportion, floatValue, tmp;
536 int count = WMGetArrayItemCount(lPtr->items);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000537
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200538 if (lPtr->idleID)
539 WMDeleteIdleHandler(lPtr->idleID);
540 lPtr->idleID = NULL;
41ff1274 dan2000-09-23 03:49:58 +0000541
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200542 paintList(lPtr);
41ff1274 dan2000-09-23 03:49:58 +0000543
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200544 if (count == 0 || count <= lPtr->fullFitLines)
545 WMSetScrollerParameters(lPtr->vScroller, 0, 1);
546 else {
547 tmp = lPtr->fullFitLines;
548 knobProportion = tmp / (float)count;
e82c30b2 kojima1999-10-03 03:47:21 +0000549
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200550 floatValue = (float)lPtr->topItem / (float)(count - lPtr->fullFitLines);
e82c30b2 kojima1999-10-03 03:47:21 +0000551
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200552 WMSetScrollerParameters(lPtr->vScroller, floatValue, knobProportion);
553 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000554}
555
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200556static void scrollForwardSelecting(void *data)
eeda795d dan2000-10-03 12:38:23 +0000557{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200558 List *lPtr = (List *) data;
559 int lastSelected;
560
561 lastSelected = lPtr->topItem + lPtr->fullFitLines + lPtr->flags.dontFitAll - 1;
562
563 if (lastSelected >= WMGetArrayItemCount(lPtr->items) - 1) {
564 lPtr->selectID = NULL;
565 if (lPtr->flags.dontFitAll)
566 scrollByAmount(lPtr, 1);
567 return;
568 }
569
570 /* selecting NEEDS to be done before scrolling to avoid flickering */
571 if (lPtr->flags.allowMultipleSelection) {
572 WMListItem *item;
573 WMRange range;
574
575 item = WMGetFromArray(lPtr->selectedItems, 0);
576 range.position = WMGetFirstInArray(lPtr->items, item);
577 if (lastSelected + 1 >= range.position) {
578 range.count = lastSelected - range.position + 2;
579 } else {
580 range.count = lastSelected - range.position;
581 }
582 WMSetListSelectionToRange(lPtr, range);
583 } else {
584 WMSelectListItem(lPtr, lastSelected + 1);
585 }
586 scrollByAmount(lPtr, 1);
587
588 lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollForwardSelecting, lPtr);
eeda795d dan2000-10-03 12:38:23 +0000589}
590
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200591static void scrollBackwardSelecting(void *data)
eeda795d dan2000-10-03 12:38:23 +0000592{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200593 List *lPtr = (List *) data;
594
595 if (lPtr->topItem < 1) {
596 lPtr->selectID = NULL;
597 return;
598 }
599
600 /* selecting NEEDS to be done before scrolling to avoid flickering */
601 if (lPtr->flags.allowMultipleSelection) {
602 WMListItem *item;
603 WMRange range;
604
605 item = WMGetFromArray(lPtr->selectedItems, 0);
606 range.position = WMGetFirstInArray(lPtr->items, item);
607 if (lPtr->topItem - 1 >= range.position) {
608 range.count = lPtr->topItem - range.position;
609 } else {
610 range.count = lPtr->topItem - range.position - 2;
611 }
612 WMSetListSelectionToRange(lPtr, range);
613 } else {
614 WMSelectListItem(lPtr, lPtr->topItem - 1);
615 }
616 scrollByAmount(lPtr, -1);
617
618 lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollBackwardSelecting, lPtr);
eeda795d dan2000-10-03 12:38:23 +0000619}
620
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200621static void handleEvents(XEvent * event, void *data)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000622{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200623 List *lPtr = (List *) data;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000624
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200625 CHECK_CLASS(data, WC_List);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000626
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200627 switch (event->type) {
628 case Expose:
629 if (event->xexpose.count != 0)
630 break;
631 paintList(lPtr);
632 break;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000633
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200634 case DestroyNotify:
635 destroyList(lPtr);
636 break;
41ff1274 dan2000-09-23 03:49:58 +0000637
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200638 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000639}
640
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200641static int matchTitle(void *item, void *title)
41ff1274 dan2000-09-23 03:49:58 +0000642{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200643 return (strcmp(((WMListItem *) item)->text, (char *)title) == 0 ? 1 : 0);
41ff1274 dan2000-09-23 03:49:58 +0000644}
645
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200646int WMFindRowOfListItemWithTitle(WMList * lPtr, char *title)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000647{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200648 return WMFindInArray(lPtr->items, matchTitle, title);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000649}
650
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200651void WMSelectListItem(WMList * lPtr, int row)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000652{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200653 WMListItem *item;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000654
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200655 if (row >= WMGetArrayItemCount(lPtr->items))
656 return;
b2478b63 dan2000-10-01 23:26:03 +0000657
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200658 if (row < 0) {
659 /* row = -1 will deselects all for backward compatibility.
660 * will be removed later. -Dan */
661 WMUnselectAllListItems(lPtr);
662 return;
663 }
e7495baf dan1999-02-17 11:06:40 +0000664
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200665 item = WMGetFromArray(lPtr->items, row);
666 if (item->selected)
667 return; /* Return if already selected */
41ff1274 dan2000-09-23 03:49:58 +0000668
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200669 if (!lPtr->flags.allowMultipleSelection) {
670 /* unselect previous selected items */
671 unselectAllListItems(lPtr, NULL);
672 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000673
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200674 /* select item */
675 item->selected = 1;
676 WMAddToArray(lPtr->selectedItems, item);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000677
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200678 if (lPtr->view->flags.mapped && row >= lPtr->topItem && row <= lPtr->topItem + lPtr->fullFitLines) {
679 paintItem(lPtr, row);
680 }
6973f07f dan2000-09-28 03:09:57 +0000681
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200682 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000683}
684
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200685void WMUnselectListItem(WMList * lPtr, int row)
aee58cbd dan2000-09-29 02:12:40 +0000686{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200687 WMListItem *item = WMGetFromArray(lPtr->items, row);
aee58cbd dan2000-09-29 02:12:40 +0000688
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200689 if (!item || !item->selected)
690 return;
aee58cbd dan2000-09-29 02:12:40 +0000691
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200692 if (!lPtr->flags.allowEmptySelection && WMGetArrayItemCount(lPtr->selectedItems) <= 1) {
693 return;
694 }
aee58cbd dan2000-09-29 02:12:40 +0000695
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200696 item->selected = 0;
697 WMRemoveFromArray(lPtr->selectedItems, item);
aee58cbd dan2000-09-29 02:12:40 +0000698
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200699 if (lPtr->view->flags.mapped && row >= lPtr->topItem && row <= lPtr->topItem + lPtr->fullFitLines) {
700 paintItem(lPtr, row);
701 }
b2478b63 dan2000-10-01 23:26:03 +0000702
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200703 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
b2478b63 dan2000-10-01 23:26:03 +0000704}
705
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200706void WMSelectListItemsInRange(WMList * lPtr, WMRange range)
b2478b63 dan2000-10-01 23:26:03 +0000707{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200708 WMListItem *item;
709 int position = range.position, k = 1, notify = 0;
710 int total = WMGetArrayItemCount(lPtr->items);
711
712 if (!lPtr->flags.allowMultipleSelection)
713 return;
714 if (range.count == 0)
715 return; /* Nothing to select */
716
717 if (range.count < 0) {
718 range.count = -range.count;
719 k = -1;
720 }
721
722 for (; range.count > 0 && position >= 0 && position < total; range.count--) {
723 item = WMGetFromArray(lPtr->items, position);
724 if (!item->selected) {
725 item->selected = 1;
726 WMAddToArray(lPtr->selectedItems, item);
727 if (lPtr->view->flags.mapped && position >= lPtr->topItem
728 && position <= lPtr->topItem + lPtr->fullFitLines) {
729 paintItem(lPtr, position);
730 }
731 notify = 1;
732 }
733 position += k;
734 }
735
736 if (notify) {
737 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
738 }
b2478b63 dan2000-10-01 23:26:03 +0000739}
740
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200741void WMSetListSelectionToRange(WMList * lPtr, WMRange range)
b2478b63 dan2000-10-01 23:26:03 +0000742{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200743 WMListItem *item;
744 int mark1, mark2, i, k;
745 int position = range.position, notify = 0;
746 int total = WMGetArrayItemCount(lPtr->items);
747
748 if (!lPtr->flags.allowMultipleSelection)
749 return;
750
751 if (range.count == 0) {
752 WMUnselectAllListItems(lPtr);
753 return;
754 }
755
756 if (range.count < 0) {
757 mark1 = range.position + range.count + 1;
758 mark2 = range.position + 1;
759 range.count = -range.count;
760 k = -1;
761 } else {
762 mark1 = range.position;
763 mark2 = range.position + range.count;
764 k = 1;
765 }
766 if (mark1 > total)
767 mark1 = total;
768 if (mark2 < 0)
769 mark2 = 0;
770
771 WMEmptyArray(lPtr->selectedItems);
772
773 for (i = 0; i < mark1; i++) {
774 item = WMGetFromArray(lPtr->items, i);
775 if (item->selected) {
776 item->selected = 0;
777 if (lPtr->view->flags.mapped && i >= lPtr->topItem
778 && i <= lPtr->topItem + lPtr->fullFitLines) {
779 paintItem(lPtr, i);
780 }
781 notify = 1;
782 }
783 }
784 for (; range.count > 0 && position >= 0 && position < total; range.count--) {
785 item = WMGetFromArray(lPtr->items, position);
786 if (!item->selected) {
787 item->selected = 1;
788 if (lPtr->view->flags.mapped && position >= lPtr->topItem
789 && position <= lPtr->topItem + lPtr->fullFitLines) {
790 paintItem(lPtr, position);
791 }
792 notify = 1;
793 }
794 WMAddToArray(lPtr->selectedItems, item);
795 position += k;
796 }
797 for (i = mark2; i < total; i++) {
798 item = WMGetFromArray(lPtr->items, i);
799 if (item->selected) {
800 item->selected = 0;
801 if (lPtr->view->flags.mapped && i >= lPtr->topItem
802 && i <= lPtr->topItem + lPtr->fullFitLines) {
803 paintItem(lPtr, i);
804 }
805 notify = 1;
806 }
807 }
808
809 if (notify) {
810 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
811 }
aee58cbd dan2000-09-29 02:12:40 +0000812}
813
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200814void WMSelectAllListItems(WMList * lPtr)
aee58cbd dan2000-09-29 02:12:40 +0000815{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200816 int i;
817 WMListItem *item;
818
819 if (!lPtr->flags.allowMultipleSelection)
820 return;
821
822 if (WMGetArrayItemCount(lPtr->items) == WMGetArrayItemCount(lPtr->selectedItems)) {
823 return; /* All items are selected already */
824 }
825
826 WMFreeArray(lPtr->selectedItems);
827 lPtr->selectedItems = WMCreateArrayWithArray(lPtr->items);
828
829 for (i = 0; i < WMGetArrayItemCount(lPtr->items); i++) {
830 item = WMGetFromArray(lPtr->items, i);
831 if (!item->selected) {
832 item->selected = 1;
833 if (lPtr->view->flags.mapped && i >= lPtr->topItem
834 && i <= lPtr->topItem + lPtr->fullFitLines) {
835 paintItem(lPtr, i);
836 }
837 }
838 }
839
840 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
aee58cbd dan2000-09-29 02:12:40 +0000841}
842
399439d3 dan2000-10-02 11:43:28 +0000843/*
844 * Be careful from where you call this function! It doesn't honor the
845 * allowEmptySelection flag and doesn't send a notification about selection
846 * change! You need to manage these in the functions from where you call it.
847 *
848 * This will unselect all items if exceptThis is NULL, else will keep
849 * exceptThis selected.
850 * Make sure that exceptThis is one of the already selected items if not NULL!
851 *
852 */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200853static void unselectAllListItems(WMList * lPtr, WMListItem * exceptThis)
aee58cbd dan2000-09-29 02:12:40 +0000854{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200855 int i;
856 WMListItem *item;
857
858 for (i = 0; i < WMGetArrayItemCount(lPtr->items); i++) {
859 item = WMGetFromArray(lPtr->items, i);
860 if (item != exceptThis && item->selected) {
861 item->selected = 0;
862 if (lPtr->view->flags.mapped && i >= lPtr->topItem
863 && i <= lPtr->topItem + lPtr->fullFitLines) {
864 paintItem(lPtr, i);
865 }
866 }
867 }
868
869 WMEmptyArray(lPtr->selectedItems);
870 if (exceptThis != NULL) {
871 exceptThis->selected = 1;
872 WMAddToArray(lPtr->selectedItems, exceptThis);
873 }
399439d3 dan2000-10-02 11:43:28 +0000874}
875
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200876void WMUnselectAllListItems(WMList * lPtr)
399439d3 dan2000-10-02 11:43:28 +0000877{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200878 int keep;
879 WMListItem *keepItem;
399439d3 dan2000-10-02 11:43:28 +0000880
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200881 keep = lPtr->flags.allowEmptySelection ? 0 : 1;
399439d3 dan2000-10-02 11:43:28 +0000882
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200883 if (WMGetArrayItemCount(lPtr->selectedItems) == keep)
884 return;
399439d3 dan2000-10-02 11:43:28 +0000885
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200886 keepItem = (keep == 1 ? WMGetFromArray(lPtr->selectedItems, 0) : NULL);
399439d3 dan2000-10-02 11:43:28 +0000887
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200888 unselectAllListItems(lPtr, keepItem);
de991559 dan2000-10-02 06:59:18 +0000889
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200890 WMPostNotificationName(WMListSelectionDidChangeNotification, lPtr, NULL);
aee58cbd dan2000-09-29 02:12:40 +0000891}
892
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200893static int getItemIndexAt(List * lPtr, int clickY)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000894{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200895 int index;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000896
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200897 index = (clickY - 2) / lPtr->itemHeight + lPtr->topItem;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000898
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200899 if (index < 0 || index >= WMGetArrayItemCount(lPtr->items))
900 return -1;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000901
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200902 return index;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000903}
904
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200905static void toggleItemSelection(WMList * lPtr, int index)
de991559 dan2000-10-02 06:59:18 +0000906{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200907 WMListItem *item = WMGetFromArray(lPtr->items, index);
de991559 dan2000-10-02 06:59:18 +0000908
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200909 if (item && item->selected) {
910 WMUnselectListItem(lPtr, index);
911 } else {
912 WMSelectListItem(lPtr, index);
913 }
de991559 dan2000-10-02 06:59:18 +0000914}
915
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200916static void handleActionEvents(XEvent * event, void *data)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000917{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200918 List *lPtr = (List *) data;
919 int tmp, height;
920 int topItem = lPtr->topItem;
921 static int lastClicked = -1, prevItem = -1;
922
923 CHECK_CLASS(data, WC_List);
924
925 switch (event->type) {
926 case ButtonRelease:
927 /* Ignore mouse wheel events, they're not "real" button events */
928 if (event->xbutton.button == WINGsConfiguration.mouseWheelUp ||
929 event->xbutton.button == WINGsConfiguration.mouseWheelDown) {
930 break;
931 }
932
933 lPtr->flags.buttonPressed = 0;
934 if (lPtr->selectID) {
935 WMDeleteTimerHandler(lPtr->selectID);
936 lPtr->selectID = NULL;
937 }
938 tmp = getItemIndexAt(lPtr, event->xbutton.y);
939
940 if (tmp >= 0) {
941 if (lPtr->action)
942 (*lPtr->action) (lPtr, lPtr->clientData);
943 }
944
945 if (!(event->xbutton.state & ShiftMask))
946 lastClicked = prevItem = tmp;
947
948 break;
949
950 case EnterNotify:
951 if (lPtr->selectID) {
952 WMDeleteTimerHandler(lPtr->selectID);
953 lPtr->selectID = NULL;
954 }
955 break;
956
957 case LeaveNotify:
958 height = WMWidgetHeight(lPtr);
959 if (lPtr->flags.buttonPressed && !lPtr->selectID) {
960 if (event->xcrossing.y >= height) {
961 lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollForwardSelecting, lPtr);
962 } else if (event->xcrossing.y <= 0) {
963 lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollBackwardSelecting, lPtr);
964 }
965 }
966 break;
967
968 case ButtonPress:
969 if (event->xbutton.x <= WMWidgetWidth(lPtr->vScroller))
970 break;
971 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown ||
972 event->xbutton.button == WINGsConfiguration.mouseWheelUp) {
973 int amount = 0;
974
975 if (event->xbutton.state & ControlMask) {
976 amount = lPtr->fullFitLines - (1 - lPtr->flags.dontFitAll) - 1;
977 } else if (event->xbutton.state & ShiftMask) {
978 amount = 1;
979 } else {
980 amount = lPtr->fullFitLines / 3;
981 if (amount == 0)
982 amount++;
983 }
984 if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
985 amount = -amount;
986
987 scrollByAmount(lPtr, amount);
988 break;
989 }
990
991 tmp = getItemIndexAt(lPtr, event->xbutton.y);
992 lPtr->flags.buttonPressed = 1;
993
994 if (tmp >= 0) {
995 if (tmp == lastClicked && WMIsDoubleClick(event)) {
996 WMSelectListItem(lPtr, tmp);
997 if (lPtr->doubleAction)
998 (*lPtr->doubleAction) (lPtr, lPtr->doubleClientData);
999 } else {
1000 if (!lPtr->flags.allowMultipleSelection) {
1001 if (event->xbutton.state & ControlMask) {
1002 toggleItemSelection(lPtr, tmp);
1003 } else {
1004 WMSelectListItem(lPtr, tmp);
1005 }
1006 } else {
1007 WMRange range;
1008 WMListItem *lastSel;
1009
1010 if (event->xbutton.state & ControlMask) {
1011 toggleItemSelection(lPtr, tmp);
1012 } else if (event->xbutton.state & ShiftMask) {
1013 if (WMGetArrayItemCount(lPtr->selectedItems) == 0) {
1014 WMSelectListItem(lPtr, tmp);
1015 } else {
1016 lastSel = WMGetFromArray(lPtr->items, lastClicked);
1017 range.position = WMGetFirstInArray(lPtr->items, lastSel);
1018 if (tmp >= range.position)
1019 range.count = tmp - range.position + 1;
1020 else
1021 range.count = tmp - range.position - 1;
1022
1023 WMSetListSelectionToRange(lPtr, range);
1024 }
1025 } else {
1026 range.position = tmp;
1027 range.count = 1;
1028 WMSetListSelectionToRange(lPtr, range);
1029 }
1030 }
1031 }
1032 }
1033
1034 if (!(event->xbutton.state & ShiftMask))
1035 lastClicked = prevItem = tmp;
1036
1037 break;
1038
1039 case MotionNotify:
1040 height = WMWidgetHeight(lPtr);
1041 if (lPtr->selectID && event->xmotion.y > 0 && event->xmotion.y < height) {
1042 WMDeleteTimerHandler(lPtr->selectID);
1043 lPtr->selectID = NULL;
1044 }
1045 if (lPtr->flags.buttonPressed && !lPtr->selectID) {
1046 if (event->xmotion.y <= 0) {
1047 lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollBackwardSelecting, lPtr);
1048 break;
1049 } else if (event->xmotion.y >= height) {
1050 lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollForwardSelecting, lPtr);
1051 break;
1052 }
1053
1054 tmp = getItemIndexAt(lPtr, event->xmotion.y);
1055 if (tmp >= 0 && tmp != prevItem) {
1056 if (lPtr->flags.allowMultipleSelection) {
1057 WMRange range;
1058
1059 range.position = lastClicked;
1060 if (tmp >= lastClicked)
1061 range.count = tmp - lastClicked + 1;
1062 else
1063 range.count = tmp - lastClicked - 1;
1064 WMSetListSelectionToRange(lPtr, range);
1065 } else {
1066 WMSelectListItem(lPtr, tmp);
1067 }
1068 }
1069 prevItem = tmp;
1070 }
1071 break;
1072 }
1073 if (lPtr->topItem != topItem)
1074 WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
9d2e6ef9 scottc1998-09-29 22:36:29 +00001075}
1076
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001077static void updateGeometry(WMList * lPtr)
41ff1274 dan2000-09-23 03:49:58 +00001078{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001079 lPtr->fullFitLines = (lPtr->view->size.height - 4) / lPtr->itemHeight;
1080 if (lPtr->fullFitLines * lPtr->itemHeight < lPtr->view->size.height - 4) {
1081 lPtr->flags.dontFitAll = 1;
1082 } else {
1083 lPtr->flags.dontFitAll = 0;
1084 }
1085
1086 if (WMGetArrayItemCount(lPtr->items) - lPtr->topItem <= lPtr->fullFitLines) {
1087 lPtr->topItem = WMGetArrayItemCount(lPtr->items) - lPtr->fullFitLines;
1088 if (lPtr->topItem < 0)
1089 lPtr->topItem = 0;
1090 }
1091
1092 updateScroller(lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +00001093}
1094
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001095static void didResizeList(W_ViewDelegate * self, WMView * view)
41ff1274 dan2000-09-23 03:49:58 +00001096{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001097 WMList *lPtr = (WMList *) view->self;
5e4625da kojima1999-05-29 21:41:25 +00001098
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001099 WMResizeWidget(lPtr->vScroller, 1, view->size.height - 2);
c56756dc dan1999-03-14 22:35:50 +00001100
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001101 updateDoubleBufferPixmap(lPtr);
1e922744 dan2002-10-16 04:05:45 +00001102
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001103 updateGeometry(lPtr);
c56756dc dan1999-03-14 22:35:50 +00001104}
1105
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001106static void destroyList(List * lPtr)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001107{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001108 if (lPtr->idleID)
1109 WMDeleteIdleHandler(lPtr->idleID);
1110 lPtr->idleID = NULL;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001111
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001112 if (lPtr->selectID)
1113 WMDeleteTimerHandler(lPtr->selectID);
1114 lPtr->selectID = NULL;
eeda795d dan2000-10-03 12:38:23 +00001115
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001116 if (lPtr->selectedItems)
1117 WMFreeArray(lPtr->selectedItems);
f9778df7 dan2001-01-19 01:45:29 +00001118
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001119 if (lPtr->items)
1120 WMFreeArray(lPtr->items);
41ff1274 dan2000-09-23 03:49:58 +00001121
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001122 if (lPtr->doubleBuffer)
1123 XFreePixmap(lPtr->view->screen->display, lPtr->doubleBuffer);
1e922744 dan2002-10-16 04:05:45 +00001124
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001125 WMRemoveNotificationObserver(lPtr);
5e37991b dan2002-11-04 06:07:00 +00001126
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001127 wfree(lPtr);
9d2e6ef9 scottc1998-09-29 22:36:29 +00001128}