3 #include <math.h> /* for : double rint (double) */
5 typedef struct W_Browser
{
13 short usedColumnCount
; /* columns actually being used */
16 short maxVisibleColumns
;
17 short firstVisibleColumn
;
27 void *doubleClientData
;
28 WMAction
*doubleAction
;
30 WMBrowserDelegate
*delegate
;
37 unsigned int isTitled
:1;
38 unsigned int allowMultipleSelection
:1;
39 unsigned int allowEmptySelection
:1;
40 unsigned int hasScroller
:1;
43 unsigned int loaded
:1;
44 unsigned int loadingColumn
:1;
48 #define COLUMN_SPACING 4
49 #define TITLE_SPACING 2
51 #define DEFAULT_WIDTH 305
52 #define DEFAULT_HEIGHT 200
53 #define DEFAULT_HAS_SCROLLER True
54 #define DEFAULT_TITLE_HEIGHT 20
55 #define DEFAULT_IS_TITLED True
56 #define DEFAULT_MAX_VISIBLE_COLUMNS 2
57 #define DEFAULT_SEPARATOR "/"
59 #define MIN_VISIBLE_COLUMNS 1
60 #define MAX_VISIBLE_COLUMNS 32
62 #define COLUMN_IS_VISIBLE(b, c) ((c) >= (b)->firstVisibleColumn \
63 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
65 static void handleEvents(XEvent
* event
, void *data
);
66 static void destroyBrowser(WMBrowser
* bPtr
);
68 static void setupScroller(WMBrowser
* bPtr
);
70 static void scrollToColumn(WMBrowser
* bPtr
, int column
, Bool updateScroller
);
72 static void paintItem(WMList
* lPtr
, int index
, Drawable d
, char *text
, int state
, WMRect
* rect
);
74 static void loadColumn(WMBrowser
* bPtr
, int column
);
76 static void removeColumn(WMBrowser
* bPtr
, int column
);
78 static char *createTruncatedString(WMFont
* font
, const char *text
, int *textLen
, int width
);
80 static void willResizeBrowser(W_ViewDelegate
*, WMView
*, unsigned int *, unsigned int *);
82 W_ViewDelegate _BrowserViewDelegate
= {
90 WMBrowser
*WMCreateBrowser(WMWidget
* parent
)
95 wassertrv(parent
, NULL
);
97 bPtr
= wmalloc(sizeof(WMBrowser
));
99 bPtr
->widgetClass
= WC_Browser
;
101 bPtr
->view
= W_CreateView(W_VIEW(parent
));
106 bPtr
->view
->self
= bPtr
;
108 bPtr
->view
->delegate
= &_BrowserViewDelegate
;
110 WMCreateEventHandler(bPtr
->view
, ExposureMask
| StructureNotifyMask
111 | ClientMessageMask
, handleEvents
, bPtr
);
113 /* default configuration */
114 bPtr
->flags
.hasScroller
= DEFAULT_HAS_SCROLLER
;
116 bPtr
->titleHeight
= DEFAULT_TITLE_HEIGHT
;
117 bPtr
->flags
.isTitled
= DEFAULT_IS_TITLED
;
118 bPtr
->maxVisibleColumns
= DEFAULT_MAX_VISIBLE_COLUMNS
;
120 WMResizeWidget(bPtr
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
122 bPtr
->pathSeparator
= wstrdup(DEFAULT_SEPARATOR
);
124 if (bPtr
->flags
.hasScroller
)
127 for (i
= 0; i
< bPtr
->maxVisibleColumns
; i
++) {
128 WMAddBrowserColumn(bPtr
);
130 bPtr
->usedColumnCount
= 0;
132 bPtr
->selectedColumn
= -1;
137 void WMSetBrowserAllowMultipleSelection(WMBrowser
* bPtr
, Bool flag
)
141 bPtr
->flags
.allowMultipleSelection
= ((flag
== 0) ? 0 : 1);
142 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
143 WMSetListAllowMultipleSelection(bPtr
->columns
[i
], flag
);
147 void WMSetBrowserAllowEmptySelection(WMBrowser
* bPtr
, Bool flag
)
151 bPtr
->flags
.allowEmptySelection
= ((flag
== 0) ? 0 : 1);
152 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
153 WMSetListAllowEmptySelection(bPtr
->columns
[i
], flag
);
157 int WMGetBrowserMaxVisibleColumns(WMBrowser
* bPtr
)
159 return bPtr
->maxVisibleColumns
;
162 void WMSetBrowserMaxVisibleColumns(WMBrowser
* bPtr
, int columns
)
164 int curMaxVisibleColumns
;
165 int newFirstVisibleColumn
= 0;
167 assert(bPtr
!= NULL
);
169 columns
= (columns
< MIN_VISIBLE_COLUMNS
) ? MIN_VISIBLE_COLUMNS
: columns
;
170 columns
= (columns
> MAX_VISIBLE_COLUMNS
) ? MAX_VISIBLE_COLUMNS
: columns
;
171 if (columns
== bPtr
->maxVisibleColumns
) {
174 curMaxVisibleColumns
= bPtr
->maxVisibleColumns
;
175 bPtr
->maxVisibleColumns
= columns
;
176 /* browser not loaded */
177 if (!bPtr
->flags
.loaded
) {
178 if ((columns
> curMaxVisibleColumns
) && (columns
> bPtr
->columnCount
)) {
179 int i
= columns
- bPtr
->columnCount
;
180 bPtr
->usedColumnCount
= bPtr
->columnCount
;
182 WMAddBrowserColumn(bPtr
);
184 bPtr
->usedColumnCount
= 0;
186 /* browser loaded and columns > curMaxVisibleColumns */
187 } else if (columns
> curMaxVisibleColumns
) {
188 if (bPtr
->usedColumnCount
> columns
) {
189 newFirstVisibleColumn
= bPtr
->usedColumnCount
- columns
;
191 if (newFirstVisibleColumn
> bPtr
->firstVisibleColumn
) {
192 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
194 if (columns
> bPtr
->columnCount
) {
195 int i
= columns
- bPtr
->columnCount
;
196 int curUsedColumnCount
= bPtr
->usedColumnCount
;
197 bPtr
->usedColumnCount
= bPtr
->columnCount
;
199 WMAddBrowserColumn(bPtr
);
201 bPtr
->usedColumnCount
= curUsedColumnCount
;
203 /* browser loaded and columns < curMaxVisibleColumns */
205 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
206 if (newFirstVisibleColumn
+ columns
>= bPtr
->usedColumnCount
) {
207 removeColumn(bPtr
, newFirstVisibleColumn
+ columns
);
210 WMResizeWidget(bPtr
, bPtr
->view
->size
.width
, bPtr
->view
->size
.height
);
211 if (bPtr
->flags
.loaded
) {
212 XClearArea(bPtr
->view
->screen
->display
, bPtr
->view
->window
, 0, 0,
213 bPtr
->view
->size
.width
, bPtr
->titleHeight
, False
);
214 scrollToColumn(bPtr
, newFirstVisibleColumn
, True
);
218 int WMGetBrowserNumberOfColumns(WMBrowser
* bPtr
)
220 return bPtr
->usedColumnCount
;
223 void WMSetBrowserPathSeparator(WMBrowser
* bPtr
, const char *separator
)
225 if (bPtr
->pathSeparator
)
226 wfree(bPtr
->pathSeparator
);
227 bPtr
->pathSeparator
= wstrdup(separator
);
230 static void drawTitleOfColumn(WMBrowser
* bPtr
, int column
)
232 WMScreen
*scr
= bPtr
->view
->screen
;
235 x
= (column
- bPtr
->firstVisibleColumn
) * (bPtr
->columnSize
.width
+ COLUMN_SPACING
);
237 XFillRectangle(scr
->display
, bPtr
->view
->window
, WMColorGC(scr
->darkGray
), x
, 0,
238 bPtr
->columnSize
.width
, bPtr
->titleHeight
);
239 W_DrawRelief(scr
, bPtr
->view
->window
, x
, 0, bPtr
->columnSize
.width
, bPtr
->titleHeight
, WRSunken
);
241 if (column
< bPtr
->usedColumnCount
&& bPtr
->titles
[column
]) {
242 int titleLen
= strlen(bPtr
->titles
[column
]);
243 int widthC
= bPtr
->columnSize
.width
- 8;
245 if (WMWidthOfString(scr
->boldFont
, bPtr
->titles
[column
], titleLen
)
247 char *titleBuf
= createTruncatedString(scr
->boldFont
,
248 bPtr
->titles
[column
],
250 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
251 (bPtr
->titleHeight
- WMFontHeight(scr
->boldFont
)) / 2,
252 bPtr
->columnSize
.width
, WACenter
, scr
->white
, False
, titleBuf
, titleLen
);
255 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
256 (bPtr
->titleHeight
- WMFontHeight(scr
->boldFont
)) / 2,
257 bPtr
->columnSize
.width
, WACenter
, scr
->white
,
258 False
, bPtr
->titles
[column
], titleLen
);
263 WMList
*WMGetBrowserListInColumn(WMBrowser
* bPtr
, int column
)
265 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
268 return bPtr
->columns
[column
];
271 void WMSetBrowserDelegate(WMBrowser
* bPtr
, WMBrowserDelegate
* delegate
)
273 bPtr
->delegate
= delegate
;
276 int WMGetBrowserFirstVisibleColumn(WMBrowser
* bPtr
)
278 return bPtr
->firstVisibleColumn
;
281 static void removeColumn(WMBrowser
* bPtr
, int column
)
283 int i
, clearEnd
, destroyEnd
;
287 assert(bPtr
!= NULL
);
289 column
= (column
< 0) ? 0 : column
;
290 if (column
>= bPtr
->columnCount
) {
293 if (column
< bPtr
->maxVisibleColumns
) {
294 clearEnd
= bPtr
->maxVisibleColumns
;
295 destroyEnd
= bPtr
->columnCount
;
296 bPtr
->columnCount
= bPtr
->maxVisibleColumns
;
299 destroyEnd
= bPtr
->columnCount
;
300 bPtr
->columnCount
= column
;
302 if (column
< bPtr
->usedColumnCount
) {
303 bPtr
->usedColumnCount
= column
;
305 for (i
= column
; i
< clearEnd
; i
++) {
306 if (bPtr
->titles
[i
]) {
307 wfree(bPtr
->titles
[i
]);
308 bPtr
->titles
[i
] = NULL
;
310 WMClearList(bPtr
->columns
[i
]);
312 for (; i
< destroyEnd
; i
++) {
313 if (bPtr
->titles
[i
]) {
314 wfree(bPtr
->titles
[i
]);
315 bPtr
->titles
[i
] = NULL
;
317 WMRemoveNotificationObserverWithName(bPtr
, WMListSelectionDidChangeNotification
, bPtr
->columns
[i
]);
318 WMDestroyWidget(bPtr
->columns
[i
]);
319 bPtr
->columns
[i
] = NULL
;
321 clist
= wmalloc(sizeof(WMList
*) * (bPtr
->columnCount
));
322 tlist
= wmalloc(sizeof(char *) * (bPtr
->columnCount
));
323 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*) * (bPtr
->columnCount
));
324 memcpy(tlist
, bPtr
->titles
, sizeof(char *) * (bPtr
->columnCount
));
326 wfree(bPtr
->columns
);
327 bPtr
->titles
= tlist
;
328 bPtr
->columns
= clist
;
331 WMListItem
*WMGetBrowserSelectedItemInColumn(WMBrowser
* bPtr
, int column
)
333 if ((column
< 0) || (column
>= bPtr
->usedColumnCount
))
336 return WMGetListSelectedItem(bPtr
->columns
[column
]);
339 int WMGetBrowserSelectedColumn(WMBrowser
* bPtr
)
341 return bPtr
->selectedColumn
;
344 int WMGetBrowserSelectedRowInColumn(WMBrowser
* bPtr
, int column
)
346 if (column
>= 0 && column
< bPtr
->columnCount
) {
347 return WMGetListSelectedItemRow(bPtr
->columns
[column
]);
353 void WMSetBrowserColumnTitle(WMBrowser
* bPtr
, int column
, const char *title
)
356 assert(column
< bPtr
->usedColumnCount
);
358 if (bPtr
->titles
[column
])
359 wfree(bPtr
->titles
[column
]);
361 bPtr
->titles
[column
] = wstrdup(title
);
363 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
364 drawTitleOfColumn(bPtr
, column
);
368 void WMSetBrowserTitled(WMBrowser
* bPtr
, Bool flag
)
371 int columnX
, columnY
;
373 flag
= ((flag
== 0) ? 0 : 1);
375 if (bPtr
->flags
.isTitled
== flag
)
378 if (!bPtr
->flags
.isTitled
) {
379 columnY
= TITLE_SPACING
+ bPtr
->titleHeight
;
381 bPtr
->columnSize
.height
-= columnY
;
383 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
384 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
386 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
388 WMMoveWidget(bPtr
->columns
[i
], columnX
, columnY
);
391 bPtr
->columnSize
.height
+= TITLE_SPACING
+ bPtr
->titleHeight
;
393 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
394 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
396 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
398 WMMoveWidget(bPtr
->columns
[i
], columnX
, 0);
402 bPtr
->flags
.isTitled
= flag
;
405 void WMSortBrowserColumn(WMBrowser
* bPtr
, int column
)
407 WMSortListItems(bPtr
->columns
[column
]);
410 void WMSortBrowserColumnWithComparer(WMBrowser
* bPtr
, int column
, WMCompareDataProc
* func
)
412 WMSortListItemsWithComparer(bPtr
->columns
[column
], func
);
415 WMListItem
*WMInsertBrowserItem(WMBrowser
* bPtr
, int column
, int row
, const char *text
, Bool isBranch
)
419 if (column
< 0 || column
>= bPtr
->columnCount
)
422 item
= WMInsertListItem(bPtr
->columns
[column
], row
, text
);
423 item
->isBranch
= isBranch
;
428 static void willResizeBrowser(W_ViewDelegate
* self
, WMView
* view
, unsigned int *width
, unsigned int *height
)
430 WMBrowser
*bPtr
= (WMBrowser
*) view
->self
;
431 int cols
= bPtr
->maxVisibleColumns
;
435 /* Parameter not used, but tell the compiler that it is ok */
441 bPtr
->columnSize
.width
= (*width
- (cols
- 1) * COLUMN_SPACING
) / cols
;
442 bPtr
->columnSize
.height
= *height
;
444 if (bPtr
->flags
.isTitled
) {
445 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
446 bPtr
->columnSize
.height
-= colY
;
451 if (bPtr
->flags
.hasScroller
) {
452 bPtr
->columnSize
.height
-= SCROLLER_WIDTH
+ 4;
454 if (bPtr
->scroller
) {
455 WMResizeWidget(bPtr
->scroller
, *width
- 2, 1);
456 WMMoveWidget(bPtr
->scroller
, 1, *height
- SCROLLER_WIDTH
- 1);
461 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
462 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
464 WMMoveWidget(bPtr
->columns
[i
], colX
, colY
);
466 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
467 colX
+= bPtr
->columnSize
.width
+ COLUMN_SPACING
;
472 static void paintItem(WMList
* lPtr
, int index
, Drawable d
, char *text
, int state
, WMRect
* rect
)
474 WMView
*view
= W_VIEW(lPtr
);
475 W_Screen
*scr
= view
->screen
;
476 Display
*display
= scr
->display
;
477 WMFont
*font
= ((state
& WLDSIsBranch
) ? scr
->boldFont
: scr
->normalFont
);
478 WMColor
*backColor
= ((state
& WLDSSelected
) ? scr
->white
: view
->backColor
);
479 int width
, height
, x
, y
;
481 /* Parameter not used, but tell the compiler that it is ok */
484 width
= rect
->size
.width
;
485 height
= rect
->size
.height
;
489 XFillRectangle(display
, d
, WMColorGC(backColor
), x
, y
, width
, height
);
494 /* Avoid overlaping... */
495 widthC
= (state
& WLDSIsBranch
) ? width
- 20 : width
- 8;
496 textLen
= strlen(text
);
497 if (WMWidthOfString(font
, text
, textLen
) > widthC
) {
498 char *textBuf
= createTruncatedString(font
, text
, &textLen
, widthC
);
499 W_PaintText(view
, d
, font
, x
+ 4, y
, widthC
, WALeft
, scr
->black
, False
, textBuf
, textLen
);
502 W_PaintText(view
, d
, font
, x
+ 4, y
, widthC
, WALeft
, scr
->black
, False
, text
, textLen
);
506 if (state
& WLDSIsBranch
) {
507 WMColor
*lineColor
= ((state
& WLDSSelected
) ? scr
->gray
: scr
->white
);
509 XDrawLine(display
, d
, WMColorGC(scr
->darkGray
), x
+ width
- 11, y
+ 3,
510 x
+ width
- 6, y
+ height
/ 2);
511 XDrawLine(display
, d
, WMColorGC(lineColor
), x
+ width
- 11, y
+ height
- 5,
512 x
+ width
- 6, y
+ height
/ 2);
513 XDrawLine(display
, d
, WMColorGC(scr
->black
), x
+ width
- 12, y
+ 3,
514 x
+ width
- 12, y
+ height
- 5);
518 static void scrollCallback(WMWidget
* scroller
, void *self
)
520 WMBrowser
*bPtr
= (WMBrowser
*) self
;
521 WMScroller
*sPtr
= (WMScroller
*) scroller
;
523 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
525 switch (WMGetScrollerHitPart(sPtr
)) {
526 case WSDecrementLine
:
527 if (bPtr
->firstVisibleColumn
> 0) {
528 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
- 1, True
);
532 case WSDecrementPage
:
533 case WSDecrementWheel
:
534 if (bPtr
->firstVisibleColumn
> 0) {
535 newFirst
= bPtr
->firstVisibleColumn
- bPtr
->maxVisibleColumns
;
537 scrollToColumn(bPtr
, newFirst
, True
);
541 case WSIncrementLine
:
542 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
543 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
+ 1, True
);
547 case WSIncrementPage
:
548 case WSIncrementWheel
:
549 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
550 newFirst
= bPtr
->firstVisibleColumn
+ bPtr
->maxVisibleColumns
;
552 if (newFirst
+ bPtr
->maxVisibleColumns
>= bPtr
->columnCount
)
553 newFirst
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
555 scrollToColumn(bPtr
, newFirst
, True
);
562 double value
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
564 floatValue
= WMGetScrollerValue(bPtr
->scroller
);
566 floatValue
= (floatValue
* value
) / value
;
568 newFirst
= rint(floatValue
* (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
));
570 if (bPtr
->firstVisibleColumn
!= newFirst
)
571 scrollToColumn(bPtr
, newFirst
, False
);
573 WMSetScrollerParameters(bPtr->scroller, floatValue,
574 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
585 #undef LAST_VISIBLE_COLUMN
588 static void setupScroller(WMBrowser
* bPtr
)
593 y
= bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 1;
595 sPtr
= WMCreateScroller(bPtr
);
596 WMSetScrollerAction(sPtr
, scrollCallback
, bPtr
);
597 WMMoveWidget(sPtr
, 1, y
);
598 WMResizeWidget(sPtr
, bPtr
->view
->size
.width
- 2, SCROLLER_WIDTH
);
600 bPtr
->scroller
= sPtr
;
605 void WMSetBrowserAction(WMBrowser
* bPtr
, WMAction
* action
, void *clientData
)
607 bPtr
->action
= action
;
608 bPtr
->clientData
= clientData
;
611 void WMSetBrowserDoubleAction(WMBrowser
* bPtr
, WMAction
* action
, void *clientData
)
613 bPtr
->doubleAction
= action
;
614 bPtr
->doubleClientData
= clientData
;
617 void WMSetBrowserHasScroller(WMBrowser
* bPtr
, int hasScroller
)
619 bPtr
->flags
.hasScroller
= hasScroller
;
622 char *WMSetBrowserPath(WMBrowser
* bPtr
, char *path
)
626 char *tmp
, *retPtr
= NULL
;
628 WMListItem
*listItem
;
630 /* WMLoadBrowserColumnZero must be call first */
631 if (!bPtr
->flags
.loaded
) {
635 removeColumn(bPtr
, 1);
637 WMSelectListItem(bPtr
->columns
[0], -1);
638 WMSetListPosition(bPtr
->columns
[0], 0);
642 tmp
= strtok(str
, bPtr
->pathSeparator
);
644 /* select it in the column */
645 item
= WMFindRowOfListItemWithTitle(bPtr
->columns
[i
], tmp
);
647 retPtr
= &path
[(int)(tmp
- str
)];
650 WMSelectListItem(bPtr
->columns
[i
], item
);
651 WMSetListPosition(bPtr
->columns
[i
], item
);
653 listItem
= WMGetListItem(bPtr
->columns
[i
], item
);
654 if (!listItem
|| !listItem
->isBranch
) {
658 /* load next column */
659 WMAddBrowserColumn(bPtr
);
661 loadColumn(bPtr
, i
+ 1);
663 tmp
= strtok(NULL
, bPtr
->pathSeparator
);
670 for (i
= bPtr
->usedColumnCount
- 1; (i
> -1) && !WMGetListSelectedItem(bPtr
->columns
[i
]); i
--) ;
672 bPtr
->selectedColumn
= i
;
674 if (bPtr
->columnCount
< bPtr
->maxVisibleColumns
) {
675 int i
= bPtr
->maxVisibleColumns
- bPtr
->columnCount
;
676 int curUsedColumnCount
= bPtr
->usedColumnCount
;
677 bPtr
->usedColumnCount
= bPtr
->columnCount
;
679 WMAddBrowserColumn(bPtr
);
681 bPtr
->usedColumnCount
= curUsedColumnCount
;
684 scrollToColumn(bPtr
, bPtr
->columnCount
- bPtr
->maxVisibleColumns
, True
);
689 char *WMGetBrowserPath(WMBrowser
* bPtr
)
691 return WMGetBrowserPathToColumn(bPtr
, bPtr
->columnCount
);
694 char *WMGetBrowserPathToColumn(WMBrowser
* bPtr
, int column
)
701 if (column
>= bPtr
->usedColumnCount
)
702 column
= bPtr
->usedColumnCount
- 1;
705 return wstrdup(bPtr
->pathSeparator
);
708 /* calculate size of buffer */
710 for (i
= 0; i
<= column
; i
++) {
711 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
714 size
+= strlen(item
->text
);
718 slen
= size
+ (column
+ 1) * strlen(bPtr
->pathSeparator
) + 1;
719 path
= wmalloc(slen
);
720 /* ignore first `/' */
721 for (i
= 0; i
<= column
; i
++) {
722 if (wstrlcat(path
, bPtr
->pathSeparator
, slen
) >= slen
)
725 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
729 if (wstrlcat(path
, item
->text
, slen
) >= slen
)
741 WMArray
*WMGetBrowserPaths(WMBrowser
* bPtr
)
743 int column
, i
, k
, size
, selNo
;
746 WMListItem
*item
, *lastItem
;
747 WMArray
*paths
, *items
;
749 column
= bPtr
->usedColumnCount
- 1;
752 paths
= WMCreateArrayWithDestructor(1, wfree
);
753 WMAddToArray(paths
, wstrdup(bPtr
->pathSeparator
));
757 items
= WMGetListSelectedItems(bPtr
->columns
[column
]);
758 selNo
= WMGetArrayItemCount(items
);
759 paths
= WMCreateArrayWithDestructor(selNo
, wfree
);
762 WMAddToArray(paths
, WMGetBrowserPath(bPtr
));
766 /* calculate size of buffer */
768 for (i
= 0; i
< column
; i
++) {
769 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
772 size
+= strlen(item
->text
);
775 size
+= (column
+ 1) * strlen(bPtr
->pathSeparator
) + 1;
777 for (k
= 0; k
< selNo
; k
++) {
779 lastItem
= WMGetFromArray(items
, k
);
780 slen
= size
+ (lastItem
!= NULL
? strlen(lastItem
->text
) : 0);
781 path
= wmalloc(slen
);
782 /* ignore first `/' */
783 for (i
= 0; i
<= column
; i
++) {
784 if (wstrlcat(path
, bPtr
->pathSeparator
, slen
) >= slen
) {
792 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
796 if (wstrlcat(path
, item
->text
, slen
) >= slen
) {
801 WMAddToArray(paths
, path
);
807 Bool
WMBrowserAllowsMultipleSelection(WMBrowser
* bPtr
)
809 return bPtr
->flags
.allowMultipleSelection
;
812 Bool
WMBrowserAllowsEmptySelection(WMBrowser
* bPtr
)
814 return bPtr
->flags
.allowEmptySelection
;
817 static void loadColumn(WMBrowser
* bPtr
, int column
)
819 assert(bPtr
->delegate
);
820 assert(bPtr
->delegate
->createRowsForColumn
);
822 bPtr
->flags
.loadingColumn
= 1;
823 (*bPtr
->delegate
->createRowsForColumn
) (bPtr
->delegate
, bPtr
, column
, bPtr
->columns
[column
]);
824 bPtr
->flags
.loadingColumn
= 0;
826 if (bPtr
->delegate
->titleOfColumn
) {
829 title
= (*bPtr
->delegate
->titleOfColumn
) (bPtr
->delegate
, bPtr
, column
);
831 if (bPtr
->titles
[column
])
832 wfree(bPtr
->titles
[column
]);
834 bPtr
->titles
[column
] = wstrdup(title
);
836 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
837 drawTitleOfColumn(bPtr
, column
);
842 static void paintBrowser(WMBrowser
* bPtr
)
846 if (!bPtr
->view
->flags
.mapped
)
849 W_DrawRelief(bPtr
->view
->screen
, bPtr
->view
->window
, 0,
850 bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 2, bPtr
->view
->size
.width
, 22, WRSunken
);
852 if (bPtr
->flags
.isTitled
) {
853 for (i
= 0; i
< bPtr
->maxVisibleColumns
; i
++) {
854 drawTitleOfColumn(bPtr
, i
+ bPtr
->firstVisibleColumn
);
859 static void handleEvents(XEvent
* event
, void *data
)
861 WMBrowser
*bPtr
= (WMBrowser
*) data
;
863 CHECK_CLASS(data
, WC_Browser
);
865 switch (event
->type
) {
871 destroyBrowser(bPtr
);
877 static void scrollToColumn(WMBrowser
* bPtr
, int column
, Bool updateScroller
)
883 if (column
!= bPtr
->firstVisibleColumn
) {
890 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->willScroll
) {
891 (*bPtr
->delegate
->willScroll
) (bPtr
->delegate
, bPtr
);
895 bPtr
->firstVisibleColumn
= column
;
896 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
897 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
898 WMMoveWidget(bPtr
->columns
[i
], x
, WMWidgetView(bPtr
->columns
[i
])->pos
.y
);
899 if (!WMWidgetView(bPtr
->columns
[i
])->flags
.realized
)
900 WMRealizeWidget(bPtr
->columns
[i
]);
901 WMMapWidget(bPtr
->columns
[i
]);
902 x
+= bPtr
->columnSize
.width
+ COLUMN_SPACING
;
904 WMUnmapWidget(bPtr
->columns
[i
]);
908 /* update the scroller */
909 if (updateScroller
) {
910 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
911 float value
, proportion
;
913 value
= bPtr
->firstVisibleColumn
/ (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
);
914 proportion
= bPtr
->maxVisibleColumns
/ (float)bPtr
->columnCount
;
915 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
917 WMSetScrollerParameters(bPtr
->scroller
, 0, 1);
921 if (bPtr
->view
->flags
.mapped
)
924 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->didScroll
) {
925 (*bPtr
->delegate
->didScroll
) (bPtr
->delegate
, bPtr
);
929 static void listCallback(void *self
, void *clientData
)
931 WMBrowser
*bPtr
= (WMBrowser
*) clientData
;
932 WMList
*lPtr
= (WMList
*) self
;
935 static WMListItem
*oldItem
= NULL
;
936 static int oldSelNo
= 0;
938 item
= WMGetListSelectedItem(lPtr
);
939 selNo
= WMGetArrayItemCount(WMGetListSelectedItems(lPtr
));
941 if (oldItem
== NULL
|| oldItem
!= item
|| oldSelNo
!= selNo
) {
942 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
943 if (lPtr
== bPtr
->columns
[i
])
946 assert(i
< bPtr
->columnCount
);
948 bPtr
->selectedColumn
= i
;
950 /* columns at right must be cleared */
951 removeColumn(bPtr
, i
+ 1);
953 if (item
&& item
->isBranch
&& selNo
== 1) {
954 WMAddBrowserColumn(bPtr
);
956 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
959 i
= bPtr
->usedColumnCount
- bPtr
->maxVisibleColumns
;
960 scrollToColumn(bPtr
, i
, True
);
961 if (item
&& item
->isBranch
&& selNo
== 1) {
962 loadColumn(bPtr
, bPtr
->usedColumnCount
- 1);
966 /* call callback for click */
968 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
974 static void listDoubleCallback(void *self
, void *clientData
)
976 WMBrowser
*bPtr
= (WMBrowser
*) clientData
;
977 WMList
*lPtr
= (WMList
*) self
;
980 item
= WMGetListSelectedItem(lPtr
);
984 /* call callback for double click */
985 if (bPtr
->doubleAction
)
986 (*bPtr
->doubleAction
) (bPtr
, bPtr
->doubleClientData
);
989 void 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;
1004 void WMRemoveBrowserItem(WMBrowser
* bPtr
, int column
, int row
)
1008 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
1011 list
= WMGetBrowserListInColumn(bPtr
, column
);
1013 if (row
< 0 || row
>= WMGetListNumberOfRows(list
))
1016 removeColumn(bPtr
, column
+ 1);
1017 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
1018 scrollToColumn(bPtr
, 0, True
);
1020 scrollToColumn(bPtr
, bPtr
->usedColumnCount
- bPtr
->maxVisibleColumns
, True
);
1022 WMRemoveListItem(list
, row
);
1025 static void listSelectionObserver(void *observerData
, WMNotification
* notification
)
1027 WMBrowser
*bPtr
= (WMBrowser
*) observerData
;
1029 WMList
*lPtr
= (WMList
*) WMGetNotificationObject(notification
);
1031 for (column
= 0; column
< bPtr
->usedColumnCount
; column
++)
1032 if (bPtr
->columns
[column
] == lPtr
)
1035 /* this can happen when a list is being cleared with WMClearList
1036 * after the column was removed */
1037 if (column
>= bPtr
->usedColumnCount
) {
1041 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr
)) == 0)
1044 bPtr
->selectedColumn
= column
;
1047 int WMAddBrowserColumn(WMBrowser
* bPtr
)
1055 if (bPtr
->usedColumnCount
< bPtr
->columnCount
) {
1056 return bPtr
->usedColumnCount
++;
1059 bPtr
->usedColumnCount
++;
1061 if (bPtr
->flags
.isTitled
) {
1062 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
1067 index
= bPtr
->columnCount
;
1068 bPtr
->columnCount
++;
1069 clist
= wmalloc(sizeof(WMList
*) * bPtr
->columnCount
);
1070 tlist
= wmalloc(sizeof(char *) * bPtr
->columnCount
);
1071 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*) * (bPtr
->columnCount
- 1));
1072 memcpy(tlist
, bPtr
->titles
, sizeof(char *) * (bPtr
->columnCount
- 1));
1074 wfree(bPtr
->columns
);
1076 wfree(bPtr
->titles
);
1077 bPtr
->columns
= clist
;
1078 bPtr
->titles
= tlist
;
1080 bPtr
->titles
[index
] = NULL
;
1082 list
= WMCreateList(bPtr
);
1083 WMSetListAllowMultipleSelection(list
, bPtr
->flags
.allowMultipleSelection
);
1084 WMSetListAllowEmptySelection(list
, bPtr
->flags
.allowEmptySelection
);
1085 WMSetListAction(list
, listCallback
, bPtr
);
1086 WMSetListDoubleAction(list
, listDoubleCallback
, bPtr
);
1087 WMSetListUserDrawProc(list
, paintItem
);
1088 WMAddNotificationObserver(listSelectionObserver
, bPtr
, WMListSelectionDidChangeNotification
, list
);
1090 bPtr
->columns
[index
] = list
;
1092 WMResizeWidget(list
, bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
1093 WMMoveWidget(list
, (bPtr
->columnSize
.width
+ COLUMN_SPACING
) * index
, colY
);
1094 if (COLUMN_IS_VISIBLE(bPtr
, index
))
1097 /* update the scroller */
1098 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
1099 float value
, proportion
;
1101 value
= bPtr
->firstVisibleColumn
/ (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
);
1102 proportion
= bPtr
->maxVisibleColumns
/ (float)bPtr
->columnCount
;
1103 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
1109 static void destroyBrowser(WMBrowser
* bPtr
)
1113 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
1114 if (bPtr
->titles
[i
])
1115 wfree(bPtr
->titles
[i
]);
1117 wfree(bPtr
->titles
);
1119 wfree(bPtr
->pathSeparator
);
1121 WMRemoveNotificationObserver(bPtr
);
1126 static char *createTruncatedString(WMFont
* font
, const char *text
, int *textLen
, int width
)
1132 dLen
= WMWidthOfString(font
, ".", 1);
1133 slen
= *textLen
+ 4;
1134 textBuf
= wmalloc(slen
);
1136 if (width
>= 3 * dLen
) {
1137 int tmpTextLen
= *textLen
;
1139 if (wstrlcpy(textBuf
, text
, slen
) >= slen
)
1142 while (tmpTextLen
&& (WMWidthOfString(font
, textBuf
, tmpTextLen
) + 3 * dLen
> width
))
1145 if (wstrlcpy(textBuf
+ tmpTextLen
, "...", slen
) >= slen
)
1148 *textLen
= tmpTextLen
+ 3;
1150 } else if (width
>= 2 * dLen
) {
1151 if (wstrlcpy(textBuf
, "..", slen
) >= slen
)
1156 } else if (width
>= dLen
) {
1157 if (wstrlcpy(textBuf
, ".", slen
) >= slen
)