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 allowEmptySelection
:1;
46 unsigned int hasScroller
:1;
49 unsigned int loaded
:1;
50 unsigned int loadingColumn
:1;
55 #define COLUMN_SPACING 4
56 #define TITLE_SPACING 2
58 #define DEFAULT_WIDTH 305
59 #define DEFAULT_HEIGHT 200
60 #define DEFAULT_HAS_SCROLLER True
61 #define DEFAULT_TITLE_HEIGHT 20
62 #define DEFAULT_IS_TITLED True
63 #define DEFAULT_MAX_VISIBLE_COLUMNS 2
64 #define DEFAULT_SEPARATOR "/"
66 #define MIN_VISIBLE_COLUMNS 1
67 #define MAX_VISIBLE_COLUMNS 32
70 #define COLUMN_IS_VISIBLE(b, c) ((c) >= (b)->firstVisibleColumn \
71 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
74 static void handleEvents(XEvent
*event
, void *data
);
75 static void destroyBrowser(WMBrowser
*bPtr
);
77 static void setupScroller(WMBrowser
*bPtr
);
79 static void scrollToColumn(WMBrowser
*bPtr
, int column
, Bool updateScroller
);
81 static void paintItem(WMList
*lPtr
, int index
, Drawable d
, char *text
,
82 int state
, WMRect
*rect
);
84 static void loadColumn(WMBrowser
*bPtr
, int column
);
86 static void removeColumn(WMBrowser
*bPtr
, int column
);
88 static char* createTruncatedString(WMFont
*font
, char *text
, int *textLen
,
91 static void willResizeBrowser(W_ViewDelegate
*, WMView
*,
92 unsigned int*, unsigned int*);
94 W_ViewDelegate _BrowserViewDelegate
= {
105 WMCreateBrowser(WMWidget
*parent
)
110 wassertrv(parent
, NULL
);
112 bPtr
= wmalloc(sizeof(WMBrowser
));
113 memset(bPtr
, 0, sizeof(WMBrowser
));
115 bPtr
->widgetClass
= WC_Browser
;
117 bPtr
->view
= W_CreateView(W_VIEW(parent
));
122 bPtr
->view
->self
= bPtr
;
124 bPtr
->view
->delegate
= &_BrowserViewDelegate
;
126 WMCreateEventHandler(bPtr
->view
, ExposureMask
|StructureNotifyMask
127 |ClientMessageMask
, handleEvents
, bPtr
);
129 /* default configuration */
130 bPtr
->flags
.hasScroller
= DEFAULT_HAS_SCROLLER
;
132 bPtr
->titleHeight
= DEFAULT_TITLE_HEIGHT
;
133 bPtr
->flags
.isTitled
= DEFAULT_IS_TITLED
;
134 bPtr
->maxVisibleColumns
= DEFAULT_MAX_VISIBLE_COLUMNS
;
136 WMResizeWidget(bPtr
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
138 bPtr
->pathSeparator
= wstrdup(DEFAULT_SEPARATOR
);
140 if (bPtr
->flags
.hasScroller
)
143 for (i
=0; i
<bPtr
->maxVisibleColumns
; i
++) {
144 WMAddBrowserColumn(bPtr
);
146 bPtr
->usedColumnCount
= 0;
148 bPtr
->selectedColumn
= -1;
155 WMSetBrowserAllowMultipleSelection(WMBrowser
*bPtr
, Bool flag
)
159 bPtr
->flags
.allowMultipleSelection
= ((flag
==0) ? 0 : 1);
160 for (i
=0; i
<bPtr
->columnCount
; i
++) {
161 WMSetListAllowMultipleSelection(bPtr
->columns
[i
], flag
);
167 WMSetBrowserAllowEmptySelection(WMBrowser
*bPtr
, Bool flag
)
171 bPtr
->flags
.allowEmptySelection
= ((flag
==0) ? 0 : 1);
172 for (i
=0; i
<bPtr
->columnCount
; i
++) {
173 WMSetListAllowEmptySelection(bPtr
->columns
[i
], flag
);
179 WMGetBrowserMaxVisibleColumns(WMBrowser
*bPtr
)
181 return bPtr
->maxVisibleColumns
;
186 WMSetBrowserMaxVisibleColumns(WMBrowser
*bPtr
, int columns
)
188 int curMaxVisibleColumns
;
189 int newFirstVisibleColumn
= 0;
193 columns
= (columns
< MIN_VISIBLE_COLUMNS
) ? MIN_VISIBLE_COLUMNS
: columns
;
194 columns
= (columns
> MAX_VISIBLE_COLUMNS
) ? MAX_VISIBLE_COLUMNS
: columns
;
195 if (columns
== bPtr
->maxVisibleColumns
) {
198 curMaxVisibleColumns
= bPtr
->maxVisibleColumns
;
199 bPtr
->maxVisibleColumns
= columns
;
200 /* browser not loaded */
201 if (!bPtr
->flags
.loaded
) {
202 if ((columns
> curMaxVisibleColumns
) && (columns
> bPtr
->columnCount
)) {
203 int i
= columns
- bPtr
->columnCount
;
204 bPtr
->usedColumnCount
= bPtr
->columnCount
;
206 WMAddBrowserColumn(bPtr
);
208 bPtr
->usedColumnCount
= 0;
210 /* browser loaded and columns > curMaxVisibleColumns */
211 } else if (columns
> curMaxVisibleColumns
) {
212 if (bPtr
->usedColumnCount
> columns
) {
213 newFirstVisibleColumn
= bPtr
->usedColumnCount
- columns
;
215 if (newFirstVisibleColumn
> bPtr
->firstVisibleColumn
) {
216 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
218 if (columns
> bPtr
->columnCount
) {
219 int i
= columns
- bPtr
->columnCount
;
220 int curUsedColumnCount
= bPtr
->usedColumnCount
;
221 bPtr
->usedColumnCount
= bPtr
->columnCount
;
223 WMAddBrowserColumn(bPtr
);
225 bPtr
->usedColumnCount
= curUsedColumnCount
;
227 /* browser loaded and columns < curMaxVisibleColumns */
229 newFirstVisibleColumn
= bPtr
->firstVisibleColumn
;
230 if (newFirstVisibleColumn
+ columns
>= bPtr
->usedColumnCount
) {
231 removeColumn(bPtr
, newFirstVisibleColumn
+ columns
);
234 WMResizeWidget(bPtr
, bPtr
->view
->size
.width
, bPtr
->view
->size
.height
);
235 if (bPtr
->flags
.loaded
) {
236 XClearArea(bPtr
->view
->screen
->display
, bPtr
->view
->window
, 0, 0,
237 bPtr
->view
->size
.width
, bPtr
->titleHeight
, False
);
238 scrollToColumn (bPtr
, newFirstVisibleColumn
, True
);
244 WMGetBrowserNumberOfColumns(WMBrowser
*bPtr
)
246 return bPtr
->usedColumnCount
;
250 WMSetBrowserPathSeparator(WMBrowser
*bPtr
, char *separator
)
252 if (bPtr
->pathSeparator
)
253 wfree(bPtr
->pathSeparator
);
254 bPtr
->pathSeparator
= wstrdup(separator
);
260 drawTitleOfColumn(WMBrowser
*bPtr
, int column
)
262 WMScreen
*scr
= bPtr
->view
->screen
;
265 x
=(column
-bPtr
->firstVisibleColumn
)*(bPtr
->columnSize
.width
+COLUMN_SPACING
);
267 XFillRectangle(scr
->display
, bPtr
->view
->window
, WMColorGC(scr
->darkGray
), x
, 0,
268 bPtr
->columnSize
.width
, bPtr
->titleHeight
);
269 W_DrawRelief(scr
, bPtr
->view
->window
, x
, 0,
270 bPtr
->columnSize
.width
, bPtr
->titleHeight
, WRSunken
);
272 if (column
< bPtr
->usedColumnCount
&& bPtr
->titles
[column
]) {
273 int titleLen
= strlen(bPtr
->titles
[column
]);
274 int widthC
= bPtr
->columnSize
.width
-8;
276 if (WMWidthOfString(scr
->boldFont
, bPtr
->titles
[column
], titleLen
)
278 char *titleBuf
= createTruncatedString(scr
->boldFont
,
279 bPtr
->titles
[column
],
281 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
282 (bPtr
->titleHeight
-WMFontHeight(scr
->boldFont
))/2,
283 bPtr
->columnSize
.width
, WACenter
, scr
->white
,
284 False
, titleBuf
, titleLen
);
287 W_PaintText(bPtr
->view
, bPtr
->view
->window
, scr
->boldFont
, x
,
288 (bPtr
->titleHeight
-WMFontHeight(scr
->boldFont
))/2,
289 bPtr
->columnSize
.width
, WACenter
, scr
->white
,
290 False
, bPtr
->titles
[column
], titleLen
);
297 WMGetBrowserListInColumn(WMBrowser
*bPtr
, int column
)
299 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
302 return bPtr
->columns
[column
];
307 WMSetBrowserDelegate(WMBrowser
*bPtr
, WMBrowserDelegate
*delegate
)
309 bPtr
->delegate
= delegate
;
314 WMGetBrowserFirstVisibleColumn(WMBrowser
*bPtr
)
316 return bPtr
->firstVisibleColumn
;
321 removeColumn(WMBrowser
*bPtr
, int column
)
323 int i
, clearEnd
, destroyEnd
;
329 column
= (column
< 0) ? 0 : column
;
330 if (column
>= bPtr
->columnCount
) {
333 if (column
< bPtr
->maxVisibleColumns
) {
334 clearEnd
= bPtr
->maxVisibleColumns
;
335 destroyEnd
= bPtr
->columnCount
;
336 bPtr
->columnCount
= bPtr
->maxVisibleColumns
;
339 destroyEnd
= bPtr
->columnCount
;
340 bPtr
->columnCount
= column
;
342 if (column
< bPtr
->usedColumnCount
) {
343 bPtr
->usedColumnCount
= column
;
345 for (i
=column
; i
< clearEnd
; i
++) {
346 if (bPtr
->titles
[i
]) {
347 wfree(bPtr
->titles
[i
]);
348 bPtr
->titles
[i
] = NULL
;
350 WMClearList(bPtr
->columns
[i
]);
352 for (;i
< destroyEnd
; i
++) {
353 if (bPtr
->titles
[i
]) {
354 wfree(bPtr
->titles
[i
]);
355 bPtr
->titles
[i
] = NULL
;
357 WMRemoveNotificationObserverWithName(bPtr
,
358 WMListSelectionDidChangeNotification
,
360 WMDestroyWidget(bPtr
->columns
[i
]);
361 bPtr
->columns
[i
] = NULL
;
363 clist
= wmalloc(sizeof(WMList
*) * (bPtr
->columnCount
));
364 tlist
= wmalloc(sizeof(char*) * (bPtr
->columnCount
));
365 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*) * (bPtr
->columnCount
));
366 memcpy(tlist
, bPtr
->titles
, sizeof(char*) * (bPtr
->columnCount
));
368 wfree(bPtr
->columns
);
369 bPtr
->titles
= tlist
;
370 bPtr
->columns
= clist
;
375 WMGetBrowserSelectedItemInColumn(WMBrowser
*bPtr
, int column
)
377 if ((column
< 0) || (column
>= bPtr
->usedColumnCount
))
380 return WMGetListSelectedItem(bPtr
->columns
[column
]);
386 WMGetBrowserSelectedColumn(WMBrowser
*bPtr
)
388 return bPtr
->selectedColumn
;
393 WMGetBrowserSelectedRowInColumn(WMBrowser
*bPtr
, int column
)
395 if (column
>= 0 && column
< bPtr
->columnCount
) {
396 return WMGetListSelectedItemRow(bPtr
->columns
[column
]);
404 WMSetBrowserColumnTitle(WMBrowser
*bPtr
, int column
, char *title
)
407 assert(column
< bPtr
->usedColumnCount
);
409 if (bPtr
->titles
[column
])
410 wfree(bPtr
->titles
[column
]);
412 bPtr
->titles
[column
] = wstrdup(title
);
414 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
415 drawTitleOfColumn(bPtr
, column
);
421 WMSetBrowserTitled(WMBrowser
*bPtr
, Bool flag
)
424 int columnX
, columnY
;
426 flag
= ((flag
==0) ? 0 : 1);
428 if (bPtr
->flags
.isTitled
== flag
)
433 if (!bPtr
->flags
.isTitled
) {
434 columnY
= TITLE_SPACING
+ bPtr
->titleHeight
;
436 bPtr
->columnSize
.height
-= columnY
;
438 for (i
=0; i
<bPtr
->columnCount
; i
++) {
439 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
,
440 bPtr
->columnSize
.height
);
442 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
444 WMMoveWidget(bPtr
->columns
[i
], columnX
, columnY
);
447 bPtr
->columnSize
.height
+= TITLE_SPACING
+ bPtr
->titleHeight
;
449 for (i
=0; i
<bPtr
->columnCount
; i
++) {
450 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
,
451 bPtr
->columnSize
.height
);
453 columnX
= WMWidgetView(bPtr
->columns
[i
])->pos
.x
;
455 WMMoveWidget(bPtr
->columns
[i
], columnX
, 0);
459 bPtr
->flags
.isTitled
= flag
;
464 WMSortBrowserColumn(WMBrowser
*bPtr
, int column
)
466 WMSortListItems(bPtr
->columns
[column
]);
471 WMSortBrowserColumnWithComparer(WMBrowser
*bPtr
, int column
,
472 WMCompareDataProc
*func
)
474 WMSortListItemsWithComparer(bPtr
->columns
[column
], func
);
480 WMInsertBrowserItem(WMBrowser
*bPtr
, int column
, int row
, char *text
,
485 if (column
< 0 || column
>= bPtr
->columnCount
)
488 item
= WMInsertListItem(bPtr
->columns
[column
], row
, text
);
489 item
->isBranch
= isBranch
;
498 willResizeBrowser(W_ViewDelegate
*self
, WMView
*view
,
499 unsigned int *width
, unsigned int *height
)
501 WMBrowser
*bPtr
= (WMBrowser
*)view
->self
;
502 int cols
= bPtr
->maxVisibleColumns
;
509 bPtr
->columnSize
.width
= (*width
-(cols
-1)*COLUMN_SPACING
) / cols
;
510 bPtr
->columnSize
.height
= *height
;
512 if (bPtr
->flags
.isTitled
) {
513 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
514 bPtr
->columnSize
.height
-= colY
;
519 if (bPtr
->flags
.hasScroller
) {
520 bPtr
->columnSize
.height
-= SCROLLER_WIDTH
+ 4;
522 if (bPtr
->scroller
) {
523 WMResizeWidget(bPtr
->scroller
, *width
-2, 1);
524 WMMoveWidget(bPtr
->scroller
, 1, *height
-SCROLLER_WIDTH
-1);
529 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
530 WMResizeWidget(bPtr
->columns
[i
], bPtr
->columnSize
.width
,
531 bPtr
->columnSize
.height
);
533 WMMoveWidget(bPtr
->columns
[i
], colX
, colY
);
535 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
536 colX
+= bPtr
->columnSize
.width
+COLUMN_SPACING
;
543 paintItem(WMList
*lPtr
, int index
, Drawable drawable
, char *text
, int state
,
546 WMView
*view
= W_VIEW(lPtr
);
547 W_Screen
*scr
= view
->screen
;
548 Display
*display
= scr
->display
;
549 WMFont
*font
= ((state
& WLDSIsBranch
) ? scr
->boldFont
: scr
->normalFont
);
550 int width
, height
, x
, y
, textLen
;
551 Drawable d
= drawable
;
553 width
= rect
->size
.width
;
554 height
= rect
->size
.height
;
557 textLen
= strlen(text
);
559 #ifdef DOUBLE_BUFFER_no
561 d
= XCreatePixmap(display
, drawable
, width
, height
, scr
->depth
);
562 if (state
& WLDSSelected
)
563 XFillRectangle(display
, d
, WMColorGC(scr
->white
), 0, 0, width
, height
);
565 XFillRectangle(display
, d
, WMColorGC(view
->backColor
), 0, 0, width
, height
);
567 if (state
& WLDSSelected
)
568 XFillRectangle(display
, d
, WMColorGC(scr
->white
), x
, y
, width
, height
);
570 //XFillRectangle(display, d, WMColorGC(view->backColor), x, y, width, height);
571 XClearArea(display
, d
, x
, y
, width
, height
, False
);
575 /* Avoid overlaping... */
576 int widthC
= (state
& WLDSIsBranch
) ? width
-20 : width
-8;
577 if (WMWidthOfString(font
, text
, textLen
) > widthC
) {
578 char *textBuf
= createTruncatedString(font
, text
, &textLen
, widthC
);
579 W_PaintText(view
, d
, font
, x
+4, y
, widthC
,
580 WALeft
, scr
->black
, False
, textBuf
, textLen
);
583 W_PaintText(view
, d
, font
, x
+4, y
, widthC
,
584 WALeft
, scr
->black
, False
, text
, textLen
);
588 if (state
& WLDSIsBranch
) {
589 XDrawLine(display
, d
, WMColorGC(scr
->darkGray
), x
+width
-11, y
+3,
590 x
+width
-6, y
+height
/2);
591 if (state
& WLDSSelected
)
592 XDrawLine(display
, d
,WMColorGC(scr
->gray
), x
+width
-11, y
+height
-5,
593 x
+width
-6, y
+height
/2);
595 XDrawLine(display
, d
,WMColorGC(scr
->white
), x
+width
-11, y
+height
-5,
596 x
+width
-6, y
+height
/2);
597 XDrawLine(display
, d
, WMColorGC(scr
->black
), x
+width
-12, y
+3,
598 x
+width
-12, y
+height
-5);
601 #ifdef DOUBLE_BUFFER_no
602 XCopyArea(display
, d
, drawable
, scr
->copyGC
, 0, 0, width
, height
,
603 rect
->pos
.x
, rect
->pos
.y
);
604 XFreePixmap(display
, d
);
610 scrollCallback(WMWidget
*scroller
, void *self
)
612 WMBrowser
*bPtr
= (WMBrowser
*)self
;
613 WMScroller
*sPtr
= (WMScroller
*)scroller
;
615 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
617 switch (WMGetScrollerHitPart(sPtr
)) {
618 case WSDecrementLine
:
619 if (bPtr
->firstVisibleColumn
> 0) {
620 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
-1, True
);
624 case WSDecrementPage
:
625 case WSDecrementWheel
:
626 if (bPtr
->firstVisibleColumn
> 0) {
627 newFirst
= bPtr
->firstVisibleColumn
- bPtr
->maxVisibleColumns
;
629 scrollToColumn(bPtr
, newFirst
, True
);
634 case WSIncrementLine
:
635 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
636 scrollToColumn(bPtr
, bPtr
->firstVisibleColumn
+1, True
);
640 case WSIncrementPage
:
641 case WSIncrementWheel
:
642 if (LAST_VISIBLE_COLUMN
< bPtr
->usedColumnCount
) {
643 newFirst
= bPtr
->firstVisibleColumn
+ bPtr
->maxVisibleColumns
;
645 if (newFirst
+bPtr
->maxVisibleColumns
>= bPtr
->columnCount
)
646 newFirst
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
648 scrollToColumn(bPtr
, newFirst
, True
);
655 double value
= bPtr
->columnCount
- bPtr
->maxVisibleColumns
;
657 floatValue
= WMGetScrollerValue(bPtr
->scroller
);
659 floatValue
= (floatValue
*value
)/value
;
661 newFirst
= rint(floatValue
*(float)(bPtr
->columnCount
- bPtr
->maxVisibleColumns
));
663 if (bPtr
->firstVisibleColumn
!= newFirst
)
664 scrollToColumn(bPtr
, newFirst
, False
);
666 WMSetScrollerParameters(bPtr->scroller, floatValue,
667 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
678 #undef LAST_VISIBLE_COLUMN
683 setupScroller(WMBrowser
*bPtr
)
688 y
= bPtr
->view
->size
.height
- SCROLLER_WIDTH
- 1;
690 sPtr
= WMCreateScroller(bPtr
);
691 WMSetScrollerAction(sPtr
, scrollCallback
, bPtr
);
692 WMMoveWidget(sPtr
, 1, y
);
693 WMResizeWidget(sPtr
, bPtr
->view
->size
.width
-2, SCROLLER_WIDTH
);
695 bPtr
->scroller
= sPtr
;
702 WMSetBrowserAction(WMBrowser
*bPtr
, WMAction
*action
, void *clientData
)
704 bPtr
->action
= action
;
705 bPtr
->clientData
= clientData
;
710 WMSetBrowserDoubleAction(WMBrowser
*bPtr
, WMAction
*action
, void *clientData
)
712 bPtr
->doubleAction
= action
;
713 bPtr
->doubleClientData
= clientData
;
718 WMSetBrowserHasScroller(WMBrowser
*bPtr
, int hasScroller
)
720 bPtr
->flags
.hasScroller
= hasScroller
;
725 WMSetBrowserPath(WMBrowser
*bPtr
, char *path
)
729 char *tmp
, *retPtr
= NULL
;
731 WMListItem
*listItem
;
733 /* WMLoadBrowserColumnZero must be call first */
734 if (!bPtr
->flags
.loaded
) {
738 removeColumn(bPtr
, 1);
740 WMSelectListItem(bPtr
->columns
[0], -1);
741 WMSetListPosition(bPtr
->columns
[0], 0);
745 tmp
= strtok(str
, bPtr
->pathSeparator
);
747 /* select it in the column */
748 item
= WMFindRowOfListItemWithTitle(bPtr
->columns
[i
], tmp
);
750 retPtr
= &path
[(int)(tmp
- str
)];
753 WMSelectListItem(bPtr
->columns
[i
], item
);
754 WMSetListPosition(bPtr
->columns
[i
], item
);
756 listItem
= WMGetListItem(bPtr
->columns
[i
], item
);
757 if (!listItem
|| !listItem
->isBranch
) {
761 /* load next column */
762 WMAddBrowserColumn(bPtr
);
764 loadColumn(bPtr
, i
+1);
766 tmp
= strtok(NULL
, bPtr
->pathSeparator
);
773 for (i
= bPtr
->usedColumnCount
- 1;
774 (i
> -1) && !WMGetListSelectedItem(bPtr
->columns
[i
]);
777 bPtr
->selectedColumn
= i
;
779 if (bPtr
->columnCount
< bPtr
->maxVisibleColumns
) {
780 int i
= bPtr
->maxVisibleColumns
- bPtr
->columnCount
;
781 int curUsedColumnCount
= bPtr
->usedColumnCount
;
782 bPtr
->usedColumnCount
= bPtr
->columnCount
;
784 WMAddBrowserColumn(bPtr
);
786 bPtr
->usedColumnCount
= curUsedColumnCount
;
789 scrollToColumn(bPtr
, bPtr
->columnCount
- bPtr
->maxVisibleColumns
, True
);
796 WMGetBrowserPath(WMBrowser
*bPtr
)
798 return WMGetBrowserPathToColumn(bPtr
, bPtr
->columnCount
);
803 WMGetBrowserPathToColumn(WMBrowser
*bPtr
, int column
)
809 if (column
>= bPtr
->usedColumnCount
)
810 column
= bPtr
->usedColumnCount
-1;
813 return wstrdup(bPtr
->pathSeparator
);
816 /* calculate size of buffer */
818 for (i
= 0; i
<= column
; i
++) {
819 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
822 size
+= strlen(item
->text
);
826 path
= wmalloc(size
+(column
+1)*strlen(bPtr
->pathSeparator
)+1);
829 for (i
= 0; i
<= column
; i
++) {
830 strcat(path
, bPtr
->pathSeparator
);
831 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
834 strcat(path
, item
->text
);
842 WMGetBrowserPaths(WMBrowser
*bPtr
)
844 int column
, i
, k
, size
, selNo
;
846 WMListItem
*item
, *lastItem
;
847 WMArray
*paths
, *items
;
849 column
= bPtr
->usedColumnCount
-1;
852 paths
= WMCreateArrayWithDestructor(1, wfree
);
853 WMAddToArray(paths
, wstrdup(bPtr
->pathSeparator
));
857 items
= WMGetListSelectedItems(bPtr
->columns
[column
]);
858 selNo
= WMGetArrayItemCount(items
);
859 paths
= WMCreateArrayWithDestructor(selNo
, wfree
);
862 WMAddToArray(paths
, WMGetBrowserPath(bPtr
));
866 /* calculate size of buffer */
868 for (i
=0; i
<column
; i
++) {
869 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
872 size
+= strlen(item
->text
);
875 size
+= (column
+1)*strlen(bPtr
->pathSeparator
)+1;
877 for (k
=0; k
<selNo
; k
++) {
879 lastItem
= WMGetFromArray(items
, k
);
880 path
= wmalloc(size
+ (lastItem
!=NULL
? strlen(lastItem
->text
) : 0));
883 for (i
=0; i
<=column
; i
++) {
884 strcat(path
, bPtr
->pathSeparator
);
888 item
= WMGetListSelectedItem(bPtr
->columns
[i
]);
892 strcat(path
, item
->text
);
894 WMAddToArray(paths
, path
);
902 WMBrowserAllowsMultipleSelection(WMBrowser
*bPtr
)
904 return bPtr
->flags
.allowMultipleSelection
;
909 WMBrowserAllowsEmptySelection(WMBrowser
*bPtr
)
911 return bPtr
->flags
.allowEmptySelection
;
916 loadColumn(WMBrowser
*bPtr
, int column
)
918 assert(bPtr
->delegate
);
919 assert(bPtr
->delegate
->createRowsForColumn
);
921 bPtr
->flags
.loadingColumn
= 1;
922 (*bPtr
->delegate
->createRowsForColumn
)(bPtr
->delegate
, bPtr
, column
,
923 bPtr
->columns
[column
]);
924 bPtr
->flags
.loadingColumn
= 0;
926 if (bPtr
->delegate
->titleOfColumn
) {
929 title
= (*bPtr
->delegate
->titleOfColumn
)(bPtr
->delegate
, bPtr
, column
);
931 if (bPtr
->titles
[column
])
932 wfree(bPtr
->titles
[column
]);
934 bPtr
->titles
[column
] = wstrdup(title
);
936 if (COLUMN_IS_VISIBLE(bPtr
, column
) && bPtr
->flags
.isTitled
) {
937 drawTitleOfColumn(bPtr
, column
);
944 paintBrowser(WMBrowser
*bPtr
)
948 if (!bPtr
->view
->flags
.mapped
)
951 W_DrawRelief(bPtr
->view
->screen
, bPtr
->view
->window
, 0,
952 bPtr
->view
->size
.height
-SCROLLER_WIDTH
-2,
953 bPtr
->view
->size
.width
, 22, WRSunken
);
955 if (bPtr
->flags
.isTitled
) {
956 for (i
=0; i
<bPtr
->maxVisibleColumns
; i
++) {
957 drawTitleOfColumn(bPtr
, i
+bPtr
->firstVisibleColumn
);
964 handleEvents(XEvent
*event
, void *data
)
966 WMBrowser
*bPtr
= (WMBrowser
*)data
;
968 CHECK_CLASS(data
, WC_Browser
);
971 switch (event
->type
) {
977 destroyBrowser(bPtr
);
986 scrollToColumn(WMBrowser
*bPtr
, int column
, Bool updateScroller
)
993 if (column
!= bPtr
->firstVisibleColumn
) {
1000 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->willScroll
) {
1001 (*bPtr
->delegate
->willScroll
)(bPtr
->delegate
, bPtr
);
1005 bPtr
->firstVisibleColumn
= column
;
1006 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
1007 if (COLUMN_IS_VISIBLE(bPtr
, i
)) {
1008 WMMoveWidget(bPtr
->columns
[i
], x
,
1009 WMWidgetView(bPtr
->columns
[i
])->pos
.y
);
1010 if (!WMWidgetView(bPtr
->columns
[i
])->flags
.realized
)
1011 WMRealizeWidget(bPtr
->columns
[i
]);
1012 WMMapWidget(bPtr
->columns
[i
]);
1013 x
+= bPtr
->columnSize
.width
+ COLUMN_SPACING
;
1015 WMUnmapWidget(bPtr
->columns
[i
]);
1019 /* update the scroller */
1020 if (updateScroller
) {
1021 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
1022 float value
, proportion
;
1024 value
= bPtr
->firstVisibleColumn
1025 /(float)(bPtr
->columnCount
-bPtr
->maxVisibleColumns
);
1026 proportion
= bPtr
->maxVisibleColumns
/(float)bPtr
->columnCount
;
1027 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
1029 WMSetScrollerParameters(bPtr
->scroller
, 0, 1);
1033 if (bPtr
->view
->flags
.mapped
)
1036 if (notify
&& bPtr
->delegate
&& bPtr
->delegate
->didScroll
) {
1037 (*bPtr
->delegate
->didScroll
)(bPtr
->delegate
, bPtr
);
1043 listCallback(void *self
, void *clientData
)
1045 WMBrowser
*bPtr
= (WMBrowser
*)clientData
;
1046 WMList
*lPtr
= (WMList
*)self
;
1049 static WMListItem
*oldItem
= NULL
;
1050 static int oldSelNo
= 0;
1052 item
= WMGetListSelectedItem(lPtr
);
1053 selNo
= WMGetArrayItemCount(WMGetListSelectedItems(lPtr
));
1055 if (oldItem
==NULL
|| oldItem
!=item
|| oldSelNo
!=selNo
) {
1056 for (i
=0; i
<bPtr
->columnCount
; i
++) {
1057 if (lPtr
== bPtr
->columns
[i
])
1060 assert(i
<bPtr
->columnCount
);
1062 bPtr
->selectedColumn
= i
;
1064 /* columns at right must be cleared */
1065 removeColumn(bPtr
, i
+1);
1066 /* open directory */
1067 if (item
&& item
->isBranch
&& selNo
==1) {
1068 WMAddBrowserColumn(bPtr
);
1070 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
1073 i
= bPtr
->usedColumnCount
-bPtr
->maxVisibleColumns
;
1074 scrollToColumn(bPtr
, i
, True
);
1075 if (item
&& item
->isBranch
&& selNo
==1) {
1076 loadColumn(bPtr
, bPtr
->usedColumnCount
-1);
1080 /* call callback for click */
1082 (*bPtr
->action
)(bPtr
, bPtr
->clientData
);
1090 listDoubleCallback(void *self
, void *clientData
)
1092 WMBrowser
*bPtr
= (WMBrowser
*)clientData
;
1093 WMList
*lPtr
= (WMList
*)self
;
1096 item
= WMGetListSelectedItem(lPtr
);
1100 /* call callback for double click */
1101 if (bPtr
->doubleAction
)
1102 (*bPtr
->doubleAction
)(bPtr
, bPtr
->doubleClientData
);
1107 WMLoadBrowserColumnZero(WMBrowser
*bPtr
)
1109 if (!bPtr
->flags
.loaded
) {
1110 /* create column 0 */
1111 WMAddBrowserColumn(bPtr
);
1113 loadColumn(bPtr
, 0);
1115 /* make column 0 visible */
1116 scrollToColumn(bPtr
, 0, True
);
1118 bPtr
->flags
.loaded
= 1;
1124 WMRemoveBrowserItem(WMBrowser
*bPtr
, int column
, int row
)
1128 if (column
< 0 || column
>= bPtr
->usedColumnCount
)
1131 list
= WMGetBrowserListInColumn(bPtr
, column
);
1133 if (row
< 0 || row
>= WMGetListNumberOfRows(list
))
1136 removeColumn(bPtr
, column
+1);
1137 if (bPtr
->usedColumnCount
< bPtr
->maxVisibleColumns
)
1138 scrollToColumn(bPtr
, 0, True
);
1140 scrollToColumn(bPtr
, bPtr
->usedColumnCount
-bPtr
->maxVisibleColumns
,
1143 WMRemoveListItem(list
, row
);
1148 listSelectionObserver(void *observerData
, WMNotification
*notification
)
1150 WMBrowser
*bPtr
= (WMBrowser
*)observerData
;
1152 WMList
*lPtr
= (WMList
*)WMGetNotificationObject(notification
);
1154 for (column
= 0; column
< bPtr
->usedColumnCount
; column
++)
1155 if (bPtr
->columns
[column
] == lPtr
)
1158 /* this can happen when a list is being cleared with WMClearList
1159 * after the column was removed */
1160 if (column
>= bPtr
->usedColumnCount
) {
1164 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr
)) == 0)
1167 bPtr
->selectedColumn
= column
;
1172 WMAddBrowserColumn(WMBrowser
*bPtr
)
1181 if (bPtr
->usedColumnCount
< bPtr
->columnCount
) {
1182 return bPtr
->usedColumnCount
++;
1185 bPtr
->usedColumnCount
++;
1187 if (bPtr
->flags
.isTitled
) {
1188 colY
= TITLE_SPACING
+ bPtr
->titleHeight
;
1193 index
= bPtr
->columnCount
;
1194 bPtr
->columnCount
++;
1195 clist
= wmalloc(sizeof(WMList
*)*bPtr
->columnCount
);
1196 tlist
= wmalloc(sizeof(char*)*bPtr
->columnCount
);
1197 memcpy(clist
, bPtr
->columns
, sizeof(WMList
*)*(bPtr
->columnCount
-1));
1198 memcpy(tlist
, bPtr
->titles
, sizeof(char*)*(bPtr
->columnCount
-1));
1200 wfree(bPtr
->columns
);
1202 wfree(bPtr
->titles
);
1203 bPtr
->columns
= clist
;
1204 bPtr
->titles
= tlist
;
1206 bPtr
->titles
[index
] = NULL
;
1208 list
= WMCreateList(bPtr
);
1209 WMSetListAllowMultipleSelection(list
, bPtr
->flags
.allowMultipleSelection
);
1210 WMSetListAllowEmptySelection(list
, bPtr
->flags
.allowEmptySelection
);
1211 WMSetListAction(list
, listCallback
, bPtr
);
1212 WMSetListDoubleAction(list
, listDoubleCallback
, bPtr
);
1213 WMSetListUserDrawProc(list
, paintItem
);
1214 WMAddNotificationObserver(listSelectionObserver
, bPtr
,
1215 WMListSelectionDidChangeNotification
, list
);
1217 bPtr
->columns
[index
] = list
;
1219 WMResizeWidget(list
, bPtr
->columnSize
.width
, bPtr
->columnSize
.height
);
1220 WMMoveWidget(list
, (bPtr
->columnSize
.width
+COLUMN_SPACING
)*index
, colY
);
1221 if (COLUMN_IS_VISIBLE(bPtr
, index
))
1224 /* update the scroller */
1225 if (bPtr
->columnCount
> bPtr
->maxVisibleColumns
) {
1226 float value
, proportion
;
1228 value
= bPtr
->firstVisibleColumn
1229 /(float)(bPtr
->columnCount
-bPtr
->maxVisibleColumns
);
1230 proportion
= bPtr
->maxVisibleColumns
/(float)bPtr
->columnCount
;
1231 WMSetScrollerParameters(bPtr
->scroller
, value
, proportion
);
1240 destroyBrowser(WMBrowser
*bPtr
)
1244 for (i
= 0; i
< bPtr
->columnCount
; i
++) {
1245 if (bPtr
->titles
[i
])
1246 wfree(bPtr
->titles
[i
]);
1248 wfree(bPtr
->titles
);
1250 wfree(bPtr
->pathSeparator
);
1252 WMRemoveNotificationObserver(bPtr
);
1259 createTruncatedString(WMFont
*font
, char *text
, int *textLen
, int width
)
1261 int dLen
= WMWidthOfString(font
, ".", 1);
1262 char *textBuf
= (char*)wmalloc((*textLen
)+4);
1264 if (width
>= 3*dLen
) {
1265 int dddLen
= 3*dLen
;
1266 int tmpTextLen
= *textLen
;
1268 strcpy(textBuf
, text
);
1270 && (WMWidthOfString(font
, textBuf
, tmpTextLen
)+dddLen
> width
))
1272 strcpy(textBuf
+tmpTextLen
, "...");
1273 *textLen
= tmpTextLen
+3;
1274 } else if (width
>= 2*dLen
) {
1275 strcpy(textBuf
, "..");
1277 } else if (width
>= dLen
) {
1278 strcpy(textBuf
, ".");