0.51.1 pre snapshot. Be careful, it may be buggy. It fixes some bugs though.
[wmaker-crm.git] / WINGs / wbrowser.c
blobb2eaaec6936d0f6844ce1ec81d730d653d6f63cf
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;
150 void
151 WMSetBrowserMaxVisibleColumns(WMBrowser *bPtr, int columns)
153 int curMaxVisibleColumns;
154 int newFirstVisibleColumn = 0;
156 assert ((int) bPtr);
158 columns = (columns < MIN_VISIBLE_COLUMNS) ? MIN_VISIBLE_COLUMNS : columns;
159 columns = (columns > MAX_VISIBLE_COLUMNS) ? MAX_VISIBLE_COLUMNS : columns;
160 if (columns == bPtr->maxVisibleColumns) {
161 return;
163 curMaxVisibleColumns = bPtr->maxVisibleColumns;
164 bPtr->maxVisibleColumns = columns;
165 /* browser not loaded */
166 if (!bPtr->flags.loaded) {
167 if ((columns > curMaxVisibleColumns) && (columns > bPtr->columnCount)) {
168 int i = columns - bPtr->columnCount;
169 bPtr->usedColumnCount = bPtr->columnCount;
170 while (i--) {
171 WMAddBrowserColumn(bPtr);
173 bPtr->usedColumnCount = 0;
175 /* browser loaded and columns > curMaxVisibleColumns */
176 } else if (columns > curMaxVisibleColumns) {
177 if (bPtr->usedColumnCount > columns) {
178 newFirstVisibleColumn = bPtr->usedColumnCount - columns;
180 if (newFirstVisibleColumn > bPtr->firstVisibleColumn) {
181 newFirstVisibleColumn = bPtr->firstVisibleColumn;
183 if (columns > bPtr->columnCount) {
184 int i = columns - bPtr->columnCount;
185 int curUsedColumnCount = bPtr->usedColumnCount;
186 bPtr->usedColumnCount = bPtr->columnCount;
187 while (i--) {
188 WMAddBrowserColumn(bPtr);
190 bPtr->usedColumnCount = curUsedColumnCount;
192 /* browser loaded and columns < curMaxVisibleColumns */
193 } else {
194 newFirstVisibleColumn = bPtr->firstVisibleColumn;
195 if (newFirstVisibleColumn + columns >= bPtr->usedColumnCount) {
196 removeColumn(bPtr, newFirstVisibleColumn + columns);
199 resizeBrowser(bPtr, bPtr->view->size.width, bPtr->view->size.height);
200 if (bPtr->flags.loaded) {
201 XClearArea(bPtr->view->screen->display, bPtr->view->window, 0, 0,
202 bPtr->view->size.width, bPtr->titleHeight, False);
203 scrollToColumn (bPtr, newFirstVisibleColumn, True);
208 int
209 WMGetBrowserNumberOfColumns(WMBrowser *bPtr)
211 return bPtr->usedColumnCount;
214 void
215 WMSetBrowserPathSeparator(WMBrowser *bPtr, char *separator)
217 if (bPtr->pathSeparator)
218 free(bPtr->pathSeparator);
219 bPtr->pathSeparator = wstrdup(separator);
224 static void
225 drawTitleOfColumn(WMBrowser *bPtr, int column)
227 WMScreen *scr = bPtr->view->screen;
228 int x;
230 x=(column-bPtr->firstVisibleColumn)*(bPtr->columnSize.width+COLUMN_SPACING);
232 XFillRectangle(scr->display, bPtr->view->window, W_GC(scr->darkGray), x, 0,
233 bPtr->columnSize.width, bPtr->titleHeight);
234 W_DrawRelief(scr, bPtr->view->window, x, 0,
235 bPtr->columnSize.width, bPtr->titleHeight, WRSunken);
237 if (column < bPtr->usedColumnCount && bPtr->titles[column]) {
238 int titleLen = strlen(bPtr->titles[column]);
239 int widthC = bPtr->columnSize.width-8;
241 if (WMWidthOfString(scr->boldFont, bPtr->titles[column], titleLen)
242 > widthC) {
243 char *titleBuf = createTruncatedString(scr->boldFont,
244 bPtr->titles[column],
245 &titleLen, widthC);
246 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
247 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
248 bPtr->columnSize.width, WACenter, W_GC(scr->white),
249 False, titleBuf, titleLen);
250 free (titleBuf);
251 } else {
252 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
253 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
254 bPtr->columnSize.width, WACenter, W_GC(scr->white),
255 False, bPtr->titles[column], titleLen);
261 void
262 WMSetBrowserColumnTitle(WMBrowser *bPtr, int column, char *title)
264 assert(column >= 0);
265 assert(column < bPtr->usedColumnCount);
267 if (bPtr->titles[column])
268 free(bPtr->titles[column]);
270 bPtr->titles[column] = wstrdup(title);
272 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
273 drawTitleOfColumn(bPtr, column);
278 WMList*
279 WMGetBrowserListInColumn(WMBrowser *bPtr, int column)
281 if (column < 0 || column >= bPtr->usedColumnCount)
282 return NULL;
284 return bPtr->columns[column];
288 void
289 WMSetBrowserFillColumnProc(WMBrowser *bPtr, WMBrowserFillColumnProc *proc)
291 bPtr->fillColumn = proc;
295 int
296 WMGetBrowserFirstVisibleColumn(WMBrowser *bPtr)
298 return bPtr->firstVisibleColumn;
302 static void
303 removeColumn(WMBrowser *bPtr, int column)
305 int i, clearEnd, destroyEnd;
306 WMList **clist;
307 char **tlist;
309 assert ((int) bPtr);
311 column = (column < 0) ? 0 : column;
312 if (column >= bPtr->columnCount) {
313 return;
315 if (column < bPtr->maxVisibleColumns) {
316 clearEnd = bPtr->maxVisibleColumns;
317 destroyEnd = bPtr->columnCount;
318 bPtr->columnCount = bPtr->maxVisibleColumns;
319 } else {
320 clearEnd = column;
321 destroyEnd = bPtr->columnCount;
322 bPtr->columnCount = column;
324 if (column < bPtr->usedColumnCount) {
325 bPtr->usedColumnCount = column;
327 for (i=column; i < clearEnd; i++) {
328 if (bPtr->titles[i]) {
329 free(bPtr->titles[i]);
330 bPtr->titles[i] = NULL;
332 WMClearList(bPtr->columns[i]);
334 for (;i < destroyEnd; i++) {
335 if (bPtr->titles[i]) {
336 free(bPtr->titles[i]);
337 bPtr->titles[i] = NULL;
339 WMRemoveNotificationObserverWithName(bPtr,
340 WMListSelectionDidChangeNotification,
341 bPtr->columns[i]);
342 WMDestroyWidget(bPtr->columns[i]);
343 bPtr->columns[i] = NULL;
345 clist = wmalloc(sizeof(WMList*) * (bPtr->columnCount));
346 tlist = wmalloc(sizeof(char*) * (bPtr->columnCount));
347 memcpy(clist, bPtr->columns, sizeof(WMList*) * (bPtr->columnCount));
348 memcpy(tlist, bPtr->titles, sizeof(char*) * (bPtr->columnCount));
349 free(bPtr->titles);
350 free(bPtr->columns);
351 bPtr->titles = tlist;
352 bPtr->columns = clist;
356 WMListItem*
357 WMGetBrowserSelectedItemInColumn(WMBrowser *bPtr, int column)
359 if ((column < 0) || (column >= bPtr->usedColumnCount))
360 return NULL;
362 return WMGetListSelectedItem(bPtr->columns[column]);
368 WMGetBrowserSelectedColumn(WMBrowser *bPtr)
370 return bPtr->selectedColumn;
375 WMGetBrowserSelectedRowInColumn(WMBrowser *bPtr, int column)
377 if (column >= 0 && column < bPtr->columnCount) {
378 return WMGetListSelectedItemRow(bPtr->columns[column]);
379 } else {
380 return -1;
385 void
386 WMSetBrowserTitled(WMBrowser *bPtr, Bool flag)
388 int i;
389 int columnX, columnY;
391 if (bPtr->flags.isTitled == flag)
392 return;
394 columnX = 0;
396 if (!bPtr->flags.isTitled) {
397 columnY = TITLE_SPACING + bPtr->titleHeight;
399 bPtr->columnSize.height -= columnY;
401 for (i=0; i<bPtr->columnCount; i++) {
402 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
403 bPtr->columnSize.height);
405 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
407 WMMoveWidget(bPtr->columns[i], columnX, columnY);
409 } else {
410 bPtr->columnSize.height += TITLE_SPACING + bPtr->titleHeight;
412 for (i=0; i<bPtr->columnCount; i++) {
413 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
414 bPtr->columnSize.height);
416 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
418 WMMoveWidget(bPtr->columns[i], columnX, 0);
422 bPtr->flags.isTitled = flag;
426 WMListItem*
427 WMAddSortedBrowserItem(WMBrowser *bPtr, int column, char *text, Bool isBranch)
429 WMListItem *item;
431 if (column < 0 || column >= bPtr->columnCount)
432 return NULL;
434 item = WMAddSortedListItem(bPtr->columns[column], text);
435 item->isBranch = isBranch;
437 return item;
442 WMListItem*
443 WMInsertBrowserItem(WMBrowser *bPtr, int column, int row, char *text,
444 Bool isBranch)
446 WMListItem *item;
448 if (column < 0 || column >= bPtr->columnCount)
449 return NULL;
451 item = WMInsertListItem(bPtr->columns[column], row, text);
452 item->isBranch = isBranch;
454 return item;
460 static void
461 resizeBrowser(WMWidget *w, unsigned int width, unsigned int height)
463 WMBrowser *bPtr = (WMBrowser*)w;
464 int cols = bPtr->maxVisibleColumns;
465 int colX, colY;
466 int i;
468 assert(width > 0);
469 assert(height > 0);
471 bPtr->columnSize.width = (width-(cols-1)*COLUMN_SPACING) / cols;
472 bPtr->columnSize.height = height;
474 if (bPtr->flags.isTitled) {
475 colY = TITLE_SPACING + bPtr->titleHeight;
476 bPtr->columnSize.height -= colY;
477 } else {
478 colY = 0;
481 if (bPtr->flags.hasScroller) {
482 bPtr->columnSize.height -= SCROLLER_WIDTH + 4;
484 if (bPtr->scroller) {
485 WMResizeWidget(bPtr->scroller, width-2, 1);
486 WMMoveWidget(bPtr->scroller, 1, height-SCROLLER_WIDTH-1);
490 colX = 0;
491 for (i = 0; i < bPtr->columnCount; i++) {
492 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
493 bPtr->columnSize.height);
495 WMMoveWidget(bPtr->columns[i], colX, colY);
497 if (COLUMN_IS_VISIBLE(bPtr, i)) {
498 colX += bPtr->columnSize.width+COLUMN_SPACING;
502 W_ResizeView(bPtr->view, width, height);
506 static void
507 paintItem(WMList *lPtr, int index, Drawable d, char *text, int state,
508 WMRect *rect)
510 WMView *view = W_VIEW(lPtr);
511 W_Screen *scr = view->screen;
512 int width, height, x, y;
514 width = rect->size.width;
515 height = rect->size.height;
516 x = rect->pos.x;
517 y = rect->pos.y;
519 if (state & WLDSSelected)
520 XFillRectangle(scr->display, d, W_GC(scr->white), x, y,
521 width, height);
522 else
523 XClearArea(scr->display, d, x, y, width, height, False);
525 if (text) {
526 /* Avoid overlaping... */
527 int textLen = strlen(text);
528 int widthC = (state & WLDSIsBranch) ? width-20 : width-8;
529 if (WMWidthOfString(scr->normalFont, text, textLen) > widthC) {
530 char *textBuf = createTruncatedString(scr->normalFont,
531 text, &textLen, widthC);
532 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
533 WALeft, W_GC(scr->black), False, textBuf, textLen);
534 free(textBuf);
535 } else {
536 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
537 WALeft, W_GC(scr->black), False, text, textLen);
541 if (state & WLDSIsBranch) {
542 XDrawLine(scr->display, d, W_GC(scr->darkGray), x+width-11, y+3,
543 x+width-6, y+height/2);
544 if (state & WLDSSelected)
545 XDrawLine(scr->display, d,W_GC(scr->gray), x+width-11, y+height-5,
546 x+width-6, y+height/2);
547 else
548 XDrawLine(scr->display, d,W_GC(scr->white), x+width-11, y+height-5,
549 x+width-6, y+height/2);
550 XDrawLine(scr->display, d, W_GC(scr->black), x+width-12, y+3,
551 x+width-12, y+height-5);
556 static void
557 scrollCallback(WMWidget *scroller, void *self)
559 WMBrowser *bPtr = (WMBrowser*)self;
560 WMScroller *sPtr = (WMScroller*)scroller;
561 int newFirst;
562 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
564 switch (WMGetScrollerHitPart(sPtr)) {
565 case WSDecrementLine:
566 if (bPtr->firstVisibleColumn > 0) {
567 scrollToColumn(bPtr, bPtr->firstVisibleColumn-1, True);
569 break;
571 case WSDecrementPage:
572 if (bPtr->firstVisibleColumn > 0) {
573 newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
575 scrollToColumn(bPtr, newFirst, True);
577 break;
580 case WSIncrementLine:
581 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
582 scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
584 break;
586 case WSIncrementPage:
587 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
588 newFirst = bPtr->firstVisibleColumn + bPtr->maxVisibleColumns;
590 if (newFirst+bPtr->maxVisibleColumns >= bPtr->columnCount)
591 newFirst = bPtr->columnCount - bPtr->maxVisibleColumns;
593 scrollToColumn(bPtr, newFirst, True);
595 break;
597 case WSKnob:
599 double floatValue;
600 double value = bPtr->columnCount - bPtr->maxVisibleColumns;
602 floatValue = WMGetScrollerValue(bPtr->scroller);
604 floatValue = (floatValue*value)/value;
606 newFirst = rint(floatValue*(float)(bPtr->columnCount - bPtr->maxVisibleColumns));
608 if (bPtr->firstVisibleColumn != newFirst)
609 scrollToColumn(bPtr, newFirst, False);
610 /* else
611 WMSetScrollerParameters(bPtr->scroller, floatValue,
612 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
616 break;
618 case WSKnobSlot:
619 case WSNoPart:
620 /* do nothing */
621 break;
623 #undef LAST_VISIBLE_COLUMN
627 static void
628 setupScroller(WMBrowser *bPtr)
630 WMScroller *sPtr;
631 int y;
633 y = bPtr->view->size.height - SCROLLER_WIDTH - 1;
635 sPtr = WMCreateScroller(bPtr);
636 WMSetScrollerAction(sPtr, scrollCallback, bPtr);
637 WMMoveWidget(sPtr, 1, y);
638 WMResizeWidget(sPtr, bPtr->view->size.width-2, SCROLLER_WIDTH);
640 bPtr->scroller = sPtr;
642 WMMapWidget(sPtr);
646 void
647 WMSetBrowserAction(WMBrowser *bPtr, WMAction *action, void *clientData)
649 bPtr->action = action;
650 bPtr->clientData = clientData;
654 void
655 WMSetBrowserDoubleAction(WMBrowser *bPtr, WMAction *action, void *clientData)
657 bPtr->doubleAction = action;
658 bPtr->doubleClientData = clientData;
662 void
663 WMSetBrowserHasScroller(WMBrowser *bPtr, int hasScroller)
665 bPtr->flags.hasScroller = hasScroller;
670 char*
671 WMSetBrowserPath(WMBrowser *bPtr, char *path)
673 int i;
674 char *str = wstrdup(path);
675 char *tmp, *retPtr = NULL;
676 int item;
677 WMListItem *listItem;
679 /* WMLoadBrowserColumnZero must be call first */
680 if (!bPtr->flags.loaded) {
681 return False;
684 removeColumn(bPtr, 1);
686 i = 0;
687 tmp = strtok(str, bPtr->pathSeparator);
688 while (tmp) {
689 /* select it in the column */
690 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
691 if (item<0) {
692 retPtr = &path[(int)(tmp - str)];
693 break;
695 WMSelectListItem(bPtr->columns[i], item);
696 WMSetListPosition(bPtr->columns[i], item);
698 listItem = WMGetListItem(bPtr->columns[i], item);
699 if (!listItem || !listItem->isBranch) {
700 break;
703 /* load next column */
704 WMAddBrowserColumn(bPtr);
706 loadColumn(bPtr, i+1);
708 tmp = strtok(NULL, bPtr->pathSeparator);
710 i++;
712 free(str);
714 for (i = bPtr->usedColumnCount - 1;
715 (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
716 i--);
718 bPtr->selectedColumn = i;
720 if (bPtr->columnCount < bPtr->maxVisibleColumns) {
721 int i = bPtr->maxVisibleColumns - bPtr->columnCount;
722 int curUsedColumnCount = bPtr->usedColumnCount;
723 bPtr->usedColumnCount = bPtr->columnCount;
724 while (i--) {
725 WMAddBrowserColumn(bPtr);
727 bPtr->usedColumnCount = curUsedColumnCount;
730 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
732 return retPtr;
736 char*
737 WMGetBrowserPath(WMBrowser *bPtr)
739 return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
743 char*
744 WMGetBrowserPathToColumn(WMBrowser *bPtr, int column)
746 int i, size;
747 char *path;
748 WMListItem *item;
750 if (column >= bPtr->usedColumnCount)
751 column = bPtr->usedColumnCount-1;
753 if (column < 0) {
754 return wstrdup(bPtr->pathSeparator);
757 /* calculate size of buffer */
758 size = 0;
759 for (i = 0; i <= column; i++) {
760 item = WMGetListSelectedItem(bPtr->columns[i]);
761 if (!item)
762 break;
763 size += strlen(item->text);
766 /* get the path */
767 path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
768 /* ignore first / */
769 *path = 0;
770 for (i = 0; i <= column; i++) {
771 strcat(path, bPtr->pathSeparator);
772 item = WMGetListSelectedItem(bPtr->columns[i]);
773 if (!item)
774 break;
775 strcat(path, item->text);
778 return path;
782 static void
783 loadColumn(WMBrowser *bPtr, int column)
785 assert(bPtr->fillColumn);
787 bPtr->flags.loadingColumn = 1;
788 (*bPtr->fillColumn)(bPtr, column);
789 bPtr->flags.loadingColumn = 0;
793 static void
794 paintBrowser(WMBrowser *bPtr)
796 int i;
798 if (!bPtr->view->flags.mapped)
799 return;
801 W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
802 bPtr->view->size.height-SCROLLER_WIDTH-2,
803 bPtr->view->size.width, 22, WRSunken);
805 if (bPtr->flags.isTitled) {
806 for (i=0; i<bPtr->maxVisibleColumns; i++) {
807 drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
813 static void
814 handleEvents(XEvent *event, void *data)
816 WMBrowser *bPtr = (WMBrowser*)data;
818 CHECK_CLASS(data, WC_Browser);
821 switch (event->type) {
822 case Expose:
823 paintBrowser(bPtr);
824 break;
826 case DestroyNotify:
827 destroyBrowser(bPtr);
828 break;
835 static void
836 scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller)
838 int i;
839 int x;
840 int notify = 0;
843 if (column != bPtr->firstVisibleColumn)
844 notify = 1;
846 if (column < 0)
847 column = 0;
849 x = 0;
850 bPtr->firstVisibleColumn = column;
851 for (i = 0; i < bPtr->columnCount; i++) {
852 if (COLUMN_IS_VISIBLE(bPtr, i)) {
853 WMMoveWidget(bPtr->columns[i], x,
854 WMWidgetView(bPtr->columns[i])->pos.y);
855 if (!WMWidgetView(bPtr->columns[i])->flags.realized)
856 WMRealizeWidget(bPtr->columns[i]);
857 WMMapWidget(bPtr->columns[i]);
858 x += bPtr->columnSize.width+COLUMN_SPACING;
859 } else {
860 WMUnmapWidget(bPtr->columns[i]);
864 /* update the scroller */
865 if (updateScroller) {
866 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
867 float value, proportion;
869 value = bPtr->firstVisibleColumn
870 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
871 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
872 WMSetScrollerParameters(bPtr->scroller, value, proportion);
873 } else {
874 WMSetScrollerParameters(bPtr->scroller, 0, 1);
878 if (bPtr->view->flags.mapped)
879 paintBrowser(bPtr);
881 if (notify)
882 WMPostNotificationName(WMBrowserDidScrollNotification, bPtr, NULL);
886 static void
887 listCallback(void *self, void *clientData)
889 WMBrowser *bPtr = (WMBrowser*)clientData;
890 WMList *lPtr = (WMList*)self;
891 WMListItem *item;
892 static WMListItem *oldItem = NULL;
893 int i;
895 item = WMGetListSelectedItem(lPtr);
896 if (!item || oldItem == item)
897 return;
899 for (i=0; i<bPtr->columnCount; i++) {
900 if (lPtr == bPtr->columns[i])
901 break;
903 assert(i<bPtr->columnCount);
905 /* columns at right must be cleared */
906 removeColumn(bPtr, i+1);
907 /* open directory */
908 if (item->isBranch) {
909 WMAddBrowserColumn(bPtr);
910 loadColumn(bPtr, bPtr->usedColumnCount-1);
912 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
913 i = 0;
914 else
915 i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
916 scrollToColumn(bPtr, i, True);
918 /* call callback for click */
919 if (bPtr->action)
920 (*bPtr->action)(bPtr, bPtr->clientData);
922 oldItem = item;
926 static void
927 listDoubleCallback(void *self, void *clientData)
929 WMBrowser *bPtr = (WMBrowser*)clientData;
930 WMList *lPtr = (WMList*)self;
931 WMListItem *item;
933 item = WMGetListSelectedItem(lPtr);
934 if (!item)
935 return;
937 /* call callback for double click */
938 if (bPtr->doubleAction)
939 (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
943 void
944 WMLoadBrowserColumnZero(WMBrowser *bPtr)
946 if (!bPtr->flags.loaded) {
947 /* create column 0 */
948 WMAddBrowserColumn(bPtr);
950 loadColumn(bPtr, 0);
952 /* make column 0 visible */
953 scrollToColumn(bPtr, 0, True);
955 bPtr->flags.loaded = 1;
960 void
961 WMRemoveBrowserItem(WMBrowser *bPtr, int column, int row)
963 WMList *list;
965 if (column < 0 || column >= bPtr->usedColumnCount)
966 return;
968 list = WMGetBrowserListInColumn(bPtr, column);
970 if (row < 0 || row >= WMGetListNumberOfRows(list))
971 return;
973 removeColumn(bPtr, column+1);
974 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
975 scrollToColumn(bPtr, 0, True);
976 else
977 scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
978 True);
980 WMRemoveListItem(list, row);
984 static void
985 listSelectionObserver(void *observerData, WMNotification *notification)
987 WMBrowser *bPtr = (WMBrowser*)observerData;
988 int column, item = (int)WMGetNotificationClientData(notification);
989 WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
991 for (column=0; column<bPtr->usedColumnCount; column++)
992 if (bPtr->columns[column] == lPtr)
993 break;
995 /* this can happen when a list is being cleared with WMClearList
996 * after the column was removed */
997 if (column >= bPtr->usedColumnCount) {
998 return;
1001 if (item < 0)
1002 column--;
1004 bPtr->selectedColumn = column;
1009 WMAddBrowserColumn(WMBrowser *bPtr)
1011 WMList *list;
1012 WMList **clist;
1013 char **tlist;
1014 int colY;
1015 int index;
1018 if (bPtr->usedColumnCount < bPtr->columnCount) {
1019 return bPtr->usedColumnCount++;
1022 bPtr->usedColumnCount++;
1024 if (bPtr->flags.isTitled) {
1025 colY = TITLE_SPACING + bPtr->titleHeight;
1026 } else {
1027 colY = 0;
1030 index = bPtr->columnCount;
1031 bPtr->columnCount++;
1032 clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
1033 tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
1034 memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
1035 memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
1036 if (bPtr->columns)
1037 free(bPtr->columns);
1038 if (bPtr->titles)
1039 free(bPtr->titles);
1040 bPtr->columns = clist;
1041 bPtr->titles = tlist;
1043 bPtr->titles[index] = NULL;
1045 list = WMCreateList(bPtr);
1046 WMSetListAction(list, listCallback, bPtr);
1047 WMSetListDoubleAction(list, listDoubleCallback, bPtr);
1048 WMSetListUserDrawProc(list, paintItem);
1049 WMAddNotificationObserver(listSelectionObserver, bPtr,
1050 WMListSelectionDidChangeNotification, list);
1052 bPtr->columns[index] = list;
1054 WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
1055 WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
1056 if (COLUMN_IS_VISIBLE(bPtr, index))
1057 WMMapWidget(list);
1059 /* update the scroller */
1060 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
1061 float value, proportion;
1063 value = bPtr->firstVisibleColumn
1064 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1065 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1066 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1069 return index;
1074 static void
1075 destroyBrowser(WMBrowser *bPtr)
1077 int i;
1079 for (i=0; i<bPtr->columnCount; i++) {
1080 if (bPtr->titles[i])
1081 free(bPtr->titles[i]);
1083 free(bPtr->titles);
1085 free(bPtr->pathSeparator);
1087 WMRemoveNotificationObserver(bPtr);
1089 free(bPtr);
1093 static char*
1094 createTruncatedString(WMFont *font, char *text, int *textLen, int width)
1096 int dLen = WMWidthOfString(font, ".", 1);
1097 char *textBuf = (char*)wmalloc((*textLen)+4);
1099 if (width >= 3*dLen) {
1100 int dddLen = 3*dLen;
1101 int tmpTextLen = *textLen;
1103 strcpy(textBuf, text);
1104 while (tmpTextLen
1105 && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
1106 tmpTextLen--;
1107 strcpy(textBuf+tmpTextLen, "...");
1108 *textLen = tmpTextLen+3;
1109 } else if (width >= 2*dLen) {
1110 strcpy(textBuf, "..");
1111 *textLen = 2;
1112 } else if (width >= dLen) {
1113 strcpy(textBuf, ".");
1114 *textLen = 1;
1115 } else {
1116 *textBuf = '\0';
1117 *textLen = 0;
1119 return textBuf;