Back to index

texmacs  1.0.7.15
edit_mouse.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : edit_mouse.cpp
00004 * DESCRIPTION: Mouse handling
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "edit_interface.hpp"
00013 #include "tm_buffer.hpp"
00014 #include "timer.hpp"
00015 #include "link.hpp"
00016 #include "analyze.hpp"
00017 #include "drd_mode.hpp"
00018 #include "message.hpp"
00019 #include "window.hpp"
00020 
00021 /******************************************************************************
00022 * Routines for the mouse
00023 ******************************************************************************/
00024 
00025 void
00026 edit_interface_rep::mouse_click (SI x, SI y) {
00027   if (eb->action ("click", x, y, 0) != "") return;
00028   start_x   = x;
00029   start_y   = y;
00030   send_mouse_grab (this, true);
00031 }
00032 
00033 bool
00034 edit_interface_rep::mouse_extra_click (SI x, SI y) {
00035   go_to (x, y);
00036   if (eb->action ("double-click", x, y, 0) != "") return true;
00037   go_to (x, y);
00038   path p1, p2;
00039   get_selection (p1, p2);
00040   if ((p1==p2) || path_less (tp, p1) || path_less (p2, tp)) select (tp, tp);
00041   select_enlarge ();
00042   if (selection_active_any ())
00043     selection_set ("mouse", selection_get (), true);
00044   return false;
00045 }
00046 
00047 void
00048 edit_interface_rep::mouse_drag (SI x, SI y) {
00049   if (inside_graphics ()) return;
00050   if (eb->action ("drag", x, y, 0) != "") return;
00051   go_to (x, y);
00052   end_x  = x;
00053   end_y  = y;
00054   selection_visible ();
00055   path sp= find_innermost_scroll (eb, tp);
00056   path p1= tree_path (sp, start_x, start_y, 0);
00057   path p2= tree_path (sp, end_x  , end_y  , 0);
00058   if (path_inf (p2, p1)) {
00059     path temp= p1;
00060     p1= p2;
00061     p2= temp;
00062   }
00063   set_selection (p1, p2);
00064   notify_change (THE_SELECTION);
00065 }
00066 
00067 void
00068 edit_interface_rep::mouse_select (SI x, SI y, int mods, bool drag) {
00069   if (eb->action ("select" , x, y, 0) != "") return;
00070   if (!is_nil (active_ids) && (mods & 256) == 0) {
00071     call ("link-follow-ids", object (active_ids), object ("click"));
00072     return;
00073   }
00074   tree g;
00075   bool b0= inside_graphics (false);
00076   bool b= inside_graphics ();
00077   if (b) g= get_graphics ();
00078   go_to (x, y);
00079   if ((!b0 && inside_graphics (false)) || (b0 && !inside_graphics (false)))
00080     drag= false;
00081   if (!b && inside_graphics ())
00082     eval ("(graphics-reset-context 'begin)");
00083   tree g2= get_graphics ();
00084   if (b && (!inside_graphics () || obtain_ip (g) != obtain_ip (g2))) {
00085     invalidate_graphical_object ();
00086     eval ("(graphics-reset-context 'exit)");
00087   }
00088   if (!drag) {
00089     path sp= find_innermost_scroll (eb, tp);
00090     path p0= tree_path (sp, x, y, 0);
00091     set_selection (p0, p0);
00092     notify_change (THE_SELECTION);
00093   }
00094   if (selection_active_any ())
00095     selection_set ("mouse", selection_get (), true);
00096 }
00097 
00098 void
00099 edit_interface_rep::mouse_paste (SI x, SI y) { (void) x; (void) y;
00100   if (eb->action ("paste", x, y, 0) != "") return;
00101   go_to (x, y);
00102   selection_paste ("mouse");
00103 }
00104 
00105 void
00106 edit_interface_rep::mouse_adjust (SI x, SI y) {
00107   if (eb->action ("adjust", x, y, 0) != "") return;
00108   x /= sfactor; y /= sfactor;
00109   abs_round (x, y);
00110   if (is_nil (popup_win)) {
00111     SI wx, wy;
00112     ::get_position (get_window (this), wx, wy);
00113     widget wid;
00114     SERVER (menu_widget ("(vertical (link texmacs-popup-menu))", wid));
00115     widget popup_wid= popup_widget (wid);
00116     popup_win= ::popup_window_widget (popup_wid, "Popup menu");
00117 #if defined (QTTEXMACS) || defined(AQUATEXMACS)
00118     SI ox, oy;
00119     get_position (this, ox, oy);
00120     set_position (popup_win, wx+     x, wy+     y);
00121 #else
00122     set_position (popup_win, wx+ ox+ x, wy+ oy+ y);
00123 #endif
00124     set_visibility (popup_win, true);
00125     send_keyboard_focus (this);
00126     send_mouse_grab (popup_wid, true);
00127   }
00128 }
00129 
00130 void
00131 edit_interface_rep::mouse_scroll (SI x, SI y, bool up) {
00132   string action= up? string ("scroll up"): string ("scroll down");
00133   if (eb->action (action , x, y, 0) != "") return;
00134   SI dy= 100*PIXEL;
00135   if (!up) dy= -dy;
00136   path sp= find_innermost_scroll (eb, tp);
00137   if (is_nil (sp)) {
00138     SERVER (scroll_where (x, y));
00139     y += dy;
00140     SERVER (scroll_to (x, y));
00141   }
00142   else {
00143     SI x, y, sx, sy;
00144     rectangle outer, inner;
00145     find_canvas_info (eb, sp, x, y, sx, sy, outer, inner);
00146     SI ty= inner->y2 - inner->y1;
00147     SI cy= outer->y2 - outer->y1;
00148     if (ty > cy) {
00149       tree   old_yt= eb[path_up (sp)]->get_info ("scroll-y");
00150       string old_ys= as_string (old_yt);
00151       double old_p = 0.0;
00152       if (ends (old_ys, "%")) old_p= as_double (old_ys (0, N(old_ys)-1));
00153       double new_p= old_p + 100.0 * ((double) dy) / ((double) (ty - cy));
00154       new_p= max (min (new_p, 100.0), 0.0);
00155       tree new_yt= as_string (new_p) * "%";
00156       if (new_yt != old_yt && is_accessible (obtain_ip (old_yt))) {
00157        object fun= symbol_object ("tree-set");
00158        object cmd= list_object (fun, old_yt, new_yt);
00159        exec_delayed (scheme_cmd (cmd));
00160        temp_invalid_cursor= true;
00161       }
00162     }
00163   }
00164 }
00165 
00166 /******************************************************************************
00167 * getting the cursor (both for text and graphics)
00168 ******************************************************************************/
00169 
00170 cursor
00171 edit_interface_rep::get_cursor () {
00172   if (inside_graphics ()) {
00173     frame f= find_frame ();
00174     if (!is_nil (f)) {
00175       point p= f [point (last_x, last_y)];
00176       p= f (adjust (p));
00177       SI x= (SI) p[0];
00178       SI y= (SI) p[1];
00179       return cursor (x, y, 0, -5*pixel, 5*pixel, 1.0);
00180     }
00181   }
00182   return copy (the_cursor ());
00183 }
00184 
00185 void
00186 edit_interface_rep::set_pointer (string name) {
00187   send_mouse_pointer (this, name);
00188 }
00189 
00190 void
00191 edit_interface_rep::set_pointer (
00192   string curs_name, string mask_name)
00193 {
00194   send_mouse_pointer (this, curs_name, mask_name);
00195 }
00196 
00197 /******************************************************************************
00198 * Active loci
00199 ******************************************************************************/
00200 
00201 void
00202 edit_interface_rep::update_active_loci () {
00203   int old_mode= set_access_mode (DRD_ACCESS_SOURCE);
00204   path cp= path_up (tree_path (path (), last_x, last_y, 0));
00205   set_access_mode (old_mode);
00206   tree mt= subtree (et, cp);
00207   path p = cp;
00208   list<string> ids1, ids2;
00209   rectangles rs1, rs2;
00210   eb->loci (last_x, last_y, 0, ids1, rs1);
00211   while (rp <= p) {
00212     ids2 << get_ids (subtree (et, p));
00213     p= path_up (p);
00214   }
00215 
00216   locus_new_rects= rectangles ();
00217   active_ids= list<string> ();
00218   if (!is_nil (ids1 * ids2) && !has_changed (THE_FOCUS)) {
00219     list<tree> l= as_list_tree (call ("link-active-upwards", object (mt)));
00220     while (!is_nil (l)) {
00221       tree lt= l->item;
00222       path lp= reverse (obtain_ip (lt));
00223       selection sel= eb->find_check_selection (lp * start(lt), lp * end(lt));
00224       rs2 << outline (sel->rs, pixel);
00225       l= l->next;
00226     }
00227     ids1= as_list_string (call ("link-active-ids", object (ids1)));
00228     ids2= as_list_string (call ("link-active-ids", object (ids2)));
00229     if (is_nil (ids1)) rs1= rectangles ();
00230     // FIXME: we should keep track which id corresponds to which rectangle
00231     locus_new_rects= rs1 * rs2;
00232     active_ids= ids1 * ids2;
00233   }
00234   if (locus_new_rects != locus_rects) notify_change (THE_LOCUS);
00235 }
00236 
00237 /******************************************************************************
00238 * drag and double click detection for left button
00239 ******************************************************************************/
00240 
00241 static void*  left_handle  = NULL;
00242 static bool   left_started = false;
00243 static bool   left_dragging= false;
00244 static SI     left_x= 0;
00245 static SI     left_y= 0;
00246 static time_t left_last= 0;
00247 
00248 void
00249 drag_left_reset () {
00250   left_started = false;
00251   left_dragging= false;
00252   left_x       = 0;
00253   left_y       = 0;
00254 }
00255 
00256 static string
00257 detect_left_drag (void* handle, string type, SI x, SI y, time_t t, SI d) {
00258   if (left_handle != handle) drag_left_reset ();
00259   left_handle= handle;
00260   if (type == "press-left") {
00261     left_dragging= true;
00262     left_started = true;
00263     left_x       = x;
00264     left_y       = y;
00265   }
00266   else if (type == "move") {
00267     if (left_started) {
00268       if (norm (point (x - left_x, y - left_y)) < d) return "wait-left";
00269       left_started= false;
00270       return "start-drag-left";
00271     }
00272     if (left_dragging) return "dragging-left";
00273   }
00274   else if (type == "release-left") {
00275     if (left_started) drag_left_reset ();
00276     if (left_dragging) {
00277       drag_left_reset ();
00278       return "end-drag-left";
00279     }
00280     if ((t >= left_last) && ((t - left_last) <= 500)) {
00281       left_last= t;
00282       return "double-left";
00283     }
00284     left_last= t;
00285   }
00286   return type;
00287 }
00288 
00289 /******************************************************************************
00290 * drag and double click detection for right button
00291 ******************************************************************************/
00292 
00293 static void*  right_handle  = NULL;
00294 static bool   right_started = false;
00295 static bool   right_dragging= false;
00296 static SI     right_x= 0;
00297 static SI     right_y= 0;
00298 static time_t right_last= 0;
00299 
00300 void
00301 drag_right_reset () {
00302   right_started = false;
00303   right_dragging= false;
00304   right_x       = 0;
00305   right_y       = 0;
00306   right_last    = 0;
00307 }
00308 
00309 static string
00310 detect_right_drag (void* handle, string type, SI x, SI y, time_t t, SI d) {
00311   if (right_handle != handle) drag_right_reset ();
00312   right_handle= handle;
00313   if (type == "press-right") {
00314     right_dragging= true;
00315     right_started = true;
00316     right_x       = x;
00317     right_y       = y;
00318   }
00319   else if (type == "move") {
00320     if (right_started) {
00321       if (norm (point (x - right_x, y - right_y)) < d) return "wait-right";
00322       right_started= false;
00323       return "start-drag-right";
00324     }
00325     if (right_dragging) return "dragging-right";
00326   }
00327   else if (type == "release-right") {
00328     if (right_started) drag_right_reset ();
00329     if (right_dragging) {
00330       drag_right_reset ();
00331       return "end-drag-right";
00332     }
00333     if ((t >= right_last) && ((t - right_last) <= 500)) {
00334       right_last= t;
00335       return "double-right";
00336     }
00337     right_last= t;
00338   }
00339   return type;
00340 }
00341 
00342 /******************************************************************************
00343 * dispatching
00344 ******************************************************************************/
00345 
00346 void
00347 edit_interface_rep::mouse_any (string type, SI x, SI y, int mods, time_t t) {
00348   //cout << "Mouse any " << type << ", " << x << ", " << y << "; " << mods << ", " << t << "\n";
00349   if (t < last_t && (last_x != 0 || last_y != 0 || last_t != 0)) {
00350     //cout << "Ignored " << type << ", " << x << ", " << y << "; " << mods << ", " << t << "\n";
00351     return;
00352   }
00353   last_x= x; last_y= y; last_t= t;
00354   bool move_like=
00355     (type == "move" || type == "dragging-left" || type == "dragging-right");
00356   if ((!move_like) || (is_attached (this) && !check_event (MOTION_EVENT)))
00357     update_active_loci ();
00358   if (type == "move")
00359     call ("link-follow-ids", object (active_ids), object ("mouse-over"));
00360 
00361   if (type == "leave")
00362     set_pointer ("XC_top_left_arrow");
00363   if ((!move_like) && (type != "enter") && (type != "leave"))
00364     set_input_normal ();
00365   if (!is_nil (popup_win) && (type != "leave")) {
00366     set_visibility (popup_win, false);
00367     destroy_window_widget (popup_win);
00368     popup_win= widget ();
00369   }
00370 
00371   if (inside_graphics (false)) {
00372     if (mouse_graphics (type, x, y, mods, t)) return;
00373     if (!over_graphics (x, y))
00374       eval ("(graphics-reset-context 'text-cursor)");
00375   }
00376 
00377   if (type == "press-left" || type == "start-drag-left") mouse_click (x, y);
00378   if (type == "dragging-left") {
00379     if (is_attached (this) && check_event (DRAG_EVENT)) return;
00380     mouse_drag (x, y);
00381   }
00382   if (type == "release-left" || type == "end-drag-left") {
00383     send_mouse_grab (this, false);
00384     mouse_select (x, y, mods, type == "end-drag-left");
00385   }
00386   if (type == "double-left") {
00387     send_mouse_grab (this, false);
00388     if (mouse_extra_click (x, y))
00389       drag_left_reset ();
00390   }
00391   if (type == "press-middle") mouse_paste (x, y);
00392   if (type == "press-right") mouse_adjust (x, y);
00393   if (type == "press-up") mouse_scroll (x, y, true);
00394   if (type == "press-down") mouse_scroll (x, y, false);
00395 
00396   if ((type == "press-left") ||
00397       (type == "release-left") ||
00398       (type == "end-drag-left") ||
00399       (type == "press-middle") ||
00400       (type == "press-right"))
00401     notify_change (THE_DECORATIONS);
00402 }
00403 
00404 /******************************************************************************
00405 * Event handlers
00406 ******************************************************************************/
00407 
00408 static void
00409 call_mouse_event (string kind, SI x, SI y, SI m, time_t t) {
00410   array<object> args;
00411   args << object (kind) << object (x) << object (y)
00412        << object (m) << object ((double) t);
00413   call ("mouse-event", args);
00414 }
00415 
00416 static void
00417 delayed_call_mouse_event (string kind, SI x, SI y, SI m, time_t t) {
00418   // NOTE: interestingly, the (:idle 1) is not necessary for the Qt port
00419   // but is required for appropriate updating when using the X11 port
00420   string cmd=
00421     "(delayed (:idle 1) (mouse-event " * scm_quote (kind) * " " *
00422     as_string (x) * " " * as_string (y) * " " *
00423     as_string (m) * " " * as_string ((long int) t) * "))";
00424   eval (cmd);
00425 }
00426 
00427 void
00428 edit_interface_rep::handle_mouse (string kind, SI x, SI y, int m, time_t t) {
00429   if (is_nil (eb)) apply_changes ();
00430   start_editing ();
00431   x *= sfactor;
00432   y *= sfactor;
00433   //cout << kind << " (" << x << ", " << y << "; " << m << ")"
00434   //     << " at " << t << "\n";
00435 
00436   string rew= kind;
00437   rew= detect_left_drag ((void*) this, rew, x, y, t, 5 * PIXEL * sfactor);
00438   if (rew == "start-drag-left") {
00439     call_mouse_event (rew, left_x, left_y, m, t);
00440     delayed_call_mouse_event ("dragging-left", x, y, m, t);
00441   }
00442   else {
00443     rew= detect_right_drag ((void*) this, rew, x, y, t, 5 * PIXEL * sfactor);
00444     if (rew == "start-drag-right") {
00445       call_mouse_event (rew, right_x, right_y, m, t);
00446       delayed_call_mouse_event ("dragging-right", x, y, m, t);
00447     }
00448     else call_mouse_event (rew, x, y, m, t);
00449   }
00450 
00451   end_editing ();
00452 }