cvsimport
[fvwm.git] / modules / FvwmButtons / draw.c
blob20119a8ce41d5645c58ebec1fca0b6ceea032212
1 /* -*-c-*- */
2 /*
3 * Fvwmbuttons, copyright 1996, Jarl Totland
5 * This module, and the entire GoodStuff program, and the concept for
6 * interfacing this module to the Window Manager, are all original work
7 * by Robert Nation
9 * Copyright 1993, Robert Nation. No guarantees or warantees or anything
10 * are provided or implied in any way whatsoever. Use this program at your
11 * own risk. Permission to use this program for any purpose is given,
12 * as long as the copyright is kept intact.
16 /* This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 /* ------------------------------- includes -------------------------------- */
33 #include "config.h"
35 #ifdef HAVE_SYS_BSDTYPES_H
36 #include <sys/bsdtypes.h> /* Saul */
37 #endif
39 #include <unistd.h>
40 #include <ctype.h>
41 #include <stdio.h>
43 #include <X11/Xlib.h>
44 #include <X11/Xutil.h>
45 #include <X11/Xproto.h>
46 #include <X11/Xatom.h>
47 #include <X11/Intrinsic.h>
49 #include "libs/Colorset.h"
50 #include "libs/fvwmlib.h"
51 #include "libs/Rectangles.h"
52 #include "libs/FShape.h" /* FShapesSupported */
53 #include "libs/Graphics.h"
54 #include "FvwmButtons.h"
55 #include "misc.h" /* ConstrainSize() */
56 #include "icons.h" /* ConfigureIconWindow() */
57 #include "button.h"
58 #include "draw.h"
60 extern Pixmap shapeMask;
61 extern GC transGC;
63 /* ---------------- Functions that design and draw buttons ----------------- */
65 /**
66 *** RelieveButton()
67 *** Draws the relief pattern around a window.
68 **/
69 void RelieveButton(Window wn,int width,int x,int y,int w,int h,Pixel relief,
70 Pixel shadow,int rev)
72 XGCValues gcv;
73 GC swapGC, reliefGC, shadowGC;
75 if(!width)
76 return;
78 gcv.foreground=relief;
79 XChangeGC(Dpy,NormalGC,GCForeground,&gcv);
80 reliefGC=NormalGC;
82 gcv.foreground=shadow;
83 XChangeGC(Dpy,ShadowGC,GCForeground,&gcv);
84 shadowGC=ShadowGC;
86 if(width<0)
88 width=-width;
89 rev = !rev;
91 if(rev)
93 swapGC=reliefGC;
94 reliefGC=shadowGC;
95 shadowGC=swapGC;
98 RelieveRectangle(Dpy,wn,x,y,w-1,h-1,reliefGC,shadowGC,width);
102 *** MakeButton()
103 *** To position subwindows in a button: icons and swallowed windows.
105 void MakeButton(button_info *b)
107 /* This is resposible for drawing the contents of a button, placing the
108 * icon and/or swallowed item in the correct position inside potential
109 * padding or frame.
111 int ih,iw,ix,iy;
112 FlocaleFont *Ffont;
114 if(!b)
116 fprintf(stderr,"%s: BUG: MakeButton called with NULL pointer\n",MyName);
117 exit(2);
119 if(b->flags.b_Container)
121 fprintf(stderr,"%s: BUG: MakeButton called with container\n",MyName);
122 exit(2);
125 if (b->flags.b_Swallow && !b->flags.b_Icon &&
126 buttonSwallowCount(b) < 3)
128 return;
131 /* Check if parent container has an icon as background */
132 if (b->parent->c->flags.b_IconBack || b->parent->c->flags.b_IconParent)
133 b->flags.b_IconParent = 1;
135 /* Check if parent container has a colorset definition as background */
136 if (b->parent->c->flags.b_Colorset || b->parent->c->flags.b_ColorsetParent)
137 b->flags.b_ColorsetParent = 1;
139 Ffont = buttonFont(b);
141 GetInternalSize(b,&ix,&iy,&iw,&ih);
143 /* At this point iw,ih,ix and iy should be correct. Now all we have to do is
144 place title and iconwin in their proper positions */
146 /* For now, hardcoded window centered, title bottom centered, below window */
147 if(buttonSwallowCount(b)==3 && b->flags.b_Swallow)
149 long supplied;
150 if(!b->IconWin)
152 fprintf(stderr,"%s: BUG: Swallowed window has no IconWin\n",MyName);
153 exit(2);
156 if (b->flags.b_Title && Ffont && !(buttonJustify(b)&b_Horizontal))
157 ih -= Ffont->height;
159 b->icon_w=iw;
160 b->icon_h=ih;
162 if (iw>0 && ih>0)
164 if (!(buttonSwallow(b)&b_NoHints))
166 if(!XGetWMNormalHints(Dpy,b->IconWin,b->hints,&supplied))
167 b->hints->flags=0;
168 ConstrainSize(b->hints, &b->icon_w, &b->icon_h);
170 if (b->flags.b_Right)
171 ix += iw-b->icon_w;
172 else if (!b->flags.b_Left)
173 ix += (iw-b->icon_w)/2;
174 if (!b->flags.b_Top)
175 iy += (ih-b->icon_h)/2;
176 XMoveResizeWindow(Dpy,b->IconWin,ix,iy,b->icon_w,b->icon_h);
178 else
180 XMoveWindow(Dpy,b->IconWin, -32768, -32768);
185 static int buttonBGColorset(button_info *b)
187 if (b->flags.b_Hangon)
189 if (b->flags.b_PressColorset)
190 return b->pressColorset;
191 if (UberButton->c->flags.b_PressColorset &&
192 (buttonSwallow(b) & b_UseOld))
194 return UberButton->c->pressColorset;
197 else if (b == ActiveButton && b->flags.b_ActiveColorset)
198 return b->activeColorset; /* button has it's own active CS */
199 else if (b == ActiveButton && UberButton->c->flags.b_ActiveColorset)
200 return UberButton->c->activeColorset; /* global active CS */
201 else if (b == CurrentButton && b->flags.b_PressColorset)
202 return b->pressColorset; /* button has it's own press CS */
203 else if (b == CurrentButton && UberButton->c->flags.b_PressColorset)
204 return UberButton->c->pressColorset; /* global press CS */
206 if (b->flags.b_Colorset)
207 return b->colorset;
209 return -1;
215 *** RedrawButton()
217 *** draw can be:
218 *** DRAW_CLEAN: draw the relief, the foreground bg if any and if this
219 *** the case draw the title and the icon if b_Icon. This is safe
220 *** but the button background may be not repaint (if the bg of the button
221 *** come from a parent button).
222 *** DRAW_ALL: as above but draw the title and the icon if b_Icon in
223 *** any case. WARRNING: the title and the icon (b_Icon) must be cleaned:
224 *** if the button has a bg this is the case, but if the bg of the button
225 *** come from a parent button this is not the case and xft title and
226 *** icons will be not draw correctly.
227 *** So DRAW_ALL is ok only when you draw buttons recursively.
228 *** DRAW_FORCE: draw the button and its parent fg bg. Use this only if
229 *** you need to draw an individual button (not in a recursive drawing)
231 *** If pev is != NULL the expose member give to use the rectangle on
232 *** which we want to draw.
235 void RedrawButton(button_info *b, int draw, XEvent *pev)
237 int i,j,k,BH,BW;
238 int f,of,x,y,px,py;
239 int ix,iy,iw,ih;
240 XGCValues gcv;
241 int rev = 0;
242 int rev_xor = 0;
243 Pixel fc;
244 Pixel bc;
245 Pixel hc;
246 Pixel sc;
247 int cset;
248 XRectangle clip;
249 Bool clean = False;
250 Bool cleaned = False;
251 Bool clear_bg = False;
252 char *title;
254 if (b->parent == NULL)
256 return;
259 cset = buttonColorset(b);
260 if (cset >= 0)
262 fc = Colorset[cset].fg;
263 bc = Colorset[cset].bg;
264 hc = Colorset[cset].hilite;
265 sc = Colorset[cset].shadow;
267 else
269 fc = buttonFore(b);
270 bc = buttonBack(b);
271 hc = buttonHilite(b);
272 sc = buttonShadow(b);
275 BW = buttonWidth(b);
276 BH = buttonHeight(b);
277 buttonInfo(b,&x,&y,&px,&py,&f);
278 GetInternalSize(b,&ix,&iy,&iw,&ih);
280 /* This probably isn't the place for this, but it seems to work here
281 * and not elsewhere, so... */
282 if (b->flags.b_Swallow && (buttonSwallowCount(b) == 3) &&
283 b->IconWin != None)
285 XSetWindowBorderWidth(Dpy,b->IconWin,0);
288 if (draw == DRAW_FORCE)
290 button_info *tmpb;
292 if (buttonBackgroundButton(b, &tmpb))
294 /* found the button which holds the bg */
295 if (b == tmpb)
297 /* ok the button will be cleaned now */
298 clean = True;
300 else
302 XEvent e;
304 cleaned = True;
305 e.xexpose.x = ix;
306 e.xexpose.y = iy;
307 e.xexpose.width = iw;
308 e.xexpose.height = ih;
310 RedrawButton(tmpb, DRAW_CLEAN, &e);
313 else
315 /* bg of the button is the true bg */
316 clean = True;
317 clear_bg = True;
321 /* ------------------------------------------------------------------ */
323 if (b->flags.b_Panel)
325 if ((b->newflags.panel_mapped))
327 rev = 1;
329 if (b == CurrentButton && is_pointer_in_current_button)
331 rev_xor = 1;
334 else if (b->flags.b_Hangon ||
335 (b == CurrentButton && is_pointer_in_current_button))
337 /* Hanging swallow or held down by user */
338 rev = 1;
341 if (b->flags.b_Action)
343 /* If this is a Desk button that takes you to here.. */
344 int n;
346 for (n = 0; n < 4; n++)
348 if (b->action[n])
350 if (strncasecmp(
351 b->action[n], "GotoDeskAndPage", 15)
352 == 0)
354 k = sscanf(&b->action[n][15], "%d", &i);
355 if (k == 1 && i == new_desk)
357 rev=1;
359 break;
361 else if (strncasecmp(
362 b->action[n], "Desk", 4) == 0)
364 k = sscanf(
365 &b->action[n][4], "%d%d", &i,
366 &j);
367 if (k == 2 && i == 0 && j == new_desk)
369 rev=1;
371 break;
373 else if (strncasecmp(
374 b->action[n], "GotoDesk", 8) == 0)
376 k = sscanf(
377 &b->action[n][8], "%d%d", &i,
378 &j);
379 if (k == 2 && i == 0 && j == new_desk)
381 rev=1;
383 break;
389 /* ------------------------------------------------------------------ */
390 of = f;
391 f=abs(f);
393 if (draw == DRAW_CLEAN)
395 clean = True;
397 else if (draw == DRAW_ALL)
399 /* this means that the button is clean or it will be
400 * cleaned now */
401 cleaned = True;
402 clean = True;
405 if(clean && BW>2*f && BH>2*f)
407 Bool do_draw = True;
409 gcv.foreground = bc;
410 XChangeGC(Dpy,NormalGC,GCForeground,&gcv);
411 clip.x = x+f;
412 clip.y = y+f;
413 clip.width = BW-2*f;
414 clip.height = BH-2*f;
416 do_draw = (!pev ||
417 frect_get_intersection(
418 x+f, y+f, BW-2*f, BH-2*f,
419 pev->xexpose.x, pev->xexpose.y,
420 pev->xexpose.width, pev->xexpose.height,
421 &clip));
423 if(b->flags.b_Container)
425 if (b->c->flags.b_Colorset)
427 if (do_draw)
429 cleaned = True;
430 SetClippedRectangleBackground(
431 Dpy, MyWindow,
432 x+f, y+f, BW-2*f, BH-2*f, &clip,
433 &Colorset[b->c->colorset],
434 Pdepth, NormalGC);
437 else if (!b->c->flags.b_IconParent &&
438 !b->c->flags.b_ColorsetParent)
440 int x1 = x + f;
441 int y1 = y + f;
442 int w1 = px;
443 int h1 = py;
444 int w2 = w1;
445 int h2 = h1;
446 int w = BW - 2 * f;
447 int h = BH - 2 * f;
448 int lx = x1, ly = y1, lh = 0, lw = 0;
450 w2 += iw - b->c->width;
451 h2 += ih - b->c->height;
453 if(w1)
455 lw = w1;
456 lh = h;
458 if(w2)
460 lx = x1+w-w2;
461 lw = w2;
462 lh = h;
464 if(h1)
466 lw = w;
467 lh = h1;
469 if(h2)
471 ly = y1+h-h2;
472 lw = w;
473 lh = h2;
475 clip.x = lx;
476 clip.y = ly;
477 clip.width = lw;
478 clip.height = lh;
479 if ((lw>0 && lh >0) &&
480 (!pev || frect_get_intersection(
481 lx, ly, lw, lh,
482 pev->xexpose.x, pev->xexpose.y,
483 pev->xexpose.width, pev->xexpose.height,
484 &clip)))
486 XFillRectangle(
487 Dpy,MyWindow,NormalGC,
488 clip.x, clip.y,
489 clip.width,clip.height);
492 } /* container */
493 else if (buttonSwallowCount(b) == 3 && b->flags.b_Swallow &&
494 b->flags.b_Colorset)
496 /* Set the back color of the buttons for shaped apps
497 * (olicha 00-03-09) and also for transparent modules */
498 if (do_draw)
500 cleaned = True;
501 SetClippedRectangleBackground(
502 Dpy, MyWindow, x+f, y+f, BW-2*f, BH-2*f,
503 &clip, &Colorset[b->colorset],
504 Pdepth, NormalGC);
506 #if 0
507 if (!b->swallow & b_FvwmModule)
509 change_swallowed_window_colorset(b, False);
511 #endif
513 else if (do_draw)
515 int cs = buttonBGColorset(b);
516 cleaned = True;
517 XClearArea(Dpy, MyWindow, clip.x,
518 clip.y, clip.width, clip.height,
519 False);
520 if (cs >= 0)
522 SetClippedRectangleBackground(
523 Dpy, MyWindow, x, y, BW, BH, &clip,
524 &Colorset[cs],Pdepth, NormalGC);
526 else if (b->flags.b_Back &&
527 !UberButton->c->flags.b_Colorset)
529 XFillRectangle(Dpy, MyWindow, NormalGC,
530 clip.x, clip.y, clip.width,
531 clip.height);
534 /* b_TransBack is set when using "Pixmap none" -
535 this uses the X11 non-rectangular window shape
536 extension. Dynamically changing the window
537 shape (due to ActiveIcon, etc.) can cause an
538 infinite number of Enter/Leave-Notify events,
539 so we need to be careful. Currently, to avoid this
540 we ensure ActiveColorset is specified if
541 ActiveIcon is used. */
542 if (UberButton->c->flags.b_TransBack &&
543 FShapesSupported)
545 int w = buttonWidth(b), h = buttonHeight(b);
546 FvwmPicture *icon = buttonIcon(b);
547 XGCValues gcv;
549 if (transGC == NULL)
551 transGC = fvwmlib_XCreateGC(
552 Dpy, shapeMask, 0, &gcv);
554 XSetClipMask(Dpy, transGC, None);
555 XSetForeground(Dpy, transGC, 0);
556 XFillRectangle(Dpy, shapeMask, transGC,x,y,w,h);
557 XSetForeground(Dpy, transGC, 1);
559 if (!icon || icon->mask == None || cs >= 0)
561 XFillRectangle(Dpy, shapeMask, transGC,
562 x, y, w, h);
564 else
566 int xx, yy, ww, hh;
567 GetIconPosition(b,
568 icon, &xx, &yy, &ww, &hh);
569 XCopyArea(Dpy, icon->mask, shapeMask,
570 transGC, 0, 0, icon->width,
571 icon->height, xx, yy);
573 if (buttonTitle(b))
575 DrawTitle(b, shapeMask, transGC, NULL);
577 FShapeCombineMask(Dpy, MyWindow, FShapeBounding,
578 0, 0, shapeMask, FShapeSet);
583 /* ------------------------------------------------------------------ */
585 title = buttonTitle(b);
586 if (cleaned)
588 gcv.foreground = fc;
589 gcv.background = bc;
590 XChangeGC(Dpy, NormalGC, GCForeground | GCBackground, &gcv);
591 if (title)
593 DrawTitle(b, MyWindow, NormalGC, pev);
597 if (title == NULL && b->flags.b_Panel &&
598 (b->panel_flags.panel_indicator))
600 XGCValues gcv;
601 int ix, iy, iw, ih;
602 int is;
604 /* draw the panel indicator, but not if there is a title */
605 /* FIXEME: and if we have an icon */
606 if (b->slide_direction != SLIDE_GEOMETRY &&
607 b->indicator_size != 0 && (b->indicator_size & 1) == 0)
609 /* make sure we have an odd number */
610 b->indicator_size--;
612 is = b->indicator_size;
614 gcv.foreground = hc;
615 XChangeGC(Dpy,NormalGC,GCForeground,&gcv);
616 gcv.foreground = sc;
617 XChangeGC(Dpy,ShadowGC,GCForeground,&gcv);
619 GetInternalSize(b, &ix, &iy, &iw, &ih);
620 if (is != 0)
622 /* limit to user specified size */
623 if (is < iw)
625 ix += (iw - is) / 2;
626 iw = is;
628 if (is < ih)
630 iy += (ih - is) / 2;
631 ih = is;
635 if (b->slide_direction == SLIDE_GEOMETRY)
637 if (ih < iw)
639 ix += (iw - ih) / 2;
640 iw = ih;
642 else if (iw < ih)
644 iy += (ih - iw) / 2;
645 ih = iw;
647 RelieveRectangle(
648 Dpy, MyWindow, ix, iy, iw - 1, ih - 1,
649 NormalGC, ShadowGC, 1);
651 else
653 char dir = b->slide_direction;
655 if (rev)
657 switch (dir)
659 case SLIDE_UP:
660 dir = SLIDE_DOWN;
661 break;
662 case SLIDE_DOWN:
663 dir = SLIDE_UP;
664 break;
665 case SLIDE_LEFT:
666 dir = SLIDE_RIGHT;
667 break;
668 case SLIDE_RIGHT:
669 dir = SLIDE_LEFT;
670 break;
673 DrawTrianglePattern(
674 Dpy, MyWindow, NormalGC, ShadowGC, None,
675 ix, iy, iw, ih, 0, dir, 1, 0, 0);
677 } /* panel indicator */
679 if (cleaned)
681 if (iconFlagSet(b))
683 /* draw icon */
684 DrawForegroundIcon(b, pev);
688 /* relief */
689 RelieveButton(MyWindow,of,x,y,BW,BH,hc,sc,rev ^ rev_xor);
693 *** Writes out title.
695 void DrawTitle(button_info *b,Window win,GC gc, XEvent *pev)
697 int BH;
698 int ix,iy,iw,ih;
699 FlocaleFont *Ffont=buttonFont(b);
700 int justify=buttonJustify(b);
701 int l,i,xpos;
702 char *s = NULL;
703 int just=justify&b_TitleHoriz; /* Left, center, right */
704 XGCValues gcv;
705 XRectangle clip;
706 Region region = None;
707 FvwmPicture *pic;
708 unsigned short bIconFlagSet;
710 BH = buttonHeight(b);
712 GetInternalSize(b,&ix,&iy,&iw,&ih);
714 /* ------------------------------------------------------------------ */
716 s = buttonTitle(b);
718 if (!s || !Ffont)
719 return;
721 if (Ffont->font)
723 gcv.font = Ffont->font->fid;
724 XChangeGC(Dpy, gc, GCFont, &gcv);
727 pic = buttonIcon(b);
728 bIconFlagSet = iconFlagSet(b);
730 /* If a title is to be shown, truncate it until it fits */
731 if(justify&b_Horizontal && !b->flags.b_Right)
733 if (bIconFlagSet)
735 ix += pic->width+buttonXPad(b);
736 iw -= pic->width+buttonXPad(b);
738 else if (b->flags.b_Swallow && buttonSwallowCount(b)==3)
740 ix += b->icon_w+buttonXPad(b);
741 iw -= b->icon_w+buttonXPad(b);
745 l = strlen(s);
746 i = FlocaleTextWidth(Ffont,s,l);
748 if(i>iw)
750 if(just==2)
752 while(i>iw && *s)
754 i=FlocaleTextWidth(Ffont,++s,--l);
757 else /* Left or center - cut off its tail */
759 while(i>iw && l>0)
761 i=FlocaleTextWidth(Ffont,s,--l);
765 if(just==0 || ((justify&b_Horizontal) && b->flags.b_Right)) /* Left */
767 xpos=ix;
769 else if(just==2) /* Right */
771 xpos=max(ix,ix+iw-i);
773 else /* Centered, I guess */
775 xpos=ix+(iw-i)/2;
778 if(*s && l>0 && BH>=Ffont->height) /* Clip it somehow? */
780 FlocaleWinString FwinString;
781 int cset;
783 memset(&FwinString, 0, sizeof(FwinString));
784 FwinString.str = s;
785 FwinString.win = win;
786 FwinString.gc = gc;
787 cset = buttonColorset(b);
788 if (cset >= 0)
790 FwinString.colorset = &Colorset[cset];
791 FwinString.flags.has_colorset = 1;
793 FwinString.x = xpos;
794 /* If there is more than the title, put it at the bottom */
795 /* Unless stack flag is set, put it to the right of icon */
796 if ((bIconFlagSet ||
797 ((buttonSwallowCount(b)==3) && b->flags.b_Swallow)) &&
798 !(justify&b_Horizontal))
800 FwinString.y = iy+ih-Ffont->descent;
801 /* Shrink the space available for icon/window */
802 ih-=Ffont->height;
804 /* Or else center vertically */
805 else
807 FwinString.y =
808 iy + (ih+ Ffont->ascent - Ffont->descent)/2;
811 clip.x = FwinString.x;
812 clip.y = FwinString.y - Ffont->ascent;
813 clip.width = i;
814 clip.height = Ffont->height;
815 if (pev)
817 if (!frect_get_intersection(
818 FwinString.x, FwinString.y - Ffont->ascent,
819 i, Ffont->height,
820 pev->xexpose.x, pev->xexpose.y,
821 pev->xexpose.width, pev->xexpose.height,
822 &clip))
824 return;
827 XSetClipRectangles(
828 Dpy, FwinString.gc, 0, 0, &clip, 1, Unsorted);
829 region = XCreateRegion();
830 XUnionRectWithRegion (&clip, region, region);
831 FwinString.flags.has_clip_region = True;
832 FwinString.clip_region = region;
833 if (0 && Ffont->fftf.fftfont != NULL)
835 XClearArea(
836 Dpy, win,
837 clip.x, clip.y, clip.width, clip.height,
838 False);
840 FlocaleDrawString(Dpy, Ffont, &FwinString, 0);
841 XSetClipMask(Dpy, FwinString.gc, None);
842 if (region)
844 XDestroyRegion(region);