Update German translation
[wmaker-crm.git] / WINGs / wbrowser.c
blobb539543a41e7e83c8b934134e6d47556ec8c0395
2 #include "WINGsP.h"
3 #include <math.h> /* for : double rint (double) */
5 typedef struct W_Browser {
6 W_Class widgetClass;
7 W_View *view;
9 char **titles;
10 WMList **columns;
12 short columnCount;
13 short usedColumnCount; /* columns actually being used */
14 short minColumnWidth;
16 short maxVisibleColumns;
17 short firstVisibleColumn;
19 short titleHeight;
21 short selectedColumn;
23 WMSize columnSize;
25 void *clientData;
26 WMAction *action;
27 void *doubleClientData;
28 WMAction *doubleAction;
30 WMBrowserDelegate *delegate;
32 WMScroller *scroller;
34 char *pathSeparator;
36 struct {
37 unsigned int isTitled:1;
38 unsigned int allowMultipleSelection:1;
39 unsigned int allowEmptySelection:1;
40 unsigned int hasScroller:1;
42 /* */
43 unsigned int loaded:1;
44 unsigned int loadingColumn:1;
45 } flags;
46 } Browser;
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 = {
83 NULL,
84 NULL,
85 NULL,
86 NULL,
87 willResizeBrowser
90 WMBrowser *WMCreateBrowser(WMWidget * parent)
92 WMBrowser *bPtr;
93 int i;
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));
103 if (!bPtr->view) {
104 wfree(bPtr);
105 return NULL;
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)
126 setupScroller(bPtr);
128 for (i = 0; i < bPtr->maxVisibleColumns; i++) {
129 WMAddBrowserColumn(bPtr);
131 bPtr->usedColumnCount = 0;
133 bPtr->selectedColumn = -1;
135 return bPtr;
138 void WMSetBrowserAllowMultipleSelection(WMBrowser * bPtr, Bool flag)
140 int i;
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)
150 int i;
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) {
173 return;
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;
182 while (i--) {
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;
199 while (i--) {
200 WMAddBrowserColumn(bPtr);
202 bPtr->usedColumnCount = curUsedColumnCount;
204 /* browser loaded and columns < curMaxVisibleColumns */
205 } else {
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;
234 int x;
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)
247 > widthC) {
248 char *titleBuf = createTruncatedString(scr->boldFont,
249 bPtr->titles[column],
250 &titleLen, widthC);
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);
254 wfree(titleBuf);
255 } else {
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)
267 return NULL;
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;
285 WMList **clist;
286 char **tlist;
288 assert(bPtr != NULL);
290 column = (column < 0) ? 0 : column;
291 if (column >= bPtr->columnCount) {
292 return;
294 if (column < bPtr->maxVisibleColumns) {
295 clearEnd = bPtr->maxVisibleColumns;
296 destroyEnd = bPtr->columnCount;
297 bPtr->columnCount = bPtr->maxVisibleColumns;
298 } else {
299 clearEnd = column;
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));
326 wfree(bPtr->titles);
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))
335 return NULL;
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]);
349 } else {
350 return -1;
354 void WMSetBrowserColumnTitle(WMBrowser * bPtr, int column, char *title)
356 assert(column >= 0);
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)
371 int i;
372 int columnX, columnY;
374 flag = ((flag == 0) ? 0 : 1);
376 if (bPtr->flags.isTitled == flag)
377 return;
379 columnX = 0;
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);
393 } else {
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)
420 WMListItem *item;
422 if (column < 0 || column >= bPtr->columnCount)
423 return NULL;
425 item = WMInsertListItem(bPtr->columns[column], row, text);
426 item->isBranch = isBranch;
428 return item;
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;
435 int colX, colY;
436 int i;
438 assert(*width > 0);
439 assert(*height > 0);
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;
447 } else {
448 colY = 0;
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);
460 colX = 0;
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;
483 x = rect->pos.x;
484 y = rect->pos.y;
485 textLen = strlen(text);
487 XFillRectangle(display, d, WMColorGC(backColor), x, y, width, height);
489 if (text) {
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);
495 wfree(textBuf);
496 } else {
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;
517 int newFirst;
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);
525 break;
527 case WSDecrementPage:
528 case WSDecrementWheel:
529 if (bPtr->firstVisibleColumn > 0) {
530 newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
532 scrollToColumn(bPtr, newFirst, True);
534 break;
536 case WSIncrementLine:
537 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
538 scrollToColumn(bPtr, bPtr->firstVisibleColumn + 1, True);
540 break;
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);
552 break;
554 case WSKnob:
556 double floatValue;
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);
567 /*else
568 WMSetScrollerParameters(bPtr->scroller, floatValue,
569 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
573 break;
575 case WSKnobSlot:
576 case WSNoPart:
577 /* do nothing */
578 break;
580 #undef LAST_VISIBLE_COLUMN
583 static void setupScroller(WMBrowser * bPtr)
585 WMScroller *sPtr;
586 int y;
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;
597 WMMapWidget(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)
619 int i;
620 char *str;
621 char *tmp, *retPtr = NULL;
622 int item;
623 WMListItem *listItem;
625 /* WMLoadBrowserColumnZero must be call first */
626 if (!bPtr->flags.loaded) {
627 return False;
630 removeColumn(bPtr, 1);
632 WMSelectListItem(bPtr->columns[0], -1);
633 WMSetListPosition(bPtr->columns[0], 0);
635 i = 0;
636 str = wstrdup(path);
637 tmp = strtok(str, bPtr->pathSeparator);
638 while (tmp) {
639 /* select it in the column */
640 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
641 if (item < 0) {
642 retPtr = &path[(int)(tmp - str)];
643 break;
645 WMSelectListItem(bPtr->columns[i], item);
646 WMSetListPosition(bPtr->columns[i], item);
648 listItem = WMGetListItem(bPtr->columns[i], item);
649 if (!listItem || !listItem->isBranch) {
650 break;
653 /* load next column */
654 WMAddBrowserColumn(bPtr);
656 loadColumn(bPtr, i + 1);
658 tmp = strtok(NULL, bPtr->pathSeparator);
660 i++;
663 wfree(str);
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;
673 while (i--) {
674 WMAddBrowserColumn(bPtr);
676 bPtr->usedColumnCount = curUsedColumnCount;
679 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
681 return retPtr;
684 char *WMGetBrowserPath(WMBrowser * bPtr)
686 return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
689 char *WMGetBrowserPathToColumn(WMBrowser * bPtr, int column)
691 int i, size;
692 char *path;
693 WMListItem *item;
695 if (column >= bPtr->usedColumnCount)
696 column = bPtr->usedColumnCount - 1;
698 if (column < 0) {
699 return wstrdup(bPtr->pathSeparator);
702 /* calculate size of buffer */
703 size = 0;
704 for (i = 0; i <= column; i++) {
705 item = WMGetListSelectedItem(bPtr->columns[i]);
706 if (!item)
707 break;
708 size += strlen(item->text);
711 /* get the path */
712 path = wmalloc(size + (column + 1) * strlen(bPtr->pathSeparator) + 1);
713 /* ignore first / */
714 *path = 0;
715 for (i = 0; i <= column; i++) {
716 strcat(path, bPtr->pathSeparator);
717 item = WMGetListSelectedItem(bPtr->columns[i]);
718 if (!item)
719 break;
720 strcat(path, item->text);
723 return path;
726 WMArray *WMGetBrowserPaths(WMBrowser * bPtr)
728 int column, i, k, size, selNo;
729 char *path;
730 WMListItem *item, *lastItem;
731 WMArray *paths, *items;
733 column = bPtr->usedColumnCount - 1;
735 if (column < 0) {
736 paths = WMCreateArrayWithDestructor(1, wfree);
737 WMAddToArray(paths, wstrdup(bPtr->pathSeparator));
738 return paths;
741 items = WMGetListSelectedItems(bPtr->columns[column]);
742 selNo = WMGetArrayItemCount(items);
743 paths = WMCreateArrayWithDestructor(selNo, wfree);
745 if (selNo <= 1) {
746 WMAddToArray(paths, WMGetBrowserPath(bPtr));
747 return paths;
750 /* calculate size of buffer */
751 size = 0;
752 for (i = 0; i < column; i++) {
753 item = WMGetListSelectedItem(bPtr->columns[i]);
754 if (!item)
755 break;
756 size += strlen(item->text);
759 size += (column + 1) * strlen(bPtr->pathSeparator) + 1;
761 for (k = 0; k < selNo; k++) {
762 /* get the path */
763 lastItem = WMGetFromArray(items, k);
764 path = wmalloc(size + (lastItem != NULL ? strlen(lastItem->text) : 0));
765 /* ignore first / */
766 *path = 0;
767 for (i = 0; i <= column; i++) {
768 strcat(path, bPtr->pathSeparator);
769 if (i == column) {
770 item = lastItem;
771 } else {
772 item = WMGetListSelectedItem(bPtr->columns[i]);
774 if (!item)
775 break;
776 strcat(path, item->text);
778 WMAddToArray(paths, path);
781 return paths;
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) {
804 char *title;
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)
821 int i;
823 if (!bPtr->view->flags.mapped)
824 return;
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) {
843 case Expose:
844 paintBrowser(bPtr);
845 break;
847 case DestroyNotify:
848 destroyBrowser(bPtr);
849 break;
854 static void scrollToColumn(WMBrowser * bPtr, int column, Bool updateScroller)
856 int i;
857 int x;
858 int notify = 0;
860 if (column != bPtr->firstVisibleColumn) {
861 notify = 1;
864 if (column < 0)
865 column = 0;
867 if (notify && bPtr->delegate && bPtr->delegate->willScroll) {
868 (*bPtr->delegate->willScroll) (bPtr->delegate, bPtr);
871 x = 0;
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;
880 } else {
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);
893 } else {
894 WMSetScrollerParameters(bPtr->scroller, 0, 1);
898 if (bPtr->view->flags.mapped)
899 paintBrowser(bPtr);
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;
910 WMListItem *item;
911 int i, selNo;
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])
921 break;
923 assert(i < bPtr->columnCount);
925 bPtr->selectedColumn = i;
927 /* columns at right must be cleared */
928 removeColumn(bPtr, i + 1);
929 /* open directory */
930 if (item && item->isBranch && selNo == 1) {
931 WMAddBrowserColumn(bPtr);
933 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
934 i = 0;
935 else
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 */
944 if (bPtr->action)
945 (*bPtr->action) (bPtr, bPtr->clientData);
947 oldItem = item;
948 oldSelNo = selNo;
951 static void listDoubleCallback(void *self, void *clientData)
953 WMBrowser *bPtr = (WMBrowser *) clientData;
954 WMList *lPtr = (WMList *) self;
955 WMListItem *item;
957 item = WMGetListSelectedItem(lPtr);
958 if (!item)
959 return;
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);
972 loadColumn(bPtr, 0);
974 /* make column 0 visible */
975 scrollToColumn(bPtr, 0, True);
977 bPtr->flags.loaded = 1;
981 void WMRemoveBrowserItem(WMBrowser * bPtr, int column, int row)
983 WMList *list;
985 if (column < 0 || column >= bPtr->usedColumnCount)
986 return;
988 list = WMGetBrowserListInColumn(bPtr, column);
990 if (row < 0 || row >= WMGetListNumberOfRows(list))
991 return;
993 removeColumn(bPtr, column + 1);
994 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
995 scrollToColumn(bPtr, 0, True);
996 else
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;
1005 int column;
1006 WMList *lPtr = (WMList *) WMGetNotificationObject(notification);
1008 for (column = 0; column < bPtr->usedColumnCount; column++)
1009 if (bPtr->columns[column] == lPtr)
1010 break;
1012 /* this can happen when a list is being cleared with WMClearList
1013 * after the column was removed */
1014 if (column >= bPtr->usedColumnCount) {
1015 return;
1018 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr)) == 0)
1019 column--;
1021 bPtr->selectedColumn = column;
1024 int WMAddBrowserColumn(WMBrowser * bPtr)
1026 WMList *list;
1027 WMList **clist;
1028 char **tlist;
1029 int colY;
1030 int index;
1032 if (bPtr->usedColumnCount < bPtr->columnCount) {
1033 return bPtr->usedColumnCount++;
1036 bPtr->usedColumnCount++;
1038 if (bPtr->flags.isTitled) {
1039 colY = TITLE_SPACING + bPtr->titleHeight;
1040 } else {
1041 colY = 0;
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));
1050 if (bPtr->columns)
1051 wfree(bPtr->columns);
1052 if (bPtr->titles)
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))
1072 WMMapWidget(list);
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);
1083 return index;
1086 static void destroyBrowser(WMBrowser * bPtr)
1088 int i;
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);
1100 wfree(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))
1114 tmpTextLen--;
1115 strcpy(textBuf + tmpTextLen, "...");
1116 *textLen = tmpTextLen + 3;
1117 } else if (width >= 2 * dLen) {
1118 strcpy(textBuf, "..");
1119 *textLen = 2;
1120 } else if (width >= dLen) {
1121 strcpy(textBuf, ".");
1122 *textLen = 1;
1123 } else {
1124 *textBuf = '\0';
1125 *textLen = 0;
1127 return textBuf;