- the list multiple selection code is working now. it still needs some
[wmaker-crm.git] / WINGs / wbrowser.c
blob3c0c2d0819c52b27c5ea6f292db2418920d7423e
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* createTruncatedString(WMFont *font, char *text, int *textLen,
88 int width);
90 static void willResizeBrowser(W_ViewDelegate*, WMView*,
91 unsigned int*, unsigned int*);
93 W_ViewDelegate _BrowserViewDelegate = {
94 NULL,
95 NULL,
96 NULL,
97 NULL,
98 willResizeBrowser
103 WMBrowser*
104 WMCreateBrowser(WMWidget *parent)
106 WMBrowser *bPtr;
107 int i;
109 wassertrv(parent, NULL);
111 bPtr = wmalloc(sizeof(WMBrowser));
112 memset(bPtr, 0, sizeof(WMBrowser));
114 bPtr->widgetClass = WC_Browser;
116 bPtr->view = W_CreateView(W_VIEW(parent));
117 if (!bPtr->view) {
118 wfree(bPtr);
119 return NULL;
121 bPtr->view->self = bPtr;
123 bPtr->view->delegate = &_BrowserViewDelegate;
125 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask
126 |ClientMessageMask, handleEvents, bPtr);
128 /* default configuration */
129 bPtr->flags.hasScroller = DEFAULT_HAS_SCROLLER;
131 bPtr->titleHeight = DEFAULT_TITLE_HEIGHT;
132 bPtr->flags.isTitled = DEFAULT_IS_TITLED;
133 bPtr->maxVisibleColumns = DEFAULT_MAX_VISIBLE_COLUMNS;
135 WMResizeWidget(bPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
137 bPtr->pathSeparator = wstrdup(DEFAULT_SEPARATOR);
139 if (bPtr->flags.hasScroller)
140 setupScroller(bPtr);
142 for (i=0; i<bPtr->maxVisibleColumns; i++) {
143 WMAddBrowserColumn(bPtr);
145 bPtr->usedColumnCount = 0;
147 bPtr->selectedColumn = -1;
149 return bPtr;
154 WMGetBrowserMaxVisibleColumns(WMBrowser *bPtr)
156 return bPtr->maxVisibleColumns;
160 void
161 WMSetBrowserMaxVisibleColumns(WMBrowser *bPtr, int columns)
163 int curMaxVisibleColumns;
164 int newFirstVisibleColumn = 0;
166 assert ((int) bPtr);
168 columns = (columns < MIN_VISIBLE_COLUMNS) ? MIN_VISIBLE_COLUMNS : columns;
169 columns = (columns > MAX_VISIBLE_COLUMNS) ? MAX_VISIBLE_COLUMNS : columns;
170 if (columns == bPtr->maxVisibleColumns) {
171 return;
173 curMaxVisibleColumns = bPtr->maxVisibleColumns;
174 bPtr->maxVisibleColumns = columns;
175 /* browser not loaded */
176 if (!bPtr->flags.loaded) {
177 if ((columns > curMaxVisibleColumns) && (columns > bPtr->columnCount)) {
178 int i = columns - bPtr->columnCount;
179 bPtr->usedColumnCount = bPtr->columnCount;
180 while (i--) {
181 WMAddBrowserColumn(bPtr);
183 bPtr->usedColumnCount = 0;
185 /* browser loaded and columns > curMaxVisibleColumns */
186 } else if (columns > curMaxVisibleColumns) {
187 if (bPtr->usedColumnCount > columns) {
188 newFirstVisibleColumn = bPtr->usedColumnCount - columns;
190 if (newFirstVisibleColumn > bPtr->firstVisibleColumn) {
191 newFirstVisibleColumn = bPtr->firstVisibleColumn;
193 if (columns > bPtr->columnCount) {
194 int i = columns - bPtr->columnCount;
195 int curUsedColumnCount = bPtr->usedColumnCount;
196 bPtr->usedColumnCount = bPtr->columnCount;
197 while (i--) {
198 WMAddBrowserColumn(bPtr);
200 bPtr->usedColumnCount = curUsedColumnCount;
202 /* browser loaded and columns < curMaxVisibleColumns */
203 } else {
204 newFirstVisibleColumn = bPtr->firstVisibleColumn;
205 if (newFirstVisibleColumn + columns >= bPtr->usedColumnCount) {
206 removeColumn(bPtr, newFirstVisibleColumn + columns);
209 WMResizeWidget(bPtr, bPtr->view->size.width, bPtr->view->size.height);
210 if (bPtr->flags.loaded) {
211 XClearArea(bPtr->view->screen->display, bPtr->view->window, 0, 0,
212 bPtr->view->size.width, bPtr->titleHeight, False);
213 scrollToColumn (bPtr, newFirstVisibleColumn, True);
219 WMGetBrowserNumberOfColumns(WMBrowser *bPtr)
221 return bPtr->usedColumnCount;
224 void
225 WMSetBrowserPathSeparator(WMBrowser *bPtr, char *separator)
227 if (bPtr->pathSeparator)
228 wfree(bPtr->pathSeparator);
229 bPtr->pathSeparator = wstrdup(separator);
234 static void
235 drawTitleOfColumn(WMBrowser *bPtr, int column)
237 WMScreen *scr = bPtr->view->screen;
238 int x;
240 x=(column-bPtr->firstVisibleColumn)*(bPtr->columnSize.width+COLUMN_SPACING);
242 XFillRectangle(scr->display, bPtr->view->window, WMColorGC(scr->darkGray), x, 0,
243 bPtr->columnSize.width, bPtr->titleHeight);
244 W_DrawRelief(scr, bPtr->view->window, x, 0,
245 bPtr->columnSize.width, bPtr->titleHeight, WRSunken);
247 if (column < bPtr->usedColumnCount && bPtr->titles[column]) {
248 int titleLen = strlen(bPtr->titles[column]);
249 int widthC = bPtr->columnSize.width-8;
251 if (WMWidthOfString(scr->boldFont, bPtr->titles[column], titleLen)
252 > widthC) {
253 char *titleBuf = createTruncatedString(scr->boldFont,
254 bPtr->titles[column],
255 &titleLen, widthC);
256 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
257 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
258 bPtr->columnSize.width, WACenter, WMColorGC(scr->white),
259 False, titleBuf, titleLen);
260 free (titleBuf);
261 } else {
262 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
263 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
264 bPtr->columnSize.width, WACenter, WMColorGC(scr->white),
265 False, bPtr->titles[column], titleLen);
271 WMList*
272 WMGetBrowserListInColumn(WMBrowser *bPtr, int column)
274 if (column < 0 || column >= bPtr->usedColumnCount)
275 return NULL;
277 return bPtr->columns[column];
281 void
282 WMSetBrowserDelegate(WMBrowser *bPtr, WMBrowserDelegate *delegate)
284 bPtr->delegate = delegate;
289 WMGetBrowserFirstVisibleColumn(WMBrowser *bPtr)
291 return bPtr->firstVisibleColumn;
295 static void
296 removeColumn(WMBrowser *bPtr, int column)
298 int i, clearEnd, destroyEnd;
299 WMList **clist;
300 char **tlist;
302 assert ((int) bPtr);
304 column = (column < 0) ? 0 : column;
305 if (column >= bPtr->columnCount) {
306 return;
308 if (column < bPtr->maxVisibleColumns) {
309 clearEnd = bPtr->maxVisibleColumns;
310 destroyEnd = bPtr->columnCount;
311 bPtr->columnCount = bPtr->maxVisibleColumns;
312 } else {
313 clearEnd = column;
314 destroyEnd = bPtr->columnCount;
315 bPtr->columnCount = column;
317 if (column < bPtr->usedColumnCount) {
318 bPtr->usedColumnCount = column;
320 for (i=column; i < clearEnd; i++) {
321 if (bPtr->titles[i]) {
322 wfree(bPtr->titles[i]);
323 bPtr->titles[i] = NULL;
325 WMClearList(bPtr->columns[i]);
327 for (;i < destroyEnd; i++) {
328 if (bPtr->titles[i]) {
329 wfree(bPtr->titles[i]);
330 bPtr->titles[i] = NULL;
332 WMRemoveNotificationObserverWithName(bPtr,
333 WMListSelectionDidChangeNotification,
334 bPtr->columns[i]);
335 WMDestroyWidget(bPtr->columns[i]);
336 bPtr->columns[i] = NULL;
338 clist = wmalloc(sizeof(WMList*) * (bPtr->columnCount));
339 tlist = wmalloc(sizeof(char*) * (bPtr->columnCount));
340 memcpy(clist, bPtr->columns, sizeof(WMList*) * (bPtr->columnCount));
341 memcpy(tlist, bPtr->titles, sizeof(char*) * (bPtr->columnCount));
342 wfree(bPtr->titles);
343 wfree(bPtr->columns);
344 bPtr->titles = tlist;
345 bPtr->columns = clist;
349 WMListItem*
350 WMGetBrowserSelectedItemInColumn(WMBrowser *bPtr, int column)
352 if ((column < 0) || (column >= bPtr->usedColumnCount))
353 return NULL;
355 return WMGetListSelectedItem(bPtr->columns[column]);
361 WMGetBrowserSelectedColumn(WMBrowser *bPtr)
363 return bPtr->selectedColumn;
368 WMGetBrowserSelectedRowInColumn(WMBrowser *bPtr, int column)
370 if (column >= 0 && column < bPtr->columnCount) {
371 return WMGetListSelectedItemRow(bPtr->columns[column]);
372 } else {
373 return -1;
378 void
379 WMSetBrowserColumnTitle(WMBrowser *bPtr, int column, char *title)
381 assert(column >= 0);
382 assert(column < bPtr->usedColumnCount);
384 if (bPtr->titles[column])
385 wfree(bPtr->titles[column]);
387 bPtr->titles[column] = wstrdup(title);
389 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
390 drawTitleOfColumn(bPtr, column);
395 void
396 WMSetBrowserTitled(WMBrowser *bPtr, Bool flag)
398 int i;
399 int columnX, columnY;
401 if (bPtr->flags.isTitled == flag)
402 return;
404 columnX = 0;
406 if (!bPtr->flags.isTitled) {
407 columnY = TITLE_SPACING + bPtr->titleHeight;
409 bPtr->columnSize.height -= columnY;
411 for (i=0; i<bPtr->columnCount; i++) {
412 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
413 bPtr->columnSize.height);
415 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
417 WMMoveWidget(bPtr->columns[i], columnX, columnY);
419 } else {
420 bPtr->columnSize.height += TITLE_SPACING + bPtr->titleHeight;
422 for (i=0; i<bPtr->columnCount; i++) {
423 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
424 bPtr->columnSize.height);
426 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
428 WMMoveWidget(bPtr->columns[i], columnX, 0);
432 bPtr->flags.isTitled = flag;
436 void
437 WMSortBrowserColumn(WMBrowser *bPtr, int column)
439 WMSortListItems(bPtr->columns[column]);
443 void
444 WMSortBrowserColumnWithComparer(WMBrowser *bPtr, int column,
445 WMCompareDataProc *func)
447 WMSortListItemsWithComparer(bPtr->columns[column], func);
452 WMListItem*
453 WMInsertBrowserItem(WMBrowser *bPtr, int column, int row, char *text,
454 Bool isBranch)
456 WMListItem *item;
458 if (column < 0 || column >= bPtr->columnCount)
459 return NULL;
461 item = WMInsertListItem(bPtr->columns[column], row, text);
462 item->isBranch = isBranch;
464 return item;
470 static void
471 willResizeBrowser(W_ViewDelegate *self, WMView *view,
472 unsigned int *width, unsigned int *height)
474 WMBrowser *bPtr = (WMBrowser*)view->self;
475 int cols = bPtr->maxVisibleColumns;
476 int colX, colY;
477 int i;
479 assert(*width > 0);
480 assert(*height > 0);
482 bPtr->columnSize.width = (*width-(cols-1)*COLUMN_SPACING) / cols;
483 bPtr->columnSize.height = *height;
485 if (bPtr->flags.isTitled) {
486 colY = TITLE_SPACING + bPtr->titleHeight;
487 bPtr->columnSize.height -= colY;
488 } else {
489 colY = 0;
492 if (bPtr->flags.hasScroller) {
493 bPtr->columnSize.height -= SCROLLER_WIDTH + 4;
495 if (bPtr->scroller) {
496 WMResizeWidget(bPtr->scroller, *width-2, 1);
497 WMMoveWidget(bPtr->scroller, 1, *height-SCROLLER_WIDTH-1);
501 colX = 0;
502 for (i = 0; i < bPtr->columnCount; i++) {
503 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
504 bPtr->columnSize.height);
506 WMMoveWidget(bPtr->columns[i], colX, colY);
508 if (COLUMN_IS_VISIBLE(bPtr, i)) {
509 colX += bPtr->columnSize.width+COLUMN_SPACING;
515 static void
516 paintItem(WMList *lPtr, int index, Drawable d, char *text, int state,
517 WMRect *rect)
519 WMView *view = W_VIEW(lPtr);
520 W_Screen *scr = view->screen;
521 int width, height, x, y;
523 width = rect->size.width;
524 height = rect->size.height;
525 x = rect->pos.x;
526 y = rect->pos.y;
528 if (state & WLDSSelected)
529 XFillRectangle(scr->display, d, WMColorGC(scr->white), x, y,
530 width, height);
531 else
532 XClearArea(scr->display, d, x, y, width, height, False);
534 if (text) {
535 /* Avoid overlaping... */
536 WMFont *font = (state & WLDSIsBranch) ? scr->boldFont : scr->normalFont;
537 int textLen = strlen(text);
538 int widthC = (state & WLDSIsBranch) ? width-20 : width-8;
539 if (WMWidthOfString(font, text, textLen) > widthC) {
540 char *textBuf = createTruncatedString(font, text, &textLen, widthC);
541 W_PaintText(view, d, font, x+4, y, widthC,
542 WALeft, WMColorGC(scr->black), False, textBuf, textLen);
543 wfree(textBuf);
544 } else {
545 W_PaintText(view, d, font, x+4, y, widthC,
546 WALeft, WMColorGC(scr->black), False, text, textLen);
550 if (state & WLDSIsBranch) {
551 XDrawLine(scr->display, d, WMColorGC(scr->darkGray), x+width-11, y+3,
552 x+width-6, y+height/2);
553 if (state & WLDSSelected)
554 XDrawLine(scr->display, d,WMColorGC(scr->gray), x+width-11, y+height-5,
555 x+width-6, y+height/2);
556 else
557 XDrawLine(scr->display, d,WMColorGC(scr->white), x+width-11, y+height-5,
558 x+width-6, y+height/2);
559 XDrawLine(scr->display, d, WMColorGC(scr->black), x+width-12, y+3,
560 x+width-12, y+height-5);
565 static void
566 scrollCallback(WMWidget *scroller, void *self)
568 WMBrowser *bPtr = (WMBrowser*)self;
569 WMScroller *sPtr = (WMScroller*)scroller;
570 int newFirst;
571 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
573 switch (WMGetScrollerHitPart(sPtr)) {
574 case WSDecrementLine:
575 if (bPtr->firstVisibleColumn > 0) {
576 scrollToColumn(bPtr, bPtr->firstVisibleColumn-1, True);
578 break;
580 case WSDecrementPage:
581 case WSDecrementWheel:
582 if (bPtr->firstVisibleColumn > 0) {
583 newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
585 scrollToColumn(bPtr, newFirst, True);
587 break;
590 case WSIncrementLine:
591 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
592 scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
594 break;
596 case WSIncrementPage:
597 case WSIncrementWheel:
598 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
599 newFirst = bPtr->firstVisibleColumn + bPtr->maxVisibleColumns;
601 if (newFirst+bPtr->maxVisibleColumns >= bPtr->columnCount)
602 newFirst = bPtr->columnCount - bPtr->maxVisibleColumns;
604 scrollToColumn(bPtr, newFirst, True);
606 break;
608 case WSKnob:
610 double floatValue;
611 double value = bPtr->columnCount - bPtr->maxVisibleColumns;
613 floatValue = WMGetScrollerValue(bPtr->scroller);
615 floatValue = (floatValue*value)/value;
617 newFirst = rint(floatValue*(float)(bPtr->columnCount - bPtr->maxVisibleColumns));
619 if (bPtr->firstVisibleColumn != newFirst)
620 scrollToColumn(bPtr, newFirst, False);
621 /* else
622 WMSetScrollerParameters(bPtr->scroller, floatValue,
623 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
627 break;
629 case WSKnobSlot:
630 case WSNoPart:
631 /* do nothing */
632 break;
634 #undef LAST_VISIBLE_COLUMN
638 static void
639 setupScroller(WMBrowser *bPtr)
641 WMScroller *sPtr;
642 int y;
644 y = bPtr->view->size.height - SCROLLER_WIDTH - 1;
646 sPtr = WMCreateScroller(bPtr);
647 WMSetScrollerAction(sPtr, scrollCallback, bPtr);
648 WMMoveWidget(sPtr, 1, y);
649 WMResizeWidget(sPtr, bPtr->view->size.width-2, SCROLLER_WIDTH);
651 bPtr->scroller = sPtr;
653 WMMapWidget(sPtr);
657 void
658 WMSetBrowserAction(WMBrowser *bPtr, WMAction *action, void *clientData)
660 bPtr->action = action;
661 bPtr->clientData = clientData;
665 void
666 WMSetBrowserDoubleAction(WMBrowser *bPtr, WMAction *action, void *clientData)
668 bPtr->doubleAction = action;
669 bPtr->doubleClientData = clientData;
673 void
674 WMSetBrowserHasScroller(WMBrowser *bPtr, int hasScroller)
676 bPtr->flags.hasScroller = hasScroller;
681 char*
682 WMSetBrowserPath(WMBrowser *bPtr, char *path)
684 int i;
685 char *str = wstrdup(path);
686 char *tmp, *retPtr = NULL;
687 int item;
688 WMListItem *listItem;
690 /* WMLoadBrowserColumnZero must be call first */
691 if (!bPtr->flags.loaded) {
692 return False;
695 removeColumn(bPtr, 1);
697 WMSelectListItem(bPtr->columns[0], -1);
698 WMSetListPosition(bPtr->columns[0], 0);
700 i = 0;
701 tmp = strtok(str, bPtr->pathSeparator);
702 while (tmp) {
703 /* select it in the column */
704 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
705 if (item<0) {
706 retPtr = &path[(int)(tmp - str)];
707 break;
709 WMSelectListItem(bPtr->columns[i], item);
710 WMSetListPosition(bPtr->columns[i], item);
712 listItem = WMGetListItem(bPtr->columns[i], item);
713 if (!listItem || !listItem->isBranch) {
714 break;
717 /* load next column */
718 WMAddBrowserColumn(bPtr);
720 loadColumn(bPtr, i+1);
722 tmp = strtok(NULL, bPtr->pathSeparator);
724 i++;
726 wfree(str);
728 for (i = bPtr->usedColumnCount - 1;
729 (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
730 i--);
732 bPtr->selectedColumn = i;
734 if (bPtr->columnCount < bPtr->maxVisibleColumns) {
735 int i = bPtr->maxVisibleColumns - bPtr->columnCount;
736 int curUsedColumnCount = bPtr->usedColumnCount;
737 bPtr->usedColumnCount = bPtr->columnCount;
738 while (i--) {
739 WMAddBrowserColumn(bPtr);
741 bPtr->usedColumnCount = curUsedColumnCount;
744 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
746 return retPtr;
750 char*
751 WMGetBrowserPath(WMBrowser *bPtr)
753 return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
758 char*
759 WMGetBrowserPathToColumn(WMBrowser *bPtr, int column)
761 int i, size;
762 char *path;
763 WMListItem *item;
765 if (column >= bPtr->usedColumnCount)
766 column = bPtr->usedColumnCount-1;
768 if (column < 0) {
769 return wstrdup(bPtr->pathSeparator);
772 /* calculate size of buffer */
773 size = 0;
774 for (i = 0; i <= column; i++) {
775 item = WMGetListSelectedItem(bPtr->columns[i]);
776 if (!item)
777 break;
778 size += strlen(item->text);
781 /* get the path */
782 path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
783 /* ignore first / */
784 *path = 0;
785 for (i = 0; i <= column; i++) {
786 strcat(path, bPtr->pathSeparator);
787 item = WMGetListSelectedItem(bPtr->columns[i]);
788 if (!item)
789 break;
790 strcat(path, item->text);
793 return path;
797 static void
798 loadColumn(WMBrowser *bPtr, int column)
800 assert(bPtr->delegate);
801 assert(bPtr->delegate->createRowsForColumn);
803 bPtr->flags.loadingColumn = 1;
804 (*bPtr->delegate->createRowsForColumn)(bPtr->delegate, bPtr, column,
805 bPtr->columns[column]);
806 bPtr->flags.loadingColumn = 0;
808 if (bPtr->delegate->titleOfColumn) {
809 char *title;
811 title = (*bPtr->delegate->titleOfColumn)(bPtr->delegate, bPtr, column);
813 if (bPtr->titles[column])
814 wfree(bPtr->titles[column]);
816 bPtr->titles[column] = wstrdup(title);
818 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
819 drawTitleOfColumn(bPtr, column);
825 static void
826 paintBrowser(WMBrowser *bPtr)
828 int i;
830 if (!bPtr->view->flags.mapped)
831 return;
833 W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
834 bPtr->view->size.height-SCROLLER_WIDTH-2,
835 bPtr->view->size.width, 22, WRSunken);
837 if (bPtr->flags.isTitled) {
838 for (i=0; i<bPtr->maxVisibleColumns; i++) {
839 drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
845 static void
846 handleEvents(XEvent *event, void *data)
848 WMBrowser *bPtr = (WMBrowser*)data;
850 CHECK_CLASS(data, WC_Browser);
853 switch (event->type) {
854 case Expose:
855 paintBrowser(bPtr);
856 break;
858 case DestroyNotify:
859 destroyBrowser(bPtr);
860 break;
867 static void
868 scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller)
870 int i;
871 int x;
872 int notify = 0;
875 if (column != bPtr->firstVisibleColumn) {
876 notify = 1;
879 if (column < 0)
880 column = 0;
882 if (notify && bPtr->delegate && bPtr->delegate->willScroll) {
883 (*bPtr->delegate->willScroll)(bPtr->delegate, bPtr);
886 x = 0;
887 bPtr->firstVisibleColumn = column;
888 for (i = 0; i < bPtr->columnCount; i++) {
889 if (COLUMN_IS_VISIBLE(bPtr, i)) {
890 WMMoveWidget(bPtr->columns[i], x,
891 WMWidgetView(bPtr->columns[i])->pos.y);
892 if (!WMWidgetView(bPtr->columns[i])->flags.realized)
893 WMRealizeWidget(bPtr->columns[i]);
894 WMMapWidget(bPtr->columns[i]);
895 x += bPtr->columnSize.width + COLUMN_SPACING;
896 } else {
897 WMUnmapWidget(bPtr->columns[i]);
901 /* update the scroller */
902 if (updateScroller) {
903 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
904 float value, proportion;
906 value = bPtr->firstVisibleColumn
907 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
908 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
909 WMSetScrollerParameters(bPtr->scroller, value, proportion);
910 } else {
911 WMSetScrollerParameters(bPtr->scroller, 0, 1);
915 if (bPtr->view->flags.mapped)
916 paintBrowser(bPtr);
918 if (notify && bPtr->delegate && bPtr->delegate->didScroll) {
919 (*bPtr->delegate->didScroll)(bPtr->delegate, bPtr);
924 static void
925 listCallback(void *self, void *clientData)
927 WMBrowser *bPtr = (WMBrowser*)clientData;
928 WMList *lPtr = (WMList*)self;
929 WMListItem *item;
930 static WMListItem *oldItem = NULL;
931 int i;
933 item = WMGetListSelectedItem(lPtr);
934 if (!item) {
935 oldItem = item;
936 return;
939 if (oldItem != item) {
940 for (i=0; i<bPtr->columnCount; i++) {
941 if (lPtr == bPtr->columns[i])
942 break;
944 assert(i<bPtr->columnCount);
946 bPtr->selectedColumn = i;
948 /* columns at right must be cleared */
949 removeColumn(bPtr, i+1);
950 /* open directory */
951 if (item->isBranch) {
952 WMAddBrowserColumn(bPtr);
954 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
955 i = 0;
956 else
957 i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
958 scrollToColumn(bPtr, i, True);
959 if (item->isBranch) {
960 loadColumn(bPtr, bPtr->usedColumnCount-1);
965 /* call callback for click */
966 if (bPtr->action)
967 (*bPtr->action)(bPtr, bPtr->clientData);
969 oldItem = item;
973 static void
974 listDoubleCallback(void *self, void *clientData)
976 WMBrowser *bPtr = (WMBrowser*)clientData;
977 WMList *lPtr = (WMList*)self;
978 WMListItem *item;
980 item = WMGetListSelectedItem(lPtr);
981 if (!item)
982 return;
984 /* call callback for double click */
985 if (bPtr->doubleAction)
986 (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
990 void
991 WMLoadBrowserColumnZero(WMBrowser *bPtr)
993 if (!bPtr->flags.loaded) {
994 /* create column 0 */
995 WMAddBrowserColumn(bPtr);
997 loadColumn(bPtr, 0);
999 /* make column 0 visible */
1000 scrollToColumn(bPtr, 0, True);
1002 bPtr->flags.loaded = 1;
1007 void
1008 WMRemoveBrowserItem(WMBrowser *bPtr, int column, int row)
1010 WMList *list;
1012 if (column < 0 || column >= bPtr->usedColumnCount)
1013 return;
1015 list = WMGetBrowserListInColumn(bPtr, column);
1017 if (row < 0 || row >= WMGetListNumberOfRows(list))
1018 return;
1020 removeColumn(bPtr, column+1);
1021 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
1022 scrollToColumn(bPtr, 0, True);
1023 else
1024 scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
1025 True);
1027 WMRemoveListItem(list, row);
1031 static void
1032 listSelectionObserver(void *observerData, WMNotification *notification)
1034 WMBrowser *bPtr = (WMBrowser*)observerData;
1035 int column;
1036 WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
1038 for (column = 0; column < bPtr->usedColumnCount; column++)
1039 if (bPtr->columns[column] == lPtr)
1040 break;
1042 /* this can happen when a list is being cleared with WMClearList
1043 * after the column was removed */
1044 if (column >= bPtr->usedColumnCount) {
1045 return;
1048 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr)) == 0)
1049 column--;
1051 bPtr->selectedColumn = column;
1056 WMAddBrowserColumn(WMBrowser *bPtr)
1058 WMList *list;
1059 WMList **clist;
1060 char **tlist;
1061 int colY;
1062 int index;
1065 if (bPtr->usedColumnCount < bPtr->columnCount) {
1066 return bPtr->usedColumnCount++;
1069 bPtr->usedColumnCount++;
1071 if (bPtr->flags.isTitled) {
1072 colY = TITLE_SPACING + bPtr->titleHeight;
1073 } else {
1074 colY = 0;
1077 index = bPtr->columnCount;
1078 bPtr->columnCount++;
1079 clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
1080 tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
1081 memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
1082 memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
1083 if (bPtr->columns)
1084 wfree(bPtr->columns);
1085 if (bPtr->titles)
1086 wfree(bPtr->titles);
1087 bPtr->columns = clist;
1088 bPtr->titles = tlist;
1090 bPtr->titles[index] = NULL;
1092 list = WMCreateList(bPtr);
1093 WMSetListAction(list, listCallback, bPtr);
1094 WMSetListDoubleAction(list, listDoubleCallback, bPtr);
1095 WMSetListUserDrawProc(list, paintItem);
1096 WMAddNotificationObserver(listSelectionObserver, bPtr,
1097 WMListSelectionDidChangeNotification, list);
1099 bPtr->columns[index] = list;
1101 WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
1102 WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
1103 if (COLUMN_IS_VISIBLE(bPtr, index))
1104 WMMapWidget(list);
1106 /* update the scroller */
1107 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
1108 float value, proportion;
1110 value = bPtr->firstVisibleColumn
1111 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1112 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1113 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1116 return index;
1121 static void
1122 destroyBrowser(WMBrowser *bPtr)
1124 int i;
1126 for (i = 0; i < bPtr->columnCount; i++) {
1127 if (bPtr->titles[i])
1128 wfree(bPtr->titles[i]);
1130 wfree(bPtr->titles);
1132 wfree(bPtr->pathSeparator);
1134 WMRemoveNotificationObserver(bPtr);
1136 wfree(bPtr);
1140 static char*
1141 createTruncatedString(WMFont *font, char *text, int *textLen, int width)
1143 int dLen = WMWidthOfString(font, ".", 1);
1144 char *textBuf = (char*)wmalloc((*textLen)+4);
1146 if (width >= 3*dLen) {
1147 int dddLen = 3*dLen;
1148 int tmpTextLen = *textLen;
1150 strcpy(textBuf, text);
1151 while (tmpTextLen
1152 && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
1153 tmpTextLen--;
1154 strcpy(textBuf+tmpTextLen, "...");
1155 *textLen = tmpTextLen+3;
1156 } else if (width >= 2*dLen) {
1157 strcpy(textBuf, "..");
1158 *textLen = 2;
1159 } else if (width >= dLen) {
1160 strcpy(textBuf, ".");
1161 *textLen = 1;
1162 } else {
1163 *textBuf = '\0';
1164 *textLen = 0;
1166 return textBuf;