
/******************************************************************************
* MODULE     : edit_mouse.cpp
* DESCRIPTION: Mouse handling
* 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 "edit_interface.hpp"
#include "tm_buffer.hpp"
#include "timer.hpp"
#include "link.hpp"

/******************************************************************************
* dispatching
******************************************************************************/

void
edit_interface_rep::mouse_any (string type, SI x, SI y, time_t t) {
  last_x= x; last_y= y;
  mark_undo_blocks ();
  update_active_loci ();

  if (type == "leave")
    set_pointer ("XC_top_left_arrow");
  if ((type != "move") && (type != "enter") && (type != "leave"))
    set_input_normal ();
  if ((popup_win != NULL) && (type != "leave")) {
    popup_win->map ();
    delete popup_win;
    popup_win= NULL;
    this << emit_mouse_grab (false);
  }

  if (inside_graphics (false)) {
    string type2= type;
    if (type == "enter") {
      dragging= start_drag= false;
      right_dragging= start_right_drag= false;
    }
    if (type == "press-left")
      start_drag= true;
    if (type == "press-right")
      start_right_drag= true;

    if (start_drag && type == "move") {
      type2= "start-drag";
      start_drag= false;
      dragging= true;
    }
    else if (dragging && (type == "move"))
      type2= "dragging"; 
    if (dragging && (type == "release-left"))
      type2= "end-drag";

    if (start_right_drag && type == "move") {
      type2= "start-right-drag";
      start_right_drag= false;
      right_dragging= true;
    }
    else if (right_dragging && (type == "move"))
      type2= "right-dragging"; 
    if (right_dragging && (type == "release-right"))
      type2= "end-right-drag";

    if (type == "release-left")
      dragging= start_drag= false;
    if (type == "release-right")
      right_dragging= start_right_drag= false;
    if (mouse_graphics (type2, x, y, t)) return;
    if (!over_graphics (x, y))
      eval ("(graphics-reset-context 'text-cursor)");
  }

  if (type == "press-left") mouse_click (x, y);
  if (dragging && (type == "move")) {
    if (attached () && dis->check_event (DRAG_EVENT)) return;
    mouse_drag (x, y);
  }
  if (type == "release-left" || type == "release-right") {
    dragging= right_dragging= false;
    this << emit_mouse_grab (false);
    if ((t >= last_click) && ((t - last_click) <= 250)) {
      last_click= t;
      if (mouse_extra_click (x, y))
	last_click= t- 1000;
    }
    else {
      last_click= t;
      mouse_select (x, y);
    }
  }
  if (type == "press-middle") mouse_paste (x, y);
  if (type == "press-right") mouse_adjust (x, y);
  if (type == "press-up") mouse_scroll (x, y, true);
  if (type == "press-down") mouse_scroll (x, y, false);

  if ((type == "press-left") ||
      (type == "release-left") ||
      (type == "press-middle") ||
      (type == "press-right"))
    notify_change (THE_DECORATIONS);
}

/******************************************************************************
* Routines for the mouse
******************************************************************************/

void
edit_interface_rep::mouse_click (SI x, SI y) {
  if (eb->action ("click", x, y, 0) != "") return;
  start_x   = x;
  start_y   = y;
  start_drag= dragging= true;
  start_right_drag= right_dragging= false;
  this << emit_mouse_grab (true);
}

bool
edit_interface_rep::mouse_extra_click (SI x, SI y) {
  go_to (x, y);
  if (eb->action ("double-click", x, y, 0) != "") return true;
  go_to (x, y);
  path p1, p2;
  get_selection (p1, p2);
  if ((p1==p2) || path_less (tp, p1) || path_less (p2, tp)) select (tp, tp);
  select_enlarge ();
  return false;
}

void
edit_interface_rep::mouse_drag (SI x, SI y) {
  if (inside_graphics ()) return;
  if (eb->action ("drag", x, y, 0) != "") return;
  end_x  = x;
  end_y  = y;
  selection_visible ();
  path p1= tree_path (start_x, start_y, 0);
  path p2= tree_path (end_x  , end_y  , 0);
  if (path_inf (p2, p1)) {
    path temp= p1;
    p1= p2;
    p2= temp;
  }
  set_selection (p1, p2);
  if ((p1 == p2) && start_drag) return;
  start_drag= start_right_drag= false;
  notify_change (THE_SELECTION);
}

void
edit_interface_rep::mouse_select (SI x, SI y) {
  if (eb->action ("select" , x, y, 0) != "") return;
  if (!nil (active_ids) && (get_kbd_modifiers () & 1) == 0) {
    call ("link-follow-ids", object (active_ids));
    return;
  }
  tree g;
  bool b0= inside_graphics (false);
  bool b= inside_graphics ();
  if (b) g= get_graphics ();
  go_to (x, y);
  if ((!b0 && inside_graphics (false)) || (b0 && !inside_graphics (false))) {
    dragging= start_drag= false;
    right_dragging= start_right_drag= false;
  }
  if (!b && inside_graphics ())
    eval ("(graphics-reset-context 'begin)");
  tree g2= get_graphics ();
  if (b && (!inside_graphics () || obtain_ip (g) != obtain_ip (g2))) {
    invalidate_graphical_object ();
    eval ("(graphics-reset-context 'exit)");
  }
  if (selection_active_any ())
    selection_set ("primary", selection_get (), true);
}

void
edit_interface_rep::mouse_paste (SI x, SI y) { (void) x; (void) y;
  if (eb->action ("paste", x, y, 0) != "") return;
  selection_copy ();
  selection_paste ();
}

void
edit_interface_rep::mouse_adjust (SI x, SI y) {
  if (eb->action ("adjust", x, y, 0) != "") return;
  x /= sfactor; y /= sfactor;
  abs_round (x, y);
  if (popup_win == NULL) {
    SI wx, wy;
    win->get_position (wx, wy);
    widget wid;
    SERVER (menu_widget ("(vertical (link texmacs-popup-menu))", wid));
    widget popup_wid= popup_widget (wid, center);
    popup_win= popup_window (popup_wid, wx+ ox+ x, wy+ oy+ y);
    popup_win->map ();
    this << emit_mouse_grab (true);
    popup_wid << set_integer ("grabbed", 1);
    // popup_wid << set_integer ("freeze", 1);
    popup_wid << emit_mouse_grab (true);
  }
}

void
edit_interface_rep::mouse_scroll (SI x, SI y, bool up) {
  string action= up? string ("scroll up"): string ("scroll down");
  if (eb->action (action , x, y, 0) != "") return;
  SI dy= 100*PIXEL;
  if (!up) dy= -dy;
  SERVER (scroll_where (x, y));
  y += dy;
  SERVER (scroll_to (x, y));
}

/******************************************************************************
* getting the cursor (both for text and graphics)
******************************************************************************/

cursor
edit_interface_rep::get_cursor () {
  if (inside_graphics ()) {
    frame f= find_frame ();
    if (!nil (f)) {
      point p= f [point (last_x, last_y)];
      p= f (adjust (p));
      SI x= (SI) p[0];
      SI y= (SI) p[1];
      return cursor (x, y, 0, -5*pixel, 5*pixel, 1.0);
    }
  }
  return copy (the_cursor ());
}

void
edit_interface_rep::set_pointer (string name) {
  sv->get_display()->set_pointer(name);
}

void
edit_interface_rep::set_pointer (
  string curs_name, string mask_name)
{
  sv->get_display()->set_pointer(curs_name, mask_name);
}

/******************************************************************************
* Active loci
******************************************************************************/

void
edit_interface_rep::update_active_loci () {
  path cp= path_up (tree_path (last_x, last_y, 0));
  tree mt= subtree (et, cp);
  path p = cp;
  list<string> ids1, ids2;
  rectangles rs1, rs2;
  eb->loci (last_x, last_y, 0, ids1, rs1);
  while (rp <= p) {
    ids2 << get_ids (subtree (et, p));
    p= path_up (p);
  }

  locus_new_rects= rectangles ();
  active_ids= list<string> ();
  if (!nil (ids1 * ids2) && !has_changed (THE_FOCUS)) {
    list<tree> l= as_list_tree (call ("link-active-upwards", object (mt)));
    while (!nil (l)) {
      tree lt= l->item;
      path lp= reverse (obtain_ip (lt));
      selection sel= eb->find_check_selection (lp * start(lt), lp * end(lt));
      rs2 << outline (sel->rs, pixel);
      l= l->next;
    }
    ids1= as_list_string (call ("link-active-ids", object (ids1)));
    ids2= as_list_string (call ("link-active-ids", object (ids2)));
    if (nil (ids1)) rs1= rectangles ();
    // FIXME: we should keep track which id corresponds to which rectangle
    locus_new_rects= rs1 * rs2;
    active_ids= ids1 * ids2;
  }
  if (locus_new_rects != locus_rects) notify_change (THE_LOCUS);
}

/******************************************************************************
* Event handlers
******************************************************************************/

void
edit_interface_rep::handle_mouse (mouse_event ev) {
  string type= ev->type;
  SI     x   = ev->x*sfactor;
  SI     y   = ev->y*sfactor;
  mouse_any (type, x, y, ev->t);
}
