7 char *WMListDidScrollNotification
= "WMListDidScrollNotification";
10 typedef struct W_List
{
14 WMListItem
*items
; /* array of items */
21 short topItem
; /* index of first visible item */
23 short fullFitLines
; /* no of lines that fit entirely */
27 void *doubleClientData
;
28 WMAction
*doubleAction
;
32 WMHandlerID
*idleID
; /* for updating the scroller after
35 WMScroller
*vScroller
;
37 WMScroller *hScroller;
41 unsigned int allowMultipleSelection
:1;
42 unsigned int userDrawn
:1;
43 unsigned int userItemHeight
:1;
45 unsigned int dontFitAll
:1; /* 1 = last item won't be fully visible */
46 unsigned int redrawPending
:1;
47 unsigned int buttonPressed
:1;
48 unsigned int buttonWasPressed
:1;
54 #define DEFAULT_WIDTH 150
55 #define DEFAULT_HEIGHT 150
58 static void destroyList(List
*lPtr
);
59 static void paintList(List
*lPtr
);
62 static void handleEvents(XEvent
*event
, void *data
);
63 static void handleActionEvents(XEvent
*event
, void *data
);
64 static void updateScroller(List
*lPtr
);
66 static void vScrollCallBack(WMWidget
*scroller
, void *self
);
68 static void resizeList();
71 W_ViewProcedureTable _ListViewProcedures
= {
80 WMCreateList(WMWidget
*parent
)
83 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
85 lPtr
= wmalloc(sizeof(List
));
86 memset(lPtr
, 0, sizeof(List
));
88 lPtr
->widgetClass
= WC_List
;
90 lPtr
->view
= W_CreateView(W_VIEW(parent
));
95 lPtr
->view
->self
= lPtr
;
97 WMCreateEventHandler(lPtr
->view
, ExposureMask
|StructureNotifyMask
98 |ClientMessageMask
, handleEvents
, lPtr
);
100 WMCreateEventHandler(lPtr
->view
, ButtonPressMask
|ButtonReleaseMask
101 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
,
102 handleActionEvents
, lPtr
);
104 lPtr
->itemHeight
= WMFontHeight(scrPtr
->normalFont
) + 1;
106 /* create the vertical scroller */
107 lPtr
->vScroller
= WMCreateScroller(lPtr
);
108 WMMoveWidget(lPtr
->vScroller
, 1, 1);
109 WMSetScrollerArrowsPosition(lPtr
->vScroller
, WSAMaxEnd
);
111 WMSetScrollerAction(lPtr
->vScroller
, vScrollCallBack
, lPtr
);
113 /* make the scroller map itself when it's realized */
114 WMMapWidget(lPtr
->vScroller
);
116 resizeList(lPtr
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
118 lPtr
->selectedItem
= -1;
126 WMAddSortedListItem(WMList
*lPtr
, char *text
)
132 item
= wmalloc(sizeof(WMListItem
));
133 memset(item
, 0, sizeof(WMListItem
));
134 item
->text
= wstrdup(text
);
138 } else if (strcmp(lPtr
->items
->text
, text
) > 0) {
139 item
->nextPtr
= lPtr
->items
;
147 while (tmp
->nextPtr
) {
148 if (strcmp(tmp
->nextPtr
->text
, text
) >= 0) {
149 item
->nextPtr
= tmp
->nextPtr
;
164 if (lPtr
->selectedItem
>= index
)
165 lPtr
->selectedItem
++;
167 /* update the scroller when idle, so that we don't waste time
168 * updating it when another item is going to be added later */
170 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
179 WMInsertListItem(WMList
*lPtr
, int row
, char *text
)
182 WMListItem
*tmp
= lPtr
->items
;
184 CHECK_CLASS(lPtr
, WC_List
);
186 item
= wmalloc(sizeof(WMListItem
));
187 memset(item
, 0, sizeof(WMListItem
));
188 item
->text
= wstrdup(text
);
190 if (lPtr
->selectedItem
>= row
&& lPtr
->selectedItem
>= 0)
191 lPtr
->selectedItem
++;
193 if (lPtr
->items
==NULL
) {
195 } else if (row
== 0) {
196 item
->nextPtr
= lPtr
->items
;
198 } else if (row
< 0) {
203 row
= lPtr
->itemCount
;
208 item
->nextPtr
= tmp
->nextPtr
;
214 /* update the scroller when idle, so that we don't waste time
215 * updating it when another item is going to be added later */
217 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
225 WMRemoveListItem(WMList
*lPtr
, int row
)
229 int topItem
= lPtr
->topItem
;
231 CHECK_CLASS(lPtr
, WC_List
);
233 if (row
< 0 || row
>= lPtr
->itemCount
)
236 if (lPtr
->selectedItem
== row
)
237 lPtr
->selectedItem
= -1;
238 else if (lPtr
->selectedItem
> row
)
239 lPtr
->selectedItem
--;
241 if (row
<= lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
)
243 if (lPtr
->topItem
< 0)
247 if (lPtr
->items
->text
)
248 free(lPtr
->items
->text
);
250 tmp
= lPtr
->items
->nextPtr
;
257 llist
= llist
->nextPtr
;
258 tmp
= llist
->nextPtr
;
259 llist
->nextPtr
= llist
->nextPtr
->nextPtr
;
270 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
272 if (lPtr
->topItem
!= topItem
)
273 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
279 WMGetListItem(WMList
*lPtr
, int row
)
283 listPtr
= lPtr
->items
;
286 listPtr
= listPtr
->nextPtr
;
294 WMSetListUserDrawProc(WMList
*lPtr
, WMListDrawProc
*proc
)
296 lPtr
->flags
.userDrawn
= 1;
302 WMSetListUserDrawItemHeight(WMList
*lPtr
, unsigned short height
)
306 lPtr
->flags
.userItemHeight
= 1;
307 lPtr
->itemHeight
= height
;
312 WMClearList(WMList
*lPtr
)
314 WMListItem
*item
, *tmp
;
326 lPtr
->selectedItem
= -1;
329 WMDeleteIdleHandler(lPtr
->idleID
);
332 if (lPtr
->view
->flags
.realized
) {
333 updateScroller(lPtr
);
339 WMSetListAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
341 lPtr
->action
= action
;
342 lPtr
->clientData
= clientData
;
347 WMSetListDoubleAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
349 lPtr
->doubleAction
= action
;
350 lPtr
->doubleClientData
= clientData
;
355 WMGetListSelectedItem(WMList
*lPtr
)
357 int i
= lPtr
->selectedItem
;
360 if (lPtr
->selectedItem
< 0)
365 item
= item
->nextPtr
;
372 WMGetListSelectedItemRow(WMList
*lPtr
)
374 return lPtr
->selectedItem
;
379 WMGetListItemHeight(WMList
*lPtr
)
381 return lPtr
->itemHeight
;
386 WMSetListPosition(WMList
*lPtr
, int row
)
389 if (lPtr
->topItem
+ lPtr
->fullFitLines
> lPtr
->itemCount
)
390 lPtr
->topItem
= lPtr
->itemCount
- lPtr
->fullFitLines
;
392 if (lPtr
->topItem
< 0)
395 if (lPtr
->view
->flags
.realized
)
396 updateScroller(lPtr
);
401 WMSetListBottomPosition(WMList
*lPtr
, int row
)
403 if (lPtr
->itemCount
> lPtr
->fullFitLines
) {
404 lPtr
->topItem
= row
- lPtr
->fullFitLines
;
405 if (lPtr
->topItem
< 0)
407 if (lPtr
->view
->flags
.realized
)
408 updateScroller(lPtr
);
414 WMGetListNumberOfRows(WMList
*lPtr
)
416 return lPtr
->itemCount
;
420 WMGetListPosition(WMList
*lPtr
)
422 return lPtr
->topItem
;
427 vScrollCallBack(WMWidget
*scroller
, void *self
)
429 WMList
*lPtr
= (WMList
*)self
;
430 WMScroller
*sPtr
= (WMScroller
*)scroller
;
432 int topItem
= lPtr
->topItem
;
434 height
= lPtr
->view
->size
.height
- 4;
436 switch (WMGetScrollerHitPart(sPtr
)) {
437 case WSDecrementLine
:
438 if (lPtr
->topItem
> 0) {
441 updateScroller(lPtr
);
445 case WSDecrementPage
:
446 if (lPtr
->topItem
> 0) {
447 lPtr
->topItem
-= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
448 if (lPtr
->topItem
< 0)
451 updateScroller(lPtr
);
456 case WSIncrementLine
:
457 if (lPtr
->topItem
+ lPtr
->fullFitLines
< lPtr
->itemCount
) {
460 updateScroller(lPtr
);
464 case WSIncrementPage
:
465 if (lPtr
->topItem
+ lPtr
->fullFitLines
< lPtr
->itemCount
) {
466 lPtr
->topItem
+= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
468 if (lPtr
->topItem
+ lPtr
->fullFitLines
> lPtr
->itemCount
)
469 lPtr
->topItem
= lPtr
->itemCount
- lPtr
->fullFitLines
;
471 updateScroller(lPtr
);
477 int oldTopItem
= lPtr
->topItem
;
479 lPtr
->topItem
= WMGetScrollerValue(lPtr
->vScroller
) *
480 (float)(lPtr
->itemCount
- lPtr
->fullFitLines
);
482 if (oldTopItem
!= lPtr
->topItem
)
493 if (lPtr
->topItem
!= topItem
)
494 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
499 paintItem(List
*lPtr
, int index
)
501 WMView
*view
= lPtr
->view
;
502 W_Screen
*scr
= view
->screen
;
503 int width
, height
, x
, y
;
508 itemPtr
= lPtr
->items
;
510 itemPtr
= itemPtr
->nextPtr
;
512 width
= lPtr
->view
->size
.width
- 2 - 19;
513 height
= lPtr
->itemHeight
;
515 y
= 2 + (index
-lPtr
->topItem
) * lPtr
->itemHeight
+ 1;
517 if (lPtr
->flags
.userDrawn
) {
521 rect
.size
.width
= width
;
522 rect
.size
.height
= height
;
526 flags
= itemPtr
->uflags
;
527 if (itemPtr
->disabled
)
528 flags
|= WLDSDisabled
;
529 if (itemPtr
->selected
)
530 flags
|= WLDSSelected
;
531 if (itemPtr
->isBranch
)
532 flags
|= WLDSIsBranch
;
535 (*lPtr
->draw
)(lPtr
, index
, view
->window
, itemPtr
->text
, flags
,
538 if (itemPtr
->selected
)
539 XFillRectangle(scr
->display
, view
->window
, W_GC(scr
->white
), x
, y
,
542 XClearArea(scr
->display
, view
->window
, x
, y
, width
, height
, False
);
544 W_PaintText(view
, view
->window
, scr
->normalFont
, x
+4, y
, width
,
545 WALeft
, W_GC(scr
->black
), False
,
546 itemPtr
->text
, strlen(itemPtr
->text
));
553 paintList(List
*lPtr
)
555 W_Screen
*scrPtr
= lPtr
->view
->screen
;
558 if (!lPtr
->view
->flags
.mapped
)
561 if (lPtr
->itemCount
>0) {
562 if (lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
> lPtr
->itemCount
) {
563 lim
= lPtr
->itemCount
- lPtr
->topItem
;
564 XClearArea(scrPtr
->display
, lPtr
->view
->window
, 19,
565 2+lim
*lPtr
->itemHeight
, lPtr
->view
->size
.width
-21,
566 lPtr
->view
->size
.height
-lim
*lPtr
->itemHeight
-3, False
);
568 lim
= lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
;
570 for (i
= lPtr
->topItem
; i
< lPtr
->topItem
+ lim
; i
++) {
574 XClearWindow(scrPtr
->display
, lPtr
->view
->window
);
576 W_DrawRelief(scrPtr
, lPtr
->view
->window
, 0, 0, lPtr
->view
->size
.width
,
577 lPtr
->view
->size
.height
, WRSunken
);
582 scrollTo(List
*lPtr
, int newTop
)
589 updateScroller(List
*lPtr
)
591 float knobProportion
, floatValue
, tmp
;
594 WMDeleteIdleHandler(lPtr
->idleID
);
599 if (lPtr
->itemCount
== 0 || lPtr
->itemCount
<= lPtr
->fullFitLines
)
600 WMSetScrollerParameters(lPtr
->vScroller
, 0, 1);
602 tmp
= lPtr
->fullFitLines
;
603 knobProportion
= tmp
/(float)lPtr
->itemCount
;
605 floatValue
= (float)lPtr
->topItem
/(float)(lPtr
->itemCount
- lPtr
->fullFitLines
);
607 WMSetScrollerParameters(lPtr
->vScroller
, floatValue
, knobProportion
);
613 handleEvents(XEvent
*event
, void *data
)
615 List
*lPtr
= (List
*)data
;
617 CHECK_CLASS(data
, WC_List
);
620 switch (event
->type
) {
622 if (event
->xexpose
.count
!=0)
636 WMFindRowOfListItemWithTitle(WMList
*lPtr
, char *title
)
642 for (i
=0, item
=lPtr
->items
; item
!=NULL
; item
=item
->nextPtr
, i
++) {
643 if (strcmp(item
->text
, title
)==0) {
654 WMSelectListItem(WMList
*lPtr
, int row
)
659 if (row
>= lPtr
->itemCount
)
662 if (!lPtr
->flags
.allowMultipleSelection
) {
663 /* unselect previous selected item */
664 if (lPtr
->selectedItem
>= 0) {
665 itemPtr
= lPtr
->items
;
666 for (i
=0; i
<lPtr
->selectedItem
; i
++)
667 itemPtr
= itemPtr
->nextPtr
;
669 if (itemPtr
->selected
) {
670 itemPtr
->selected
= 0;
671 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
672 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
)
679 if (!lPtr
->flags
.allowMultipleSelection
)
680 lPtr
->selectedItem
= -1;
685 itemPtr
= lPtr
->items
;
686 for (i
=0; i
<row
; i
++)
687 itemPtr
= itemPtr
->nextPtr
;
688 if (lPtr
->flags
.allowMultipleSelection
)
689 itemPtr
->selected
= !itemPtr
->selected
;
691 itemPtr
->selected
= 1;
693 if (lPtr
->view
->flags
.mapped
) {
694 paintItem(lPtr
, row
);
696 if ((row
-lPtr
->topItem
+lPtr
->fullFitLines
)*lPtr
->itemHeight
697 > lPtr
->view
->size
.height
-2)
698 W_DrawRelief(lPtr
->view
->screen
, lPtr
->view
->window
, 0, 0,
699 lPtr
->view
->size
.width
, lPtr
->view
->size
.height
,
702 lPtr
->selectedItem
= row
;
707 getItemIndexAt(List
*lPtr
, int clickY
)
711 index
= (clickY
- 2) / lPtr
->itemHeight
+ lPtr
->topItem
;
713 if (index
< 0 || index
>= lPtr
->itemCount
)
721 handleActionEvents(XEvent
*event
, void *data
)
723 List
*lPtr
= (List
*)data
;
725 int topItem
= lPtr
->topItem
;
727 CHECK_CLASS(data
, WC_List
);
729 switch (event
->type
) {
731 lPtr
->flags
.buttonPressed
= 0;
732 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
734 if (tmp
== lPtr
->selectedItem
&& tmp
>= 0) {
737 (*lPtr
->action
)(lPtr
, lPtr
->clientData
);
742 lPtr
->flags
.buttonPressed
= lPtr
->flags
.buttonWasPressed
;
743 lPtr
->flags
.buttonWasPressed
= 0;
747 lPtr
->flags
.buttonWasPressed
= lPtr
->flags
.buttonPressed
;
748 lPtr
->flags
.buttonPressed
= 0;
752 if (event
->xbutton
.x
> WMWidgetWidth(lPtr
->vScroller
)) {
753 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
756 WMSelectListItem(lPtr
, tmp
);
757 lPtr
->selectedItem
= tmp
;
759 lPtr
->flags
.buttonPressed
= 1;
761 if (WMIsDoubleClick(event
)) {
762 if (lPtr
->doubleAction
)
763 (*lPtr
->doubleAction
)(lPtr
, lPtr
->doubleClientData
);
769 if (lPtr
->flags
.buttonPressed
) {
770 tmp
= getItemIndexAt(lPtr
, event
->xmotion
.y
);
771 if (tmp
>=0 && tmp
!= lPtr
->selectedItem
) {
772 WMSelectListItem(lPtr
, tmp
);
773 lPtr
->selectedItem
= tmp
;
778 if (lPtr
->topItem
!= topItem
)
779 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
784 resizeList(WMList
*lPtr
, unsigned int width
, unsigned int height
)
786 W_ResizeView(lPtr
->view
, width
, height
);
788 WMResizeWidget(lPtr
->vScroller
, 1, height
-2);
790 lPtr
->fullFitLines
= (height
- 4) / lPtr
->itemHeight
;
791 if (lPtr
->fullFitLines
* lPtr
->itemHeight
< height
-4) {
792 lPtr
->flags
.dontFitAll
= 1;
794 lPtr
->flags
.dontFitAll
= 0;
800 destroyList(List
*lPtr
)
805 WMDeleteIdleHandler(lPtr
->idleID
);
808 while (lPtr
->items
!=NULL
) {
809 itemPtr
= lPtr
->items
;
813 lPtr
->items
= itemPtr
->nextPtr
;