*** empty log message ***
[wmaker-crm.git] / WINGs / wtabview.c
blob6c1b2aa0f41a537d2f389ed28d8425c168a2cb2e
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 WMTabViewTypes type:2;
33 unsigned tabbed:1;
34 unsigned dontFitAll:1;
36 unsigned leftDown:1; // scrolling button state
37 unsigned rightDown:1;
38 } flags;
39 } TabView;
43 #define DEFAULT_WIDTH 40
44 #define DEFAULT_HEIGHT 40
46 #define NORMAL_SIDE_OFFSET 8
47 #define BUTTONED_SIDE_OFFSET 20
50 static void destroyTabView(TabView *tPtr);
51 static void paintTabView(TabView *tPtr);
54 static void W_SetTabViewItemParent(WMTabViewItem *item, WMTabView *parent);
56 static void W_DrawLabel(WMTabViewItem *item, Drawable d, WMRect rect);
58 static void W_UnmapTabViewItem(WMTabViewItem *item);
60 static void W_MapTabViewItem(WMTabViewItem *item);
62 static WMView *W_TabViewItemView(WMTabViewItem *item);
64 static void recalcTabWidth(TabView *tPtr);
67 static void
68 handleEvents(XEvent *event, void *data)
70 TabView *tPtr = (TabView*)data;
72 CHECK_CLASS(data, WC_TabView);
74 switch (event->type) {
75 case Expose:
76 if (event->xexpose.count!=0)
77 break;
78 paintTabView(tPtr);
79 break;
81 case ButtonPress:
83 WMTabViewItem *item = WMTabViewItemAtPoint(tPtr,
84 event->xbutton.x,
85 event->xbutton.y);
86 if (item) {
87 WMSelectTabViewItem(tPtr, item);
88 } else if (tPtr->flags.dontFitAll) {
89 int redraw;
90 if (event->xbutton.x < BUTTONED_SIDE_OFFSET) {
91 if (tPtr->firstVisible > 0) {
92 redraw = 1;
93 tPtr->firstVisible--;
95 } else if (event->xbutton.x - BUTTONED_SIDE_OFFSET
96 > tPtr->visibleTabs*(tPtr->tabWidth-10)) {
98 if (tPtr->firstVisible + tPtr->visibleTabs
99 < tPtr->itemCount) {
100 redraw = 1;
101 tPtr->firstVisible++;
105 if (redraw) {
106 paintTabView(tPtr);
110 break;
112 case DestroyNotify:
113 destroyTabView(tPtr);
114 break;
120 WMTabView*
121 WMCreateTabView(WMWidget *parent)
123 TabView *tPtr;
124 WMScreen *scr = WMWidgetScreen(parent);
126 tPtr = wmalloc(sizeof(TabView));
127 memset(tPtr, 0, sizeof(TabView));
129 tPtr->widgetClass = WC_TabView;
131 tPtr->view = W_CreateView(W_VIEW(parent));
132 if (!tPtr->view) {
133 wfree(tPtr);
134 return NULL;
136 tPtr->view->self = tPtr;
138 tPtr->lightGray = WMCreateRGBColor(scr, 0xd9d9, 0xd9d9, 0xd9d9, False);
139 tPtr->tabColor = WMCreateRGBColor(scr, 0x8420, 0x8420, 0x8420, False);
141 tPtr->font = WMRetainFont(scr->normalFont);
143 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
144 |ButtonPressMask, handleEvents, tPtr);
146 WMResizeWidget(tPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
148 tPtr->tabHeight = WMFontHeight(tPtr->font) + 3;
150 return tPtr;
154 void
155 WMSetTabViewDelegate(WMTabView *tPtr, WMTabViewDelegate *delegate)
157 tPtr->delegate = delegate;
161 void
162 WMAddItemInTabView(WMTabView *tPtr, WMTabViewItem *item)
164 WMInsertItemInTabView(tPtr, tPtr->itemCount, item);
168 void
169 WMInsertItemInTabView(WMTabView *tPtr, int index, WMTabViewItem *item)
171 wassertr(W_TabViewItemView(item) != NULL);
173 if (tPtr->maxItems == tPtr->itemCount) {
174 WMTabViewItem **items;
176 items = wrealloc(tPtr->items,
177 sizeof(WMTabViewItem*) * (tPtr->maxItems + 10));
178 memset(&items[tPtr->maxItems], 0, sizeof(WMTabViewItem*) * 10);
179 tPtr->items = items;
180 tPtr->maxItems += 10;
183 if (index > tPtr->itemCount)
184 index = tPtr->itemCount;
186 if (index == 0 && tPtr->items[0]) {
187 W_UnmapTabViewItem(tPtr->items[0]);
190 if (index < tPtr->itemCount) {
191 memmove(&tPtr->items[index + 1], &tPtr->items[index],
192 tPtr->itemCount - index);
195 tPtr->items[index] = item;
197 tPtr->itemCount++;
199 recalcTabWidth(tPtr);
201 W_SetTabViewItemParent(item, tPtr);
203 W_UnmapTabViewItem(item);
205 W_ReparentView(W_TabViewItemView(item), tPtr->view, 1,
206 tPtr->tabHeight + 1);
208 W_ResizeView(W_TabViewItemView(item), tPtr->view->size.width - 3,
209 tPtr->view->size.height - tPtr->tabHeight - 3);
211 if (index == 0) {
212 W_MapTabViewItem(item);
214 if (tPtr->delegate && tPtr->delegate->didChangeNumberOfItems)
215 (*tPtr->delegate->didChangeNumberOfItems)(tPtr->delegate, tPtr);
219 void
220 WMRemoveTabViewItem(WMTabView *tPtr, WMTabViewItem *item)
222 int i;
224 for (i = 0; i < tPtr->itemCount; i++) {
225 if (tPtr->items[i] == item) {
226 if (i < tPtr->itemCount - 1)
227 memmove(&tPtr->items[i], &tPtr->items[i + 1],
228 tPtr->itemCount - i - 1);
229 else
230 tPtr->items[i] = NULL;
232 W_SetTabViewItemParent(item, NULL);
234 tPtr->itemCount--;
235 break;
238 if (tPtr->delegate && tPtr->delegate->didChangeNumberOfItems)
239 (*tPtr->delegate->didChangeNumberOfItems)(tPtr->delegate, tPtr);
244 static Bool
245 isInside(int x, int y, int width, int height, int px, int py)
247 if (py >= y + height - 3 && py <= y + height
248 && px >= x + py - (y + height - 3)
249 && px <= x + width - (py - (y + height - 3))) {
251 return True;
253 if (py >= y + 3 && py < y + height - 3
254 && px >= x + 3 + ((y + 3) - py)*3/7
255 && px <= x + width - 3 - ((y + 3) - py)*3/7) {
257 return True;
259 if (py >= y && py < y + 3
260 && px >= x + 7 + py - y
261 && px <= x + width - 7 - (py - y)) {
263 return True;
265 return False;
269 WMTabViewItem*
270 WMTabViewItemAtPoint(WMTabView *tPtr, int x, int y)
272 int i;
273 int offset;
274 int count = tPtr->visibleTabs;
275 int first = tPtr->firstVisible;
277 if (tPtr->flags.dontFitAll) {
278 offset = BUTTONED_SIDE_OFFSET;
280 i = tPtr->selectedItem - tPtr->firstVisible;
281 if (i >= 0 && i < tPtr->visibleTabs
282 && isInside(offset + (tPtr->tabWidth-10)*i, 0, tPtr->tabWidth,
283 tPtr->tabHeight, x, y)) {
284 return tPtr->items[tPtr->selectedItem];
286 } else {
287 offset = NORMAL_SIDE_OFFSET;
289 i = tPtr->selectedItem;
290 if (isInside(offset + (tPtr->tabWidth-10)*i, 0, tPtr->tabWidth,
291 tPtr->tabHeight, x, y)) {
292 return tPtr->items[i];
296 for (i = 0; i < count; i++) {
297 if (isInside(offset + (tPtr->tabWidth-10)*i, 0, tPtr->tabWidth,
298 tPtr->tabHeight, x, y)) {
299 return tPtr->items[i+first];
302 return NULL;
306 void
307 WMSelectFirstTabViewItem(WMTabView *tPtr)
309 WMSelectTabViewItemAtIndex(tPtr, 0);
313 void
314 WMSelectLastTabViewItem(WMTabView *tPtr)
316 WMSelectTabViewItemAtIndex(tPtr, tPtr->itemCount);
320 void
321 WMSelectNextTabViewItem(WMTabView *tPtr)
323 WMSelectTabViewItemAtIndex(tPtr, tPtr->selectedItem + 1);
327 void
328 WMSelectPreviousTabViewItem(WMTabView *tPtr)
330 WMSelectTabViewItemAtIndex(tPtr, tPtr->selectedItem - 1);
334 WMTabViewItem*
335 WMGetSelectedTabViewItem(WMTabView *tPtr)
337 return tPtr->items[tPtr->selectedItem];
341 void
342 WMSelectTabViewItem(WMTabView *tPtr, WMTabViewItem *item)
344 int i;
346 for (i = 0; i < tPtr->itemCount; i++) {
347 if (tPtr->items[i] == item) {
348 WMSelectTabViewItemAtIndex(tPtr, i);
349 break;
355 void
356 WMSelectTabViewItemAtIndex(WMTabView *tPtr, int index)
358 WMTabViewItem *item;
360 if (index == tPtr->selectedItem)
361 return;
363 if (index < 0)
364 index = 0;
365 else if (index >= tPtr->itemCount)
366 index = tPtr->itemCount - 1;
368 item = tPtr->items[tPtr->selectedItem];
370 if (tPtr->delegate && tPtr->delegate->shouldSelectItem)
371 if (!(*tPtr->delegate->shouldSelectItem)(tPtr->delegate, tPtr,
372 tPtr->items[index]))
373 return;
375 if (tPtr->delegate && tPtr->delegate->willSelectItem)
376 (*tPtr->delegate->willSelectItem)(tPtr->delegate, tPtr,
377 tPtr->items[index]);
379 W_UnmapTabViewItem(item);
382 item = tPtr->items[index];
384 W_MapTabViewItem(item);
386 tPtr->selectedItem = index;
388 if (tPtr->delegate && tPtr->delegate->didSelectItem)
389 (*tPtr->delegate->didSelectItem)(tPtr->delegate, tPtr,
390 tPtr->items[index]);
394 static void
395 recalcTabWidth(TabView *tPtr)
397 int i;
398 int twidth = W_VIEW(tPtr)->size.width;
399 int width;
401 tPtr->tabWidth = 0;
402 for (i = 0; i < tPtr->itemCount; i++) {
403 char *str = WMGetTabViewItemLabel(tPtr->items[i]);
405 if (str) {
406 width = WMWidthOfString(tPtr->font, str, strlen(str));
407 if (width > tPtr->tabWidth)
408 tPtr->tabWidth = width;
411 tPtr->tabWidth += 30;
412 if ((tPtr->tabWidth + 2) * tPtr->itemCount > twidth - 2*NORMAL_SIDE_OFFSET) {
413 tPtr->flags.dontFitAll = 1;
414 tPtr->firstVisible = 0;
415 tPtr->visibleTabs = (twidth - 2*BUTTONED_SIDE_OFFSET) / (tPtr->tabWidth-10);
416 } else {
417 tPtr->flags.dontFitAll = 0;
418 tPtr->firstVisible = 0;
419 tPtr->visibleTabs = tPtr->itemCount;
424 static void
425 drawRelief(W_Screen *scr, Drawable d, int x, int y, unsigned int width,
426 unsigned int height)
428 Display *dpy = scr->display;
429 GC bgc = WMColorGC(scr->black);
430 GC wgc = WMColorGC(scr->white);
431 GC dgc = WMColorGC(scr->darkGray);
433 XDrawLine(dpy, d, wgc, x, y, x, y+height-1);
435 XDrawLine(dpy, d, bgc, x, y+height-1, x+width-1, y+height-1);
436 XDrawLine(dpy, d, dgc, x+1, y+height-2, x+width-2, y+height-2);
438 XDrawLine(dpy, d, bgc, x+width-1, y, x+width-1, y+height-1);
439 XDrawLine(dpy, d, dgc, x+width-2, y+1, x+width-2, y+height-2);
443 static void
444 drawTab(TabView *tPtr, Drawable d, int x, int y,
445 unsigned width, unsigned height, Bool selected)
447 WMScreen *scr = W_VIEW(tPtr)->screen;
448 Display *dpy = scr->display;
449 GC white = WMColorGC(selected ? scr->white : tPtr->lightGray);
450 GC black = WMColorGC(scr->black);
451 GC dark = WMColorGC(scr->darkGray);
452 GC light = WMColorGC(scr->gray);
453 XPoint trap[8];
455 trap[0].x = x + (selected ? 0 : 1);
456 trap[0].y = y + height - (selected ? 0 : 1);
458 trap[1].x = x + 3;
459 trap[1].y = y + height - 3;
461 trap[2].x = x + 10 - 3;
462 trap[2].y = y + 3;
464 trap[3].x = x + 10;
465 trap[3].y = y;
467 trap[4].x = x + width - 10;
468 trap[4].y = y;
470 trap[5].x = x + width - 10 + 3;
471 trap[5].y = y + 3;
473 trap[6].x = x + width - 3;
474 trap[6].y = y + height - 3;
476 trap[7].x = x + width - (selected ? 0 : 1);
477 trap[7].y = y + height - (selected ? 0 : 1);
479 XFillPolygon(dpy, d, selected ? light : WMColorGC(tPtr->tabColor), trap, 8,
480 Convex, CoordModeOrigin);
482 XDrawLine(dpy, d, white, trap[0].x, trap[0].y, trap[1].x, trap[1].y);
483 XDrawLine(dpy, d, white, trap[1].x, trap[1].y, trap[2].x, trap[2].y);
484 XDrawLine(dpy, d, white, trap[2].x, trap[2].y, trap[3].x, trap[3].y);
485 XDrawLine(dpy, d, white, trap[3].x, trap[3].y, trap[4].x, trap[4].y);
486 XDrawLine(dpy, d, dark, trap[4].x, trap[4].y, trap[5].x, trap[5].y);
487 XDrawLine(dpy, d, black, trap[5].x, trap[5].y, trap[6].x, trap[6].y);
488 XDrawLine(dpy, d, black, trap[6].x, trap[6].y, trap[7].x, trap[7].y);
490 XDrawLine(dpy, d, selected ? light : WMColorGC(scr->white),
491 trap[0].x, trap[0].y, trap[7].x, trap[7].y);
495 static void
496 paintDot(TabView *tPtr, Drawable d, int x, int y)
498 WMScreen *scr = W_VIEW(tPtr)->screen;
499 Display *dpy = scr->display;
500 GC white = WMColorGC(scr->white);
501 GC black = WMColorGC(scr->black);
503 XFillRectangle(dpy, d, black, x, y, 2, 2);
504 XDrawPoint(dpy, d, white, x, y);
509 static void
510 paintTabView(TabView *tPtr)
512 Pixmap buffer;
513 WMScreen *scr = W_VIEW(tPtr)->screen;
514 Display *dpy = scr->display;
515 GC white = WMColorGC(scr->white);
516 int i;
517 WMRect rect;
519 if (tPtr->flags.type == WTTopTabsBevelBorder) {
520 int count = tPtr->visibleTabs;
521 int first = tPtr->firstVisible;
522 int offs;
523 int moreAtLeft;
524 int moreAtRight;
525 int selectedIsVisible;
527 buffer = XCreatePixmap(dpy, W_VIEW(tPtr)->window,
528 W_VIEW(tPtr)->size.width, tPtr->tabHeight,
529 W_VIEW(tPtr)->screen->depth);
531 XFillRectangle(dpy, buffer, WMColorGC(W_VIEW(tPtr)->backColor),
532 0, 0, W_VIEW(tPtr)->size.width, tPtr->tabHeight);
534 rect.pos.y = 2;
535 if (tPtr->flags.dontFitAll) {
536 rect.pos.x = 15 + BUTTONED_SIDE_OFFSET;
537 offs = BUTTONED_SIDE_OFFSET;
538 moreAtLeft = first > 0;
539 moreAtRight = (first + count) < tPtr->itemCount;
540 if (tPtr->selectedItem >= first
541 && tPtr->selectedItem < first + count)
542 selectedIsVisible = 1;
543 else
544 selectedIsVisible = 0;
545 } else {
546 rect.pos.x = 15 + NORMAL_SIDE_OFFSET;
547 offs = NORMAL_SIDE_OFFSET;
548 moreAtLeft = 0;
549 moreAtRight = 0;
550 selectedIsVisible = 1;
552 rect.size.width = tPtr->tabWidth;
553 rect.size.height = tPtr->tabHeight;
555 for (i = count - (moreAtRight ? 0 : 1);
556 i >= (moreAtLeft ? -1 : 0); i--) {
557 if (!selectedIsVisible || i != (tPtr->selectedItem-first)) {
558 drawTab(tPtr, buffer, offs + (rect.size.width-10)*i, 0,
559 rect.size.width, rect.size.height, False);
563 if (selectedIsVisible) {
564 drawTab(tPtr, buffer,
565 offs + (rect.size.width-10) * (tPtr->selectedItem - first),
566 0, rect.size.width, rect.size.height, True);
568 XDrawLine(dpy, buffer, white, 0, tPtr->tabHeight - 1,
569 offs, tPtr->tabHeight - 1);
571 XDrawLine(dpy, buffer, white,
572 offs + 10 + (rect.size.width-10) * count,
573 tPtr->tabHeight - 1, W_VIEW(tPtr)->size.width - 1,
574 tPtr->tabHeight - 1);
575 } else {
576 XDrawLine(dpy, buffer, white, 0, tPtr->tabHeight - 1,
577 W_VIEW(tPtr)->size.width, tPtr->tabHeight - 1);
580 for (i = 0; i < count; i++) {
581 W_DrawLabel(tPtr->items[first+i], buffer, rect);
583 rect.pos.x += rect.size.width - 10;
586 if (moreAtLeft) {
587 paintDot(tPtr, buffer, 4, 10);
588 paintDot(tPtr, buffer, 7, 10);
589 paintDot(tPtr, buffer, 10, 10);
591 if (moreAtRight) {
592 int x;
594 x = BUTTONED_SIDE_OFFSET - 5 + tPtr->visibleTabs * (tPtr->tabWidth - 10);
596 x = x + (W_VIEW(tPtr)->size.width - x)/2;
598 paintDot(tPtr, buffer, x + 5, 10);
599 paintDot(tPtr, buffer, x + 8, 10);
600 paintDot(tPtr, buffer, x + 11, 10);
603 XCopyArea(dpy, buffer, W_VIEW(tPtr)->window, scr->copyGC, 0, 0,
604 W_VIEW(tPtr)->size.width, tPtr->tabHeight, 0, 0);
606 XFreePixmap(dpy, buffer);
608 switch (tPtr->flags.type) {
609 case WTTopTabsBevelBorder:
610 drawRelief(scr, W_VIEW(tPtr)->window, 0, tPtr->tabHeight - 1,
611 W_VIEW(tPtr)->size.width,
612 W_VIEW(tPtr)->size.height - tPtr->tabHeight + 1);
613 break;
615 case WTNoTabsBevelBorder:
616 W_DrawRelief(scr, W_VIEW(tPtr)->window, 0, 0, W_VIEW(tPtr)->size.width,
617 W_VIEW(tPtr)->size.height, WRRaised);
618 break;
620 case WTNoTabsLineBorder:
621 W_DrawRelief(scr, W_VIEW(tPtr)->window, 0, 0, W_VIEW(tPtr)->size.width,
622 W_VIEW(tPtr)->size.height, WRSimple);
623 break;
625 case WTNoTabsNoBorder:
626 break;
631 static void
632 destroyTabView(TabView *tPtr)
634 int i;
636 for (i = 0; i < tPtr->itemCount; i++) {
637 WMSetTabViewItemView(tPtr->items[i], NULL);
638 WMDestroyTabViewItem(tPtr->items[i]);
640 wfree(tPtr->items);
642 WMReleaseColor(tPtr->lightGray);
643 WMReleaseColor(tPtr->tabColor);
644 WMReleaseFont(tPtr->font);
646 wfree(tPtr);
649 /******************************************************************/
652 typedef struct W_TabViewItem {
653 WMTabView *tabView;
655 W_View *view;
657 char *label;
659 int identifier;
661 struct {
662 unsigned visible:1;
663 } flags;
664 } TabViewItem;
667 static void
668 W_SetTabViewItemParent(WMTabViewItem *item, WMTabView *parent)
670 item->tabView = parent;
674 static void
675 W_DrawLabel(WMTabViewItem *item, Drawable d, WMRect rect)
677 WMScreen *scr = W_VIEW(item->tabView)->screen;
679 if (!item->label)
680 return;
682 WMDrawString(scr, d, WMColorGC(scr->black), item->tabView->font,
683 rect.pos.x, rect.pos.y, item->label, strlen(item->label));
687 static void
688 W_UnmapTabViewItem(WMTabViewItem *item)
690 wassertr(item->view);
692 W_UnmapView(item->view);
694 item->flags.visible = 0;
698 static void
699 W_MapTabViewItem(WMTabViewItem *item)
701 wassertr(item->view);
703 W_MapView(item->view);
705 item->flags.visible = 1;
709 static WMView*
710 W_TabViewItemView(WMTabViewItem *item)
712 return item->view;
716 WMTabViewItem*
717 WMCreateTabViewItemWithIdentifier(int identifier)
719 WMTabViewItem *item;
721 item = wmalloc(sizeof(WMTabViewItem));
722 memset(item, 0, sizeof(WMTabViewItem));
724 item->identifier = identifier;
726 return item;
731 WMGetTabViewItemIdentifier(WMTabViewItem *item)
733 return item->identifier;
737 void
738 WMSetTabViewFont(WMTabView *tPtr, WMFont *font)
740 if (tPtr->font)
741 WMReleaseFont(tPtr->font);
743 tPtr->font = WMRetainFont(font);
744 tPtr->tabHeight = WMFontHeight(tPtr->font) + 3;
745 recalcTabWidth(tPtr);
749 void
750 WMSetTabViewItemLabel(WMTabViewItem *item, char *label)
752 if (item->label)
753 wfree(item->label);
755 item->label = wstrdup(label);
757 if (item->tabView)
758 recalcTabWidth(item->tabView);
762 char*
763 WMGetTabViewItemLabel(WMTabViewItem *item)
765 return item->label;
769 void
770 WMSetTabViewItemView(WMTabViewItem *item, WMView *view)
772 item->view = view;
776 WMView*
777 WMGetTabViewItemView(WMTabViewItem *item)
779 return item->view;
783 void
784 WMDestroyTabViewItem(WMTabViewItem *item)
786 if (item->label)
787 wfree(item->label);
789 if (item->view)
790 W_DestroyView(item->view);
792 wfree(item);