Subject: Fancy Titlebars: Updated Patch

From: Suzanne Britton <[email-masked]>
Date: Sun, 7 Oct 2001 07:00:37 -0400

Okay, here's a somewhat cleaned-up patch for nifty titlebars. I still wouldn't
call it ready for general use (not enough testing, not enough review by the
people who know what they're doing ;-), but the titleunder bug seems to be
fixed and the config syntax is much more reasonable. Bonus: the patch is
smaller!

The syntax is now: MultiPixmap [main] [under text] [left of text]
                               [right of text] [far left] [far right]

e.g.: TitleStyle ActiveUp MultiPixmap main.xpm under.xpm lt.xpm rt.xpm - -

The read code has been integrated into ReadDecorFace, so now this can be
combined with pretty much anything else TitleStyle supports (flags, defining
multiple states at once, etc.)

Suzanne

-- 
[email-masked] - http://www.igs.net/~tril/
"I'm aware of that, sweetheart. It's just that when I wake up to a hissing
goat skull on my nightstand, and it hops off and runs across the floor on
spider legs, I sleep a lot better knowing where it ran off to."
                                                          - Ted (Red Meat)
--- ChangeLog.orig	Wed Oct  3 20:36:03 2001
+++ ChangeLog	Sun Oct  7 04:04:43 2001
@@ -1,3 +1,29 @@
+2001-10-07  Suzanne Britton  <[email-masked]>
+
+	* config.h.in: Added FANCY_TITLEBARS option, off by default
+
+	* fvwm/screen.h:
+	[All changes #ifdeffed FANCY_TITLEBARS]
+	Added TITLE_PADDING define for fancy titlebar rendering
+	Added tb_pixmap_enum
+	Added MultiPixmap to DecorFaceType enum
+	Added Picture **extra_pictures to DecorFace structure
+
+	* fvwm/borders.c:
+	[All changes #ifdeffed FANCY_TITLEBARS]
+	(DrawButton): Handle UseTitleStyle where TitleStyle is MultiPixmap
+	(DrawMultiPixmapTitlebar): [new]
+	(RedrawTitle):
+	Call DrawFancyTitlebar for MultiPixmap style
+	Skip DrawString calls for MultiPixmap style (title is drawn inside
+	DrawMultiPixmapTitlebar)
+
+	* fvwm/builtins.c:
+	[All changes #ifdeffed FANCY_TITLEBARS]
+	(FreeDecorFace): Free up all pictures for a MultiPixmap decor
+	(ReadDecorFace): Call ReadMultiPixmapDecor for a MultiPixmap TitleStyle
+	(ReadMultiPixmapDecor): [new]
+
 2001-09-16  Dominik Vogt  <[email-masked]>
 
 	* configure.in:
--- AUTHORS.orig	Wed Oct  3 20:59:12 2001
+++ AUTHORS	Wed Oct  3 20:58:41 2001
@@ -182,6 +182,8 @@
 Echo. Improve modules: Make FvwmM4 pass args on to m4, font-related
 seg-fault bug fix in FvwmButtons.
 
+Suzanne Britton:
+Multi-pixmap titlebars patch.
 
 Along with a cast of thousands (well, dozens) mentioned in old
 ChangeLog entries.  If you find your name below, please send an entry
--- config.h.in.orig	Wed Oct  3 20:05:40 2001
+++ config.h.in	Sat Sep 29 20:59:34 2001
@@ -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/screen.h.orig	Wed Oct  3 20:06:01 2001
+++ fvwm/screen.h	Sun Oct  7 04:30:01 2001
@@ -54,6 +54,11 @@
 #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_UNDER, TBP_LTEXT, TBP_RTEXT, TBP_LEFT,
+                     TBP_RIGHT, NUM_TB_PIXMAPS};
+#endif
 
 typedef struct
 {
@@ -70,6 +75,9 @@
     GradientButton              ,
     PixmapButton                ,
     TiledPixmapButton           ,
+#ifdef FANCY_TITLEBARS
+    MultiPixmap                 ,
+#endif
 #ifdef MINI_ICONS
     MiniIconButton              ,
 #endif
@@ -123,6 +131,9 @@
   struct
   {
     Picture *p;
+#ifdef FANCY_TITLEBARS
+    Picture **extra_pictures;
+#endif
     Pixel back;
     struct
     {
--- fvwm/borders.c.orig	Wed Oct  3 20:06:21 2001
+++ fvwm/borders.c	Sun Oct  7 05:32:43 2001
@@ -271,7 +271,14 @@
   case MiniIconButton:
   case PixmapButton:
   case TiledPixmapButton:
-    if (type == PixmapButton || type == TiledPixmapButton)
+#ifdef FANCY_TITLEBARS
+  case MultiPixmap:       /* in case of UseTitleStyle */
+#endif
+    if (type == PixmapButton || type == TiledPixmapButton
+#ifdef FANCY_TITLEBARS
+        || type == MultiPixmap
+#endif
+       )
     {
       p = df->u.p;
     }
@@ -448,6 +455,161 @@
   return;
 }
 
+#ifdef FANCY_TITLEBARS
+/****************************************************************************
+ *
+ *  Redraws multi-pixmap titlebar ([email-masked])
+ *
+ ****************************************************************************/
+static void DrawMultiPixmapTitlebar(FvwmWindow *window, TitleButton *decor)
+{
+  Window       title_win;
+  GC           gc;
+  const char  *title;
+  int          title_width, title_height, text_width, text_offset, text_y_pos;
+  int          left_space, right_space, under_offset, under_width;
+  int          state;
+  mwm_flags    state_flags;
+  Bool         toggled;
+  Picture     *main_pic, **extra_pics;
+
+  state = BS_Inactive;
+  if (Scr.Hilite == window) {
+    if (PressedW == title_win)
+      state = BS_ActiveDown;
+    else
+      state = BS_ActiveUp;
+  }
+  state_flags = TB_MWM_DECOR_FLAGS(*decor);
+  toggled = (HAS_MWM_BUTTONS(window) &&
+         ((state_flags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(window)) ||
+          (state_flags & MWM_DECOR_SHADE && IS_SHADED(window)) ||
+          (state_flags & MWM_DECOR_STICK && IS_STICKY(window))));
+  if (toggled)
+    state += BS_ToggledActiveUp;
+
+  main_pic   = decor->state[state].u.p;
+  extra_pics = decor->state[state].u.extra_pictures;
+
+  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 (main_pic) {
+    Pixmap pm = CreateTiledPixmap(dpy, main_pic->picture, main_pic->width,
+                                  main_pic->height, title_width, title_height,
+                                  main_pic->depth, gc);
+    XCopyArea(dpy, pm, title_win, gc, 0, 0, title_width, title_height, 0, 0);
+    XFreePixmap(dpy, pm);
+  }
+
+  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(*decor)) {
+    case JUST_LEFT:
+      text_offset = TITLE_PADDING;
+      if (extra_pics[TBP_LTEXT-1])
+        text_offset += extra_pics[TBP_LTEXT-1]->width;
+      if (extra_pics[TBP_LEFT-1])
+        text_offset += extra_pics[TBP_LEFT-1]->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 (extra_pics[TBP_RTEXT-1])
+        text_offset -= extra_pics[TBP_RTEXT-1]->width;
+      if (extra_pics[TBP_RIGHT-1])
+        text_offset -= extra_pics[TBP_RIGHT-1]->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 (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 (extra_pics[TBP_UNDER-1]) {
+      Pixmap pm = CreateTiledPixmap(dpy,
+                                    extra_pics[TBP_UNDER-1]->picture,
+                                    extra_pics[TBP_UNDER-1]->width,
+                                    extra_pics[TBP_UNDER-1]->height,
+                                    under_width, title_height,
+                                    extra_pics[TBP_UNDER-1]->depth,
+                                    gc);
+      XCopyArea(dpy, pm, title_win, gc, 0, 0, under_width, title_height,
+                under_offset, 0);
+      XFreePixmap(dpy, pm);
+    }
+    if (extra_pics[TBP_LTEXT-1] &&
+        extra_pics[TBP_LTEXT-1]->width <= left_space) {
+      XCopyArea(dpy, extra_pics[TBP_LTEXT-1]->picture, title_win, gc, 0, 0,
+                extra_pics[TBP_LTEXT-1]->width, title_height,
+                under_offset - extra_pics[TBP_LTEXT-1]->width, 0);
+      left_space -= extra_pics[TBP_LTEXT-1]->width;
+    }
+    if (extra_pics[TBP_RTEXT-1] &&
+        extra_pics[TBP_RTEXT-1]->width <= right_space) {
+      XCopyArea(dpy, extra_pics[TBP_RTEXT-1]->picture, title_win, gc, 0, 0,
+                extra_pics[TBP_RTEXT-1]->width, title_height,
+                under_offset + under_width, 0);
+      right_space -= extra_pics[TBP_RTEXT-1]->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 (extra_pics[TBP_LEFT-1] && extra_pics[TBP_LEFT-1]->width <= left_space) {
+    XCopyArea(dpy, extra_pics[TBP_LEFT-1]->picture, title_win, gc, 0, 0,
+              extra_pics[TBP_LEFT-1]->width, title_height, 0, 0);
+    left_space -= extra_pics[TBP_LEFT-1]->width;
+  }
+  if (extra_pics[TBP_RIGHT-1] && extra_pics[TBP_RIGHT-1]->width <= right_space) {
+    XCopyArea(dpy, extra_pics[TBP_RIGHT-1]->picture, title_win, gc, 0, 0,
+              extra_pics[TBP_RIGHT-1]->width, title_height,
+              title_width - extra_pics[TBP_RIGHT-1]->width, 0);
+    right_space -= extra_pics[TBP_RIGHT-1]->width;
+  }
+}
+#endif /* FANCY_TITLEBARS */
+
 /* rules to get button state */
 static enum ButtonState get_button_state(
   Bool has_focus, Bool toggled, Window w)
@@ -1276,6 +1438,11 @@
     /* draw compound titlebar ([email-masked]) */
     Bool is_lowest = True;
 
+#ifdef FANCY_TITLEBARS
+    if (df->style.face_type == MultiPixmap)
+      DrawMultiPixmapTitlebar(t, &(GetDecor(t, titlebar)));
+    else
+#endif
     if (PressedW == t->title_w)
     {
 #ifdef MULTISTYLE
@@ -1319,6 +1486,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 +1501,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	Wed Oct  3 20:06:10 2001
+++ fvwm/builtins.c	Sun Oct  7 06:31:27 2001
@@ -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];
@@ -1401,6 +1404,10 @@
 
 void FreeDecorFace(Display *dpy, DecorFace *df)
 {
+#ifdef FANCY_TITLEBARS
+  int i;
+#endif
+
   switch (DFS_FACE_TYPE(df->style))
   {
   case GradientButton:
@@ -1418,6 +1425,20 @@
       DestroyPicture(dpy, df->u.p);
     break;
 
+#ifdef FANCY_TITLEBARS
+  case MultiPixmap:
+    if (df->u.p)
+      DestroyPicture(dpy, df->u.p);
+    if (df->u.extra_pictures) {
+      for (i=0; i < NUM_TB_PIXMAPS-1; i++) {
+        if (df->u.extra_pictures[i])
+          DestroyPicture(dpy, df->u.extra_pictures[i]);
+      }
+      free(df->u.extra_pictures);
+    }
+    break;
+#endif
+
   case VectorButton:
   case DefaultVectorButton:
     if (df->u.vector.x)
@@ -1649,6 +1670,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)
     {
@@ -1956,6 +1990,49 @@
   return end;
 }
 
+#ifdef FANCY_TITLEBARS
+/*****************************************************************************
+ *
+ * Reads a multi-pixmap titlebar config. This should consist of NUM_TB_PIXMAPS
+ * pixmap filenames, any of which may be '-' for "no pixmap". ([email-masked])
+ *
+ ****************************************************************************/
+static char *ReadMultiPixmapDecor(char *s, DecorFace *df)
+{
+  char *pixmap_filenames[NUM_TB_PIXMAPS];
+  Picture **p;
+  int i;
+
+  for (i=0; i < NUM_TB_PIXMAPS; i++) {
+    s = GetNextToken(s, &pixmap_filenames[i]);
+    if (!pixmap_filenames[i]) {
+      fvwm_msg(ERR, "ReadMultiPixmapDecor",
+               "Expecting %d pixmap filenames for MultiPixmap style (use '-' to specify 'no pixmap')",
+               NUM_TB_PIXMAPS);
+      return NULL;
+    }
+  }
+  if (!df->u.extra_pictures)
+    df->u.extra_pictures = (Picture**)safecalloc(NUM_TB_PIXMAPS-1,
+                                                 sizeof(Picture*));
+  df->style.face_type = MultiPixmap;
+
+  for (i=0; i < NUM_TB_PIXMAPS; i++) {
+    if (StrEquals(pixmap_filenames[i], "-"))
+      continue;
+    p = (i == 0 ? &(df->u.p) : &(df->u.extra_pictures[i-1]));
+    *p = CachePicture(dpy, Scr.NoFocusWin, NULL, pixmap_filenames[i],
+                     Scr.ColorLimit);
+    if (!(*p))
+      fvwm_msg(WARN, "ReadMultiPixmapDecor", "Pixmap '%s' could not be loaded",
+               pixmap_filenames[i]);
+  }
+
+  for (i=0; i < NUM_TB_PIXMAPS; i++)
+    free(pixmap_filenames[i]);
+  return s;
+}
+#endif /* FANCY_TITLEBARS */
 
 #ifdef USEDECOR
 /*****************************************************************************