- added WMGetLabelText()
[wmaker-crm.git] / WINGs / wtabview.c
blob19229b82f84d447cfa0392616064b57ac14538b8
2 #include "WINGsP.h"
5 typedef struct W_TabView {
6 W_Class widgetClass;
7 W_View *view;
9 struct W_TabViewItem **items;
10 int itemCount;
11 int maxItems; /* size of items array, can be increased */
13 int selectedItem;
14 int firstVisible;
16 int visibleTabs;
18 WMFont *font;
20 WMColor *lightGray;
21 WMColor *tabColor;
23 WMTabViewDelegate *delegate;
25 short tabWidth;
26 short tabHeight;
28 struct {
29 WMReliefType relief:4;
30 WMTitlePosition titlePosition:4;
31 WMTabViewType type:2;
33 unsigned tabbed:1;
34 unsigned dontFitAll:1;
35 } flags;
36 } TabView;
40 #define DEFAULT_WIDTH 40
41 #define DEFAULT_HEIGHT 40
43 #define NORMAL_SIDE_OFFSET 8
44 #define BUTTONED_SIDE_OFFSET 20
47 static void destroyTabView(TabView *tPtr);
48 static void paintTabView(TabView *tPtr);
51 static void W_SetTabViewItemParent(WMTabViewItem *item, WMTabView *parent);
53 static void W_DrawLabel(WMTabViewItem *item, Drawable d, WMRect rect);
55 static void W_UnmapTabViewItem(WMTabViewItem *item);
57 static void W_MapTabViewItem(WMTabViewItem *item);
59 static WMView *W_TabViewItemView(WMTabViewItem *item);
61 static void recalcTabWidth(TabView *tPtr);
64 static void
65 handleEvents(XEvent *event, void *data)
67 TabView *tPtr = (TabView*)data;
69 CHECK_CLASS(data, WC_TabView);
71 switch (event->type) {
72 case Expose:
73 if (event->xexpose.count!=0)
74 break;
75 paintTabView(tPtr);
76 break;
78 case ButtonPress:
80 WMTabViewItem *item = WMTabViewItemAtPoint(tPtr,
81 event->xbutton.x,
82 event->xbutton.y);
83 if (item) {
84 WMSelectTabViewItem(tPtr, item);
85 } else if (tPtr->flags.dontFitAll) {
86 int redraw;
87 if (event->xbutton.x < BUTTONED_SIDE_OFFSET) {
88 if (tPtr->firstVisible > 0) {
89 redraw = 1;
90 tPtr->firstVisible--;
92 } else if (event->xbutton.x - BUTTONED_SIDE_OFFSET
93 > tPtr->visibleTabs*(tPtr->tabWidth-10)) {
95 if (tPtr->firstVisible + tPtr->visibleTabs
96 < tPtr->itemCount) {
97 redraw = 1;
98 tPtr->firstVisible++;
102 if (redraw) {
103 paintTabView(tPtr);
107 break;
109 case DestroyNotify:
110 destroyTabView(tPtr);
111 break;
117 WMTabView*
118 WMCreateTabView(WMWidget *parent)
120 TabView *tPtr;
121 WMScreen *scr = WMWidgetScreen(parent);
123 tPtr = wmalloc(sizeof(TabView));
124 memset(tPtr, 0, sizeof(TabView));
126 tPtr->widgetClass = WC_TabView;
128 tPtr->view = W_CreateView(W_VIEW(parent));
129 if (!tPtr->view) {
130 wfree(tPtr);
131 return NULL;
133 tPtr->view->self = tPtr;
135 tPtr->lightGray = WMCreateRGBColor(scr, 0xd9d9, 0xd9d9, 0xd9d9, False);
136 tPtr->tabColor = WMCreateRGBColor(scr, 0x8420, 0x8420, 0x8420, False);
138 tPtr->font = WMRetainFont(scr->normalFont);
140 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
141 |ButtonPressMask, handleEvents, tPtr);
143 WMResizeWidget(tPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
145 tPtr->tabHeight = WMFontHeight(tPtr->font) + 3;
147 return tPtr;
151 void
152 WMSetTabViewDelegate(WMTabView *tPtr, WMTabViewDelegate *delegate)
154 tPtr->delegate = delegate;
158 void
159 WMAddItemInTabView(WMTabView *tPtr, WMTabViewItem *item)
161 WMInsertItemInTabView(tPtr, tPtr->itemCount, item);
165 void
166 WMInsertItemInTabView(WMTabView *tPtr, int index, WMTabViewItem *item)
168 wassertr(W_TabViewItemView(item) != NULL);
170 if (tPtr->maxItems == tPtr->itemCount) {
171 WMTabViewItem **items;
173 items = wrealloc(tPtr->items,
174 sizeof(WMTabViewItem*) * (tPtr->maxItems + 10));
175 memset(&items[tPtr->maxItems], 0, sizeof(WMTabViewItem*) * 10);
176 tPtr->items = items;
177 tPtr->maxItems += 10;
180 if (index > tPtr->itemCount)
181 index = tPtr->itemCount;
183 if (index == 0 && tPtr->items[0]) {
184 W_UnmapTabViewItem(tPtr->items[0]);
187 if (index < tPtr->itemCount) {
188 memmove(&tPtr->items[index + 1], &tPtr->items[index],
189 tPtr->itemCount - index);
192 tPtr->items[index] = item;
194 tPtr->itemCount++;
196 recalcTabWidth(tPtr);
198 W_SetTabViewItemParent(item, tPtr);
200 W_UnmapTabViewItem(item);
202 W_ReparentView(W_TabViewItemView(item), tPtr->view, 1,
203 tPtr->tabHeight + 1);
205 W_ResizeView(W_TabViewItemView(item), tPtr->view->size.width - 3,
206 tPtr->view->size.height - tPtr->tabHeight - 3);
208 if (index == 0) {
209 W_MapTabViewItem(item);
211 if (tPtr->delegate && tPtr->delegate->didChangeNumberOfItems)
212 (*tPtr->delegate->didChangeNumberOfItems)(tPtr->delegate, tPtr);
216 void
217 WMRemoveTabViewItem(WMTabView *tPtr, WMTabViewItem *item)
219 int i;
221 for (i = 0; i < tPtr->itemCount; i++) {
222 if (tPtr->items[i] == item) {
223 if (i < tPtr->itemCount - 1)
224 memmove(&tPtr->items[i], &tPtr->items[i + 1],
225 tPtr->itemCount - i - 1);
226 else
227 tPtr->items[i] = NULL;
229 W_SetTabViewItemParent(item, NULL);
231 tPtr->itemCount--;
232 break;
235 if (tPtr->delegate && tPtr->delegate->didChangeNumberOfItems)
236 (*tPtr->delegate->didChangeNumberOfItems)(tPtr->delegate, tPtr);
241 static Bool
242 isInside(int x, int y, int width, int height, int px, int py)
244 if (py >= y + height - 3 && py <= y + height
245 && px >= x + py - (y + height - 3)
246 && px <= x + width - (py - (y + height - 3))) {
248 return True;
250 if (py >= y + 3 && py < y + height - 3
251 && px >= x + 3 + ((y + 3) - py)*3/7
252 && px <= x + width - 3 - ((y + 3) - py)*3/7) {
254 return True;
256 if (py >= y && py < y + 3
257 && px >= x + 7 + py - y
258 && px <= x + width - 7 - (py - y)) {
260 return True;
262 return False;
266 WMTabViewItem*
267 WMTabViewItemAtPoint(WMTabView *tPtr, int x, int y)
269 int i;
270 int offset;
271 int count = tPtr->visibleTabs;
272 int first = tPtr->firstVisible;
274 if (tPtr->flags.dontFitAll) {
275 offset = BUTTONED_SIDE_OFFSET;
277 i = tPtr->selectedItem - tPtr->firstVisible;
278 if (i >= 0 && i < tPtr->visibleTabs
279 && isInside(offset + (tPtr->tabWidth-10)*i, 0, tPtr->tabWidth,
280 tPtr->tabHeight, x, y)) {
281 return tPtr->items[tPtr->selectedItem];
283 } else {
284 offset = NORMAL_SIDE_OFFSET;
286 i = tPtr->selectedItem;
287 if (isInside(offset + (tPtr->tabWidth-10)*i, 0, tPtr->tabWidth,
288 tPtr->tabHeight, x, y)) {
289 return tPtr->items[i];
293 for (i = 0; i < count; i++) {
294 if (isInside(offset + (tPtr->tabWidth-10)*i, 0, tPtr->tabWidth,
295 tPtr->tabHeight, x, y)) {
296 return tPtr->items[i+first];
299 return NULL;
303 void
304 WMSelectFirstTabViewItem(WMTabView *tPtr)
306 WMSelectTabViewItemAtIndex(tPtr, 0);
310 void
311 WMSelectLastTabViewItem(WMTabView *tPtr)
313 WMSelectTabViewItemAtIndex(tPtr, tPtr->itemCount);
317 void
318 WMSelectNextTabViewItem(WMTabView *tPtr)
320 WMSelectTabViewItemAtIndex(tPtr, tPtr->selectedItem + 1);
324 void
325 WMSelectPreviousTabViewItem(WMTabView *tPtr)
327 WMSelectTabViewItemAtIndex(tPtr, tPtr->selectedItem - 1);
331 WMTabViewItem*
332 WMGetSelectedTabViewItem(WMTabView *tPtr)
334 return tPtr->items[tPtr->selectedItem];
338 void
339 WMSelectTabViewItem(WMTabView *tPtr, WMTabViewItem *item)
341 int i;
343 for (i = 0; i < tPtr->itemCount; i++) {
344 if (tPtr->items[i] == item) {
345 WMSelectTabViewItemAtIndex(tPtr, i);
346 break;
352 void
353 WMSelectTabViewItemAtIndex(WMTabView *tPtr, int index)
355 WMTabViewItem *item;
357 if (index == tPtr->selectedItem)
358 return;
360 if (index < 0)
361 index = 0;
362 else if (index >= tPtr->itemCount)
363 index = tPtr->itemCount - 1;
365 item = tPtr->items[tPtr->selectedItem];
367 if (tPtr->delegate && tPtr->delegate->shouldSelectItem)
368 if (!(*tPtr->delegate->shouldSelectItem)(tPtr->delegate, tPtr,
369 tPtr->items[index]))
370 return;
372 if (tPtr->delegate && tPtr->delegate->willSelectItem)
373 (*tPtr->delegate->willSelectItem)(tPtr->delegate, tPtr,
374 tPtr->items[index]);
376 W_UnmapTabViewItem(item);
379 item = tPtr->items[index];
381 W_MapTabViewItem(item);
383 tPtr->selectedItem = index;
385 if (tPtr->delegate && tPtr->delegate->didSelectItem)
386 (*tPtr->delegate->didSelectItem)(tPtr->delegate, tPtr,
387 tPtr->items[index]);
391 static void
392 recalcTabWidth(TabView *tPtr)
394 int i;
395 int twidth = W_VIEW(tPtr)->size.width;
396 int width;
398 tPtr->tabWidth = 0;
399 for (i = 0; i < tPtr->itemCount; i++) {
400 char *str = WMGetTabViewItemLabel(tPtr->items[i]);
402 if (str) {
403 width = WMWidthOfString(tPtr->font, str, strlen(str));
404 if (width > tPtr->tabWidth)
405 tPtr->tabWidth = width;
408 tPtr->tabWidth += 30;
409 if ((tPtr->tabWidth + 2) * tPtr->itemCount > twidth - 2*NORMAL_SIDE_OFFSET) {
410 tPtr->flags.dontFitAll = 1;
411 tPtr->firstVisible = 0;
412 tPtr->visibleTabs = (twidth - 2*BUTTONED_SIDE_OFFSET) / (tPtr->tabWidth-10);
413 } else {
414 tPtr->flags.dontFitAll = 0;
415 tPtr->firstVisible = 0;
416 tPtr->visibleTabs = tPtr->itemCount;
421 static void
422 drawRelief(W_Screen *scr, Drawable d, int x, int y, unsigned int width,
423 unsigned int height)
425 Display *dpy = scr->display;
426 GC bgc = WMColorGC(scr->black);
427 GC wgc = WMColorGC(scr->white);
428 GC dgc = WMColorGC(scr->darkGray);
430 XDrawLine(dpy, d, wgc, x, y, x, y+height-1);
432 XDrawLine(dpy, d, bgc, x, y+height-1, x+width-1, y+height-1);
433 XDrawLine(dpy, d, dgc, x+1, y+height-2, x+width-2, y+height-2);
435 XDrawLine(dpy, d, bgc, x+width-1, y, x+width-1, y+height-1);
436 XDrawLine(dpy, d, dgc, x+width-2, y+1, x+width-2, y+height-2);
440 static void
441 drawTab(TabView *tPtr, Drawable d, int x, int y,
442 unsigned width, unsigned height, Bool selected)
444 WMScreen *scr = W_VIEW(tPtr)->screen;
445 Display *dpy = scr->display;
446 GC white = WMColorGC(selected ? scr->white : tPtr->lightGray);
447 GC black = WMColorGC(scr->black);
448 GC dark = WMColorGC(scr->darkGray);
449 GC light = WMColorGC(scr->gray);
450 XPoint trap[8];
452 trap[0].x = x + (selected ? 0 : 1);
453 trap[0].y = y + height - (selected ? 0 : 1);
455 trap[1].x = x + 3;
456 trap[1].y = y + height - 3;
458 trap[2].x = x + 10 - 3;
459 trap[2].y = y + 3;
461 trap[3].x = x + 10;
462 trap[3].y = y;
464 trap[4].x = x + width - 10;
465 trap[4].y = y;
467 trap[5].x = x + width - 10 + 3;
468 trap[5].y = y + 3;
470 trap[6].x = x + width - 3;
471 trap[6].y = y + height - 3;
473 trap[7].x = x + width - (selected ? 0 : 1);
474 trap[7].y = y + height - (selected ? 0 : 1);
476 XFillPolygon(dpy, d, selected ? light : WMColorGC(tPtr->tabColor), trap, 8,
477 Convex, CoordModeOrigin);
479 XDrawLine(dpy, d, white, trap[0].x, trap[0].y, trap[1].x, trap[1].y);
480 XDrawLine(dpy, d, white, trap[1].x, trap[1].y, trap[2].x, trap[2].y);
481 XDrawLine(dpy, d, white, trap[2].x, trap[2].y, trap[3].x, trap[3].y);
482 XDrawLine(dpy, d, white, trap[3].x, trap[3].y, trap[4].x, trap[4].y);
483 XDrawLine(dpy, d, dark, trap[4].x, trap[4].y, trap[5].x, trap[5].y);
484 XDrawLine(dpy, d, black, trap[5].x, trap[5].y, trap[6].x, trap[6].y);
485 XDrawLine(dpy, d, black, trap[6].x, trap[6].y, trap[7].x, trap[7].y);
487 XDrawLine(dpy, d, selected ? light : WMColorGC(scr->white),
488 trap[0].x, trap[0].y, trap[7].x, trap[7].y);
492 static void
493 paintDot(TabView *tPtr, Drawable d, int x, int y)
495 WMScreen *scr = W_VIEW(tPtr)->screen;
496 Display *dpy = scr->display;
497 GC white = WMColorGC(scr->white);
498 GC black = WMColorGC(scr->black);
500 XFillRectangle(dpy, d, black, x, y, 2, 2);
501 XDrawPoint(dpy, d, white, x, y);
506 static void
507 paintTabView(TabView *tPtr)
509 Pixmap buffer;
510 WMScreen *scr = W_VIEW(tPtr)->screen;
511 Display *dpy = scr->display;
512 GC white = WMColorGC(scr->white);
513 int i;
514 WMRect rect;
516 if (tPtr->flags.type == WTTopTabsBevelBorder) {
517 int count = tPtr->visibleTabs;
518 int first = tPtr->firstVisible;
519 int offs;
520 int moreAtLeft;
521 int moreAtRight;
522 int selectedIsVisible;
524 buffer = XCreatePixmap(dpy, W_VIEW(tPtr)->window,
525 W_VIEW(tPtr)->size.width, tPtr->tabHeight,
526 W_VIEW(tPtr)->screen->depth);
528 XFillRectangle(dpy, buffer, WMColorGC(W_VIEW(tPtr)->backColor),
529 0, 0, W_VIEW(tPtr)->size.width, tPtr->tabHeight);
531 rect.pos.y = 2;
532 if (tPtr->flags.dontFitAll) {
533 rect.pos.x = 15 + BUTTONED_SIDE_OFFSET;
534 offs = BUTTONED_SIDE_OFFSET;
535 moreAtLeft = first > 0;
536 moreAtRight = (first + count) < tPtr->itemCount;
537 if (tPtr->selectedItem >= first
538 && tPtr->selectedItem < first + count)
539 selectedIsVisible = 1;
540 else
541 selectedIsVisible = 0;
542 } else {
543 rect.pos.x = 15 + NORMAL_SIDE_OFFSET;
544 offs = NORMAL_SIDE_OFFSET;
545 moreAtLeft = 0;
546 moreAtRight = 0;
547 selectedIsVisible = 1;
549 rect.size.width = tPtr->tabWidth;
550 rect.size.height = tPtr->tabHeight;
552 for (i = count - (moreAtRight ? 0 : 1);
553 i >= (moreAtLeft ? -1 : 0); i--) {
554 if (!selectedIsVisible || i != (tPtr->selectedItem-first)) {
555 drawTab(tPtr, buffer, offs + (rect.size.width-10)*i, 0,
556 rect.size.width, rect.size.height, False);
560 if (selectedIsVisible) {
561 drawTab(tPtr, buffer,
562 offs + (rect.size.width-10) * (tPtr->selectedItem - first),
563 0, rect.size.width, rect.size.height, True);
565 XDrawLine(dpy, buffer, white, 0, tPtr->tabHeight - 1,
566 offs, tPtr->tabHeight - 1);
568 XDrawLine(dpy, buffer, white,
569 offs + 10 + (rect.size.width-10) * count,
570 tPtr->tabHeight - 1, W_VIEW(tPtr)->size.width - 1,
571 tPtr->tabHeight - 1);
572 } else {
573 XDrawLine(dpy, buffer, white, 0, tPtr->tabHeight - 1,
574 W_VIEW(tPtr)->size.width, tPtr->tabHeight - 1);
577 for (i = 0; i < count; i++) {
578 W_DrawLabel(tPtr->items[first+i], buffer, rect);
580 rect.pos.x += rect.size.width - 10;
583 if (moreAtLeft) {
584 paintDot(tPtr, buffer, 4, 10);
585 paintDot(tPtr, buffer, 7, 10);
586 paintDot(tPtr, buffer, 10, 10);
588 if (moreAtRight) {
589 int x;
591 x = BUTTONED_SIDE_OFFSET - 5 + tPtr->visibleTabs * (tPtr->tabWidth - 10);
593 x = x + (W_VIEW(tPtr)->size.width - x)/2;
595 paintDot(tPtr, buffer, x + 5, 10);
596 paintDot(tPtr, buffer, x + 8, 10);
597 paintDot(tPtr, buffer, x + 11, 10);
600 XCopyArea(dpy, buffer, W_VIEW(tPtr)->window, scr->copyGC, 0, 0,
601 W_VIEW(tPtr)->size.width, tPtr->tabHeight, 0, 0);
603 XFreePixmap(dpy, buffer);
605 switch (tPtr->flags.type) {
606 case WTTopTabsBevelBorder:
607 drawRelief(scr, W_VIEW(tPtr)->window, 0, tPtr->tabHeight - 1,
608 W_VIEW(tPtr)->size.width,
609 W_VIEW(tPtr)->size.height - tPtr->tabHeight + 1);
610 break;
612 case WTNoTabsBevelBorder:
613 W_DrawRelief(scr, W_VIEW(tPtr)->window, 0, 0, W_VIEW(tPtr)->size.width,
614 W_VIEW(tPtr)->size.height, WRRaised);
615 break;
617 case WTNoTabsLineBorder:
618 W_DrawRelief(scr, W_VIEW(tPtr)->window, 0, 0, W_VIEW(tPtr)->size.width,
619 W_VIEW(tPtr)->size.height, WRSimple);
620 break;
622 case WTNoTabsNoBorder:
623 break;
628 static void
629 destroyTabView(TabView *tPtr)
631 int i;
633 for (i = 0; i < tPtr->itemCount; i++) {
634 WMSetTabViewItemView(tPtr->items[i], NULL);
635 WMDestroyTabViewItem(tPtr->items[i]);
637 wfree(tPtr->items);
639 WMReleaseColor(tPtr->lightGray);
640 WMReleaseColor(tPtr->tabColor);
641 WMReleaseFont(tPtr->font);
643 wfree(tPtr);
646 /******************************************************************/
649 typedef struct W_TabViewItem {
650 WMTabView *tabView;
652 W_View *view;
654 char *label;
656 int identifier;
658 struct {
659 unsigned visible:1;
660 } flags;
661 } TabViewItem;
664 static void
665 W_SetTabViewItemParent(WMTabViewItem *item, WMTabView *parent)
667 item->tabView = parent;
671 static void
672 W_DrawLabel(WMTabViewItem *item, Drawable d, WMRect rect)
674 WMScreen *scr = W_VIEW(item->tabView)->screen;
676 if (!item->label)
677 return;
679 WMDrawString(scr, d, WMColorGC(scr->black), item->tabView->font,
680 rect.pos.x, rect.pos.y, item->label, strlen(item->label));
684 static void
685 W_UnmapTabViewItem(WMTabViewItem *item)
687 wassertr(item->view);
689 W_UnmapView(item->view);
691 item->flags.visible = 0;
695 static void
696 W_MapTabViewItem(WMTabViewItem *item)
698 wassertr(item->view);
700 W_MapView(item->view);
702 item->flags.visible = 1;
706 static WMView*
707 W_TabViewItemView(WMTabViewItem *item)
709 return item->view;
713 WMTabViewItem*
714 WMCreateTabViewItemWithIdentifier(int identifier)
716 WMTabViewItem *item;
718 item = wmalloc(sizeof(WMTabViewItem));
719 memset(item, 0, sizeof(WMTabViewItem));
721 item->identifier = identifier;
723 return item;
727 WMTabViewItem*
728 WMCreateTabViewItem(int identifier, char *label)
730 WMTabViewItem *item;
732 item = wmalloc(sizeof(WMTabViewItem));
733 memset(item, 0, sizeof(WMTabViewItem));
735 item->identifier = identifier;
736 WMSetTabViewItemLabel(item, label);
738 return item;
744 WMGetTabViewItemIdentifier(WMTabViewItem *item)
746 return item->identifier;
750 void
751 WMSetTabViewFont(WMTabView *tPtr, WMFont *font)
753 if (tPtr->font)
754 WMReleaseFont(tPtr->font);
756 tPtr->font = WMRetainFont(font);
757 tPtr->tabHeight = WMFontHeight(tPtr->font) + 3;
758 recalcTabWidth(tPtr);
762 void
763 WMSetTabViewItemLabel(WMTabViewItem *item, char *label)
765 if (item->label)
766 wfree(item->label);
768 item->label = wstrdup(label);
770 if (item->tabView)
771 recalcTabWidth(item->tabView);
775 char*
776 WMGetTabViewItemLabel(WMTabViewItem *item)
778 return item->label;
782 void
783 WMSetTabViewItemView(WMTabViewItem *item, WMView *view)
785 item->view = view;
789 WMView*
790 WMGetTabViewItemView(WMTabViewItem *item)
792 return item->view;
796 void
797 WMDestroyTabViewItem(WMTabViewItem *item)
799 if (item->label)
800 wfree(item->label);
802 if (item->view)
803 W_DestroyView(item->view);
805 wfree(item);