1 
2 /******************************************************************************
3 * MODULE     : tm_window.cpp
4 * DESCRIPTION: Main TeXmacs windows
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 "tm_window.hpp"
13 #include "tm_data.hpp"
14 #include "message.hpp"
15 #include "dictionary.hpp"
16 #include "merge_sort.hpp"
17 #include "iterator.hpp"
18 #include "boot.hpp"
19 
20 int geometry_w= 800, geometry_h= 600;
21 int geometry_x= 0  , geometry_y= 0;
22 
23 widget texmacs_window_widget (widget wid, tree geom);
24 widget make_menu_widget (object menu);
25 void refresh_size (widget wid, bool exact);
26 
27 /******************************************************************************
28 * User preference management concerning the geometry of windows
29 ******************************************************************************/
30 
31 hashmap<string,string> window_names ("");
32 
33 string
unique_window_name(string name)34 unique_window_name (string name) {
35   for (int i=1; true; i++) {
36     string wname= name;
37     if (i > 1) wname= wname * ":" * as_string (i);
38     if (!window_names->contains (wname)) {
39       window_names (wname)= name;
40       return wname;
41     }
42   }
43 }
44 
45 static bool
move_accept(int old_x,int old_y,int x,int y)46 move_accept (int old_x, int old_y, int x, int y) {
47   if (old_x == x && old_y == y) return false;
48   return true;
49 }
50 
51 void
notify_window_move(string name,SI xx,SI yy)52 notify_window_move (string name, SI xx, SI yy) {
53   int x=  xx / PIXEL;
54   int y= -yy / PIXEL;
55   if (name != "popup") {
56     //cout << "Move " << name << " to " << x << ", " << y << "\n";
57     string old_x= get_user_preference ("abscissa " * name, "");
58     string old_y= get_user_preference ("ordinate " * name, "");
59     //cout << "Move " << name << ": " << old_x << ", " << old_y
60     //<< " --> " << x << ", " << y << "\n";
61     if (old_x == "" || old_y == "" ||
62         move_accept (as_int (old_x), as_int (old_y), x, y)) {
63       set_user_preference ("abscissa " * name, as_string (x));
64       set_user_preference ("ordinate " * name, as_string (y));
65     }
66   }
67 }
68 
69 static bool
resize_accept(int old_w,int old_h,int w,int h)70 resize_accept (int old_w, int old_h, int w, int h) {
71   if (old_w == w && old_h == h) return false;
72 #ifdef QTTEXMACS
73   if (old_w == w && old_h == h + 16) return false;
74   if (old_w == w && old_h == h - 40) return false;
75 #endif
76   return true;
77 }
78 
79 void
notify_window_resize(string name,SI ww,SI hh)80 notify_window_resize (string name, SI ww, SI hh) {
81   int w= ww / PIXEL;
82   int h= hh / PIXEL;
83   if (name != "popup") {
84     //cout << "Resize " << name << " to " << ww << ", " << hh << "\n";
85     string old_w= get_user_preference ("width " * name, "");
86     string old_h= get_user_preference ("height " * name, "");
87     //cout << "Resize " << name << ": " << old_w << ", " << old_h
88     //<< " --> " << w << ", " << h << "\n";
89     if (old_w == "" || old_h == "" ||
90         resize_accept (as_int (old_w), as_int (old_h), w, h)) {
91       set_user_preference ("width " * name, as_string (w));
92       set_user_preference ("height " * name, as_string (h));
93     }
94   }
95 }
96 
97 void
notify_window_destroy(string name)98 notify_window_destroy (string name) {
99   window_names->reset (name);
100 }
101 
102 void
get_preferred_position(string name,SI & xx,SI & yy)103 get_preferred_position (string name, SI& xx, SI& yy) {
104   if (has_user_preference ("abscissa " * name)) {
105     int x= as_int (get_user_preference ("abscissa " * name));
106     int y= as_int (get_user_preference ("ordinate " * name));
107 
108     xx=  x * PIXEL;
109     yy= -y * PIXEL;
110   }
111 }
112 
113 void
get_preferred_size(string name,SI & ww,SI & hh)114 get_preferred_size (string name, SI& ww, SI& hh) {
115   if (has_user_preference ("width " * name)) {
116     int w= as_int (get_user_preference ("width " * name));
117     int h= as_int (get_user_preference ("height " * name));
118     ww= w * PIXEL;
119     hh= h * PIXEL;
120   }
121 }
122 
123 /******************************************************************************
124 * Meta editor constructor and destructor
125 ******************************************************************************/
126 
127 static int tm_window_serial= 0;
128 
tm_window_rep(widget wid2,tree geom)129 tm_window_rep::tm_window_rep (widget wid2, tree geom):
130   win (texmacs_window_widget (wid2, geom)),
131   wid (wid2), id (create_window_id ()),
132   serial (tm_window_serial++),
133   menu_current (object ()), menu_cache (widget ()),
134   text_ptr (NULL)
135 {
136   zoomf= get_server () -> get_default_zoom_factor ();
137 }
138 
tm_window_rep(tree doc,command quit)139 tm_window_rep::tm_window_rep (tree doc, command quit):
140   win (texmacs_widget (0, quit)),
141   wid (win), id (url_none ()),
142   serial (tm_window_serial++),
143   menu_current (object ()), menu_cache (widget ()),
144   text_ptr (NULL)
145 {
146   zoomf= get_server () -> get_default_zoom_factor ();
147 }
148 
~tm_window_rep()149 tm_window_rep::~tm_window_rep () {
150   if (!is_none (id)) destroy_window_id (id);
151 }
152 
153 /******************************************************************************
154 * Creation of TeXmacs window
155 ******************************************************************************/
156 
157 widget
texmacs_window_widget(widget wid,tree geom)158 texmacs_window_widget (widget wid, tree geom) {
159   int W, H;
160   int w= geometry_w, h= geometry_h;
161   int x= geometry_x, y= geometry_y;
162   bool custom= is_tuple (geom) && N (geom) >= 2;
163 #ifndef QTTEXMACS
164   if (use_side_tools) { w += 200; h += 100; }
165 #endif
166   if (custom) {
167     w= as_int (geom[0]);
168     h= as_int (geom[1]);
169   }
170   gui_root_extents (W, H); W /= PIXEL; H /= PIXEL;
171   if (x < 0) x= W + x + 1 - w;
172   if (y < 0) y= H + y + 1 - h;
173   string name= "TeXmacs";
174   name= unique_window_name (name);
175   widget win= plain_window_widget (wid, name);
176   SI xx= x * PIXEL, yy= -y * PIXEL;
177   SI ww= w * PIXEL, hh=  h * PIXEL;
178   if (!custom) {
179     get_preferred_position (name, xx, yy);
180     get_preferred_size (name, ww, hh);
181   }
182   set_size (win, ww, hh);
183   set_position (win, xx, yy);
184   return win;
185 }
186 
187 /******************************************************************************
188 * Closing embedded TeXmacs widgets
189 ******************************************************************************/
190 
191 class close_embedded_command_rep: public command_rep {
192   tm_view vw;
193 public:
close_embedded_command_rep(tm_view vw2)194   close_embedded_command_rep (tm_view vw2): vw (vw2) {}
195   void apply ();
print(tm_ostream & out)196   tm_ostream& print (tm_ostream& out) {
197     return out << "Close_Embedded widget command"; }
198 };
199 
200 void
apply()201 close_embedded_command_rep::apply () {
202   //cout << "Destroy " << vw->buf->buf->name << "\n";
203   ASSERT (!is_nil(vw->ed), "embedded command acting on deleted editor");
204   url foc= abstract_window (vw->ed->mvw->win);
205   if (is_none (foc)) {
206     array<url> a= windows_list ();
207     ASSERT (N(a) != 0, "no remaining windows");
208     foc= a[0];
209   }
210   window_focus (foc);
211   //cout << "Changed focus\n";
212   tm_window win= vw->win;
213   ASSERT (N (buffer_to_views (vw->buf->buf->name)) == 1,
214           "invalid cloned embedded TeXmacs widget");
215   remove_buffer (vw->buf->buf->name);
216   //cout << "Deleted buffer\n";
217   tm_delete (win);
218   //cout << "Deleted window\n";
219 }
220 
221 command
close_embedded_command(tm_view vw)222 close_embedded_command (tm_view vw) {
223   return tm_new<close_embedded_command_rep> (vw);
224 }
225 
226 /******************************************************************************
227 * Embedded TeXmacs widgets
228 ******************************************************************************/
229 
230 url
embedded_name(url name)231 embedded_name (url name) {
232   static int nr= 0;
233   if (!is_none (name)) return name;
234   nr++;
235   return url (string ("tmfs://aux/TeXmacs-input-" * as_string (nr)));
236 }
237 
238 tree
enrich_embedded_document(tree body,tree style)239 enrich_embedded_document (tree body, tree style) {
240   tree orig= body;
241   if (is_func (body, WITH)) body= body[N(body)-1];
242   if (!is_func (body, DOCUMENT)) body= tree (DOCUMENT, body);
243   hashmap<string,tree> initial (UNINIT);
244   initial (PAGE_MEDIUM)= "automatic";
245   initial (PAGE_SCREEN_LEFT)= "4px";
246   initial (PAGE_SCREEN_RIGHT)= "4px";
247   initial (PAGE_SCREEN_TOP)= "2px";
248   initial (PAGE_SCREEN_BOT)= "2px";
249   if (is_func (orig, WITH))
250     for (int i=0; i+2<N(orig); i+=2)
251       if (is_atomic (orig[i]))
252         initial (orig[i]->label)= orig[i+1];
253   tree doc (DOCUMENT);
254   doc << compound ("TeXmacs", TEXMACS_VERSION);
255   doc << style; //compound ("style", style);
256   doc << compound ("body", body);
257   doc << compound ("initial", make_collection (initial));
258   return doc;
259 }
260 
261 widget
texmacs_input_widget(tree doc,tree style,url wname)262 texmacs_input_widget (tree doc, tree style, url wname) {
263   doc= enrich_embedded_document (doc, style);
264   url       base = get_master_buffer (get_current_buffer ());
265   tm_view   curvw= concrete_view (get_current_view ());
266   url       name = embedded_name (wname);
267   if (contains (name, get_all_buffers ())) set_buffer_tree (name, doc);
268   else create_buffer (name, doc);
269   tm_view   vw   = concrete_view (get_passive_view (name));
270   tm_window win  = tm_new<tm_window_rep> (doc, command ());
271   set_master_buffer (name, base);
272   vw->win= win;
273   set_scrollable (win->wid, vw->ed);
274   vw->ed->cvw= win->wid.rep;
275   vw->ed->mvw= curvw;
276   return wrapped_widget (win->wid, close_embedded_command (vw));
277 }
278 
279 /******************************************************************************
280 * Meta mathods
281 ******************************************************************************/
282 
283 void
set_window_name(string s)284 tm_window_rep::set_window_name (string s) {
285   if (cur_title != s) {
286     cur_title= s;
287     set_name (wid, s);
288   }
289 }
290 
291 void
set_modified(bool flag)292 tm_window_rep::set_modified (bool flag) {
293   ::set_modified (wid, flag);
294 }
295 
296 void
set_window_url(url u)297 tm_window_rep::set_window_url (url u) {
298   if (!is_none (u)) set_file (wid, as_string (u));
299 }
300 
301 void
map()302 tm_window_rep::map () {
303   set_visibility (win, true);
304 }
305 
306 void
unmap()307 tm_window_rep::unmap () {
308   set_visibility (win, false);
309 }
310 
311 void
refresh()312 tm_window_rep::refresh () {
313   menu_cache= hashmap<object,widget> (widget ());
314 }
315 
316 /******************************************************************************
317 * Menus
318 ******************************************************************************/
319 
320 #ifdef QTTEXMACS
321 bool menu_caching= false;
322 #else
323 bool menu_caching= true;
324 #endif
325 
326 bool
get_menu_widget(int which,string menu,widget & w)327 tm_window_rep::get_menu_widget (int which, string menu, widget& w) {
328   object xmenu= call ("menu-expand", eval ("'" * menu));
329   //cout << "xmenu= " << xmenu << "\n";
330   if (menu_cache->contains (xmenu)) {
331     //if (menu_current[which] == xmenu) cout << "Same " << menu << "\n";
332     if (menu_current[which] == xmenu) return false;
333     menu_current (which)= xmenu;
334     //cout << "Cached " << menu << "\n";
335     w= menu_cache [xmenu];
336     return true;
337   }
338   menu_current (which)= xmenu;
339   //cout << "Compute " << menu << "\n";
340   object umenu= eval ("'" * menu);
341   w= make_menu_widget (umenu);
342   if (menu_caching) menu_cache (xmenu)= w;
343   return true;
344 }
345 
346 void
menu_main(string menu)347 tm_window_rep::menu_main (string menu) {
348   eval ("(lazy-initialize-force)");
349   widget w;
350   if (get_menu_widget (-1, menu, w))
351     ::set_main_menu (wid, w);
352 }
353 
354 void
menu_icons(int which,string menu)355 tm_window_rep::menu_icons (int which, string menu) {
356   eval ("(lazy-initialize-force)");
357   widget w;
358   if (get_menu_widget (which, menu, w)) {
359     if      (which == 0) set_main_icons (wid, w);
360     else if (which == 1) set_mode_icons (wid, w);
361     else if (which == 2) set_focus_icons (wid, w);
362     else if (which == 3) set_user_icons (wid, w);
363   }
364 }
365 
366 void
side_tools(int which,string tools)367 tm_window_rep::side_tools (int which, string tools) {
368   eval ("(lazy-initialize-force)");
369   widget w;
370   if (get_menu_widget (10 + which, tools, w)) {
371     if (which == 0) set_side_tools (wid, w);
372   }
373 }
374 
375 void
bottom_tools(int which,string tools)376 tm_window_rep::bottom_tools (int which, string tools) {
377   eval ("(lazy-initialize-force)");
378   widget w;
379   if (get_menu_widget (20 + which, tools, w)) {
380     if (which == 0) set_bottom_tools (wid, w);
381   }
382 }
383 
384 void
set_header_flag(bool flag)385 tm_window_rep::set_header_flag (bool flag) {
386   set_header_visibility (wid, flag);
387 }
388 
389 void
set_icon_bar_flag(int which,bool flag)390 tm_window_rep::set_icon_bar_flag (int which, bool flag) {
391   if      (which == 0) set_main_icons_visibility (wid, flag);
392   else if (which == 1) set_mode_icons_visibility (wid, flag);
393   else if (which == 2) set_focus_icons_visibility (wid, flag);
394   else if (which == 3) set_user_icons_visibility (wid, flag);
395 }
396 
397 void
set_side_tools_flag(int which,bool flag)398 tm_window_rep::set_side_tools_flag (int which, bool flag) {
399   if (which == 0) set_side_tools_visibility (wid, flag);
400 }
401 
402 void
set_bottom_tools_flag(int which,bool flag)403 tm_window_rep::set_bottom_tools_flag (int which, bool flag) {
404   if (which == 0) set_bottom_tools_visibility (wid, flag);
405 }
406 
407 bool
get_header_flag()408 tm_window_rep::get_header_flag () {
409   return get_header_visibility (wid);
410 }
411 
412 bool
get_icon_bar_flag(int which)413 tm_window_rep::get_icon_bar_flag (int which) {
414   if      (which == 0) return get_main_icons_visibility (wid);
415   else if (which == 1) return get_mode_icons_visibility (wid);
416   else if (which == 2) return get_focus_icons_visibility (wid);
417   else if (which == 3) return get_user_icons_visibility (wid);
418   else return false;
419 }
420 
421 bool
get_side_tools_flag(int which)422 tm_window_rep::get_side_tools_flag (int which) {
423   if (which == 0) return get_side_tools_visibility (wid);
424   else return false;
425 }
426 
427 bool
get_bottom_tools_flag(int which)428 tm_window_rep::get_bottom_tools_flag (int which) {
429   if (which == 0) return get_bottom_tools_visibility (wid);
430   else return false;
431 }
432 
433 /******************************************************************************
434 * The canvas
435 ******************************************************************************/
436 
437 void
set_window_zoom_factor(double zoom)438 tm_window_rep::set_window_zoom_factor (double zoom) {
439   zoomf= zoom;
440   ::set_zoom_factor (wid, zoom);
441 }
442 
443 double
get_window_zoom_factor()444 tm_window_rep::get_window_zoom_factor () {
445   return zoomf;
446 }
447 
448 void
get_visible(SI & x1,SI & y1,SI & x2,SI & y2)449 tm_window_rep::get_visible (SI& x1, SI& y1, SI& x2, SI& y2) {
450   get_visible_part (wid, x1, y1, x2, y2);
451 }
452 
453 void
get_extents(SI & x1,SI & y1,SI & x2,SI & y2)454 tm_window_rep::get_extents (SI& x1, SI& y1, SI& x2, SI& y2) {
455   ::get_extents (wid, x1, y1, x2, y2);
456 }
457 
458 void
set_extents(SI x1,SI y1,SI x2,SI y2)459 tm_window_rep::set_extents (SI x1, SI y1, SI x2, SI y2) {
460   ::set_extents (wid, x1, y1, x2, y2);
461 }
462 
463 void
set_scrollbars(int i)464 tm_window_rep::set_scrollbars (int i) {
465   ::set_scrollbars_visibility (wid, i);
466 }
467 
468 void
get_scroll_pos(SI & x,SI & y)469 tm_window_rep::get_scroll_pos (SI& x, SI& y) {
470   get_scroll_position (wid, x, y);
471 }
472 
473 void
set_scroll_pos(SI x,SI y)474 tm_window_rep::set_scroll_pos (SI x, SI y) {
475   set_scroll_position (wid, x, y);
476 }
477 
478 /******************************************************************************
479 * The footer as a status bar
480 ******************************************************************************/
481 
482 bool
get_footer_flag()483 tm_window_rep::get_footer_flag () {
484   return get_footer_visibility (wid);
485 }
486 
487 void
set_footer_flag(bool flag)488 tm_window_rep::set_footer_flag (bool flag) {
489   set_footer_visibility (wid, flag);
490 }
491 
492 void
set_left_footer(string s)493 tm_window_rep::set_left_footer (string s) {
494   ::set_left_footer (wid, s);
495 }
496 
497 void
set_right_footer(string s)498 tm_window_rep::set_right_footer (string s) {
499   ::set_right_footer (wid, s);
500 }
501 
502 /******************************************************************************
503 * Interactive commands on the footer
504 ******************************************************************************/
505 
506 class ia_command_rep: public command_rep {
507   tm_window_rep* win;
508 public:
ia_command_rep(tm_window_rep * win2)509   ia_command_rep (tm_window_rep* win2): win (win2) {}
apply()510   void apply () { win->interactive_return (); }
print(tm_ostream & out)511   tm_ostream& print (tm_ostream& out) { return out << "tm_window command"; }
512 };
513 
514 bool
get_interactive_mode()515 tm_window_rep::get_interactive_mode () {
516   return ::get_interactive_mode (wid);
517 }
518 
519 void
set_interactive_mode(bool flag)520 tm_window_rep::set_interactive_mode (bool flag) {
521   ::set_interactive_mode (wid, flag);
522 }
523 
524 void
interactive(string name,string type,array<string> def,string & s,command cmd)525 tm_window_rep::interactive (string name, string type, array<string> def,
526 			    string& s, command cmd)
527 {
528   if (get_interactive_mode ()) { s= "cancel"; return; }
529   text_ptr = &s;
530   call_back= cmd;
531   widget tw = text_widget (translate (name), 0, black, false);
532   widget inp= input_text_widget (tm_new<ia_command_rep> (this), type, def,
533                                  WIDGET_STYLE_MINI);
534   set_interactive_prompt (wid, tw);
535   set_interactive_input (wid, inp);
536   set_interactive_mode (true);
537 }
538 
539 void
interactive_return()540 tm_window_rep::interactive_return () {
541   *text_ptr= get_interactive_input (wid);
542   text_ptr= NULL;
543   set_interactive_mode (false);
544   call_back ();
545 }
546 
547 /******************************************************************************
548 * Other top level windows
549 ******************************************************************************/
550 
551 static hashmap<int,widget> window_table (NULL);
552 static time_t refresh_time= 0;
553 
554 int
window_handle()555 window_handle () {
556   static int window_next= 1;
557   return window_next++;
558 }
559 
560 void
window_create(int win,widget wid,string name,bool plain)561 window_create (int win, widget wid, string name, bool plain) {
562   widget pww;
563   if (plain)
564     pww= plain_window_widget (wid, name);
565   else
566     pww= popup_window_widget (wid, name);
567   window_table (win)= pww;
568 }
569 
570 void
window_create(int win,widget wid,string name,command quit)571 window_create (int win, widget wid, string name, command quit) {
572   widget pww;
573   pww= plain_window_widget (wid, name, quit);
574   window_table (win)= pww;
575 }
576 
577 /*
578 FIXME: this old implementation does not work in the presence
579 of texmacs_input widgets.  The current hack remedies this situation
580 by explicitly signalling the widget destruction slot before
581 the actual destruction of the widget.  This is still not sufficient
582 in the case of Qt though and also might cause the desruction slot
583 to be signalled twice.
584 
585 void
586 window_delete (int win) {
587   ASSERT (window_table->contains (win), "window does not exist");
588   widget pww= window_table [win];
589   window_table->reset (win);
590   destroy_window_widget (pww);
591 }
592 */
593 
594 void
window_delete(int win)595 window_delete (int win) {
596   static hashmap<int,bool> busy (false);
597   if (busy->contains (win)) return;
598   busy (win)= true;
599   ASSERT (window_table->contains (win), "window does not exist");
600   widget pww= window_table [win];
601   window_table->reset (win);
602   send_destroy (pww);
603   destroy_window_widget (pww);
604   busy (win)= false;
605 }
606 
607 void
window_show(int win)608 window_show (int win) {
609   ASSERT (window_table->contains (win), "window does not exist");
610   widget pww= window_table [win];
611   set_visibility (pww, true);
612 }
613 
614 void
window_hide(int win)615 window_hide (int win) {
616   ASSERT (window_table->contains (win), "window does not exist");
617   widget pww= window_table [win];
618   set_visibility (pww, false);
619 }
620 
621 scheme_tree
window_get_size(int win)622 window_get_size (int win) {
623   ASSERT (window_table->contains (win), "window does not exist");
624   widget pww= window_table [win];
625   int w, h;
626   get_size(pww, w, h);
627   return tuple (as_string (w/PIXEL), as_string (h/PIXEL));
628 }
629 
630 void
window_set_size(int win,int w,int h)631 window_set_size (int win, int w, int h) {
632   ASSERT (window_table->contains (win), "window does not exist");
633   widget pww= window_table [win];
634   set_size (pww, w*PIXEL, h*PIXEL);
635 }
636 
637 scheme_tree
window_get_position(int win)638 window_get_position (int win) {
639   ASSERT (window_table->contains (win), "window does not exist");
640   widget pww= window_table [win];
641   int x, y;
642   get_position(pww, x, y);
643   return tuple (as_string (x/PIXEL), as_string (y/PIXEL));
644 }
645 
646 void
window_set_position(int win,int x,int y)647 window_set_position (int win, int x, int y) {
648   ASSERT (window_table->contains (win), "window does not exist");
649   widget pww= window_table [win];
650   set_position (pww, x*PIXEL, y*PIXEL);
651 }
652 
653 void
windows_delayed_refresh(int ms)654 windows_delayed_refresh (int ms) {
655   refresh_time= texmacs_time () + ms;
656 }
657 
658 void
windows_refresh(string kind)659 windows_refresh (string kind) {
660   if (kind == "auto" && texmacs_time () < refresh_time) return;
661   iterator<int> it= iterate (window_table);
662   while (it->busy ()) {
663     int id= it->next ();
664     send_refresh (window_table[id], kind);
665 #ifdef X11TEXMACS
666     if (kind == "auto") refresh_size (window_table[id], false);
667 #endif
668   }
669   if (kind == "auto") windows_delayed_refresh (1000000000);
670 }
671