Hello all,

I've been tinkering with the FVWM 2.4.2 source code lately, trying to rectify
one of its few remaining lacks of themability :-) I've developed a patch which
adds a new type of TitleStyle. You can specify up to six pixmaps for the
titlebar in each state: main, under text, left of text, right of text, far
left, and far right (the first two are tiled, the rest aren't). This allows
FVWM to do a quite pleasing rendition of themes like Eazel-Blue and Crux
(though without the fancy borders).

The config syntax looks like this currently:
   TitleStyle Fancy <state> <main pixmap> <far left> <left of text>
                    <under text> <right of text> <far right>

You can specify "no pixmap" at any position by substituting '-' for a pixmap
filename. The main pixmap will be tiled over any resulting empty spaces.

Bearing in mind that the patch is currently buggy (titleunder pixmap
often doesn't update properly), unpolished, and the work of a complete
FVWM/X programming newbie, you can find it attached to this e-mail and
apply it to 2.4.2 via "patch -p0 <patchfile" from the top source dir. You
also have to #define FANCY_TITLEBARS in config.h to activate any of
the new code.

Would there be any interest in integrating this into the main source tree?

Suzanne

-- 
[EMAIL PROTECTED] - 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   Wed Oct  3 20:40:15 2001
@@ -1,3 +1,33 @@
+2001-10-03  Suzanne Britton  <[EMAIL PROTECTED]>
+
+       * 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 FancyTitlebar to DecorFaceType
+       Added tb_pixmap_enum
+       Added Titlebar struct (identical to TitleButton except for
+       Picture **pixmaps member)
+       Changed "TitleButton titlebar" in FvwmDecor to "Titlebar titlebar"
+
+       * fvwm/borders.c:
+       [All changes #ifdeffed FANCY_TITLEBARS]
+       (DrawFancyTitlebar): [new]
+       (RedrawTitle):
+       Call DrawFancyTitlebar for FancyTitlebar style
+       Skip DrawString calls for FancyTitlebar style (title is drawn inside
+       DrawFancyTitlebar)
+
+       * fvwm/builtins.c:
+       [All changes #ifdeffed FANCY_TITLEBARS]
+       (ReadTitlebar): [new]
+       (ReadFancyTitlebar): [new]
+       (do_title_style): Call ReadFancyTitlebar or ReadTitlebar for
+       TitleStyle command (instead of ReadTitleButton)
+       (InitFvwmDecor): Init decor->titlebar.pixmaps to NULL
+       (DestroyFvwmDecor): Free up decor->titlebar.pixmaps
+
 2001-09-16  Dominik Vogt  <[EMAIL PROTECTED]>
 
        * 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       Wed Oct  3 20:38:53 2001
@@ -50,6 +50,10 @@
 #define SIZE_HINDENT 5
 #define SIZE_VINDENT 3
 
+#ifdef FANCY_TITLEBARS
+#define TITLE_PADDING 5
+#endif
+
 /* colormap focus styes */
 #define COLORMAP_FOLLOWS_MOUSE 1 /* default */
 #define COLORMAP_FOLLOWS_FOCUS 2
@@ -70,6 +74,9 @@
     GradientButton              ,
     PixmapButton                ,
     TiledPixmapButton           ,
+#ifdef FANCY_TITLEBARS
+    FancyTitlebar               ,
+#endif
 #ifdef MINI_ICONS
     MiniIconButton              ,
 #endif
@@ -191,6 +198,22 @@
   DecorFace state[BS_MaxButtonState];
 } TitleButton;
 
+#ifdef FANCY_TITLEBARS
+enum tb_pixmap_enum {TBP_MAIN, TBP_LEFT, TBP_LOT, TBP_UNDER, TBP_ROT,
+                     TBP_RIGHT, NUM_TB_PIXMAPS};
+typedef struct
+{
+  unsigned just : 2; /* was JustificationType : 2 */
+  struct
+  {
+    unsigned has_changed : 1;
+    mwm_flags mwm_decor_flags : 9;
+  } flags;
+  DecorFace state[BS_MaxButtonState];
+  Picture **pixmaps;
+} Titlebar;
+#endif
+
 #define TB_FLAGS(tb)              ((tb).flags)
 #define TB_STATE(tb)              ((tb).state)
 #define TB_JUSTIFICATION(tb)      ((tb).just)
@@ -216,7 +239,11 @@
   int title_height;           /* explicitly specified title bar height */
   /* titlebar buttons */
   TitleButton buttons[NUMBER_OF_BUTTONS];
+#ifdef FANCY_TITLEBARS
+  Titlebar titlebar;
+#else
   TitleButton titlebar;
+#endif
   struct BorderStyle
   {
     DecorFace active, inactive;
--- fvwm/borders.c.orig Wed Oct  3 20:06:21 2001
+++ fvwm/borders.c      Wed Oct  3 20:38:42 2001
@@ -448,6 +448,173 @@
   return;
 }
 
+#ifdef FANCY_TITLEBARS
+/****************************************************************************
+ *
+ *  Redraws multi-pixmap titlebar ([EMAIL PROTECTED])
+ *
+ ****************************************************************************/
+static void DrawFancyTitlebar(FvwmWindow *window, Titlebar *decor)
+{
+  Window       title_win;
+  Picture    **pixmaps;
+  GC           gc;
+  const char  *title;
+  int          title_width, title_height, text_width, text_offset;
+  int          left_space, right_space;
+  int          state = BS_Inactive;
+  int          under_offset, under_width;
+  Bool         toggled;
+  mwm_flags    state_flags;
+
+  if (!(decor->pixmaps)) {
+    fvwm_msg(ERR, "DrawFancyTitlebar", "no pixmaps defined");
+    return;
+  }
+
+  gc = Scr.TitleGC;
+  title_win = window->title_w;
+  title = window->name;
+  title_width = window->title_g.width;
+  title_height = window->title_g.height;
+
+  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;
+  pixmaps = decor->pixmaps + (state * NUM_TB_PIXMAPS);
+
+  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 (pixmaps[TBP_LEFT])
+        text_offset += pixmaps[TBP_LEFT]->width;
+      if (pixmaps[TBP_LOT])
+        text_offset += pixmaps[TBP_LOT]->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 (pixmaps[TBP_RIGHT])
+        text_offset -= pixmaps[TBP_RIGHT]->width;
+      if (pixmaps[TBP_ROT])
+        text_offset -= pixmaps[TBP_ROT]->width;
+      if (text_offset < 0)
+        text_offset = 0;
+      break;
+    default:
+      text_offset = (title_width - text_width) / 2;
+      break;
+    }
+  }
+  else {
+    text_width = 0;
+    text_offset = -1;
+  }
+
+  if (pixmaps[TBP_MAIN]) {
+    Pixmap pm = CreateTiledPixmap(dpy,
+                                  pixmaps[TBP_MAIN]->picture,
+                                  pixmaps[TBP_MAIN]->width,
+                                  pixmaps[TBP_MAIN]->height,
+                                  title_width,
+                                  title_height,
+                                  pixmaps[TBP_MAIN]->depth,
+                                  gc);
+    XCopyArea(dpy, pm, title_win, gc, 0, 0, title_width, title_height, 0, 0);
+    XFreePixmap(dpy, pm);
+  }
+
+  under_offset = text_offset;
+  under_width = text_width;
+  if (under_offset >= TITLE_PADDING) {
+    under_offset -= TITLE_PADDING;
+    under_width += TITLE_PADDING;
+  }
+  if (under_offset >= 0 &&
+      under_offset + under_width + TITLE_PADDING <= title_width)
+    under_width += TITLE_PADDING;
+  left_space =  ((under_offset < 0) ? title_width/2 : under_offset);
+  right_space = ((under_offset < 0) ? title_width/2 :
+                                     title_width - under_width - under_offset);
+
+  if (pixmaps[TBP_UNDER] && text_width > 0) {
+    Pixmap pm = CreateTiledPixmap(dpy,
+                                  pixmaps[TBP_UNDER]->picture,
+                                  pixmaps[TBP_UNDER]->width,
+                                  pixmaps[TBP_UNDER]->height,
+                                  under_width, title_height,
+                                  pixmaps[TBP_UNDER]->depth,
+                                  gc);
+    XCopyArea(dpy, pm, title_win, gc, 0, 0, under_width, title_height,
+              under_offset, 0);
+    XFreePixmap(dpy, pm);
+  }
+  if (pixmaps[TBP_LEFT] && pixmaps[TBP_LEFT]->width <= left_space) {
+    XCopyArea(dpy, pixmaps[TBP_LEFT]->picture, title_win, gc, 0, 0,
+              pixmaps[TBP_LEFT]->width, title_height, 0, 0);
+    left_space -= pixmaps[TBP_LEFT]->width;
+  }
+  if (pixmaps[TBP_RIGHT] && pixmaps[TBP_RIGHT]->width <= right_space) {
+    XCopyArea(dpy, pixmaps[TBP_RIGHT]->picture, title_win, gc, 0, 0,
+              pixmaps[TBP_RIGHT]->width, title_height,
+              title_width - pixmaps[TBP_RIGHT]->width, 0);
+    right_space -= pixmaps[TBP_RIGHT]->width;
+  }
+  if (pixmaps[TBP_LOT] && pixmaps[TBP_LOT]->width <= left_space &&
+      text_width > 0) {
+    XCopyArea(dpy, pixmaps[TBP_LOT]->picture, title_win, gc, 0, 0,
+              pixmaps[TBP_LOT]->width, title_height,
+              under_offset - pixmaps[TBP_LOT]->width, 0);
+    left_space -= pixmaps[TBP_LOT]->width;
+  }
+  if (pixmaps[TBP_ROT] && pixmaps[TBP_ROT]->width <= right_space &&
+      text_width > 0 ) {
+    XCopyArea(dpy, pixmaps[TBP_ROT]->picture, title_win, gc, 0, 0,
+              pixmaps[TBP_ROT]->width, title_height,
+              under_offset + under_width, 0);
+    right_space -= pixmaps[TBP_ROT]->width;
+  }
+
+  if (title) {
+    int title_y_pos = title_height
+                      - (title_height - window->title_font.font->ascent) / 2
+                      - 1;
+    if (title_height > 19)
+      title_y_pos--;
+    if (title_y_pos < 0)
+      title_y_pos = 0;
+#ifdef I18N_MB
+    XmbDrawString(dpy, title_win, window->title_font.fontset, gc, text_offset,
+                  gc, text_offset, title_y_pos, title, strlen(title));
+#else
+    XDrawString(dpy, title_win, gc, text_offset, title_y_pos, title,
+                strlen(title));
+#endif
+  }
+}
+#endif /* FANCY_TITLEBARS */
+
 /* rules to get button state */
 static enum ButtonState get_button_state(
   Bool has_focus, Bool toggled, Window w)
@@ -1276,6 +1443,11 @@
     /* draw compound titlebar ([EMAIL PROTECTED]) */
     Bool is_lowest = True;
 
+#ifdef FANCY_TITLEBARS
+    if (df->style.face_type == FancyTitlebar)
+      DrawFancyTitlebar(t, &(GetDecor(t, titlebar)));
+    else
+#endif
     if (PressedW == t->title_w)
     {
 #ifdef MULTISTYLE
@@ -1319,6 +1491,11 @@
       break;
     }
 
+#ifdef FANCY_TITLEBARS
+    if (TB_STATE(GetDecor(t, titlebar))[title_state].style.face_type
+        != FancyTitlebar)
+#endif
+    {
 #ifdef I18N_MB
     if(t->name != (char *)NULL)
       XmbDrawString(dpy, t->title_w, t->title_font.fontset,
@@ -1329,6 +1506,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     Wed Oct  3 21:07:51 2001
@@ -63,6 +63,10 @@
 
 static char *ReadTitleButton(
     char *s, TitleButton *tb, Boolean append, int button);
+#ifdef FANCY_TITLEBARS
+static char *ReadTitlebar(char *s, Titlebar *tb, Boolean append, int button);
+static char *ReadFancyTitlebar(char *s, Titlebar *tb, Boolean append);
+#endif
 static void DestroyFvwmDecor(FvwmDecor *decor);
 
 extern float rgpctMovementDefault[32];
@@ -1164,10 +1168,20 @@
       if (action)
        action += next;
     }
+#ifdef FANCY_TITLEBARS
+    else if (StrEquals(parm, "fancy")) {
+      action = ReadFancyTitlebar(prev, &decor->titlebar, do_add);
+    }
+    else
+    {
+      action = ReadTitlebar(prev, &decor->titlebar, do_add, -1);
+    }
+#else
     else
     {
       action = ReadTitleButton(prev, &decor->titlebar, do_add, -1);
     }
+#endif
   }
 }
 
@@ -1956,6 +1970,224 @@
   return end;
 }
 
+#ifdef FANCY_TITLEBARS
+
+/*****************************************************************************
+ *
+ * Reads a (non-FANCY) titlebar description. Nearly identical to
+ * ReadTitleButton. ([EMAIL PROTECTED])
+ *
+ ****************************************************************************/
+static char *ReadTitlebar(
+    char *s, Titlebar *tb, Boolean append, int button)
+{
+  char *end = NULL;
+  char *spec;
+  char *t;
+  int i;
+  int bs;
+  int pstyle = 0;
+  DecorFace tmpdf;
+  Bool do_set_all = False;
+
+  s = SkipSpaces(s, NULL, 0);
+  t = GetNextTokenIndex(s, button_states, 0, &bs);
+  if (bs != BS_All)
+  {
+    s = SkipSpaces(t, NULL, 0);
+  }
+  else
+  {
+    do_set_all = True;
+  }
+
+  if (*s == '(')
+  {
+    int len;
+    pstyle = 1;
+    if (!(end = strchr(++s, ')')))
+    {
+      fvwm_msg(ERR, "ReadTitlebar", "missing parenthesis: %s", s);
+      return NULL;
+    }
+    s = SkipSpaces(s, NULL, 0);
+    len = end - s + 1;
+    spec = safemalloc(len);
+    strncpy(spec, s, len - 1);
+    spec[len - 1] = 0;
+  }
+  else
+  {
+    spec = s;
+  }
+
+  spec = SkipSpaces(spec, NULL, 0);
+  /* setup temporary in case button read fails */
+  memset(&tmpdf, 0, sizeof(DecorFace));
+  DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
+
+  if (strncmp(spec, "--",2)==0)
+  {
+    /* only change flags */
+    if (do_set_all)
+    {
+      for (i = 0; i < BS_MaxButtonState; ++i)
+      {
+       ReadDecorFace(spec, &TB_STATE(*tb)[i], BS_All, !i);
+      }
+    }
+    else
+    {
+      ReadDecorFace(spec, &TB_STATE(*tb)[bs], button, True);
+    }
+  }
+  else if (ReadDecorFace(spec, &tmpdf, button, True))
+  {
+    int b = (do_set_all) ? 0 : bs;
+#ifdef MULTISTYLE
+    if (append)
+    {
+      DecorFace *head = &TB_STATE(*tb)[b];
+      DecorFace *tail = head;
+      DecorFace *next;
+
+      while (tail->next)
+      {
+       tail = tail->next;
+      }
+      tail->next = (DecorFace *)safemalloc(sizeof(DecorFace));
+      memcpy(tail->next, &tmpdf, sizeof(DecorFace));
+      if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
+         DFS_FACE_TYPE((&TB_STATE(*tb)[b])->style) == DefaultVectorButton)
+      {
+       /* override the default vector style */
+       memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle));
+       DFS_FACE_TYPE(tail->next->style) = VectorButton;
+       next = head->next;
+       head->next = NULL;
+       FreeDecorFace(dpy, head);
+       memcpy(head, next, sizeof(DecorFace));
+       free(next);
+      }
+      if (do_set_all)
+      {
+       for (i = 1; i < BS_MaxButtonState; ++i)
+       {
+         if (i == b)
+         {
+           /* already done above */
+           continue;
+         }
+         head = &TB_STATE(*tb)[i];
+         tail = head;
+         while (tail->next)
+         {
+           tail = tail->next;
+         }
+         tail->next = (DecorFace *)safemalloc(sizeof(DecorFace));
+         memset(&DFS_FLAGS(tail->next->style), 0,
+                sizeof(DFS_FLAGS(tail->next->style)));
+         DFS_FACE_TYPE(tail->next->style) = SimpleButton;
+         tail->next->next = NULL;
+         ReadDecorFace(spec, tail->next, button, False);
+         if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
+             DFS_FACE_TYPE((&TB_STATE(*tb)[i])->style) == DefaultVectorButton)
+         {
+           /* override the default vector style */
+           memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle));
+           DFS_FACE_TYPE(tail->next->style) = VectorButton;
+           next = head->next;
+           head->next = NULL;
+           FreeDecorFace(dpy, head);
+           memcpy(head, next, sizeof(DecorFace));
+           free(next);
+         }
+       }
+      }
+    }
+    else
+#endif
+    {
+      FreeDecorFace(dpy, &TB_STATE(*tb)[b]);
+      memcpy(&(TB_STATE(*tb)[b]), &tmpdf, sizeof(DecorFace));
+      if (do_set_all)
+      {
+       for (i = 1; i < BS_MaxButtonState; ++i)
+       {
+         ReadDecorFace(spec, &TB_STATE(*tb)[i], button, False);
+       }
+      }
+    }
+  }
+  if (pstyle)
+  {
+    free(spec);
+    end++;
+    end = SkipSpaces(end, NULL, 0);
+  }
+
+  return end;
+}
+
+/*****************************************************************************
+ *
+ * Reads a multi-pixmap titlebar description ([EMAIL PROTECTED])
+ *
+ ****************************************************************************/
+static char *ReadFancyTitlebar(char *s, Titlebar *tb, Boolean append)
+{
+  int state;
+  int i, j;
+  char *pixmap_filenames[NUM_TB_PIXMAPS];
+
+  s = SkipSpaces(s, NULL, 0);
+  s = SkipNTokens(s, 1); /* skip "Fancy" */
+  s = SkipSpaces(s, NULL, 0);
+  s = GetNextTokenIndex(s, button_states, 0, &state);
+  if (state < 0) {
+    fvwm_msg(ERR, "ReadFancyTitlebar", "Expecting a state specification (e.g. 
Inactive) after TitleStyle Fancy");
+    return s;
+  }
+  s = SkipSpaces(s, NULL, 0);
+
+  for (i=0; i < NUM_TB_PIXMAPS; i++) {
+    s = GetNextToken(s, &pixmap_filenames[i]);
+    if (!pixmap_filenames[i]) {
+      fvwm_msg(ERR, "ReadFancyTitlebar", "Expecting %d pixmap filenames after 
TitleStyle Fancy (use '-' to specify no pixmap)", NUM_TB_PIXMAPS);
+      return s;
+    }
+  }
+  if (!(tb->pixmaps)) {
+    tb->pixmaps = (Picture**)safemalloc(NUM_TB_PIXMAPS * BS_MaxButtonState *
+                                        sizeof(Picture*));
+    memset(tb->pixmaps, 0, NUM_TB_PIXMAPS * BS_MaxButtonState *
+           sizeof(Picture*));
+  }
+
+  tb->state[state].style.face_type = FancyTitlebar;
+
+  for (i=0, j = state * NUM_TB_PIXMAPS;
+       i < NUM_TB_PIXMAPS;
+       i++, j++) {
+    if (tb->pixmaps[j])
+      DestroyPicture(dpy, tb->pixmaps[j]);
+    if (StrEquals(pixmap_filenames[i], "-"))
+      tb->pixmaps[j] = NULL;
+    else {
+      tb->pixmaps[j] = CachePicture(dpy, Scr.NoFocusWin, NULL,
+                                    pixmap_filenames[i], Scr.ColorLimit);
+      if (!tb->pixmaps[j])
+        fvwm_msg(WARN, "ReadFancyTitlebar", "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
 /*****************************************************************************
@@ -2138,6 +2370,9 @@
   {
     DFS_FACE_TYPE(TB_STATE(decor->titlebar)[i].style) = SimpleButton;
   }
+#ifdef FANCY_TITLEBARS
+  decor->titlebar.pixmaps = NULL;
+#endif
 
   /* initialize border texture styles */
   DFS_FACE_TYPE(decor->BorderStyle.active.style) = SimpleButton;
@@ -2170,6 +2405,17 @@
   {
     free(decor->tag);
     decor->tag = NULL;
+  }
+#endif
+#ifdef FANCY_TITLEBARS
+  if (decor->titlebar.pixmaps) {
+    int j;
+    for (j=0; j < NUM_TB_PIXMAPS * BS_MaxButtonState; j++) {
+      if (decor->titlebar.pixmaps[j])
+        DestroyPicture(dpy, decor->titlebar.pixmaps[j]);
+    }
+    free(decor->titlebar.pixmaps);
+    decor->titlebar.pixmaps = NULL;
   }
 #endif
 }

Reply via email to