--- config.h.in.orig Fri Aug 23 22:45:55 2002 +++ config.h.in Fri Aug 23 22:47:53 2002 @@ -2,6 +2,9 @@ /* Suffix for config filenames */ #define FVWMRC ".fvwm2rc" +/* Define for fancy, multi-pixmap-themeable titlebars */ +#undef FANCY_TITLEBARS + /* Define if gdk-imlib is used */ #undef GDK_IMLIB --- fvwm/fvwm2.1.orig Fri Aug 23 22:46:02 2002 +++ fvwm/fvwm2.1 Fri Aug 23 22:47:53 2002 @@ -6313,7 +6313,46 @@ ButtonStyle All ActiveUp (-- flat) Inactive \\ (-- flat) .EE +If fvwm is compiled with the FANCY_TITLEBARS option enabled, an additional +TitleStyle is available: MultiPixmap. This allows you to specify different +pixmaps for different parts of the titlebar. Some of them are tiled or +stretched to fit a particular space; others are discrete "transition" pixmaps +which are not resized in any way. The definable pixmaps are: +.EX +- Main (tiled/stretched): The main titlebar pixmap +- LeftMain (tiled/stretched): Left of title text +- RightMain (tiled/stretched): Right of title text +- UnderText (tiled/stretched): Underneath title text +- LeftOfText: Pixmap just to the left of the title text +- RightOfText: Pixmap just to the right of the title text +- LeftEnd: Pixmap at the far left end of the titlebar + (just before buttons) +- RightEnd: Pixmap at the far right end of the titlebar + (just before buttons) +- Buttons (tiled): Tiled under buttons in case of + UseTitleStyle +- LeftButtons (tiled): Tiled under left buttons in case of + UseTitleStyle +- RightButtons (tiled): Tiled under right buttons in case of + UseTitleStyle +.EE +None of these are mandatory except for Main (or, if you don't define Main, +you must define both LeftMain and RightMain). If no "Buttons" pixmaps are +defined and UseTitleStyle is specified for one or more buttons, Main, +LeftMain, or RightMain will be used as appropriate. +The syntax for this style type is: +.EX +MultiPixmap
() , ... +.EE +continuing for whatever pixmaps you want to define. By default, all +non-transition pixmaps are tiled. If you want one stretched instead (this is +allowed for Main, LeftMain, RightMain, and UnderText), add "(stretched)" after +the section name: +.EX +MultiPixmap Main (stretched) foo.xpm, UnderText bar.xpm +.EE +Otherwise you can omit (), as shown with UnderText. .TP .BI "UpdateDecor [" decor "]" This command is kept mainly for backwards compatibility. Since --- fvwm/screen.h.orig Fri Aug 23 22:46:06 2002 +++ fvwm/screen.h Fri Aug 23 22:47:53 2002 @@ -54,6 +54,13 @@ #define COLORMAP_FOLLOWS_MOUSE 1 /* default */ #define COLORMAP_FOLLOWS_FOCUS 2 +#ifdef FANCY_TITLEBARS +#define TITLE_PADDING 5 +enum tb_pixmap_enum {TBP_MAIN, TBP_LEFT_MAIN, TBP_RIGHT_MAIN, TBP_UNDER_TEXT, + TBP_LEFT_OF_TEXT, TBP_RIGHT_OF_TEXT, TBP_LEFT_END, + TBP_RIGHT_END, TBP_BUTTONS, TBP_LEFT_BUTTONS, + TBP_RIGHT_BUTTONS, NUM_TB_PIXMAPS}; +#endif typedef struct { @@ -70,6 +77,9 @@ GradientButton , PixmapButton , TiledPixmapButton , +#ifdef FANCY_TITLEBARS + MultiPixmap , +#endif #ifdef MINI_ICONS MiniIconButton , #endif @@ -123,6 +133,10 @@ struct { Picture *p; +#ifdef FANCY_TITLEBARS + Picture **multi_pixmaps; + short multi_stretch_flags; +#endif Pixel back; struct { --- fvwm/borders.c.orig Fri Aug 23 22:46:10 2002 +++ fvwm/borders.c Fri Aug 23 22:47:53 2002 @@ -271,10 +271,29 @@ case MiniIconButton: case PixmapButton: case TiledPixmapButton: +#ifdef FANCY_TITLEBARS + case MultiPixmap: /* in case of UseTitleStyle */ +#endif if (type == PixmapButton || type == TiledPixmapButton) { p = df->u.p; } +#ifdef FANCY_TITLEBARS + else if (type == MultiPixmap) { + if (left1right0 && df->u.multi_pixmaps[TBP_LEFT_BUTTONS]) + p = df->u.multi_pixmaps[TBP_LEFT_BUTTONS]; + else if (!left1right0 && df->u.multi_pixmaps[TBP_RIGHT_BUTTONS]) + p = df->u.multi_pixmaps[TBP_RIGHT_BUTTONS]; + else if (df->u.multi_pixmaps[TBP_BUTTONS]) + p = df->u.multi_pixmaps[TBP_BUTTONS]; + else if (left1right0 && df->u.multi_pixmaps[TBP_LEFT_MAIN]) + p = df->u.multi_pixmaps[TBP_LEFT_MAIN]; + else if (!left1right0 && df->u.multi_pixmaps[TBP_RIGHT_MAIN]) + p = df->u.multi_pixmaps[TBP_RIGHT_MAIN]; + else + p = df->u.multi_pixmaps[TBP_MAIN]; + } +#endif else { if (!t->mini_icon) @@ -298,6 +317,9 @@ if (is_lowest && !p->mask && pbutton_background_pixmap) { if (type == TiledPixmapButton || +#ifdef FANCY_TITLEBARS + type == MultiPixmap || +#endif (p && p->width >= width && p->height >= height)) { /* better performance: set a background pixmap if possible, i.e. if the @@ -326,7 +348,11 @@ XClearWindow(dpy, win); } } - if (type != TiledPixmapButton) + if (type != TiledPixmapButton +#ifdef FANCY_TITLEBARS + && type != MultiPixmap +#endif + ) { if (DFS_H_JUSTIFICATION(df->style) == JUST_RIGHT) x += (int)(width - p->width); @@ -354,7 +380,11 @@ XSetClipMask(dpy, Scr.TransMaskGC, p->mask); XSetClipOrigin(dpy, Scr.TransMaskGC, x, y); - if (type != TiledPixmapButton) + if (type != TiledPixmapButton +#ifdef FANCY_TITLEBARS + && type != MultiPixmap +#endif + ) { int cx = x; int cy = y; @@ -448,6 +478,159 @@ return; } +#ifdef FANCY_TITLEBARS + +/**************************************************************************** + * + * Tile or stretch src into dest, starting at the given location and + * continuing for the given width and height. This is a utility function used + * by DrawMultiPixmapTitlebar. (tril@igs.net) + * + ****************************************************************************/ +static void RenderIntoWindow(GC gc, Picture *src, Window dest, int x_start, + int y_start, int width, int height, Bool stretch) +{ + Pixmap pm; + if (stretch) + pm = CreateStretchPixmap(dpy, src->picture, src->width, src->height, + src->depth, width, height, gc); + else + pm = CreateTiledPixmap(dpy, src->picture, src->width, src->height, + width, height, src->depth, gc); + XCopyArea(dpy, pm, dest, gc, 0, 0, width, height, x_start, y_start); + XFreePixmap(dpy, pm); +} + +/**************************************************************************** + * + * Redraws multi-pixmap titlebar (tril@igs.net) + * + ****************************************************************************/ +static void DrawMultiPixmapTitlebar(FvwmWindow *window, DecorFace *df) +{ + Window title_win; + GC gc; + const char *title; + Picture **pm; + short stretch_flags; + int title_width, title_height, text_width, text_offset, text_y_pos; + int left_space, right_space, under_offset, under_width; + + pm = df->u.multi_pixmaps; + stretch_flags = df->u.multi_stretch_flags; + gc = Scr.TitleGC; + XSetClipMask(dpy, gc, None); + title_win = window->title_w; + title = window->name; + title_width = window->title_g.width; + title_height = window->title_g.height; + + if (pm[TBP_MAIN]) + RenderIntoWindow(gc, pm[TBP_MAIN], title_win, 0, 0, title_width, + title_height, (stretch_flags & (1 << TBP_MAIN))); + else if (!title) + RenderIntoWindow(gc, pm[TBP_LEFT_MAIN], title_win, 0, 0, title_width, + title_height, (stretch_flags & (1 << TBP_LEFT_MAIN))); + + if (title) { +#ifdef I18N_MB + text_width = XmbTextEscapement(window->title_font.fontset, title, + strlen(title)); +#else + text_width = XTextWidth(window->title_font.font, title, strlen(title)); +#endif + if (text_width > title_width) + text_width = title_width; + switch (TB_JUSTIFICATION(GetDecor(window, titlebar))) { + case JUST_LEFT: + text_offset = TITLE_PADDING; + if (pm[TBP_LEFT_OF_TEXT]) + text_offset += pm[TBP_LEFT_OF_TEXT]->width; + if (pm[TBP_LEFT_END]) + text_offset += pm[TBP_LEFT_END]->width; + if (text_offset > title_width - text_width) + text_offset = title_width - text_width; + break; + case JUST_RIGHT: + text_offset = title_width - text_width - TITLE_PADDING; + if (pm[TBP_RIGHT_OF_TEXT]) + text_offset -= pm[TBP_RIGHT_OF_TEXT]->width; + if (pm[TBP_RIGHT_END]) + text_offset -= pm[TBP_RIGHT_END]->width; + if (text_offset < 0) + text_offset = 0; + break; + default: + text_offset = (title_width - text_width) / 2; + break; + } + under_offset = text_offset; + under_width = text_width; + /* If there's title padding, put it *inside* the undertext area: */ + if (under_offset >= TITLE_PADDING) { + under_offset -= TITLE_PADDING; + under_width += TITLE_PADDING; + } + if (under_offset + under_width + TITLE_PADDING <= title_width) + under_width += TITLE_PADDING; + left_space = under_offset; + right_space = title_width - left_space - under_width; + + if (pm[TBP_LEFT_MAIN] && left_space > 0) + RenderIntoWindow(gc, pm[TBP_LEFT_MAIN], title_win, 0, 0, left_space, + title_height, (stretch_flags & (1 << TBP_LEFT_MAIN))); + if (pm[TBP_RIGHT_MAIN] && right_space > 0) + RenderIntoWindow(gc, pm[TBP_RIGHT_MAIN], title_win, + under_offset + under_width, 0, right_space, + title_height, (stretch_flags & (1 << TBP_RIGHT_MAIN))); + if (pm[TBP_UNDER_TEXT] && under_width > 0) + RenderIntoWindow(gc, pm[TBP_UNDER_TEXT], title_win, under_offset, 0, + under_width, title_height, + (stretch_flags & (1 << TBP_UNDER_TEXT))); + if (pm[TBP_LEFT_OF_TEXT] && + pm[TBP_LEFT_OF_TEXT]->width <= left_space) { + XCopyArea(dpy, pm[TBP_LEFT_OF_TEXT]->picture, title_win, gc, 0, 0, + pm[TBP_LEFT_OF_TEXT]->width, title_height, + under_offset - pm[TBP_LEFT_OF_TEXT]->width, 0); + left_space -= pm[TBP_LEFT_OF_TEXT]->width; + } + if (pm[TBP_RIGHT_OF_TEXT] && + pm[TBP_RIGHT_OF_TEXT]->width <= right_space) { + XCopyArea(dpy, pm[TBP_RIGHT_OF_TEXT]->picture, title_win, gc, 0, 0, + pm[TBP_RIGHT_OF_TEXT]->width, title_height, + under_offset + under_width, 0); + right_space -= pm[TBP_RIGHT_OF_TEXT]->width; + } + + text_y_pos = title_height + - (title_height - window->title_font.font->ascent) / 2 + - 1; + if (title_height > 19) + text_y_pos--; + if (text_y_pos < 0) + text_y_pos = 0; +#ifdef I18N_MB + XmbDrawString(dpy, title_win, window->title_font.fontset, gc, text_offset, + gc, text_offset, text_y_pos, title, strlen(title)); +#else + XDrawString(dpy, title_win, gc, text_offset, text_y_pos, title, + strlen(title)); +#endif + } + else + left_space = right_space = title_width; + + if (pm[TBP_LEFT_END] && pm[TBP_LEFT_END]->width <= left_space) + XCopyArea(dpy, pm[TBP_LEFT_END]->picture, title_win, gc, 0, 0, + pm[TBP_LEFT_END]->width, title_height, 0, 0); + if (pm[TBP_RIGHT_END] && pm[TBP_RIGHT_END]->width <= right_space) + XCopyArea(dpy, pm[TBP_RIGHT_END]->picture, title_win, gc, 0, 0, + pm[TBP_RIGHT_END]->width, title_height, + title_width - pm[TBP_RIGHT_END]->width, 0); +} + +#endif /* FANCY_TITLEBARS */ + /* rules to get button state */ static enum ButtonState get_button_state( Bool has_focus, Bool toggled, Window w) @@ -1029,6 +1212,7 @@ Window expose_win, XRectangle *rclip) { int i; + int left1right0; Bool is_lowest = True; Pixmap *pass_bg_pixmap; @@ -1042,8 +1226,10 @@ for (i = 0; i < NUMBER_OF_BUTTONS; i++) { - if (t->button_w[i] != None && (((i & 1) && i / 2 < Scr.nr_right_buttons) || - (!(i & 1) && i / 2 < Scr.nr_left_buttons))) + left1right0 = !(i&1); + if (t->button_w[i] != None && + ((!left1right0 && i/2 < Scr.nr_right_buttons) || + (left1right0 && i/2 < Scr.nr_left_buttons))) { mwm_flags stateflags = TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])); @@ -1097,8 +1283,8 @@ DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height, tsdf, cd->relief_gc, cd->shadow_gc, cd->fore_color, cd->back_color, is_lowest, - TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL, - pass_bg_pixmap); + TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), + left1right0, NULL, pass_bg_pixmap); is_lowest = False; } } @@ -1110,8 +1296,8 @@ DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height, df, cd->relief_gc, cd->shadow_gc, cd->fore_color, cd->back_color, is_lowest, - TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL, - pass_bg_pixmap); + TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), left1right0, + NULL, pass_bg_pixmap); is_lowest = False; } @@ -1276,6 +1462,11 @@ /* draw compound titlebar (veliaa@rpi.edu) */ Bool is_lowest = True; +#ifdef FANCY_TITLEBARS + if (df->style.face_type == MultiPixmap) + DrawMultiPixmapTitlebar(t, df); + else +#endif if (PressedW == t->title_w) { #ifdef MULTISTYLE @@ -1319,6 +1510,11 @@ break; } +#ifdef FANCY_TITLEBARS + if (TB_STATE(GetDecor(t, titlebar))[title_state].style.face_type + != MultiPixmap) +#endif + { #ifdef I18N_MB if(t->name != (char *)NULL) XmbDrawString(dpy, t->title_w, t->title_font.fontset, @@ -1329,6 +1525,7 @@ XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off, t->title_font.y + 1, t->name, strlen(t->name)); #endif + } } /* --- fvwm/builtins.c.orig Fri Aug 23 22:46:12 2002 +++ fvwm/builtins.c Fri Aug 23 22:47:53 2002 @@ -63,6 +63,9 @@ static char *ReadTitleButton( char *s, TitleButton *tb, Boolean append, int button); +#ifdef FANCY_TITLEBARS +static char *ReadMultiPixmapDecor(char *s, DecorFace *df); +#endif static void DestroyFvwmDecor(FvwmDecor *decor); extern float rgpctMovementDefault[32]; @@ -1418,6 +1421,10 @@ void FreeDecorFace(Display *dpy, DecorFace *df) { +#ifdef FANCY_TITLEBARS + int i; +#endif + switch (DFS_FACE_TYPE(df->style)) { case GradientButton: @@ -1435,6 +1442,18 @@ DestroyPicture(dpy, df->u.p); break; +#ifdef FANCY_TITLEBARS + case MultiPixmap: + if (df->u.multi_pixmaps) { + for (i=0; i < NUM_TB_PIXMAPS; i++) { + if (df->u.multi_pixmaps[i]) + DestroyPicture(dpy, df->u.multi_pixmaps[i]); + } + free(df->u.multi_pixmaps); + } + break; +#endif + case VectorButton: case DefaultVectorButton: if (df->u.vector.x) @@ -1666,6 +1685,19 @@ else DFS_FACE_TYPE(df->style) = PixmapButton; } +#ifdef FANCY_TITLEBARS + else if (strncasecmp(style,"MultiPixmap",11)==0) { + if (button != -1) { + if (verbose) + fvwm_msg(ERR, "ReadDecorFace", + "MultiPixmap is only valid for TitleStyle"); + return False; + } + s = ReadMultiPixmapDecor(s, df); + if (!s) + return False; + } +#endif #ifdef MINI_ICONS else if (strncasecmp (style, "MiniIcon", 8) == 0) { @@ -1973,6 +2005,78 @@ return end; } +#ifdef FANCY_TITLEBARS +/***************************************************************************** + * + * Reads a multi-pixmap titlebar config. (tril@igs.net) + * + ****************************************************************************/ +static char *ReadMultiPixmapDecor(char *s, DecorFace *df) +{ + static char *pm_names[NUM_TB_PIXMAPS+1] = { + "Main", + "LeftMain", + "RightMain", + "UnderText", + "LeftOfText", + "RightOfText", + "LeftEnd", + "RightEnd", + "Buttons", + "LeftButtons", + "RightButtons", + NULL + }; + Picture **pm; + char *token; + Bool stretched; + int pm_id, i; + + df->style.face_type = MultiPixmap; + df->u.multi_pixmaps = pm = + (Picture**)safecalloc(NUM_TB_PIXMAPS, sizeof(Picture*)); + + s = GetNextTokenIndex(s, pm_names, 0, &pm_id); + while (pm_id >= 0) { + s = DoPeekToken(s, &token, ",()", NULL, NULL); + stretched = False; + if (StrEquals(token, "stretched")) { + stretched = True; + s = DoPeekToken(s, &token, ",", NULL, NULL); + } + else if (StrEquals(token, "tiled")) + s = DoPeekToken(s, &token, ",", NULL, NULL); + if (!token) + break; + if (pm[pm_id]) { + fvwm_msg(WARN, "ReadMultiPixmapDecor", + "Ignoring already-specified %s pixmap", pm_names[i]); + continue; + } + if (stretched) + df->u.multi_stretch_flags |= (1 << pm_id); + pm[pm_id] = CachePicture(dpy, Scr.NoFocusWin, NULL, token, Scr.ColorLimit); + if (!pm[pm_id]) + fvwm_msg(ERR, "ReadMultiPixmapDecor", "Pixmap '%s' could not be loaded", + token); + s = GetNextTokenIndex(s, pm_names, 0, &pm_id); + } + + if (!pm[TBP_MAIN] && !(pm[TBP_LEFT_MAIN] && pm[TBP_RIGHT_MAIN])) { + fvwm_msg(ERR, "ReadMultiPixmapDecor", + "No Main pixmap found for TitleStyle MultiPixmap " + "(you must specify either Main, or both LeftMain and RightMain)"); + for (i=0; i < NUM_TB_PIXMAPS; i++) { + if (pm[i]) + DestroyPicture(dpy, pm[i]); + } + free(pm); + return NULL; + } + + return s; +} +#endif /* FANCY_TITLEBARS */ #ifdef USEDECOR /*****************************************************************************