Enable silent-rules for compilation
[fsviewer.git] / src / FSBrowser.c
blob507f224bf17477b49b4b6d551e9ef0675aa463cb
1 #include <WMaker.h>
2 #include <WINGs/WINGsP.h>
3 #include <math.h> /* for : double rint (double) */
5 #include "FSBrowser.h"
7 char *FSBrowserDidScrollNotification = "FSBrowserDidScrollNotification";
10 typedef struct W_FSBrowser {
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 FSBrowserFillColumnProc *fillColumn;
37 FSBrowserGetItemNameProc *parseItem;
39 WMScroller *scroller;
41 char *pathSeparator;
43 struct {
44 WMReliefType relief:3;
45 unsigned int isTitled:1;
46 unsigned int allowMultipleSelection:1;
47 unsigned int hasScroller:1;
48 unsigned int emptyCol:1;
50 /* */
51 unsigned int loaded:1;
52 unsigned int loadingColumn:1;
53 } flags;
54 } _FSBrowser;
57 #define COLUMN_SPACING 4
58 #define TITLE_SPACING 2
60 #define DEFAULT_WIDTH 305
61 #define DEFAULT_HEIGHT 200
62 #define DEFAULT_HAS_SCROLLER True
63 #define DEFAULT_TITLE_HEIGHT 20
64 #define DEFAULT_IS_TITLED True
65 #define DEFAULT_MAX_VISIBLE_COLUMNS 2
66 #define DEFAULT_SEPARATOR "/"
67 #define DEFAULT_RELIEF WRSunken
69 #define MIN_VISIBLE_COLUMNS 1
70 #define MAX_VISIBLE_COLUMNS 32
73 #define COLUMN_IS_VISIBLE(b, c) ((c) >= (b)->firstVisibleColumn \
74 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
77 static void handleEvents(XEvent *event, void *data);
78 static void destroyBrowser(FSBrowser *bPtr);
80 static void setupScroller(FSBrowser *bPtr);
82 static void scrollToColumn(FSBrowser *bPtr, int column, Bool updateScroller);
84 static void paintItem(WMList *lPtr, int index, Drawable d, char *text,
85 int state, WMRect *rect);
87 static void loadColumn(FSBrowser *bPtr, int column);
89 static void removeColumn(FSBrowser *bPtr, int column);
90 static void paintBrowser(FSBrowser *bPtr);
92 static char*
93 createTruncatedString(WMFont *font, char *text, int *textLen, int width);
95 static void willResizeFSBrowser(W_ViewDelegate *self, WMView *view,
96 unsigned int *width, unsigned int *height);
97 /* static void resizeFSBrowser(WMWidget*, unsigned int, unsigned int); */
99 /* W_ViewProcedureTable _FSBrowserViewProcedures = { */
100 /* NULL, */
101 /* resizeFSBrowser, */
102 /* NULL */
103 /* }; */
105 static W_ViewDelegate _FSBrowserViewDelegate = {
106 NULL,
107 NULL,
108 NULL,
109 NULL,
110 willResizeFSBrowser
115 // Widget class ID.
116 static W_Class fsBrowserWidgetClass = 0;
118 W_Class InitFSBrowser(WMScreen *scr)
120 // Register the widget with WINGs and get the widget class ID.
121 if (!(fsBrowserWidgetClass))
123 fsBrowserWidgetClass =
124 /* W_RegisterUserWidget(&_FSBrowserViewProcedures); */
125 W_RegisterUserWidget();
128 return fsBrowserWidgetClass;
131 FSBrowser*
132 FSCreateBrowser(WMWidget *parent)
134 FSBrowser *bPtr;
135 int i;
137 wassertrv(parent, NULL);
139 bPtr = wmalloc(sizeof(FSBrowser));
140 memset(bPtr, 0, sizeof(FSBrowser));
142 bPtr->widgetClass = fsBrowserWidgetClass/* WC_Browser */;
144 bPtr->view = W_CreateView(W_VIEW(parent));
145 if (!bPtr->view) {
146 free(bPtr);
147 return NULL;
149 bPtr->view->self = bPtr;
150 bPtr->view->delegate = &_FSBrowserViewDelegate;
152 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask
153 |ClientMessageMask, handleEvents, bPtr);
155 /* default configuration */
156 bPtr->flags.hasScroller = DEFAULT_HAS_SCROLLER;
158 bPtr->titleHeight = DEFAULT_TITLE_HEIGHT;
159 bPtr->flags.isTitled = DEFAULT_IS_TITLED;
160 bPtr->flags.emptyCol = 1;
161 bPtr->maxVisibleColumns = DEFAULT_MAX_VISIBLE_COLUMNS;
163 WMResizeWidget(bPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
164 /* resizeFSBrowser(bPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT); */
166 bPtr->pathSeparator = wstrdup(DEFAULT_SEPARATOR);
168 if (bPtr->flags.hasScroller)
169 setupScroller(bPtr);
171 for (i=0; i<bPtr->maxVisibleColumns; i++) {
172 FSAddBrowserColumn(bPtr);
174 bPtr->usedColumnCount = 0;
176 bPtr->selectedColumn = -1;
178 return bPtr;
182 void
183 FSSetBrowserMaxVisibleColumns(FSBrowser *bPtr, int columns)
185 int curMaxVisibleColumns;
186 int newFirstVisibleColumn = 0;
188 assert ((int) bPtr);
190 columns = (columns < MIN_VISIBLE_COLUMNS) ? MIN_VISIBLE_COLUMNS : columns;
191 columns = (columns > MAX_VISIBLE_COLUMNS) ? MAX_VISIBLE_COLUMNS : columns;
192 if (columns == bPtr->maxVisibleColumns) {
193 return;
195 curMaxVisibleColumns = bPtr->maxVisibleColumns;
196 bPtr->maxVisibleColumns = columns;
197 /* browser not loaded */
198 if (!bPtr->flags.loaded) {
199 if ((columns > curMaxVisibleColumns) && (columns > bPtr->columnCount)) {
200 int i = columns - bPtr->columnCount;
201 bPtr->usedColumnCount = bPtr->columnCount;
202 while (i--) {
203 FSAddBrowserColumn(bPtr);
205 bPtr->usedColumnCount = 0;
207 /* browser loaded and columns > curMaxVisibleColumns */
208 } else if (columns > curMaxVisibleColumns) {
209 if (bPtr->usedColumnCount > columns) {
210 newFirstVisibleColumn = bPtr->usedColumnCount - columns;
212 if (newFirstVisibleColumn > bPtr->firstVisibleColumn) {
213 newFirstVisibleColumn = bPtr->firstVisibleColumn;
215 if (columns > bPtr->columnCount) {
216 int i = columns - bPtr->columnCount;
217 int curUsedColumnCount = bPtr->usedColumnCount;
218 bPtr->usedColumnCount = bPtr->columnCount;
219 while (i--) {
220 FSAddBrowserColumn(bPtr);
222 bPtr->usedColumnCount = curUsedColumnCount;
224 /* browser loaded and columns < curMaxVisibleColumns */
225 } else {
226 newFirstVisibleColumn = bPtr->firstVisibleColumn;
227 if (newFirstVisibleColumn + columns >= bPtr->usedColumnCount) {
228 removeColumn(bPtr, newFirstVisibleColumn + columns);
231 WMResizeWidget(bPtr, bPtr->view->size.width, bPtr->view->size.height);
232 /* resizeFSBrowser(bPtr, bPtr->view->size.width, bPtr->view->size.height); */
233 if (bPtr->flags.loaded) {
234 XClearArea(bPtr->view->screen->display, bPtr->view->window, 0, 0,
235 bPtr->view->size.width, bPtr->titleHeight, False);
236 scrollToColumn (bPtr, newFirstVisibleColumn, True);
241 int
242 FSGetBrowserNumberOfColumns(FSBrowser *bPtr)
244 return bPtr->usedColumnCount;
247 void
248 FSSetBrowserPathSeparator(FSBrowser *bPtr, char *separator)
250 if (bPtr->pathSeparator)
251 free(bPtr->pathSeparator);
252 bPtr->pathSeparator = wstrdup(separator);
257 static void
258 drawTitleOfColumn(FSBrowser *bPtr, int column)
260 WMScreen *scr = bPtr->view->screen;
261 int x;
263 x=(column-bPtr->firstVisibleColumn)*(bPtr->columnSize.width+COLUMN_SPACING);
265 XFillRectangle(scr->display, bPtr->view->window, WMColorGC(scr->darkGray), x, 0,
266 bPtr->columnSize.width, bPtr->titleHeight);
267 W_DrawRelief(scr, bPtr->view->window, x, 0,
268 bPtr->columnSize.width, bPtr->titleHeight, WRSunken);
270 if (column < bPtr->usedColumnCount && bPtr->titles[column]) {
271 int titleLen = strlen(bPtr->titles[column]);
272 int widthC = bPtr->columnSize.width-8;
274 if (WMWidthOfString(scr->boldFont, bPtr->titles[column], titleLen)
275 > widthC) {
276 char *titleBuf = createTruncatedString(scr->boldFont,
277 bPtr->titles[column],
278 &titleLen, widthC);
279 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
280 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
281 bPtr->columnSize.width, WACenter, WMWhiteColor(scr),
282 False, titleBuf, titleLen);
283 free (titleBuf);
284 } else {
285 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
286 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
287 bPtr->columnSize.width, WACenter, WMWhiteColor(scr),
288 False, bPtr->titles[column], titleLen);
294 void
295 FSSetBrowserColumnTitle(FSBrowser *bPtr, int column, char *title)
297 assert(column >= 0);
298 assert(column < bPtr->usedColumnCount);
300 if (bPtr->titles[column])
301 free(bPtr->titles[column]);
303 bPtr->titles[column] = wstrdup(title);
305 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
306 drawTitleOfColumn(bPtr, column);
311 WMList*
312 FSGetBrowserListInColumn(FSBrowser *bPtr, int column)
314 if (column < 0 || column >= bPtr->usedColumnCount)
315 return NULL;
317 return bPtr->columns[column];
321 void
322 FSSetBrowserFillColumnProc(FSBrowser *bPtr, FSBrowserFillColumnProc *proc)
324 bPtr->fillColumn = proc;
328 void
329 FSSetBrowserGetItemNameProc(FSBrowser *bPtr, FSBrowserGetItemNameProc *proc)
331 bPtr->parseItem = proc;
335 int
336 FSGetBrowserFirstVisibleColumn(FSBrowser *bPtr)
338 return bPtr->firstVisibleColumn;
342 static void
343 removeColumn(FSBrowser *bPtr, int column)
345 int i, clearEnd, destroyEnd;
346 WMList **clist;
347 char **tlist;
349 assert ((int) bPtr);
351 column = (column < 0) ? 0 : column;
352 if (column >= bPtr->columnCount) {
353 return;
355 if (column < bPtr->maxVisibleColumns) {
356 clearEnd = bPtr->maxVisibleColumns;
357 destroyEnd = bPtr->columnCount;
358 bPtr->columnCount = bPtr->maxVisibleColumns;
359 } else {
360 clearEnd = column;
361 destroyEnd = bPtr->columnCount;
362 bPtr->columnCount = column;
364 if (column < bPtr->usedColumnCount) {
365 bPtr->usedColumnCount = column;
367 for (i=column; i < clearEnd; i++) {
368 if (bPtr->titles[i]) {
369 free(bPtr->titles[i]);
370 bPtr->titles[i] = NULL;
372 WMClearList(bPtr->columns[i]);
374 for (;i < destroyEnd; i++)
376 int j = 0;
377 int numRows = 0;
378 WMListItem *item = NULL;
380 if (bPtr->titles[i]) {
381 free(bPtr->titles[i]);
382 bPtr->titles[i] = NULL;
384 WMRemoveNotificationObserverWithName(bPtr,
385 WMListSelectionDidChangeNotification,
386 bPtr->columns[i]);
389 * Free the client data.
390 * This assumes is it a string or the like.
391 * Hopefully it won't slow things down too much.
392 * Should use a callback to allow for more complicated
393 * clientData structures.
395 /* numRows = WMGetListNumberOfRows(bPtr->columns[i]); */
396 /* for(j = 0; j < numRows; j++) */
397 /* { */
399 /* item = WMGetListItem(bPtr->columns[i], j); */
400 /* if(item->clientData) */
401 /* { */
402 /* free(item->clientData); */
403 /* item->clientData = NULL; */
404 /* } */
405 /* } */
406 WMDestroyWidget(bPtr->columns[i]);
407 bPtr->columns[i] = NULL;
409 clist = wmalloc(sizeof(WMList*) * (bPtr->columnCount));
410 tlist = wmalloc(sizeof(char*) * (bPtr->columnCount));
411 memcpy(clist, bPtr->columns, sizeof(WMList*) * (bPtr->columnCount));
412 memcpy(tlist, bPtr->titles, sizeof(char*) * (bPtr->columnCount));
413 free(bPtr->titles);
414 free(bPtr->columns);
415 bPtr->titles = tlist;
416 bPtr->columns = clist;
420 WMListItem*
421 FSGetBrowserSelectedItemInColumn(FSBrowser *bPtr, int column)
423 if ((column < 0) || (column >= bPtr->usedColumnCount))
424 return NULL;
426 return WMGetListSelectedItem(bPtr->columns[column]);
432 FSGetBrowserSelectedColumn(FSBrowser *bPtr)
434 return bPtr->selectedColumn;
439 FSGetBrowserSelectedRowInColumn(FSBrowser *bPtr, int column)
441 if (column >= 0 && column < bPtr->columnCount) {
442 return WMGetListSelectedItemRow(bPtr->columns[column]);
443 } else {
444 return -1;
449 void
450 FSSetBrowserTitled(FSBrowser *bPtr, Bool flag)
452 int i;
453 int columnX, columnY;
455 if (bPtr->flags.isTitled == flag)
456 return;
458 columnX = 0;
460 if (!bPtr->flags.isTitled) {
461 columnY = TITLE_SPACING + bPtr->titleHeight;
463 bPtr->columnSize.height -= columnY;
465 for (i=0; i<bPtr->columnCount; i++) {
466 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
467 bPtr->columnSize.height);
469 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
471 WMMoveWidget(bPtr->columns[i], columnX, columnY);
473 } else {
474 bPtr->columnSize.height += TITLE_SPACING + bPtr->titleHeight;
476 for (i=0; i<bPtr->columnCount; i++) {
477 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
478 bPtr->columnSize.height);
480 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
482 WMMoveWidget(bPtr->columns[i], columnX, 0);
486 bPtr->flags.isTitled = flag;
490 /* WMListItem* */
491 /* FSAddSortedBrowserItem(FSBrowser *bPtr, int column, char *text, Bool isBranch) */
492 /* { */
493 /* WMListItem *item; */
495 /* if (column < 0 || column >= bPtr->columnCount) */
496 /* return NULL; */
498 /* item = WMAddSortedListItem(bPtr->columns[column], text); */
499 /* item->isBranch = isBranch; */
501 /* return item; */
502 /* } */
506 WMListItem*
507 FSInsertBrowserItem(FSBrowser *bPtr, int column, int row, char *text,
508 Bool isBranch)
510 WMListItem *item;
512 if (column < 0 || column >= bPtr->columnCount)
513 return NULL;
515 item = WMInsertListItem(bPtr->columns[column], row, text);
516 item->isBranch = isBranch;
518 return item;
522 void
523 FSSetBrowserRelief(FSBrowser *bPtr, WMReliefType relief)
525 bPtr->flags.relief = relief;
526 if (bPtr->view->flags.realized) {
527 paintBrowser(bPtr);
532 static void
533 willResizeFSBrowser(W_ViewDelegate *self, WMView *view,
534 unsigned int *width, unsigned int *height)
536 FSBrowser *bPtr = (FSBrowser*)view->self;
537 int cols = bPtr->maxVisibleColumns;
538 int colX, colY;
539 int i;
541 assert(*width > 0);
542 assert(*height > 0);
544 bPtr->columnSize.width = (*width-(cols-1)*COLUMN_SPACING) / cols;
545 bPtr->columnSize.height = *height;
547 if (bPtr->flags.isTitled) {
548 colY = TITLE_SPACING + bPtr->titleHeight;
549 bPtr->columnSize.height -= colY;
550 } else {
551 colY = 0;
554 if (bPtr->flags.hasScroller) {
555 bPtr->columnSize.height -= SCROLLER_WIDTH + 4;
557 if (bPtr->scroller) {
558 WMResizeWidget(bPtr->scroller, *width-2, 1);
559 WMMoveWidget(bPtr->scroller, 1, *height-SCROLLER_WIDTH-1);
563 colX = 0;
564 for (i = 0; i < bPtr->columnCount; i++) {
565 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
566 bPtr->columnSize.height);
568 WMMoveWidget(bPtr->columns[i], colX, colY);
570 if (COLUMN_IS_VISIBLE(bPtr, i)) {
571 colX += bPtr->columnSize.width+COLUMN_SPACING;
575 /* W_ResizeView(bPtr->view, *width, *height); */
577 /* static void */
578 /* resizeFSBrowser(WMWidget *w, unsigned int width, unsigned int height) */
579 /* { */
580 /* FSBrowser *bPtr = (FSBrowser*)w; */
581 /* int cols = bPtr->maxVisibleColumns; */
582 /* int colX, colY; */
583 /* int i; */
585 /* assert(width > 0); */
586 /* assert(height > 0); */
588 /* bPtr->columnSize.width = (width-(cols-1)*COLUMN_SPACING) / cols; */
589 /* bPtr->columnSize.height = height; */
591 /* if (bPtr->flags.isTitled) { */
592 /* colY = TITLE_SPACING + bPtr->titleHeight; */
593 /* bPtr->columnSize.height -= colY; */
594 /* } else { */
595 /* colY = 0; */
596 /* } */
598 /* if (bPtr->flags.hasScroller) { */
599 /* bPtr->columnSize.height -= SCROLLER_WIDTH + 4; */
601 /* if (bPtr->scroller) { */
602 /* WMResizeWidget(bPtr->scroller, width-2, 1); */
603 /* WMMoveWidget(bPtr->scroller, 1, height-SCROLLER_WIDTH-1); */
604 /* } */
605 /* } */
607 /* colX = 0; */
608 /* for (i = 0; i < bPtr->columnCount; i++) { */
609 /* WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width, */
610 /* bPtr->columnSize.height); */
612 /* WMMoveWidget(bPtr->columns[i], colX, colY); */
614 /* if (COLUMN_IS_VISIBLE(bPtr, i)) { */
615 /* colX += bPtr->columnSize.width+COLUMN_SPACING; */
616 /* } */
617 /* } */
619 /* W_ResizeView(bPtr->view, width, height); */
620 /* } */
623 static void
624 paintItem(WMList *lPtr, int index, Drawable d, char *text, int state,
625 WMRect *rect)
627 WMView *view = W_VIEW(lPtr);
628 W_Screen *scr = view->screen;
629 int width, height, x, y;
630 /* WMListItem *item; */
632 /* item = WMGetListItem(lPtr, index); */
634 width = rect->size.width;
635 height = rect->size.height;
636 x = rect->pos.x;
637 y = rect->pos.y;
639 if (state & WLDSSelected)
640 WMPaintColorSwatch(WMWhiteColor(scr), d, x, y,
641 width, height);
642 else
643 WMPaintColorSwatch(WMGetWidgetBackgroundColor(lPtr), d, x, y,
644 width, height);
646 if (text) {
647 /* Avoid overlaping... */
648 int textLen = strlen(text);
649 int widthC = (state & WLDSIsBranch) ? width-20 : width-8;
650 if (WMWidthOfString(scr->normalFont, text, textLen) > widthC) {
651 char *textBuf = createTruncatedString(scr->normalFont,
652 text, &textLen, widthC);
653 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
654 WALeft, WMBlackColor(scr), False, textBuf, textLen);
655 free(textBuf);
656 } else {
657 W_PaintText(view, d, scr->normalFont, x+4, y, widthC,
658 WALeft, WMBlackColor(scr), False, text, textLen);
662 if (state & WLDSIsBranch) {
663 XDrawLine(scr->display, d, WMColorGC(scr->darkGray), x+width-11, y+3,
664 x+width-6, y+height/2);
665 if (state & WLDSSelected)
666 XDrawLine(scr->display, d,WMColorGC(scr->gray), x+width-11, y+height-5,
667 x+width-6, y+height/2);
668 else
669 XDrawLine(scr->display, d,WMColorGC(scr->white), x+width-11, y+height-5,
670 x+width-6, y+height/2);
671 XDrawLine(scr->display, d, WMColorGC(scr->black), x+width-12, y+3,
672 x+width-12, y+height-5);
677 static void
678 scrollCallback(WMWidget *scroller, void *self)
680 FSBrowser *bPtr = (FSBrowser*)self;
681 WMScroller *sPtr = (WMScroller*)scroller;
682 int newFirst;
683 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
685 switch (WMGetScrollerHitPart(sPtr)) {
686 case WSDecrementLine:
687 if (bPtr->firstVisibleColumn > 0) {
688 scrollToColumn(bPtr, bPtr->firstVisibleColumn-1, True);
690 break;
692 case WSDecrementPage:
693 if (bPtr->firstVisibleColumn > 0) {
694 newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
696 scrollToColumn(bPtr, newFirst, True);
698 break;
701 case WSIncrementLine:
702 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
703 scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
705 break;
707 case WSIncrementPage:
708 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
709 newFirst = bPtr->firstVisibleColumn + bPtr->maxVisibleColumns;
711 if (newFirst+bPtr->maxVisibleColumns >= bPtr->columnCount)
712 newFirst = bPtr->columnCount - bPtr->maxVisibleColumns;
714 scrollToColumn(bPtr, newFirst, True);
716 break;
718 case WSKnob:
720 double floatValue;
721 double value = bPtr->columnCount - bPtr->maxVisibleColumns;
723 floatValue = WMGetScrollerValue(bPtr->scroller);
725 floatValue = (floatValue*value)/value;
727 newFirst = rint(floatValue*(float)(bPtr->columnCount - bPtr->maxVisibleColumns));
729 if (bPtr->firstVisibleColumn != newFirst)
730 scrollToColumn(bPtr, newFirst, False);
731 /* else
732 WMSetScrollerParameters(bPtr->scroller, floatValue,
733 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
737 break;
739 case WSKnobSlot:
740 case WSNoPart:
741 /* do nothing */
742 break;
744 #undef LAST_VISIBLE_COLUMN
748 static void
749 setupScroller(FSBrowser *bPtr)
751 WMScroller *sPtr;
752 int y;
754 y = bPtr->view->size.height - SCROLLER_WIDTH - 1;
756 sPtr = WMCreateScroller(bPtr);
757 WMSetScrollerAction(sPtr, scrollCallback, bPtr);
758 WMMoveWidget(sPtr, 1, y);
759 WMResizeWidget(sPtr, bPtr->view->size.width-2, SCROLLER_WIDTH);
761 bPtr->scroller = sPtr;
763 WMMapWidget(sPtr);
767 void
768 FSSetBrowserAction(FSBrowser *bPtr, WMAction *action, void *clientData)
770 bPtr->action = action;
771 bPtr->clientData = clientData;
775 void
776 FSSetBrowserDoubleAction(FSBrowser *bPtr, WMAction *action, void *clientData)
778 bPtr->doubleAction = action;
779 bPtr->doubleClientData = clientData;
783 void
784 FSSetBrowserHasScroller(FSBrowser *bPtr, int hasScroller)
786 bPtr->flags.hasScroller = hasScroller;
788 if(bPtr->flags.hasScroller)
789 WMMapWidget(bPtr->scroller);
790 else
791 WMUnmapWidget(bPtr->scroller);
796 char*
797 FSSetBrowserPath(FSBrowser *bPtr, char *path)
799 int i;
800 char *str = wstrdup(path);
801 char *tmp, *retPtr = NULL;
802 int item;
803 WMListItem *listItem;
805 /* WMLoadBrowserColumnZero must be call first */
806 if (!bPtr->flags.loaded) {
807 return False;
810 removeColumn(bPtr, 1);
812 i = 0;
813 tmp = strtok(str, bPtr->pathSeparator);
814 while (tmp) {
815 /* select it in the column */
816 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
817 if (item<0) {
818 retPtr = &path[(int)(tmp - str)];
819 break;
821 WMSelectListItem(bPtr->columns[i], item);
822 WMSetListPosition(bPtr->columns[i], item);
824 listItem = WMGetListItem(bPtr->columns[i], item);
825 if (!listItem || (!listItem->isBranch && !bPtr->flags.emptyCol) ) {
826 break;
829 /* load next column */
830 FSAddBrowserColumn(bPtr);
832 loadColumn(bPtr, i+1);
834 tmp = strtok(NULL, bPtr->pathSeparator);
835 i++;
837 free(str);
839 for (i = bPtr->usedColumnCount - 1;
840 (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
841 i--);
843 bPtr->selectedColumn = i;
845 if (bPtr->columnCount < bPtr->maxVisibleColumns) {
846 int i = bPtr->maxVisibleColumns - bPtr->columnCount;
847 int curUsedColumnCount = bPtr->usedColumnCount;
848 bPtr->usedColumnCount = bPtr->columnCount;
849 while (i--) {
850 FSAddBrowserColumn(bPtr);
852 bPtr->usedColumnCount = curUsedColumnCount;
855 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
857 return retPtr;
861 char*
862 FSGetBrowserPath(FSBrowser *bPtr)
864 return FSGetBrowserPathToColumn(bPtr, bPtr->columnCount);
868 char*
869 FSGetBrowserPathToColumn(FSBrowser *bPtr, int column)
871 int i, size;
872 char *path;
873 WMListItem *item;
875 if (column >= bPtr->usedColumnCount)
876 column = bPtr->usedColumnCount-1;
878 if (column < 0) {
879 return wstrdup(bPtr->pathSeparator);
882 /* calculate size of buffer */
883 size = 0;
884 for (i = 0; i <= column; i++) {
885 item = WMGetListSelectedItem(bPtr->columns[i]);
886 if (!item)
887 break;
888 /* if(bPtr->parseItem) */
889 /* size += strlen((*bPtr->parseItem)(item)); */
890 /* else */
891 size += strlen(item->text);
894 /* get the path */
895 path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
896 /* ignore first / */
897 *path = 0;
898 for (i = 0; i <= column; i++) {
899 strcat(path, bPtr->pathSeparator);
900 item = WMGetListSelectedItem(bPtr->columns[i]);
901 if (!item)
902 break;
903 /* if(bPtr->parseItem) */
904 /* strcat(path, (*bPtr->parseItem)(item)); */
905 /* else */
906 strcat(path, item->text);
909 return path;
913 static void
914 loadColumn(FSBrowser *bPtr, int column)
916 assert(bPtr->fillColumn);
917 bPtr->flags.loadingColumn = 1;
918 (*bPtr->fillColumn)(bPtr, column);
919 bPtr->flags.loadingColumn = 0;
923 static void
924 paintBrowser(FSBrowser *bPtr)
926 int i;
928 if (!bPtr->view->flags.mapped)
929 return;
931 if(bPtr->flags.hasScroller)
932 W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
933 bPtr->view->size.height-SCROLLER_WIDTH-2,
934 bPtr->view->size.width, 22, bPtr->flags.relief);
936 if (bPtr->flags.isTitled) {
937 for (i=0; i<bPtr->maxVisibleColumns; i++) {
938 drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
944 static void
945 handleEvents(XEvent *event, void *data)
947 FSBrowser *bPtr = (FSBrowser*)data;
949 /* CHECK_CLASS(data, fsBrowserWidgetClass); */
952 switch (event->type) {
953 case Expose:
954 paintBrowser(bPtr);
955 break;
957 case DestroyNotify:
958 destroyBrowser(bPtr);
959 break;
966 static void
967 scrollToColumn(FSBrowser *bPtr, int column, Bool updateScroller)
969 int i;
970 int x;
971 int notify = 0;
974 if (column != bPtr->firstVisibleColumn)
975 notify = 1;
977 if (column < 0)
978 column = 0;
980 x = 0;
981 bPtr->firstVisibleColumn = column;
982 for (i = 0; i < bPtr->columnCount; i++) {
983 if (COLUMN_IS_VISIBLE(bPtr, i)) {
984 WMMoveWidget(bPtr->columns[i], x,
985 WMWidgetView(bPtr->columns[i])->pos.y);
986 if (!WMWidgetView(bPtr->columns[i])->flags.realized)
987 WMRealizeWidget(bPtr->columns[i]);
988 WMMapWidget(bPtr->columns[i]);
989 x += bPtr->columnSize.width + COLUMN_SPACING;
990 } else {
991 WMUnmapWidget(bPtr->columns[i]);
995 /* update the scroller */
996 if (updateScroller) {
997 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
998 float value, proportion;
1000 value = bPtr->firstVisibleColumn
1001 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1002 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1003 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1004 } else {
1005 WMSetScrollerParameters(bPtr->scroller, 0, 1);
1009 if (bPtr->view->flags.mapped)
1010 paintBrowser(bPtr);
1012 if (notify)
1014 WMPostNotificationName(FSBrowserDidScrollNotification, bPtr, NULL);
1019 static void
1020 listCallback(void *self, void *clientData)
1022 FSBrowser *bPtr = (FSBrowser*)clientData;
1023 WMList *lPtr = (WMList*)self;
1024 WMListItem *item;
1025 static WMListItem *oldItem = NULL;
1026 int i;
1028 item = WMGetListSelectedItem(lPtr);
1029 if (!item) {
1030 oldItem = item;
1031 return;
1034 if (oldItem != item) {
1035 for (i=0; i<bPtr->columnCount; i++) {
1036 if (lPtr == bPtr->columns[i])
1037 break;
1039 assert(i<bPtr->columnCount);
1041 bPtr->selectedColumn = i;
1043 /* columns at right must be cleared */
1044 removeColumn(bPtr, i+1);
1045 /* open directory */
1046 if (item->isBranch || bPtr->flags.emptyCol) {
1047 FSAddBrowserColumn(bPtr);
1049 if(bPtr->flags.emptyCol)
1051 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
1052 i = 0;
1054 if(!COLUMN_IS_VISIBLE(bPtr, i+1))
1056 scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
1058 else
1060 scrollToColumn(bPtr, bPtr->firstVisibleColumn, True);
1063 loadColumn(bPtr, bPtr->usedColumnCount-1);
1065 else
1067 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
1068 i = 0;
1069 else
1070 i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
1072 scrollToColumn(bPtr, i, True);
1073 if (item->isBranch)
1075 loadColumn(bPtr, bPtr->usedColumnCount-1);
1079 /* call callback for click */
1080 if (bPtr->action)
1081 (*bPtr->action)(bPtr, bPtr->clientData);
1086 oldItem = item;
1090 static void
1091 listDoubleCallback(void *self, void *clientData)
1093 FSBrowser *bPtr = (FSBrowser*)clientData;
1094 WMList *lPtr = (WMList*)self;
1095 WMListItem *item;
1097 item = WMGetListSelectedItem(lPtr);
1098 if (!item)
1099 return;
1101 /* call callback for double click */
1102 if (bPtr->doubleAction)
1103 (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
1107 void
1108 FSLoadBrowserColumnZero(FSBrowser *bPtr)
1110 if (!bPtr->flags.loaded) {
1111 /* create column 0 */
1112 FSAddBrowserColumn(bPtr);
1114 loadColumn(bPtr, 0);
1116 /* make column 0 visible */
1117 scrollToColumn(bPtr, 0, True);
1119 bPtr->flags.loaded = 1;
1121 else
1123 removeColumn(bPtr, 0);
1124 FSAddBrowserColumn(bPtr);
1126 loadColumn(bPtr, 0);
1127 /* make column 0 visible */
1128 scrollToColumn(bPtr, 0, True);
1133 void
1134 FSRemoveBrowserItem(FSBrowser *bPtr, int column, int row)
1136 WMList *list;
1138 if (column < 0 || column >= bPtr->usedColumnCount)
1139 return;
1141 list = FSGetBrowserListInColumn(bPtr, column);
1143 if (row < 0 || row >= WMGetListNumberOfRows(list))
1144 return;
1146 removeColumn(bPtr, column+1);
1147 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
1148 scrollToColumn(bPtr, 0, True);
1149 else
1150 scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
1151 True);
1153 WMRemoveListItem(list, row);
1157 static void
1158 listSelectionObserver(void *observerData, WMNotification *notification)
1160 FSBrowser *bPtr = (FSBrowser*)observerData;
1161 int column, item = (int)WMGetNotificationClientData(notification);
1162 WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
1164 for (column=0; column<bPtr->usedColumnCount; column++)
1165 if (bPtr->columns[column] == lPtr)
1166 break;
1168 /* this can happen when a list is being cleared with WMClearList
1169 * after the column was removed */
1170 if (column >= bPtr->usedColumnCount) {
1171 return;
1174 if (item < 0)
1175 column--;
1177 bPtr->selectedColumn = column;
1182 FSAddBrowserColumn(FSBrowser *bPtr)
1184 WMList *list;
1185 WMList **clist;
1186 char **tlist;
1187 int colY;
1188 int index;
1190 if (bPtr->usedColumnCount < bPtr->columnCount) {
1191 return bPtr->usedColumnCount++;
1194 bPtr->usedColumnCount++;
1196 if (bPtr->flags.isTitled) {
1197 colY = TITLE_SPACING + bPtr->titleHeight;
1198 } else {
1199 colY = 0;
1202 index = bPtr->columnCount;
1203 bPtr->columnCount++;
1204 clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
1205 tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
1206 memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
1207 memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
1208 if (bPtr->columns)
1209 free(bPtr->columns);
1210 if (bPtr->titles)
1211 free(bPtr->titles);
1212 bPtr->columns = clist;
1213 bPtr->titles = tlist;
1215 bPtr->titles[index] = NULL;
1217 list = WMCreateList(bPtr);
1218 WMSetListAction(list, listCallback, bPtr);
1219 WMSetListDoubleAction(list, listDoubleCallback, bPtr);
1220 WMSetListUserDrawProc(list, paintItem);
1221 WMAddNotificationObserver(listSelectionObserver, bPtr,
1222 WMListSelectionDidChangeNotification, list);
1224 bPtr->columns[index] = list;
1226 WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
1227 WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
1228 if (COLUMN_IS_VISIBLE(bPtr, index))
1229 WMMapWidget(list);
1231 /* update the scroller */
1232 if (bPtr->columnCount > bPtr->maxVisibleColumns)
1234 float value, proportion;
1236 value = bPtr->firstVisibleColumn
1237 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1238 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1239 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1242 return index;
1247 static void
1248 destroyBrowser(FSBrowser *bPtr)
1250 int i;
1252 for (i=0; i<bPtr->columnCount; i++) {
1253 if (bPtr->titles[i])
1254 free(bPtr->titles[i]);
1256 free(bPtr->titles);
1258 free(bPtr->pathSeparator);
1260 WMRemoveNotificationObserver(bPtr);
1262 free(bPtr);
1266 static char*
1267 createTruncatedString(WMFont *font, char *text, int *textLen, int width)
1269 int dLen = WMWidthOfString(font, ".", 1);
1270 char *textBuf = (char*)wmalloc((*textLen)+4);
1272 if (width >= 3*dLen) {
1273 int dddLen = 3*dLen;
1274 int tmpTextLen = *textLen;
1276 strcpy(textBuf, text);
1277 while (tmpTextLen
1278 && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
1279 tmpTextLen--;
1280 strcpy(textBuf+tmpTextLen, "...");
1281 *textLen = tmpTextLen+3;
1282 } else if (width >= 2*dLen) {
1283 strcpy(textBuf, "..");
1284 *textLen = 2;
1285 } else if (width >= dLen) {
1286 strcpy(textBuf, ".");
1287 *textLen = 1;
1288 } else {
1289 *textBuf = '\0';
1290 *textLen = 0;
1292 return textBuf;
1295 void
1296 FSScrollBrowserToColumn(FSBrowser *bPtr, int column, Bool updateScroller)
1298 scrollToColumn(bPtr, column, updateScroller);
1301 void FSSetBrowserDisplayFileCol(FSBrowser *bPtr, unsigned int display)
1303 bPtr->flags.emptyCol = display;