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