- Added double buffering when drawing a WMFrame title with an AA font to avoid
[wmaker-crm.git] / WINGs / wbrowser.c
blobabba3eed6a55cc366acada8ef2e5aacac4c1f5be
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 allowEmptySelection:1;
46 unsigned int hasScroller:1;
48 /* */
49 unsigned int loaded:1;
50 unsigned int loadingColumn:1;
51 } flags;
52 } Browser;
55 #define COLUMN_SPACING 4
56 #define TITLE_SPACING 2
58 #define DEFAULT_WIDTH 305
59 #define DEFAULT_HEIGHT 200
60 #define DEFAULT_HAS_SCROLLER True
61 #define DEFAULT_TITLE_HEIGHT 20
62 #define DEFAULT_IS_TITLED True
63 #define DEFAULT_MAX_VISIBLE_COLUMNS 2
64 #define DEFAULT_SEPARATOR "/"
66 #define MIN_VISIBLE_COLUMNS 1
67 #define MAX_VISIBLE_COLUMNS 32
70 #define COLUMN_IS_VISIBLE(b, c) ((c) >= (b)->firstVisibleColumn \
71 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
74 static void handleEvents(XEvent *event, void *data);
75 static void destroyBrowser(WMBrowser *bPtr);
77 static void setupScroller(WMBrowser *bPtr);
79 static void scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller);
81 static void paintItem(WMList *lPtr, int index, Drawable d, char *text,
82 int state, WMRect *rect);
84 static void loadColumn(WMBrowser *bPtr, int column);
86 static void removeColumn(WMBrowser *bPtr, int column);
88 static char* createTruncatedString(WMFont *font, char *text, int *textLen,
89 int width);
91 static void willResizeBrowser(W_ViewDelegate*, WMView*,
92 unsigned int*, unsigned int*);
94 W_ViewDelegate _BrowserViewDelegate = {
95 NULL,
96 NULL,
97 NULL,
98 NULL,
99 willResizeBrowser
104 WMBrowser*
105 WMCreateBrowser(WMWidget *parent)
107 WMBrowser *bPtr;
108 int i;
110 wassertrv(parent, NULL);
112 bPtr = wmalloc(sizeof(WMBrowser));
113 memset(bPtr, 0, sizeof(WMBrowser));
115 bPtr->widgetClass = WC_Browser;
117 bPtr->view = W_CreateView(W_VIEW(parent));
118 if (!bPtr->view) {
119 wfree(bPtr);
120 return NULL;
122 bPtr->view->self = bPtr;
124 bPtr->view->delegate = &_BrowserViewDelegate;
126 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask
127 |ClientMessageMask, handleEvents, bPtr);
129 /* default configuration */
130 bPtr->flags.hasScroller = DEFAULT_HAS_SCROLLER;
132 bPtr->titleHeight = DEFAULT_TITLE_HEIGHT;
133 bPtr->flags.isTitled = DEFAULT_IS_TITLED;
134 bPtr->maxVisibleColumns = DEFAULT_MAX_VISIBLE_COLUMNS;
136 WMResizeWidget(bPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
138 bPtr->pathSeparator = wstrdup(DEFAULT_SEPARATOR);
140 if (bPtr->flags.hasScroller)
141 setupScroller(bPtr);
143 for (i=0; i<bPtr->maxVisibleColumns; i++) {
144 WMAddBrowserColumn(bPtr);
146 bPtr->usedColumnCount = 0;
148 bPtr->selectedColumn = -1;
150 return bPtr;
154 void
155 WMSetBrowserAllowMultipleSelection(WMBrowser *bPtr, Bool flag)
157 int i;
159 bPtr->flags.allowMultipleSelection = ((flag==0) ? 0 : 1);
160 for (i=0; i<bPtr->columnCount; i++) {
161 WMSetListAllowMultipleSelection(bPtr->columns[i], flag);
166 void
167 WMSetBrowserAllowEmptySelection(WMBrowser *bPtr, Bool flag)
169 int i;
171 bPtr->flags.allowEmptySelection = ((flag==0) ? 0 : 1);
172 for (i=0; i<bPtr->columnCount; i++) {
173 WMSetListAllowEmptySelection(bPtr->columns[i], flag);
179 WMGetBrowserMaxVisibleColumns(WMBrowser *bPtr)
181 return bPtr->maxVisibleColumns;
185 void
186 WMSetBrowserMaxVisibleColumns(WMBrowser *bPtr, int columns)
188 int curMaxVisibleColumns;
189 int newFirstVisibleColumn = 0;
191 assert ((int) bPtr);
193 columns = (columns < MIN_VISIBLE_COLUMNS) ? MIN_VISIBLE_COLUMNS : columns;
194 columns = (columns > MAX_VISIBLE_COLUMNS) ? MAX_VISIBLE_COLUMNS : columns;
195 if (columns == bPtr->maxVisibleColumns) {
196 return;
198 curMaxVisibleColumns = bPtr->maxVisibleColumns;
199 bPtr->maxVisibleColumns = columns;
200 /* browser not loaded */
201 if (!bPtr->flags.loaded) {
202 if ((columns > curMaxVisibleColumns) && (columns > bPtr->columnCount)) {
203 int i = columns - bPtr->columnCount;
204 bPtr->usedColumnCount = bPtr->columnCount;
205 while (i--) {
206 WMAddBrowserColumn(bPtr);
208 bPtr->usedColumnCount = 0;
210 /* browser loaded and columns > curMaxVisibleColumns */
211 } else if (columns > curMaxVisibleColumns) {
212 if (bPtr->usedColumnCount > columns) {
213 newFirstVisibleColumn = bPtr->usedColumnCount - columns;
215 if (newFirstVisibleColumn > bPtr->firstVisibleColumn) {
216 newFirstVisibleColumn = bPtr->firstVisibleColumn;
218 if (columns > bPtr->columnCount) {
219 int i = columns - bPtr->columnCount;
220 int curUsedColumnCount = bPtr->usedColumnCount;
221 bPtr->usedColumnCount = bPtr->columnCount;
222 while (i--) {
223 WMAddBrowserColumn(bPtr);
225 bPtr->usedColumnCount = curUsedColumnCount;
227 /* browser loaded and columns < curMaxVisibleColumns */
228 } else {
229 newFirstVisibleColumn = bPtr->firstVisibleColumn;
230 if (newFirstVisibleColumn + columns >= bPtr->usedColumnCount) {
231 removeColumn(bPtr, newFirstVisibleColumn + columns);
234 WMResizeWidget(bPtr, bPtr->view->size.width, bPtr->view->size.height);
235 if (bPtr->flags.loaded) {
236 XClearArea(bPtr->view->screen->display, bPtr->view->window, 0, 0,
237 bPtr->view->size.width, bPtr->titleHeight, False);
238 scrollToColumn (bPtr, newFirstVisibleColumn, True);
244 WMGetBrowserNumberOfColumns(WMBrowser *bPtr)
246 return bPtr->usedColumnCount;
249 void
250 WMSetBrowserPathSeparator(WMBrowser *bPtr, char *separator)
252 if (bPtr->pathSeparator)
253 wfree(bPtr->pathSeparator);
254 bPtr->pathSeparator = wstrdup(separator);
259 static void
260 drawTitleOfColumn(WMBrowser *bPtr, int column)
262 WMScreen *scr = bPtr->view->screen;
263 int x;
265 x=(column-bPtr->firstVisibleColumn)*(bPtr->columnSize.width+COLUMN_SPACING);
267 XFillRectangle(scr->display, bPtr->view->window, WMColorGC(scr->darkGray), x, 0,
268 bPtr->columnSize.width, bPtr->titleHeight);
269 W_DrawRelief(scr, bPtr->view->window, x, 0,
270 bPtr->columnSize.width, bPtr->titleHeight, WRSunken);
272 if (column < bPtr->usedColumnCount && bPtr->titles[column]) {
273 int titleLen = strlen(bPtr->titles[column]);
274 int widthC = bPtr->columnSize.width-8;
276 if (WMWidthOfString(scr->boldFont, bPtr->titles[column], titleLen)
277 > widthC) {
278 char *titleBuf = createTruncatedString(scr->boldFont,
279 bPtr->titles[column],
280 &titleLen, widthC);
281 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
282 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
283 bPtr->columnSize.width, WACenter, scr->white,
284 False, titleBuf, titleLen);
285 wfree (titleBuf);
286 } else {
287 W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
288 (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
289 bPtr->columnSize.width, WACenter, scr->white,
290 False, bPtr->titles[column], titleLen);
296 WMList*
297 WMGetBrowserListInColumn(WMBrowser *bPtr, int column)
299 if (column < 0 || column >= bPtr->usedColumnCount)
300 return NULL;
302 return bPtr->columns[column];
306 void
307 WMSetBrowserDelegate(WMBrowser *bPtr, WMBrowserDelegate *delegate)
309 bPtr->delegate = delegate;
314 WMGetBrowserFirstVisibleColumn(WMBrowser *bPtr)
316 return bPtr->firstVisibleColumn;
320 static void
321 removeColumn(WMBrowser *bPtr, int column)
323 int i, clearEnd, destroyEnd;
324 WMList **clist;
325 char **tlist;
327 assert ((int) bPtr);
329 column = (column < 0) ? 0 : column;
330 if (column >= bPtr->columnCount) {
331 return;
333 if (column < bPtr->maxVisibleColumns) {
334 clearEnd = bPtr->maxVisibleColumns;
335 destroyEnd = bPtr->columnCount;
336 bPtr->columnCount = bPtr->maxVisibleColumns;
337 } else {
338 clearEnd = column;
339 destroyEnd = bPtr->columnCount;
340 bPtr->columnCount = column;
342 if (column < bPtr->usedColumnCount) {
343 bPtr->usedColumnCount = column;
345 for (i=column; i < clearEnd; i++) {
346 if (bPtr->titles[i]) {
347 wfree(bPtr->titles[i]);
348 bPtr->titles[i] = NULL;
350 WMClearList(bPtr->columns[i]);
352 for (;i < destroyEnd; i++) {
353 if (bPtr->titles[i]) {
354 wfree(bPtr->titles[i]);
355 bPtr->titles[i] = NULL;
357 WMRemoveNotificationObserverWithName(bPtr,
358 WMListSelectionDidChangeNotification,
359 bPtr->columns[i]);
360 WMDestroyWidget(bPtr->columns[i]);
361 bPtr->columns[i] = NULL;
363 clist = wmalloc(sizeof(WMList*) * (bPtr->columnCount));
364 tlist = wmalloc(sizeof(char*) * (bPtr->columnCount));
365 memcpy(clist, bPtr->columns, sizeof(WMList*) * (bPtr->columnCount));
366 memcpy(tlist, bPtr->titles, sizeof(char*) * (bPtr->columnCount));
367 wfree(bPtr->titles);
368 wfree(bPtr->columns);
369 bPtr->titles = tlist;
370 bPtr->columns = clist;
374 WMListItem*
375 WMGetBrowserSelectedItemInColumn(WMBrowser *bPtr, int column)
377 if ((column < 0) || (column >= bPtr->usedColumnCount))
378 return NULL;
380 return WMGetListSelectedItem(bPtr->columns[column]);
386 WMGetBrowserSelectedColumn(WMBrowser *bPtr)
388 return bPtr->selectedColumn;
393 WMGetBrowserSelectedRowInColumn(WMBrowser *bPtr, int column)
395 if (column >= 0 && column < bPtr->columnCount) {
396 return WMGetListSelectedItemRow(bPtr->columns[column]);
397 } else {
398 return -1;
403 void
404 WMSetBrowserColumnTitle(WMBrowser *bPtr, int column, char *title)
406 assert(column >= 0);
407 assert(column < bPtr->usedColumnCount);
409 if (bPtr->titles[column])
410 wfree(bPtr->titles[column]);
412 bPtr->titles[column] = wstrdup(title);
414 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
415 drawTitleOfColumn(bPtr, column);
420 void
421 WMSetBrowserTitled(WMBrowser *bPtr, Bool flag)
423 int i;
424 int columnX, columnY;
426 flag = ((flag==0) ? 0 : 1);
428 if (bPtr->flags.isTitled == flag)
429 return;
431 columnX = 0;
433 if (!bPtr->flags.isTitled) {
434 columnY = TITLE_SPACING + bPtr->titleHeight;
436 bPtr->columnSize.height -= columnY;
438 for (i=0; i<bPtr->columnCount; i++) {
439 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
440 bPtr->columnSize.height);
442 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
444 WMMoveWidget(bPtr->columns[i], columnX, columnY);
446 } else {
447 bPtr->columnSize.height += TITLE_SPACING + bPtr->titleHeight;
449 for (i=0; i<bPtr->columnCount; i++) {
450 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
451 bPtr->columnSize.height);
453 columnX = WMWidgetView(bPtr->columns[i])->pos.x;
455 WMMoveWidget(bPtr->columns[i], columnX, 0);
459 bPtr->flags.isTitled = flag;
463 void
464 WMSortBrowserColumn(WMBrowser *bPtr, int column)
466 WMSortListItems(bPtr->columns[column]);
470 void
471 WMSortBrowserColumnWithComparer(WMBrowser *bPtr, int column,
472 WMCompareDataProc *func)
474 WMSortListItemsWithComparer(bPtr->columns[column], func);
479 WMListItem*
480 WMInsertBrowserItem(WMBrowser *bPtr, int column, int row, char *text,
481 Bool isBranch)
483 WMListItem *item;
485 if (column < 0 || column >= bPtr->columnCount)
486 return NULL;
488 item = WMInsertListItem(bPtr->columns[column], row, text);
489 item->isBranch = isBranch;
491 return item;
497 static void
498 willResizeBrowser(W_ViewDelegate *self, WMView *view,
499 unsigned int *width, unsigned int *height)
501 WMBrowser *bPtr = (WMBrowser*)view->self;
502 int cols = bPtr->maxVisibleColumns;
503 int colX, colY;
504 int i;
506 assert(*width > 0);
507 assert(*height > 0);
509 bPtr->columnSize.width = (*width-(cols-1)*COLUMN_SPACING) / cols;
510 bPtr->columnSize.height = *height;
512 if (bPtr->flags.isTitled) {
513 colY = TITLE_SPACING + bPtr->titleHeight;
514 bPtr->columnSize.height -= colY;
515 } else {
516 colY = 0;
519 if (bPtr->flags.hasScroller) {
520 bPtr->columnSize.height -= SCROLLER_WIDTH + 4;
522 if (bPtr->scroller) {
523 WMResizeWidget(bPtr->scroller, *width-2, 1);
524 WMMoveWidget(bPtr->scroller, 1, *height-SCROLLER_WIDTH-1);
528 colX = 0;
529 for (i = 0; i < bPtr->columnCount; i++) {
530 WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
531 bPtr->columnSize.height);
533 WMMoveWidget(bPtr->columns[i], colX, colY);
535 if (COLUMN_IS_VISIBLE(bPtr, i)) {
536 colX += bPtr->columnSize.width+COLUMN_SPACING;
542 static void
543 paintItem(WMList *lPtr, int index, Drawable drawable, char *text, int state,
544 WMRect *rect)
546 WMView *view = W_VIEW(lPtr);
547 W_Screen *scr = view->screen;
548 Display *display = scr->display;
549 WMFont *font = ((state & WLDSIsBranch) ? scr->boldFont : scr->normalFont);
550 int width, height, x, y, textLen;
551 Drawable d = drawable;
553 width = rect->size.width;
554 height = rect->size.height;
555 x = rect->pos.x;
556 y = rect->pos.y;
557 textLen = strlen(text);
559 #ifdef DOUBLE_BUFFER_no
560 x = y = 0;
561 d = XCreatePixmap(display, drawable, width, height, scr->depth);
562 if (state & WLDSSelected)
563 XFillRectangle(display, d, WMColorGC(scr->white), 0, 0, width, height);
564 else
565 XFillRectangle(display, d, WMColorGC(view->backColor), 0, 0, width, height);
566 #else
567 if (state & WLDSSelected)
568 XFillRectangle(display, d, WMColorGC(scr->white), x, y, width, height);
569 else
570 //XFillRectangle(display, d, WMColorGC(view->backColor), x, y, width, height);
571 XClearArea(display, d, x, y, width, height, False);
572 #endif
574 if (text) {
575 /* Avoid overlaping... */
576 int widthC = (state & WLDSIsBranch) ? width-20 : width-8;
577 if (WMWidthOfString(font, text, textLen) > widthC) {
578 char *textBuf = createTruncatedString(font, text, &textLen, widthC);
579 W_PaintText(view, d, font, x+4, y, widthC,
580 WALeft, scr->black, False, textBuf, textLen);
581 wfree(textBuf);
582 } else {
583 W_PaintText(view, d, font, x+4, y, widthC,
584 WALeft, scr->black, False, text, textLen);
588 if (state & WLDSIsBranch) {
589 XDrawLine(display, d, WMColorGC(scr->darkGray), x+width-11, y+3,
590 x+width-6, y+height/2);
591 if (state & WLDSSelected)
592 XDrawLine(display, d,WMColorGC(scr->gray), x+width-11, y+height-5,
593 x+width-6, y+height/2);
594 else
595 XDrawLine(display, d,WMColorGC(scr->white), x+width-11, y+height-5,
596 x+width-6, y+height/2);
597 XDrawLine(display, d, WMColorGC(scr->black), x+width-12, y+3,
598 x+width-12, y+height-5);
601 #ifdef DOUBLE_BUFFER_no
602 XCopyArea(display, d, drawable, scr->copyGC, 0, 0, width, height,
603 rect->pos.x, rect->pos.y);
604 XFreePixmap(display, d);
605 #endif
609 static void
610 scrollCallback(WMWidget *scroller, void *self)
612 WMBrowser *bPtr = (WMBrowser*)self;
613 WMScroller *sPtr = (WMScroller*)scroller;
614 int newFirst;
615 #define LAST_VISIBLE_COLUMN bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
617 switch (WMGetScrollerHitPart(sPtr)) {
618 case WSDecrementLine:
619 if (bPtr->firstVisibleColumn > 0) {
620 scrollToColumn(bPtr, bPtr->firstVisibleColumn-1, True);
622 break;
624 case WSDecrementPage:
625 case WSDecrementWheel:
626 if (bPtr->firstVisibleColumn > 0) {
627 newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
629 scrollToColumn(bPtr, newFirst, True);
631 break;
634 case WSIncrementLine:
635 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
636 scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
638 break;
640 case WSIncrementPage:
641 case WSIncrementWheel:
642 if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
643 newFirst = bPtr->firstVisibleColumn + bPtr->maxVisibleColumns;
645 if (newFirst+bPtr->maxVisibleColumns >= bPtr->columnCount)
646 newFirst = bPtr->columnCount - bPtr->maxVisibleColumns;
648 scrollToColumn(bPtr, newFirst, True);
650 break;
652 case WSKnob:
654 double floatValue;
655 double value = bPtr->columnCount - bPtr->maxVisibleColumns;
657 floatValue = WMGetScrollerValue(bPtr->scroller);
659 floatValue = (floatValue*value)/value;
661 newFirst = rint(floatValue*(float)(bPtr->columnCount - bPtr->maxVisibleColumns));
663 if (bPtr->firstVisibleColumn != newFirst)
664 scrollToColumn(bPtr, newFirst, False);
665 /* else
666 WMSetScrollerParameters(bPtr->scroller, floatValue,
667 bPtr->maxVisibleColumns/(float)bPtr->columnCount);
671 break;
673 case WSKnobSlot:
674 case WSNoPart:
675 /* do nothing */
676 break;
678 #undef LAST_VISIBLE_COLUMN
682 static void
683 setupScroller(WMBrowser *bPtr)
685 WMScroller *sPtr;
686 int y;
688 y = bPtr->view->size.height - SCROLLER_WIDTH - 1;
690 sPtr = WMCreateScroller(bPtr);
691 WMSetScrollerAction(sPtr, scrollCallback, bPtr);
692 WMMoveWidget(sPtr, 1, y);
693 WMResizeWidget(sPtr, bPtr->view->size.width-2, SCROLLER_WIDTH);
695 bPtr->scroller = sPtr;
697 WMMapWidget(sPtr);
701 void
702 WMSetBrowserAction(WMBrowser *bPtr, WMAction *action, void *clientData)
704 bPtr->action = action;
705 bPtr->clientData = clientData;
709 void
710 WMSetBrowserDoubleAction(WMBrowser *bPtr, WMAction *action, void *clientData)
712 bPtr->doubleAction = action;
713 bPtr->doubleClientData = clientData;
717 void
718 WMSetBrowserHasScroller(WMBrowser *bPtr, int hasScroller)
720 bPtr->flags.hasScroller = hasScroller;
724 char*
725 WMSetBrowserPath(WMBrowser *bPtr, char *path)
727 int i;
728 char *str;
729 char *tmp, *retPtr = NULL;
730 int item;
731 WMListItem *listItem;
733 /* WMLoadBrowserColumnZero must be call first */
734 if (!bPtr->flags.loaded) {
735 return False;
738 removeColumn(bPtr, 1);
740 WMSelectListItem(bPtr->columns[0], -1);
741 WMSetListPosition(bPtr->columns[0], 0);
743 i = 0;
744 str = wstrdup(path);
745 tmp = strtok(str, bPtr->pathSeparator);
746 while (tmp) {
747 /* select it in the column */
748 item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
749 if (item<0) {
750 retPtr = &path[(int)(tmp - str)];
751 break;
753 WMSelectListItem(bPtr->columns[i], item);
754 WMSetListPosition(bPtr->columns[i], item);
756 listItem = WMGetListItem(bPtr->columns[i], item);
757 if (!listItem || !listItem->isBranch) {
758 break;
761 /* load next column */
762 WMAddBrowserColumn(bPtr);
764 loadColumn(bPtr, i+1);
766 tmp = strtok(NULL, bPtr->pathSeparator);
768 i++;
771 wfree(str);
773 for (i = bPtr->usedColumnCount - 1;
774 (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
775 i--);
777 bPtr->selectedColumn = i;
779 if (bPtr->columnCount < bPtr->maxVisibleColumns) {
780 int i = bPtr->maxVisibleColumns - bPtr->columnCount;
781 int curUsedColumnCount = bPtr->usedColumnCount;
782 bPtr->usedColumnCount = bPtr->columnCount;
783 while (i--) {
784 WMAddBrowserColumn(bPtr);
786 bPtr->usedColumnCount = curUsedColumnCount;
789 scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
791 return retPtr;
795 char*
796 WMGetBrowserPath(WMBrowser *bPtr)
798 return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
802 char*
803 WMGetBrowserPathToColumn(WMBrowser *bPtr, int column)
805 int i, size;
806 char *path;
807 WMListItem *item;
809 if (column >= bPtr->usedColumnCount)
810 column = bPtr->usedColumnCount-1;
812 if (column < 0) {
813 return wstrdup(bPtr->pathSeparator);
816 /* calculate size of buffer */
817 size = 0;
818 for (i = 0; i <= column; i++) {
819 item = WMGetListSelectedItem(bPtr->columns[i]);
820 if (!item)
821 break;
822 size += strlen(item->text);
825 /* get the path */
826 path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
827 /* ignore first / */
828 *path = 0;
829 for (i = 0; i <= column; i++) {
830 strcat(path, bPtr->pathSeparator);
831 item = WMGetListSelectedItem(bPtr->columns[i]);
832 if (!item)
833 break;
834 strcat(path, item->text);
837 return path;
841 WMArray*
842 WMGetBrowserPaths(WMBrowser *bPtr)
844 int column, i, k, size, selNo;
845 char *path;
846 WMListItem *item, *lastItem;
847 WMArray *paths, *items;
849 column = bPtr->usedColumnCount-1;
851 if (column < 0) {
852 paths = WMCreateArrayWithDestructor(1, wfree);
853 WMAddToArray(paths, wstrdup(bPtr->pathSeparator));
854 return paths;
857 items = WMGetListSelectedItems(bPtr->columns[column]);
858 selNo = WMGetArrayItemCount(items);
859 paths = WMCreateArrayWithDestructor(selNo, wfree);
861 if (selNo <= 1) {
862 WMAddToArray(paths, WMGetBrowserPath(bPtr));
863 return paths;
866 /* calculate size of buffer */
867 size = 0;
868 for (i=0; i<column; i++) {
869 item = WMGetListSelectedItem(bPtr->columns[i]);
870 if (!item)
871 break;
872 size += strlen(item->text);
875 size += (column+1)*strlen(bPtr->pathSeparator)+1;
877 for (k=0; k<selNo; k++) {
878 /* get the path */
879 lastItem = WMGetFromArray(items, k);
880 path = wmalloc(size + (lastItem!=NULL ? strlen(lastItem->text) : 0));
881 /* ignore first / */
882 *path = 0;
883 for (i=0; i<=column; i++) {
884 strcat(path, bPtr->pathSeparator);
885 if (i == column) {
886 item = lastItem;
887 } else {
888 item = WMGetListSelectedItem(bPtr->columns[i]);
890 if (!item)
891 break;
892 strcat(path, item->text);
894 WMAddToArray(paths, path);
897 return paths;
901 Bool
902 WMBrowserAllowsMultipleSelection(WMBrowser *bPtr)
904 return bPtr->flags.allowMultipleSelection;
908 Bool
909 WMBrowserAllowsEmptySelection(WMBrowser *bPtr)
911 return bPtr->flags.allowEmptySelection;
915 static void
916 loadColumn(WMBrowser *bPtr, int column)
918 assert(bPtr->delegate);
919 assert(bPtr->delegate->createRowsForColumn);
921 bPtr->flags.loadingColumn = 1;
922 (*bPtr->delegate->createRowsForColumn)(bPtr->delegate, bPtr, column,
923 bPtr->columns[column]);
924 bPtr->flags.loadingColumn = 0;
926 if (bPtr->delegate->titleOfColumn) {
927 char *title;
929 title = (*bPtr->delegate->titleOfColumn)(bPtr->delegate, bPtr, column);
931 if (bPtr->titles[column])
932 wfree(bPtr->titles[column]);
934 bPtr->titles[column] = wstrdup(title);
936 if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
937 drawTitleOfColumn(bPtr, column);
943 static void
944 paintBrowser(WMBrowser *bPtr)
946 int i;
948 if (!bPtr->view->flags.mapped)
949 return;
951 W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
952 bPtr->view->size.height-SCROLLER_WIDTH-2,
953 bPtr->view->size.width, 22, WRSunken);
955 if (bPtr->flags.isTitled) {
956 for (i=0; i<bPtr->maxVisibleColumns; i++) {
957 drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
963 static void
964 handleEvents(XEvent *event, void *data)
966 WMBrowser *bPtr = (WMBrowser*)data;
968 CHECK_CLASS(data, WC_Browser);
971 switch (event->type) {
972 case Expose:
973 paintBrowser(bPtr);
974 break;
976 case DestroyNotify:
977 destroyBrowser(bPtr);
978 break;
985 static void
986 scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller)
988 int i;
989 int x;
990 int notify = 0;
993 if (column != bPtr->firstVisibleColumn) {
994 notify = 1;
997 if (column < 0)
998 column = 0;
1000 if (notify && bPtr->delegate && bPtr->delegate->willScroll) {
1001 (*bPtr->delegate->willScroll)(bPtr->delegate, bPtr);
1004 x = 0;
1005 bPtr->firstVisibleColumn = column;
1006 for (i = 0; i < bPtr->columnCount; i++) {
1007 if (COLUMN_IS_VISIBLE(bPtr, i)) {
1008 WMMoveWidget(bPtr->columns[i], x,
1009 WMWidgetView(bPtr->columns[i])->pos.y);
1010 if (!WMWidgetView(bPtr->columns[i])->flags.realized)
1011 WMRealizeWidget(bPtr->columns[i]);
1012 WMMapWidget(bPtr->columns[i]);
1013 x += bPtr->columnSize.width + COLUMN_SPACING;
1014 } else {
1015 WMUnmapWidget(bPtr->columns[i]);
1019 /* update the scroller */
1020 if (updateScroller) {
1021 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
1022 float value, proportion;
1024 value = bPtr->firstVisibleColumn
1025 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1026 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1027 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1028 } else {
1029 WMSetScrollerParameters(bPtr->scroller, 0, 1);
1033 if (bPtr->view->flags.mapped)
1034 paintBrowser(bPtr);
1036 if (notify && bPtr->delegate && bPtr->delegate->didScroll) {
1037 (*bPtr->delegate->didScroll)(bPtr->delegate, bPtr);
1042 static void
1043 listCallback(void *self, void *clientData)
1045 WMBrowser *bPtr = (WMBrowser*)clientData;
1046 WMList *lPtr = (WMList*)self;
1047 WMListItem *item;
1048 int i, selNo;
1049 static WMListItem *oldItem = NULL;
1050 static int oldSelNo = 0;
1052 item = WMGetListSelectedItem(lPtr);
1053 selNo = WMGetArrayItemCount(WMGetListSelectedItems(lPtr));
1055 if (oldItem==NULL || oldItem!=item || oldSelNo!=selNo) {
1056 for (i=0; i<bPtr->columnCount; i++) {
1057 if (lPtr == bPtr->columns[i])
1058 break;
1060 assert(i<bPtr->columnCount);
1062 bPtr->selectedColumn = i;
1064 /* columns at right must be cleared */
1065 removeColumn(bPtr, i+1);
1066 /* open directory */
1067 if (item && item->isBranch && selNo==1) {
1068 WMAddBrowserColumn(bPtr);
1070 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
1071 i = 0;
1072 else
1073 i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
1074 scrollToColumn(bPtr, i, True);
1075 if (item && item->isBranch && selNo==1) {
1076 loadColumn(bPtr, bPtr->usedColumnCount-1);
1080 /* call callback for click */
1081 if (bPtr->action)
1082 (*bPtr->action)(bPtr, bPtr->clientData);
1084 oldItem = item;
1085 oldSelNo = selNo;
1089 static void
1090 listDoubleCallback(void *self, void *clientData)
1092 WMBrowser *bPtr = (WMBrowser*)clientData;
1093 WMList *lPtr = (WMList*)self;
1094 WMListItem *item;
1096 item = WMGetListSelectedItem(lPtr);
1097 if (!item)
1098 return;
1100 /* call callback for double click */
1101 if (bPtr->doubleAction)
1102 (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
1106 void
1107 WMLoadBrowserColumnZero(WMBrowser *bPtr)
1109 if (!bPtr->flags.loaded) {
1110 /* create column 0 */
1111 WMAddBrowserColumn(bPtr);
1113 loadColumn(bPtr, 0);
1115 /* make column 0 visible */
1116 scrollToColumn(bPtr, 0, True);
1118 bPtr->flags.loaded = 1;
1123 void
1124 WMRemoveBrowserItem(WMBrowser *bPtr, int column, int row)
1126 WMList *list;
1128 if (column < 0 || column >= bPtr->usedColumnCount)
1129 return;
1131 list = WMGetBrowserListInColumn(bPtr, column);
1133 if (row < 0 || row >= WMGetListNumberOfRows(list))
1134 return;
1136 removeColumn(bPtr, column+1);
1137 if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
1138 scrollToColumn(bPtr, 0, True);
1139 else
1140 scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
1141 True);
1143 WMRemoveListItem(list, row);
1147 static void
1148 listSelectionObserver(void *observerData, WMNotification *notification)
1150 WMBrowser *bPtr = (WMBrowser*)observerData;
1151 int column;
1152 WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
1154 for (column = 0; column < bPtr->usedColumnCount; column++)
1155 if (bPtr->columns[column] == lPtr)
1156 break;
1158 /* this can happen when a list is being cleared with WMClearList
1159 * after the column was removed */
1160 if (column >= bPtr->usedColumnCount) {
1161 return;
1164 if (WMGetArrayItemCount(WMGetListSelectedItems(lPtr)) == 0)
1165 column--;
1167 bPtr->selectedColumn = column;
1172 WMAddBrowserColumn(WMBrowser *bPtr)
1174 WMList *list;
1175 WMList **clist;
1176 char **tlist;
1177 int colY;
1178 int index;
1181 if (bPtr->usedColumnCount < bPtr->columnCount) {
1182 return bPtr->usedColumnCount++;
1185 bPtr->usedColumnCount++;
1187 if (bPtr->flags.isTitled) {
1188 colY = TITLE_SPACING + bPtr->titleHeight;
1189 } else {
1190 colY = 0;
1193 index = bPtr->columnCount;
1194 bPtr->columnCount++;
1195 clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
1196 tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
1197 memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
1198 memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
1199 if (bPtr->columns)
1200 wfree(bPtr->columns);
1201 if (bPtr->titles)
1202 wfree(bPtr->titles);
1203 bPtr->columns = clist;
1204 bPtr->titles = tlist;
1206 bPtr->titles[index] = NULL;
1208 list = WMCreateList(bPtr);
1209 WMSetListAllowMultipleSelection(list, bPtr->flags.allowMultipleSelection);
1210 WMSetListAllowEmptySelection(list, bPtr->flags.allowEmptySelection);
1211 WMSetListAction(list, listCallback, bPtr);
1212 WMSetListDoubleAction(list, listDoubleCallback, bPtr);
1213 WMSetListUserDrawProc(list, paintItem);
1214 WMAddNotificationObserver(listSelectionObserver, bPtr,
1215 WMListSelectionDidChangeNotification, list);
1217 bPtr->columns[index] = list;
1219 WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
1220 WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
1221 if (COLUMN_IS_VISIBLE(bPtr, index))
1222 WMMapWidget(list);
1224 /* update the scroller */
1225 if (bPtr->columnCount > bPtr->maxVisibleColumns) {
1226 float value, proportion;
1228 value = bPtr->firstVisibleColumn
1229 /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
1230 proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
1231 WMSetScrollerParameters(bPtr->scroller, value, proportion);
1234 return index;
1239 static void
1240 destroyBrowser(WMBrowser *bPtr)
1242 int i;
1244 for (i = 0; i < bPtr->columnCount; i++) {
1245 if (bPtr->titles[i])
1246 wfree(bPtr->titles[i]);
1248 wfree(bPtr->titles);
1250 wfree(bPtr->pathSeparator);
1252 WMRemoveNotificationObserver(bPtr);
1254 wfree(bPtr);
1258 static char*
1259 createTruncatedString(WMFont *font, char *text, int *textLen, int width)
1261 int dLen = WMWidthOfString(font, ".", 1);
1262 char *textBuf = (char*)wmalloc((*textLen)+4);
1264 if (width >= 3*dLen) {
1265 int dddLen = 3*dLen;
1266 int tmpTextLen = *textLen;
1268 strcpy(textBuf, text);
1269 while (tmpTextLen
1270 && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
1271 tmpTextLen--;
1272 strcpy(textBuf+tmpTextLen, "...");
1273 *textLen = tmpTextLen+3;
1274 } else if (width >= 2*dLen) {
1275 strcpy(textBuf, "..");
1276 *textLen = 2;
1277 } else if (width >= dLen) {
1278 strcpy(textBuf, ".");
1279 *textLen = 1;
1280 } else {
1281 *textBuf = '\0';
1282 *textLen = 0;
1284 return textBuf;