/* This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/****************************************************************************
 * This module is all original code
 * by Rob Nation
 * Copyright 1993, Robert Nation
 *     You may use this code for any purpose, as long as the original
 *     copyright remains in the source code and all documentation
 ****************************************************************************/


/***********************************************************************
 *
 * fvwm window border drawing code
 *
 ***********************************************************************/

#include "config.h"

#include <stdio.h>
#include <signal.h>
#include <string.h>

#include "libs/fvwmlib.h"
#include "libs/FShape.h"
#include "fvwm.h"
#include "externs.h"
#include "events.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "defaults.h"
#include "geometry.h"
#include "borders.h"
#include "builtins.h"
#include "icons.h"
#include "module_interface.h"
#include "libs/Colorset.h"

typedef struct
{
  int relief_width;
  GC relief_gc;
  GC shadow_gc;
  Pixel fore_color;
  Pixel back_color;
  Pixmap back_pixmap;
  XSetWindowAttributes attributes;
  unsigned long valuemask;
  Pixmap texture_pixmap;
  XSetWindowAttributes notex_attributes;
  unsigned long notex_valuemask;

  struct
  {
    unsigned has_color_changed : 1;
  } flags;
} common_decorations_type;

XGCValues Globalgcv;
unsigned long Globalgcm;

extern Window PressedW;

/* used by several drawing functions */
static void ClipClear(Window win, XRectangle *rclip, Bool do_flush_expose)
{
  if (rclip)
  {
    XClearArea(
      dpy, win, rclip->x, rclip->y, rclip->width, rclip->height, False);
  }
  else
  {
    if (do_flush_expose)
    {
      flush_expose(win);
    }
    XClearWindow(dpy, win);
  }

  return;
}

/***********************************************************************
 * change by KitS@bartley.demon.co.uk to correct popups off title buttons
 *
 *  Procedure:
 *ButtonPosition - find the actual position of the button
 *                 since some buttons may be disabled
 *
 *  Returned Value:
 *The button count from left or right taking in to account
 *that some buttons may not be enabled for this window
 *
 *  Inputs:
 *      context - context as per the global Context
 *      t       - the window (FvwmWindow) to test against
 *
 ***********************************************************************/
int ButtonPosition(int context, FvwmWindow *t)
{
  int i = 0;
  int buttons = -1;
  int end = Scr.nr_left_buttons;

  if (context & C_RALL)
  {
    i  = 1;
    end = Scr.nr_right_buttons;
  }
  {
    for (; i / 2 < end; i += 2)
    {
      if (t->button_w[i])
      {
	buttons++;
      }
      /* is this the button ? */
      if (((1 << i) * C_L1) & context)
	return buttons;
    }
  }
  /* you never know... */
  return 0;
}

static void change_window_background(
  Window w, unsigned long valuemask, XSetWindowAttributes *attributes)
{
  XChangeWindowAttributes(dpy, w, valuemask, attributes);
  XClearWindow(dpy, w);
}

/****************************************************************************
 *
 *  Draws a little pattern within a window (more complex)
 *
 ****************************************************************************/
static void DrawLinePattern(
  Window win, GC ReliefGC, GC ShadowGC, Pixel fore_color, Pixel back_color,
  struct vector_coords *coords, int w, int h)
{
  int i;

  /* It is rare, so evaluate this only when needed */
  GC fore_GC = NULL, back_GC = NULL;
  if (coords->use_fgbg)
  {
    Globalgcv.foreground = fore_color;
    Globalgcm = GCForeground;
    XChangeGC(dpy, Scr.ScratchGC3, Globalgcm, &Globalgcv);
    fore_GC = Scr.ScratchGC3;
    Globalgcv.foreground = back_color;
    XChangeGC(dpy, Scr.ScratchGC4, Globalgcm, &Globalgcv);
    back_GC = Scr.ScratchGC4;
  }

  for (i = 1; i < coords->num; ++i)
  {
      XDrawLine(dpy,win,
		(coords->use_fgbg & (1 << i))
		  ? (coords->line_style & (1 << i)) ? fore_GC : back_GC
		  : (coords->line_style & (1 << i)) ? ReliefGC : ShadowGC,
		w * coords->x[i-1]/100,
		h * coords->y[i-1]/100,
		w * coords->x[i]/100,
		h * coords->y[i]/100);
  }
}

/* used by DrawButton() */
static void clip_button_pixmap(
  int *ox, int *oy, int *cw, int *ch, int*cx, int *cy, XRectangle *rclip)
{
  if (rclip)
  {
    if (rclip->x > *cx)
    {
      *ox = (rclip->x - *cx);
      *cx += *ox;
      *cw -= *ox;
    }
    if (*cx + *cw > rclip->x + rclip->width)
    {
      *cw = rclip->x - *cx + rclip->width;
    }
    if (rclip->y > *cy)
    {
      *oy = (rclip->y - *cy);
      *cy += *oy;
      *ch -= *oy;
    }
    if (*cy + *ch > rclip->y + rclip->height)
    {
      *ch = rclip->y - *cy + rclip->height;
    }
  }

  return;
}

/****************************************************************************
 *
 *  Redraws buttons (veliaa@rpi.edu)
 *
 ****************************************************************************/
static void DrawButton(
  FvwmWindow *t, Window win, int w, int h, DecorFace *df, GC ReliefGC,
  GC ShadowGC, Pixel fore_color, Pixel back_color, Bool is_lowest,
  mwm_flags stateflags, int left1right0, XRectangle *rclip,
  Pixmap *pbutton_background_pixmap)
{
  register DecorFaceType type = DFS_FACE_TYPE(df->style);
  Picture *p;
  int border = 0;
  int x;
  int y;
  int width;
  int height;

  /* Note: it is assumed that ReliefGC and ShadowGC are already clipped with
   * the rclip rectangle when this function is called. */
  switch (type)
  {
  case SimpleButton:
    break;

  case SolidButton:
    if (pbutton_background_pixmap)
    {
      XSetWindowBackground(dpy, win, df->u.back);
      *pbutton_background_pixmap = None;
    }
    ClipClear(win, rclip, True);
    break;

  case VectorButton:
  case DefaultVectorButton:
    if(HAS_MWM_BUTTONS(t) &&
       ((stateflags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(t)) ||
	(stateflags & MWM_DECOR_SHADE && IS_SHADED(t)) ||
	(stateflags & MWM_DECOR_STICK && IS_STICKY(t))))
    {
      DrawLinePattern(win, ShadowGC, ReliefGC, fore_color, back_color,
        &df->u.vector, w, h);
    }
    else
    {
      DrawLinePattern(win, ReliefGC, ShadowGC, fore_color, back_color,
        &df->u.vector, w, h);
    }
    break;

#ifdef MINI_ICONS
  case MiniIconButton:
  case PixmapButton:
  case TiledPixmapButton:
    if (type == PixmapButton || type == TiledPixmapButton)
    {
      p = df->u.p;
    }
    else
    {
      if (!t->mini_icon)
	break;
      p = t->mini_icon;
    }
#else
  case PixmapButton:
  case TiledPixmapButton:
    p = df->u.p;
#endif /* MINI_ICONS */
    if (DFS_BUTTON_RELIEF(df->style) == DFS_BUTTON_IS_FLAT)
      border = 0;
    else
      border = HAS_MWM_BORDER(t) ? 1 : 2;
    x = border;
    y = border;
    width = w - border * 2;
    height = h - border * 2;

    if (is_lowest && !p->mask && pbutton_background_pixmap)
    {
      if (type == TiledPixmapButton ||
	  (p && p->width >= width && p->height >= height))
      {
	/* better performance: set a background pixmap if possible, i.e. if the
	 * decoration is the lowest one in the button and it has no transparent
	 * parts and it is tiled or bigger than the button */
	if (*pbutton_background_pixmap != p->picture)
	{
	  *pbutton_background_pixmap = p->picture;
	  XSetWindowBackgroundPixmap(dpy, win, p->picture);
	  ClipClear(win, rclip, True);
	}
	else
	{
	  ClipClear(win, rclip, True);
	}
	break;
      }
    }
    else if (pbutton_background_pixmap && *pbutton_background_pixmap != None)
    {
      XSetWindowBackgroundPixmap(dpy, win, None);
      if (pbutton_background_pixmap)
      {
	*pbutton_background_pixmap = None;
	flush_expose(win);
	XClearWindow(dpy, win);
      }
    }
    if (type != TiledPixmapButton)
    {
      if (DFS_H_JUSTIFICATION(df->style) == JUST_RIGHT)
	x += (int)(width - p->width);
      else if (DFS_H_JUSTIFICATION(df->style) == JUST_CENTER)
	/* round up for left buttons, down for right buttons */
	x += (int)(width - p->width + left1right0) / 2;
      if (DFS_V_JUSTIFICATION(df->style) == JUST_BOTTOM)
	y += (int)(height - p->height);
      else if (DFS_V_JUSTIFICATION(df->style) == JUST_CENTER)
	/* round up */
	y += (int)(height - p->height + 1) / 2;
      if (x < border)
	x = border;
      if (y < border)
	y = border;
      if (width > p->width)
	width = p->width;
      if (height > p->height)
	height = p->height;
      if (width > w - x - border)
	width = w - x - border;
      if (height > h - y - border)
	height = h - y - border;
    }

    XSetClipMask(dpy, Scr.TransMaskGC, p->mask);
    XSetClipOrigin(dpy, Scr.TransMaskGC, x, y);
    if (type != TiledPixmapButton)
    {
      int cx = x;
      int cy = y;
      int cw = width;
      int ch = height;
      int ox = 0;
      int oy = 0;

      clip_button_pixmap(&ox, &oy, &cw, &ch, &cx, &cy, rclip);
      if (cw > 0 && ch > 0)
      {
	XCopyArea(
	  dpy, p->picture, win, Scr.TransMaskGC, ox, oy, cw, ch, cx, cy);
      }
    }
    else
    {
      int xi;
      int yi;

      for (yi = border; yi < height; yi += p->height)
      {
	for (xi = border; xi < width; xi += p->width)
	{
	  int lw = width - xi;
	  int lh = height - yi;
	  int cx;
	  int cy;
	  int cw;
	  int ch;
	  int ox;
	  int oy;

	  if (lw > p->width)
	    lw = p->width;
	  if (lh > p->height)
	    lh = p->height;
	  if (rclip)
	  {
	    if (xi + lw <= rclip->x || xi >= rclip->x + rclip->width ||
		yi + lh <= rclip->y || yi >= rclip->y + rclip->height)
	    {
	      continue;
	    }
	  }
	  cx = xi;
	  cy = yi;
	  cw = lw;
	  ch = lh;
	  ox = 0;
	  oy = 0;
	  clip_button_pixmap(&ox, &oy, &cw, &ch, &cx, &cy, rclip);
	  if (cw > 0 && ch > 0)
	  {
	    XSetClipOrigin(dpy, Scr.TransMaskGC, xi, yi);
	    XCopyArea(dpy, p->picture, win, Scr.TransMaskGC,
		      ox, oy, cw, ch, cx, cy);
	  }
	}
      }
    }
    XSetClipMask(dpy, Scr.TransMaskGC, None);
    break;

  case GradientButton:
  {
    unsigned int g_width;
    unsigned int g_height;

    if (!rclip)
    {
      flush_expose(win);
    }
    XSetClipMask(dpy, Scr.TransMaskGC, None);
    /* find out the size the pixmap should be */
    CalculateGradientDimensions(
      dpy, win, df->u.grad.npixels, df->u.grad.gradient_type,
      &g_width, &g_height);
    /* draw the gradient directly into the window */
    CreateGradientPixmap(
      dpy, win, Scr.TransMaskGC, df->u.grad.gradient_type, g_width, g_height,
      df->u.grad.npixels, df->u.grad.pixels, win, 0, 0, w, h, rclip);
  }
  break;

  default:
    fvwm_msg(ERR, "DrawButton", "unknown button type");
    break;
  }

  return;
}

/* rules to get button state */
static enum ButtonState get_button_state(
  Bool has_focus, Bool toggled, Window w)
{
  if (Scr.gs.use_active_down_buttons)
  {
    if (Scr.gs.use_inactive_buttons && !has_focus)
    {
      return (toggled) ? BS_ToggledInactive : BS_Inactive;
    }
    else
    {
      if (PressedW == w)
      {
	return (toggled) ? BS_ToggledActiveDown : BS_ActiveDown;
      }
      else
      {
	return (toggled) ? BS_ToggledActiveUp : BS_ActiveUp;
      }
    }
  }
  else
  {
    if (Scr.gs.use_inactive_buttons && !has_focus)
    {
      return (toggled) ? BS_ToggledInactive : BS_Inactive;
    }
    else
    {
      return (toggled) ? BS_ToggledActiveUp : BS_ActiveUp;
    }
  }
}

static const char ulgc[] = { 1, 0, 0, 0x7f, 2, 1, 1 };
static const char brgc[] = { 1, 1, 2, 0x7f, 0, 0, 3 };

/* called twice by RedrawBorder */
static void draw_frame_relief(
  FvwmWindow *t, GC rgc, GC sgc, GC tgc, GC dgc, int w_dout, int w_hiout,
  int w_trout, int w_c, int w_trin, int w_shin, int w_din)
{
  int i;
  int offset = 0;
  int width = t->frame_g.width - 1;
  int height = t->frame_g.height - 1;
  int w[7];
  GC gc[4];

  w[0] = w_dout;
  w[1] = w_hiout;
  w[2] = w_trout;
  w[3] = w_c;
  w[4] = w_trin;
  w[5] = w_shin;
  w[6] = w_din;
  gc[0] = rgc;
  gc[1] = sgc;
  gc[2] = tgc;
  gc[3] = dgc;

  for (i = 0; i < 7; i++)
  {
    if (ulgc[i] != 0x7f && w[i] > 0)
    {
      RelieveRectangle(
	dpy, t->decor_w, offset, offset, width, height, gc[(int)ulgc[i]],
	gc[(int)brgc[i]], w[i]);
    }
    offset += w[i];
    width -= 2 * w[i];
    height -= 2 * w[i];
  }
}

/****************************************************************************
 *
 * Redraws the windows borders
 *
 ****************************************************************************/
static void RedrawBorder(
  common_decorations_type *cd, FvwmWindow *t, Bool has_focus, int force,
  Window expose_win, XRectangle *rclip)
{
  static GC tgc = None;
  int i;
  GC rgc;
  GC sgc;
  DecorFaceStyle *borderstyle;
  Bool is_reversed = False;
  Bool is_clipped = False;
  int w_dout;
  int w_hiout;
  int w_trout;
  int w_c;
  int w_trin;
  int w_shin;
  int w_din;
  int sum;
  int trim;

  if (tgc == None && !HAS_MWM_BORDER(t))
  {
    XGCValues xgcv;

    xgcv.function = GXnoop;
    xgcv.plane_mask = 0;
    tgc = fvwmlib_XCreateGC(dpy, t->frame, GCFunction | GCPlaneMask, &xgcv);
  }
  /*
   * draw the border; resize handles are InputOnly so draw in the decor_w and
   * it will show through. The entire border is redrawn so flush all exposes
   * up to this point.
   */
#if 0
  flush_expose(t->decor_w);
#endif

  if (t->boundary_width < 2)
  {
    /*
     * for mono - put a black border on
     */
    if (Pdepth <2)
    {
      XSetWindowBackgroundPixmap(dpy, t->decor_w, cd->back_pixmap);
      XClearWindow(dpy, t->decor_w);
    }
    else
    {
      if (cd->texture_pixmap != None)
	XSetWindowBackgroundPixmap(dpy, t->decor_w, cd->texture_pixmap);
      XClearWindow(dpy, t->decor_w);
    }
    return;
  }

  /*
   * for color, make it the color of the decoration background
   */

  /* get the border style bits */
  borderstyle = (has_focus) ?
    &GetDecor(t, BorderStyle.active.style) :
    &GetDecor(t, BorderStyle.inactive.style);

  is_reversed ^= (borderstyle->flags.button_relief == DFS_BUTTON_IS_SUNK);
  if (is_reversed)
  {
    sgc = cd->relief_gc;
    rgc = cd->shadow_gc;
  }
  else
  {
    rgc = cd->relief_gc;
    sgc = cd->shadow_gc;
  }

  /*
   * remove any lines drawn in the border if hidden handles or noinset and if
   * a handle was pressed
   */

  if (PressedW == None &&
      (DFS_HAS_NO_INSET(*borderstyle) ||
       DFS_HAS_HIDDEN_HANDLES(*borderstyle)))
  {
    ClipClear(t->decor_w, rclip, False);
  }

  /*
   * Nothing to do if flat
   */

  if (borderstyle->flags.button_relief == DFS_BUTTON_IS_FLAT)
    return;

  /*
   * draw the inside relief
   */

  if (HAS_MWM_BORDER(t))
  {
    /* MWM borders look like this:
     *
     * HHCCCCS  from outside to inside on the left and top border
     * SSCCCCH  from outside to inside on the bottom and right border
     * |||||||
     * |||||||__ w_shin       (inner shadow area)
     * ||||||___ w_c          (transparent area)
     * |||||____ w_c          (transparent area)
     * ||||_____ w_c          (transparent area)
     * |||______ w_c          (transparent area)
     * ||_______ w_hiout      (outer hilight area)
     * |________ w_hiout      (outer hilight area)
     *
     *
     * C = original colour
     * H = hilight
     * S = shadow
     */
    w_dout = 0;
    w_hiout = 2;
    w_trout = 0;
    w_trin = 0;
    w_shin = 1;
    w_din = 0;
    sum = 3;
    trim = sum - t->boundary_width + 1;
  }
  else
  {
    /* Fvwm borders look like this:
     *
     * SHHCCSS  from outside to inside on the left and top border
     * SSCCHHS  from outside to inside on the bottom and right border
     * |||||||
     * |||||||__ w_din        (inner dark area)
     * ||||||___ w_shin       (inner shadow area)
     * |||||____ w_trin       (inner transparent/shadow area)
     * ||||_____ w_c          (transparent area)
     * |||______ w_trout      (outer transparent/hilight area)
     * ||_______ w_hiout      (outer hilight area)
     * |________ w_dout       (outer dark area)
     *
     * C = original colour
     * H = hilight
     * S = shadow
     *
     * reduced to 5 pixels it looks like this:
     *
     * SHHCS
     * SSCHS
     * |||||
     * |||||__ w_din        (inner dark area)
     * ||||___ w_trin       (inner transparent/shadow area)
     * |||____ w_trout      (outer transparent/hilight area)
     * ||_____ w_hiout      (outer hilight area)
     * |______ w_dout       (outer dark area)
     */
    w_dout = 1;
    w_hiout = 1;
    w_trout = 1;
    w_trin = 1;
    w_shin = 1;
    w_din = 1;
    /* w_trout + w_trin counts only as one pixel of border because they let one
     * pixel of the original colour shine through. */
    sum = 6;
    trim = sum - t->boundary_width;
  }
  if (DFS_HAS_NO_INSET(*borderstyle))
  {
    w_shin = 0;
    sum--;
    trim--;
    if (w_trin)
    {
      w_trout = 0;
      w_trin = 0;
      w_din = 0;
      w_hiout = 1;
      sum -= 2;
      trim -= 2;
    }
  }
  /* If the border is too thin to accomodate the standard look, we remove parts
   * of the border so that at least one pixel of the original colour is
   * visible. We make an exception for windows with a border width of 2,
   * though. */
  if ((!IS_SHADED(t) || HAS_TITLE(t)) && t->boundary_width == 2)
    trim--;
  if (trim < 0)
    trim = 0;
  for ( ; trim > 0; trim--)
  {
    if (w_hiout > 1)
      w_hiout--;
    else if (w_shin > 0)
      w_shin--;
    else if (w_hiout > 0)
      w_hiout--;
    else if (w_trout > 0)
    {
      w_trout = 0;
      w_trin = 0;
      w_din = 0;
      w_hiout = 1;
    }
    sum--;
  }
  w_c = t->boundary_width - sum;
  if (rclip)
  {
    XSetClipRectangles(dpy, rgc, 0, 0, rclip, 1, Unsorted);
    XSetClipRectangles(dpy, sgc, 0, 0, rclip, 1, Unsorted);
    is_clipped = True;
  }
  draw_frame_relief(
    t, rgc, sgc, tgc, sgc,
    w_dout, w_hiout, w_trout, w_c, w_trin, w_shin, w_din);

  /*
   * draw the handle marks
   */

  /* draw the handles as eight marks around the border */
  if (HAS_BORDER(t) && (t->boundary_width > 1) &&
      !DFS_HAS_HIDDEN_HANDLES(*borderstyle))
  {
    /* MWM border windows have thin 3d effects
     * FvwmBorders have 3 pixels top/left, 2 bot/right so this makes
     * calculating the length of the marks difficult, top and bottom
     * marks for FvwmBorders are different if NoInset is specified */
    int inset = (w_shin || w_din);
    XSegment marks[8];
    int k;

    /*
     * draw the relief
     */
    for (k = 0; k < cd->relief_width; k++)
    {
      int loff = k;
      int boff = k + w_dout + 1;
      int length = t->boundary_width - boff - inset;

      if (length < 0)
	break;
      /* hilite marks */
      i = 0;
      /* top left */
      marks[i].x1 = t->visual_corner_width + loff;
      marks[i].x2 = t->visual_corner_width + loff;
      marks[i].y1 = w_dout;
      marks[i].y2 = marks[i].y1 + length;
      i++;
      /* top right */
      marks[i].x1 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].x2 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].y1 = w_dout;
      marks[i].y2 = marks[i].y1 + length;
      i++;
      /* bot left */
      marks[i].x1 = t->visual_corner_width + loff;
      marks[i].x2 = t->visual_corner_width + loff;
      marks[i].y1 = t->frame_g.height - boff;
      marks[i].y2 = marks[i].y1 - length;
      i++;
      /* bot right */
      marks[i].x1 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].x2 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].y1 = t->frame_g.height - boff;
      marks[i].y2 = marks[i].y1 - length;
      i++;
      if (!IS_SHADED(t))
      {
	/* left top */
	marks[i].x1 = w_dout;
	marks[i].x2 = marks[i].x1 + length;
	marks[i].y1 = t->visual_corner_width + loff;
	marks[i].y2 = t->visual_corner_width + loff;
	i++;
	/* left bot */
	marks[i].x1 = w_dout;
	marks[i].x2 = marks[i].x1 + length;
	marks[i].y1 = t->frame_g.height-t->visual_corner_width + loff;
	marks[i].y2 = t->frame_g.height-t->visual_corner_width + loff;
	i++;
	/* right top */
	marks[i].x1 = t->frame_g.width - boff;
	marks[i].x2 = marks[i].x1 - length;
	marks[i].y1 = t->visual_corner_width + loff;
	marks[i].y2 = t->visual_corner_width + loff;
	i++;
	/* right bot */
	marks[i].x1 = t->frame_g.width - boff;
	marks[i].x2 = marks[i].x1 - length;
	marks[i].y1 = t->frame_g.height-t->visual_corner_width + loff;
	marks[i].y2 = t->frame_g.height-t->visual_corner_width + loff;
	i++;
      }
      XDrawSegments(dpy, t->decor_w, rgc, marks, i);

      /* shadow marks, reuse the array (XDrawSegments doesn't trash it) */
      i = 0;
      loff = 1 + k + k;
      /* top left */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      /* top right */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      /* bot left */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      /* bot right */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      if (!IS_SHADED(t))
      {
	/* left top */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
	/* left bot */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
	/* right top */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
	/* right bot */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
      }
      XDrawSegments(dpy, t->decor_w, sgc, marks, i);
    }
  }

  /*
   * now draw the pressed in part on top if we have depressable borders
   */

  /* a bit hacky to draw twice but you should see the code it replaces, never
   * mind the esoterics, feel the thin-ness */
  if ((HAS_BORDER(t) || PressedW == t->decor_w || PressedW == t->frame) &&
      HAS_DEPRESSABLE_BORDER(t))
  {
    XRectangle r;
    Bool is_pressed = False;

    if (PressedW == t->sides[0])
    {
      /* top */
      r.x = t->visual_corner_width;
      r.y = 1;
      r.width = t->frame_g.width - 2 * t->visual_corner_width;
      r.height = t->boundary_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->sides[1])
    {
      /* right */
      if (!IS_SHADED(t))
      {
	r.x = t->frame_g.width - t->boundary_width;
	r.y = t->visual_corner_width;
	r.width = t->boundary_width - 1;
	r.height = t->frame_g.height - 2 * t->visual_corner_width;
	is_pressed = True;
      }
    }
    else if (PressedW == t->sides[2])
    {
      /* bottom */
      r.x = t->visual_corner_width;
      r.y = t->frame_g.height - t->boundary_width;
      r.width = t->frame_g.width - 2 * t->visual_corner_width;
      r.height = t->boundary_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->sides[3])
    {
      /* left */
      if (!IS_SHADED(t))
      {
	r.x = 1;
	r.y = t->visual_corner_width;
	r.width = t->boundary_width - 1;
	r.height = t->frame_g.height - 2 * t->visual_corner_width;
	is_pressed = True;
      }
    }
    else if (PressedW == t->corners[0])
    {
      /* top left */
      r.x = 1;
      r.y = 1;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->corners[1])
    {
      /* top right */
      r.x = t->frame_g.width - t->visual_corner_width;
      r.y = 1;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->corners[2])
    {
      /* bottom left */
      r.x = 1;
      r.y = t->frame_g.height - t->visual_corner_width;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->corners[3])
    {
      /* bottom right */
      r.x = t->frame_g.width - t->visual_corner_width;
      r.y = t->frame_g.height - t->visual_corner_width;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->decor_w || PressedW == t->frame)
    {
      /* whole border */
      r.x = 1;
      r.y = 1;
      r.width = t->frame_g.width - 2;
      r.height = t->frame_g.height - 2;
      is_pressed = True;
    }

    if (is_pressed == True)
    {
      Bool is_not_empty = True;

      if (rclip)
      {
	is_not_empty = intersect_xrectangles(&r, rclip);
      }
      if (is_not_empty)
      {
	XSetClipRectangles(dpy, rgc, 0, 0, &r, 1, Unsorted);
	XSetClipRectangles(dpy, sgc, 0, 0, &r, 1, Unsorted);
	draw_frame_relief(
	  t, sgc, rgc, tgc, sgc,
	  w_dout, w_hiout, w_trout, w_c, w_trin, w_shin, w_din);
	is_clipped = True;
      }
    }
  }
  if (is_clipped)
  {
    XSetClipMask(dpy, rgc, None);
    XSetClipMask(dpy, sgc, None);
  }

  return;
}

/****************************************************************************
 *
 * Redraws the window buttons
 *
 ****************************************************************************/
static void RedrawButtons(
  common_decorations_type *cd, FvwmWindow *t, Bool has_focus, int force,
  Window expose_win, XRectangle *rclip)
{
  int i;
  Bool is_lowest = True;
  Pixmap *pass_bg_pixmap;

  /* Note: the rclip rectangle is not used in this function. Buttons are usually
   * so small that it makes not much sense to limit drawing to a clip rectangle.
   */

  /*
   * draw buttons
   */

  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)))
    {
      mwm_flags stateflags =
	TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i]));
      Bool toggled =
	(HAS_MWM_BUTTONS(t) &&
	 ((stateflags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(t)) ||
	  (stateflags & MWM_DECOR_SHADE && IS_SHADED(t)) ||
	  (stateflags & MWM_DECOR_STICK && IS_STICKY(t))));
      enum ButtonState bs =
	get_button_state(has_focus, toggled, t->button_w[i]);
      DecorFace *df = &TB_STATE(GetDecor(t, buttons[i]))[bs];
      if(flush_expose(t->button_w[i]) || expose_win == t->button_w[i] ||
	 expose_win == None || cd->flags.has_color_changed)
      {
	int is_inverted = (PressedW == t->button_w[i]);

	if (DFS_USE_BORDER_STYLE(df->style))
	{
	  XChangeWindowAttributes(
	    dpy, t->button_w[i], cd->valuemask, &cd->attributes);
	  if (df->u.p)
	  {
	    t->button_background_pixmap[i] = df->u.p->picture;
	  }
	  if (df->u.p && df->u.p->picture)
	  {
	    pass_bg_pixmap = NULL;
	  }
	  else
	  {
	    pass_bg_pixmap = &t->button_background_pixmap[i];
	  }
	}
	else
	{
	  XChangeWindowAttributes(
	    dpy, t->button_w[i], cd->notex_valuemask, &cd->notex_attributes);
	  t->button_background_pixmap[i] = None;
	  pass_bg_pixmap = &t->button_background_pixmap[i];
	}
	XClearWindow(dpy, t->button_w[i]);
	if (DFS_USE_TITLE_STYLE(df->style))
	{
	  DecorFace *tsdf = &TB_STATE(GetDecor(t, titlebar))[bs];

	  is_lowest = True;
#ifdef MULTISTYLE
	  for (; tsdf; tsdf = tsdf->next)
#endif
	  {
	    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);
	    is_lowest = False;
	  }
	}
	is_lowest = True;
#ifdef MULTISTYLE
	for (; df; df = df->next)
#endif
	{
	  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);
	  is_lowest = False;
	}

	{
	  Bool reverse = is_inverted;

	  switch (DFS_BUTTON_RELIEF(
	    TB_STATE(GetDecor(t, buttons[i]))[bs].style))
	  {
	  case DFS_BUTTON_IS_SUNK:
	    reverse = !is_inverted;
	  case DFS_BUTTON_IS_UP:
	    RelieveRectangle2(
	      dpy, t->button_w[i], 0, 0, t->title_g.height - 1,
	      t->title_g.height - 1, (reverse ? cd->shadow_gc : cd->relief_gc),
	      (reverse ? cd->relief_gc : cd->shadow_gc), cd->relief_width);
	    break;
	  default:
	    /* flat */
	    break;
	  }
	}
      }
    } /* if (HAS_BUTTON(i))*/
  } /* for */
}


/****************************************************************************
 *
 *  Redraws just the title bar
 *
 ****************************************************************************/
static void RedrawTitle(
  common_decorations_type *cd, FvwmWindow *t, Bool has_focus, XRectangle *rclip)
{
  int hor_off;
  int w;
  int i;
  enum ButtonState title_state;
  DecorFaceStyle *tb_style;
  Pixmap *pass_bg_pixmap;
  GC rgc = cd->relief_gc;
  GC sgc = cd->shadow_gc;
  Bool reverse = False;
  Bool is_clipped = False;
  Bool toggled =
    (HAS_MWM_BUTTONS(t) &&
     ((TB_HAS_MWM_DECOR_MAXIMIZE(GetDecor(t, titlebar)) && IS_MAXIMIZED(t))||
      (TB_HAS_MWM_DECOR_SHADE(GetDecor(t, titlebar)) && IS_SHADED(t)) ||
      (TB_HAS_MWM_DECOR_STICK(GetDecor(t, titlebar)) && IS_STICKY(t))));

  if (PressedW == t->title_w)
  {
    GC tgc;

    tgc = sgc;
    sgc = rgc;
    rgc = tgc;
  }
#if 0
  flush_expose(t->title_w);
#endif

  if (t->name != (char *)NULL)
  {
#ifdef I18N_MB /* cannot use macro here, rewriting... */
    w = XmbTextEscapement(t->title_font.fontset,t->name, strlen(t->name));
#else
    w = XTextWidth(t->title_font.font, t->name, strlen(t->name));
#endif
    if (w > t->title_g.width - 12)
      w = t->title_g.width - 4;
    if (w < 0)
      w = 0;
  }
  else
  {
    w = 0;
  }

  title_state = get_button_state(has_focus, toggled, t->title_w);
  tb_style = &TB_STATE(GetDecor(t, titlebar))[title_state].style;
  switch (TB_JUSTIFICATION(GetDecor(t, titlebar)))
  {
  case JUST_LEFT:
    hor_off = WINDOW_TITLE_TEXT_OFFSET;
    break;
  case JUST_RIGHT:
    hor_off = t->title_g.width - w - WINDOW_TITLE_TEXT_OFFSET;
    break;
  case JUST_CENTER:
  default:
    hor_off = (t->title_g.width - w) / 2;
    break;
  }

  NewFontAndColor(
    t->title_font.font->fid, cd->fore_color, cd->back_color);

  /* the next bit tries to minimize redraw (veliaa@rpi.edu) */
  /* we need to check for UseBorderStyle for the titlebar */
  {
    DecorFace *df = (has_focus) ?
      &GetDecor(t,BorderStyle.active) : &GetDecor(t,BorderStyle.inactive);

    if (DFS_USE_BORDER_STYLE(*tb_style) &&
	DFS_FACE_TYPE(df->style) == TiledPixmapButton &&
	df->u.p)
    {
      XSetWindowBackgroundPixmap(dpy, t->title_w, df->u.p->picture);
      t->title_background_pixmap = df->u.p->picture;
      pass_bg_pixmap = NULL;
    }
    else
    {
      pass_bg_pixmap = &t->title_background_pixmap;
    }
  }
  ClipClear(t->title_w, rclip, False);
  if (rclip)
  {
    XSetClipRectangles(dpy, rgc, 0, 0, rclip, 1, Unsorted);
    XSetClipRectangles(dpy, sgc, 0, 0, rclip, 1, Unsorted);
    XSetClipRectangles(dpy, Scr.TitleGC, 0, 0, rclip, 1, Unsorted);
    is_clipped = True;
  }

  /*
   * draw title
   */

  if (Pdepth < 2)
  {
    /* for mono, we clear an area in the title bar where the window
     * title goes, so that its more legible. For color, no need */
    RelieveRectangle(
      dpy, t->title_w, 0, 0, hor_off - 3, t->title_g.height - 1, rgc, sgc,
      cd->relief_width);
    RelieveRectangle(
      dpy, t->title_w, hor_off + w + 2, 0, t->title_g.width - w - hor_off - 3,
      t->title_g.height - 1, rgc, sgc, cd->relief_width);
    XFillRectangle(
      dpy, t->title_w, ((PressedW == t->title_w) ? sgc : rgc), hor_off - 2, 0,
      w+4,t->title_g.height);
    XDrawLine(
      dpy, t->title_w, sgc, hor_off + w + 1, 0, hor_off + w + 1,
      t->title_g.height);
    if(t->name != (char *)NULL)
#ifdef I18N_MB
      XmbDrawString(dpy, t->title_w, t->title_font.fontset,
		    Scr.TitleGC, hor_off,
		    t->title_font.y + 1, t->name, strlen(t->name));
#else
      XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off,
                  t->title_font.y + 1, t->name, strlen(t->name));
#endif
  }
  else
  {
    DecorFace *df = &TB_STATE(GetDecor(t, titlebar))[title_state];
    /* draw compound titlebar (veliaa@rpi.edu) */
    Bool is_lowest = True;

    if (PressedW == t->title_w)
    {
#ifdef MULTISTYLE
      for (; df; df = df->next)
#endif
      {
	DrawButton(
	  t, t->title_w, t->title_g.width, t->title_g.height, df, sgc,
	  rgc, cd->fore_color, cd->back_color, is_lowest, 0, 1, rclip,
          pass_bg_pixmap);
	is_lowest = False;
      }
    }
    else
    {
#ifdef MULTISTYLE
      for (; df; df = df->next)
#endif
      {
	DrawButton(
	  t, t->title_w, t->title_g.width, t->title_g.height, df, rgc,
	  sgc, cd->fore_color, cd->back_color, is_lowest, 0, 1, rclip,
          pass_bg_pixmap);
	is_lowest = False;
      }
    }
    /*
     * draw title relief
     */
    switch (DFS_BUTTON_RELIEF(*tb_style))
    {
    case DFS_BUTTON_IS_SUNK:
      reverse = 1;
    case DFS_BUTTON_IS_UP:
      RelieveRectangle2(
	dpy, t->title_w, 0, 0, t->title_g.width - 1, t->title_g.height - 1,
	(reverse) ? sgc : rgc, (reverse) ? rgc : sgc, cd->relief_width);
      break;
    default:
      /* flat */
      break;
    }

#ifdef I18N_MB
    if(t->name != (char *)NULL)
      XmbDrawString(dpy, t->title_w, t->title_font.fontset,
		    Scr.TitleGC, hor_off,
		    t->title_font.y + 1, t->name, strlen(t->name));
#else
    if(t->name != (char *)NULL)
      XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off,
		  t->title_font.y + 1, t->name, strlen(t->name));
#endif
  }

  /*
   * draw the 'sticky' lines
   */

  if (IS_STICKY(t) || HAS_STIPPLED_TITLE(t))
  {
    /* an odd number of lines every WINDOW_TITLE_STICK_VERT_DIST pixels */
    int num =
      (int)(t->title_g.height / WINDOW_TITLE_STICK_VERT_DIST / 2) * 2 - 1;
    int min = t->title_g.height / 2 - num * 2 + 1;
    int max =
            t->title_g.height / 2 + num * 2 - WINDOW_TITLE_STICK_VERT_DIST + 1;
    int left_x = WINDOW_TITLE_STICK_OFFSET;
    int left_w = hor_off - left_x - WINDOW_TITLE_TO_STICK_GAP;
    int right_x = hor_off + w + WINDOW_TITLE_TO_STICK_GAP - 1;
    int right_w = t->title_g.width - right_x - WINDOW_TITLE_STICK_OFFSET;

    if (left_w < WINDOW_TITLE_STICK_MIN_WIDTH)
    {
      left_x = 0;
      left_w = WINDOW_TITLE_STICK_MIN_WIDTH;
    }
    if (right_w < WINDOW_TITLE_STICK_MIN_WIDTH)
    {
      right_w = WINDOW_TITLE_STICK_MIN_WIDTH;
      right_x = t->title_g.width - WINDOW_TITLE_STICK_MIN_WIDTH - 1;
    }
    for (i = min; i <= max; i += WINDOW_TITLE_STICK_VERT_DIST)
    {
      if (left_w > 0)
      {
	RelieveRectangle(
          dpy, t->title_w, left_x, i, left_w, 1, sgc, rgc, 1);
      }
      if (right_w > 0)
      {
	RelieveRectangle(
          dpy, t->title_w, right_x, i, right_w, 1, sgc, rgc, 1);
      }
    }
  }

  if (is_clipped)
  {
    XSetClipMask(dpy, rgc, None);
    XSetClipMask(dpy, sgc, None);
    XSetClipMask(dpy, Scr.TitleGC, None);
  }

  return;
}


void SetupTitleBar(FvwmWindow *tmp_win, int w, int h)
{
  XWindowChanges xwc;
  unsigned long xwcm;
  int i;
  int buttons = 0;
  int ww = tmp_win->frame_g.width - 2 * tmp_win->boundary_width;
  int rest = 0;
  int bw;

  if (HAS_BOTTOM_TITLE(tmp_win))
  {
    tmp_win->title_g.y =
      h - tmp_win->boundary_width - tmp_win->title_g.height;
    tmp_win->title_top_height = 0;
  }
  else
  {
    tmp_win->title_g.y = tmp_win->boundary_width;
    tmp_win->title_top_height = tmp_win->title_g.height;
  }
  tmp_win->title_g.x = tmp_win->boundary_width;

  xwcm = CWX | CWY | CWHeight | CWWidth;
  xwc.y = tmp_win->title_g.y;
  xwc.width = tmp_win->title_g.height;
  xwc.height = tmp_win->title_g.height;
  for (i = 0; i < NUMBER_OF_BUTTONS; i++)
  {
    if (tmp_win->button_w[i])
      buttons++;
  }
  ww = tmp_win->frame_g.width - 2 * tmp_win->boundary_width -
    tmp_win->title_g.width;
  if (ww < buttons * xwc.width)
  {
    xwc.width = ww / buttons;
    if (xwc.width < 1)
      xwc.width = 1;
    if (xwc.width > tmp_win->title_g.height)
      xwc.width = tmp_win->title_g.height;
    rest = ww - buttons * xwc.width;
    if (rest > 0)
      xwc.width++;
  }
  /* left */
  xwc.x = tmp_win->boundary_width;
  for (i = 0; i < NUMBER_OF_BUTTONS; i += 2)
  {
    if (tmp_win->button_w[i] != None)
    {
      if (xwc.x + xwc.width < w - tmp_win->boundary_width)
      {
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
	xwc.x += xwc.width;
        tmp_win->title_g.x += xwc.width;
      }
      else
      {
	xwc.x = -tmp_win->title_g.height;
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
      }
      rest--;
      if (rest == 0)
      {
        xwc.width--;
      }
    }
  }
  bw = xwc.width;

  /* title */
  if (tmp_win->title_g.width <= 0 || ww < 0)
    tmp_win->title_g.x = -10;
  xwc.x = tmp_win->title_g.x;
  xwc.width = tmp_win->title_g.width;
  XConfigureWindow(dpy, tmp_win->title_w, xwcm, &xwc);

  /* right */
  xwc.width = bw;
  xwc.x = w - tmp_win->boundary_width - xwc.width;
  for (i = 1 ; i < NUMBER_OF_BUTTONS; i += 2)
  {
    if (tmp_win->button_w[i] != None)
    {
      if (xwc.x > tmp_win->boundary_width)
      {
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
	xwc.x -= xwc.width;
      }
      else
      {
	xwc.x = -tmp_win->title_g.height;
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
      }
      rest--;
      if (rest == 0)
      {
        xwc.width--;
        xwc.x++;
      }
    }
  }

  return;
}


static void get_common_decorations(
  common_decorations_type *cd, FvwmWindow *t, draw_window_parts draw_parts,
  Bool has_focus, int force, Window expose_win, Bool is_border,
  Bool do_change_gcs)
{
  color_quad *draw_colors;

  if (has_focus)
  {
    /* are we using textured borders? */
    if (DFS_FACE_TYPE(
      GetDecor(t, BorderStyle.active.style)) == TiledPixmapButton)
    {
      cd->texture_pixmap = GetDecor(t, BorderStyle.active.u.p->picture);
    }
    cd->back_pixmap= Scr.gray_pixmap;
    if (is_border)
      draw_colors = &(t->border_hicolors);
    else
      draw_colors = &(t->hicolors);
  }
  else
  {
    if (DFS_FACE_TYPE(GetDecor(t, BorderStyle.inactive.style)) ==
	TiledPixmapButton)
    {
      cd->texture_pixmap = GetDecor(t, BorderStyle.inactive.u.p->picture);
    }
    if(IS_STICKY(t))
      cd->back_pixmap = Scr.sticky_gray_pixmap;
    else
      cd->back_pixmap = Scr.light_gray_pixmap;
    if (is_border)
      draw_colors = &(t->border_colors);
    else
      draw_colors = &(t->colors);
  }
  cd->fore_color = draw_colors->fore;
  cd->back_color = draw_colors->back;
  if (do_change_gcs)
  {
    Globalgcv.foreground = draw_colors->hilight;
    Globalgcm = GCForeground;
    XChangeGC(dpy, Scr.ScratchGC1, Globalgcm, &Globalgcv);
    cd->relief_gc = Scr.ScratchGC1;
    Globalgcv.foreground = draw_colors->shadow;
    XChangeGC(dpy, Scr.ScratchGC2, Globalgcm, &Globalgcv);
    cd->shadow_gc = Scr.ScratchGC2;
  }

  /* MWMBorder style means thin 3d effects */
  cd->relief_width = (HAS_MWM_BORDER(t) ? 1 : 2);

  if (cd->texture_pixmap)
  {
    cd->attributes.background_pixmap = cd->texture_pixmap;
    cd->valuemask = CWBackPixmap;
    if (Pdepth < 2)
    {
      cd->notex_attributes.background_pixmap = cd->back_pixmap;
      cd->notex_valuemask = CWBackPixmap;
    }
    else
    {
      cd->notex_attributes.background_pixel = cd->back_color;
      cd->notex_valuemask = CWBackPixel;
    }
  }
  else
  {
    if (Pdepth < 2)
    {
      cd->attributes.background_pixmap = cd->back_pixmap;
      cd->valuemask = CWBackPixmap;
      cd->notex_attributes.background_pixmap = cd->back_pixmap;
      cd->notex_valuemask = CWBackPixmap;
    }
    else
    {
      cd->attributes.background_pixel = cd->back_color;
      cd->valuemask = CWBackPixel;
      cd->notex_attributes.background_pixel = cd->back_color;
      cd->notex_valuemask = CWBackPixel;
    }
  }
}

void draw_clipped_decorations(
  FvwmWindow *t, draw_window_parts draw_parts, Bool has_focus, int force,
  Window expose_win, XRectangle *rclip)
{
  common_decorations_type cd;
  Bool is_frame_redraw_allowed = False;
  Bool is_button_redraw_allowed = False;
  Bool is_title_redraw_allowed = False;
  Bool do_redraw_title = False;
  Bool do_redraw_buttons = False;
  Bool do_change_gcs = False;

  if (!t)
    return;
  memset(&cd, 0, sizeof(cd));
  if (force || expose_win == None)
  {
    is_frame_redraw_allowed = True;
    is_button_redraw_allowed = True;
    is_title_redraw_allowed = True;
  }
  else if (expose_win == t->title_w)
    is_title_redraw_allowed = True;
  else if (expose_win == t->frame || expose_win == t->decor_w)
    /* sides and corners are InputOnly and incapable of triggering Expose events
     */
  {
    is_frame_redraw_allowed = True;
  }
  else if (expose_win != None)
    is_button_redraw_allowed = True;

  if (has_focus)
  {
    /* don't re-draw just for kicks */
    if(!force && Scr.Hilite == t)
    {
      is_frame_redraw_allowed = False;
      is_button_redraw_allowed = False;
      is_title_redraw_allowed = False;
    }
    else
    {
      if (Scr.Hilite != t || force > 1)
	cd.flags.has_color_changed = True;
      if (Scr.Hilite != t && Scr.Hilite != NULL)
      {
	/* make sure that the previously highlighted window got
	 * unhighlighted */
	DrawDecorations(Scr.Hilite, DRAW_ALL, False, True, None);
      }
      Scr.Hilite = t;
    }
  }
  else
  {
    /* don't re-draw just for kicks */
    if (!force && Scr.Hilite != t)
    {
      is_frame_redraw_allowed = False;
      is_button_redraw_allowed = False;
      is_title_redraw_allowed = False;
    }
    else if (Scr.Hilite == t || force > 1)
    {
      cd.flags.has_color_changed = True;
      Scr.Hilite = NULL;
    }
  }
  if(IS_ICONIFIED(t))
  {
    DrawIconWindow(t);
    return;
  }

  /* calculate some values and flags */
  if ((draw_parts & DRAW_TITLE) && HAS_TITLE(t) && is_title_redraw_allowed)
  {
    do_redraw_title = True;
    do_change_gcs = True;
  }
  if ((draw_parts & DRAW_BUTTONS) && HAS_TITLE(t) && is_button_redraw_allowed)
  {
    do_redraw_buttons = True;
    do_change_gcs = True;
  }
  get_common_decorations(
    &cd, t, draw_parts, has_focus, force, expose_win, False, do_change_gcs);

  /* redraw */
  if (cd.flags.has_color_changed && HAS_TITLE(t))
  {
    if (!do_redraw_title || !rclip)
    {
      if (t->title_background_pixmap == None || force)
      {
	change_window_background(
	  t->title_w, cd.notex_valuemask, &cd.notex_attributes);
	t->title_background_pixmap = None;
      }
    }
  }
  if (do_redraw_buttons)
  {
    RedrawButtons(&cd, t, has_focus, force, expose_win, rclip);
  }
  if (do_redraw_title)
  {
    RedrawTitle(&cd, t, has_focus, rclip);
  }
  if (cd.flags.has_color_changed ||
      ((draw_parts & DRAW_FRAME) && (is_frame_redraw_allowed)))
  {
    get_common_decorations(
      &cd, t, draw_parts, has_focus, force, expose_win, True, True);
    if (!rclip || !IS_WINDOW_BORDER_DRAWN(t))
    {
      change_window_background(t->decor_w, cd.valuemask, &cd.attributes);
      SET_WINDOW_BORDER_DRAWN(t, 1);
      rclip = NULL;
    }
    RedrawBorder(&cd, t, has_focus, force, expose_win, rclip);
  }

  /* Sync to make the change look fast! */
  XSync(dpy,0);
}

void DrawDecorations(
  FvwmWindow *t, draw_window_parts draw_parts, Bool has_focus, int force,
  Window expose_win)
{
  draw_clipped_decorations(t, draw_parts, has_focus, force, expose_win, NULL);

  return;
}


/***********************************************************************
 *
 *  Procedure:
 *      SetupFrame - set window sizes
 *
 *  Inputs:
 *      tmp_win - the FvwmWindow pointer
 *      x       - the x coordinate of the upper-left outer corner of the frame
 *      y       - the y coordinate of the upper-left outer corner of the frame
 *      w       - the width of the frame window w/o border
 *      h       - the height of the frame window w/o border
 *
 *  Special Considerations:
 *      This routine will check to make sure the window is not completely
 *      off the display, if it is, it'll bring some of it back on.
 *
 *      The tmp_win->frame_XXX variables should NOT be updated with the
 *      values of x,y,w,h prior to calling this routine, since the new
 *      values are compared against the old to see whether a synthetic
 *      ConfigureNotify event should be sent.  (It should be sent if the
 *      window was moved but not resized.)
 *
 ************************************************************************/

void SetupFrame(
  FvwmWindow *tmp_win, int x, int y, int w, int h, Bool sendEvent)
{
  XWindowChanges xwc;
  unsigned long xwcm;
  int i;
  Bool is_resized = False;
  Bool is_moved = False;
  int xwidth;
  int ywidth;
  int left;
  int right;
  int shaded = IS_SHADED(tmp_win);

  int xmove_sign;
  int ymove_sign;
  int xsize_sign;
  int ysize_sign;
  int i0 = 0;
  int i1 = 0;
  int id = 0;
  int dx;
  int dy;
  int px;
  int py;
  int decor_gravity;
  Bool is_order_reversed = False;

#ifdef FVWM_DEBUG_MSGS
  fvwm_msg(DBG,"SetupFrame",
           "Routine Entered (x == %d, y == %d, w == %d, h == %d)",
           x, y, w, h);
#endif

  if (w < 1)
  {
    w = 1;
  }
  if (h < 1)
  {
    h = 1;
  }
  if (w != tmp_win->frame_g.width || h != tmp_win->frame_g.height)
    is_resized = True;
  if (x != tmp_win->frame_g.x || y != tmp_win->frame_g.y)
    is_moved = True;

  /*
   * According to the July 27, 1988 ICCCM draft, we should send a
   * "synthetic" ConfigureNotify event to the client if the window
   * was moved but not resized.
   */
  if (is_moved && !is_resized)
    sendEvent = True;

  /* optimization code for opaque resize */
#define SIGN(x) (((x) < 0) ? -1 : !!(x))
  xmove_sign = SIGN(x - tmp_win->frame_g.x);
  ymove_sign = SIGN(y - tmp_win->frame_g.y);
  xsize_sign = SIGN(w - tmp_win->frame_g.width);
  ysize_sign = SIGN(h - tmp_win->frame_g.height);
#undef SIGN

  if (HAS_BOTTOM_TITLE(tmp_win))
  {
    decor_gravity = SouthEastGravity;
  }
  else
  {
    decor_gravity = NorthWestGravity;
  }

  if (xsize_sign != 0 || ysize_sign != 0)
  {
    if (ysize_sign == 0 || xsize_sign > 0)
      is_order_reversed = (xsize_sign >= 0);
    else
      is_order_reversed = (ysize_sign >= 0);
  }
  else
  {
    is_order_reversed = False;
  }

  /*
   * Set up the title geometry first
   */

  if (is_resized)
  {
    left = tmp_win->nr_left_buttons;
    right = tmp_win->nr_right_buttons;
    tmp_win->title_g.width = w - (left + right) * tmp_win->title_g.height
                           - 2 * tmp_win->boundary_width;
    if(tmp_win->title_g.width < 1)
      tmp_win->title_g.width = 1;
  }

  /*
   * Now set up the client, the parent and the frame window
   */

  set_decor_gravity(
    tmp_win, decor_gravity, NorthWestGravity, NorthWestGravity);
  tmp_win->attr.width = w - 2 * tmp_win->boundary_width;
  tmp_win->attr.height =
    h - tmp_win->title_g.height - 2*tmp_win->boundary_width;
  if (tmp_win->attr.height <= 1 || tmp_win->attr.width <= 1)
    is_order_reversed = 0;
  px = tmp_win->boundary_width;
  py = tmp_win->boundary_width + tmp_win->title_top_height;
  dx = 0;
  dy = 0;
  if (is_order_reversed && decor_gravity == SouthEastGravity)
  {
    dx = tmp_win->frame_g.width - w;
    dy = tmp_win->frame_g.height - h;
  }

  if (is_order_reversed)
  {
    i0 = 3;
    i1 = -1;
    id = -1;
  }
  else
  {
    i0 = 0;
    i1 = 4;
    id = 1;
  }
  for (i = i0; i != i1; i += id)
  {
    switch(i)
    {
    case 0:
      XMoveResizeWindow(dpy, tmp_win->frame, x, y, w, h);
      break;
    case 1:
      if (!shaded)
      {
	XMoveResizeWindow(
	dpy, tmp_win->Parent, px, py, max(tmp_win->attr.width, 1),
	max(tmp_win->attr.height, 1));
      }
      else
      {
        XMoveWindow(dpy, tmp_win->Parent, px, py);
      }
      break;
    case 2:
      if (!shaded)
      {
	XResizeWindow(
	  dpy, tmp_win->w, max(tmp_win->attr.width, 1),
	  max(tmp_win->attr.height, 1));
	/* This reduces flickering */
	XSync(dpy, 0);
      }
      else if (HAS_BOTTOM_TITLE(tmp_win))
      {
	rectangle big_g;

	big_g = (IS_MAXIMIZED(tmp_win)) ? tmp_win->max_g : tmp_win->normal_g;
	get_relative_geometry(&big_g, &big_g);
	XMoveWindow(dpy, tmp_win->w, 0, 1 - big_g.height +
		    2 * tmp_win->boundary_width + tmp_win->title_g.height);
      }
      else
      {
	XMoveWindow(dpy, tmp_win->w, 0, 0);
      }

      break;
    case 3:
      XMoveResizeWindow(dpy, tmp_win->decor_w, dx, dy, w, h);
      break;
    }
  }
  set_decor_gravity(
    tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
  XMoveWindow(dpy, tmp_win->decor_w, 0, 0);
  tmp_win->frame_g.x = x;
  tmp_win->frame_g.y = y;
  tmp_win->frame_g.width = w;
  tmp_win->frame_g.height = h;

  /*
   * Set up the decoration windows
   */

  if (is_resized)
  {
    if (HAS_TITLE(tmp_win))
    {
      SetupTitleBar(tmp_win, w, h);
    }

    if(HAS_BORDER(tmp_win))
    {
      int add;

      tmp_win->visual_corner_width = tmp_win->corner_width;
      if(w < 2*tmp_win->corner_width)
        tmp_win->visual_corner_width = w / 3;
      if (h < 2*tmp_win->corner_width && !shaded)
        tmp_win->visual_corner_width = h / 3;
      xwidth = w - 2 * tmp_win->visual_corner_width;
      ywidth = h - 2 * tmp_win->visual_corner_width;
      xwcm = CWWidth | CWHeight | CWX | CWY;
      if(xwidth<2)
        xwidth = 2;
      if(ywidth<2)
        ywidth = 2;

      if (IS_SHADED(tmp_win))
	add = tmp_win->visual_corner_width / 3;
      else
	add = 0;
      for(i = 0; i < 4; i++)
      {
        if(i==0)
        {
          xwc.x = tmp_win->visual_corner_width;
          xwc.y = 0;
          xwc.height = tmp_win->boundary_width;
          xwc.width = xwidth;
        }
        else if (i==1)
        {
	  xwc.x = w - tmp_win->boundary_width;
          xwc.y = tmp_win->visual_corner_width;
	  if (IS_SHADED(tmp_win))
	  {
	    xwc.y /= 3;
	    xwc.height = add + 2;
	  }
	  else
	  {
	    xwc.height = ywidth;
	  }
          xwc.width = tmp_win->boundary_width;

        }
        else if(i==2)
        {
          xwc.x = tmp_win->visual_corner_width;
          xwc.y = h - tmp_win->boundary_width;
          xwc.height = tmp_win->boundary_width;
          xwc.width = xwidth;
        }
        else
        {
	  xwc.x = 0;
          xwc.y = tmp_win->visual_corner_width;
	  if (IS_SHADED(tmp_win))
	  {
	    xwc.y /= 3;
	    xwc.height = add + 2;
	  }
	  else
	  {
	    xwc.height = ywidth;
	  }
          xwc.width = tmp_win->boundary_width;
        }
	XConfigureWindow(dpy, tmp_win->sides[i], xwcm, &xwc);
      }

      xwcm = CWX|CWY|CWWidth|CWHeight;
      xwc.width = tmp_win->visual_corner_width;
      xwc.height = tmp_win->visual_corner_width;
      for(i = 0; i < 4; i++)
      {
        if (i & 0x1)
          xwc.x = w - tmp_win->visual_corner_width;
        else
          xwc.x = 0;

        if (i & 0x2)
          xwc.y = h - tmp_win->visual_corner_width + add;
        else
          xwc.y = -add;

	XConfigureWindow(dpy, tmp_win->corners[i], xwcm, &xwc);
      }
    }
  }


  /*
   * Set up window shape
   */

  if (FShapesSupported && tmp_win->wShaped && is_resized)
  {
    SetShape(tmp_win, w);
  }

  /*
   * Send ConfigureNotify
   */

  XSync(dpy,0);
  /* must not send events to shaded windows because this might cause them to
   * look at their current geometry */
  if (sendEvent && !shaded)
  {
    SendConfigureNotify(tmp_win, x, y, w, h, 0, True);
#ifdef FVWM_DEBUG_MSGS
    fvwm_msg(DBG,"SetupFrame","Sent ConfigureNotify (w == %d, h == %d)",
             w,h);
#endif
  }

  XSync(dpy,0);
  BroadcastConfig(M_CONFIGURE_WINDOW,tmp_win);
#if 0
fprintf(stderr,"sf: window '%s'\n", tmp_win->name);
fprintf(stderr,"    frame:  %5d %5d, %4d x %4d\n", tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width, tmp_win->frame_g.height);
fprintf(stderr,"    normal: %5d %5d, %4d x %4d\n", tmp_win->normal_g.x, tmp_win->normal_g.y, tmp_win->normal_g.width, tmp_win->normal_g.height);
if (IS_MAXIMIZED(tmp_win))
fprintf(stderr,"    max:    %5d %5d, %4d x %4d, %5d %5d\n", tmp_win->max_g.x, tmp_win->max_g.y, tmp_win->max_g.width, tmp_win->max_g.height, tmp_win->max_offset.x, tmp_win->max_offset.y);
#endif
}

void ForceSetupFrame(
  FvwmWindow *tmp_win, int x, int y, int w, int h, Bool sendEvent)
{
  tmp_win->frame_g.x = x + 1;
  tmp_win->frame_g.y = y + 1;
  tmp_win->frame_g.width = 0;
  tmp_win->frame_g.height = 0;
  SetupFrame(tmp_win, x, y, w, h, sendEvent);
}

void set_decor_gravity(
  FvwmWindow *tmp_win, int gravity, int parent_gravity, int client_gravity)
{
  int valuemask = CWWinGravity;
  XSetWindowAttributes xcwa;
  int i;

  xcwa.win_gravity = client_gravity;
  XChangeWindowAttributes(dpy, tmp_win->w, valuemask, &xcwa);
  xcwa.win_gravity = parent_gravity;
  XChangeWindowAttributes(dpy, tmp_win->Parent, valuemask, &xcwa);
  xcwa.win_gravity = gravity;
  XChangeWindowAttributes(dpy, tmp_win->decor_w, valuemask, &xcwa);
  if (HAS_TITLE(tmp_win))
  {
    int left_gravity;
    int right_gravity;

    if (gravity == StaticGravity || gravity == ForgetGravity)
    {
      left_gravity = gravity;
      right_gravity = gravity;
    }
    else
    {
      /* West...Gravity */
      left_gravity = gravity - ((gravity - 1) % 3);
      /* East...Gravity */
      right_gravity = left_gravity + 2;
    }
    xcwa.win_gravity = left_gravity;
    XChangeWindowAttributes(dpy, tmp_win->title_w, valuemask, &xcwa);
    for (i = 0; i < NUMBER_OF_BUTTONS; i++)
    {
      if (i == NR_LEFT_BUTTONS)
	xcwa.win_gravity = right_gravity;
      if (tmp_win->button_w[i])
	XChangeWindowAttributes(dpy, tmp_win->button_w[i], valuemask, &xcwa);
    }
  }
}


/****************************************************************************
 *
 * Sets up the shaped window borders
 *
 ****************************************************************************/
void SetShape(FvwmWindow *tmp_win, int w)
{
  if (FShapesSupported)
  {
    if (tmp_win->wShaped)
    {
      XRectangle rect;

      FShapeCombineShape(
	dpy, tmp_win->frame, FShapeBounding, tmp_win->boundary_width,
	tmp_win->title_top_height + tmp_win->boundary_width, tmp_win->w,
	FShapeBounding, FShapeSet);
      if (tmp_win->title_w)
      {
	/* windows w/ titles */
	rect.x = tmp_win->boundary_width;
	rect.y = tmp_win->title_g.y;
	rect.width = w - 2*tmp_win->boundary_width;
	rect.height = tmp_win->title_g.height;

	FShapeCombineRectangles(
	  dpy, tmp_win->frame, FShapeBounding, 0, 0, &rect, 1, FShapeUnion,
	  Unsorted);
      }
    }
    else
    {
      /* unset shape */
      FShapeCombineMask(
	dpy, tmp_win->frame, FShapeBounding, 0, 0, None, FShapeSet);
    }
  }
}

/****************************************************************************
 *
 *  Sets the allowed button states
 *
 ****************************************************************************/
void CMD_ButtonState(F_CMD_ARGS)
{
  char *token;

  while ((token = PeekToken(action, &action)))
  {
    static char first = True;
    if (!token && first)
    {
      Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
      Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
      return;
    }
    first = False;
    if (StrEquals("activedown", token))
    {
      Scr.gs.use_active_down_buttons =
	ParseToggleArgument(action, &action, DEFAULT_USE_ACTIVE_DOWN_BUTTONS,
			    True);
    }
    else if (StrEquals("inactive", token))
    {
      Scr.gs.use_inactive_buttons =
	ParseToggleArgument(action, &action, DEFAULT_USE_INACTIVE_BUTTONS,
			    True);
    }
    else
    {
      Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
      Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
      fvwm_msg(ERR, "cmd_button_state", "unknown button state %s\n", token);
      return;
    }
  }
  return;
}


/****************************************************************************
 *
 *  Sets the border style (veliaa@rpi.edu)
 *
 ****************************************************************************/
void CMD_BorderStyle(F_CMD_ARGS)
{
  char *parm;
  char *prev;
#ifdef USEDECOR
  FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
#else
  FvwmDecor *decor = &Scr.DefaultDecor;
#endif

  Scr.flags.do_need_window_update = 1;
  decor->flags.has_changed = 1;

  for (prev = action; (parm = PeekToken(action, &action)); prev = action)
  {
    if (StrEquals(parm, "active") || StrEquals(parm, "inactive"))
    {
      int len;
      char *end, *tmp;
      DecorFace tmpdf, *df;
      memset(&tmpdf.style, 0, sizeof(tmpdf.style));
      DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
#ifdef MULTISTYLE
      tmpdf.next = NULL;
#endif
#ifdef MINI_ICONS
      tmpdf.u.p = NULL;
#endif
      if (StrEquals(parm,"active"))
	df = &decor->BorderStyle.active;
      else
	df = &decor->BorderStyle.inactive;
      df->flags.has_changed = 1;
      while (isspace(*action))
	++action;
      if (*action != '(')
      {
	if (!*action)
	{
	  fvwm_msg(ERR,"SetBorderStyle",
		   "error in %s border specification", parm);
	  return;
	}
	while (isspace(*action))
	  ++action;
	if (ReadDecorFace(action, &tmpdf,-1,True))
	{
	  FreeDecorFace(dpy, df);
	  *df = tmpdf;
	}
	break;
      }
      end = strchr(++action, ')');
      if (!end)
      {
	fvwm_msg(ERR,"SetBorderStyle",
		 "error in %s border specification", parm);
	return;
      }
      len = end - action + 1;
      tmp = safemalloc(len);
      strncpy(tmp, action, len - 1);
      tmp[len - 1] = 0;
      ReadDecorFace(tmp, df,-1,True);
      free(tmp);
      action = end + 1;
    }
    else if (strcmp(parm,"--")==0)
    {
      if (ReadDecorFace(prev, &decor->BorderStyle.active,-1,True))
	ReadDecorFace(prev, &decor->BorderStyle.inactive,-1,False);
      decor->BorderStyle.active.flags.has_changed = 1;
      decor->BorderStyle.inactive.flags.has_changed = 1;
      break;
    }
    else
    {
      DecorFace tmpdf;
      memset(&tmpdf.style, 0, sizeof(tmpdf.style));
      DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
#ifdef MULTISTYLE
      tmpdf.next = NULL;
#endif
#ifdef MINI_ICONS
      tmpdf.u.p = NULL;
#endif
      if (ReadDecorFace(prev, &tmpdf,-1,True))
      {
	FreeDecorFace(dpy,&decor->BorderStyle.active);
	decor->BorderStyle.active = tmpdf;
	ReadDecorFace(prev, &decor->BorderStyle.inactive,-1,False);
	decor->BorderStyle.active.flags.has_changed = 1;
	decor->BorderStyle.inactive.flags.has_changed = 1;
      }
      break;
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1