1 
2 /******************************************************************************
3 * MODULE     : edit_interface.cpp
4 * DESCRIPTION: interface between the editor and the window manager
5 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "Interface/edit_interface.hpp"
13 #include "file.hpp"
14 #include "convert.hpp"
15 #include "server.hpp"
16 #include "tm_window.hpp"
17 #include "Metafont/tex_files.hpp"
18 #include "data_cache.hpp"
19 #include "drd_mode.hpp"
20 #include "message.hpp"
21 #include "tree_traverse.hpp"
22 #include "boot.hpp"
23 #ifdef EXPERIMENTAL
24 #include "../../Style/Evaluate/evaluate_main.hpp"
25 #endif
26 #include "gui.hpp" // for gui_interrupted
27 
28 extern void (*env_next_prog)(void);
29 
30 /*static*/ string
MODE_LANGUAGE(string mode)31 MODE_LANGUAGE (string mode) {
32   if (mode == "text") return LANGUAGE;
33   else if (mode == "math") return MATH_LANGUAGE;
34   else if (mode == "prog") return PROG_LANGUAGE;
35   else if (mode == "src") return LANGUAGE;
36   std_error << "Invalid mode " << mode << ", assuming text mode instead\n";
37   return LANGUAGE;
38 }
39 
40 /******************************************************************************
41 * Main edit_interface routines
42 ******************************************************************************/
43 
edit_interface_rep()44 edit_interface_rep::edit_interface_rep ():
45   env_change (0),
46   last_change (texmacs_time()), last_update (last_change-1),
47   do_animate (false), next_animate (last_change-1),
48   full_screen (false), got_focus (false),
49   sh_s (""), sh_mark (0), pre_edit_s (""), pre_edit_mark (0),
50   popup_win (),
51   message_l (""), message_r (""), last_l (""), last_r (""),
52   zoomf (sv->get_default_zoom_factor ()),
53   magf (zoomf / std_shrinkf),
54   pixel ((SI) tm_round ((std_shrinkf * PIXEL) / zoomf)), copy_always (),
55   last_x (0), last_y (0), last_t (0),
56   table_selection (false), mouse_adjusting (false),
57   oc (0, 0), temp_invalid_cursor (false),
58   shadow (NULL), stored (NULL),
59   cur_sb (2), cur_wb (2)
60 {
61   input_mode= INPUT_NORMAL;
62   gui_root_extents (cur_wx, cur_wy);
63 }
64 
~edit_interface_rep()65 edit_interface_rep::~edit_interface_rep () {
66   if (shadow != NULL) tm_delete (shadow);
67   if (stored != NULL) tm_delete (stored);
68   shadow = NULL;
69   stored = NULL;
70 }
71 
operator tree()72 edit_interface_rep::operator tree () {
73   return tuple ("editor", as_string (get_name ()));
74 }
75 
76 void
suspend()77 edit_interface_rep::suspend () {
78   if (got_focus) {
79     interrupt_shortcut ();
80     set_message ("", "", false);
81   }
82   got_focus= false;
83   notify_change (THE_FOCUS);
84   if (shadow != NULL) tm_delete (shadow);
85   if (stored != NULL) tm_delete (stored);
86   shadow = NULL;
87   stored = NULL;
88 }
89 
90 void
resume()91 edit_interface_rep::resume () {
92   got_focus= true;
93   SERVER (menu_main ("(horizontal (link texmacs-menu))"));
94   SERVER (menu_icons (0, "(horizontal (link texmacs-main-icons))"));
95   SERVER (menu_icons (1, "(horizontal (link texmacs-mode-icons))"));
96   SERVER (menu_icons (2, "(horizontal (link texmacs-focus-icons))"));
97   SERVER (menu_icons (3, "(horizontal (link texmacs-extra-icons))"));
98   if (use_side_tools)
99     { SERVER (side_tools (0, "(vertical (link texmacs-side-tools))")); }
100   SERVER (bottom_tools (0, "(vertical (link texmacs-bottom-tools))"));
101   cur_sb= 2;
102   notify_change (THE_FOCUS + THE_EXTENTS);
103   path new_tp= make_cursor_accessible (tp, true);
104   if (new_tp != tp) {
105     notify_change (THE_CURSOR);
106     tp= new_tp;
107   }
108 }
109 
110 void
keyboard_focus_on(string field)111 edit_interface_rep::keyboard_focus_on (string field) {
112   array<url> a= buffer_to_windows (buf->buf->name);
113   if (N(a) >= 1) {
114     tm_window win= concrete_window (a[0]);
115     send_keyboard_focus_on (win->wid, field);
116   }
117 }
118 
119 /******************************************************************************
120 * Routines for dealing with shrinked coordinates
121 ******************************************************************************/
122 
123 int
get_pixel_size()124 edit_interface_rep::get_pixel_size () {
125   return pixel;
126 }
127 
128 void
set_zoom_factor(double zoom)129 edit_interface_rep::set_zoom_factor (double zoom) {
130   zoomf = zoom;
131   magf  = zoomf / std_shrinkf;
132   pixel = (int) tm_round ((std_shrinkf * PIXEL) / zoomf);
133 }
134 
135 void
invalidate(SI x1,SI y1,SI x2,SI y2)136 edit_interface_rep::invalidate (SI x1, SI y1, SI x2, SI y2) {
137   send_invalidate (this, (SI) floor (x1*magf), (SI) floor (y1*magf),
138                          (SI) ceil  (x2*magf), (SI) ceil  (y2*magf));
139 }
140 
141 void
invalidate(rectangles rs)142 edit_interface_rep::invalidate (rectangles rs) {
143   while (!is_nil (rs)) {
144     invalidate (rs->item->x1-pixel, rs->item->y1-pixel,
145 		rs->item->x2+pixel, rs->item->y2+pixel);
146     rs= rs->next;
147   }
148 }
149 
150 void
invalidate_all()151 edit_interface_rep::invalidate_all () {
152   send_invalidate_all (this);
153 }
154 
155 void
update_visible()156 edit_interface_rep::update_visible () {
157   SERVER (get_visible (vx1, vy1, vx2, vy2));
158   vx1= (SI) (vx1 / magf); vy1= (SI) (vy1 / magf);
159   vx2= (SI) (vx2 / magf); vy2= (SI) (vy2 / magf);
160 }
161 
162 SI
get_visible_width()163 edit_interface_rep::get_visible_width () {
164   update_visible ();
165   return vx2 - vx1;
166 }
167 
168 SI
get_visible_height()169 edit_interface_rep::get_visible_height () {
170   update_visible ();
171   return vy2 - vy1;
172 }
173 
174 #ifdef QTTEXMACS
175 #include <QApplication>
176 #include <QStyle>
177 static SI
scrollbar_width()178 scrollbar_width () {
179   return (qApp->style()->pixelMetric (QStyle::PM_ScrollBarExtent) + 2) * PIXEL;
180 }
181 #else
182 static SI
scrollbar_width()183 scrollbar_width () {
184   return 20 * PIXEL;
185 }
186 #endif
187 
188 SI
get_window_width()189 edit_interface_rep::get_window_width () {
190   SI w, h;
191   widget me= ::get_canvas (widget (cvw));
192   ::get_size (me, w, h);
193   bool sb= (get_init_string (SCROLL_BARS) != "false");
194   if (full_screen) {
195     string medium= get_init_string (PAGE_MEDIUM);
196     if (medium == "automatic" || medium == "beamer") sb= false;
197   }
198   if (sb) w -= scrollbar_width ();
199   return w;
200 }
201 
202 SI
get_window_height()203 edit_interface_rep::get_window_height () {
204   SI w, h;
205   widget me= ::get_canvas (widget (cvw));
206   ::get_size (me, w, h);
207   return h;
208 }
209 
210 void
scroll_to(SI x,SI y)211 edit_interface_rep::scroll_to (SI x, SI y) {
212   stored_rects= rectangles ();
213   copy_always = rectangles ();
214   SERVER (scroll_to ((SI) (x * magf), ((SI) (y * magf))));
215 }
216 
217 void
set_extents(SI x1,SI y1,SI x2,SI y2)218 edit_interface_rep::set_extents (SI x1, SI y1, SI x2, SI y2) {
219   stored_rects= rectangles ();
220   copy_always = rectangles ();
221   SERVER (set_extents ((SI) floor (x1*magf), (SI) floor (y1*magf),
222                        (SI) ceil  (x2*magf), (SI) ceil  (y2*magf)));
223 }
224 
225 /******************************************************************************
226 * Scroll so as to make the cursor and the selection visible
227 ******************************************************************************/
228 
229 void
cursor_visible()230 edit_interface_rep::cursor_visible () {
231   path sp= find_innermost_scroll (eb, tp);
232   cursor cu= get_cursor ();
233   if (is_nil (sp)) {
234     update_visible ();
235     cu->y1 -= 2*pixel; cu->y2 += 2*pixel;
236     if ((cu->ox+ ((SI) (cu->y1 * cu->slope)) <  vx1) ||
237 	(cu->ox+ ((SI) (cu->y2 * cu->slope)) >= vx2) ||
238 	(cu->oy+ cu->y1 <  vy1) ||
239 	(cu->oy+ cu->y2 >= vy2))
240       {
241 	scroll_to (cu->ox, cu->oy);
242 	send_invalidate_all (this);
243       }
244   }
245   else {
246     SI x, y, sx, sy;
247     rectangle outer, inner;
248     find_canvas_info (eb, sp, x, y, sx, sy, outer, inner);
249     if ((cu->ox+ ((SI) (cu->y1 * cu->slope)) < x + outer->x1) ||
250 	(cu->ox+ ((SI) (cu->y2 * cu->slope)) > x + outer->x2))
251       {
252 	SI tx= inner->x2 - inner->x1;
253 	SI cx= outer->x2 - outer->x1;
254 	if (tx > cx) {
255 	  SI outer_cx= cu->ox - x;
256 	  SI inner_cx= outer_cx - sx;
257 	  SI dx= inner_cx - inner->x1;
258 	  double p= 100.0 * ((double) (dx - (cx>>1))) / ((double) (tx-cx));
259 	  p= max (min (p, 100.0), 0.0);
260 	  tree old_xt= eb[path_up (sp)]->get_info ("scroll-x");
261 	  tree new_xt= as_string (p) * "%";
262 	  if (new_xt != old_xt && is_accessible (obtain_ip (old_xt))) {
263 	    object fun= symbol_object ("tree-set");
264 	    object cmd= list_object (fun, old_xt, new_xt);
265 	    exec_delayed (scheme_cmd (cmd));
266 	    temp_invalid_cursor= true;
267 	  }
268 	}
269       }
270     if ((cu->oy+ cu->y1 < y + outer->y1) ||
271 	(cu->oy+ cu->y2 > y + outer->y2))
272       {
273 	SI ty= inner->y2 - inner->y1;
274 	SI cy= outer->y2 - outer->y1;
275 	if (ty > cy) {
276 	  SI outer_cy= cu->oy + ((cu->y1 + cu->y2) >> 1) - y;
277 	  SI inner_cy= outer_cy - sy;
278 	  SI dy= inner_cy - inner->y1;
279 	  double p= 100.0 * ((double) (dy - (cy>>1))) / ((double) (ty-cy));
280 	  p= max (min (p, 100.0), 0.0);
281 	  tree old_yt= eb[path_up (sp)]->get_info ("scroll-y");
282 	  tree new_yt= as_string (p) * "%";
283 	  if (new_yt != old_yt && is_accessible (obtain_ip (old_yt))) {
284 	    object fun= symbol_object ("tree-set");
285 	    object cmd= list_object (fun, old_yt, new_yt);
286 	    exec_delayed (scheme_cmd (cmd));
287 	    temp_invalid_cursor= true;
288 	  }
289 	}
290       }
291   }
292 }
293 
294 void
selection_visible()295 edit_interface_rep::selection_visible () {
296   update_visible ();
297   if ((vx2 - vx1 <= 80*pixel) || (vy2 - vy1 <= 80*pixel)) return;
298 
299   SI extra= (cur_sb == 1? 20 * pixel: 0);
300   bool scroll_x= (end_x < vx1 + extra) || (end_x >= vx2 - extra);
301   bool scroll_y= (end_y < vy1 + extra) || (end_y >= vy2 - extra);
302   SI new_x= vx1;
303   if (scroll_x) new_x= end_x - ((vx2-vx1)>>1);
304   SI new_y= vy2;
305   if (scroll_y) new_y= end_y + ((vy2-vy1)>>1);
306 
307   if (scroll_x || scroll_y) {
308     scroll_to (new_x, new_y);
309     send_invalidate_all (this);
310     SI old_vx1= vx1, old_vy1= vy1;
311     update_visible ();
312     end_x += vx1- old_vx1;
313     end_y += vy1- old_vy1;
314   }
315 }
316 
317 /******************************************************************************
318 * Computation of environment rectangles
319 ******************************************************************************/
320 
321 static bool
is_graphical(tree t)322 is_graphical (tree t) {
323   return
324     is_func (t, _POINT) ||
325     is_func (t, LINE) || is_func (t, CLINE) ||
326     is_func (t, ARC) || is_func (t, CARC) ||
327     is_func (t, SPLINE) || is_func (t, CSPLINE);
328 }
329 
330 static void
correct_adjacent(rectangles & rs1,rectangles & rs2)331 correct_adjacent (rectangles& rs1, rectangles& rs2) {
332   if (N(rs1) != 1 || N(rs2) != 1) return;
333   SI bot1= rs1->item->y1;
334   SI top2= rs2->item->y2;
335   if (rs1->item->y1 <= rs2->item->y1) {
336     //cout << "Discard " << rs1->item->y1 << ", " << rs2->item->y1 << "\n";
337     return;
338   }
339   if (rs1->item->y2 <= rs2->item->y2) {
340     //cout << "Discard " << rs1->item->y2 << ", " << rs2->item->y2 << "\n";
341     return;
342   }
343   SI mid= (bot1 + top2) >> 1;
344   rs1->item->y1= mid;
345   rs2->item->y2= mid;
346 }
347 
348 void
compute_env_rects(path p,rectangles & rs,bool recurse)349 edit_interface_rep::compute_env_rects (path p, rectangles& rs, bool recurse) {
350   if (p == rp) return;
351   tree st= subtree (et, p);
352   if ((is_func (st, TABLE) || is_func (st, SUBTABLE)) &&
353       recurse && get_preference ("show table cells") == "on") {
354     rectangles rl;
355     for (int i=0; i<N(st); i++) {
356       if (is_func (st[i], ROW))
357         for (int j=0; j<N(st[i]); j++) {
358           selection sel= eb->find_check_selection (p*i*j*0, p*i*j*1);
359           rectangles rsel= copy (thicken (sel->rs, 0, 2 * pixel));
360           if (i > 0 && is_func (st[i-1], ROW) && j < N(st[i-1])) {
361             selection bis= eb->find_check_selection (p*(i-1)*j*0, p*(i-1)*j*1);
362             rectangles rbis= copy (thicken (bis->rs, 0, 2 * pixel));
363             correct_adjacent (rbis, rsel);
364           }
365           if (i+1 < N(st) && is_func (st[i+1], ROW) && j < N(st[i+1])) {
366             selection bis= eb->find_check_selection (p*(i+1)*j*0, p*(i+1)*j*1);
367             rectangles rbis= copy (thicken (bis->rs, 0, 2 * pixel));
368             correct_adjacent (rsel, rbis);
369           }
370           rectangles selp= thicken (rsel,  pixel/2,  pixel/2);
371           rectangles selm= thicken (rsel, -pixel/2, -pixel/2);
372           rl << simplify (::correct (selp - selm));
373         }
374     }
375     rs << simplify (rl);
376     if (recurse) compute_env_rects (path_up (p), rs, recurse);
377   }
378   else if (is_atomic (st) ||
379            drd->is_child_enforcing (st) ||
380            //is_document (st) || is_concat (st) ||
381            is_func (st, TABLE) || is_func (st, SUBTABLE) ||
382            is_func (st, ROW) || is_func (st, TFORMAT) ||
383            is_graphical (st) ||
384            (is_func (st, WITH) && is_graphical (st[N(st)-1])) ||
385            (is_func (st, WITH) && is_graphical_text (st[N(st)-1])) ||
386            is_compound (st, "shared", 3) ||
387            (is_compound (st, "math", 1) &&
388             is_compound (subtree (et, path_up (p)), "input")))
389     compute_env_rects (path_up (p), rs, recurse);
390   else {
391     int new_mode= DRD_ACCESS_NORMAL;
392     if (get_init_string (MODE) == "src") new_mode= DRD_ACCESS_SOURCE;
393     int old_mode= set_access_mode (new_mode);
394     tree st= subtree (et, p);
395     if (is_accessible_cursor (et, p * right_index (st)) || in_source ()) {
396       bool right;
397       path p1= p * 0, p2= p * 1, q1, q2;
398       if (is_script (subtree (et, p), right) ||
399           is_func (st, TEXT_AT) ||
400           is_func (st, MATH_AT))
401         {
402           p1= start (et, p * 0);
403           p2= end   (et, p * 0);
404         }
405       if (is_func (st, CELL)) { q1= p1; q2= p2; }
406       else selection_correct (p1, p2, q1, q2);
407       selection sel= eb->find_check_selection (q1, q2);
408       if (N(focus_get ()) >= N(p))
409         if (!recurse || get_preference ("show full context") == "on")
410           rs << outline (sel->rs, pixel);
411     }
412     set_access_mode (old_mode);
413     if (recurse || N(rs) == 0)
414       compute_env_rects (path_up (p), rs, recurse);
415   }
416 }
417 
418 /******************************************************************************
419 * handling changes
420 ******************************************************************************/
421 
422 void
notify_change(int change)423 edit_interface_rep::notify_change (int change) {
424   env_change= env_change | change;
425   needs_update ();
426   if ((change & (THE_TREE | THE_SELECTION | THE_CURSOR)) != 0)
427     manual_focus_set (path (), (change & THE_TREE) != 0);
428 }
429 
430 bool
has_changed(int question)431 edit_interface_rep::has_changed (int question) {
432   return (env_change & question) != 0;
433 }
434 
435 int
idle_time(int event_type)436 edit_interface_rep::idle_time (int event_type) {
437   if (env_change == 0 &&
438       got_focus &&
439       (!query_invalid (this)) &&
440       (!check_event (event_type)))
441     return texmacs_time () - last_change;
442   else return 0;
443 }
444 
445 int
change_time()446 edit_interface_rep::change_time () {
447   return last_change;
448 }
449 
450 void
apply_changes()451 edit_interface_rep::apply_changes () {
452   //cout << "Apply changes\n";
453   //cout << "et= " << et << "\n";
454   //cout << "tp= " << tp << "\n";
455   //cout << HRULE << "\n";
456   if (env_change == 0) {
457     if (last_change-last_update > 0 &&
458         idle_time (INTERRUPTED_EVENT) >= 1000/6)
459     {
460       SERVER (menu_main ("(horizontal (link texmacs-menu))"));
461       SERVER (menu_icons (0, "(horizontal (link texmacs-main-icons))"));
462       SERVER (menu_icons (1, "(horizontal (link texmacs-mode-icons))"));
463       SERVER (menu_icons (2, "(horizontal (link texmacs-focus-icons))"));
464       SERVER (menu_icons (3, "(horizontal (link texmacs-extra-icons))"));
465       if (use_side_tools)
466         { SERVER (side_tools (0, "(vertical (link texmacs-side-tools))")); }
467       SERVER (bottom_tools (0, "(vertical (link texmacs-bottom-tools))"));
468       set_footer ();
469       if (has_current_window ()) {
470         array<url> ws= buffer_to_windows (
471                          window_to_buffer (
472                            abstract_window (concrete_window ())));
473         int n= N(ws);
474         bool ns= need_save ();
475         for (int i=0; i<n; i++)
476           concrete_window (ws[i])->set_modified (ns);
477       }
478       if (!gui_interrupted ()) drd_update ();
479       cache_memorize ();
480       last_update= last_change;
481       save_user_preferences ();
482     }
483     return;
484   }
485 
486   // cout << "Applying changes " << env_change << " to " << get_name() << "\n";
487   // time_t t1= texmacs_time ();
488 
489   // cout << "Always\n";
490   update_visible ();
491 
492   // cout << "Handling automatic resizing\n";
493   int sb= 1;
494   if (is_attached (this) && has_current_window ()) {
495     tree new_zoom= as_string (zoomf);
496     tree old_zoom= get_init_value (ZOOM_FACTOR);
497     if (new_zoom != old_zoom) {
498       init_env (ZOOM_FACTOR, new_zoom);
499       notify_change (THE_ENVIRONMENT);
500     }
501   }
502   if (is_attached (this) &&
503       has_current_window () &&
504       get_init_string (PAGE_MEDIUM) == "automatic")
505     {
506       SI wx, wy;
507       if (cvw == NULL) ::get_size (get_window (this), wx, wy);
508       else ::get_size (widget (cvw), wx, wy);
509       if (get_init_string (SCROLL_BARS) == "false") sb= 0;
510       if (get_server () -> in_full_screen_mode ()) sb= 0;
511       if (sb) wx -= scrollbar_width();
512       if (wx != cur_wx || wy != cur_wy) {
513 	cur_wx= wx; cur_wy= wy;
514 	init_env (PAGE_SCREEN_WIDTH, as_string ((SI) (wx/magf)) * "tmpt");
515 	init_env (PAGE_SCREEN_HEIGHT, as_string ((SI) (wy/magf)) * "tmpt");
516 	notify_change (THE_ENVIRONMENT);
517       }
518     }
519   if (get_init_string (PAGE_MEDIUM) == "beamer" && full_screen) sb= 0;
520   if (sb != cur_sb) {
521     cur_sb= sb;
522     if (has_current_window ())
523       concrete_window () -> set_scrollbars (sb);
524   }
525 
526   // window decorations (menu bar, icon bars, footer)
527   int wb= 2;
528   if (is_attached (this)) {
529     string val= get_init_string (WINDOW_BARS);
530     if (val == "auto") wb= 2;
531     else if (val == "false") wb= 0;
532     else if (val == "true") wb= 1;
533     if (wb != cur_wb) {
534       cur_wb= wb;
535       if (wb != 2) {
536         get_server () -> show_header (wb);
537         get_server () -> show_footer (wb);
538       }
539     }
540   }
541 
542   // cout << "Handling selection\n";
543   if (env_change & (THE_TREE+THE_ENVIRONMENT+THE_SELECTION)) {
544     if (!is_nil (selection_rects)) {
545       invalidate (selection_rects);
546       if (!selection_active_any ()) {
547         set_selection (tp, tp);
548         selection_rects= rectangles ();
549       }
550     }
551     if (N (alt_selection_rects) != 0) {
552       rectangles visible (rectangle (vx1, vy1, vx2, vy2));
553       for (int i=0; i<N(alt_selection_rects); i++)
554         invalidate (alt_selection_rects[i] & visible);
555       range_set alt_sel= append (get_alt_selection ("alternate"),
556                                  get_alt_selection ("brackets"));
557       if (is_empty (alt_sel))
558         alt_selection_rects= array<rectangles> ();
559     }
560   }
561 
562   // cout << "Handling environment\n";
563   if (env_change & THE_ENVIRONMENT)
564     typeset_invalidate_all ();
565 
566   // cout << "Handling tree\n";
567   if (env_change & (THE_TREE+THE_ENVIRONMENT)) {
568     typeset_invalidate_env ();
569     SI x1, y1, x2, y2;
570     typeset (x1, y1, x2, y2);
571     invalidate (x1- 2*pixel, y1- 2*pixel, x2+ 2*pixel, y2+ 2*pixel);
572     // check_data_integrety ();
573     the_ghost_cursor()= eb->find_check_cursor (tp);
574   }
575 
576 #ifdef EXPERIMENTAL
577   if (env_change & THE_ENVIRONMENT)
578     environment_update ();
579   if (env_change & THE_TREE) {
580     cout << HRULE;
581     mem= evaluate (ste, cct);
582     tree rew= mem->get_tree ();
583     cout << HRULE;
584     cout << tree_to_texmacs (rew) << LF;
585     //print_tree (rew);
586   }
587 #endif
588 
589   // cout << "Handling extents\n";
590   if (env_change & (THE_TREE+THE_ENVIRONMENT+THE_EXTENTS)) {
591     string medium= get_init_string (PAGE_MEDIUM);
592     SI ex1= (SI) (((double) eb->x1) * magf);
593     SI ey1= (SI) (((double) eb->y1) * magf);
594     SI ex2= (SI) (((double) eb->x2) * magf);
595     SI ey2= (SI) (((double) eb->y2) * magf);
596     abs_round (ex1, ey1);
597     abs_round (ex2, ey2);
598     SI w, h;
599     widget me= ::get_canvas (widget (cvw));
600     ::get_size (me, w, h);
601 #ifdef X11TEXMACS
602     w -= 2*PIXEL;
603     h -= 2*PIXEL;
604 #endif
605     if (cur_sb && ey2 - ey1 > h) w -= scrollbar_width ();
606     if (cur_sb && ex2 - ex1 > w) h -= scrollbar_width ();
607     if (ex2 - ex1 <= w + 2*PIXEL) {
608       if (medium == "automatic" ||
609           (medium == "beamer" && full_screen))
610         ex2= ex1 + w;
611       else {
612 #ifdef X11TEXMACS
613         ex1= (ex1 + ex2 - w) / 2;
614         abs_round (ex1);
615         ex2= ex1 + w;
616 #endif
617       }
618     }
619     if (ey2 - ey1 <= h + 2*PIXEL) {
620       if (medium == "papyrus" || medium == "automatic" ||
621           (medium == "beamer" && full_screen))
622         ey1= ey2 - h;
623       else {
624 #ifdef X11TEXMACS
625         ey1= (ey1 + ey2 - h) / 2;
626         abs_round (ey1);
627         ey2= ey1 + h;
628 #endif
629       }
630     }
631     SERVER (set_extents (ex1, ey1, ex2, ey2));
632     //set_extents (eb->x1, eb->y1, eb->x2, eb->y2);
633   }
634 
635   // cout << "Cursor\n";
636   temp_invalid_cursor= false;
637   if (env_change & (THE_TREE+THE_ENVIRONMENT+THE_EXTENTS+
638                     THE_CURSOR+THE_SELECTION+THE_FOCUS)) {
639     SI /*P1= pixel,*/ P2= 2*pixel, P3= 3*pixel;
640     int THE_CURSOR_BAK= env_change & THE_CURSOR;
641     go_to_here ();
642     env_change= (env_change & (~THE_CURSOR)) | THE_CURSOR_BAK;
643     if (env_change & (THE_TREE+THE_ENVIRONMENT+THE_EXTENTS+THE_CURSOR))
644       if (!inside_active_graphics ())
645         cursor_visible ();
646 
647     cursor cu= get_cursor();
648     rectangle ocr (oc->ox+ ((SI) (oc->y1*oc->slope))- P3, oc->oy+ oc->y1- P3,
649                    oc->ox+ ((SI) (oc->y2*oc->slope))+ P2, oc->oy+ oc->y2+ P3);
650     copy_always= rectangles (ocr, copy_always);
651     invalidate (ocr->x1, ocr->y1, ocr->x2, ocr->y2);
652     rectangle ncr (cu->ox+ ((SI) (cu->y1*cu->slope))- P3, cu->oy+ cu->y1- P3,
653                    cu->ox+ ((SI) (cu->y2*cu->slope))+ P2, cu->oy+ cu->y2+ P3);
654     invalidate (ncr->x1, ncr->y1, ncr->x2, ncr->y2);
655     copy_always= rectangles (ncr, copy_always);
656     oc= copy (cu);
657 
658     // set hot spot in the gui
659     send_cursor (this, (SI) floor (cu->ox * magf),
660                        (SI) floor (cu->oy * magf));
661 
662     path sp= selection_get_cursor_path ();
663     bool semantic_flag= semantic_active (path_up (sp));
664     bool full_context= (get_preference ("show full context") == "on");
665     bool table_cells= (get_preference ("show table cells") == "on");
666     bool show_focus= (get_preference ("show focus") == "on");
667     bool semantic_only= (get_preference ("show only semantic focus") == "on");
668     rectangles old_env_rects= env_rects;
669     rectangles old_foc_rects= foc_rects;
670     env_rects= rectangles ();
671     foc_rects= rectangles ();
672     path pp= path_up (tp);
673     tree pt= subtree (et, pp);
674     if (none_accessible (pt));
675     else pp= path_up (pp);
676     if (full_context || table_cells)
677       compute_env_rects (pp, env_rects, true);
678     if (show_focus && (!semantic_flag || !semantic_only))
679       compute_env_rects (pp, foc_rects, false);
680     if (env_rects != old_env_rects) {
681       invalidate (old_env_rects);
682       invalidate (env_rects);
683     }
684     else if (env_change & THE_FOCUS) invalidate (env_rects);
685     if (foc_rects != old_foc_rects) {
686       invalidate (old_foc_rects);
687       invalidate (foc_rects);
688     }
689     else if (env_change & THE_FOCUS) invalidate (foc_rects);
690 
691     rectangles old_sem_rects= sem_rects;
692     bool old_sem_correct= sem_correct;
693     sem_rects= rectangles ();
694     sem_correct= true;
695     if (semantic_flag && show_focus) {
696       path sp= selection_get_cursor_path ();
697       path p1= tp, p2= tp;
698       if (selection_active_any ()) selection_get (p1, p2);
699       sem_correct= semantic_select (path_up (sp), p1, p2, 2);
700       if (!sem_correct) {
701         path sr= semantic_root (path_up (sp));
702         p1= start (et, sr);
703         p2= end (et, sr);
704       }
705       path q1, q2;
706       selection_correct (p1, p2, q1, q2);
707       selection sel= eb->find_check_selection (q1, q2);
708       sem_rects << outline (sel->rs, pixel);
709     }
710     if (sem_rects != old_sem_rects || sem_correct != old_sem_correct) {
711       invalidate (old_sem_rects);
712       invalidate (sem_rects);
713     }
714     else if (env_change & THE_FOCUS) invalidate (sem_rects);
715 
716     invalidate_graphical_object ();
717   }
718 
719   // cout << "Handling selection\n";
720   if (env_change & THE_SELECTION) {
721     if (selection_active_any ()) {
722       table_selection= selection_active_table ();
723       selection sel; selection_get (sel);
724       rectangles rs= thicken (sel->rs, pixel, 3*pixel);
725 #ifndef QTTEXMACS
726       rs= simplify (::correct (rs - thicken (rs, -pixel, -pixel)));
727 #endif
728       selection_rects= rs;
729       invalidate (selection_rects);
730     }
731     range_set alt_sel= append (get_alt_selection ("alternate"),
732                                get_alt_selection ("brackets"));
733     if (!is_empty (alt_sel)) {
734       alt_selection_rects= array<rectangles> ();
735       for (int i=0; i+1<N(alt_sel); i+=2) {
736         range_set sub_sel= simple_range (alt_sel[i], alt_sel[i+1]);
737         selection sel= compute_selection (sub_sel);
738         rectangles rs= thicken (sel->rs, pixel, 3*pixel);
739 #ifndef QTTEXMACS
740         rs= simplify (::correct (rs - thicken (rs, -pixel, -pixel)));
741 #endif
742         if (N(rs) != 0) alt_selection_rects << rs;
743       }
744       rectangles visible (rectangle (vx1, vy1, vx2, vy2));
745       for (int i=0; i<N(alt_selection_rects); i++)
746         invalidate (alt_selection_rects[i] & visible);
747     }
748   }
749 
750   // cout << "Handling locus highlighting\n";
751   if (env_change & (THE_TREE+THE_ENVIRONMENT+THE_EXTENTS)) {
752     update_mouse_loci ();
753     update_focus_loci ();
754   }
755   if (env_change & THE_LOCUS) {
756     if (locus_new_rects != locus_rects) {
757       invalidate (locus_rects);
758       invalidate (locus_new_rects);
759       locus_rects= locus_new_rects;
760     }
761   }
762 
763   // cout << "Handling backing store\n";
764   if (!is_nil (stored_rects)) {
765     if (env_change & (THE_TREE+THE_ENVIRONMENT+THE_SELECTION+THE_EXTENTS))
766       stored_rects= rectangles ();
767   }
768   if (inside_active_graphics ()) {
769     SI gx1, gy1, gx2, gy2;
770     if (find_graphical_region (gx1, gy1, gx2, gy2)) {
771       rectangle gr= rectangle (gx1, gy1, gx2, gy2);
772       if (!is_nil (gr - stored_rects))
773         invalidate (gx1, gy1, gx2, gy2);
774     }
775   }
776 
777   // cout << "Handling environment changes\n";
778   if (env_change & THE_ENVIRONMENT)
779     send_invalidate_all (this);
780 
781   // cout << "Applied changes\n";
782   // time_t t2= texmacs_time ();
783   // if (t2 - t1 >= 10) cout << "apply_changes took " << t2-t1 << "ms\n";
784   env_change  = 0;
785   last_change = texmacs_time ();
786   last_update = last_change-1;
787   manual_focus_release ();
788 }
789 
790 /******************************************************************************
791 * Animations
792 ******************************************************************************/
793 
794 void
animate()795 edit_interface_rep::animate () {
796   // cout << do_animate << ", " << next_animate << "\n";
797   if (do_animate && texmacs_time () - next_animate >= 0) {
798     bool flag= false;
799     time_t at= 0;
800     rectangles rs;
801     eb->anim_get_invalid (flag, at, rs);
802     if (flag && texmacs_time () - at >= 0)
803       invalidate (rs);
804     do_animate  = flag;
805     next_animate= at;
806   }
807 }
808 
809 /******************************************************************************
810 * Miscellaneous routines
811 ******************************************************************************/
812 
813 void
full_screen_mode(bool flag)814 edit_interface_rep::full_screen_mode (bool flag) {
815   full_screen= flag;
816   send_invalidate_all (this);
817 }
818 
819 void
before_menu_action()820 edit_interface_rep::before_menu_action () {
821   archive_state ();
822   start_editing ();
823   set_input_normal ();
824 }
825 
826 void
after_menu_action()827 edit_interface_rep::after_menu_action () {
828   notify_change (THE_DECORATIONS);
829   end_editing ();
830   windows_delayed_refresh (1);
831 }
832 
833 void
cancel_menu_action()834 edit_interface_rep::cancel_menu_action () {
835   notify_change (THE_DECORATIONS);
836   cancel_editing ();
837   windows_delayed_refresh (1);
838 }
839 
840 rectangle
get_window_extents()841 edit_interface_rep::get_window_extents () {
842   SI ox, oy, w, h;
843   widget me= ::get_canvas (widget (cvw));
844   ::get_position (me, ox, oy);
845   ::get_size (me, w, h);
846   SI vx1, vy1, vx2, vy2;
847   SERVER (get_visible (vx1, vy1, vx2, vy2));
848   ox -= vx1; oy -= vy2;
849   return rectangle (ox, oy - h, ox + w, oy);
850 }
851 
852 cursor
search_cursor(path p)853 edit_interface_rep::search_cursor (path p) {
854   return eb->find_check_cursor (p);
855 }
856 
857 selection
search_selection(path start,path end)858 edit_interface_rep::search_selection (path start, path end) {
859   selection sel= eb->find_check_selection (start, end);
860   rectangle r= least_upper_bound (sel->rs) / std_shrinkf;
861   return sel;
862 }
863 
864 /******************************************************************************
865 * event handlers
866 ******************************************************************************/
867 
868 bool
is_editor_widget()869 edit_interface_rep::is_editor_widget () {
870   return true;
871 }
872 
873 void
handle_get_size_hint(SI & w,SI & h)874 edit_interface_rep::handle_get_size_hint (SI& w, SI& h) {
875   gui_root_extents (w, h);
876 }
877 
878 void
handle_notify_resize(SI w,SI h)879 edit_interface_rep::handle_notify_resize (SI w, SI h) {
880   (void) w; (void) h;
881   notify_change (THE_TREE);
882 }
883 
884 void
handle_set_zoom_factor(double zoom)885 edit_interface_rep::handle_set_zoom_factor (double zoom) {
886   set_zoom_factor (zoom);
887 }
888