

/******************************************************************************
* MODULE     : canvas_widget.cpp
* DESCRIPTION: A canvas widget with horizontal and vertical scrollbars.
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#include "Widget/basic_widget.hpp"
#include "Event/attribute_event.hpp"
#include "Widget/scroll_widget.hpp"
#include "ps_device.hpp"
#include "Widget/layout.hpp"
SI get_dx (gravity grav, int w);
SI get_dy (gravity grav, int h);

/******************************************************************************
* Canvas widgets
******************************************************************************/

class canvas_widget_rep: public basic_widget_rep {
  SI ex1, ey1, ex2, ey2;
  SI last_w, last_h;
  bool show_scroll_bars;
  widget hor; bool hor_active;
  widget ver; bool ver_active;

public:
  canvas_widget_rep (widget child, gravity grav);
  operator tree ();
  void set_extents (SI Ex1, SI Ey1, SI Ex2, SI Ey2);

  void handle_get_size (get_size_event ev);
  void handle_set_widget (set_widget_event ev);
  void handle_position (position_event ev);
  void handle_repaint (repaint_event ev);
  void handle_set_integer (set_integer_event ev);
  void handle_set_coord4 (set_coord4_event ev);
  void handle_set_string (set_string_event ev);
  bool handle (event ev);
};

/******************************************************************************
* Creation of canvas widgets
******************************************************************************/

canvas_widget_rep::canvas_widget_rep (widget child, gravity grav):
  basic_widget_rep (child->dis, 1), show_scroll_bars (true)
{
  a[0] = new scrollable_widget_rep (child, grav);
  hor  = new hor_scrollbar_widget_rep (a[0]);
  ver  = new ver_scrollbar_widget_rep (a[0]);
  a[0] << set_hor_bar (NULL); hor_active= false;
  a[0] << set_ver_bar (NULL); ver_active= false;
  ex1= ey1= ex2= ey2= last_w= last_h= 0;
}

canvas_widget_rep::operator tree () {
  return tree (TUPLE, "canvas", (tree) a[0]);
}

void
canvas_widget_rep::set_extents (SI Ex1, SI Ey1, SI Ex2, SI Ey2) {
  abs_outer_round (Ex1, Ey1, Ex2, Ey2);
  SI ew= Ex2- Ex1, eh= Ey2- Ey1;
  bool old_hor_active= hor_active, old_ver_active= ver_active;
  if ((ew<=(w-2*PIXEL)) && (eh<=(h-2*PIXEL))) {
    hor_active= false; ver_active= false; }
  else { hor_active= ew > (w-20*PIXEL); ver_active= eh > (h-20*PIXEL); }

  gravity grav= a[0]->grav;
  SI ww= w- (show_scroll_bars? (ver_active? 20*PIXEL: 2*PIXEL): 0);
  SI wh= h- (show_scroll_bars? (hor_active? 20*PIXEL: 2*PIXEL): 0);
  if (Ex2- Ex1 < ww) {
    SI cxr= get_dx (grav, ww);
    SI cxc= Ex1+ get_dx (grav, Ex2- Ex1);
    Ex1= cxc- cxr;
    Ex2= cxc- cxr+ ww;
  }
  if (Ey2- Ey1 < wh) {
    SI cyr= get_dy (grav, wh);
    SI cyc= Ey2+ get_dy (grav, Ey2- Ey1);
    Ey1= cyc- cyr- wh;
    Ey2= cyc- cyr;
  }

  bool bars_changed=
    (old_hor_active != hor_active) || (old_ver_active != ver_active);
  bool visibility_changed=
    ((!show_scroll_bars) && (N(a)>1)) ||
    (show_scroll_bars && (N(a)==1) && (hor_active || ver_active));
  bool extents_changed=
    (Ex1 != ex1) || (Ey1 != ey1) || (Ex2 != ex2) || (Ey2 != ey2);
  ex1= Ex1; ey1= Ey1; ex2= Ex2; ey2= Ey2;

  if (bars_changed || visibility_changed) {
    if (hor_active) a[0] << set_hor_bar (hor);
    else a[0] << set_hor_bar (NULL);
    if (ver_active) a[0] << set_ver_bar (ver);
    else a[0] << set_ver_bar (NULL);
    a->resize(1);
    if (show_scroll_bars && hor_active) a << hor;
    if (show_scroll_bars && ver_active) a << ver;
    if (attached ()) {
      if (hor_active) hor << emit_attach_window (win);
      if (ver_active) ver << emit_attach_window (win);
      this << emit_reposition ();
    }
  }

  if (bars_changed || extents_changed) {
    a[0] << ::set_extents (ex1, ey1, ex2, ey2);
    if (attached ()) this << emit_invalidate_all ();
  }
}

/******************************************************************************
* Event handling
******************************************************************************/

void
canvas_widget_rep::handle_get_size (get_size_event ev) {
  if (ev->mode==-1) {
    ev->w= 32*PIXEL;
    ev->h= 32*PIXEL;
  }
  if (ev->mode== 1) dis->get_max_size (ev->w, ev->h);
}

void
canvas_widget_rep::handle_set_widget (set_widget_event ev) {
  if (ev->which == "scrollable") a[0]->a[0]= ev->w;
  else basic_widget_rep::handle_set_widget (ev);
}

void
canvas_widget_rep::handle_position (position_event ev) { (void) ev;
  if ((w != last_w) || (h != last_h)) {
    last_w= w; last_h= h;
    set_extents (ex1, ey1, ex2, ey2);
    if (attached ()) this << emit_invalidate_all ();
  }

  if (!show_scroll_bars)
    a[0] << emit_position (0, 0, w, h);
  else if (hor_active && ver_active) {
    a[0] << emit_position (PIXEL, -PIXEL, w-20*PIXEL, h-20*PIXEL);
    a[1] << emit_position (0, 16*PIXEL-h, w-18*PIXEL, 16*PIXEL);
    a[2] << emit_position (w-16*PIXEL, 0, 16*PIXEL, h-18*PIXEL);
  }
  else if (hor_active && (!ver_active)) {
    a[0] << emit_position (PIXEL, -PIXEL, w-2*PIXEL, h-20*PIXEL);
    a[1] << emit_position (0, 16*PIXEL-h, w, 16*PIXEL);
  }
  else if ((!hor_active) && ver_active) {
    a[0] << emit_position (PIXEL, -PIXEL, w-20*PIXEL, h-2*PIXEL);
    a[1] << emit_position (w-16*PIXEL, 0, 16*PIXEL, h);
  }
  else a[0] << emit_position (PIXEL, -PIXEL, w-2*PIXEL, h-2*PIXEL);
}

extern void indent ();

void
canvas_widget_rep::handle_repaint (repaint_event ev) { (void) ev;
  if (!show_scroll_bars);
  else if (hor_active && ver_active) {
    layout_default (win, w- 16*PIXEL, -h, w, -h+ 16*PIXEL);
    layout_default (win, 0, -h+ 16*PIXEL, w, -h+ 18*PIXEL);
    layout_default (win, w- 18*PIXEL, -h, w- 16*PIXEL, 0);
    layout_lower (win, 0, -h+ 18*PIXEL, w- 18*PIXEL, 0);
  }
  else if (hor_active && (!ver_active)) {
    layout_default (win, 0, -h+ 16*PIXEL, w, -h+ 18*PIXEL);
    layout_dark_outline (win, 0, -h+ 15*PIXEL, w, 0);
    win->set_line_style (PIXEL);
    win->set_color (layout_light (win));
    win->line (PIXEL, -h+ 18*PIXEL, w-2*PIXEL, -h+ 18*PIXEL);
  }
  else if ((!hor_active) && ver_active) {
    layout_default (win, w- 18*PIXEL, -h, w- 16*PIXEL, 0);
    layout_dark_outline (win, 0, -h, w- 15*PIXEL, 0);
    win->set_line_style (PIXEL);
    win->set_color (layout_light (win));
    win->line (w- 19*PIXEL, -h+PIXEL, w- 19*PIXEL, -2*PIXEL);
  }
  else layout_dark_outline (win, 0, -h, w, 0);
}

void
canvas_widget_rep::handle_set_integer (set_integer_event ev) {
  if (ev->which == "scrollbars") {
    if (((bool) ev->i) != show_scroll_bars) {
      show_scroll_bars= (bool) ev->i;
      set_extents (ex1, ey1, ex2, ey2);
      if (attached ()) this << emit_invalidate_all ();
    }
  }
  else a[0]->a[0] << ev;
}

void
canvas_widget_rep::handle_set_coord4 (set_coord4_event ev) {
  if (ev->which == "extents") set_extents (ev->c1, ev->c2, ev->c3, ev->c4);
  else a[0] << ev;
}

void
canvas_widget_rep::handle_set_string (set_string_event ev) {
  if (ev->which == "background") a[0] << ev;
  else a[0]->a[0] << ev;
}

bool
canvas_widget_rep::handle (event ev) {
  switch (ev->type) {
  case GET_SIZE_EVENT:
  case SET_WIDGET_EVENT:
  case ATTACH_WINDOW_EVENT:
  case POSITION_EVENT:
  case UPDATE_EVENT:
  case INVALIDATE_EVENT:
  case MOUSE_EVENT:
  case REPAINT_EVENT:
  case FIND_CHILD_EVENT:
    return basic_widget_rep::handle (ev);
  case SET_INTEGER_EVENT: {
    set_integer_event e (ev);
    handle_set_integer (e);
    return true;
  }
  case SET_COORD4_EVENT: {
    set_coord4_event e (ev);
    handle_set_coord4 (e);
    return true;
  }
  case SET_STRING_EVENT: {
    set_string_event e (ev);
    handle_set_string (e);
    return true;
  }
  case GET_COORD4_EVENT:
  case SET_COORD2_EVENT:
  case GET_COORD2_EVENT:
    a[0] << ev;
    return true;
  default:
    a[0]->a[0] << ev;
    return true;
  }
}

/******************************************************************************
* exported routines
******************************************************************************/

event
set_scrollable (widget w) {
  return set_widget ("scrollable", w);
}

widget
canvas_widget (widget w, gravity grav) {
  return new canvas_widget_rep (w, grav);
}
