started Appearance update in WPrefs
[wmaker-crm.git] / WINGs / wbrowser.c
blobe4279dc837497631f81c66a35a2c8a0efa285765
5 #include "WINGsP.h"
6 #include <math.h> /* for : double rint (double) */
10 typedef struct W_Browser {
11 W_Class widgetClass;
12 W_View *view;
14 char **titles;
15 WMList **columns;
17 short columnCount;
18 short usedColumnCount; /* columns actually being used */
19 short minColumnWidth;
21 short maxVisibleColumns;
22 short firstVisibleColumn;
24 short titleHeight;
26 short selectedColumn;
28 WMSize columnSize;
31 void *clientData;
32 WMAction *action;
33 void *doubleClientData;
34 WMAction *doubleAction;
36 WMBrowserDelegate *delegate;
38 WMScroller *scroller;
40 char *pathSeparator;
42 struct {
43 unsigned int isTitled:1;
44 unsigned int allowMultipleSelection:1;
45 unsigned int hasScroller:1;
47 /* */
48 unsigned int loaded:1;
49 unsigned int loadingColumn:1;
50 } flags;
51 } Browser;
54 #define COLUMN_SPACING 4
55 #define TITLE_SPACING 2
57 #define DEFAULT_WIDTH 305
58 #define DEFAULT_HEIGHT 200
59 #define DEFAULT_HAS_SCROLLER True
60 #define DEFAULT_TITLE_HEIGHT 20
61 #define DEFAULT_IS_TITLED True
62 #define DEFAULT_MAX_VISIBLE_COLUMNS 2
63 #define DEFAULT_SEPARATOR "/"
65 #define MIN_VISIBLE_COLUMNS 1
66 #define MAX_VISIBLE_COLUMNS 32
69 #define COLUMN_IS_VISIBLE(b, c) ((c) >= (b)->firstVisibleColumn \
70 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
73 static void handleEvents(XEvent *event, void *data);
74 static void destroyBrowser(WMBrowser *bPtr);
76 static void setupScroller(WMBrowser *bPtr);
78 static void scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller);
80 static void paintItem(WMList *lPtr, int index, Drawable d, char *text,
81 int state, WMRect *rect);
83 static void loadColumn(WMBrowser *bPtr, int column);
85 static void removeColumn(WMBrowser *bPtr, int column);
87 static char*
88 createTruncatedString(WMFont *font, char *text, int *textLen, int width);
90 static void resizeBrowser(WMWidget*, unsigned int, unsigned int);
92 W_ViewProcedureTable _BrowserViewProcedures = {
93 NULL,
94 resizeBrowser,
95 NULL
100 WMBrowser*
101 WMCreateBrowser(WMWidget *parent)
103 WMBrowser *bPtr;
104 int i;
106 wassertrv(parent, NULL);
108 bPtr = wmalloc(sizeof(WMBrowser));
109 memset(bPtr, 0, sizeof(WMBrowser));
111 bPtr->widgetClass = WC_Browser;
113 bPtr->view = W_CreateView(W_VIEW(parent));
114 if (!bPtr->view) {
115 free(bPtr);
116 return NULL;
118 bPtr->view->self = bPtr;
120 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask
121 |ClientMessageMask, handleEvents, bPtr);
123 /* default configuration */
124 bPtr->flags.hasScroller = DEFAULT_HAS_SCROLLER;
126 bPtr->titleHeight = DEFAULT_TITLE_HEIGHT;
127 bPtr->flags.isTitled = DEFAULT_IS_TITLED;
128 bPtr->maxVisibleColumns = DEFAULT_MAX_VISIBLE_COLUMNS;
130 resizeBrowser(bPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
132 bPtr->pathSeparator = wstrdup(DEFAULT_SEPARATOR);
134 if (bPtr->flags.hasScroller)
135 setupScroller(bPtr);
137 for (i=0; i<bPtr->maxVisibleColumns; i++) {
138 WMAddBrowserColumn(bPtr);
140 bPtr->usedColumnCount = 0;
142 bPtr->selectedColumn = -1;
144 return bPtr;
149 WMGetBrowserMaxVisibleColumns(WMBrowser *bPtr)
151 return bPtr->maxVisibleColumns;
155 void
156 WMSetBrowserMaxVisibleColumns(WMBrowser *bPtr, int columns)
158 int curMaxVisibleColumns;
159 int newFirstVisibleColumn = 0;
161 assert ((int) bPtr);
163 columns = (columns < MIN_VISIBLE_COLUMNS) ? MIN_VISIBLE_COLUMNS : columns;
164 columns = (columns > MAX_VISIBLE_COLUMNS) ? MAX_VISIBLE_COLUMNS : columns;
165 if (columns == bPtr->maxVisibleColumns) {
166 return;
168 curMaxVisibleColumns = bPtr->maxVisibleColumns;
169 bPtr->maxVisibleColumns = columns;
170 /* browser not loaded */
171 if (!bPtr->flags.loaded) {
172 if ((columns > curMaxVisibleColumns) && (columns > bPtr->columnCount)) {
173 int i = columns - bPtr->columnCount;
174 bPtr->usedColumnCount = bPtr->columnCount;
175 while (i--) {
176 WMAddBrowserColumn(bPtr);
178 bPtr->usedColumnCount = 0;
180 /* browser loaded and columns > curMaxVisibleColumns */
181 } else if (columns > curMaxVisibleColumns) {
182 if (bPtr->usedColumnCount > columns) {
183 newFirstVisibleColumn = bPtr->usedColumnCount - columns;
185 if (newFirstVisibleColumn > bPtr->firstVisibleColumn) {
186 newFirstVisibleColumn = bPtr->firstVisibleColumn;
188 if (columns > bPtr->columnCount) {
189 int i = columns - bPtr->columnCount;
190 int curUsedColumnCount = bPtr->usedColumnCount;
191 bPtr->usedColumnCount = bPtr->columnCount;
192 while (i--) {
193 WMAddBrowserColumn(bPtr);
195 bPtr->usedColumnCount = curUsedColumnCount;
197 /* browser loaded and columns < curMaxVisibleColumns */
198 } else {
199 newFirstVisibleColumn = bPtr->firstVisibleColumn;
200 if (newFirstVisibleColumn + columns >= bPtr->usedColumnCount) {
201 removeColumn(bPtr, newFirstVisibleColumn + columns);
204 resizeBrowser(bPtr, bPtr->view->size.width, bPtr->view->size.height);
205 if (bPtr->flags.loaded) {
206 XClearArea(bPtr->view->screen->display, bPtr->view->window, 0, 0,
207 bPtr->view->size.width, bPtr->titleHeight, False);
208 scrollToColumn (bPtr, newFirstVisibleColumn, True);
213 int
214 WMGetBrowserNumberOfColumns(WMBrowser *bPtr)
216 return bPtr->usedColumnCount;
219 void
220 WMSetBrowserPathSeparator(WMBrowser *bPtr, char *separator)
222 if (bPtr->pathSeparator)
223 free(bPtr->pathSeparator);
224 bPtr->pathSeparator = wstrdup(separator);
229 static void
230 drawTitleOfColumn(WMBrowser *bPtr, int column)
232 WMScreen *scr = bPtr->view->screen;
233 int x;
235 x=(column-bPtr->firstVisibleColumn)*(bPtr->columnSize.width+COLUMN_SPACING);
237 XFillRectangle(scr->display, bPtr->view->window, WMColorGC(scr->darkGray), x, 0,
238 bPtr->columnSize.width, bPtr->titleHeight);
239 W_DrawRelief(scr, bPtr->view->window, x, 0,
240 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, WMColorGC(scr->white),
254 False, titleBuf, titleLen);
255 free (titleBuf);
256 } else {
257 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
258 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
259 bPtr->columnSize.width, WACenter, WMColorGC(scr->white),
260 False, bPtr->titles[column], titleLen);
266 WMList*
267 WMGetBrowserListInColumn(WMBrowser *bPtr, int column)
269 if (column < 0 || column >= bPtr->usedColumnCount)
270 return NULL;
272 return bPtr->columns[column];
276 void
277 WMSetBrowserDelegate(WMBrowser *bPtr, WMBrowserDelegate *delegate)
279 bPtr->delegate = delegate;
283 int
284 WMGetBrowserFirstVisibleColumn(WMBrowser *bPtr)
286 return bPtr->firstVisibleColumn;
290 static void
291 removeColumn(WMBrowser *bPtr, int column)
293 int i, clearEnd, destroyEnd;
294 WMList **clist;
295 char **tlist;
297 assert ((int) bPtr);
299 column = (column < 0) ? 0 : column;
300 if (column >= bPtr->columnCount) {
301 return;
303 if (column < bPtr->maxVisibleColumns) {
304 clearEnd = bPtr->maxVisibleColumns;
305 destroyEnd = bPtr->columnCount;
306 bPtr->columnCount = bPtr->maxVisibleColumns;
307 } else {
308 clearEnd = column;
309 destroyEnd = bPtr->columnCount;
310 bPtr->columnCount = column;
312 if (column < bPtr->usedColumnCount) {
313 bPtr->usedColumnCount = column;
315 for (i=column; i < clearEnd; i++) {
316 if (bPtr->titles[i]) {
317 free(bPtr->titles[i]);
318 bPtr->titles[i] = NULL;
320 WMClearList(bPtr->columns[i]);
322 for (;i < destroyEnd; i++) {
323 if (bPtr->titles[i]) {
324 free(bPtr->titles[i]);
325 bPtr->titles[i] = NULL;
327 WMRemoveNotificationObserverWithName(bPtr,
328 WMListSelectionDidChangeNotification,
329 bPtr->columns[i]);
330 WMDestroyWidget(bPtr->columns[i]);
331 bPtr->columns[i] = NULL;
333 clist = wmalloc(sizeof(WMList*) * (bPtr->columnCount));
334 tlist = wmalloc(sizeof(char*) * (bPtr->columnCount));
335 memcpy(clist, bPtr->columns, sizeof(WMList*) * (bPtr->columnCount));
336 memcpy(tlist, bPtr->titles, sizeof(char*) * (bPtr->columnCount));
337 free(bPtr->titles);
338 free(bPtr->columns);
339 bPtr->titles = tlist;
340 bPtr->columns = clist;
344 WMListItem*
345 WMGetBrowserSelectedItemInColumn(WMBrowser *bPtr, int column)
347 if ((column < 0) || (column >= bPtr->usedColumnCount))
348 return NULL;
350 return WMGetListSelectedItem(bPtr->columns[column]);
356 WMGetBrowserSelectedColumn(WMBrowser *bPtr)
358 return bPtr->selectedColumn;
363 WMGetBrowserSelectedRowInColumn(WMBrowser *bPtr, int column)
365 if (column >= 0 && column < bPtr->columnCount) {
366 return WMGetListSelectedItemRow(bPtr->columns[column]);
367 } else {
368 return -1;
373 void
374 WMSetBrowserColumnTitle(WMBrowser *bPtr, int column, char *title)
376 assert(column >= 0);
377 assert(column < bPtr->usedColumnCount);
379 if (bPtr->titles[column])
380 free(bPtr->titles[column]);
382 bPtr->titles[column] = wstrdup(title);
384 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
385 drawTitleOfColumn(bPtr, column);
390 void
391 WMSetBrowserTitled(WMBrowser *bPtr, Bool flag)
393 int i;
394 int columnX, columnY;
396 if (bPtr->flags.isTitled == flag)
397 return;
399 columnX = 0;
401 if (!bPtr->flags.isTitled) {
402 columnY = TITLE_SPACING + bPtr->titleHeight;
404 bPtr->columnSize.height -= columnY;
406 for (i=0; i<bPtr->columnCount; i++) {
407 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
408 bPtr->columnSize.height);
410 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
412 WMMoveWidget(bPtr->columns[i], columnX, columnY);
414 } else {
415 bPtr->columnSize.height += TITLE_SPACING + bPtr->titleHeight;
417 for (i=0; i<bPtr->columnCount; i++) {
418 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
419 bPtr->columnSize.height);
421 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
423 WMMoveWidget(bPtr->columns[i], columnX, 0);
427 bPtr->flags.isTitled = flag;
431 WMListItem*
432 WMAddSortedBrowserItem(WMBrowser *bPtr, int column, char *text, Bool isBranch)
434 WMListItem *item;
436 if (column < 0 || column >= bPtr->columnCount)
437 return NULL;
439 item = WMAddSortedListItem(bPtr->columns[column], text);
440 item->isBranch = isBranch;
442 return item;
447 WMListItem*
448 WMInsertBrowserItem(WMBrowser *bPtr, int column, int row, char *text,
449 Bool isBranch)
451 WMListItem *item;
453 if (column < 0 || column >= bPtr->columnCount)
454 return NULL;
456 item = WMInsertListItem(bPtr->columns[column], row, text);
457 item->isBranch = isBranch;
459 return item;
465 static void
466 resizeBrowser(WMWidget *w, unsigned int width, unsigned int height)
468 WMBrowser *bPtr = (WMBrowser*)w;
469 int cols = bPtr->maxVisibleColumns;
470 int colX, colY;
471 int i;
473 assert(width > 0);
474 assert(height > 0);
476 bPtr->columnSize.width = (width-(cols-1)*COLUMN_SPACING) / cols;
477 bPtr->columnSize.height = height;
479 if (bPtr->flags.isTitled) {
480 colY = TITLE_SPACING + bPtr->titleHeight;
481 bPtr->columnSize.height -= colY;
482 } else {
483 colY = 0;
486 if (bPtr->flags.hasScroller) {
487 bPtr->columnSize.height -= SCROLLER_WIDTH + 4;
489 if (bPtr->scroller) {
490 WMResizeWidget(bPtr->scroller, width-2, 1);
491 WMMoveWidget(bPtr->scroller, 1, height-SCROLLER_WIDTH-1);
495 colX = 0;
496 for (i = 0; i < bPtr->columnCount; i++) {
497 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
498 bPtr->columnSize.height);
500 WMMoveWidget(bPtr->columns[i], colX, colY);
502 if (COLUMN_IS_VISIBLE(bPtr, i)) {
503 colX += bPtr->columnSize.width+COLUMN_SPACING;
507 W_ResizeView(bPtr->view, width, height);
511 static void
512 paintItem(WMList *lPtr, int index, Drawable d, char *text, int state,
513 WMRect *rect)
515 WMView *view = W_VIEW(lPtr);
516 W_Screen *scr = view->screen;
517 int width, height, x, y;
519 width = rect->size.width;
520 height = rect->size.height;
521 x = rect->pos.x;
522 y = rect->pos.y;
524 if (state & WLDSSelected)
525 XFillRectangle(scr->display, d, WMColorGC(scr->white), x, y,
526 width, height);
527 else
528 XClearArea(scr->display, d, x, y, width, height, False);
530 if (text) {
531 /* Avoid overlaping... */
532 int textLen = strlen(text);
533 int widthC = (state & WLDSIsBranch) ? width-20 : width-8;
534 if (WMWidthOfString(scr->normalFont, text, textLen) > widthC) {
535 char *textBuf = createTruncatedString(scr->normalFont,
536 text, &textLen, widthC);
537 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
538 WALeft, WMColorGC(scr->black), False, textBuf, textLen);
539 free(textBuf);
540 } else {
541 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
542 WALeft, WMColorGC(scr->black), False, text, textLen);
546 if (state & WLDSIsBranch) {
547 XDrawLine(scr->display, d, WMColorGC(scr->darkGray), x+width-11, y+3,
548 x+width-6, y+height/2);
549 if (state & WLDSSelected)
550 XDrawLine(scr->display, d,WMColorGC(scr->gray), x+width-11, y+height-5,
551 x+width-6, y+height/2);
552 else
553 XDrawLine(scr->display, d,WMColorGC(scr->white), x+width-11, y+height-5,
554 x+width-6, y+height/2);
555 XDrawLine(scr->display, d, WMColorGC(scr->black), x+width-12, y+3,
556 x+width-12, y+height-5);
561 static void
562 scrollCallback(WMWidget *scroller, void *self)
564 WMBrowser *bPtr = (WMBrowser*)self;
565 WMScroller *sPtr = (WMScroller*)scroller;
566 int newFirst;
567 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
569 switch (WMGetScrollerHitPart(sPtr)) {
570 case WSDecrementLine:
571 if (bPtr->firstVisibleColumn > 0) {
572 scrollToColumn(bPtr, bPtr->firstVisibleColumn-1, True);
574 break;
576 case WSDecrementPage:
577 if (bPtr->firstVisibleColumn > 0) {
578 newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
580 scrollToColumn(bPtr, newFirst, True);
582 break;
585 case WSIncrementLine:
586 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
587 scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
589 break;
591 case WSIncrementPage:
592 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
593 newFirst = bPtr->firstVisibleColumn + bPtr->maxVisibleColumns;
595 if (newFirst+bPtr->maxVisibleColumns >= bPtr->columnCount)
596 newFirst = bPtr->columnCount - bPtr->maxVisibleColumns;
598 scrollToColumn(bPtr, newFirst, True);
600 break;
602 case WSKnob:
604 double floatValue;
605 double value = bPtr->columnCount - bPtr->maxVisibleColumns;
607 floatValue = WMGetScrollerValue(bPtr->scroller);
609 floatValue = (floatValue*value)/value;
611 newFirst = rint(floatValue*(float)(bPtr->columnCount - bPtr->maxVisibleColumns));
613 if (bPtr->firstVisibleColumn != newFirst)
614 scrollToColumn(bPtr, newFirst, False);
615 /* else
616 WMSetScrollerParameters(bPtr->scroller, floatValue,
617 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
621 break;
623 case WSKnobSlot:
624 case WSNoPart:
625 /* do nothing */
626 break;
628 #undef LAST_VISIBLE_COLUMN
632 static void
633 setupScroller(WMBrowser *bPtr)
635 WMScroller *sPtr;
636 int y;
638 y = bPtr->view->size.height - SCROLLER_WIDTH - 1;
640 sPtr = WMCreateScroller(bPtr);
641 WMSetScrollerAction(sPtr, scrollCallback, bPtr);
642 WMMoveWidget(sPtr, 1, y);
643 WMResizeWidget(sPtr, bPtr->view->size.width-2, SCROLLER_WIDTH);
645 bPtr->scroller = sPtr;
647 WMMapWidget(sPtr);
651 void
652 WMSetBrowserAction(WMBrowser *bPtr, WMAction *action, void *clientData)
654 bPtr->action = action;
655 bPtr->clientData = clientData;
659 void
660 WMSetBrowserDoubleAction(WMBrowser *bPtr, WMAction *action, void *clientData)
662 bPtr->doubleAction = action;
663 bPtr->doubleClientData = clientData;
667 void
668 WMSetBrowserHasScroller(WMBrowser *bPtr, int hasScroller)
670 bPtr->flags.hasScroller = hasScroller;
675 char*
676 WMSetBrowserPath(WMBrowser *bPtr, char *path)
678 int i;
679 char *str = wstrdup(path);
680 char *tmp, *retPtr = NULL;
681 int item;
682 WMListItem *listItem;
684 /* WMLoadBrowserColumnZero must be call first */
685 if (!bPtr->flags.loaded) {
686 return False;
689 removeColumn(bPtr, 1);
691 WMSelectListItem(bPtr->columns[0], -1);
692 WMSetListPosition(bPtr->columns[0], 0);
694 i = 0;
695 tmp = strtok(str, bPtr->pathSeparator);
696 while (tmp) {
697 /* select it in the column */
698 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
699 if (item<0) {
700 retPtr = &path[(int)(tmp - str)];
701 break;
703 WMSelectListItem(bPtr->columns[i], item);
704 WMSetListPosition(bPtr->columns[i], item);
706 listItem = WMGetListItem(bPtr->columns[i], item);
707 if (!listItem || !listItem->isBranch) {
708 break;
711 /* load next column */
712 WMAddBrowserColumn(bPtr);
714 loadColumn(bPtr, i+1);
716 tmp = strtok(NULL, bPtr->pathSeparator);
718 i++;
720 free(str);
722 for (i = bPtr->usedColumnCount - 1;
723 (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
724 i--);
726 bPtr->selectedColumn = i;
728 if (bPtr->columnCount < bPtr->maxVisibleColumns) {
729 int i = bPtr->maxVisibleColumns - bPtr->columnCount;
730 int curUsedColumnCount = bPtr->usedColumnCount;
731 bPtr->usedColumnCount = bPtr->columnCount;
732 while (i--) {
733 WMAddBrowserColumn(bPtr);
735 bPtr->usedColumnCount = curUsedColumnCount;
738 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
740 return retPtr;
744 char*
745 WMGetBrowserPath(WMBrowser *bPtr)
747 return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
752 char*
753 WMGetBrowserPathToColumn(WMBrowser *bPtr, int column)
755 int i, size;
756 char *path;
757 WMListItem *item;
759 if (column >= bPtr->usedColumnCount)
760 column = bPtr->usedColumnCount-1;
762 if (column < 0) {
763 return wstrdup(bPtr->pathSeparator);
766 /* calculate size of buffer */
767 size = 0;
768 for (i = 0; i <= column; i++) {
769 item = WMGetListSelectedItem(bPtr->columns[i]);
770 if (!item)
771 break;
772 size += strlen(item->text);
775 /* get the path */
776 path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
777 /* ignore first / */
778 *path = 0;
779 for (i = 0; i <= column; i++) {
780 strcat(path, bPtr->pathSeparator);
781 item = WMGetListSelectedItem(bPtr->columns[i]);
782 if (!item)
783 break;
784 strcat(path, item->text);
787 return path;
791 static void
792 loadColumn(WMBrowser *bPtr, int column)
794 assert(bPtr->delegate);
795 assert(bPtr->delegate->createRowsForColumn);
797 bPtr->flags.loadingColumn = 1;
798 (*bPtr->delegate->createRowsForColumn)(bPtr->delegate, bPtr, column,
799 bPtr->columns[column]);
800 bPtr->flags.loadingColumn = 0;
802 if (bPtr->delegate->titleOfColumn) {
803 char *title;
805 title = (*bPtr->delegate->titleOfColumn)(bPtr->delegate, bPtr, column);
807 if (bPtr->titles[column])
808 free(bPtr->titles[column]);
810 bPtr->titles[column] = title;
815 static void
816 paintBrowser(WMBrowser *bPtr)
818 int i;
820 if (!bPtr->view->flags.mapped)
821 return;
823 W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
824 bPtr->view->size.height-SCROLLER_WIDTH-2,
825 bPtr->view->size.width, 22, WRSunken);
827 if (bPtr->flags.isTitled) {
828 for (i=0; i<bPtr->maxVisibleColumns; i++) {
829 drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
835 static void
836 handleEvents(XEvent *event, void *data)
838 WMBrowser *bPtr = (WMBrowser*)data;
840 CHECK_CLASS(data, WC_Browser);
843 switch (event->type) {
844 case Expose:
845 paintBrowser(bPtr);
846 break;
848 case DestroyNotify:
849 destroyBrowser(bPtr);
850 break;
857 static void
858 scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller)
860 int i;
861 int x;
862 int notify = 0;
865 if (column != bPtr->firstVisibleColumn) {
866 notify = 1;
869 if (column < 0)
870 column = 0;
872 if (notify && bPtr->delegate && bPtr->delegate->willScroll) {
873 (*bPtr->delegate->willScroll)(bPtr->delegate, bPtr);
876 x = 0;
877 bPtr->firstVisibleColumn = column;
878 for (i = 0; i < bPtr->columnCount; i++) {
879 if (COLUMN_IS_VISIBLE(bPtr, i)) {
880 WMMoveWidget(bPtr->columns[i], x,
881 WMWidgetView(bPtr->columns[i])->pos.y);
882 if (!WMWidgetView(bPtr->columns[i])->flags.realized)
883 WMRealizeWidget(bPtr->columns[i]);
884 WMMapWidget(bPtr->columns[i]);
885 x += bPtr->columnSize.width + COLUMN_SPACING;
886 } else {
887 WMUnmapWidget(bPtr->columns[i]);
891 /* update the scroller */
892 if (updateScroller) {
893 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
894 float value, proportion;
896 value = bPtr->firstVisibleColumn
897 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
898 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
899 WMSetScrollerParameters(bPtr->scroller, value, proportion);
900 } else {
901 WMSetScrollerParameters(bPtr->scroller, 0, 1);
905 if (bPtr->view->flags.mapped)
906 paintBrowser(bPtr);
908 if (notify && bPtr->delegate && bPtr->delegate->didScroll) {
909 (*bPtr->delegate->didScroll)(bPtr->delegate, bPtr);
914 static void
915 listCallback(void *self, void *clientData)
917 WMBrowser *bPtr = (WMBrowser*)clientData;
918 WMList *lPtr = (WMList*)self;
919 WMListItem *item;
920 static WMListItem *oldItem = NULL;
921 int i;
923 item = WMGetListSelectedItem(lPtr);
924 if (!item) {
925 oldItem = item;
926 return;
929 if (oldItem != item) {
930 for (i=0; i<bPtr->columnCount; i++) {
931 if (lPtr == bPtr->columns[i])
932 break;
934 assert(i<bPtr->columnCount);
936 bPtr->selectedColumn = i;
938 /* columns at right must be cleared */
939 removeColumn(bPtr, i+1);
940 /* open directory */
941 if (item->isBranch) {
942 WMAddBrowserColumn(bPtr);
944 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
945 i = 0;
946 else
947 i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
948 scrollToColumn(bPtr, i, True);
949 if (item->isBranch) {
950 loadColumn(bPtr, bPtr->usedColumnCount-1);
955 /* call callback for click */
956 if (bPtr->action)
957 (*bPtr->action)(bPtr, bPtr->clientData);
959 oldItem = item;
963 static void
964 listDoubleCallback(void *self, void *clientData)
966 WMBrowser *bPtr = (WMBrowser*)clientData;
967 WMList *lPtr = (WMList*)self;
968 WMListItem *item;
970 item = WMGetListSelectedItem(lPtr);
971 if (!item)
972 return;
974 /* call callback for double click */
975 if (bPtr->doubleAction)
976 (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
980 void
981 WMLoadBrowserColumnZero(WMBrowser *bPtr)
983 if (!bPtr->flags.loaded) {
984 /* create column 0 */
985 WMAddBrowserColumn(bPtr);
987 loadColumn(bPtr, 0);
989 /* make column 0 visible */
990 scrollToColumn(bPtr, 0, True);
992 bPtr->flags.loaded = 1;
997 void
998 WMRemoveBrowserItem(WMBrowser *bPtr, int column, int row)
1000 WMList *list;
1002 if (column < 0 || column >= bPtr->usedColumnCount)
1003 return;
1005 list = WMGetBrowserListInColumn(bPtr, column);
1007 if (row < 0 || row >= WMGetListNumberOfRows(list))
1008 return;
1010 removeColumn(bPtr, column+1);
1011 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
1012 scrollToColumn(bPtr, 0, True);
1013 else
1014 scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
1015 True);
1017 WMRemoveListItem(list, row);
1021 static void
1022 listSelectionObserver(void *observerData, WMNotification *notification)
1024 WMBrowser *bPtr = (WMBrowser*)observerData;
1025 int column, item = (int)WMGetNotificationClientData(notification);
1026 WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
1028 for (column = 0; column < bPtr->usedColumnCount; column++)
1029 if (bPtr->columns[column] == lPtr)
1030 break;
1032 /* this can happen when a list is being cleared with WMClearList
1033 * after the column was removed */
1034 if (column >= bPtr->usedColumnCount) {
1035 return;
1038 if (item < 0)
1039 column--;
1041 bPtr->selectedColumn = column;
1046 WMAddBrowserColumn(WMBrowser *bPtr)
1048 WMList *list;
1049 WMList **clist;
1050 char **tlist;
1051 int colY;
1052 int index;
1055 if (bPtr->usedColumnCount < bPtr->columnCount) {
1056 return bPtr->usedColumnCount++;
1059 bPtr->usedColumnCount++;
1061 if (bPtr->flags.isTitled) {
1062 colY = TITLE_SPACING + bPtr->titleHeight;
1063 } else {
1064 colY = 0;
1067 index = bPtr->columnCount;
1068 bPtr->columnCount++;
1069 clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
1070 tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
1071 memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
1072 memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
1073 if (bPtr->columns)
1074 free(bPtr->columns);
1075 if (bPtr->titles)
1076 free(bPtr->titles);
1077 bPtr->columns = clist;
1078 bPtr->titles = tlist;
1080 bPtr->titles[index] = NULL;
1082 list = WMCreateList(bPtr);
1083 WMSetListAction(list, listCallback, bPtr);
1084 WMSetListDoubleAction(list, listDoubleCallback, bPtr);
1085 WMSetListUserDrawProc(list, paintItem);
1086 WMAddNotificationObserver(listSelectionObserver, bPtr,
1087 WMListSelectionDidChangeNotification, list);
1089 bPtr->columns[index] = list;
1091 WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
1092 WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
1093 if (COLUMN_IS_VISIBLE(bPtr, index))
1094 WMMapWidget(list);
1096 /* update the scroller */
1097 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
1098 float value, proportion;
1100 value = bPtr->firstVisibleColumn
1101 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1102 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1103 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1106 return index;
1111 static void
1112 destroyBrowser(WMBrowser *bPtr)
1114 int i;
1116 for (i = 0; i < bPtr->columnCount; i++) {
1117 if (bPtr->titles[i])
1118 free(bPtr->titles[i]);
1120 free(bPtr->titles);
1122 free(bPtr->pathSeparator);
1124 WMRemoveNotificationObserver(bPtr);
1126 free(bPtr);
1130 static char*
1131 createTruncatedString(WMFont *font, char *text, int *textLen, int width)
1133 int dLen = WMWidthOfString(font, ".", 1);
1134 char *textBuf = (char*)wmalloc((*textLen)+4);
1136 if (width >= 3*dLen) {
1137 int dddLen = 3*dLen;
1138 int tmpTextLen = *textLen;
1140 strcpy(textBuf, text);
1141 while (tmpTextLen
1142 && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
1143 tmpTextLen--;
1144 strcpy(textBuf+tmpTextLen, "...");
1145 *textLen = tmpTextLen+3;
1146 } else if (width >= 2*dLen) {
1147 strcpy(textBuf, "..");
1148 *textLen = 2;
1149 } else if (width >= dLen) {
1150 strcpy(textBuf, ".");
1151 *textLen = 1;
1152 } else {
1153 *textBuf = '\0';
1154 *textLen = 0;
1156 return textBuf;