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
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 -------------------------------- */
35 #ifdef HAVE_SYS_BSDTYPES_H
36 #include <sys/bsdtypes.h> /* Saul */
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() */
60 extern Pixmap shapeMask
;
63 /* ---------------- Functions that design and draw buttons ----------------- */
67 *** Draws the relief pattern around a window.
69 void RelieveButton(Window wn
,int width
,int x
,int y
,int w
,int h
,Pixel relief
,
73 GC swapGC
, reliefGC
, shadowGC
;
78 gcv
.foreground
=relief
;
79 XChangeGC(Dpy
,NormalGC
,GCForeground
,&gcv
);
82 gcv
.foreground
=shadow
;
83 XChangeGC(Dpy
,ShadowGC
,GCForeground
,&gcv
);
98 RelieveRectangle(Dpy
,wn
,x
,y
,w
-1,h
-1,reliefGC
,shadowGC
,width
);
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
116 fprintf(stderr
,"%s: BUG: MakeButton called with NULL pointer\n",MyName
);
119 if(b
->flags
.b_Container
)
121 fprintf(stderr
,"%s: BUG: MakeButton called with container\n",MyName
);
125 if (b
->flags
.b_Swallow
&& !b
->flags
.b_Icon
&&
126 buttonSwallowCount(b
) < 3)
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
)
152 fprintf(stderr
,"%s: BUG: Swallowed window has no IconWin\n",MyName
);
156 if (b
->flags
.b_Title
&& Ffont
&& !(buttonJustify(b
)&b_Horizontal
))
164 if (!(buttonSwallow(b
)&b_NoHints
))
166 if(!XGetWMNormalHints(Dpy
,b
->IconWin
,b
->hints
,&supplied
))
168 ConstrainSize(b
->hints
, &b
->icon_w
, &b
->icon_h
);
170 if (b
->flags
.b_Right
)
172 else if (!b
->flags
.b_Left
)
173 ix
+= (iw
-b
->icon_w
)/2;
175 iy
+= (ih
-b
->icon_h
)/2;
176 XMoveResizeWindow(Dpy
,b
->IconWin
,ix
,iy
,b
->icon_w
,b
->icon_h
);
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
)
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
)
250 Bool cleaned
= False
;
251 Bool clear_bg
= False
;
254 if (b
->parent
== NULL
)
259 cset
= buttonColorset(b
);
262 fc
= Colorset
[cset
].fg
;
263 bc
= Colorset
[cset
].bg
;
264 hc
= Colorset
[cset
].hilite
;
265 sc
= Colorset
[cset
].shadow
;
271 hc
= buttonHilite(b
);
272 sc
= buttonShadow(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) &&
285 XSetWindowBorderWidth(Dpy
,b
->IconWin
,0);
288 if (draw
== DRAW_FORCE
)
292 if (buttonBackgroundButton(b
, &tmpb
))
294 /* found the button which holds the bg */
297 /* ok the button will be cleaned now */
307 e
.xexpose
.width
= iw
;
308 e
.xexpose
.height
= ih
;
310 RedrawButton(tmpb
, DRAW_CLEAN
, &e
);
315 /* bg of the button is the true bg */
321 /* ------------------------------------------------------------------ */
323 if (b
->flags
.b_Panel
)
325 if ((b
->newflags
.panel_mapped
))
329 if (b
== CurrentButton
&& is_pointer_in_current_button
)
334 else if (b
->flags
.b_Hangon
||
335 (b
== CurrentButton
&& is_pointer_in_current_button
))
337 /* Hanging swallow or held down by user */
341 if (b
->flags
.b_Action
)
343 /* If this is a Desk button that takes you to here.. */
346 for (n
= 0; n
< 4; n
++)
351 b
->action
[n
], "GotoDeskAndPage", 15)
354 k
= sscanf(&b
->action
[n
][15], "%d", &i
);
355 if (k
== 1 && i
== new_desk
)
361 else if (strncasecmp(
362 b
->action
[n
], "Desk", 4) == 0)
365 &b
->action
[n
][4], "%d%d", &i
,
367 if (k
== 2 && i
== 0 && j
== new_desk
)
373 else if (strncasecmp(
374 b
->action
[n
], "GotoDesk", 8) == 0)
377 &b
->action
[n
][8], "%d%d", &i
,
379 if (k
== 2 && i
== 0 && j
== new_desk
)
389 /* ------------------------------------------------------------------ */
393 if (draw
== DRAW_CLEAN
)
397 else if (draw
== DRAW_ALL
)
399 /* this means that the button is clean or it will be
405 if(clean
&& BW
>2*f
&& BH
>2*f
)
410 XChangeGC(Dpy
,NormalGC
,GCForeground
,&gcv
);
414 clip
.height
= BH
-2*f
;
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
,
423 if(b
->flags
.b_Container
)
425 if (b
->c
->flags
.b_Colorset
)
430 SetClippedRectangleBackground(
432 x
+f
, y
+f
, BW
-2*f
, BH
-2*f
, &clip
,
433 &Colorset
[b
->c
->colorset
],
437 else if (!b
->c
->flags
.b_IconParent
&&
438 !b
->c
->flags
.b_ColorsetParent
)
448 int lx
= x1
, ly
= y1
, lh
= 0, lw
= 0;
450 w2
+= iw
- b
->c
->width
;
451 h2
+= ih
- b
->c
->height
;
479 if ((lw
>0 && lh
>0) &&
480 (!pev
|| frect_get_intersection(
482 pev
->xexpose
.x
, pev
->xexpose
.y
,
483 pev
->xexpose
.width
, pev
->xexpose
.height
,
487 Dpy
,MyWindow
,NormalGC
,
489 clip
.width
,clip
.height
);
493 else if (buttonSwallowCount(b
) == 3 && b
->flags
.b_Swallow
&&
496 /* Set the back color of the buttons for shaped apps
497 * (olicha 00-03-09) and also for transparent modules */
501 SetClippedRectangleBackground(
502 Dpy
, MyWindow
, x
+f
, y
+f
, BW
-2*f
, BH
-2*f
,
503 &clip
, &Colorset
[b
->colorset
],
507 if (!b
->swallow
& b_FvwmModule
)
509 change_swallowed_window_colorset(b
, False
);
515 int cs
= buttonBGColorset(b
);
517 XClearArea(Dpy
, MyWindow
, clip
.x
,
518 clip
.y
, clip
.width
, clip
.height
,
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
,
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
&&
545 int w
= buttonWidth(b
), h
= buttonHeight(b
);
546 FvwmPicture
*icon
= buttonIcon(b
);
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
,
568 icon
, &xx
, &yy
, &ww
, &hh
);
569 XCopyArea(Dpy
, icon
->mask
, shapeMask
,
570 transGC
, 0, 0, icon
->width
,
571 icon
->height
, xx
, yy
);
575 DrawTitle(b
, shapeMask
, transGC
, NULL
);
577 FShapeCombineMask(Dpy
, MyWindow
, FShapeBounding
,
578 0, 0, shapeMask
, FShapeSet
);
583 /* ------------------------------------------------------------------ */
585 title
= buttonTitle(b
);
590 XChangeGC(Dpy
, NormalGC
, GCForeground
| GCBackground
, &gcv
);
593 DrawTitle(b
, MyWindow
, NormalGC
, pev
);
597 if (title
== NULL
&& b
->flags
.b_Panel
&&
598 (b
->panel_flags
.panel_indicator
))
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 */
612 is
= b
->indicator_size
;
615 XChangeGC(Dpy
,NormalGC
,GCForeground
,&gcv
);
617 XChangeGC(Dpy
,ShadowGC
,GCForeground
,&gcv
);
619 GetInternalSize(b
, &ix
, &iy
, &iw
, &ih
);
622 /* limit to user specified size */
635 if (b
->slide_direction
== SLIDE_GEOMETRY
)
648 Dpy
, MyWindow
, ix
, iy
, iw
- 1, ih
- 1,
649 NormalGC
, ShadowGC
, 1);
653 char dir
= b
->slide_direction
;
674 Dpy
, MyWindow
, NormalGC
, ShadowGC
, None
,
675 ix
, iy
, iw
, ih
, 0, dir
, 1, 0, 0);
677 } /* panel indicator */
684 DrawForegroundIcon(b
, pev
);
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
)
699 FlocaleFont
*Ffont
=buttonFont(b
);
700 int justify
=buttonJustify(b
);
703 int just
=justify
&b_TitleHoriz
; /* Left, center, right */
706 Region region
= None
;
708 unsigned short bIconFlagSet
;
710 BH
= buttonHeight(b
);
712 GetInternalSize(b
,&ix
,&iy
,&iw
,&ih
);
714 /* ------------------------------------------------------------------ */
723 gcv
.font
= Ffont
->font
->fid
;
724 XChangeGC(Dpy
, gc
, GCFont
, &gcv
);
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
)
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
);
746 i
= FlocaleTextWidth(Ffont
,s
,l
);
754 i
=FlocaleTextWidth(Ffont
,++s
,--l
);
757 else /* Left or center - cut off its tail */
761 i
=FlocaleTextWidth(Ffont
,s
,--l
);
765 if(just
==0 || ((justify
&b_Horizontal
) && b
->flags
.b_Right
)) /* Left */
769 else if(just
==2) /* Right */
771 xpos
=max(ix
,ix
+iw
-i
);
773 else /* Centered, I guess */
778 if(*s
&& l
>0 && BH
>=Ffont
->height
) /* Clip it somehow? */
780 FlocaleWinString FwinString
;
783 memset(&FwinString
, 0, sizeof(FwinString
));
785 FwinString
.win
= win
;
787 cset
= buttonColorset(b
);
790 FwinString
.colorset
= &Colorset
[cset
];
791 FwinString
.flags
.has_colorset
= 1;
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 */
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 */
804 /* Or else center vertically */
808 iy
+ (ih
+ Ffont
->ascent
- Ffont
->descent
)/2;
811 clip
.x
= FwinString
.x
;
812 clip
.y
= FwinString
.y
- Ffont
->ascent
;
814 clip
.height
= Ffont
->height
;
817 if (!frect_get_intersection(
818 FwinString
.x
, FwinString
.y
- Ffont
->ascent
,
820 pev
->xexpose
.x
, pev
->xexpose
.y
,
821 pev
->xexpose
.width
, pev
->xexpose
.height
,
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
)
837 clip
.x
, clip
.y
, clip
.width
, clip
.height
,
840 FlocaleDrawString(Dpy
, Ffont
, &FwinString
, 0);
841 XSetClipMask(Dpy
, FwinString
.gc
, None
);
844 XDestroyRegion(region
);