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
)
380 if (!bPtr
->flags
.isTitled
) {
381 columnY
= TITLE_SPACING
+ bPtr
->titleHeight
;
383 bPtr
->columnSize
.height
-= columnY
;
385 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
386 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
388 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
390 WMMoveWidget(bPtr
->columns
[i
], columnX
, columnY
);
393 bPtr
->columnSize
.height
+= TITLE_SPACING
+ bPtr
->titleHeight
;
395 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
396 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
398 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
400 WMMoveWidget(bPtr
->columns
[i
], columnX
, 0);
404 bPtr
->flags
.isTitled
= flag
;
407 void WMSortBrowserColumn(WMBrowser
* bPtr
, int column
)
409 WMSortListItems(bPtr
->columns
[column
]);
412 void WMSortBrowserColumnWithComparer(WMBrowser
* bPtr
, int column
, WMCompareDataProc
* func
)
414 WMSortListItemsWithComparer(bPtr
->columns
[column
], func
);
417 WMListItem
*WMInsertBrowserItem(WMBrowser
* bPtr
, int column
, int row
, const char *text
, Bool isBranch
)
421 if (column
< 0 || column
>= bPtr
->columnCount
)
424 item
= WMInsertListItem(bPtr
->columns
[column
], row
, text
);
425 item
->isBranch
= isBranch
;
430 static void willResizeBrowser(W_ViewDelegate
* self
, WMView
* view
, unsigned int *width
, unsigned int *height
)
432 WMBrowser
*bPtr
= (WMBrowser
*) view
->self
;
433 int cols
= bPtr
->maxVisibleColumns
;
440 bPtr
->columnSize
.width
= (*width
- (cols
- 1) * COLUMN_SPACING
) / cols
;
441 bPtr
->columnSize
.height
= *height
;
443 if (bPtr
->flags
.isTitled
) {
444 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
445 bPtr
->columnSize
.height
-= colY
;
450 if (bPtr
->flags
.hasScroller
) {
451 bPtr
->columnSize
.height
-= SCROLLER_WIDTH
+ 4;
453 if (bPtr
->scroller
) {
454 WMResizeWidget(bPtr
->scroller
, *width
- 2, 1);
455 WMMoveWidget(bPtr
->scroller
, 1, *height
- SCROLLER_WIDTH
- 1);
460 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
461 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
463 WMMoveWidget(bPtr
->columns
[i
], colX
, colY
);
465 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
466 colX
+= bPtr
->columnSize
.width
+ COLUMN_SPACING
;
471 static void paintItem(WMList
* lPtr
, int index
, Drawable d
, char *text
, int state
, WMRect
* rect
)
473 WMView
*view
= W_VIEW(lPtr
);
474 W_Screen
*scr
= view
->screen
;
475 Display
*display
= scr
->display
;
476 WMFont
*font
= ((state
& WLDSIsBranch
) ? scr
->boldFont
: scr
->normalFont
);
477 WMColor
*backColor
= ((state
& WLDSSelected
) ? scr
->white
: view
->backColor
);
478 int width
, height
, x
, y
, textLen
;
480 width
= rect
->size
.width
;
481 height
= rect
->size
.height
;
484 textLen
= strlen(text
);
486 XFillRectangle(display
, d
, WMColorGC(backColor
), x
, y
, width
, height
);
489 /* Avoid overlaping... */
490 int widthC
= (state
& WLDSIsBranch
) ? width
- 20 : width
- 8;
491 if (WMWidthOfString(font
, text
, textLen
) > widthC
) {
492 char *textBuf
= createTruncatedString(font
, text
, &textLen
, widthC
);
493 W_PaintText(view
, d
, font
, x
+ 4, y
, widthC
, WALeft
, scr
->black
, False
, textBuf
, textLen
);
496 W_PaintText(view
, d
, font
, x
+ 4, y
, widthC
, WALeft
, scr
->black
, False
, text
, textLen
);
500 if (state
& WLDSIsBranch
) {
501 WMColor
*lineColor
= ((state
& WLDSSelected
) ? scr
->gray
: scr
->white
);
503 XDrawLine(display
, d
, WMColorGC(scr
->darkGray
), x
+ width
- 11, y
+ 3,
504 x
+ width
- 6, y
+ height
/ 2);
505 XDrawLine(display
, d
, WMColorGC(lineColor
), x
+ width
- 11, y
+ height
- 5,
506 x
+ width
- 6, y
+ height
/ 2);
507 XDrawLine(display
, d
, WMColorGC(scr
->black
), x
+ width
- 12, y
+ 3,
508 x
+ width
- 12, y
+ height
- 5);
512 static void scrollCallback(WMWidget
* scroller
, void *self
)
514 WMBrowser
*bPtr
= (WMBrowser
*) self
;
515 WMScroller
*sPtr
= (WMScroller
*) scroller
;
517 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
519 switch (WMGetScrollerHitPart(sPtr
)) {
520 case WSDecrementLine
:
521 if (bPtr
->firstVisibleColumn
> 0) {
522 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
- 1, True
);
526 case WSDecrementPage
:
527 case WSDecrementWheel
:
528 if (bPtr
->firstVisibleColumn
> 0) {
529 newFirst
= bPtr
->firstVisibleColumn
- bPtr
->maxVisibleColumns
;
531 scrollToColumn(bPtr
, newFirst
, True
);
535 case WSIncrementLine
:
536 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
537 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
+ 1, True
);
541 case WSIncrementPage
:
542 case WSIncrementWheel
:
543 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
544 newFirst
= bPtr
->firstVisibleColumn
+ bPtr
->maxVisibleColumns
;
546 if (newFirst
+ bPtr
->maxVisibleColumns
>= bPtr
->columnCount
)
547 newFirst
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
549 scrollToColumn(bPtr
, newFirst
, True
);
556 double value
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
558 floatValue
= WMGetScrollerValue(bPtr
->scroller
);
560 floatValue
= (floatValue
* value
) / value
;
562 newFirst
= rint(floatValue
* (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
));
564 if (bPtr
->firstVisibleColumn
!= newFirst
)
565 scrollToColumn(bPtr
, newFirst
, False
);
567 WMSetScrollerParameters(bPtr->scroller, floatValue,
568 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
579 #undef LAST_VISIBLE_COLUMN
582 static void setupScroller(WMBrowser
* bPtr
)
587 y
= bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 1;
589 sPtr
= WMCreateScroller(bPtr
);
590 WMSetScrollerAction(sPtr
, scrollCallback
, bPtr
);
591 WMMoveWidget(sPtr
, 1, y
);
592 WMResizeWidget(sPtr
, bPtr
->view
->size
.width
- 2, SCROLLER_WIDTH
);
594 bPtr
->scroller
= sPtr
;
599 void WMSetBrowserAction(WMBrowser
* bPtr
, WMAction
* action
, void *clientData
)
601 bPtr
->action
= action
;
602 bPtr
->clientData
= clientData
;
605 void WMSetBrowserDoubleAction(WMBrowser
* bPtr
, WMAction
* action
, void *clientData
)
607 bPtr
->doubleAction
= action
;
608 bPtr
->doubleClientData
= clientData
;
611 void WMSetBrowserHasScroller(WMBrowser
* bPtr
, int hasScroller
)
613 bPtr
->flags
.hasScroller
= hasScroller
;
616 char *WMSetBrowserPath(WMBrowser
* bPtr
, char *path
)
620 char *tmp
, *retPtr
= NULL
;
622 WMListItem
*listItem
;
624 /* WMLoadBrowserColumnZero must be call first */
625 if (!bPtr
->flags
.loaded
) {
629 removeColumn(bPtr
, 1);
631 WMSelectListItem(bPtr
->columns
[0], -1);
632 WMSetListPosition(bPtr
->columns
[0], 0);
636 tmp
= strtok(str
, bPtr
->pathSeparator
);
638 /* select it in the column */
639 item
= WMFindRowOfListItemWithTitle(bPtr
->columns
[i
], tmp
);
641 retPtr
= &path
[(int)(tmp
- str
)];
644 WMSelectListItem(bPtr
->columns
[i
], item
);
645 WMSetListPosition(bPtr
->columns
[i
], item
);
647 listItem
= WMGetListItem(bPtr
->columns
[i
], item
);
648 if (!listItem
|| !listItem
->isBranch
) {
652 /* load next column */
653 WMAddBrowserColumn(bPtr
);
655 loadColumn(bPtr
, i
+ 1);
657 tmp
= strtok(NULL
, bPtr
->pathSeparator
);
664 for (i
= bPtr
->usedColumnCount
- 1; (i
> -1) && !WMGetListSelectedItem(bPtr
->columns
[i
]); i
--) ;
666 bPtr
->selectedColumn
= i
;
668 if (bPtr
->columnCount
< bPtr
->maxVisibleColumns
) {
669 int i
= bPtr
->maxVisibleColumns
- bPtr
->columnCount
;
670 int curUsedColumnCount
= bPtr
->usedColumnCount
;
671 bPtr
->usedColumnCount
= bPtr
->columnCount
;
673 WMAddBrowserColumn(bPtr
);
675 bPtr
->usedColumnCount
= curUsedColumnCount
;
678 scrollToColumn(bPtr
, bPtr
->columnCount
- bPtr
->maxVisibleColumns
, True
);
683 char *WMGetBrowserPath(WMBrowser
* bPtr
)
685 return WMGetBrowserPathToColumn(bPtr
, bPtr
->columnCount
);
688 char *WMGetBrowserPathToColumn(WMBrowser
* bPtr
, int column
)
695 if (column
>= bPtr
->usedColumnCount
)
696 column
= bPtr
->usedColumnCount
- 1;
699 return wstrdup(bPtr
->pathSeparator
);
702 /* calculate size of buffer */
704 for (i
= 0; i
<= column
; i
++) {
705 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
708 size
+= strlen(item
->text
);
712 slen
= size
+ (column
+ 1) * strlen(bPtr
->pathSeparator
) + 1;
713 path
= wmalloc(slen
);
714 /* ignore first `/' */
715 for (i
= 0; i
<= column
; i
++) {
716 if (wstrlcat(path
, bPtr
->pathSeparator
, slen
) >= slen
)
719 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
723 if (wstrlcat(path
, item
->text
, slen
) >= slen
)
735 WMArray
*WMGetBrowserPaths(WMBrowser
* bPtr
)
737 int column
, i
, k
, size
, selNo
;
740 WMListItem
*item
, *lastItem
;
741 WMArray
*paths
, *items
;
743 column
= bPtr
->usedColumnCount
- 1;
746 paths
= WMCreateArrayWithDestructor(1, wfree
);
747 WMAddToArray(paths
, wstrdup(bPtr
->pathSeparator
));
751 items
= WMGetListSelectedItems(bPtr
->columns
[column
]);
752 selNo
= WMGetArrayItemCount(items
);
753 paths
= WMCreateArrayWithDestructor(selNo
, wfree
);
756 WMAddToArray(paths
, WMGetBrowserPath(bPtr
));
760 /* calculate size of buffer */
762 for (i
= 0; i
< column
; i
++) {
763 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
766 size
+= strlen(item
->text
);
769 size
+= (column
+ 1) * strlen(bPtr
->pathSeparator
) + 1;
771 for (k
= 0; k
< selNo
; k
++) {
773 lastItem
= WMGetFromArray(items
, k
);
774 slen
= size
+ (lastItem
!= NULL
? strlen(lastItem
->text
) : 0);
775 path
= wmalloc(slen
);
776 /* ignore first `/' */
777 for (i
= 0; i
<= column
; i
++) {
778 if (wstrlcat(path
, bPtr
->pathSeparator
, slen
) >= slen
) {
785 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
789 if (wstrlcat(path
, item
->text
, slen
) >= slen
) {
794 WMAddToArray(paths
, path
);
800 Bool
WMBrowserAllowsMultipleSelection(WMBrowser
* bPtr
)
802 return bPtr
->flags
.allowMultipleSelection
;
805 Bool
WMBrowserAllowsEmptySelection(WMBrowser
* bPtr
)
807 return bPtr
->flags
.allowEmptySelection
;
810 static void loadColumn(WMBrowser
* bPtr
, int column
)
812 assert(bPtr
->delegate
);
813 assert(bPtr
->delegate
->createRowsForColumn
);
815 bPtr
->flags
.loadingColumn
= 1;
816 (*bPtr
->delegate
->createRowsForColumn
) (bPtr
->delegate
, bPtr
, column
, bPtr
->columns
[column
]);
817 bPtr
->flags
.loadingColumn
= 0;
819 if (bPtr
->delegate
->titleOfColumn
) {
822 title
= (*bPtr
->delegate
->titleOfColumn
) (bPtr
->delegate
, bPtr
, column
);
824 if (bPtr
->titles
[column
])
825 wfree(bPtr
->titles
[column
]);
827 bPtr
->titles
[column
] = wstrdup(title
);
829 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
830 drawTitleOfColumn(bPtr
, column
);
835 static void paintBrowser(WMBrowser
* bPtr
)
839 if (!bPtr
->view
->flags
.mapped
)
842 W_DrawRelief(bPtr
->view
->screen
, bPtr
->view
->window
, 0,
843 bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 2, bPtr
->view
->size
.width
, 22, WRSunken
);
845 if (bPtr
->flags
.isTitled
) {
846 for (i
= 0; i
< bPtr
->maxVisibleColumns
; i
++) {
847 drawTitleOfColumn(bPtr
, i
+ bPtr
->firstVisibleColumn
);
852 static void handleEvents(XEvent
* event
, void *data
)
854 WMBrowser
*bPtr
= (WMBrowser
*) data
;
856 CHECK_CLASS(data
, WC_Browser
);
858 switch (event
->type
) {
864 destroyBrowser(bPtr
);
870 static void scrollToColumn(WMBrowser
* bPtr
, int column
, Bool updateScroller
)
876 if (column
!= bPtr
->firstVisibleColumn
) {
883 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->willScroll
) {
884 (*bPtr
->delegate
->willScroll
) (bPtr
->delegate
, bPtr
);
888 bPtr
->firstVisibleColumn
= column
;
889 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
890 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
891 WMMoveWidget(bPtr
->columns
[i
], x
, WMWidgetView(bPtr
->columns
[i
])->pos
.y
);
892 if (!WMWidgetView(bPtr
->columns
[i
])->flags
.realized
)
893 WMRealizeWidget(bPtr
->columns
[i
]);
894 WMMapWidget(bPtr
->columns
[i
]);
895 x
+= bPtr
->columnSize
.width
+ COLUMN_SPACING
;
897 WMUnmapWidget(bPtr
->columns
[i
]);
901 /* update the scroller */
902 if (updateScroller
) {
903 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
904 float value
, proportion
;
906 value
= bPtr
->firstVisibleColumn
/ (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
);
907 proportion
= bPtr
->maxVisibleColumns
/ (float)bPtr
->columnCount
;
908 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
910 WMSetScrollerParameters(bPtr
->scroller
, 0, 1);
914 if (bPtr
->view
->flags
.mapped
)
917 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->didScroll
) {
918 (*bPtr
->delegate
->didScroll
) (bPtr
->delegate
, bPtr
);
922 static void listCallback(void *self
, void *clientData
)
924 WMBrowser
*bPtr
= (WMBrowser
*) clientData
;
925 WMList
*lPtr
= (WMList
*) self
;
928 static WMListItem
*oldItem
= NULL
;
929 static int oldSelNo
= 0;
931 item
= WMGetListSelectedItem(lPtr
);
932 selNo
= WMGetArrayItemCount(WMGetListSelectedItems(lPtr
));
934 if (oldItem
== NULL
|| oldItem
!= item
|| oldSelNo
!= selNo
) {
935 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
936 if (lPtr
== bPtr
->columns
[i
])
939 assert(i
< bPtr
->columnCount
);
941 bPtr
->selectedColumn
= i
;
943 /* columns at right must be cleared */
944 removeColumn(bPtr
, i
+ 1);
946 if (item
&& item
->isBranch
&& selNo
== 1) {
947 WMAddBrowserColumn(bPtr
);
949 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
952 i
= bPtr
->usedColumnCount
- bPtr
->maxVisibleColumns
;
953 scrollToColumn(bPtr
, i
, True
);
954 if (item
&& item
->isBranch
&& selNo
== 1) {
955 loadColumn(bPtr
, bPtr
->usedColumnCount
- 1);
959 /* call callback for click */
961 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
967 static void listDoubleCallback(void *self
, void *clientData
)
969 WMBrowser
*bPtr
= (WMBrowser
*) clientData
;
970 WMList
*lPtr
= (WMList
*) self
;
973 item
= WMGetListSelectedItem(lPtr
);
977 /* call callback for double click */
978 if (bPtr
->doubleAction
)
979 (*bPtr
->doubleAction
) (bPtr
, bPtr
->doubleClientData
);
982 void WMLoadBrowserColumnZero(WMBrowser
* bPtr
)
984 if (!bPtr
->flags
.loaded
) {
985 /* create column 0 */
986 WMAddBrowserColumn(bPtr
);
990 /* make column 0 visible */
991 scrollToColumn(bPtr
, 0, True
);
993 bPtr
->flags
.loaded
= 1;
997 void WMRemoveBrowserItem(WMBrowser
* bPtr
, int column
, int row
)
1001 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
1004 list
= WMGetBrowserListInColumn(bPtr
, column
);
1006 if (row
< 0 || row
>= WMGetListNumberOfRows(list
))
1009 removeColumn(bPtr
, column
+ 1);
1010 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
1011 scrollToColumn(bPtr
, 0, True
);
1013 scrollToColumn(bPtr
, bPtr
->usedColumnCount
- bPtr
->maxVisibleColumns
, True
);
1015 WMRemoveListItem(list
, row
);
1018 static void listSelectionObserver(void *observerData
, WMNotification
* notification
)
1020 WMBrowser
*bPtr
= (WMBrowser
*) observerData
;
1022 WMList
*lPtr
= (WMList
*) WMGetNotificationObject(notification
);
1024 for (column
= 0; column
< bPtr
->usedColumnCount
; column
++)
1025 if (bPtr
->columns
[column
] == lPtr
)
1028 /* this can happen when a list is being cleared with WMClearList
1029 * after the column was removed */
1030 if (column
>= bPtr
->usedColumnCount
) {
1034 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr
)) == 0)
1037 bPtr
->selectedColumn
= column
;
1040 int WMAddBrowserColumn(WMBrowser
* bPtr
)
1048 if (bPtr
->usedColumnCount
< bPtr
->columnCount
) {
1049 return bPtr
->usedColumnCount
++;
1052 bPtr
->usedColumnCount
++;
1054 if (bPtr
->flags
.isTitled
) {
1055 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
1060 index
= bPtr
->columnCount
;
1061 bPtr
->columnCount
++;
1062 clist
= wmalloc(sizeof(WMList
*) * bPtr
->columnCount
);
1063 tlist
= wmalloc(sizeof(char *) * bPtr
->columnCount
);
1064 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*) * (bPtr
->columnCount
- 1));
1065 memcpy(tlist
, bPtr
->titles
, sizeof(char *) * (bPtr
->columnCount
- 1));
1067 wfree(bPtr
->columns
);
1069 wfree(bPtr
->titles
);
1070 bPtr
->columns
= clist
;
1071 bPtr
->titles
= tlist
;
1073 bPtr
->titles
[index
] = NULL
;
1075 list
= WMCreateList(bPtr
);
1076 WMSetListAllowMultipleSelection(list
, bPtr
->flags
.allowMultipleSelection
);
1077 WMSetListAllowEmptySelection(list
, bPtr
->flags
.allowEmptySelection
);
1078 WMSetListAction(list
, listCallback
, bPtr
);
1079 WMSetListDoubleAction(list
, listDoubleCallback
, bPtr
);
1080 WMSetListUserDrawProc(list
, paintItem
);
1081 WMAddNotificationObserver(listSelectionObserver
, bPtr
, WMListSelectionDidChangeNotification
, list
);
1083 bPtr
->columns
[index
] = list
;
1085 WMResizeWidget(list
, bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
1086 WMMoveWidget(list
, (bPtr
->columnSize
.width
+ COLUMN_SPACING
) * index
, colY
);
1087 if (COLUMN_IS_VISIBLE(bPtr
, index
))
1090 /* update the scroller */
1091 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
1092 float value
, proportion
;
1094 value
= bPtr
->firstVisibleColumn
/ (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
);
1095 proportion
= bPtr
->maxVisibleColumns
/ (float)bPtr
->columnCount
;
1096 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
1102 static void destroyBrowser(WMBrowser
* bPtr
)
1106 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
1107 if (bPtr
->titles
[i
])
1108 wfree(bPtr
->titles
[i
]);
1110 wfree(bPtr
->titles
);
1112 wfree(bPtr
->pathSeparator
);
1114 WMRemoveNotificationObserver(bPtr
);
1119 static char *createTruncatedString(WMFont
* font
, const char *text
, int *textLen
, int width
)
1125 dLen
= WMWidthOfString(font
, ".", 1);
1126 slen
= *textLen
+ 4;
1127 textBuf
= wmalloc(slen
);
1129 if (width
>= 3 * dLen
) {
1130 int tmpTextLen
= *textLen
;
1132 if (wstrlcpy(textBuf
, text
, slen
) >= slen
)
1135 while (tmpTextLen
&& (WMWidthOfString(font
, textBuf
, tmpTextLen
) + 3 * dLen
> width
))
1138 if (wstrlcpy(textBuf
+ tmpTextLen
, "...", slen
) >= slen
)
1141 *textLen
= tmpTextLen
+ 3;
1143 } else if (width
>= 2 * dLen
) {
1144 if (wstrlcpy(textBuf
, "..", slen
) >= slen
)
1149 } else if (width
>= dLen
) {
1150 if (wstrlcpy(textBuf
, ".", slen
) >= slen
)