*** empty log message ***
[wmaker-crm.git] / WINGs / wbrowser.c
blob97164fd89c86bdfe43d4f13835e6111564dcdd30
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 i = 0;
694 tmp = strtok(str, bPtr->pathSeparator);
695 while (tmp) {
696 /* select it in the column */
697 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
698 if (item<0) {
699 retPtr = &path[(int)(tmp - str)];
700 break;
702 WMSelectListItem(bPtr->columns[i], item);
703 WMSetListPosition(bPtr->columns[i], item);
705 listItem = WMGetListItem(bPtr->columns[i], item);
706 if (!listItem || !listItem->isBranch) {
707 break;
710 /* load next column */
711 WMAddBrowserColumn(bPtr);
713 loadColumn(bPtr, i+1);
715 tmp = strtok(NULL, bPtr->pathSeparator);
717 i++;
719 free(str);
721 for (i = bPtr->usedColumnCount - 1;
722 (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
723 i--);
725 bPtr->selectedColumn = i;
727 if (bPtr->columnCount < bPtr->maxVisibleColumns) {
728 int i = bPtr->maxVisibleColumns - bPtr->columnCount;
729 int curUsedColumnCount = bPtr->usedColumnCount;
730 bPtr->usedColumnCount = bPtr->columnCount;
731 while (i--) {
732 WMAddBrowserColumn(bPtr);
734 bPtr->usedColumnCount = curUsedColumnCount;
737 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
739 return retPtr;
743 char*
744 WMGetBrowserPath(WMBrowser *bPtr)
746 return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
750 char*
751 WMGetBrowserPathToColumn(WMBrowser *bPtr, int column)
753 int i, size;
754 char *path;
755 WMListItem *item;
757 if (column >= bPtr->usedColumnCount)
758 column = bPtr->usedColumnCount-1;
760 if (column < 0) {
761 return wstrdup(bPtr->pathSeparator);
764 /* calculate size of buffer */
765 size = 0;
766 for (i = 0; i <= column; i++) {
767 item = WMGetListSelectedItem(bPtr->columns[i]);
768 if (!item)
769 break;
770 size += strlen(item->text);
773 /* get the path */
774 path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
775 /* ignore first / */
776 *path = 0;
777 for (i = 0; i <= column; i++) {
778 strcat(path, bPtr->pathSeparator);
779 item = WMGetListSelectedItem(bPtr->columns[i]);
780 if (!item)
781 break;
782 strcat(path, item->text);
785 return path;
789 static void
790 loadColumn(WMBrowser *bPtr, int column)
792 assert(bPtr->fillColumn);
794 bPtr->flags.loadingColumn = 1;
795 (*bPtr->fillColumn)(bPtr, column);
796 bPtr->flags.loadingColumn = 0;
800 static void
801 paintBrowser(WMBrowser *bPtr)
803 int i;
805 if (!bPtr->view->flags.mapped)
806 return;
808 W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
809 bPtr->view->size.height-SCROLLER_WIDTH-2,
810 bPtr->view->size.width, 22, WRSunken);
812 if (bPtr->flags.isTitled) {
813 for (i=0; i<bPtr->maxVisibleColumns; i++) {
814 drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
820 static void
821 handleEvents(XEvent *event, void *data)
823 WMBrowser *bPtr = (WMBrowser*)data;
825 CHECK_CLASS(data, WC_Browser);
828 switch (event->type) {
829 case Expose:
830 paintBrowser(bPtr);
831 break;
833 case DestroyNotify:
834 destroyBrowser(bPtr);
835 break;
842 static void
843 scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller)
845 int i;
846 int x;
847 int notify = 0;
850 if (column != bPtr->firstVisibleColumn)
851 notify = 1;
853 if (column < 0)
854 column = 0;
856 x = 0;
857 bPtr->firstVisibleColumn = column;
858 for (i = 0; i < bPtr->columnCount; i++) {
859 if (COLUMN_IS_VISIBLE(bPtr, i)) {
860 WMMoveWidget(bPtr->columns[i], x,
861 WMWidgetView(bPtr->columns[i])->pos.y);
862 if (!WMWidgetView(bPtr->columns[i])->flags.realized)
863 WMRealizeWidget(bPtr->columns[i]);
864 WMMapWidget(bPtr->columns[i]);
865 x += bPtr->columnSize.width + COLUMN_SPACING;
866 } else {
867 WMUnmapWidget(bPtr->columns[i]);
871 /* update the scroller */
872 if (updateScroller) {
873 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
874 float value, proportion;
876 value = bPtr->firstVisibleColumn
877 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
878 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
879 WMSetScrollerParameters(bPtr->scroller, value, proportion);
880 } else {
881 WMSetScrollerParameters(bPtr->scroller, 0, 1);
885 if (bPtr->view->flags.mapped)
886 paintBrowser(bPtr);
888 if (notify)
889 WMPostNotificationName(WMBrowserDidScrollNotification, bPtr, NULL);
893 static void
894 listCallback(void *self, void *clientData)
896 WMBrowser *bPtr = (WMBrowser*)clientData;
897 WMList *lPtr = (WMList*)self;
898 WMListItem *item;
899 static WMListItem *oldItem = NULL;
900 int i;
902 item = WMGetListSelectedItem(lPtr);
903 if (!item) {
904 oldItem = item;
905 return;
908 if (oldItem != item) {
909 for (i=0; i<bPtr->columnCount; i++) {
910 if (lPtr == bPtr->columns[i])
911 break;
913 assert(i<bPtr->columnCount);
915 bPtr->selectedColumn = i;
917 /* columns at right must be cleared */
918 removeColumn(bPtr, i+1);
919 /* open directory */
920 if (item->isBranch) {
921 WMAddBrowserColumn(bPtr);
923 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
924 i = 0;
925 else
926 i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
927 scrollToColumn(bPtr, i, True);
928 if (item->isBranch) {
929 loadColumn(bPtr, bPtr->usedColumnCount-1);
934 /* call callback for click */
935 if (bPtr->action)
936 (*bPtr->action)(bPtr, bPtr->clientData);
938 oldItem = item;
942 static void
943 listDoubleCallback(void *self, void *clientData)
945 WMBrowser *bPtr = (WMBrowser*)clientData;
946 WMList *lPtr = (WMList*)self;
947 WMListItem *item;
949 item = WMGetListSelectedItem(lPtr);
950 if (!item)
951 return;
953 /* call callback for double click */
954 if (bPtr->doubleAction)
955 (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
959 void
960 WMLoadBrowserColumnZero(WMBrowser *bPtr)
962 if (!bPtr->flags.loaded) {
963 /* create column 0 */
964 WMAddBrowserColumn(bPtr);
966 loadColumn(bPtr, 0);
968 /* make column 0 visible */
969 scrollToColumn(bPtr, 0, True);
971 bPtr->flags.loaded = 1;
976 void
977 WMRemoveBrowserItem(WMBrowser *bPtr, int column, int row)
979 WMList *list;
981 if (column < 0 || column >= bPtr->usedColumnCount)
982 return;
984 list = WMGetBrowserListInColumn(bPtr, column);
986 if (row < 0 || row >= WMGetListNumberOfRows(list))
987 return;
989 removeColumn(bPtr, column+1);
990 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
991 scrollToColumn(bPtr, 0, True);
992 else
993 scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
994 True);
996 WMRemoveListItem(list, row);
1000 static void
1001 listSelectionObserver(void *observerData, WMNotification *notification)
1003 WMBrowser *bPtr = (WMBrowser*)observerData;
1004 int column, item = (int)WMGetNotificationClientData(notification);
1005 WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
1007 for (column=0; column<bPtr->usedColumnCount; column++)
1008 if (bPtr->columns[column] == lPtr)
1009 break;
1011 /* this can happen when a list is being cleared with WMClearList
1012 * after the column was removed */
1013 if (column >= bPtr->usedColumnCount) {
1014 return;
1017 if (item < 0)
1018 column--;
1020 bPtr->selectedColumn = column;
1025 WMAddBrowserColumn(WMBrowser *bPtr)
1027 WMList *list;
1028 WMList **clist;
1029 char **tlist;
1030 int colY;
1031 int index;
1034 if (bPtr->usedColumnCount < bPtr->columnCount) {
1035 return bPtr->usedColumnCount++;
1038 bPtr->usedColumnCount++;
1040 if (bPtr->flags.isTitled) {
1041 colY = TITLE_SPACING + bPtr->titleHeight;
1042 } else {
1043 colY = 0;
1046 index = bPtr->columnCount;
1047 bPtr->columnCount++;
1048 clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
1049 tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
1050 memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
1051 memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
1052 if (bPtr->columns)
1053 free(bPtr->columns);
1054 if (bPtr->titles)
1055 free(bPtr->titles);
1056 bPtr->columns = clist;
1057 bPtr->titles = tlist;
1059 bPtr->titles[index] = NULL;
1061 list = WMCreateList(bPtr);
1062 WMSetListAction(list, listCallback, bPtr);
1063 WMSetListDoubleAction(list, listDoubleCallback, bPtr);
1064 WMSetListUserDrawProc(list, paintItem);
1065 WMAddNotificationObserver(listSelectionObserver, bPtr,
1066 WMListSelectionDidChangeNotification, list);
1068 bPtr->columns[index] = list;
1070 WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
1071 WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
1072 if (COLUMN_IS_VISIBLE(bPtr, index))
1073 WMMapWidget(list);
1075 /* update the scroller */
1076 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
1077 float value, proportion;
1079 value = bPtr->firstVisibleColumn
1080 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1081 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1082 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1085 return index;
1090 static void
1091 destroyBrowser(WMBrowser *bPtr)
1093 int i;
1095 for (i=0; i<bPtr->columnCount; i++) {
1096 if (bPtr->titles[i])
1097 free(bPtr->titles[i]);
1099 free(bPtr->titles);
1101 free(bPtr->pathSeparator);
1103 WMRemoveNotificationObserver(bPtr);
1105 free(bPtr);
1109 static char*
1110 createTruncatedString(WMFont *font, char *text, int *textLen, int width)
1112 int dLen = WMWidthOfString(font, ".", 1);
1113 char *textBuf = (char*)wmalloc((*textLen)+4);
1115 if (width >= 3*dLen) {
1116 int dddLen = 3*dLen;
1117 int tmpTextLen = *textLen;
1119 strcpy(textBuf, text);
1120 while (tmpTextLen
1121 && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
1122 tmpTextLen--;
1123 strcpy(textBuf+tmpTextLen, "...");
1124 *textLen = tmpTextLen+3;
1125 } else if (width >= 2*dLen) {
1126 strcpy(textBuf, "..");
1127 *textLen = 2;
1128 } else if (width >= dLen) {
1129 strcpy(textBuf, ".");
1130 *textLen = 1;
1131 } else {
1132 *textBuf = '\0';
1133 *textLen = 0;
1135 return textBuf;