PageRenderTime 39ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/TeXmacs-1.0.7.11-src/src/Edit/Interface/edit_cursor.cpp

#
C++ | 533 lines | 443 code | 52 blank | 38 comment | 158 complexity | d462487ae011f81a1b5de9b9ee95e25c MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /******************************************************************************
  2. * MODULE : cursor.cpp
  3. * DESCRIPTION: cursor handling
  4. * COPYRIGHT : (C) 1999 Joris van der Hoeven
  5. *******************************************************************************
  6. * This software falls under the GNU general public license version 3 or later.
  7. * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
  8. * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
  9. ******************************************************************************/
  10. #include "edit_cursor.hpp"
  11. #include "iterator.hpp"
  12. #include "tm_buffer.hpp"
  13. #include "tree_traverse.hpp"
  14. #include "drd_mode.hpp"
  15. #include "analyze.hpp"
  16. /******************************************************************************
  17. * Constructor and destructor
  18. ******************************************************************************/
  19. edit_cursor_rep::edit_cursor_rep ():
  20. cu (0, 0), mv (0, 0), mv_status (0) {}
  21. edit_cursor_rep::~edit_cursor_rep () {}
  22. cursor& edit_cursor_rep::the_cursor () { return cu; }
  23. cursor& edit_cursor_rep::the_ghost_cursor () { return mv; }
  24. /******************************************************************************
  25. * Cursor movement
  26. ******************************************************************************/
  27. #define DELTA (1<<23)
  28. static bool searching_forwards;
  29. path
  30. edit_cursor_rep::make_cursor_accessible (path p, bool forwards) {
  31. //time_t t1= texmacs_time ();
  32. path start_p= p;
  33. bool inverse= false;
  34. int old_mode= get_access_mode ();
  35. if (get_init_string (MODE) == "src")
  36. set_access_mode (DRD_ACCESS_SOURCE);
  37. while (!is_accessible_cursor (et, p) && !in_source ()) {
  38. path pp;
  39. ASSERT (rp <= p, "path outside document");
  40. p= rp * closest_inside (subtree (et, rp), p / rp);
  41. if (forwards ^ inverse)
  42. pp= rp * next_valid (subtree (et, rp), p / rp);
  43. else
  44. pp= rp * previous_valid (subtree (et, rp), p / rp);
  45. if (pp == p) {
  46. if (inverse) break;
  47. else { p= start_p; inverse= true; }
  48. }
  49. else p= pp;
  50. }
  51. set_access_mode (old_mode);
  52. //time_t t2= texmacs_time ();
  53. //if (t2-t1 >= 1) cout << "made_cursor_accessible took " << t2-t1 << "ms\n";
  54. return p;
  55. }
  56. path
  57. edit_cursor_rep::tree_path (path sp, SI x, SI y, SI delta) {
  58. path p= correct_cursor (et, find_scrolled_tree_path (eb, sp, x, y, delta));
  59. return make_cursor_accessible (p, searching_forwards);
  60. }
  61. bool
  62. edit_cursor_rep::cursor_move_sub (SI& x0, SI& y0, SI& d0, SI dx, SI dy) {
  63. path sp= find_innermost_scroll (eb, tp);
  64. searching_forwards= dx == 1 || dy == -1;
  65. int i,d;
  66. path ref_p= tree_path (sp, x0, y0, d0);
  67. if (ref_p != tp) {
  68. tp= ref_p;
  69. return true;
  70. }
  71. // cout << "ref_p = " << ref_p << "\n";
  72. if (ref_p == tree_path (sp, x0, y0, d0+ dx*DELTA)) {
  73. for (i=1; i<DELTA; i=i<<1)
  74. if (ref_p != tree_path (sp, x0+ dx*i, y0+ dy*i, d0+ dx*DELTA))
  75. break;
  76. if (i>=DELTA) return false;
  77. for (d=i>>2; d>=1; d=d>>1)
  78. if (ref_p != tree_path (sp, x0+ dx*(i-d), y0+ dy*(i-d), d0+ dx*DELTA))
  79. i-=d;
  80. x0 += dx*i;
  81. y0 += dy*i;
  82. }
  83. // cout << "path = " << tree_path (sp, x0, y0, d0) << "\n";
  84. if (dx!=0) {
  85. if (ref_p == tree_path (sp, x0, y0, d0)) {
  86. for (i=1; i<DELTA; i=i<<1)
  87. if (ref_p != tree_path (sp, x0, y0, d0+ dx*i)) break;
  88. if (i>=DELTA)
  89. FAILED ("inconsistent cursor handling");
  90. for (d=i>>2; d>=1; d=d>>1)
  91. if (ref_p != tree_path (sp, x0, y0, d0+ dx*(i-d))) i-=d;
  92. d0 += dx*i;
  93. }
  94. else {
  95. for (i=1; i<DELTA; i=i<<1)
  96. if (ref_p == tree_path (sp, x0, y0, d0- dx*i)) break;
  97. if (i<DELTA) {
  98. for (d=i>>2; d>=1; d=d>>1)
  99. if (ref_p == tree_path (sp, x0, y0, d0- dx*(i-d))) i-=d;
  100. i--;
  101. d0 -= dx*i;
  102. }
  103. else { // exceptional case
  104. ref_p= tree_path (sp, x0, y0, d0- dx*DELTA);
  105. for (i=1; i<DELTA; i=i<<1)
  106. if (ref_p == tree_path (sp, x0, y0, d0- dx*i)) break;
  107. for (d=i>>2; d>=1; d=d>>1)
  108. if (ref_p == tree_path (sp, x0, y0, d0- dx*(i-d))) i-=d;
  109. d0 -= dx*i;
  110. }
  111. }
  112. }
  113. tp= tree_path (sp, x0, y0, d0);
  114. return true;
  115. }
  116. void
  117. edit_cursor_rep::cursor_move (SI dx, SI dy) {
  118. //time_t t1= texmacs_time ();
  119. //stretched_print ((tree) eb, false);
  120. cursor_move_sub (mv->ox, mv->oy, mv->delta, dx, dy);
  121. //time_t t2= texmacs_time ();
  122. //if (t2 - t1 >= 10) cout << "cursor_move took " << t2-t1 << "ms\n";
  123. }
  124. /******************************************************************************
  125. * Routines affecting both the cursor and the ghost cursor
  126. ******************************************************************************/
  127. void
  128. edit_cursor_rep::adjust_ghost_cursor (int status) {
  129. if (status==mv_status) {
  130. if (status!=HORIZONTAL) {
  131. mv->ox = cu->ox;
  132. mv->delta= cu->delta;
  133. }
  134. if (status!=VERTICAL)
  135. mv->oy= cu->oy;
  136. }
  137. }
  138. void
  139. edit_cursor_rep::notify_cursor_moved (int status) {
  140. mv_status= status;
  141. cu= eb->find_check_cursor (tp);
  142. notify_change (THE_CURSOR);
  143. }
  144. void
  145. edit_cursor_rep::go_to (SI x, SI y, bool absolute) {
  146. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  147. tp= tree_path (absolute? path (): find_innermost_scroll (eb, tp), x, y, 0);
  148. notify_cursor_moved (CENTER);
  149. mv->ox = x;
  150. mv->oy = y;
  151. mv->delta= 0;
  152. }
  153. void
  154. edit_cursor_rep::go_left_physical () {
  155. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  156. adjust_ghost_cursor (VERTICAL);
  157. cursor_move (-1, 0);
  158. notify_cursor_moved (HORIZONTAL);
  159. select_from_cursor_if_active ();
  160. }
  161. void
  162. edit_cursor_rep::go_right_physical () {
  163. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  164. adjust_ghost_cursor (VERTICAL);
  165. cursor_move (1, 0);
  166. notify_cursor_moved (HORIZONTAL);
  167. select_from_cursor_if_active ();
  168. }
  169. void
  170. edit_cursor_rep::go_up () {
  171. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  172. path scroll_p= find_innermost_scroll (eb, tp);
  173. path start_p= tree_path (scroll_p, -(1 << 30), 1 << 30, 0);
  174. if (tp == start_p) return;
  175. path old_p= tp;
  176. adjust_ghost_cursor (HORIZONTAL);
  177. cursor_move (0, 1);
  178. notify_cursor_moved (VERTICAL);
  179. if (tp == old_p) tp= start_p;
  180. select_from_cursor_if_active ();
  181. }
  182. void
  183. edit_cursor_rep::go_down () {
  184. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  185. path scroll_p= find_innermost_scroll (eb, tp);
  186. path end_p= tree_path (scroll_p, 1 << 30, -(1 << 30), 0);
  187. if (tp == end_p) return;
  188. path old_p= tp;
  189. adjust_ghost_cursor (HORIZONTAL);
  190. cursor_move (0, -1);
  191. notify_cursor_moved (VERTICAL);
  192. if (tp == old_p) tp= end_p;
  193. select_from_cursor_if_active ();
  194. }
  195. void
  196. edit_cursor_rep::go_page_up () {
  197. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  198. path sp= find_innermost_scroll (eb, tp);
  199. if (is_nil (sp)) go_to (mv->ox, min (mv->oy + get_window_height (), eb->y2));
  200. else {
  201. SI x, y, sx, sy;
  202. rectangle outer, inner;
  203. box b= eb[path_up (sp)];
  204. find_canvas_info (eb, sp, x, y, sx, sy, outer, inner);
  205. go_to (mv->ox, min (mv->oy + b->h (), y + sy + inner->y2), false);
  206. }
  207. select_from_cursor_if_active ();
  208. }
  209. void
  210. edit_cursor_rep::go_page_down () {
  211. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  212. path sp= find_innermost_scroll (eb, tp);
  213. if (is_nil (sp)) go_to (mv->ox, max (mv->oy - get_window_height (), eb->y1));
  214. else {
  215. SI x, y, sx, sy;
  216. rectangle outer, inner;
  217. box b= eb[path_up (sp)];
  218. find_canvas_info (eb, sp, x, y, sx, sy, outer, inner);
  219. go_to (mv->ox, max (mv->oy - b->h (), y + sy + inner->y1), false);
  220. }
  221. select_from_cursor_if_active ();
  222. }
  223. /******************************************************************************
  224. * Adapt physical horizontal cursor movement to line breaking
  225. ******************************************************************************/
  226. void
  227. edit_cursor_rep::go_left () {
  228. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  229. path old_tp= copy (tp);
  230. go_left_physical ();
  231. if (tp != old_tp && inside_same (et, old_tp, tp, DOCUMENT)) return;
  232. path p= previous_valid (et, old_tp);
  233. if (rp < p) go_to (p);
  234. select_from_cursor_if_active ();
  235. }
  236. void
  237. edit_cursor_rep::go_right () {
  238. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  239. path old_tp= copy (tp);
  240. go_right_physical ();
  241. if (tp != old_tp && inside_same (et, old_tp, tp, DOCUMENT)) return;
  242. path p= next_valid (et, old_tp);
  243. if (rp < p) go_to (p);
  244. select_from_cursor_if_active ();
  245. }
  246. void
  247. edit_cursor_rep::go_start_line () {
  248. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  249. while (true) {
  250. cursor old_cu= copy (cu);
  251. cursor old_mv= copy (mv);
  252. path old_tp= copy (tp);
  253. go_left_physical ();
  254. if (tp == old_tp || !more_inside (et, tp, old_tp, DOCUMENT)) {
  255. notify_cursor_moved (HORIZONTAL);
  256. cu= old_cu;
  257. mv= old_mv;
  258. tp= old_tp;
  259. select_from_cursor_if_active ();
  260. return;
  261. }
  262. }
  263. }
  264. void
  265. edit_cursor_rep::go_end_line () {
  266. if (has_changed (THE_TREE+THE_ENVIRONMENT)) return;
  267. while (true) {
  268. cursor old_cu= copy (cu);
  269. cursor old_mv= copy (mv);
  270. path old_tp= copy (tp);
  271. go_right_physical ();
  272. if (tp == old_tp || !more_inside (et, tp, old_tp, DOCUMENT)) {
  273. notify_cursor_moved (HORIZONTAL);
  274. cu= old_cu;
  275. mv= old_mv;
  276. tp= old_tp;
  277. select_from_cursor_if_active ();
  278. return;
  279. }
  280. }
  281. }
  282. /******************************************************************************
  283. * Logical cursor changes
  284. ******************************************************************************/
  285. void
  286. edit_cursor_rep::adjust_cursor () {
  287. path sp= find_innermost_scroll (eb, tp);
  288. cursor mv= copy (cu);
  289. SI dx= PIXEL << 8, ddelta= 0;
  290. path p= tree_path (sp, mv->ox, mv->oy, mv->delta);
  291. if (p != tp) {
  292. // cout << "Cursors don't match\n";
  293. while (dx != 0 || ddelta != 0) {
  294. // cout << " " << tp << ", " << p << "\n";
  295. p= tree_path (sp, mv->ox, mv->oy, mv->delta);
  296. int eps= (path_inf (p, tp)? 1: -1);
  297. if (p == tp) eps= (mv->ox < cu->ox? 1: -1);
  298. if (p == tp && mv->ox == cu->ox) eps= (mv->delta < cu->delta? 1: -1);
  299. if (dx > 0) {
  300. if (p != tp ||
  301. tree_path (sp, mv->ox + eps * dx, mv->oy, mv->delta) == tp)
  302. mv->ox += eps * dx;
  303. dx >>= 1;
  304. if (dx == 0) ddelta= DELTA;
  305. }
  306. else if (ddelta > 0) {
  307. if (p != tp ||
  308. tree_path (sp, mv->ox, mv->oy, mv->delta + eps * ddelta) == tp)
  309. mv->delta += eps * ddelta;
  310. ddelta >>= 1;
  311. }
  312. }
  313. }
  314. if (p == tp) cu= mv;
  315. }
  316. void
  317. edit_cursor_rep::go_to_here () {
  318. cu= eb->find_check_cursor (tp);
  319. if (!cu->valid || !valid_cursor (et, tp)) {
  320. tp= super_correct (et, tp);
  321. cu= eb->find_check_cursor (tp);
  322. }
  323. if (!cu->valid || !valid_cursor (et, tp)) {
  324. tp= make_cursor_accessible (tp, false);
  325. cu= eb->find_check_cursor (tp);
  326. }
  327. if (cu->valid) adjust_cursor ();
  328. if (mv_status == DIRECT) mv= copy (cu);
  329. notify_change (THE_CURSOR);
  330. }
  331. void
  332. edit_cursor_rep::go_to (path p) {
  333. if (rp <= p) {
  334. tp= p;
  335. mv_status= DIRECT;
  336. if (!has_changed (THE_TREE+THE_ENVIRONMENT)) {
  337. cu= eb->find_check_cursor (tp);
  338. if (cu->valid) adjust_cursor ();
  339. mv= copy (cu);
  340. }
  341. notify_change (THE_CURSOR);
  342. }
  343. }
  344. void
  345. edit_cursor_rep::go_to_correct (path p) {
  346. p= correct_cursor (et, p);
  347. go_to (p);
  348. }
  349. void
  350. edit_cursor_rep::go_to_start (path p) {
  351. go_to (start (et, p));
  352. }
  353. void
  354. edit_cursor_rep::go_to_end (path p) {
  355. go_to (end (et, p));
  356. }
  357. void
  358. edit_cursor_rep::go_to_border (path p, bool at_start) {
  359. if (at_start) go_to_start (p);
  360. else go_to_end (p);
  361. }
  362. void
  363. edit_cursor_rep::go_start () {
  364. go_to (correct_cursor (et, rp * 0));
  365. select_from_cursor_if_active ();
  366. }
  367. void
  368. edit_cursor_rep::go_end () {
  369. go_to (correct_cursor (et, rp * 1));
  370. select_from_cursor_if_active ();
  371. }
  372. void
  373. edit_cursor_rep::go_start_paragraph () {
  374. path p= search_parent_upwards (DOCUMENT);
  375. go_to (start (et, p));
  376. select_from_cursor_if_active ();
  377. }
  378. void
  379. edit_cursor_rep::go_end_paragraph () {
  380. path p= search_parent_upwards (DOCUMENT);
  381. go_to (end (et, p));
  382. select_from_cursor_if_active ();
  383. }
  384. void
  385. edit_cursor_rep::go_start_of (tree_label what) {
  386. path p= search_upwards (what);
  387. if (!is_nil (p)) go_to (start (et, p));
  388. }
  389. void
  390. edit_cursor_rep::go_end_of (tree_label what) {
  391. path p= search_upwards (what);
  392. if (!is_nil (p)) go_to (end (et, p));
  393. }
  394. void
  395. edit_cursor_rep::go_start_with (string var, string val) {
  396. path p= search_upwards_with (var, val);
  397. if (!is_nil (p)) go_to (start (et, p));
  398. }
  399. void
  400. edit_cursor_rep::go_end_with (string var, string val) {
  401. path p= search_upwards_with (var, val);
  402. if (!is_nil (p)) go_to (end (et, p));
  403. }
  404. /******************************************************************************
  405. * Jumping to a label
  406. ******************************************************************************/
  407. tree
  408. edit_cursor_rep::get_labels () {
  409. tree r (TUPLE);
  410. hashmap<string,tree> h= buf->ref;
  411. if (buf->prj != NULL) {
  412. h= copy (buf->prj->ref);
  413. h->join (buf->ref);
  414. }
  415. iterator<string> it= iterate (h);
  416. while (it->busy ()) {
  417. string ref= it->next ();
  418. r << ref;
  419. }
  420. return r;
  421. }
  422. static path
  423. search_label (tree t, string which) {
  424. if (is_atomic (t)) return path ();
  425. else if (t == tree (LABEL, which)) return path (1);
  426. else if (is_compound (t, "tag", 2) && t[0] == which)
  427. return path (1, start (t[1]));
  428. else {
  429. int i, n=N(t);
  430. for (i=0; i<n; i++) {
  431. path q= search_label (t[i], which);
  432. if (!is_nil (q)) return path (i, q);
  433. }
  434. return path ();
  435. }
  436. }
  437. void
  438. edit_cursor_rep::show_cursor_if_hidden () {
  439. if (!is_accessible_cursor (et, tp) && !in_source ()) {
  440. eval ("(use-modules (utils edit variants))");
  441. eval ("(cursor-show-hidden)");
  442. }
  443. }
  444. void
  445. edit_cursor_rep::go_to_label (string s) {
  446. path p= search_label (et, s);
  447. if (!is_nil (p)) {
  448. go_to (p);
  449. show_cursor_if_hidden ();
  450. return;
  451. }
  452. if (!is_nil (eb)) {
  453. p= eb->find_tag (s);
  454. if (!is_nil (p)) {
  455. go_to (p);
  456. show_cursor_if_hidden ();
  457. return;
  458. }
  459. }
  460. tree val= (buf->prj==NULL? buf->ref[s]: buf->prj->ref[s]);
  461. if (is_func (val, TUPLE, 3) && is_atomic (val[2])) {
  462. string extra= val[2]->label;
  463. if (starts (extra, "#")) {
  464. string part= extra (1, N (extra));
  465. int i= search_forwards (".", part);
  466. if (i >= 0) part= part (0, i);
  467. string show= "(show-hidden-part " * scm_quote (part) * ")";
  468. string jump= "(go-to-label " * scm_quote (s) * ")";
  469. exec_delayed (scheme_cmd ("(if " * show * " (delayed " * jump * "))"));
  470. }
  471. else {
  472. url u= relative (buf->name, url (extra));
  473. if (u != buf->name) {
  474. string new_buf = scm_quote (as_string (u));
  475. string load_buf= "(load-buffer (url-system " * new_buf * "))";
  476. string jump_to = "(go-to-label " * scm_quote (s) * ")";
  477. exec_delayed (scheme_cmd ("(begin " * load_buf * " " * jump_to * ")"));
  478. }
  479. }
  480. }
  481. }