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
, 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
));
98 memset(bPtr
, 0, sizeof(WMBrowser
));
100 bPtr
->widgetClass
= WC_Browser
;
102 bPtr
->view
= W_CreateView(W_VIEW(parent
));
107 bPtr
->view
->self
= bPtr
;
109 bPtr
->view
->delegate
= &_BrowserViewDelegate
;
111 WMCreateEventHandler(bPtr
->view
, ExposureMask
| StructureNotifyMask
112 | ClientMessageMask
, handleEvents
, bPtr
);
114 /* default configuration */
115 bPtr
->flags
.hasScroller
= DEFAULT_HAS_SCROLLER
;
117 bPtr
->titleHeight
= DEFAULT_TITLE_HEIGHT
;
118 bPtr
->flags
.isTitled
= DEFAULT_IS_TITLED
;
119 bPtr
->maxVisibleColumns
= DEFAULT_MAX_VISIBLE_COLUMNS
;
121 WMResizeWidget(bPtr
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
123 bPtr
->pathSeparator
= wstrdup(DEFAULT_SEPARATOR
);
125 if (bPtr
->flags
.hasScroller
)
128 for (i
= 0; i
< bPtr
->maxVisibleColumns
; i
++) {
129 WMAddBrowserColumn(bPtr
);
131 bPtr
->usedColumnCount
= 0;
133 bPtr
->selectedColumn
= -1;
138 void WMSetBrowserAllowMultipleSelection(WMBrowser
* bPtr
, Bool flag
)
142 bPtr
->flags
.allowMultipleSelection
= ((flag
== 0) ? 0 : 1);
143 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
144 WMSetListAllowMultipleSelection(bPtr
->columns
[i
], flag
);
148 void WMSetBrowserAllowEmptySelection(WMBrowser
* bPtr
, Bool flag
)
152 bPtr
->flags
.allowEmptySelection
= ((flag
== 0) ? 0 : 1);
153 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
154 WMSetListAllowEmptySelection(bPtr
->columns
[i
], flag
);
158 int WMGetBrowserMaxVisibleColumns(WMBrowser
* bPtr
)
160 return bPtr
->maxVisibleColumns
;
163 void WMSetBrowserMaxVisibleColumns(WMBrowser
* bPtr
, int columns
)
165 int curMaxVisibleColumns
;
166 int newFirstVisibleColumn
= 0;
168 assert(bPtr
!= NULL
);
170 columns
= (columns
< MIN_VISIBLE_COLUMNS
) ? MIN_VISIBLE_COLUMNS
: columns
;
171 columns
= (columns
> MAX_VISIBLE_COLUMNS
) ? MAX_VISIBLE_COLUMNS
: columns
;
172 if (columns
== bPtr
->maxVisibleColumns
) {
175 curMaxVisibleColumns
= bPtr
->maxVisibleColumns
;
176 bPtr
->maxVisibleColumns
= columns
;
177 /* browser not loaded */
178 if (!bPtr
->flags
.loaded
) {
179 if ((columns
> curMaxVisibleColumns
) && (columns
> bPtr
->columnCount
)) {
180 int i
= columns
- bPtr
->columnCount
;
181 bPtr
->usedColumnCount
= bPtr
->columnCount
;
183 WMAddBrowserColumn(bPtr
);
185 bPtr
->usedColumnCount
= 0;
187 /* browser loaded and columns > curMaxVisibleColumns */
188 } else if (columns
> curMaxVisibleColumns
) {
189 if (bPtr
->usedColumnCount
> columns
) {
190 newFirstVisibleColumn
= bPtr
->usedColumnCount
- columns
;
192 if (newFirstVisibleColumn
> bPtr
->firstVisibleColumn
) {
193 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
195 if (columns
> bPtr
->columnCount
) {
196 int i
= columns
- bPtr
->columnCount
;
197 int curUsedColumnCount
= bPtr
->usedColumnCount
;
198 bPtr
->usedColumnCount
= bPtr
->columnCount
;
200 WMAddBrowserColumn(bPtr
);
202 bPtr
->usedColumnCount
= curUsedColumnCount
;
204 /* browser loaded and columns < curMaxVisibleColumns */
206 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
207 if (newFirstVisibleColumn
+ columns
>= bPtr
->usedColumnCount
) {
208 removeColumn(bPtr
, newFirstVisibleColumn
+ columns
);
211 WMResizeWidget(bPtr
, bPtr
->view
->size
.width
, bPtr
->view
->size
.height
);
212 if (bPtr
->flags
.loaded
) {
213 XClearArea(bPtr
->view
->screen
->display
, bPtr
->view
->window
, 0, 0,
214 bPtr
->view
->size
.width
, bPtr
->titleHeight
, False
);
215 scrollToColumn(bPtr
, newFirstVisibleColumn
, True
);
219 int WMGetBrowserNumberOfColumns(WMBrowser
* bPtr
)
221 return bPtr
->usedColumnCount
;
224 void WMSetBrowserPathSeparator(WMBrowser
* bPtr
, char *separator
)
226 if (bPtr
->pathSeparator
)
227 wfree(bPtr
->pathSeparator
);
228 bPtr
->pathSeparator
= wstrdup(separator
);
231 static void drawTitleOfColumn(WMBrowser
* bPtr
, int column
)
233 WMScreen
*scr
= bPtr
->view
->screen
;
236 x
= (column
- bPtr
->firstVisibleColumn
) * (bPtr
->columnSize
.width
+ COLUMN_SPACING
);
238 XFillRectangle(scr
->display
, bPtr
->view
->window
, WMColorGC(scr
->darkGray
), x
, 0,
239 bPtr
->columnSize
.width
, bPtr
->titleHeight
);
240 W_DrawRelief(scr
, bPtr
->view
->window
, x
, 0, bPtr
->columnSize
.width
, bPtr
->titleHeight
, WRSunken
);
242 if (column
< bPtr
->usedColumnCount
&& bPtr
->titles
[column
]) {
243 int titleLen
= strlen(bPtr
->titles
[column
]);
244 int widthC
= bPtr
->columnSize
.width
- 8;
246 if (WMWidthOfString(scr
->boldFont
, bPtr
->titles
[column
], titleLen
)
248 char *titleBuf
= createTruncatedString(scr
->boldFont
,
249 bPtr
->titles
[column
],
251 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
252 (bPtr
->titleHeight
- WMFontHeight(scr
->boldFont
)) / 2,
253 bPtr
->columnSize
.width
, WACenter
, scr
->white
, False
, titleBuf
, titleLen
);
256 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
257 (bPtr
->titleHeight
- WMFontHeight(scr
->boldFont
)) / 2,
258 bPtr
->columnSize
.width
, WACenter
, scr
->white
,
259 False
, bPtr
->titles
[column
], titleLen
);
264 WMList
*WMGetBrowserListInColumn(WMBrowser
* bPtr
, int column
)
266 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
269 return bPtr
->columns
[column
];
272 void WMSetBrowserDelegate(WMBrowser
* bPtr
, WMBrowserDelegate
* delegate
)
274 bPtr
->delegate
= delegate
;
277 int WMGetBrowserFirstVisibleColumn(WMBrowser
* bPtr
)
279 return bPtr
->firstVisibleColumn
;
282 static void removeColumn(WMBrowser
* bPtr
, int column
)
284 int i
, clearEnd
, destroyEnd
;
288 assert(bPtr
!= NULL
);
290 column
= (column
< 0) ? 0 : column
;
291 if (column
>= bPtr
->columnCount
) {
294 if (column
< bPtr
->maxVisibleColumns
) {
295 clearEnd
= bPtr
->maxVisibleColumns
;
296 destroyEnd
= bPtr
->columnCount
;
297 bPtr
->columnCount
= bPtr
->maxVisibleColumns
;
300 destroyEnd
= bPtr
->columnCount
;
301 bPtr
->columnCount
= column
;
303 if (column
< bPtr
->usedColumnCount
) {
304 bPtr
->usedColumnCount
= column
;
306 for (i
= column
; i
< clearEnd
; i
++) {
307 if (bPtr
->titles
[i
]) {
308 wfree(bPtr
->titles
[i
]);
309 bPtr
->titles
[i
] = NULL
;
311 WMClearList(bPtr
->columns
[i
]);
313 for (; i
< destroyEnd
; i
++) {
314 if (bPtr
->titles
[i
]) {
315 wfree(bPtr
->titles
[i
]);
316 bPtr
->titles
[i
] = NULL
;
318 WMRemoveNotificationObserverWithName(bPtr
, WMListSelectionDidChangeNotification
, bPtr
->columns
[i
]);
319 WMDestroyWidget(bPtr
->columns
[i
]);
320 bPtr
->columns
[i
] = NULL
;
322 clist
= wmalloc(sizeof(WMList
*) * (bPtr
->columnCount
));
323 tlist
= wmalloc(sizeof(char *) * (bPtr
->columnCount
));
324 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*) * (bPtr
->columnCount
));
325 memcpy(tlist
, bPtr
->titles
, sizeof(char *) * (bPtr
->columnCount
));
327 wfree(bPtr
->columns
);
328 bPtr
->titles
= tlist
;
329 bPtr
->columns
= clist
;
332 WMListItem
*WMGetBrowserSelectedItemInColumn(WMBrowser
* bPtr
, int column
)
334 if ((column
< 0) || (column
>= bPtr
->usedColumnCount
))
337 return WMGetListSelectedItem(bPtr
->columns
[column
]);
340 int WMGetBrowserSelectedColumn(WMBrowser
* bPtr
)
342 return bPtr
->selectedColumn
;
345 int WMGetBrowserSelectedRowInColumn(WMBrowser
* bPtr
, int column
)
347 if (column
>= 0 && column
< bPtr
->columnCount
) {
348 return WMGetListSelectedItemRow(bPtr
->columns
[column
]);
354 void WMSetBrowserColumnTitle(WMBrowser
* bPtr
, int column
, char *title
)
357 assert(column
< bPtr
->usedColumnCount
);
359 if (bPtr
->titles
[column
])
360 wfree(bPtr
->titles
[column
]);
362 bPtr
->titles
[column
] = wstrdup(title
);
364 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
365 drawTitleOfColumn(bPtr
, column
);
369 void WMSetBrowserTitled(WMBrowser
* bPtr
, Bool flag
)
372 int columnX
, columnY
;
374 flag
= ((flag
== 0) ? 0 : 1);
376 if (bPtr
->flags
.isTitled
== flag
)
381 if (!bPtr
->flags
.isTitled
) {
382 columnY
= TITLE_SPACING
+ bPtr
->titleHeight
;
384 bPtr
->columnSize
.height
-= columnY
;
386 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
387 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
389 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
391 WMMoveWidget(bPtr
->columns
[i
], columnX
, columnY
);
394 bPtr
->columnSize
.height
+= TITLE_SPACING
+ bPtr
->titleHeight
;
396 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
397 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
399 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
401 WMMoveWidget(bPtr
->columns
[i
], columnX
, 0);
405 bPtr
->flags
.isTitled
= flag
;
408 void WMSortBrowserColumn(WMBrowser
* bPtr
, int column
)
410 WMSortListItems(bPtr
->columns
[column
]);
413 void WMSortBrowserColumnWithComparer(WMBrowser
* bPtr
, int column
, WMCompareDataProc
* func
)
415 WMSortListItemsWithComparer(bPtr
->columns
[column
], func
);
418 WMListItem
*WMInsertBrowserItem(WMBrowser
* bPtr
, int column
, int row
, char *text
, Bool isBranch
)
422 if (column
< 0 || column
>= bPtr
->columnCount
)
425 item
= WMInsertListItem(bPtr
->columns
[column
], row
, text
);
426 item
->isBranch
= isBranch
;
431 static void willResizeBrowser(W_ViewDelegate
* self
, WMView
* view
, unsigned int *width
, unsigned int *height
)
433 WMBrowser
*bPtr
= (WMBrowser
*) view
->self
;
434 int cols
= bPtr
->maxVisibleColumns
;
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
, textLen
;
481 width
= rect
->size
.width
;
482 height
= rect
->size
.height
;
485 textLen
= strlen(text
);
487 XFillRectangle(display
, d
, WMColorGC(backColor
), x
, y
, width
, height
);
490 /* Avoid overlaping... */
491 int widthC
= (state
& WLDSIsBranch
) ? width
- 20 : width
- 8;
492 if (WMWidthOfString(font
, text
, textLen
) > widthC
) {
493 char *textBuf
= createTruncatedString(font
, text
, &textLen
, widthC
);
494 W_PaintText(view
, d
, font
, x
+ 4, y
, widthC
, WALeft
, scr
->black
, False
, textBuf
, textLen
);
497 W_PaintText(view
, d
, font
, x
+ 4, y
, widthC
, WALeft
, scr
->black
, False
, text
, textLen
);
501 if (state
& WLDSIsBranch
) {
502 WMColor
*lineColor
= ((state
& WLDSSelected
) ? scr
->gray
: scr
->white
);
504 XDrawLine(display
, d
, WMColorGC(scr
->darkGray
), x
+ width
- 11, y
+ 3,
505 x
+ width
- 6, y
+ height
/ 2);
506 XDrawLine(display
, d
, WMColorGC(lineColor
), x
+ width
- 11, y
+ height
- 5,
507 x
+ width
- 6, y
+ height
/ 2);
508 XDrawLine(display
, d
, WMColorGC(scr
->black
), x
+ width
- 12, y
+ 3,
509 x
+ width
- 12, y
+ height
- 5);
513 static void scrollCallback(WMWidget
* scroller
, void *self
)
515 WMBrowser
*bPtr
= (WMBrowser
*) self
;
516 WMScroller
*sPtr
= (WMScroller
*) scroller
;
518 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
520 switch (WMGetScrollerHitPart(sPtr
)) {
521 case WSDecrementLine
:
522 if (bPtr
->firstVisibleColumn
> 0) {
523 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
- 1, True
);
527 case WSDecrementPage
:
528 case WSDecrementWheel
:
529 if (bPtr
->firstVisibleColumn
> 0) {
530 newFirst
= bPtr
->firstVisibleColumn
- bPtr
->maxVisibleColumns
;
532 scrollToColumn(bPtr
, newFirst
, True
);
536 case WSIncrementLine
:
537 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
538 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
+ 1, True
);
542 case WSIncrementPage
:
543 case WSIncrementWheel
:
544 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
545 newFirst
= bPtr
->firstVisibleColumn
+ bPtr
->maxVisibleColumns
;
547 if (newFirst
+ bPtr
->maxVisibleColumns
>= bPtr
->columnCount
)
548 newFirst
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
550 scrollToColumn(bPtr
, newFirst
, True
);
557 double value
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
559 floatValue
= WMGetScrollerValue(bPtr
->scroller
);
561 floatValue
= (floatValue
* value
) / value
;
563 newFirst
= rint(floatValue
* (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
));
565 if (bPtr
->firstVisibleColumn
!= newFirst
)
566 scrollToColumn(bPtr
, newFirst
, False
);
568 WMSetScrollerParameters(bPtr->scroller, floatValue,
569 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
580 #undef LAST_VISIBLE_COLUMN
583 static void setupScroller(WMBrowser
* bPtr
)
588 y
= bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 1;
590 sPtr
= WMCreateScroller(bPtr
);
591 WMSetScrollerAction(sPtr
, scrollCallback
, bPtr
);
592 WMMoveWidget(sPtr
, 1, y
);
593 WMResizeWidget(sPtr
, bPtr
->view
->size
.width
- 2, SCROLLER_WIDTH
);
595 bPtr
->scroller
= sPtr
;
600 void WMSetBrowserAction(WMBrowser
* bPtr
, WMAction
* action
, void *clientData
)
602 bPtr
->action
= action
;
603 bPtr
->clientData
= clientData
;
606 void WMSetBrowserDoubleAction(WMBrowser
* bPtr
, WMAction
* action
, void *clientData
)
608 bPtr
->doubleAction
= action
;
609 bPtr
->doubleClientData
= clientData
;
612 void WMSetBrowserHasScroller(WMBrowser
* bPtr
, int hasScroller
)
614 bPtr
->flags
.hasScroller
= hasScroller
;
617 char *WMSetBrowserPath(WMBrowser
* bPtr
, char *path
)
621 char *tmp
, *retPtr
= NULL
;
623 WMListItem
*listItem
;
625 /* WMLoadBrowserColumnZero must be call first */
626 if (!bPtr
->flags
.loaded
) {
630 removeColumn(bPtr
, 1);
632 WMSelectListItem(bPtr
->columns
[0], -1);
633 WMSetListPosition(bPtr
->columns
[0], 0);
637 tmp
= strtok(str
, bPtr
->pathSeparator
);
639 /* select it in the column */
640 item
= WMFindRowOfListItemWithTitle(bPtr
->columns
[i
], tmp
);
642 retPtr
= &path
[(int)(tmp
- str
)];
645 WMSelectListItem(bPtr
->columns
[i
], item
);
646 WMSetListPosition(bPtr
->columns
[i
], item
);
648 listItem
= WMGetListItem(bPtr
->columns
[i
], item
);
649 if (!listItem
|| !listItem
->isBranch
) {
653 /* load next column */
654 WMAddBrowserColumn(bPtr
);
656 loadColumn(bPtr
, i
+ 1);
658 tmp
= strtok(NULL
, bPtr
->pathSeparator
);
665 for (i
= bPtr
->usedColumnCount
- 1; (i
> -1) && !WMGetListSelectedItem(bPtr
->columns
[i
]); i
--) ;
667 bPtr
->selectedColumn
= i
;
669 if (bPtr
->columnCount
< bPtr
->maxVisibleColumns
) {
670 int i
= bPtr
->maxVisibleColumns
- bPtr
->columnCount
;
671 int curUsedColumnCount
= bPtr
->usedColumnCount
;
672 bPtr
->usedColumnCount
= bPtr
->columnCount
;
674 WMAddBrowserColumn(bPtr
);
676 bPtr
->usedColumnCount
= curUsedColumnCount
;
679 scrollToColumn(bPtr
, bPtr
->columnCount
- bPtr
->maxVisibleColumns
, True
);
684 char *WMGetBrowserPath(WMBrowser
* bPtr
)
686 return WMGetBrowserPathToColumn(bPtr
, bPtr
->columnCount
);
689 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 path
= wmalloc(size
+ (column
+ 1) * strlen(bPtr
->pathSeparator
) + 1);
715 for (i
= 0; i
<= column
; i
++) {
716 strcat(path
, bPtr
->pathSeparator
);
717 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
720 strcat(path
, item
->text
);
726 WMArray
*WMGetBrowserPaths(WMBrowser
* bPtr
)
728 int column
, i
, k
, size
, selNo
;
730 WMListItem
*item
, *lastItem
;
731 WMArray
*paths
, *items
;
733 column
= bPtr
->usedColumnCount
- 1;
736 paths
= WMCreateArrayWithDestructor(1, wfree
);
737 WMAddToArray(paths
, wstrdup(bPtr
->pathSeparator
));
741 items
= WMGetListSelectedItems(bPtr
->columns
[column
]);
742 selNo
= WMGetArrayItemCount(items
);
743 paths
= WMCreateArrayWithDestructor(selNo
, wfree
);
746 WMAddToArray(paths
, WMGetBrowserPath(bPtr
));
750 /* calculate size of buffer */
752 for (i
= 0; i
< column
; i
++) {
753 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
756 size
+= strlen(item
->text
);
759 size
+= (column
+ 1) * strlen(bPtr
->pathSeparator
) + 1;
761 for (k
= 0; k
< selNo
; k
++) {
763 lastItem
= WMGetFromArray(items
, k
);
764 path
= wmalloc(size
+ (lastItem
!= NULL
? strlen(lastItem
->text
) : 0));
767 for (i
= 0; i
<= column
; i
++) {
768 strcat(path
, bPtr
->pathSeparator
);
772 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
776 strcat(path
, item
->text
);
778 WMAddToArray(paths
, path
);
784 Bool
WMBrowserAllowsMultipleSelection(WMBrowser
* bPtr
)
786 return bPtr
->flags
.allowMultipleSelection
;
789 Bool
WMBrowserAllowsEmptySelection(WMBrowser
* bPtr
)
791 return bPtr
->flags
.allowEmptySelection
;
794 static void loadColumn(WMBrowser
* bPtr
, int column
)
796 assert(bPtr
->delegate
);
797 assert(bPtr
->delegate
->createRowsForColumn
);
799 bPtr
->flags
.loadingColumn
= 1;
800 (*bPtr
->delegate
->createRowsForColumn
) (bPtr
->delegate
, bPtr
, column
, bPtr
->columns
[column
]);
801 bPtr
->flags
.loadingColumn
= 0;
803 if (bPtr
->delegate
->titleOfColumn
) {
806 title
= (*bPtr
->delegate
->titleOfColumn
) (bPtr
->delegate
, bPtr
, column
);
808 if (bPtr
->titles
[column
])
809 wfree(bPtr
->titles
[column
]);
811 bPtr
->titles
[column
] = wstrdup(title
);
813 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
814 drawTitleOfColumn(bPtr
, column
);
819 static void paintBrowser(WMBrowser
* bPtr
)
823 if (!bPtr
->view
->flags
.mapped
)
826 W_DrawRelief(bPtr
->view
->screen
, bPtr
->view
->window
, 0,
827 bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 2, bPtr
->view
->size
.width
, 22, WRSunken
);
829 if (bPtr
->flags
.isTitled
) {
830 for (i
= 0; i
< bPtr
->maxVisibleColumns
; i
++) {
831 drawTitleOfColumn(bPtr
, i
+ bPtr
->firstVisibleColumn
);
836 static void handleEvents(XEvent
* event
, void *data
)
838 WMBrowser
*bPtr
= (WMBrowser
*) data
;
840 CHECK_CLASS(data
, WC_Browser
);
842 switch (event
->type
) {
848 destroyBrowser(bPtr
);
854 static void scrollToColumn(WMBrowser
* bPtr
, int column
, Bool updateScroller
)
860 if (column
!= bPtr
->firstVisibleColumn
) {
867 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->willScroll
) {
868 (*bPtr
->delegate
->willScroll
) (bPtr
->delegate
, bPtr
);
872 bPtr
->firstVisibleColumn
= column
;
873 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
874 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
875 WMMoveWidget(bPtr
->columns
[i
], x
, WMWidgetView(bPtr
->columns
[i
])->pos
.y
);
876 if (!WMWidgetView(bPtr
->columns
[i
])->flags
.realized
)
877 WMRealizeWidget(bPtr
->columns
[i
]);
878 WMMapWidget(bPtr
->columns
[i
]);
879 x
+= bPtr
->columnSize
.width
+ COLUMN_SPACING
;
881 WMUnmapWidget(bPtr
->columns
[i
]);
885 /* update the scroller */
886 if (updateScroller
) {
887 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
888 float value
, proportion
;
890 value
= bPtr
->firstVisibleColumn
/ (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
);
891 proportion
= bPtr
->maxVisibleColumns
/ (float)bPtr
->columnCount
;
892 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
894 WMSetScrollerParameters(bPtr
->scroller
, 0, 1);
898 if (bPtr
->view
->flags
.mapped
)
901 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->didScroll
) {
902 (*bPtr
->delegate
->didScroll
) (bPtr
->delegate
, bPtr
);
906 static void listCallback(void *self
, void *clientData
)
908 WMBrowser
*bPtr
= (WMBrowser
*) clientData
;
909 WMList
*lPtr
= (WMList
*) self
;
912 static WMListItem
*oldItem
= NULL
;
913 static int oldSelNo
= 0;
915 item
= WMGetListSelectedItem(lPtr
);
916 selNo
= WMGetArrayItemCount(WMGetListSelectedItems(lPtr
));
918 if (oldItem
== NULL
|| oldItem
!= item
|| oldSelNo
!= selNo
) {
919 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
920 if (lPtr
== bPtr
->columns
[i
])
923 assert(i
< bPtr
->columnCount
);
925 bPtr
->selectedColumn
= i
;
927 /* columns at right must be cleared */
928 removeColumn(bPtr
, i
+ 1);
930 if (item
&& item
->isBranch
&& selNo
== 1) {
931 WMAddBrowserColumn(bPtr
);
933 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
936 i
= bPtr
->usedColumnCount
- bPtr
->maxVisibleColumns
;
937 scrollToColumn(bPtr
, i
, True
);
938 if (item
&& item
->isBranch
&& selNo
== 1) {
939 loadColumn(bPtr
, bPtr
->usedColumnCount
- 1);
943 /* call callback for click */
945 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
951 static void listDoubleCallback(void *self
, void *clientData
)
953 WMBrowser
*bPtr
= (WMBrowser
*) clientData
;
954 WMList
*lPtr
= (WMList
*) self
;
957 item
= WMGetListSelectedItem(lPtr
);
961 /* call callback for double click */
962 if (bPtr
->doubleAction
)
963 (*bPtr
->doubleAction
) (bPtr
, bPtr
->doubleClientData
);
966 void WMLoadBrowserColumnZero(WMBrowser
* bPtr
)
968 if (!bPtr
->flags
.loaded
) {
969 /* create column 0 */
970 WMAddBrowserColumn(bPtr
);
974 /* make column 0 visible */
975 scrollToColumn(bPtr
, 0, True
);
977 bPtr
->flags
.loaded
= 1;
981 void WMRemoveBrowserItem(WMBrowser
* bPtr
, int column
, int row
)
985 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
988 list
= WMGetBrowserListInColumn(bPtr
, column
);
990 if (row
< 0 || row
>= WMGetListNumberOfRows(list
))
993 removeColumn(bPtr
, column
+ 1);
994 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
995 scrollToColumn(bPtr
, 0, True
);
997 scrollToColumn(bPtr
, bPtr
->usedColumnCount
- bPtr
->maxVisibleColumns
, True
);
999 WMRemoveListItem(list
, row
);
1002 static void listSelectionObserver(void *observerData
, WMNotification
* notification
)
1004 WMBrowser
*bPtr
= (WMBrowser
*) observerData
;
1006 WMList
*lPtr
= (WMList
*) WMGetNotificationObject(notification
);
1008 for (column
= 0; column
< bPtr
->usedColumnCount
; column
++)
1009 if (bPtr
->columns
[column
] == lPtr
)
1012 /* this can happen when a list is being cleared with WMClearList
1013 * after the column was removed */
1014 if (column
>= bPtr
->usedColumnCount
) {
1018 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr
)) == 0)
1021 bPtr
->selectedColumn
= column
;
1024 int WMAddBrowserColumn(WMBrowser
* bPtr
)
1032 if (bPtr
->usedColumnCount
< bPtr
->columnCount
) {
1033 return bPtr
->usedColumnCount
++;
1036 bPtr
->usedColumnCount
++;
1038 if (bPtr
->flags
.isTitled
) {
1039 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
1044 index
= bPtr
->columnCount
;
1045 bPtr
->columnCount
++;
1046 clist
= wmalloc(sizeof(WMList
*) * bPtr
->columnCount
);
1047 tlist
= wmalloc(sizeof(char *) * bPtr
->columnCount
);
1048 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*) * (bPtr
->columnCount
- 1));
1049 memcpy(tlist
, bPtr
->titles
, sizeof(char *) * (bPtr
->columnCount
- 1));
1051 wfree(bPtr
->columns
);
1053 wfree(bPtr
->titles
);
1054 bPtr
->columns
= clist
;
1055 bPtr
->titles
= tlist
;
1057 bPtr
->titles
[index
] = NULL
;
1059 list
= WMCreateList(bPtr
);
1060 WMSetListAllowMultipleSelection(list
, bPtr
->flags
.allowMultipleSelection
);
1061 WMSetListAllowEmptySelection(list
, bPtr
->flags
.allowEmptySelection
);
1062 WMSetListAction(list
, listCallback
, bPtr
);
1063 WMSetListDoubleAction(list
, listDoubleCallback
, bPtr
);
1064 WMSetListUserDrawProc(list
, paintItem
);
1065 WMAddNotificationObserver(listSelectionObserver
, bPtr
, WMListSelectionDidChangeNotification
, list
);
1067 bPtr
->columns
[index
] = list
;
1069 WMResizeWidget(list
, bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
1070 WMMoveWidget(list
, (bPtr
->columnSize
.width
+ COLUMN_SPACING
) * index
, colY
);
1071 if (COLUMN_IS_VISIBLE(bPtr
, index
))
1074 /* update the scroller */
1075 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
1076 float value
, proportion
;
1078 value
= bPtr
->firstVisibleColumn
/ (float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
);
1079 proportion
= bPtr
->maxVisibleColumns
/ (float)bPtr
->columnCount
;
1080 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
1086 static void destroyBrowser(WMBrowser
* bPtr
)
1090 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
1091 if (bPtr
->titles
[i
])
1092 wfree(bPtr
->titles
[i
]);
1094 wfree(bPtr
->titles
);
1096 wfree(bPtr
->pathSeparator
);
1098 WMRemoveNotificationObserver(bPtr
);
1103 static char *createTruncatedString(WMFont
* font
, char *text
, int *textLen
, int width
)
1105 int dLen
= WMWidthOfString(font
, ".", 1);
1106 char *textBuf
= (char *)wmalloc((*textLen
) + 4);
1108 if (width
>= 3 * dLen
) {
1109 int dddLen
= 3 * dLen
;
1110 int tmpTextLen
= *textLen
;
1112 strcpy(textBuf
, text
);
1113 while (tmpTextLen
&& (WMWidthOfString(font
, textBuf
, tmpTextLen
) + dddLen
> width
))
1115 strcpy(textBuf
+ tmpTextLen
, "...");
1116 *textLen
= tmpTextLen
+ 3;
1117 } else if (width
>= 2 * dLen
) {
1118 strcpy(textBuf
, "..");
1120 } else if (width
>= dLen
) {
1121 strcpy(textBuf
, ".");