/* 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
 */

/****************************************************************************
 * Changed 10/06/97 by dje:
 * Change single IconBox into chain of IconBoxes.
 * Allow IconBox to be specified using X Geometry string.
 * Parse optional IconGrid.
 * Parse optional IconFill.
 * Use macros to make parsing more uniform, and easier to read.
 * Rewrote AddToList without tons of arg passing and merging.
 * Added a few comments.
 *
 * This module was 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
 ****************************************************************************/

/***********************************************************************
 *
 * code for parsing the fvwm style command
 *
 ***********************************************************************/
#include "config.h"
#include <stdio.h>

#include "libs/fvwmlib.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "defaults.h"
#include "update.h"
#include "style.h"
#include "builtins.h"
#include "libs/Colorset.h"
#include "borders.h"
#include "gnome.h"
#include "icons.h"
#include "focus.h"
#include "geometry.h"
#include "move_resize.h"
#include "add_window.h"
#include "module_interface.h"
#include "focus.h"

static void init_style(
  FvwmWindow *old_t, FvwmWindow *t, window_style *pstyle, short *pbuttons)
{
  /* copy the window structure because we still need some old values. */
  memcpy(old_t, t, sizeof(FvwmWindow));
  /* determine level of decoration */
  setup_style_and_decor(t, pstyle, pbuttons);
}

static void apply_window_updates(
  FvwmWindow *t, update_win *flags, window_style *pstyle, FvwmWindow *focus_w)
{
  FvwmWindow old_t;
  short buttons;
  Bool is_style_initialised = False;

  t->title_top_height = (HAS_BOTTOM_TITLE(t)) ? 0 : t->title_g.height;
  if (flags->do_update_window_grabs)
  {
    focus_grab_buttons(t, False);
  }

  if (IS_TRANSIENT(t) && flags->do_redecorate_transient)
  {
    flags->do_redecorate = True;
    flags->do_update_window_font = True;
  }

  /*
   * is_sticky
   * is_icon_sticky
   *
   * These are a bit complicated because they can move windows to a different
   * page or desk. */
  if (flags->do_update_stick_icon && IS_ICONIFIED(t) && !IS_STICKY(t))
  {
    if (IS_ICON_STICKY(pstyle))
    {
      /* stick and unstick the window to force the icon on the current page */
      handle_stick(&Event, t->frame, t, C_FRAME, "", 0, 1);
      handle_stick(&Event, t->frame, t, C_FRAME, "", 0, 0);
    }
    else
    {
      flags->do_update_icon_title = True;
    }
  }
  else if (flags->do_update_stick)
  {
    handle_stick(&Event, t->frame, t, C_FRAME, "", 0, SFIS_STICKY(*pstyle));
  }
#ifdef MINI_ICONS
  if (flags->do_update_mini_icon)
  {
    change_mini_icon(t, pstyle);
  }
#endif

  if (flags->do_update_window_font || flags->do_update_window_font_height)
  {
    if (!is_style_initialised)
    {
      init_style(&old_t, t, pstyle, &buttons);
      is_style_initialised = True;
    }
    setup_window_font(t, pstyle, flags->do_update_window_font);
    flags->do_redecorate = True;
  }
  if (flags->do_redecorate ||
      (IS_TRANSIENT(t) && flags->do_redecorate_transient))
  {
    rectangle naked_g;
    rectangle *new_g;

    if (!is_style_initialised)
    {
      init_style(&old_t, t, pstyle, &buttons);
      is_style_initialised = True;
    }

    /* redecorate */
    change_auxiliary_windows(t, buttons);

    /* calculate the new offsets */
    gravity_get_naked_geometry(
      old_t.hints.win_gravity, &old_t, &naked_g, &t->normal_g);
    gravity_translate_to_northwest_geometry_no_bw(
      old_t.hints.win_gravity, &old_t, &naked_g, &naked_g);
    gravity_add_decoration(
      old_t.hints.win_gravity, t, &t->normal_g, &naked_g);
    if (IS_MAXIMIZED(t))
    {
      int off_x = old_t.normal_g.x - old_t.max_g.x;
      int off_y = old_t.normal_g.y - old_t.max_g.y;
      int new_off_x;
      int new_off_y;

      /* maximized windows are always considered to have NorthWestGravity */
      gravity_get_naked_geometry(
	NorthWestGravity, &old_t, &naked_g, &t->max_g);
      gravity_translate_to_northwest_geometry_no_bw(
	NorthWestGravity, &old_t, &naked_g, &naked_g);
      gravity_add_decoration(
	NorthWestGravity, t, &t->max_g, &naked_g);
      new_g = &t->max_g;
      /* prevent random paging when unmaximizing after e.g. the border width has
       * changed */
      new_off_x = t->normal_g.x - t->max_g.x;
      new_off_y = t->normal_g.y - t->max_g.y;
      t->max_offset.x += new_off_x - off_x;
      t->max_offset.y += new_off_y - off_y;
    }
    else
    {
      new_g = &t->normal_g;
    }
    get_relative_geometry(&t->frame_g, new_g);

    flags->do_setup_frame = True;
    flags->do_redraw_decoration = True;
  }
  if (flags->do_resize_window)
  {
    rectangle old_g;

    setup_frame_size_limits(t, pstyle);
    old_g = t->frame_g;
    t->frame_g = t->normal_g;
    gravity_constrain_size(t->hints.win_gravity, t, &(t->frame_g), 0);
    t->normal_g = t->frame_g;
    if (IS_MAXIMIZED(t))
    {
      t->frame_g = t->max_g;
      gravity_constrain_size(
	t->hints.win_gravity, t, &(t->frame_g), CS_UPDATE_MAX_DEFECT);
      t->max_g = t->frame_g;
    }
    t->frame_g = old_g;
    gravity_constrain_size(t->hints.win_gravity, t, &(t->frame_g), 0);

    flags->do_setup_frame = True;
    flags->do_redraw_decoration = True;
  }
  if (flags->do_setup_frame)
  {
    if (IS_SHADED(t))
    {
      get_unshaded_geometry(t, &t->frame_g);
      get_shaded_geometry(t, &t->frame_g, &t->frame_g);
    }
    ForceSetupFrame(t, t->frame_g.x, t->frame_g.y, t->frame_g.width,
		    t->frame_g.height, True);
    GNOME_SetWinArea(t);
  }
  if (flags->do_update_window_color)
  {
    if (focus_w != t)
      flags->do_redraw_decoration = True;
    update_window_color_style(t, pstyle);
  }
  if (flags->do_update_window_color_hi)
  {
    if (focus_w == t)
      flags->do_redraw_decoration = True;
    update_window_color_hi_style(t, pstyle);
    flags->do_broadcast_focus = True;
  }
  if (flags->do_redraw_decoration)
  {
    FvwmWindow *u = Scr.Hilite;

    if (IS_SHADED(t))
      XRaiseWindow(dpy, t->decor_w);
    /* domivogt (6-Jun-2000): Don't check if the window is visible here.  If we
     * do, some updates are not applied and when the window becomes visible
     * again, the X Server may not redraw the window. */
    DrawDecorations(t, DRAW_ALL, (Scr.Hilite == t), 2, None);
    Scr.Hilite = u;
  }
  if (flags->do_update_icon_font)
  {
    if (!is_style_initialised)
    {
      init_style(&old_t, t, pstyle, &buttons);
      is_style_initialised = True;
    }
    setup_icon_font(t, pstyle, flags->do_update_icon_font);
    flags->do_update_icon_title = True;
  }
  if (flags->do_update_icon_boxes)
  {
    change_icon_boxes(t, pstyle);
  }
  if (flags->do_update_icon)
  {
    change_icon(t, pstyle);
    flags->do_update_icon_placement = True;
    flags->do_update_icon_title = False;
  }
  if (flags->do_update_icon_title)
  {
    RedoIconName(t);
    if (IS_ICONIFIED(t))
    {
      DrawIconWindow(t);
    }
  }
  if (flags->do_update_icon_placement)
  {
    if (IS_ICONIFIED(t))
    {
      SET_ICONIFIED(t, 0);
      Iconify(t, 0, 0);
    }
  }
  if (flags->do_setup_focus_policy)
  {
    setup_focus_policy(t);
  }
  if (flags->do_update_frame_attributes)
  {
    setup_frame_attributes(t, pstyle);
  }
  if (flags->do_update_modules_flags)
  {
    BroadcastConfig(M_CONFIGURE_WINDOW,t);
  }
  if (flags->do_broadcast_focus)
  {
    if (Scr.Hilite != NULL && t == Scr.Hilite)
    {
      BroadcastPacket(
	M_FOCUS_CHANGE, 5, Scr.Hilite->w, Scr.Hilite->frame, 0,
	Scr.Hilite->hicolors.fore, Scr.Hilite->hicolors.back);
    }
  }
  t->shade_anim_steps = pstyle->shade_anim_steps;

  return;
}

/* similar, but takes only care of destroying windows that have to go away. */
void destroy_scheduled_windows(void)
{
  FvwmWindow *t;
  FvwmWindow *next;
  Bool do_need_ungrab = False;

  if (Scr.flags.is_executing_complex_function != 0 ||
      Scr.flags.is_window_scheduled_for_destroy == 0)
    return;
  /* Grab the server during the style update! */
  if (GrabEm(CRS_WAIT, GRAB_BUSY))
    do_need_ungrab = True;
  MyXGrabServer(dpy);
  Scr.flags.is_window_scheduled_for_destroy = 0;
  /* need to destroy one or more windows before looking at the window list */
  for (t = Scr.FvwmRoot.next, next = NULL; t != NULL; t = next)
  {
    next = t->next;
    if (IS_SCHEDULED_FOR_DESTROY(t))
    {
      SET_SCHEDULED_FOR_DESTROY(t, 0);
      destroy_window(t);
    }
  }
  MyXUngrabServer(dpy);
  if (do_need_ungrab)
    UngrabEm(GRAB_BUSY);

  return;
}

/* similar to the flush_window_updates() function, but does only the updates
 * for a single window whose decor has been changed. */
void apply_decor_change(FvwmWindow *tmp_win)
{
  window_style style;
  update_win flags;

  lookup_style(tmp_win, &style);
  memset(&flags, 0, sizeof(flags));
  flags.do_redecorate = True;
  flags.do_update_window_font_height = True;
  apply_window_updates(tmp_win, &flags, &style, get_focus_window());
  Scr.flags.do_need_window_update = 1;
}

/* Check and apply new style to each window if the style has changed. */
void flush_window_updates(void)
{
  FvwmWindow *t;
  window_style style;
  FvwmWindow *focus_fw;
  Bool do_need_ungrab = False;
  update_win flags;

  /* Grab the server during the style update! */
  if (GrabEm(CRS_WAIT, GRAB_BUSY))
    do_need_ungrab = True;
  MyXGrabServer(dpy);

  /* This is necessary in case the focus policy changes. With ClickToFocus some
   * buttons have to be grabbed/ungrabbed. */
  focus_fw = get_focus_window();
  DeleteFocus(1);

  /* Apply the new default font and colours first */
  if (Scr.flags.has_default_color_changed || Scr.flags.has_default_font_changed)
    ApplyDefaultFontAndColors();

  /* update styles for all windows */
  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
  {
    memset(&flags, 0, sizeof(update_win));
    check_window_style_change(t, &flags, &style);
    if (Scr.flags.has_xinerama_state_changed)
    {
      flags.do_update_icon_boxes = True;
      flags.do_update_icon_placement = True;
    }
    if (Scr.flags.has_nr_buttons_changed)
      flags.do_redecorate = True;
    /* TODO: this is not optimised for minimal redrawing yet*/
    if (t->decor->flags.has_changed)
    {
      flags.do_redecorate = True;
      flags.do_update_window_font_height = True;
    }
    if (Scr.flags.has_default_font_changed && !HAS_ICON_FONT(t))
    {
      flags.do_update_icon_font = True;
    }
    if (Scr.flags.has_default_font_changed && !HAS_WINDOW_FONT(t))
    {
      flags.do_update_window_font = True;
    }
    if (t->decor->flags.has_title_height_changed)
    {
      flags.do_update_window_font_height = True;
    }
    if (Scr.flags.has_mouse_binding_changed)
    {
      flags.do_update_window_grabs = True;
    }
    /* now apply the changes */
    apply_window_updates(t, &flags, &style, focus_fw);
  }

  /* restore the focus; also handles the case that the previously focused
   * window is now NeverFocus */
  if (focus_fw)
  {
    SetFocusWindow(focus_fw, 1);
    if (Scr.flags.has_mouse_binding_changed)
    {
      focus_grab_buttons(focus_fw, True);
    }
  }
  else
    DeleteFocus(1);

  /* finally clean up the change flags */
  reset_style_changes();
  reset_decor_changes();
  Scr.flags.do_need_window_update = 0;
  Scr.flags.has_default_font_changed = 0;
  Scr.flags.has_default_color_changed = 0;
  Scr.flags.has_mouse_binding_changed = 0;
  Scr.flags.has_nr_buttons_changed = 0;
  Scr.flags.has_xinerama_state_changed = 0;

  MyXUngrabServer(dpy);
  if (do_need_ungrab)
    UngrabEm(GRAB_BUSY);

  return;
}

void CMD_UpdateStyles(F_CMD_ARGS)
{
  if (Scr.flags.do_need_window_update)
  {
    flush_window_updates();
  }

  return;
}


syntax highlighted by Code2HTML, v. 0.9.1