- fixed a bug with setting initial path in browsers.
[wmaker-crm.git] / WINGs / wbrowser.c
blob8d4ed7d688058d3aa7e282d78af9f0919c0b4714
5 #include "WINGsP.h"
6 #include <math.h> /* for : double rint (double) */
9 char *WMBrowserDidScrollNotification = "WMBrowserDidScrollNotification";
12 typedef struct W_Browser {
13 W_Class widgetClass;
14 W_View *view;
16 char **titles;
17 WMList **columns;
19 short columnCount;
20 short usedColumnCount; /* columns actually being used */
21 short minColumnWidth;
23 short maxVisibleColumns;
24 short firstVisibleColumn;
26 short titleHeight;
28 short selectedColumn;
30 WMSize columnSize;
33 void *clientData;
34 WMAction *action;
35 void *doubleClientData;
36 WMAction *doubleAction;
38 WMBrowserFillColumnProc *fillColumn;
40 WMScroller *scroller;
42 char *pathSeparator;
44 struct {
45 unsigned int isTitled:1;
46 unsigned int allowMultipleSelection:1;
47 unsigned int hasScroller:1;
49 /* */
50 unsigned int loaded:1;
51 unsigned int loadingColumn:1;
52 } flags;
53 } Browser;
56 #define COLUMN_SPACING 4
57 #define TITLE_SPACING 2
59 #define DEFAULT_WIDTH 305
60 #define DEFAULT_HEIGHT 200
61 #define DEFAULT_HAS_SCROLLER True
62 #define DEFAULT_TITLE_HEIGHT 20
63 #define DEFAULT_IS_TITLED True
64 #define DEFAULT_MAX_VISIBLE_COLUMNS 2
65 #define DEFAULT_SEPARATOR "/"
67 #define MIN_VISIBLE_COLUMNS 1
68 #define MAX_VISIBLE_COLUMNS 32
71 #define COLUMN_IS_VISIBLE(b, c) ((c) >= (b)->firstVisibleColumn \
72 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
75 static void handleEvents(XEvent *event, void *data);
76 static void destroyBrowser(WMBrowser *bPtr);
78 static void setupScroller(WMBrowser *bPtr);
80 static void scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller);
82 static void paintItem(WMList *lPtr, int index, Drawable d, char *text,
83 int state, WMRect *rect);
85 static void loadColumn(WMBrowser *bPtr, int column);
87 static void removeColumn(WMBrowser *bPtr, int column);
89 static char*
90 createTruncatedString(WMFont *font, char *text, int *textLen, int width);
92 static void resizeBrowser(WMWidget*, unsigned int, unsigned int);
94 W_ViewProcedureTable _BrowserViewProcedures = {
95 NULL,
96 resizeBrowser,
97 NULL
102 WMBrowser*
103 WMCreateBrowser(WMWidget *parent)
105 WMBrowser *bPtr;
106 int i;
108 wassertrv(parent, NULL);
110 bPtr = wmalloc(sizeof(WMBrowser));
111 memset(bPtr, 0, sizeof(WMBrowser));
113 bPtr->widgetClass = WC_Browser;
115 bPtr->view = W_CreateView(W_VIEW(parent));
116 if (!bPtr->view) {
117 free(bPtr);
118 return NULL;
120 bPtr->view->self = bPtr;
122 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask
123 |ClientMessageMask, handleEvents, bPtr);
125 /* default configuration */
126 bPtr->flags.hasScroller = DEFAULT_HAS_SCROLLER;
128 bPtr->titleHeight = DEFAULT_TITLE_HEIGHT;
129 bPtr->flags.isTitled = DEFAULT_IS_TITLED;
130 bPtr->maxVisibleColumns = DEFAULT_MAX_VISIBLE_COLUMNS;
132 resizeBrowser(bPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
134 bPtr->pathSeparator = wstrdup(DEFAULT_SEPARATOR);
136 if (bPtr->flags.hasScroller)
137 setupScroller(bPtr);
139 for (i=0; i<bPtr->maxVisibleColumns; i++) {
140 WMAddBrowserColumn(bPtr);
142 bPtr->usedColumnCount = 0;
144 bPtr->selectedColumn = -1;
146 return bPtr;
151 WMGetBrowserMaxVisibleColumns(WMBrowser *bPtr)
153 return bPtr->maxVisibleColumns;
157 void
158 WMSetBrowserMaxVisibleColumns(WMBrowser *bPtr, int columns)
160 int curMaxVisibleColumns;
161 int newFirstVisibleColumn = 0;
163 assert ((int) bPtr);
165 columns = (columns < MIN_VISIBLE_COLUMNS) ? MIN_VISIBLE_COLUMNS : columns;
166 columns = (columns > MAX_VISIBLE_COLUMNS) ? MAX_VISIBLE_COLUMNS : columns;
167 if (columns == bPtr->maxVisibleColumns) {
168 return;
170 curMaxVisibleColumns = bPtr->maxVisibleColumns;
171 bPtr->maxVisibleColumns = columns;
172 /* browser not loaded */
173 if (!bPtr->flags.loaded) {
174 if ((columns > curMaxVisibleColumns) && (columns > bPtr->columnCount)) {
175 int i = columns - bPtr->columnCount;
176 bPtr->usedColumnCount = bPtr->columnCount;
177 while (i--) {
178 WMAddBrowserColumn(bPtr);
180 bPtr->usedColumnCount = 0;
182 /* browser loaded and columns > curMaxVisibleColumns */
183 } else if (columns > curMaxVisibleColumns) {
184 if (bPtr->usedColumnCount > columns) {
185 newFirstVisibleColumn = bPtr->usedColumnCount - columns;
187 if (newFirstVisibleColumn > bPtr->firstVisibleColumn) {
188 newFirstVisibleColumn = bPtr->firstVisibleColumn;
190 if (columns > bPtr->columnCount) {
191 int i = columns - bPtr->columnCount;
192 int curUsedColumnCount = bPtr->usedColumnCount;
193 bPtr->usedColumnCount = bPtr->columnCount;
194 while (i--) {
195 WMAddBrowserColumn(bPtr);
197 bPtr->usedColumnCount = curUsedColumnCount;
199 /* browser loaded and columns < curMaxVisibleColumns */
200 } else {
201 newFirstVisibleColumn = bPtr->firstVisibleColumn;
202 if (newFirstVisibleColumn + columns >= bPtr->usedColumnCount) {
203 removeColumn(bPtr, newFirstVisibleColumn + columns);
206 resizeBrowser(bPtr, bPtr->view->size.width, bPtr->view->size.height);
207 if (bPtr->flags.loaded) {
208 XClearArea(bPtr->view->screen->display, bPtr->view->window, 0, 0,
209 bPtr->view->size.width, bPtr->titleHeight, False);
210 scrollToColumn (bPtr, newFirstVisibleColumn, True);
215 int
216 WMGetBrowserNumberOfColumns(WMBrowser *bPtr)
218 return bPtr->usedColumnCount;
221 void
222 WMSetBrowserPathSeparator(WMBrowser *bPtr, char *separator)
224 if (bPtr->pathSeparator)
225 free(bPtr->pathSeparator);
226 bPtr->pathSeparator = wstrdup(separator);
231 static void
232 drawTitleOfColumn(WMBrowser *bPtr, int column)
234 WMScreen *scr = bPtr->view->screen;
235 int x;
237 x=(column-bPtr->firstVisibleColumn)*(bPtr->columnSize.width+COLUMN_SPACING);
239 XFillRectangle(scr->display, bPtr->view->window, WMColorGC(scr->darkGray), x, 0,
240 bPtr->columnSize.width, bPtr->titleHeight);
241 W_DrawRelief(scr, bPtr->view->window, x, 0,
242 bPtr->columnSize.width, bPtr->titleHeight, WRSunken);
244 if (column < bPtr->usedColumnCount && bPtr->titles[column]) {
245 int titleLen = strlen(bPtr->titles[column]);
246 int widthC = bPtr->columnSize.width-8;
248 if (WMWidthOfString(scr->boldFont, bPtr->titles[column], titleLen)
249 > widthC) {
250 char *titleBuf = createTruncatedString(scr->boldFont,
251 bPtr->titles[column],
252 &titleLen, widthC);
253 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
254 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
255 bPtr->columnSize.width, WACenter, WMColorGC(scr->white),
256 False, titleBuf, titleLen);
257 free (titleBuf);
258 } else {
259 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
260 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
261 bPtr->columnSize.width, WACenter, WMColorGC(scr->white),
262 False, bPtr->titles[column], titleLen);
268 void
269 WMSetBrowserColumnTitle(WMBrowser *bPtr, int column, char *title)
271 assert(column >= 0);
272 assert(column < bPtr->usedColumnCount);
274 if (bPtr->titles[column])
275 free(bPtr->titles[column]);
277 bPtr->titles[column] = wstrdup(title);
279 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
280 drawTitleOfColumn(bPtr, column);
285 WMList*
286 WMGetBrowserListInColumn(WMBrowser *bPtr, int column)
288 if (column < 0 || column >= bPtr->usedColumnCount)
289 return NULL;
291 return bPtr->columns[column];
295 void
296 WMSetBrowserFillColumnProc(WMBrowser *bPtr, WMBrowserFillColumnProc *proc)
298 bPtr->fillColumn = proc;
302 int
303 WMGetBrowserFirstVisibleColumn(WMBrowser *bPtr)
305 return bPtr->firstVisibleColumn;
309 static void
310 removeColumn(WMBrowser *bPtr, int column)
312 int i, clearEnd, destroyEnd;
313 WMList **clist;
314 char **tlist;
316 assert ((int) bPtr);
318 column = (column < 0) ? 0 : column;
319 if (column >= bPtr->columnCount) {
320 return;
322 if (column < bPtr->maxVisibleColumns) {
323 clearEnd = bPtr->maxVisibleColumns;
324 destroyEnd = bPtr->columnCount;
325 bPtr->columnCount = bPtr->maxVisibleColumns;
326 } else {
327 clearEnd = column;
328 destroyEnd = bPtr->columnCount;
329 bPtr->columnCount = column;
331 if (column < bPtr->usedColumnCount) {
332 bPtr->usedColumnCount = column;
334 for (i=column; i < clearEnd; i++) {
335 if (bPtr->titles[i]) {
336 free(bPtr->titles[i]);
337 bPtr->titles[i] = NULL;
339 WMClearList(bPtr->columns[i]);
341 for (;i < destroyEnd; i++) {
342 if (bPtr->titles[i]) {
343 free(bPtr->titles[i]);
344 bPtr->titles[i] = NULL;
346 WMRemoveNotificationObserverWithName(bPtr,
347 WMListSelectionDidChangeNotification,
348 bPtr->columns[i]);
349 WMDestroyWidget(bPtr->columns[i]);
350 bPtr->columns[i] = NULL;
352 clist = wmalloc(sizeof(WMList*) * (bPtr->columnCount));
353 tlist = wmalloc(sizeof(char*) * (bPtr->columnCount));
354 memcpy(clist, bPtr->columns, sizeof(WMList*) * (bPtr->columnCount));
355 memcpy(tlist, bPtr->titles, sizeof(char*) * (bPtr->columnCount));
356 free(bPtr->titles);
357 free(bPtr->columns);
358 bPtr->titles = tlist;
359 bPtr->columns = clist;
363 WMListItem*
364 WMGetBrowserSelectedItemInColumn(WMBrowser *bPtr, int column)
366 if ((column < 0) || (column >= bPtr->usedColumnCount))
367 return NULL;
369 return WMGetListSelectedItem(bPtr->columns[column]);
375 WMGetBrowserSelectedColumn(WMBrowser *bPtr)
377 return bPtr->selectedColumn;
382 WMGetBrowserSelectedRowInColumn(WMBrowser *bPtr, int column)
384 if (column >= 0 && column < bPtr->columnCount) {
385 return WMGetListSelectedItemRow(bPtr->columns[column]);
386 } else {
387 return -1;
392 void
393 WMSetBrowserTitled(WMBrowser *bPtr, Bool flag)
395 int i;
396 int columnX, columnY;
398 if (bPtr->flags.isTitled == flag)
399 return;
401 columnX = 0;
403 if (!bPtr->flags.isTitled) {
404 columnY = TITLE_SPACING + bPtr->titleHeight;
406 bPtr->columnSize.height -= columnY;
408 for (i=0; i<bPtr->columnCount; i++) {
409 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
410 bPtr->columnSize.height);
412 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
414 WMMoveWidget(bPtr->columns[i], columnX, columnY);
416 } else {
417 bPtr->columnSize.height += TITLE_SPACING + bPtr->titleHeight;
419 for (i=0; i<bPtr->columnCount; i++) {
420 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
421 bPtr->columnSize.height);
423 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
425 WMMoveWidget(bPtr->columns[i], columnX, 0);
429 bPtr->flags.isTitled = flag;
433 WMListItem*
434 WMAddSortedBrowserItem(WMBrowser *bPtr, int column, char *text, Bool isBranch)
436 WMListItem *item;
438 if (column < 0 || column >= bPtr->columnCount)
439 return NULL;
441 item = WMAddSortedListItem(bPtr->columns[column], text);
442 item->isBranch = isBranch;
444 return item;
449 WMListItem*
450 WMInsertBrowserItem(WMBrowser *bPtr, int column, int row, char *text,
451 Bool isBranch)
453 WMListItem *item;
455 if (column < 0 || column >= bPtr->columnCount)
456 return NULL;
458 item = WMInsertListItem(bPtr->columns[column], row, text);
459 item->isBranch = isBranch;
461 return item;
467 static void
468 resizeBrowser(WMWidget *w, unsigned int width, unsigned int height)
470 WMBrowser *bPtr = (WMBrowser*)w;
471 int cols = bPtr->maxVisibleColumns;
472 int colX, colY;
473 int i;
475 assert(width > 0);
476 assert(height > 0);
478 bPtr->columnSize.width = (width-(cols-1)*COLUMN_SPACING) / cols;
479 bPtr->columnSize.height = height;
481 if (bPtr->flags.isTitled) {
482 colY = TITLE_SPACING + bPtr->titleHeight;
483 bPtr->columnSize.height -= colY;
484 } else {
485 colY = 0;
488 if (bPtr->flags.hasScroller) {
489 bPtr->columnSize.height -= SCROLLER_WIDTH + 4;
491 if (bPtr->scroller) {
492 WMResizeWidget(bPtr->scroller, width-2, 1);
493 WMMoveWidget(bPtr->scroller, 1, height-SCROLLER_WIDTH-1);
497 colX = 0;
498 for (i = 0; i < bPtr->columnCount; i++) {
499 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
500 bPtr->columnSize.height);
502 WMMoveWidget(bPtr->columns[i], colX, colY);
504 if (COLUMN_IS_VISIBLE(bPtr, i)) {
505 colX += bPtr->columnSize.width+COLUMN_SPACING;
509 W_ResizeView(bPtr->view, width, height);
513 static void
514 paintItem(WMList *lPtr, int index, Drawable d, char *text, int state,
515 WMRect *rect)
517 WMView *view = W_VIEW(lPtr);
518 W_Screen *scr = view->screen;
519 int width, height, x, y;
521 width = rect->size.width;
522 height = rect->size.height;
523 x = rect->pos.x;
524 y = rect->pos.y;
526 if (state & WLDSSelected)
527 XFillRectangle(scr->display, d, WMColorGC(scr->white), x, y,
528 width, height);
529 else
530 XClearArea(scr->display, d, x, y, width, height, False);
532 if (text) {
533 /* Avoid overlaping... */
534 int textLen = strlen(text);
535 int widthC = (state & WLDSIsBranch) ? width-20 : width-8;
536 if (WMWidthOfString(scr->normalFont, text, textLen) > widthC) {
537 char *textBuf = createTruncatedString(scr->normalFont,
538 text, &textLen, widthC);
539 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
540 WALeft, WMColorGC(scr->black), False, textBuf, textLen);
541 free(textBuf);
542 } else {
543 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
544 WALeft, WMColorGC(scr->black), False, text, textLen);
548 if (state & WLDSIsBranch) {
549 XDrawLine(scr->display, d, WMColorGC(scr->darkGray), x+width-11, y+3,
550 x+width-6, y+height/2);
551 if (state & WLDSSelected)
552 XDrawLine(scr->display, d,WMColorGC(scr->gray), x+width-11, y+height-5,
553 x+width-6, y+height/2);
554 else
555 XDrawLine(scr->display, d,WMColorGC(scr->white), x+width-11, y+height-5,
556 x+width-6, y+height/2);
557 XDrawLine(scr->display, d, WMColorGC(scr->black), x+width-12, y+3,
558 x+width-12, y+height-5);
563 static void
564 scrollCallback(WMWidget *scroller, void *self)
566 WMBrowser *bPtr = (WMBrowser*)self;
567 WMScroller *sPtr = (WMScroller*)scroller;
568 int newFirst;
569 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
571 switch (WMGetScrollerHitPart(sPtr)) {
572 case WSDecrementLine:
573 if (bPtr->firstVisibleColumn > 0) {
574 scrollToColumn(bPtr, bPtr->firstVisibleColumn-1, True);
576 break;
578 case WSDecrementPage:
579 if (bPtr->firstVisibleColumn > 0) {
580 newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
582 scrollToColumn(bPtr, newFirst, True);
584 break;
587 case WSIncrementLine:
588 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
589 scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
591 break;
593 case WSIncrementPage:
594 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
595 newFirst = bPtr->firstVisibleColumn + bPtr->maxVisibleColumns;
597 if (newFirst+bPtr->maxVisibleColumns >= bPtr->columnCount)
598 newFirst = bPtr->columnCount - bPtr->maxVisibleColumns;
600 scrollToColumn(bPtr, newFirst, True);
602 break;
604 case WSKnob:
606 double floatValue;
607 double value = bPtr->columnCount - bPtr->maxVisibleColumns;
609 floatValue = WMGetScrollerValue(bPtr->scroller);
611 floatValue = (floatValue*value)/value;
613 newFirst = rint(floatValue*(float)(bPtr->columnCount - bPtr->maxVisibleColumns));
615 if (bPtr->firstVisibleColumn != newFirst)
616 scrollToColumn(bPtr, newFirst, False);
617 /* else
618 WMSetScrollerParameters(bPtr->scroller, floatValue,
619 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
623 break;
625 case WSKnobSlot:
626 case WSNoPart:
627 /* do nothing */
628 break;
630 #undef LAST_VISIBLE_COLUMN
634 static void
635 setupScroller(WMBrowser *bPtr)
637 WMScroller *sPtr;
638 int y;
640 y = bPtr->view->size.height - SCROLLER_WIDTH - 1;
642 sPtr = WMCreateScroller(bPtr);
643 WMSetScrollerAction(sPtr, scrollCallback, bPtr);
644 WMMoveWidget(sPtr, 1, y);
645 WMResizeWidget(sPtr, bPtr->view->size.width-2, SCROLLER_WIDTH);
647 bPtr->scroller = sPtr;
649 WMMapWidget(sPtr);
653 void
654 WMSetBrowserAction(WMBrowser *bPtr, WMAction *action, void *clientData)
656 bPtr->action = action;
657 bPtr->clientData = clientData;
661 void
662 WMSetBrowserDoubleAction(WMBrowser *bPtr, WMAction *action, void *clientData)
664 bPtr->doubleAction = action;
665 bPtr->doubleClientData = clientData;
669 void
670 WMSetBrowserHasScroller(WMBrowser *bPtr, int hasScroller)
672 bPtr->flags.hasScroller = hasScroller;
677 char*
678 WMSetBrowserPath(WMBrowser *bPtr, char *path)
680 int i;
681 char *str = wstrdup(path);
682 char *tmp, *retPtr = NULL;
683 int item;
684 WMListItem *listItem;
686 /* WMLoadBrowserColumnZero must be call first */
687 if (!bPtr->flags.loaded) {
688 return False;
691 removeColumn(bPtr, 1);
693 WMSelectListItem(bPtr->columns[0], -1);
694 WMSetListPosition(bPtr->columns[0], 0);
696 i = 0;
697 tmp = strtok(str, bPtr->pathSeparator);
698 while (tmp) {
699 /* select it in the column */
700 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
701 if (item<0) {
702 retPtr = &path[(int)(tmp - str)];
703 break;
705 WMSelectListItem(bPtr->columns[i], item);
706 WMSetListPosition(bPtr->columns[i], item);
708 listItem = WMGetListItem(bPtr->columns[i], item);
709 if (!listItem || !listItem->isBranch) {
710 break;
713 /* load next column */
714 WMAddBrowserColumn(bPtr);
716 loadColumn(bPtr, i+1);
718 tmp = strtok(NULL, bPtr->pathSeparator);
720 i++;
722 free(str);
724 for (i = bPtr->usedColumnCount - 1;
725 (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
726 i--);
728 bPtr->selectedColumn = i;
730 if (bPtr->columnCount < bPtr->maxVisibleColumns) {
731 int i = bPtr->maxVisibleColumns - bPtr->columnCount;
732 int curUsedColumnCount = bPtr->usedColumnCount;
733 bPtr->usedColumnCount = bPtr->columnCount;
734 while (i--) {
735 WMAddBrowserColumn(bPtr);
737 bPtr->usedColumnCount = curUsedColumnCount;
740 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
742 return retPtr;
746 char*
747 WMGetBrowserPath(WMBrowser *bPtr)
749 return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
753 char*
754 WMGetBrowserPathToColumn(WMBrowser *bPtr, int column)
756 int i, size;
757 char *path;
758 WMListItem *item;
760 if (column >= bPtr->usedColumnCount)
761 column = bPtr->usedColumnCount-1;
763 if (column < 0) {
764 return wstrdup(bPtr->pathSeparator);
767 /* calculate size of buffer */
768 size = 0;
769 for (i = 0; i <= column; i++) {
770 item = WMGetListSelectedItem(bPtr->columns[i]);
771 if (!item)
772 break;
773 size += strlen(item->text);
776 /* get the path */
777 path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
778 /* ignore first / */
779 *path = 0;
780 for (i = 0; i <= column; i++) {
781 strcat(path, bPtr->pathSeparator);
782 item = WMGetListSelectedItem(bPtr->columns[i]);
783 if (!item)
784 break;
785 strcat(path, item->text);
788 return path;
792 static void
793 loadColumn(WMBrowser *bPtr, int column)
795 assert(bPtr->fillColumn);
797 bPtr->flags.loadingColumn = 1;
798 (*bPtr->fillColumn)(bPtr, column);
799 bPtr->flags.loadingColumn = 0;
803 static void
804 paintBrowser(WMBrowser *bPtr)
806 int i;
808 if (!bPtr->view->flags.mapped)
809 return;
811 W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
812 bPtr->view->size.height-SCROLLER_WIDTH-2,
813 bPtr->view->size.width, 22, WRSunken);
815 if (bPtr->flags.isTitled) {
816 for (i=0; i<bPtr->maxVisibleColumns; i++) {
817 drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
823 static void
824 handleEvents(XEvent *event, void *data)
826 WMBrowser *bPtr = (WMBrowser*)data;
828 CHECK_CLASS(data, WC_Browser);
831 switch (event->type) {
832 case Expose:
833 paintBrowser(bPtr);
834 break;
836 case DestroyNotify:
837 destroyBrowser(bPtr);
838 break;
845 static void
846 scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller)
848 int i;
849 int x;
850 int notify = 0;
853 if (column != bPtr->firstVisibleColumn)
854 notify = 1;
856 if (column < 0)
857 column = 0;
859 x = 0;
860 bPtr->firstVisibleColumn = column;
861 for (i = 0; i < bPtr->columnCount; i++) {
862 if (COLUMN_IS_VISIBLE(bPtr, i)) {
863 WMMoveWidget(bPtr->columns[i], x,
864 WMWidgetView(bPtr->columns[i])->pos.y);
865 if (!WMWidgetView(bPtr->columns[i])->flags.realized)
866 WMRealizeWidget(bPtr->columns[i]);
867 WMMapWidget(bPtr->columns[i]);
868 x += bPtr->columnSize.width + COLUMN_SPACING;
869 } else {
870 WMUnmapWidget(bPtr->columns[i]);
874 /* update the scroller */
875 if (updateScroller) {
876 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
877 float value, proportion;
879 value = bPtr->firstVisibleColumn
880 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
881 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
882 WMSetScrollerParameters(bPtr->scroller, value, proportion);
883 } else {
884 WMSetScrollerParameters(bPtr->scroller, 0, 1);
888 if (bPtr->view->flags.mapped)
889 paintBrowser(bPtr);
891 if (notify)
892 WMPostNotificationName(WMBrowserDidScrollNotification, bPtr, NULL);
896 static void
897 listCallback(void *self, void *clientData)
899 WMBrowser *bPtr = (WMBrowser*)clientData;
900 WMList *lPtr = (WMList*)self;
901 WMListItem *item;
902 static WMListItem *oldItem = NULL;
903 int i;
905 item = WMGetListSelectedItem(lPtr);
906 if (!item) {
907 oldItem = item;
908 return;
911 if (oldItem != item) {
912 for (i=0; i<bPtr->columnCount; i++) {
913 if (lPtr == bPtr->columns[i])
914 break;
916 assert(i<bPtr->columnCount);
918 bPtr->selectedColumn = i;
920 /* columns at right must be cleared */
921 removeColumn(bPtr, i+1);
922 /* open directory */
923 if (item->isBranch) {
924 WMAddBrowserColumn(bPtr);
926 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
927 i = 0;
928 else
929 i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
930 scrollToColumn(bPtr, i, True);
931 if (item->isBranch) {
932 loadColumn(bPtr, bPtr->usedColumnCount-1);
937 /* call callback for click */
938 if (bPtr->action)
939 (*bPtr->action)(bPtr, bPtr->clientData);
941 oldItem = item;
945 static void
946 listDoubleCallback(void *self, void *clientData)
948 WMBrowser *bPtr = (WMBrowser*)clientData;
949 WMList *lPtr = (WMList*)self;
950 WMListItem *item;
952 item = WMGetListSelectedItem(lPtr);
953 if (!item)
954 return;
956 /* call callback for double click */
957 if (bPtr->doubleAction)
958 (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
962 void
963 WMLoadBrowserColumnZero(WMBrowser *bPtr)
965 if (!bPtr->flags.loaded) {
966 /* create column 0 */
967 WMAddBrowserColumn(bPtr);
969 loadColumn(bPtr, 0);
971 /* make column 0 visible */
972 scrollToColumn(bPtr, 0, True);
974 bPtr->flags.loaded = 1;
979 void
980 WMRemoveBrowserItem(WMBrowser *bPtr, int column, int row)
982 WMList *list;
984 if (column < 0 || column >= bPtr->usedColumnCount)
985 return;
987 list = WMGetBrowserListInColumn(bPtr, column);
989 if (row < 0 || row >= WMGetListNumberOfRows(list))
990 return;
992 removeColumn(bPtr, column+1);
993 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
994 scrollToColumn(bPtr, 0, True);
995 else
996 scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
997 True);
999 WMRemoveListItem(list, row);
1003 static void
1004 listSelectionObserver(void *observerData, WMNotification *notification)
1006 WMBrowser *bPtr = (WMBrowser*)observerData;
1007 int column, item = (int)WMGetNotificationClientData(notification);
1008 WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
1010 for (column=0; column<bPtr->usedColumnCount; column++)
1011 if (bPtr->columns[column] == lPtr)
1012 break;
1014 /* this can happen when a list is being cleared with WMClearList
1015 * after the column was removed */
1016 if (column >= bPtr->usedColumnCount) {
1017 return;
1020 if (item < 0)
1021 column--;
1023 bPtr->selectedColumn = column;
1028 WMAddBrowserColumn(WMBrowser *bPtr)
1030 WMList *list;
1031 WMList **clist;
1032 char **tlist;
1033 int colY;
1034 int index;
1037 if (bPtr->usedColumnCount < bPtr->columnCount) {
1038 return bPtr->usedColumnCount++;
1041 bPtr->usedColumnCount++;
1043 if (bPtr->flags.isTitled) {
1044 colY = TITLE_SPACING + bPtr->titleHeight;
1045 } else {
1046 colY = 0;
1049 index = bPtr->columnCount;
1050 bPtr->columnCount++;
1051 clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
1052 tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
1053 memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
1054 memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
1055 if (bPtr->columns)
1056 free(bPtr->columns);
1057 if (bPtr->titles)
1058 free(bPtr->titles);
1059 bPtr->columns = clist;
1060 bPtr->titles = tlist;
1062 bPtr->titles[index] = NULL;
1064 list = WMCreateList(bPtr);
1065 WMSetListAction(list, listCallback, bPtr);
1066 WMSetListDoubleAction(list, listDoubleCallback, bPtr);
1067 WMSetListUserDrawProc(list, paintItem);
1068 WMAddNotificationObserver(listSelectionObserver, bPtr,
1069 WMListSelectionDidChangeNotification, list);
1071 bPtr->columns[index] = list;
1073 WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
1074 WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
1075 if (COLUMN_IS_VISIBLE(bPtr, index))
1076 WMMapWidget(list);
1078 /* update the scroller */
1079 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
1080 float value, proportion;
1082 value = bPtr->firstVisibleColumn
1083 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1084 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1085 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1088 return index;
1093 static void
1094 destroyBrowser(WMBrowser *bPtr)
1096 int i;
1098 for (i=0; i<bPtr->columnCount; i++) {
1099 if (bPtr->titles[i])
1100 free(bPtr->titles[i]);
1102 free(bPtr->titles);
1104 free(bPtr->pathSeparator);
1106 WMRemoveNotificationObserver(bPtr);
1108 free(bPtr);
1112 static char*
1113 createTruncatedString(WMFont *font, char *text, int *textLen, int width)
1115 int dLen = WMWidthOfString(font, ".", 1);
1116 char *textBuf = (char*)wmalloc((*textLen)+4);
1118 if (width >= 3*dLen) {
1119 int dddLen = 3*dLen;
1120 int tmpTextLen = *textLen;
1122 strcpy(textBuf, text);
1123 while (tmpTextLen
1124 && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
1125 tmpTextLen--;
1126 strcpy(textBuf+tmpTextLen, "...");
1127 *textLen = tmpTextLen+3;
1128 } else if (width >= 2*dLen) {
1129 strcpy(textBuf, "..");
1130 *textLen = 2;
1131 } else if (width >= dLen) {
1132 strcpy(textBuf, ".");
1133 *textLen = 1;
1134 } else {
1135 *textBuf = '\0';
1136 *textLen = 0;
1138 return textBuf;