1
2 /******************************************************************************
3 * MODULE : new_view.cpp
4 * DESCRIPTION: View management
5 * COPYRIGHT : (C) 1999-2012 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_data.hpp"
13 #include "convert.hpp"
14 #include "file.hpp"
15 #include "web_files.hpp"
16 #include "tm_link.hpp"
17 #include "message.hpp"
18 #include "dictionary.hpp"
19 #include "new_document.hpp"
20 #include "drd_std.hpp"
21
22 #if defined(OS_WIN32) || defined(__MINGW32__)
23 #define WINPATHS
24 #endif
25
26 /******************************************************************************
27 * Associating URLs to views
28 ******************************************************************************/
29
30 static hashmap<tree,int> view_number_table (0);
31 static hashmap<tree,pointer> view_table (NULL);
32
33 static int
new_view_number(url u)34 new_view_number (url u) {
35 view_number_table (u->t) += 1;
36 return view_number_table [u->t];
37 }
38
tm_view_rep(tm_buffer buf2,editor ed2)39 tm_view_rep::tm_view_rep (tm_buffer buf2, editor ed2):
40 buf (buf2), ed (ed2), win (NULL), nr (new_view_number (buf->buf->name)) {}
41
42 static string
encode_url(url u)43 encode_url (url u) {
44 if (!is_rooted (u)) return "here/" * as_string (u, URL_UNIX);
45 if (get_root (u) == "default") return "default" * as_string (u, URL_UNIX);
46 return get_root (u) * "/" * as_string (unroot (u), URL_UNIX);
47 }
48
49 static url
decode_url(string s)50 decode_url (string s) {
51 int i= search_forwards ("/", 0, s);
52 if (i < 0) return url_none ();
53 #ifdef WINPATHS
54 int j= 0;
55 if (s (0, i) == "here") j= i+1;
56 if (s (0, i) == "default") j= i;
57 if (j != 0) {
58 if (s[j+1] == ':') return url (s (j, N(s)));
59 else return url_root ("default") * url (s (j, N(s)));
60 }
61 #else
62 if (s (0, i) == "here") return url (s (i+1, N(s)));
63 if (s (0, i) == "default") return url (s (i, N(s)));
64 #endif
65 return url_root (s (0, i)) * url (s (i+1, N(s)));
66 }
67
68 url
abstract_view(tm_view vw)69 abstract_view (tm_view vw) {
70 if (vw == NULL) return url_none ();
71 string name= encode_url (vw->buf->buf->name);
72 //cout << vw->buf->buf->name << " -> " << name << "\n";
73 string nr = as_string (vw->nr);
74 return "tmfs://view/" * nr * "/" * name;
75 }
76
77 tm_view
concrete_view(url u)78 concrete_view (url u) {
79 if (is_none (u)) return NULL;
80 string s= as_string (u);
81 if (!starts (s, "tmfs://view/")) return NULL;
82 s= s (N (string ("tmfs://view/")), N(s));
83 int i= search_forwards ("/", 0, s);
84 if (i < 0) return NULL;
85 int nr= as_int (s (0, i));
86 url name= decode_url (s (i+1, N(s)));
87 //cout << s (i+1, N(s)) << " -> " << name << "\n";
88 tm_buffer buf= concrete_buffer (name);
89 if (!is_nil (buf))
90 for (i=0; i<N(buf->vws); i++)
91 if (buf->vws[i]->nr == nr)
92 return buf->vws[i];
93 return NULL;
94 }
95
96 /******************************************************************************
97 * Views associated to editor, window, or buffer
98 ******************************************************************************/
99
100 tm_view the_view= NULL;
101
102 bool
has_current_view()103 has_current_view () {
104 return the_view != NULL;
105 }
106
107 void
set_current_view(url u)108 set_current_view (url u) {
109 tm_view vw= concrete_view (u);
110 //ASSERT (is_none (u) || starts (as_string (tail (u)), "no_name") || vw != NULL, "bad view");
111 the_view= vw;
112 if (vw != NULL) {
113 the_drd = vw->ed->drd;
114 vw->buf->buf->last_visit= texmacs_time ();
115 }
116 }
117
118 url
get_current_view()119 get_current_view () {
120 ASSERT (the_view != NULL, "no active view");
121 return abstract_view (the_view);
122 }
123
124 url
get_current_view_safe()125 get_current_view_safe () {
126 if (the_view == NULL) return url_none ();
127 return abstract_view (the_view);
128 }
129
130 void notify_delete_view (url u);
131
132 editor
get_current_editor()133 get_current_editor () {
134 url u= get_current_view();
135 tm_view vw= concrete_view (u);
136 if (vw == NULL) { // HACK: shouldn't happen!
137 FAILED ("Current view is NULL");
138 notify_delete_view (u);
139 array<url> history = get_all_views();
140 if (history == NULL || N(history) == 0)
141 FAILED("View history is empty")
142 return view_to_editor (history[N(history)-1]);
143 }
144 return vw->ed;
145 }
146
147 array<url>
buffer_to_views(url name)148 buffer_to_views (url name) {
149 tm_buffer buf= concrete_buffer (name);
150 array<url> r;
151 if (is_nil (buf)) return r;
152 for (int i=0; i<N(buf->vws); i++)
153 r << abstract_view (buf->vws[i]);
154 return r;
155 }
156
157 url
view_to_buffer(url u)158 view_to_buffer (url u) {
159 tm_view vw= concrete_view (u);
160 if (vw == NULL) return url_none ();
161 return vw->buf->buf->name;
162 }
163
164 url
view_to_window(url u)165 view_to_window (url u) {
166 tm_view vw= concrete_view (u);
167 if (vw == NULL) return url_none ();
168 return abstract_window (vw->win);
169 }
170
171 editor
view_to_editor(url u)172 view_to_editor (url u) {
173 tm_view vw= concrete_view (u);
174 if (vw == NULL) {
175 notify_delete_view (u); // HACK: returns to valid (?) state.
176 failed_error << "View is " << u << "\n";
177 FAILED ("View admits no editor");
178 }
179 return vw->ed;
180 }
181
182 /******************************************************************************
183 * Viewing history
184 ******************************************************************************/
185
186 array<url> view_history;
187
188 void
notify_set_view(url u)189 notify_set_view (url u) {
190 int i;
191 for (i=0; i<N(view_history); i++)
192 if (view_history[i] == u) break;
193 if (i >= N(view_history))
194 view_history= append (u, view_history);
195 else {
196 int j;
197 for (j=i; j>0; j--)
198 view_history[j]= view_history[j-1];
199 view_history[j]= u;
200 }
201 }
202
203 void
notify_delete_view(url u)204 notify_delete_view (url u) {
205 for (int i=0; i<N(view_history); i++)
206 if (view_history[i] == u) {
207 view_history= append (range (view_history, 0, i),
208 range (view_history, i+1, N(view_history)));
209 return;
210 }
211 }
212
213 url
get_recent_view(url name,bool same,bool other,bool active,bool passive)214 get_recent_view (url name, bool same, bool other, bool active, bool passive) {
215 // Get most recent view with the following filters:
216 // If same, then the name of the buffer much be name
217 // If other, then the name of the buffer much be other than name
218 // If active, then the buffer must be active
219 // If passive, then the buffer must be passive
220 int i;
221 for (i= 0; i < N(view_history); i++) {
222 tm_view vw= concrete_view (view_history[i]);
223 if (vw != NULL) {
224 if (same && vw->buf->buf->name != name) continue;
225 if (other && vw->buf->buf->name == name) continue;
226 if (active && vw->win == NULL) continue;
227 if (passive && vw->win != NULL) continue;
228 return view_history[i];
229 }
230 }
231 return url_none ();
232 }
233
234 array<url>
get_all_views()235 get_all_views () {
236 return view_history;
237 }
238
239 /******************************************************************************
240 * Creation of views on buffers
241 ******************************************************************************/
242
243 url tm_init_buffer_file= url_none ();
244 url my_init_buffer_file= url_none ();
245
246 url
get_new_view(url name)247 get_new_view (url name) {
248 //cout << "Creating new view " << name << "\n";
249
250 create_buffer (name, tree (DOCUMENT));
251 tm_buffer buf= concrete_buffer (name);
252 editor ed = new_editor (get_server () -> get_server (), buf);
253 tm_view vw = tm_new<tm_view_rep> (buf, ed);
254 buf->vws << vw;
255 ed->set_data (buf->data);
256
257 url temp= get_current_view_safe ();
258 set_current_view (abstract_view (vw));
259 if (is_none (tm_init_buffer_file))
260 tm_init_buffer_file= "$TEXMACS_PATH/progs/init-buffer.scm";
261 if (is_none (my_init_buffer_file))
262 my_init_buffer_file= "$TEXMACS_HOME_PATH/progs/my-init-buffer.scm";
263 if (exists (tm_init_buffer_file)) exec_file (tm_init_buffer_file);
264 if (exists (my_init_buffer_file)) exec_file (my_init_buffer_file);
265 set_current_view (temp);
266
267 //cout << "View created\n";
268 return abstract_view (vw);
269 }
270
271 url
get_passive_view(url name)272 get_passive_view (url name) {
273 // Get a view on a buffer, but not one which is attached to a window
274 // Create a new view if no such view exists
275 tm_buffer buf= concrete_buffer_insist (name);
276 if (is_nil (buf)) return url_none ();
277 array<url> vs= buffer_to_views (name);
278 for (int i=0; i<N(vs); i++) {
279 url win= view_to_window (vs[i]);
280 if (is_none (win)) return vs[i];
281 }
282 return get_new_view (buf->buf->name);
283 }
284
285 url
get_recent_view(url name)286 get_recent_view (url name) {
287 // Get (most) recent view on a buffer, with a preference for
288 // the current buffer or another view attached to a window
289 array<url> vs= buffer_to_views (name);
290 if (N(vs) == 0) return get_new_view (name);
291 url u= get_current_view ();
292 if (view_to_buffer (u) == name) return u;
293 url r= get_recent_view (name, true, false, true, false);
294 if (!is_none (r)) return r;
295 r= get_recent_view (name, true, false, false, false);
296 if (!is_none (r)) return r;
297 return vs[0];
298 }
299
300 /******************************************************************************
301 * Destroying a view
302 ******************************************************************************/
303
304 void
delete_view(url u)305 delete_view (url u) {
306 tm_view vw= concrete_view (u);
307 if (vw == NULL) return;
308 tm_buffer buf= vw->buf;
309 int i, j, n= N(buf->vws);
310 for (i=0; i<n; i++)
311 if (buf->vws[i] == vw) {
312 array<tm_view> a (n-1);
313 for (j=0; j<n-1; j++)
314 if (j<i) a[j]= buf->vws[j];
315 else a[j]= buf->vws[j+1];
316 buf->vws= a;
317 }
318 notify_delete_view (u);
319 tm_delete (vw);
320 }
321
322 void
notify_rename_before(url old_name)323 notify_rename_before (url old_name) {
324 array<url> vs= buffer_to_views (old_name);
325 for (int i=0; i<N(vs); i++)
326 notify_delete_view (vs[i]);
327 }
328
329 void
notify_rename_after(url new_name)330 notify_rename_after (url new_name) {
331 array<url> vs= buffer_to_views (new_name);
332 for (int i=0; i<N(vs); i++)
333 notify_set_view (vs[i]);
334 }
335
336 /******************************************************************************
337 * Attaching and detaching views
338 ******************************************************************************/
339
340 void
attach_view(url win_u,url u)341 attach_view (url win_u, url u) {
342 tm_window win= concrete_window (win_u);
343 tm_view vw = concrete_view (u);
344 if (win == NULL || vw == NULL) return;
345 // cout << "Attach view " << vw->buf->buf->name << "\n";
346 vw->win= win;
347 widget wid= win->wid;
348 set_scrollable (wid, vw->ed);
349 vw->ed->cvw= wid.rep;
350 ASSERT (is_attached (wid), "widget should be attached");
351 vw->ed->resume ();
352 win->set_window_name (vw->buf->buf->title);
353 win->set_window_url (vw->buf->buf->name);
354 notify_set_view (u);
355 // cout << "View attached\n";
356 }
357
358 void
detach_view(url u)359 detach_view (url u) {
360 tm_view vw = concrete_view (u);
361 if (vw == NULL) return;
362 tm_window win= vw->win;
363 if (win == NULL) return;
364 // cout << "Detach view " << vw->buf->buf->name << "\n";
365 vw->win= NULL;
366 widget wid= win->wid;
367 ASSERT (is_attached (wid), "widget should be attached");
368 vw->ed->suspend ();
369 set_scrollable (wid, glue_widget ());
370 win->set_window_name ("TeXmacs");
371 win->set_window_url (url_none ());
372 // cout << "View detached\n";
373 }
374
375 /******************************************************************************
376 * Switching views
377 ******************************************************************************/
378
379 void
window_set_view(url win_u,url new_u,bool focus)380 window_set_view (url win_u, url new_u, bool focus) {
381 //cout << "set view " << win_u << ", " << new_u << ", " << focus << "\n";
382 tm_window win= concrete_window (win_u);
383 if (win == NULL) return;
384 //cout << "Found window\n";
385 tm_view new_vw= concrete_view (new_u);
386 if (new_vw == NULL || new_vw->win == win) return;
387 //cout << "Found view\n";
388 ASSERT (new_vw->win == NULL, "view attached to other window");
389 url old_u= window_to_view (win_u);
390 if (!is_none (old_u)) detach_view (old_u);
391 attach_view (win_u, new_u);
392 if (focus || get_current_view () == old_u)
393 set_current_view (new_u);
394 }
395
396 void
switch_to_buffer(url name)397 switch_to_buffer (url name) {
398 //cout << "Switching to buffer " << name << "\n";
399 url u= get_passive_view (name);
400 tm_view vw= concrete_view (u);
401 if (vw == NULL) return;
402 window_set_view (get_current_window (), u, true);
403 tm_window nwin= vw->win;
404 if (nwin != NULL)
405 nwin->set_window_zoom_factor (nwin->get_window_zoom_factor ());
406 //cout << "Switched to buffer " << new_vw->buf->buf->name << "\n";
407 }
408
409 void
focus_on_editor(editor ed)410 focus_on_editor (editor ed) {
411 array<url> bufs= get_all_buffers ();
412 for (int i=0; i<N(bufs); i++) {
413 array<url> vs= buffer_to_views (bufs[i]);
414 for (int j=0; j<N(vs); j++)
415 if (view_to_editor (vs[j]) == ed) {
416 set_current_view (vs[j]);
417 return;
418 }
419 }
420
421 /* FIXME: directly using get_all_views produces synchronization error
422 array<url> vs= get_all_views ();
423 for (int i=0; i<N(vs); i++)
424 if (view_to_editor (vs[i]) == ed) {
425 cout << "Focus on " << vs[i] << "\n";
426 set_current_view (vs[i]);
427 return;
428 }
429 */
430
431 std_warning << "Warning: editor no longer exists, "
432 << "may indicate synchronization error\n";
433 //failed_error << "Name of buffer: " << ed->buf->buf->name << "\n";
434 //FAILED ("invalid situation");
435 }
436
437 bool
focus_on_buffer(url name)438 focus_on_buffer (url name) {
439 // Focus on the most recent view on a buffer, preferably active in a window
440 // Return false if no view exists for the buffer
441 if (the_view != NULL && the_view->buf->buf->name == name) return true;
442 url r= get_recent_view (name, true, false, true, false);
443 if (is_none (r)) r= get_recent_view (name, true, false, false, false);
444 if (is_none (r)) {
445 array<url> vws= buffer_to_views (name);
446 if (N(vws) > 0) r= vws[0];
447 }
448 if (is_none (r)) return false;
449 set_current_view (r);
450 return true;
451 }
452