8 typedef struct W_List
{
12 WMListItem
*items
; /* array of items */
19 short topItem
; /* index of first visible item */
21 short fullFitLines
; /* no of lines that fit entirely */
25 void *doubleClientData
;
26 WMAction
*doubleAction
;
30 WMHandlerID
*idleID
; /* for updating the scroller after
33 WMScroller
*vScroller
;
35 WMScroller *hScroller;
39 unsigned int allowMultipleSelection
:1;
40 unsigned int userDrawn
:1;
41 unsigned int userItemHeight
:1;
43 unsigned int dontFitAll
:1; /* 1 = last item won't be fully visible */
44 unsigned int redrawPending
:1;
45 unsigned int buttonPressed
:1;
46 unsigned int buttonWasPressed
:1;
52 #define DEFAULT_WIDTH 150
53 #define DEFAULT_HEIGHT 150
56 static void destroyList(List
*lPtr
);
57 static void paintList(List
*lPtr
);
60 static void handleEvents(XEvent
*event
, void *data
);
61 static void handleActionEvents(XEvent
*event
, void *data
);
62 static void updateScroller(List
*lPtr
);
64 static void vScrollCallBack(WMWidget
*scroller
, void *self
);
66 static void resizeList();
69 W_ViewProcedureTable _ListViewProcedures
= {
78 WMCreateList(WMWidget
*parent
)
81 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
83 lPtr
= wmalloc(sizeof(List
));
84 memset(lPtr
, 0, sizeof(List
));
86 lPtr
->widgetClass
= WC_List
;
88 lPtr
->view
= W_CreateView(W_VIEW(parent
));
93 lPtr
->view
->self
= lPtr
;
95 WMCreateEventHandler(lPtr
->view
, ExposureMask
|StructureNotifyMask
96 |ClientMessageMask
, handleEvents
, lPtr
);
98 WMCreateEventHandler(lPtr
->view
, ButtonPressMask
|ButtonReleaseMask
99 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
,
100 handleActionEvents
, lPtr
);
102 lPtr
->itemHeight
= scrPtr
->normalFont
->height
+ 1;
104 /* create the vertical scroller */
105 lPtr
->vScroller
= WMCreateScroller(lPtr
);
106 WMMoveWidget(lPtr
->vScroller
, 1, 1);
107 WMSetScrollerArrowsPosition(lPtr
->vScroller
, WSAMaxEnd
);
109 WMSetScrollerAction(lPtr
->vScroller
, vScrollCallBack
, lPtr
);
111 /* make the scroller map itself when it's realized */
112 WMMapWidget(lPtr
->vScroller
);
114 resizeList(lPtr
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
116 lPtr
->selectedItem
= -1;
124 WMAddSortedListItem(WMList
*lPtr
, char *text
)
130 item
= wmalloc(sizeof(WMListItem
));
131 memset(item
, 0, sizeof(WMListItem
));
132 item
->text
= wstrdup(text
);
136 } else if (strcmp(lPtr
->items
->text
, text
) > 0) {
137 item
->nextPtr
= lPtr
->items
;
145 while (tmp
->nextPtr
) {
146 if (strcmp(tmp
->nextPtr
->text
, text
) >= 0) {
147 item
->nextPtr
= tmp
->nextPtr
;
162 if (lPtr
->selectedItem
>= index
)
163 lPtr
->selectedItem
++;
165 /* update the scroller when idle, so that we don't waste time
166 * updating it when another item is going to be added later */
168 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
177 WMInsertListItem(WMList
*lPtr
, int row
, char *text
)
180 WMListItem
*tmp
= lPtr
->items
;
182 CHECK_CLASS(lPtr
, WC_List
);
184 item
= wmalloc(sizeof(WMListItem
));
185 memset(item
, 0, sizeof(WMListItem
));
186 item
->text
= wstrdup(text
);
188 if (lPtr
->selectedItem
>= row
&& lPtr
->selectedItem
>= 0)
189 lPtr
->selectedItem
++;
191 if (lPtr
->items
==NULL
) {
193 } else if (row
== 0) {
194 item
->nextPtr
= lPtr
->items
;
196 } else if (row
< 0) {
201 row
= lPtr
->itemCount
;
206 item
->nextPtr
= tmp
->nextPtr
;
212 /* update the scroller when idle, so that we don't waste time
213 * updating it when another item is going to be added later */
215 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
223 WMRemoveListItem(WMList
*lPtr
, int row
)
228 CHECK_CLASS(lPtr
, WC_List
);
230 if (row
< 0 || row
>= lPtr
->itemCount
)
233 if (lPtr
->selectedItem
== row
)
234 lPtr
->selectedItem
= -1;
235 else if (lPtr
->selectedItem
> row
)
236 lPtr
->selectedItem
--;
238 if (row
<= lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
)
240 if (lPtr
->topItem
< 0)
244 if (lPtr
->items
->text
)
245 free(lPtr
->items
->text
);
247 tmp
= lPtr
->items
->nextPtr
;
254 llist
= llist
->nextPtr
;
255 tmp
= llist
->nextPtr
;
256 llist
->nextPtr
= llist
->nextPtr
->nextPtr
;
267 lPtr
->idleID
= WMAddIdleHandler((WMCallback
*)updateScroller
, lPtr
);
274 WMGetListItem(WMList
*lPtr
, int row
)
278 listPtr
= lPtr
->items
;
281 listPtr
= listPtr
->nextPtr
;
289 WMSetListUserDrawProc(WMList
*lPtr
, WMListDrawProc
*proc
)
291 lPtr
->flags
.userDrawn
= 1;
297 WMSetListUserDrawItemHeight(WMList
*lPtr
, unsigned short height
)
301 lPtr
->flags
.userItemHeight
= 1;
302 lPtr
->itemHeight
= height
;
307 WMClearList(WMList
*lPtr
)
309 WMListItem
*item
, *tmp
;
321 lPtr
->selectedItem
= -1;
324 WMDeleteIdleHandler(lPtr
->idleID
);
327 if (lPtr
->view
->flags
.realized
) {
328 updateScroller(lPtr
);
334 WMSetListAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
336 lPtr
->action
= action
;
337 lPtr
->clientData
= clientData
;
342 WMSetListDoubleAction(WMList
*lPtr
, WMAction
*action
, void *clientData
)
344 lPtr
->doubleAction
= action
;
345 lPtr
->doubleClientData
= clientData
;
350 WMGetListSelectedItem(WMList
*lPtr
)
352 int i
= lPtr
->selectedItem
;
355 if (lPtr
->selectedItem
< 0)
360 item
= item
->nextPtr
;
367 WMGetListSelectedItemRow(WMList
*lPtr
)
369 return lPtr
->selectedItem
;
374 WMSetListPosition(WMList
*lPtr
, int row
)
377 if (lPtr
->topItem
+ lPtr
->fullFitLines
> lPtr
->itemCount
)
378 lPtr
->topItem
= lPtr
->itemCount
- lPtr
->fullFitLines
;
380 if (lPtr
->topItem
< 0)
383 if (lPtr
->view
->flags
.realized
)
384 updateScroller(lPtr
);
389 WMSetListBottomPosition(WMList
*lPtr
, int row
)
391 if (lPtr
->itemCount
> lPtr
->fullFitLines
) {
392 lPtr
->topItem
= row
- lPtr
->fullFitLines
;
393 if (lPtr
->topItem
< 0)
395 if (lPtr
->view
->flags
.realized
)
396 updateScroller(lPtr
);
402 WMGetListNumberOfRows(WMList
*lPtr
)
404 return lPtr
->itemCount
;
408 WMGetListPosition(WMList
*lPtr
)
410 return lPtr
->topItem
;
415 vScrollCallBack(WMWidget
*scroller
, void *self
)
417 WMList
*lPtr
= (WMList
*)self
;
418 WMScroller
*sPtr
= (WMScroller
*)scroller
;
421 height
= lPtr
->view
->size
.height
- 4;
423 switch (WMGetScrollerHitPart(sPtr
)) {
424 case WSDecrementLine
:
425 if (lPtr
->topItem
> 0) {
428 updateScroller(lPtr
);
432 case WSDecrementPage
:
433 if (lPtr
->topItem
> 0) {
434 lPtr
->topItem
-= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
435 if (lPtr
->topItem
< 0)
438 updateScroller(lPtr
);
443 case WSIncrementLine
:
444 if (lPtr
->topItem
+ lPtr
->fullFitLines
< lPtr
->itemCount
) {
447 updateScroller(lPtr
);
451 case WSIncrementPage
:
452 if (lPtr
->topItem
+ lPtr
->fullFitLines
< lPtr
->itemCount
) {
453 lPtr
->topItem
+= lPtr
->fullFitLines
-(1-lPtr
->flags
.dontFitAll
)-1;
455 if (lPtr
->topItem
+ lPtr
->fullFitLines
> lPtr
->itemCount
)
456 lPtr
->topItem
= lPtr
->itemCount
- lPtr
->fullFitLines
;
458 updateScroller(lPtr
);
464 int oldTopItem
= lPtr
->topItem
;
466 lPtr
->topItem
= WMGetScrollerValue(lPtr
->vScroller
) *
467 (float)(lPtr
->itemCount
- lPtr
->fullFitLines
);
469 if (oldTopItem
!= lPtr
->topItem
)
483 paintItem(List
*lPtr
, int index
)
485 WMView
*view
= lPtr
->view
;
486 W_Screen
*scr
= view
->screen
;
487 int width
, height
, x
, y
;
492 itemPtr
= lPtr
->items
;
494 itemPtr
= itemPtr
->nextPtr
;
496 width
= lPtr
->view
->size
.width
- 2 - 19;
497 height
= lPtr
->itemHeight
;
499 y
= 2 + (index
-lPtr
->topItem
) * lPtr
->itemHeight
+ 1;
501 if (lPtr
->flags
.userDrawn
) {
505 rect
.size
.width
= width
;
506 rect
.size
.height
= height
;
510 flags
= itemPtr
->uflags
;
511 if (itemPtr
->disabled
)
512 flags
|= WLDSDisabled
;
513 if (itemPtr
->selected
)
514 flags
|= WLDSSelected
;
515 if (itemPtr
->isBranch
)
516 flags
|= WLDSIsBranch
;
519 (*lPtr
->draw
)(lPtr
, view
->window
, itemPtr
->text
, flags
, &rect
);
522 if (itemPtr
->selected
)
523 XFillRectangle(scr
->display
, view
->window
, W_GC(scr
->white
), x
, y
,
526 XClearArea(scr
->display
, view
->window
, x
, y
, width
, height
, False
);
528 W_PaintText(view
, view
->window
, scr
->normalFont
, x
+4, y
, width
,
529 WALeft
, W_GC(scr
->black
), False
,
530 itemPtr
->text
, strlen(itemPtr
->text
));
537 paintList(List
*lPtr
)
539 W_Screen
*scrPtr
= lPtr
->view
->screen
;
542 if (!lPtr
->view
->flags
.mapped
)
545 if (lPtr
->itemCount
>0) {
546 if (lPtr
->topItem
+lPtr
->fullFitLines
+lPtr
->flags
.dontFitAll
> lPtr
->itemCount
) {
547 lim
= lPtr
->itemCount
- lPtr
->topItem
;
548 XClearArea(scrPtr
->display
, lPtr
->view
->window
, 19,
549 2+lim
*lPtr
->itemHeight
, lPtr
->view
->size
.width
-21,
550 lPtr
->view
->size
.height
-lim
*lPtr
->itemHeight
-3, False
);
552 lim
= lPtr
->fullFitLines
+ lPtr
->flags
.dontFitAll
;
554 for (i
= lPtr
->topItem
; i
< lPtr
->topItem
+ lim
; i
++) {
558 XClearWindow(scrPtr
->display
, lPtr
->view
->window
);
560 W_DrawRelief(scrPtr
, lPtr
->view
->window
, 0, 0, lPtr
->view
->size
.width
,
561 lPtr
->view
->size
.height
, WRSunken
);
566 scrollTo(List
*lPtr
, int newTop
)
573 updateScroller(List
*lPtr
)
575 float knobProportion
, floatValue
, tmp
;
578 WMDeleteIdleHandler(lPtr
->idleID
);
583 if (lPtr
->itemCount
== 0 || lPtr
->itemCount
<= lPtr
->fullFitLines
)
584 WMSetScrollerParameters(lPtr
->vScroller
, 0, 1);
586 tmp
= lPtr
->fullFitLines
;
587 knobProportion
= tmp
/(float)lPtr
->itemCount
;
589 floatValue
= (float)lPtr
->topItem
/(float)(lPtr
->itemCount
- lPtr
->fullFitLines
);
591 WMSetScrollerParameters(lPtr
->vScroller
, floatValue
, knobProportion
);
597 handleEvents(XEvent
*event
, void *data
)
599 List
*lPtr
= (List
*)data
;
601 CHECK_CLASS(data
, WC_List
);
604 switch (event
->type
) {
606 if (event
->xexpose
.count
!=0)
620 WMFindRowOfListItemWithTitle(WMList
*lPtr
, char *title
)
626 for (i
=0, item
=lPtr
->items
; item
!=NULL
; item
=item
->nextPtr
, i
++) {
627 if (strcmp(item
->text
, title
)==0) {
638 WMSelectListItem(WMList
*lPtr
, int row
)
643 if (row
>= lPtr
->itemCount
)
646 if (!lPtr
->flags
.allowMultipleSelection
) {
647 /* unselect previous selected item */
648 if (lPtr
->selectedItem
>= 0) {
649 itemPtr
= lPtr
->items
;
650 for (i
=0; i
<lPtr
->selectedItem
; i
++)
651 itemPtr
= itemPtr
->nextPtr
;
653 if (itemPtr
->selected
) {
654 itemPtr
->selected
= 0;
655 if (lPtr
->view
->flags
.mapped
&& i
>=lPtr
->topItem
656 && i
<=lPtr
->topItem
+lPtr
->fullFitLines
)
663 if (!lPtr
->flags
.allowMultipleSelection
)
664 lPtr
->selectedItem
= -1;
669 itemPtr
= lPtr
->items
;
670 for (i
=0; i
<row
; i
++)
671 itemPtr
= itemPtr
->nextPtr
;
672 if (lPtr
->flags
.allowMultipleSelection
)
673 itemPtr
->selected
= !itemPtr
->selected
;
675 itemPtr
->selected
= 1;
677 if (lPtr
->view
->flags
.mapped
) {
678 paintItem(lPtr
, row
);
680 if ((row
-lPtr
->topItem
+lPtr
->fullFitLines
)*lPtr
->itemHeight
681 > lPtr
->view
->size
.height
-2)
682 W_DrawRelief(lPtr
->view
->screen
, lPtr
->view
->window
, 0, 0,
683 lPtr
->view
->size
.width
, lPtr
->view
->size
.height
,
686 lPtr
->selectedItem
= row
;
691 getItemIndexAt(List
*lPtr
, int clickY
)
695 index
= (clickY
- 2) / lPtr
->itemHeight
+ lPtr
->topItem
;
697 if (index
< 0 || index
>= lPtr
->itemCount
)
705 handleActionEvents(XEvent
*event
, void *data
)
707 List
*lPtr
= (List
*)data
;
710 CHECK_CLASS(data
, WC_List
);
712 switch (event
->type
) {
714 lPtr
->flags
.buttonPressed
= 0;
715 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
716 if (tmp
== lPtr
->selectedItem
&& tmp
>= 0) {
717 if (WMIsDoubleClick(event
)) {
718 if (lPtr
->doubleAction
)
719 (*lPtr
->doubleAction
)(lPtr
, lPtr
->doubleClientData
);
722 (*lPtr
->action
)(lPtr
, lPtr
->clientData
);
728 lPtr
->flags
.buttonPressed
= lPtr
->flags
.buttonWasPressed
;
729 lPtr
->flags
.buttonWasPressed
= 0;
733 lPtr
->flags
.buttonWasPressed
= lPtr
->flags
.buttonPressed
;
734 lPtr
->flags
.buttonPressed
= 0;
738 if (event
->xbutton
.x
> WMWidgetWidth(lPtr
->vScroller
)) {
739 tmp
= getItemIndexAt(lPtr
, event
->xbutton
.y
);
742 WMSelectListItem(lPtr
, tmp
);
743 lPtr
->selectedItem
= tmp
;
745 lPtr
->flags
.buttonPressed
= 1;
750 if (lPtr
->flags
.buttonPressed
) {
751 tmp
= getItemIndexAt(lPtr
, event
->xmotion
.y
);
752 if (tmp
>=0 && tmp
!= lPtr
->selectedItem
) {
753 WMSelectListItem(lPtr
, tmp
);
754 lPtr
->selectedItem
= tmp
;
763 resizeList(WMList
*lPtr
, unsigned int width
, unsigned int height
)
765 W_ResizeView(lPtr
->view
, width
, height
);
767 WMResizeWidget(lPtr
->vScroller
, 1, height
-2);
769 lPtr
->fullFitLines
= (height
- 4) / lPtr
->itemHeight
;
770 if (lPtr
->fullFitLines
* lPtr
->itemHeight
< height
-4) {
771 lPtr
->flags
.dontFitAll
= 1;
773 lPtr
->flags
.dontFitAll
= 0;
779 destroyList(List
*lPtr
)
784 WMDeleteIdleHandler(lPtr
->idleID
);
787 while (lPtr
->items
!=NULL
) {
788 itemPtr
= lPtr
->items
;
792 lPtr
->items
= itemPtr
->nextPtr
;