6 #include <math.h> /* for : double rint (double) */
10 typedef struct W_Browser
{
18 short usedColumnCount
; /* columns actually being used */
21 short maxVisibleColumns
;
22 short firstVisibleColumn
;
33 void *doubleClientData
;
34 WMAction
*doubleAction
;
36 WMBrowserDelegate
*delegate
;
43 unsigned int isTitled
:1;
44 unsigned int allowMultipleSelection
:1;
45 unsigned int hasScroller
:1;
48 unsigned int loaded
:1;
49 unsigned int loadingColumn
:1;
54 #define COLUMN_SPACING 4
55 #define TITLE_SPACING 2
57 #define DEFAULT_WIDTH 305
58 #define DEFAULT_HEIGHT 200
59 #define DEFAULT_HAS_SCROLLER True
60 #define DEFAULT_TITLE_HEIGHT 20
61 #define DEFAULT_IS_TITLED True
62 #define DEFAULT_MAX_VISIBLE_COLUMNS 2
63 #define DEFAULT_SEPARATOR "/"
65 #define MIN_VISIBLE_COLUMNS 1
66 #define MAX_VISIBLE_COLUMNS 32
69 #define COLUMN_IS_VISIBLE(b, c) ((c) >= (b)->firstVisibleColumn \
70 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
73 static void handleEvents(XEvent
*event
, void *data
);
74 static void destroyBrowser(WMBrowser
*bPtr
);
76 static void setupScroller(WMBrowser
*bPtr
);
78 static void scrollToColumn(WMBrowser
*bPtr
, int column
, Bool updateScroller
);
80 static void paintItem(WMList
*lPtr
, int index
, Drawable d
, char *text
,
81 int state
, WMRect
*rect
);
83 static void loadColumn(WMBrowser
*bPtr
, int column
);
85 static void removeColumn(WMBrowser
*bPtr
, int column
);
87 static char* createTruncatedString(WMFont
*font
, char *text
, int *textLen
,
90 static void willResizeBrowser(W_ViewDelegate
*, WMView
*,
91 unsigned int*, unsigned int*);
93 W_ViewDelegate _BrowserViewDelegate
= {
104 WMCreateBrowser(WMWidget
*parent
)
109 wassertrv(parent
, NULL
);
111 bPtr
= wmalloc(sizeof(WMBrowser
));
112 memset(bPtr
, 0, sizeof(WMBrowser
));
114 bPtr
->widgetClass
= WC_Browser
;
116 bPtr
->view
= W_CreateView(W_VIEW(parent
));
121 bPtr
->view
->self
= bPtr
;
123 bPtr
->view
->delegate
= &_BrowserViewDelegate
;
125 WMCreateEventHandler(bPtr
->view
, ExposureMask
|StructureNotifyMask
126 |ClientMessageMask
, handleEvents
, bPtr
);
128 /* default configuration */
129 bPtr
->flags
.hasScroller
= DEFAULT_HAS_SCROLLER
;
131 bPtr
->titleHeight
= DEFAULT_TITLE_HEIGHT
;
132 bPtr
->flags
.isTitled
= DEFAULT_IS_TITLED
;
133 bPtr
->maxVisibleColumns
= DEFAULT_MAX_VISIBLE_COLUMNS
;
135 WMResizeWidget(bPtr
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
137 bPtr
->pathSeparator
= wstrdup(DEFAULT_SEPARATOR
);
139 if (bPtr
->flags
.hasScroller
)
142 for (i
=0; i
<bPtr
->maxVisibleColumns
; i
++) {
143 WMAddBrowserColumn(bPtr
);
145 bPtr
->usedColumnCount
= 0;
147 bPtr
->selectedColumn
= -1;
154 WMGetBrowserMaxVisibleColumns(WMBrowser
*bPtr
)
156 return bPtr
->maxVisibleColumns
;
161 WMSetBrowserMaxVisibleColumns(WMBrowser
*bPtr
, int columns
)
163 int curMaxVisibleColumns
;
164 int newFirstVisibleColumn
= 0;
168 columns
= (columns
< MIN_VISIBLE_COLUMNS
) ? MIN_VISIBLE_COLUMNS
: columns
;
169 columns
= (columns
> MAX_VISIBLE_COLUMNS
) ? MAX_VISIBLE_COLUMNS
: columns
;
170 if (columns
== bPtr
->maxVisibleColumns
) {
173 curMaxVisibleColumns
= bPtr
->maxVisibleColumns
;
174 bPtr
->maxVisibleColumns
= columns
;
175 /* browser not loaded */
176 if (!bPtr
->flags
.loaded
) {
177 if ((columns
> curMaxVisibleColumns
) && (columns
> bPtr
->columnCount
)) {
178 int i
= columns
- bPtr
->columnCount
;
179 bPtr
->usedColumnCount
= bPtr
->columnCount
;
181 WMAddBrowserColumn(bPtr
);
183 bPtr
->usedColumnCount
= 0;
185 /* browser loaded and columns > curMaxVisibleColumns */
186 } else if (columns
> curMaxVisibleColumns
) {
187 if (bPtr
->usedColumnCount
> columns
) {
188 newFirstVisibleColumn
= bPtr
->usedColumnCount
- columns
;
190 if (newFirstVisibleColumn
> bPtr
->firstVisibleColumn
) {
191 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
193 if (columns
> bPtr
->columnCount
) {
194 int i
= columns
- bPtr
->columnCount
;
195 int curUsedColumnCount
= bPtr
->usedColumnCount
;
196 bPtr
->usedColumnCount
= bPtr
->columnCount
;
198 WMAddBrowserColumn(bPtr
);
200 bPtr
->usedColumnCount
= curUsedColumnCount
;
202 /* browser loaded and columns < curMaxVisibleColumns */
204 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
205 if (newFirstVisibleColumn
+ columns
>= bPtr
->usedColumnCount
) {
206 removeColumn(bPtr
, newFirstVisibleColumn
+ columns
);
209 WMResizeWidget(bPtr
, bPtr
->view
->size
.width
, bPtr
->view
->size
.height
);
210 if (bPtr
->flags
.loaded
) {
211 XClearArea(bPtr
->view
->screen
->display
, bPtr
->view
->window
, 0, 0,
212 bPtr
->view
->size
.width
, bPtr
->titleHeight
, False
);
213 scrollToColumn (bPtr
, newFirstVisibleColumn
, True
);
219 WMGetBrowserNumberOfColumns(WMBrowser
*bPtr
)
221 return bPtr
->usedColumnCount
;
225 WMSetBrowserPathSeparator(WMBrowser
*bPtr
, char *separator
)
227 if (bPtr
->pathSeparator
)
228 wfree(bPtr
->pathSeparator
);
229 bPtr
->pathSeparator
= wstrdup(separator
);
235 drawTitleOfColumn(WMBrowser
*bPtr
, int column
)
237 WMScreen
*scr
= bPtr
->view
->screen
;
240 x
=(column
-bPtr
->firstVisibleColumn
)*(bPtr
->columnSize
.width
+COLUMN_SPACING
);
242 XFillRectangle(scr
->display
, bPtr
->view
->window
, WMColorGC(scr
->darkGray
), x
, 0,
243 bPtr
->columnSize
.width
, bPtr
->titleHeight
);
244 W_DrawRelief(scr
, bPtr
->view
->window
, x
, 0,
245 bPtr
->columnSize
.width
, bPtr
->titleHeight
, WRSunken
);
247 if (column
< bPtr
->usedColumnCount
&& bPtr
->titles
[column
]) {
248 int titleLen
= strlen(bPtr
->titles
[column
]);
249 int widthC
= bPtr
->columnSize
.width
-8;
251 if (WMWidthOfString(scr
->boldFont
, bPtr
->titles
[column
], titleLen
)
253 char *titleBuf
= createTruncatedString(scr
->boldFont
,
254 bPtr
->titles
[column
],
256 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
257 (bPtr
->titleHeight
-WMFontHeight(scr
->boldFont
))/2,
258 bPtr
->columnSize
.width
, WACenter
, WMColorGC(scr
->white
),
259 False
, titleBuf
, titleLen
);
262 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
263 (bPtr
->titleHeight
-WMFontHeight(scr
->boldFont
))/2,
264 bPtr
->columnSize
.width
, WACenter
, WMColorGC(scr
->white
),
265 False
, bPtr
->titles
[column
], titleLen
);
272 WMGetBrowserListInColumn(WMBrowser
*bPtr
, int column
)
274 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
277 return bPtr
->columns
[column
];
282 WMSetBrowserDelegate(WMBrowser
*bPtr
, WMBrowserDelegate
*delegate
)
284 bPtr
->delegate
= delegate
;
289 WMGetBrowserFirstVisibleColumn(WMBrowser
*bPtr
)
291 return bPtr
->firstVisibleColumn
;
296 removeColumn(WMBrowser
*bPtr
, int column
)
298 int i
, clearEnd
, destroyEnd
;
304 column
= (column
< 0) ? 0 : column
;
305 if (column
>= bPtr
->columnCount
) {
308 if (column
< bPtr
->maxVisibleColumns
) {
309 clearEnd
= bPtr
->maxVisibleColumns
;
310 destroyEnd
= bPtr
->columnCount
;
311 bPtr
->columnCount
= bPtr
->maxVisibleColumns
;
314 destroyEnd
= bPtr
->columnCount
;
315 bPtr
->columnCount
= column
;
317 if (column
< bPtr
->usedColumnCount
) {
318 bPtr
->usedColumnCount
= column
;
320 for (i
=column
; i
< clearEnd
; i
++) {
321 if (bPtr
->titles
[i
]) {
322 wfree(bPtr
->titles
[i
]);
323 bPtr
->titles
[i
] = NULL
;
325 WMClearList(bPtr
->columns
[i
]);
327 for (;i
< destroyEnd
; i
++) {
328 if (bPtr
->titles
[i
]) {
329 wfree(bPtr
->titles
[i
]);
330 bPtr
->titles
[i
] = NULL
;
332 WMRemoveNotificationObserverWithName(bPtr
,
333 WMListSelectionDidChangeNotification
,
335 WMDestroyWidget(bPtr
->columns
[i
]);
336 bPtr
->columns
[i
] = NULL
;
338 clist
= wmalloc(sizeof(WMList
*) * (bPtr
->columnCount
));
339 tlist
= wmalloc(sizeof(char*) * (bPtr
->columnCount
));
340 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*) * (bPtr
->columnCount
));
341 memcpy(tlist
, bPtr
->titles
, sizeof(char*) * (bPtr
->columnCount
));
343 wfree(bPtr
->columns
);
344 bPtr
->titles
= tlist
;
345 bPtr
->columns
= clist
;
350 WMGetBrowserSelectedItemInColumn(WMBrowser
*bPtr
, int column
)
352 if ((column
< 0) || (column
>= bPtr
->usedColumnCount
))
355 return WMGetListSelectedItem(bPtr
->columns
[column
]);
361 WMGetBrowserSelectedColumn(WMBrowser
*bPtr
)
363 return bPtr
->selectedColumn
;
368 WMGetBrowserSelectedRowInColumn(WMBrowser
*bPtr
, int column
)
370 if (column
>= 0 && column
< bPtr
->columnCount
) {
371 return WMGetListSelectedItemRow(bPtr
->columns
[column
]);
379 WMSetBrowserColumnTitle(WMBrowser
*bPtr
, int column
, char *title
)
382 assert(column
< bPtr
->usedColumnCount
);
384 if (bPtr
->titles
[column
])
385 wfree(bPtr
->titles
[column
]);
387 bPtr
->titles
[column
] = wstrdup(title
);
389 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
390 drawTitleOfColumn(bPtr
, column
);
396 WMSetBrowserTitled(WMBrowser
*bPtr
, Bool flag
)
399 int columnX
, columnY
;
401 if (bPtr
->flags
.isTitled
== flag
)
406 if (!bPtr
->flags
.isTitled
) {
407 columnY
= TITLE_SPACING
+ bPtr
->titleHeight
;
409 bPtr
->columnSize
.height
-= columnY
;
411 for (i
=0; i
<bPtr
->columnCount
; i
++) {
412 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
,
413 bPtr
->columnSize
.height
);
415 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
417 WMMoveWidget(bPtr
->columns
[i
], columnX
, columnY
);
420 bPtr
->columnSize
.height
+= TITLE_SPACING
+ bPtr
->titleHeight
;
422 for (i
=0; i
<bPtr
->columnCount
; i
++) {
423 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
,
424 bPtr
->columnSize
.height
);
426 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
428 WMMoveWidget(bPtr
->columns
[i
], columnX
, 0);
432 bPtr
->flags
.isTitled
= flag
;
437 WMSortBrowserColumn(WMBrowser
*bPtr
, int column
)
439 WMSortListItems(bPtr
->columns
[column
]);
444 WMSortBrowserColumnWithComparer(WMBrowser
*bPtr
, int column
,
445 WMCompareDataProc
*func
)
447 WMSortListItemsWithComparer(bPtr
->columns
[column
], func
);
453 WMInsertBrowserItem(WMBrowser
*bPtr
, int column
, int row
, char *text
,
458 if (column
< 0 || column
>= bPtr
->columnCount
)
461 item
= WMInsertListItem(bPtr
->columns
[column
], row
, text
);
462 item
->isBranch
= isBranch
;
471 willResizeBrowser(W_ViewDelegate
*self
, WMView
*view
,
472 unsigned int *width
, unsigned int *height
)
474 WMBrowser
*bPtr
= (WMBrowser
*)view
->self
;
475 int cols
= bPtr
->maxVisibleColumns
;
482 bPtr
->columnSize
.width
= (*width
-(cols
-1)*COLUMN_SPACING
) / cols
;
483 bPtr
->columnSize
.height
= *height
;
485 if (bPtr
->flags
.isTitled
) {
486 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
487 bPtr
->columnSize
.height
-= colY
;
492 if (bPtr
->flags
.hasScroller
) {
493 bPtr
->columnSize
.height
-= SCROLLER_WIDTH
+ 4;
495 if (bPtr
->scroller
) {
496 WMResizeWidget(bPtr
->scroller
, *width
-2, 1);
497 WMMoveWidget(bPtr
->scroller
, 1, *height
-SCROLLER_WIDTH
-1);
502 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
503 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
,
504 bPtr
->columnSize
.height
);
506 WMMoveWidget(bPtr
->columns
[i
], colX
, colY
);
508 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
509 colX
+= bPtr
->columnSize
.width
+COLUMN_SPACING
;
516 paintItem(WMList
*lPtr
, int index
, Drawable d
, char *text
, int state
,
519 WMView
*view
= W_VIEW(lPtr
);
520 W_Screen
*scr
= view
->screen
;
521 int width
, height
, x
, y
;
523 width
= rect
->size
.width
;
524 height
= rect
->size
.height
;
528 if (state
& WLDSSelected
)
529 XFillRectangle(scr
->display
, d
, WMColorGC(scr
->white
), x
, y
,
532 XClearArea(scr
->display
, d
, x
, y
, width
, height
, False
);
535 /* Avoid overlaping... */
536 WMFont
*font
= (state
& WLDSIsBranch
) ? scr
->boldFont
: scr
->normalFont
;
537 int textLen
= strlen(text
);
538 int widthC
= (state
& WLDSIsBranch
) ? width
-20 : width
-8;
539 if (WMWidthOfString(font
, text
, textLen
) > widthC
) {
540 char *textBuf
= createTruncatedString(font
, text
, &textLen
, widthC
);
541 W_PaintText(view
, d
, font
, x
+4, y
, widthC
,
542 WALeft
, WMColorGC(scr
->black
), False
, textBuf
, textLen
);
545 W_PaintText(view
, d
, font
, x
+4, y
, widthC
,
546 WALeft
, WMColorGC(scr
->black
), False
, text
, textLen
);
550 if (state
& WLDSIsBranch
) {
551 XDrawLine(scr
->display
, d
, WMColorGC(scr
->darkGray
), x
+width
-11, y
+3,
552 x
+width
-6, y
+height
/2);
553 if (state
& WLDSSelected
)
554 XDrawLine(scr
->display
, d
,WMColorGC(scr
->gray
), x
+width
-11, y
+height
-5,
555 x
+width
-6, y
+height
/2);
557 XDrawLine(scr
->display
, d
,WMColorGC(scr
->white
), x
+width
-11, y
+height
-5,
558 x
+width
-6, y
+height
/2);
559 XDrawLine(scr
->display
, d
, WMColorGC(scr
->black
), x
+width
-12, y
+3,
560 x
+width
-12, y
+height
-5);
566 scrollCallback(WMWidget
*scroller
, void *self
)
568 WMBrowser
*bPtr
= (WMBrowser
*)self
;
569 WMScroller
*sPtr
= (WMScroller
*)scroller
;
571 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
573 switch (WMGetScrollerHitPart(sPtr
)) {
574 case WSDecrementLine
:
575 if (bPtr
->firstVisibleColumn
> 0) {
576 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
-1, True
);
580 case WSDecrementPage
:
581 if (bPtr
->firstVisibleColumn
> 0) {
582 newFirst
= bPtr
->firstVisibleColumn
- bPtr
->maxVisibleColumns
;
584 scrollToColumn(bPtr
, newFirst
, True
);
589 case WSIncrementLine
:
590 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
591 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
+1, True
);
595 case WSIncrementPage
:
596 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
597 newFirst
= bPtr
->firstVisibleColumn
+ bPtr
->maxVisibleColumns
;
599 if (newFirst
+bPtr
->maxVisibleColumns
>= bPtr
->columnCount
)
600 newFirst
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
602 scrollToColumn(bPtr
, newFirst
, True
);
609 double value
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
611 floatValue
= WMGetScrollerValue(bPtr
->scroller
);
613 floatValue
= (floatValue
*value
)/value
;
615 newFirst
= rint(floatValue
*(float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
));
617 if (bPtr
->firstVisibleColumn
!= newFirst
)
618 scrollToColumn(bPtr
, newFirst
, False
);
620 WMSetScrollerParameters(bPtr->scroller, floatValue,
621 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
632 #undef LAST_VISIBLE_COLUMN
637 setupScroller(WMBrowser
*bPtr
)
642 y
= bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 1;
644 sPtr
= WMCreateScroller(bPtr
);
645 WMSetScrollerAction(sPtr
, scrollCallback
, bPtr
);
646 WMMoveWidget(sPtr
, 1, y
);
647 WMResizeWidget(sPtr
, bPtr
->view
->size
.width
-2, SCROLLER_WIDTH
);
649 bPtr
->scroller
= sPtr
;
656 WMSetBrowserAction(WMBrowser
*bPtr
, WMAction
*action
, void *clientData
)
658 bPtr
->action
= action
;
659 bPtr
->clientData
= clientData
;
664 WMSetBrowserDoubleAction(WMBrowser
*bPtr
, WMAction
*action
, void *clientData
)
666 bPtr
->doubleAction
= action
;
667 bPtr
->doubleClientData
= clientData
;
672 WMSetBrowserHasScroller(WMBrowser
*bPtr
, int hasScroller
)
674 bPtr
->flags
.hasScroller
= hasScroller
;
680 WMSetBrowserPath(WMBrowser
*bPtr
, char *path
)
683 char *str
= wstrdup(path
);
684 char *tmp
, *retPtr
= NULL
;
686 WMListItem
*listItem
;
688 /* WMLoadBrowserColumnZero must be call first */
689 if (!bPtr
->flags
.loaded
) {
693 removeColumn(bPtr
, 1);
695 WMSelectListItem(bPtr
->columns
[0], -1);
696 WMSetListPosition(bPtr
->columns
[0], 0);
699 tmp
= strtok(str
, bPtr
->pathSeparator
);
701 /* select it in the column */
702 item
= WMFindRowOfListItemWithTitle(bPtr
->columns
[i
], tmp
);
704 retPtr
= &path
[(int)(tmp
- str
)];
707 WMSelectListItem(bPtr
->columns
[i
], item
);
708 WMSetListPosition(bPtr
->columns
[i
], item
);
710 listItem
= WMGetListItem(bPtr
->columns
[i
], item
);
711 if (!listItem
|| !listItem
->isBranch
) {
715 /* load next column */
716 WMAddBrowserColumn(bPtr
);
718 loadColumn(bPtr
, i
+1);
720 tmp
= strtok(NULL
, bPtr
->pathSeparator
);
726 for (i
= bPtr
->usedColumnCount
- 1;
727 (i
> -1) && !WMGetListSelectedItem(bPtr
->columns
[i
]);
730 bPtr
->selectedColumn
= i
;
732 if (bPtr
->columnCount
< bPtr
->maxVisibleColumns
) {
733 int i
= bPtr
->maxVisibleColumns
- bPtr
->columnCount
;
734 int curUsedColumnCount
= bPtr
->usedColumnCount
;
735 bPtr
->usedColumnCount
= bPtr
->columnCount
;
737 WMAddBrowserColumn(bPtr
);
739 bPtr
->usedColumnCount
= curUsedColumnCount
;
742 scrollToColumn(bPtr
, bPtr
->columnCount
- bPtr
->maxVisibleColumns
, True
);
749 WMGetBrowserPath(WMBrowser
*bPtr
)
751 return WMGetBrowserPathToColumn(bPtr
, bPtr
->columnCount
);
757 WMGetBrowserPathToColumn(WMBrowser
*bPtr
, int column
)
763 if (column
>= bPtr
->usedColumnCount
)
764 column
= bPtr
->usedColumnCount
-1;
767 return wstrdup(bPtr
->pathSeparator
);
770 /* calculate size of buffer */
772 for (i
= 0; i
<= column
; i
++) {
773 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
776 size
+= strlen(item
->text
);
780 path
= wmalloc(size
+(column
+1)*strlen(bPtr
->pathSeparator
)+1);
783 for (i
= 0; i
<= column
; i
++) {
784 strcat(path
, bPtr
->pathSeparator
);
785 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
788 strcat(path
, item
->text
);
796 loadColumn(WMBrowser
*bPtr
, int column
)
798 assert(bPtr
->delegate
);
799 assert(bPtr
->delegate
->createRowsForColumn
);
801 bPtr
->flags
.loadingColumn
= 1;
802 (*bPtr
->delegate
->createRowsForColumn
)(bPtr
->delegate
, bPtr
, column
,
803 bPtr
->columns
[column
]);
804 bPtr
->flags
.loadingColumn
= 0;
806 if (bPtr
->delegate
->titleOfColumn
) {
809 title
= (*bPtr
->delegate
->titleOfColumn
)(bPtr
->delegate
, bPtr
, column
);
811 if (bPtr
->titles
[column
])
812 wfree(bPtr
->titles
[column
]);
814 bPtr
->titles
[column
] = wstrdup(title
);
816 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
817 drawTitleOfColumn(bPtr
, column
);
824 paintBrowser(WMBrowser
*bPtr
)
828 if (!bPtr
->view
->flags
.mapped
)
831 W_DrawRelief(bPtr
->view
->screen
, bPtr
->view
->window
, 0,
832 bPtr
->view
->size
.height
-SCROLLER_WIDTH
-2,
833 bPtr
->view
->size
.width
, 22, WRSunken
);
835 if (bPtr
->flags
.isTitled
) {
836 for (i
=0; i
<bPtr
->maxVisibleColumns
; i
++) {
837 drawTitleOfColumn(bPtr
, i
+bPtr
->firstVisibleColumn
);
844 handleEvents(XEvent
*event
, void *data
)
846 WMBrowser
*bPtr
= (WMBrowser
*)data
;
848 CHECK_CLASS(data
, WC_Browser
);
851 switch (event
->type
) {
857 destroyBrowser(bPtr
);
866 scrollToColumn(WMBrowser
*bPtr
, int column
, Bool updateScroller
)
873 if (column
!= bPtr
->firstVisibleColumn
) {
880 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->willScroll
) {
881 (*bPtr
->delegate
->willScroll
)(bPtr
->delegate
, bPtr
);
885 bPtr
->firstVisibleColumn
= column
;
886 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
887 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
888 WMMoveWidget(bPtr
->columns
[i
], x
,
889 WMWidgetView(bPtr
->columns
[i
])->pos
.y
);
890 if (!WMWidgetView(bPtr
->columns
[i
])->flags
.realized
)
891 WMRealizeWidget(bPtr
->columns
[i
]);
892 WMMapWidget(bPtr
->columns
[i
]);
893 x
+= bPtr
->columnSize
.width
+ COLUMN_SPACING
;
895 WMUnmapWidget(bPtr
->columns
[i
]);
899 /* update the scroller */
900 if (updateScroller
) {
901 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
902 float value
, proportion
;
904 value
= bPtr
->firstVisibleColumn
905 /(float)(bPtr
->columnCount
-bPtr
->maxVisibleColumns
);
906 proportion
= bPtr
->maxVisibleColumns
/(float)bPtr
->columnCount
;
907 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
909 WMSetScrollerParameters(bPtr
->scroller
, 0, 1);
913 if (bPtr
->view
->flags
.mapped
)
916 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->didScroll
) {
917 (*bPtr
->delegate
->didScroll
)(bPtr
->delegate
, bPtr
);
923 listCallback(void *self
, void *clientData
)
925 WMBrowser
*bPtr
= (WMBrowser
*)clientData
;
926 WMList
*lPtr
= (WMList
*)self
;
928 static WMListItem
*oldItem
= NULL
;
931 item
= WMGetListSelectedItem(lPtr
);
937 if (oldItem
!= item
) {
938 for (i
=0; i
<bPtr
->columnCount
; i
++) {
939 if (lPtr
== bPtr
->columns
[i
])
942 assert(i
<bPtr
->columnCount
);
944 bPtr
->selectedColumn
= i
;
946 /* columns at right must be cleared */
947 removeColumn(bPtr
, i
+1);
949 if (item
->isBranch
) {
950 WMAddBrowserColumn(bPtr
);
952 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
955 i
= bPtr
->usedColumnCount
-bPtr
->maxVisibleColumns
;
956 scrollToColumn(bPtr
, i
, True
);
957 if (item
->isBranch
) {
958 loadColumn(bPtr
, bPtr
->usedColumnCount
-1);
963 /* call callback for click */
965 (*bPtr
->action
)(bPtr
, bPtr
->clientData
);
972 listDoubleCallback(void *self
, void *clientData
)
974 WMBrowser
*bPtr
= (WMBrowser
*)clientData
;
975 WMList
*lPtr
= (WMList
*)self
;
978 item
= WMGetListSelectedItem(lPtr
);
982 /* call callback for double click */
983 if (bPtr
->doubleAction
)
984 (*bPtr
->doubleAction
)(bPtr
, bPtr
->doubleClientData
);
989 WMLoadBrowserColumnZero(WMBrowser
*bPtr
)
991 if (!bPtr
->flags
.loaded
) {
992 /* create column 0 */
993 WMAddBrowserColumn(bPtr
);
997 /* make column 0 visible */
998 scrollToColumn(bPtr
, 0, True
);
1000 bPtr
->flags
.loaded
= 1;
1006 WMRemoveBrowserItem(WMBrowser
*bPtr
, int column
, int row
)
1010 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
1013 list
= WMGetBrowserListInColumn(bPtr
, column
);
1015 if (row
< 0 || row
>= WMGetListNumberOfRows(list
))
1018 removeColumn(bPtr
, column
+1);
1019 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
1020 scrollToColumn(bPtr
, 0, True
);
1022 scrollToColumn(bPtr
, bPtr
->usedColumnCount
-bPtr
->maxVisibleColumns
,
1025 WMRemoveListItem(list
, row
);
1030 listSelectionObserver(void *observerData
, WMNotification
*notification
)
1032 WMBrowser
*bPtr
= (WMBrowser
*)observerData
;
1034 WMList
*lPtr
= (WMList
*)WMGetNotificationObject(notification
);
1036 for (column
= 0; column
< bPtr
->usedColumnCount
; column
++)
1037 if (bPtr
->columns
[column
] == lPtr
)
1040 /* this can happen when a list is being cleared with WMClearList
1041 * after the column was removed */
1042 if (column
>= bPtr
->usedColumnCount
) {
1046 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr
)) == 0)
1049 bPtr
->selectedColumn
= column
;
1054 WMAddBrowserColumn(WMBrowser
*bPtr
)
1063 if (bPtr
->usedColumnCount
< bPtr
->columnCount
) {
1064 return bPtr
->usedColumnCount
++;
1067 bPtr
->usedColumnCount
++;
1069 if (bPtr
->flags
.isTitled
) {
1070 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
1075 index
= bPtr
->columnCount
;
1076 bPtr
->columnCount
++;
1077 clist
= wmalloc(sizeof(WMList
*)*bPtr
->columnCount
);
1078 tlist
= wmalloc(sizeof(char*)*bPtr
->columnCount
);
1079 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*)*(bPtr
->columnCount
-1));
1080 memcpy(tlist
, bPtr
->titles
, sizeof(char*)*(bPtr
->columnCount
-1));
1082 wfree(bPtr
->columns
);
1084 wfree(bPtr
->titles
);
1085 bPtr
->columns
= clist
;
1086 bPtr
->titles
= tlist
;
1088 bPtr
->titles
[index
] = NULL
;
1090 list
= WMCreateList(bPtr
);
1091 WMSetListAction(list
, listCallback
, bPtr
);
1092 WMSetListDoubleAction(list
, listDoubleCallback
, bPtr
);
1093 WMSetListUserDrawProc(list
, paintItem
);
1094 WMAddNotificationObserver(listSelectionObserver
, bPtr
,
1095 WMListSelectionDidChangeNotification
, list
);
1097 bPtr
->columns
[index
] = list
;
1099 WMResizeWidget(list
, bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
1100 WMMoveWidget(list
, (bPtr
->columnSize
.width
+COLUMN_SPACING
)*index
, colY
);
1101 if (COLUMN_IS_VISIBLE(bPtr
, index
))
1104 /* update the scroller */
1105 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
1106 float value
, proportion
;
1108 value
= bPtr
->firstVisibleColumn
1109 /(float)(bPtr
->columnCount
-bPtr
->maxVisibleColumns
);
1110 proportion
= bPtr
->maxVisibleColumns
/(float)bPtr
->columnCount
;
1111 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
1120 destroyBrowser(WMBrowser
*bPtr
)
1124 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
1125 if (bPtr
->titles
[i
])
1126 wfree(bPtr
->titles
[i
]);
1128 wfree(bPtr
->titles
);
1130 wfree(bPtr
->pathSeparator
);
1132 WMRemoveNotificationObserver(bPtr
);
1139 createTruncatedString(WMFont
*font
, char *text
, int *textLen
, int width
)
1141 int dLen
= WMWidthOfString(font
, ".", 1);
1142 char *textBuf
= (char*)wmalloc((*textLen
)+4);
1144 if (width
>= 3*dLen
) {
1145 int dddLen
= 3*dLen
;
1146 int tmpTextLen
= *textLen
;
1148 strcpy(textBuf
, text
);
1150 && (WMWidthOfString(font
, textBuf
, tmpTextLen
)+dddLen
> width
))
1152 strcpy(textBuf
+tmpTextLen
, "...");
1153 *textLen
= tmpTextLen
+3;
1154 } else if (width
>= 2*dLen
) {
1155 strcpy(textBuf
, "..");
1157 } else if (width
>= dLen
) {
1158 strcpy(textBuf
, ".");