Back to index

texmacs  1.0.7.15
edit_cursor.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : cursor.cpp
00004 * DESCRIPTION: cursor 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_cursor.hpp"
00013 #include "iterator.hpp"
00014 #include "tm_buffer.hpp"
00015 #include "tree_traverse.hpp"
00016 #include "drd_mode.hpp"
00017 #include "analyze.hpp"
00018 
00019 /******************************************************************************
00020 * Constructor and destructor
00021 ******************************************************************************/
00022 
00023 edit_cursor_rep::edit_cursor_rep ():
00024   cu (0, 0), mv (0, 0), mv_status (0) {}
00025 edit_cursor_rep::~edit_cursor_rep () {}
00026 cursor& edit_cursor_rep::the_cursor () { return cu; }
00027 cursor& edit_cursor_rep::the_ghost_cursor () { return mv; }
00028 
00029 /******************************************************************************
00030 * Cursor movement
00031 ******************************************************************************/
00032 
00033 #define DELTA (1<<23)
00034 
00035 static bool searching_forwards;
00036 
00037 path
00038 edit_cursor_rep::make_cursor_accessible (path p, bool forwards) {
00039   //time_t t1= texmacs_time ();
00040   path start_p= p;
00041   bool inverse= false;
00042   int old_mode= get_access_mode ();
00043   if (get_init_string (MODE) == "src")
00044     set_access_mode (DRD_ACCESS_SOURCE);
00045   while (!is_accessible_cursor (et, p) && !in_source ()) {
00046     path pp;
00047     ASSERT (rp <= p, "path outside document");
00048     p= rp * closest_inside (subtree (et, rp), p / rp);
00049     if (forwards ^ inverse)
00050       pp= rp * next_valid (subtree (et, rp), p / rp);
00051     else
00052       pp= rp * previous_valid (subtree (et, rp), p / rp);
00053     if (pp == p) {
00054       if (inverse) break;
00055       else { p= start_p; inverse= true; }
00056     }
00057     else p= pp;
00058   }
00059   set_access_mode (old_mode);
00060   //time_t t2= texmacs_time ();
00061   //if (t2-t1 >= 1) cout << "made_cursor_accessible took " << t2-t1 << "ms\n";
00062   return p;
00063 }
00064 
00065 path
00066 edit_cursor_rep::tree_path (path sp, SI x, SI y, SI delta) {
00067   path stp= find_scrolled_tree_path (eb, sp, x, y, delta);
00068   path p= correct_cursor (et, stp /*, searching_forwards */);
00069   return make_cursor_accessible (p, searching_forwards);
00070 }
00071 
00072 bool
00073 edit_cursor_rep::cursor_move_sub (SI& x0, SI& y0, SI& d0, SI dx, SI dy) {
00074   path sp= find_innermost_scroll (eb, tp);
00075   searching_forwards= dx == 1 || dy == -1;
00076 
00077   int i,d;
00078   path ref_p= tree_path (sp, x0, y0, d0);
00079   if (ref_p != tp) {
00080     tp= ref_p;
00081     return true;
00082   }
00083   
00084   // cout << "ref_p = " << ref_p << "\n";
00085   if (ref_p == tree_path (sp, x0, y0, d0+ dx*DELTA)) {
00086     for (i=1; i<DELTA; i=i<<1)
00087       if (ref_p != tree_path (sp, x0+ dx*i, y0+ dy*i, d0+ dx*DELTA))
00088        break;
00089     if (i>=DELTA) return false;
00090     for (d=i>>2; d>=1; d=d>>1)
00091       if (ref_p != tree_path (sp, x0+ dx*(i-d), y0+ dy*(i-d), d0+ dx*DELTA))
00092        i-=d;
00093 
00094     x0 += dx*i;
00095     y0 += dy*i;
00096   }
00097   
00098   // cout << "path  = " << tree_path (sp, x0, y0, d0) << "\n";
00099   if (dx!=0) {
00100     if (ref_p == tree_path (sp, x0, y0, d0)) {
00101       for (i=1; i<DELTA; i=i<<1)
00102        if (ref_p != tree_path (sp, x0, y0, d0+ dx*i)) break;
00103       if (i>=DELTA)
00104        FAILED ("inconsistent cursor handling");
00105       for (d=i>>2; d>=1; d=d>>1)
00106        if (ref_p != tree_path (sp, x0, y0, d0+ dx*(i-d))) i-=d;
00107       d0 += dx*i;
00108     }
00109     else {
00110       for (i=1; i<DELTA; i=i<<1)
00111        if (ref_p == tree_path (sp, x0, y0, d0- dx*i)) break;
00112       if (i<DELTA) {
00113        for (d=i>>2; d>=1; d=d>>1)
00114          if (ref_p == tree_path (sp, x0, y0, d0- dx*(i-d))) i-=d;
00115        i--;
00116        d0 -= dx*i;
00117       }
00118       else {  // exceptional case
00119        ref_p= tree_path (sp, x0, y0, d0- dx*DELTA);
00120        for (i=1; i<DELTA; i=i<<1)
00121          if (ref_p == tree_path (sp, x0, y0, d0- dx*i)) break;
00122        for (d=i>>2; d>=1; d=d>>1)
00123          if (ref_p == tree_path (sp, x0, y0, d0- dx*(i-d))) i-=d;
00124        d0 -= dx*i;
00125       }
00126     }
00127   }
00128 
00129   tp= tree_path (sp, x0, y0, d0);
00130   return true;
00131 }
00132 
00133 void
00134 edit_cursor_rep::cursor_move (SI dx, SI dy) {
00135   //time_t t1= texmacs_time ();
00136   //stretched_print ((tree) eb, false);
00137   cursor_move_sub (mv->ox, mv->oy, mv->delta, dx, dy);
00138   //time_t t2= texmacs_time ();
00139   //if (t2 - t1 >= 10) cout << "cursor_move took " << t2-t1 << "ms\n";
00140 }
00141 
00142 /******************************************************************************
00143 * Routines affecting both the cursor and the ghost cursor
00144 ******************************************************************************/
00145 
00146 void
00147 edit_cursor_rep::adjust_ghost_cursor (int status) {
00148   if (status==mv_status) {
00149     if (status!=HORIZONTAL) {
00150       mv->ox   = cu->ox;
00151       mv->delta= cu->delta;
00152     }
00153     if (status!=VERTICAL)
00154       mv->oy= cu->oy;
00155   }
00156 }
00157 
00158 void
00159 edit_cursor_rep::notify_cursor_moved (int status) {
00160   mv_status= status;
00161   cu= eb->find_check_cursor (tp);
00162   notify_change (THE_CURSOR);
00163 }
00164 
00165 void
00166 edit_cursor_rep::go_to (SI x, SI y, bool absolute) {
00167   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00168   tp= tree_path (absolute? path (): find_innermost_scroll (eb, tp), x, y, 0);
00169   notify_cursor_moved (CENTER);
00170   mv->ox   = x;
00171   mv->oy   = y;
00172   mv->delta= 0;
00173 }
00174 
00175 void
00176 edit_cursor_rep::go_left_physical () {
00177   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00178   adjust_ghost_cursor (VERTICAL);
00179   cursor_move (-1, 0);
00180   notify_cursor_moved (HORIZONTAL);
00181   select_from_cursor_if_active ();
00182 }
00183 
00184 void
00185 edit_cursor_rep::go_right_physical () {
00186   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00187   adjust_ghost_cursor (VERTICAL);
00188   cursor_move (1, 0);
00189   notify_cursor_moved (HORIZONTAL);
00190   select_from_cursor_if_active ();
00191 }
00192 
00193 void
00194 edit_cursor_rep::go_up () {
00195   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00196   path scroll_p= find_innermost_scroll (eb, tp);
00197   path start_p= tree_path (scroll_p, -(1 << 30), 1 << 30, 0);
00198   if (tp == start_p) return;
00199   path old_p= tp;
00200   adjust_ghost_cursor (HORIZONTAL);
00201   cursor_move (0, 1);
00202   notify_cursor_moved (VERTICAL);
00203   if (tp == old_p) tp= start_p;
00204   select_from_cursor_if_active ();
00205 }
00206 
00207 void
00208 edit_cursor_rep::go_down () {
00209   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00210   path scroll_p= find_innermost_scroll (eb, tp);
00211   path end_p= tree_path (scroll_p, 1 << 30, -(1 << 30), 0);
00212   if (tp == end_p) return;
00213   path old_p= tp;
00214   adjust_ghost_cursor (HORIZONTAL);
00215   cursor_move (0, -1);
00216   notify_cursor_moved (VERTICAL);
00217   if (tp == old_p) tp= end_p;
00218   select_from_cursor_if_active ();
00219 }
00220 
00221 void
00222 edit_cursor_rep::go_page_up () {
00223   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00224   path sp= find_innermost_scroll (eb, tp);
00225   if (is_nil (sp)) go_to (mv->ox, min (mv->oy + get_window_height (), eb->y2));
00226   else {
00227     SI x, y, sx, sy;
00228     rectangle outer, inner;
00229     box b= eb[path_up (sp)];
00230     find_canvas_info (eb, sp, x, y, sx, sy, outer, inner);
00231     go_to (mv->ox, min (mv->oy + b->h (), y + sy + inner->y2), false);
00232   }
00233   select_from_cursor_if_active ();
00234 }
00235 
00236 void
00237 edit_cursor_rep::go_page_down () {
00238   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00239   path sp= find_innermost_scroll (eb, tp);
00240   if (is_nil (sp)) go_to (mv->ox, max (mv->oy - get_window_height (), eb->y1));
00241   else {
00242     SI x, y, sx, sy;
00243     rectangle outer, inner;
00244     box b= eb[path_up (sp)];
00245     find_canvas_info (eb, sp, x, y, sx, sy, outer, inner);
00246     go_to (mv->ox, max (mv->oy - b->h (), y + sy + inner->y1), false);
00247   }
00248   select_from_cursor_if_active ();
00249 }
00250 
00251 /******************************************************************************
00252 * Adapt physical horizontal cursor movement to line breaking
00253 ******************************************************************************/
00254 
00255 void
00256 edit_cursor_rep::go_left () {
00257   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00258   path old_tp= copy (tp);
00259   go_left_physical ();
00260   if (tp != old_tp && inside_same (et, old_tp, tp, DOCUMENT)) return;
00261   path p= previous_valid (et, old_tp);
00262   if (rp < p) go_to (p);
00263   select_from_cursor_if_active ();
00264 }
00265 
00266 void
00267 edit_cursor_rep::go_right () {
00268   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00269   path old_tp= copy (tp);
00270   go_right_physical ();
00271   if (tp != old_tp && inside_same (et, old_tp, tp, DOCUMENT)) return;
00272   path p= next_valid (et, old_tp);
00273   if (rp < p) go_to (p);
00274   select_from_cursor_if_active ();
00275 }
00276 
00277 void
00278 edit_cursor_rep::go_start_line () {
00279   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00280   while (true) {
00281     cursor old_cu= copy (cu);
00282     cursor old_mv= copy (mv);
00283     path   old_tp= copy (tp);
00284     go_left_physical ();
00285     if (tp == old_tp || !more_inside (et, tp, old_tp, DOCUMENT)) {
00286       notify_cursor_moved (HORIZONTAL);
00287       cu= old_cu;
00288       mv= old_mv;
00289       tp= old_tp;
00290       select_from_cursor_if_active ();
00291       return;
00292     }
00293   }
00294 }
00295 
00296 void
00297 edit_cursor_rep::go_end_line () {
00298   if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
00299   while (true) {
00300     cursor old_cu= copy (cu);
00301     cursor old_mv= copy (mv);
00302     path   old_tp= copy (tp);
00303     go_right_physical ();
00304     if (tp == old_tp || !more_inside (et, tp, old_tp, DOCUMENT)) {
00305       notify_cursor_moved (HORIZONTAL);
00306       cu= old_cu;
00307       mv= old_mv;
00308       tp= old_tp;
00309       select_from_cursor_if_active ();
00310       return;
00311     }
00312   }
00313 }
00314 
00315 /******************************************************************************
00316 * Logical cursor changes
00317 ******************************************************************************/
00318 
00319 void
00320 edit_cursor_rep::adjust_cursor () {
00321   path sp= find_innermost_scroll (eb, tp);
00322   cursor mv= copy (cu);
00323   SI dx= PIXEL << 8, ddelta= 0;
00324   path p= tree_path (sp, mv->ox, mv->oy, mv->delta);
00325   if (p != tp) {
00326     // cout << "Cursors don't match\n";
00327     while (dx != 0 || ddelta != 0) {
00328       // cout << "  " << tp << ", " << p << "\n";
00329       p= tree_path (sp, mv->ox, mv->oy, mv->delta);
00330       int eps= (path_inf (p, tp)? 1: -1);
00331       if (p == tp) eps= (mv->ox < cu->ox? 1: -1);
00332       if (p == tp && mv->ox == cu->ox) eps= (mv->delta < cu->delta? 1: -1);
00333       if (dx > 0) {
00334        if (p != tp ||
00335            tree_path (sp, mv->ox + eps * dx, mv->oy, mv->delta) == tp)
00336          mv->ox += eps * dx;
00337        dx >>= 1;
00338        if (dx == 0) ddelta= DELTA;
00339       }
00340       else if (ddelta > 0) {
00341        if (p != tp ||
00342            tree_path (sp, mv->ox, mv->oy, mv->delta + eps * ddelta) == tp)
00343          mv->delta += eps * ddelta;
00344        ddelta >>= 1;
00345       }
00346     }
00347   }
00348   if (p == tp) cu= mv;
00349 }
00350 
00351 void
00352 edit_cursor_rep::go_to_here () {
00353   cu= eb->find_check_cursor (tp);
00354   if (!cu->valid || !valid_cursor (et, tp)) {
00355     tp= super_correct (et, tp);
00356     cu= eb->find_check_cursor (tp);
00357   }
00358   if (!cu->valid || !valid_cursor (et, tp)) {
00359     tp= make_cursor_accessible (tp, false);
00360     cu= eb->find_check_cursor (tp);
00361   }
00362   if (cu->valid) adjust_cursor ();
00363   if (mv_status == DIRECT) mv= copy (cu);
00364   notify_change (THE_CURSOR);
00365 }
00366 
00367 void
00368 edit_cursor_rep::go_to (path p) {
00369   if (rp <= p) {
00370     //if (tp != p) cout << "Go to " << p << "\n";
00371     tp= p;
00372     mv_status= DIRECT;
00373     if (!has_changed (THE_TREE+THE_ENVIRONMENT)) {
00374       cu= eb->find_check_cursor (tp);
00375       if (cu->valid) adjust_cursor ();
00376       mv= copy (cu);
00377     }
00378     notify_change (THE_CURSOR);
00379   }
00380 }
00381 
00382 void
00383 edit_cursor_rep::go_to_correct (path p) {
00384   p= correct_cursor (et, p);
00385   go_to (p);
00386 }
00387 
00388 void
00389 edit_cursor_rep::go_to_start (path p) {
00390   go_to (start (et, p));
00391 }
00392 
00393 void
00394 edit_cursor_rep::go_to_end (path p) {
00395   go_to (end (et, p));
00396 }
00397 
00398 void
00399 edit_cursor_rep::go_to_border (path p, bool at_start) {
00400   if (at_start) go_to_start (p);
00401   else go_to_end (p);
00402 }
00403 
00404 void
00405 edit_cursor_rep::go_start () {
00406   go_to (correct_cursor (et, rp * 0));
00407   select_from_cursor_if_active ();
00408 }
00409 
00410 void
00411 edit_cursor_rep::go_end () {
00412   go_to (correct_cursor (et, rp * 1));
00413   select_from_cursor_if_active ();
00414 }
00415 
00416 void
00417 edit_cursor_rep::go_start_paragraph () {
00418   path p= search_parent_upwards (DOCUMENT);
00419   go_to (start (et, p));
00420   select_from_cursor_if_active ();
00421 }
00422 
00423 void
00424 edit_cursor_rep::go_end_paragraph () {
00425   path p= search_parent_upwards (DOCUMENT);
00426   go_to (end (et, p));
00427   select_from_cursor_if_active ();
00428 }
00429 
00430 void
00431 edit_cursor_rep::go_start_of (tree_label what) {
00432   path p= search_upwards (what);
00433   if (!is_nil (p)) go_to (start (et, p));
00434 }
00435 
00436 void
00437 edit_cursor_rep::go_end_of (tree_label what) {
00438   path p= search_upwards (what);
00439   if (!is_nil (p)) go_to (end (et, p));
00440 }
00441 
00442 void
00443 edit_cursor_rep::go_start_with (string var, string val) {
00444   path p= search_upwards_with (var, val);
00445   if (!is_nil (p)) go_to (start (et, p));
00446 }
00447 
00448 void
00449 edit_cursor_rep::go_end_with (string var, string val) {
00450   path p= search_upwards_with (var, val);
00451   if (!is_nil (p)) go_to (end (et, p));
00452 }
00453 
00454 /******************************************************************************
00455 * Jumping to a label
00456 ******************************************************************************/
00457 
00458 tree
00459 edit_cursor_rep::get_labels () {
00460   tree r (TUPLE);
00461   hashmap<string,tree> h= buf->data->ref;
00462   if (buf->prj != NULL) {
00463     h= copy (buf->prj->data->ref);
00464     h->join (buf->data->ref);
00465   }
00466   iterator<string> it= iterate (h);
00467   while (it->busy ()) {
00468     string ref= it->next ();
00469     r << ref;
00470   }
00471   return r;
00472 }
00473 
00474 static path
00475 search_label (tree t, string which) {
00476   if (is_atomic (t)) return path ();
00477   else if (t == tree (LABEL, which)) return path (1);
00478   else if (is_compound (t, "tag", 2) && t[0] == which)
00479     return path (1, start (t[1]));
00480   else {
00481     int i, n=N(t);
00482     for (i=0; i<n; i++) {
00483       path q= search_label (t[i], which);
00484       if (!is_nil (q)) return path (i, q);
00485     }
00486     return path ();
00487   }
00488 }
00489 
00490 void
00491 edit_cursor_rep::show_cursor_if_hidden () {
00492   if (!is_accessible_cursor (et, tp) && !in_source ()) {
00493     eval ("(use-modules (utils edit variants))");
00494     eval ("(cursor-show-hidden)");
00495   }
00496 }
00497 
00498 void
00499 edit_cursor_rep::go_to_label (string s) {
00500   path p= search_label (et, s);
00501   if (!is_nil (p)) {
00502     go_to (p);
00503     show_cursor_if_hidden ();
00504     return;
00505   }
00506   if (!is_nil (eb)) {
00507     p= eb->find_tag (s);
00508     if (!is_nil (p)) {
00509       go_to (p);
00510       show_cursor_if_hidden ();
00511       return;
00512     }
00513   }
00514   tree val= (buf->prj==NULL? buf->data->ref[s]: buf->prj->data->ref[s]);
00515   if (is_func (val, TUPLE, 3) && is_atomic (val[2])) {
00516     string extra= val[2]->label;
00517     if (starts (extra, "#")) {
00518       string part= extra (1, N (extra));
00519       int i= search_forwards (".", part);
00520       if (i >= 0) part= part (0, i);
00521       string show= "(show-hidden-part " * scm_quote (part) * ")";
00522       string jump= "(go-to-label " * scm_quote (s) * ")";
00523       exec_delayed (scheme_cmd ("(if " * show * " (delayed " * jump * "))"));
00524     }
00525     else {
00526       url u= relative (buf->buf->name, url (extra));
00527       if (u != buf->buf->name) {
00528        string new_buf = scm_quote (as_string (u));
00529        string load_buf= "(load-buffer (url-system " * new_buf * "))";
00530        string jump_to = "(go-to-label " * scm_quote (s) * ")";
00531        exec_delayed (scheme_cmd ("(begin " * load_buf * " " * jump_to * ")"));
00532       }
00533     }
00534   }
00535 }