7 char *WMListDidScrollNotification
= "WMListDidScrollNotification";
8 char *WMListSelectionDidChangeNotification
= "WMListSelectionDidChangeNotification";
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 updateGeometry(WMList
*lPtr
);
69 static void resizeList();
72 W_ViewProcedureTable _ListViewProcedures
= {
81 WMCreateList(WMWidget
*parent
)
84 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
86 lPtr
= wmalloc(sizeof(List
));
87 memset(lPtr
, 0, sizeof(List
));
89 lPtr
->widgetClass
= WC_List
;
91 lPtr
->view
= W_CreateView(W_VIEW(parent
));
96 lPtr
->view
->self
= lPtr
;
98 WMCreateEventHandler(lPtr
->view
, ExposureMask
|StructureNotifyMask
99 |ClientMessageMask
, handleEvents
, lPtr
);
101 WMCreateEventHandler(lPtr
->view
, ButtonPressMask
|ButtonReleaseMask
102 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
,
103 handleActionEvents
, lPtr
);
105 lPtr
->itemHeight
= WMFontHeight(scrPtr
->normalFont
) + 1;
107 /* create the vertical scroller */
108 lPtr
->vScroller
= WMCreateScroller(lPtr
);
109 WMMoveWidget(lPtr
->vScroller
, 1, 1);
110 WMSetScrollerArrowsPosition(lPtr
->vScroller
, WSAMaxEnd
);
112 WMSetScrollerAction(lPtr
->vScroller
, vScrollCallBack
, lPtr
);
114 /* make the scroller map itself when it's realized */
115 WMMapWidget(lPtr
->vScroller
);
117 resizeList(lPtr
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
119 lPtr
->selectedItem
= -1;
127 WMAddSortedListItem(WMList
*lPtr
, char *text
)
133 item
= wmalloc(sizeof(WMListItem
));
134 memset(item
, 0, sizeof(WMListItem
));
135 item
->text
= wstrdup(text
);
139 } else if (strcmp(lPtr
->items
->text
, text
) > 0) {
140 item
->nextPtr
= lPtr
->items
;
148 while (tmp
->nextPtr
) {
149 if (strcmp(tmp
->nextPtr
->text
, text
) >= 0) {
150 item
->nextPtr
= tmp
->nextPtr
;
165 if (lPtr
->selectedItem
>= index
)
166 lPtr
->selectedItem
++;
168 /* update the scroller when idle, so that we don't waste time
169 * updating it when another item is going to be added later */
171 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
180 WMInsertListItem(WMList
*lPtr
, int row
, char *text
)
183 WMListItem
*tmp
= lPtr
->items
;
185 CHECK_CLASS(lPtr
, WC_List
);
187 item
= wmalloc(sizeof(WMListItem
));
188 memset(item
, 0, sizeof(WMListItem
));
189 item
->text
= wstrdup(text
);
192 if (lPtr
->selectedItem
>= row
&& lPtr
->selectedItem
>= 0
194 lPtr
->selectedItem
++;
196 if (lPtr
->items
==NULL
) {
198 } else if (row
== 0) {
199 item
->nextPtr
= lPtr
->items
;
201 } else if (row
< 0) {
206 row
= lPtr
->itemCount
;
211 item
->nextPtr
= tmp
->nextPtr
;
217 /* update the scroller when idle, so that we don't waste time
218 * updating it when another item is going to be added later */
220 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
228 WMRemoveListItem(WMList
*lPtr
, int row
)
232 int topItem
= lPtr
->topItem
;
235 CHECK_CLASS(lPtr
, WC_List
);
237 if (row
< 0 || row
>= lPtr
->itemCount
)
240 if (lPtr
->selectedItem
== row
) {
241 lPtr
->selectedItem
= -1;
243 } else if (lPtr
->selectedItem
> row
) {
244 lPtr
->selectedItem
--;
247 if (row
<= lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
)
249 if (lPtr
->topItem
< 0)
253 if (lPtr
->items
->text
)
254 free(lPtr
->items
->text
);
256 tmp
= lPtr
->items
->nextPtr
;
263 llist
= llist
->nextPtr
;
264 tmp
= llist
->nextPtr
;
265 llist
->nextPtr
= llist
->nextPtr
->nextPtr
;
276 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
278 if (lPtr
->topItem
!= topItem
)
279 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
281 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
,
282 (void*)((int)lPtr
->selectedItem
));
288 WMGetListItem(WMList
*lPtr
, int row
)
292 listPtr
= lPtr
->items
;
295 listPtr
= listPtr
->nextPtr
;
303 WMSetListUserDrawProc(WMList
*lPtr
, WMListDrawProc
*proc
)
305 lPtr
->flags
.userDrawn
= 1;
311 WMSetListUserDrawItemHeight(WMList
*lPtr
, unsigned short height
)
315 lPtr
->flags
.userItemHeight
= 1;
316 lPtr
->itemHeight
= height
;
318 updateGeometry(lPtr
);
323 WMClearList(WMList
*lPtr
)
325 WMListItem
*item
, *tmp
;
326 int oldSelected
= lPtr
->selectedItem
;
338 lPtr
->selectedItem
= -1;
341 WMDeleteIdleHandler(lPtr
->idleID
);
344 if (lPtr
->view
->flags
.realized
) {
345 updateScroller(lPtr
);
347 if (oldSelected
!= -1)
348 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
,
354 WMSetListAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
356 lPtr
->action
= action
;
357 lPtr
->clientData
= clientData
;
362 WMSetListDoubleAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
364 lPtr
->doubleAction
= action
;
365 lPtr
->doubleClientData
= clientData
;
370 WMGetListSelectedItem(WMList
*lPtr
)
372 int i
= lPtr
->selectedItem
;
375 if (lPtr
->selectedItem
< 0)
380 item
= item
->nextPtr
;
387 WMGetListSelectedItemRow(WMList
*lPtr
)
389 return lPtr
->selectedItem
;
394 WMGetListItemHeight(WMList
*lPtr
)
396 return lPtr
->itemHeight
;
401 WMSetListPosition(WMList
*lPtr
, int row
)
404 if (lPtr
->topItem
+ lPtr
->fullFitLines
> lPtr
->itemCount
)
405 lPtr
->topItem
= lPtr
->itemCount
- lPtr
->fullFitLines
;
407 if (lPtr
->topItem
< 0)
410 if (lPtr
->view
->flags
.realized
)
411 updateScroller(lPtr
);
416 WMSetListBottomPosition(WMList
*lPtr
, int row
)
418 if (lPtr
->itemCount
> lPtr
->fullFitLines
) {
419 lPtr
->topItem
= row
- lPtr
->fullFitLines
;
420 if (lPtr
->topItem
< 0)
422 if (lPtr
->view
->flags
.realized
)
423 updateScroller(lPtr
);
429 WMGetListNumberOfRows(WMList
*lPtr
)
431 return lPtr
->itemCount
;
435 WMGetListPosition(WMList
*lPtr
)
437 return lPtr
->topItem
;
442 vScrollCallBack(WMWidget
*scroller
, void *self
)
444 WMList
*lPtr
= (WMList
*)self
;
445 WMScroller
*sPtr
= (WMScroller
*)scroller
;
447 int topItem
= lPtr
->topItem
;
449 height
= lPtr
->view
->size
.height
- 4;
451 switch (WMGetScrollerHitPart(sPtr
)) {
452 case WSDecrementLine
:
453 if (lPtr
->topItem
> 0) {
456 updateScroller(lPtr
);
460 case WSDecrementPage
:
461 if (lPtr
->topItem
> 0) {
462 lPtr
->topItem
-= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
463 if (lPtr
->topItem
< 0)
466 updateScroller(lPtr
);
471 case WSIncrementLine
:
472 if (lPtr
->topItem
+ lPtr
->fullFitLines
< lPtr
->itemCount
) {
475 updateScroller(lPtr
);
479 case WSIncrementPage
:
480 if (lPtr
->topItem
+ lPtr
->fullFitLines
< lPtr
->itemCount
) {
481 lPtr
->topItem
+= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
483 if (lPtr
->topItem
+ lPtr
->fullFitLines
> lPtr
->itemCount
)
484 lPtr
->topItem
= lPtr
->itemCount
- lPtr
->fullFitLines
;
486 updateScroller(lPtr
);
492 int oldTopItem
= lPtr
->topItem
;
494 lPtr
->topItem
= WMGetScrollerValue(lPtr
->vScroller
) *
495 (float)(lPtr
->itemCount
- lPtr
->fullFitLines
);
497 if (oldTopItem
!= lPtr
->topItem
)
508 if (lPtr
->topItem
!= topItem
)
509 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
514 paintItem(List
*lPtr
, int index
)
516 WMView
*view
= lPtr
->view
;
517 W_Screen
*scr
= view
->screen
;
518 int width
, height
, x
, y
;
523 itemPtr
= lPtr
->items
;
525 itemPtr
= itemPtr
->nextPtr
;
527 width
= lPtr
->view
->size
.width
- 2 - 19;
528 height
= lPtr
->itemHeight
;
530 y
= 2 + (index
-lPtr
->topItem
) * lPtr
->itemHeight
+ 1;
532 if (lPtr
->flags
.userDrawn
) {
536 rect
.size
.width
= width
;
537 rect
.size
.height
= height
;
541 flags
= itemPtr
->uflags
;
542 if (itemPtr
->disabled
)
543 flags
|= WLDSDisabled
;
544 if (itemPtr
->selected
)
545 flags
|= WLDSSelected
;
546 if (itemPtr
->isBranch
)
547 flags
|= WLDSIsBranch
;
550 (*lPtr
->draw
)(lPtr
, index
, view
->window
, itemPtr
->text
, flags
,
553 if (itemPtr
->selected
)
554 XFillRectangle(scr
->display
, view
->window
, WMColorGC(scr
->white
), x
, y
,
557 XClearArea(scr
->display
, view
->window
, x
, y
, width
, height
, False
);
559 W_PaintText(view
, view
->window
, scr
->normalFont
, x
+4, y
, width
,
560 WALeft
, WMColorGC(scr
->black
), False
,
561 itemPtr
->text
, strlen(itemPtr
->text
));
568 paintList(List
*lPtr
)
570 W_Screen
*scrPtr
= lPtr
->view
->screen
;
573 if (!lPtr
->view
->flags
.mapped
)
576 if (lPtr
->itemCount
>0) {
577 if (lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
> lPtr
->itemCount
) {
578 lim
= lPtr
->itemCount
- lPtr
->topItem
;
579 XClearArea(scrPtr
->display
, lPtr
->view
->window
, 19,
580 2+lim
*lPtr
->itemHeight
, lPtr
->view
->size
.width
-21,
581 lPtr
->view
->size
.height
-lim
*lPtr
->itemHeight
-3, False
);
583 lim
= lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
;
585 for (i
= lPtr
->topItem
; i
< lPtr
->topItem
+ lim
; i
++) {
589 XClearWindow(scrPtr
->display
, lPtr
->view
->window
);
591 W_DrawRelief(scrPtr
, lPtr
->view
->window
, 0, 0, lPtr
->view
->size
.width
,
592 lPtr
->view
->size
.height
, WRSunken
);
597 scrollTo(List
*lPtr
, int newTop
)
604 updateScroller(List
*lPtr
)
606 float knobProportion
, floatValue
, tmp
;
609 WMDeleteIdleHandler(lPtr
->idleID
);
614 if (lPtr
->itemCount
== 0 || lPtr
->itemCount
<= lPtr
->fullFitLines
)
615 WMSetScrollerParameters(lPtr
->vScroller
, 0, 1);
617 tmp
= lPtr
->fullFitLines
;
618 knobProportion
= tmp
/(float)lPtr
->itemCount
;
620 floatValue
= (float)lPtr
->topItem
/(float)(lPtr
->itemCount
- lPtr
->fullFitLines
);
622 WMSetScrollerParameters(lPtr
->vScroller
, floatValue
, knobProportion
);
628 handleEvents(XEvent
*event
, void *data
)
630 List
*lPtr
= (List
*)data
;
632 CHECK_CLASS(data
, WC_List
);
635 switch (event
->type
) {
637 if (event
->xexpose
.count
!=0)
651 WMFindRowOfListItemWithTitle(WMList
*lPtr
, char *title
)
657 for (i
=0, item
=lPtr
->items
; item
!=NULL
; item
=item
->nextPtr
, i
++) {
658 if (strcmp(item
->text
, title
)==0) {
669 WMSelectListItem(WMList
*lPtr
, int row
)
674 if (row
>= lPtr
->itemCount
)
677 /* the check below must be changed when the multiple selection is
680 if (!lPtr
->flags
.allowMultipleSelection
&& row
== lPtr
->selectedItem
)
685 if (!lPtr
->flags
.allowMultipleSelection
) {
686 /* unselect previous selected item */
687 if (lPtr
->selectedItem
>= 0) {
688 itemPtr
= lPtr
->items
;
689 for (i
=0; i
<lPtr
->selectedItem
; i
++)
690 itemPtr
= itemPtr
->nextPtr
;
692 if (itemPtr
->selected
) {
693 itemPtr
->selected
= 0;
694 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
695 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
)
702 if (!lPtr
->flags
.allowMultipleSelection
) {
703 lPtr
->selectedItem
= -1;
705 WMPostNotificationName(WMListSelectionDidChangeNotification
,
706 lPtr
, (void*)((int)lPtr
->selectedItem
));
712 itemPtr
= lPtr
->items
;
713 for (i
=0; i
<row
; i
++)
714 itemPtr
= itemPtr
->nextPtr
;
715 if (lPtr
->flags
.allowMultipleSelection
)
716 itemPtr
->selected
= !itemPtr
->selected
;
718 itemPtr
->selected
= 1;
720 if (lPtr
->view
->flags
.mapped
) {
721 paintItem(lPtr
, row
);
723 if ((row
-lPtr
->topItem
+lPtr
->fullFitLines
)*lPtr
->itemHeight
724 > lPtr
->view
->size
.height
-2)
725 W_DrawRelief(lPtr
->view
->screen
, lPtr
->view
->window
, 0, 0,
726 lPtr
->view
->size
.width
, lPtr
->view
->size
.height
,
729 lPtr
->selectedItem
= row
;
731 WMPostNotificationName(WMListSelectionDidChangeNotification
, lPtr
,
732 (void*)((int)lPtr
->selectedItem
));
737 getItemIndexAt(List
*lPtr
, int clickY
)
741 index
= (clickY
- 2) / lPtr
->itemHeight
+ lPtr
->topItem
;
743 if (index
< 0 || index
>= lPtr
->itemCount
)
751 handleActionEvents(XEvent
*event
, void *data
)
753 List
*lPtr
= (List
*)data
;
755 int topItem
= lPtr
->topItem
;
757 CHECK_CLASS(data
, WC_List
);
759 switch (event
->type
) {
761 lPtr
->flags
.buttonPressed
= 0;
762 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
764 if (tmp
== lPtr
->selectedItem
&& tmp
>= 0) {
766 (*lPtr
->action
)(lPtr
, lPtr
->clientData
);
771 lPtr
->flags
.buttonPressed
= lPtr
->flags
.buttonWasPressed
;
772 lPtr
->flags
.buttonWasPressed
= 0;
776 lPtr
->flags
.buttonWasPressed
= lPtr
->flags
.buttonPressed
;
777 lPtr
->flags
.buttonPressed
= 0;
781 if (event
->xbutton
.x
> WMWidgetWidth(lPtr
->vScroller
)) {
782 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
783 lPtr
->flags
.buttonPressed
= 1;
786 if (tmp
== lPtr
->selectedItem
&& WMIsDoubleClick(event
)) {
787 WMSelectListItem(lPtr
, tmp
);
788 if (lPtr
->doubleAction
)
789 (*lPtr
->doubleAction
)(lPtr
, lPtr
->doubleClientData
);
791 WMSelectListItem(lPtr
, tmp
);
798 if (lPtr
->flags
.buttonPressed
) {
799 tmp
= getItemIndexAt(lPtr
, event
->xmotion
.y
);
800 if (tmp
>=0 && tmp
!= lPtr
->selectedItem
) {
801 WMSelectListItem(lPtr
, tmp
);
806 if (lPtr
->topItem
!= topItem
)
807 WMPostNotificationName(WMListDidScrollNotification
, lPtr
, NULL
);
812 updateGeometry(WMList
*lPtr
)
814 lPtr
->fullFitLines
= (lPtr
->view
->size
.height
- 4) / lPtr
->itemHeight
;
815 if (lPtr
->fullFitLines
* lPtr
->itemHeight
< lPtr
->view
->size
.height
- 4) {
816 lPtr
->flags
.dontFitAll
= 1;
818 lPtr
->flags
.dontFitAll
= 0;
821 if (lPtr
->itemCount
- lPtr
->topItem
<= lPtr
->fullFitLines
) {
822 lPtr
->topItem
= lPtr
->itemCount
- lPtr
->fullFitLines
;
823 if (lPtr
->topItem
< 0)
827 updateScroller(lPtr
);
832 resizeList(WMList
*lPtr
, unsigned int width
, unsigned int height
)
834 W_ResizeView(lPtr
->view
, width
, height
);
836 WMResizeWidget(lPtr
->vScroller
, 1, height
-2);
838 updateGeometry(lPtr
);
843 destroyList(List
*lPtr
)
848 WMDeleteIdleHandler(lPtr
->idleID
);
851 while (lPtr
->items
!=NULL
) {
852 itemPtr
= lPtr
->items
;
856 lPtr
->items
= itemPtr
->nextPtr
;