point: buffer-outer-container overflow:hidden, buffer-container manually sized
[conkeror.git] / modules / window.js
blob5eac25da879e06a74517c4690641da592ef60929
1 /**
2  * (C) Copyright 2007 John J. Foerch
3  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
4  *
5  * Use, modification, and distribution are subject to the terms specified in the
6  * COPYING file.
7 **/
9 require("mode.js");
11 var define_window_local_hook = simple_local_hook_definer();
12 var define_window_local_coroutine_hook = simple_local_coroutine_hook_definer();
15 define_hook("make_window_hook");
17 var window_watcher = Cc["@mozilla.org/embedcomp/window-watcher;1"]
18     .getService(Ci.nsIWindowWatcher);
20 function generate_new_window_tag (tag) {
21     var existing = [];
22     var exact_match = false;
23     var en = window_watcher.getWindowEnumerator();
24     if (tag == '')
25         tag = null;
26     var re;
27     if (tag)
28         re = new RegExp ("^" + tag + "<(\\d+)>$");
29     else
30         re = new RegExp ("^(\\d+)$");
31     while (en.hasMoreElements()) {
32         var w = en.getNext().QueryInterface(Ci.nsIDOMWindow);
33         if ('tag' in w)  {
34             if (tag && w.tag == tag) {
35                 exact_match = true;
36                 continue;
37             }
38             var re_result = re.exec(w.tag);
39             if (re_result)
40                 existing.push(re_result[1]);
41         }
42     }
43     if (tag && ! exact_match)
44         return tag;
46     existing.sort(function (a, b) { return a - b; });
48     var n = 1;
49     for (var i = 0; i < existing.length; i++) {
50         if (existing[i] < n) continue;
51         if (existing[i] == n) { n++; continue; }
52         break;
53     }
54     if (tag)
55         return tag + "<" + n + ">";
56     else
57         return n;
60 function make_chrome_window (chrome_uri, args) {
61     return window_watcher.openWindow(null, chrome_uri, "_blank",
62                                      "chrome,dialog=no,all,resizable", args);
65 var conkeror_chrome_uri = "chrome://conkeror-gui/content/conkeror.xul";
67 function make_window (initial_buffer_creator, tag) {
68     var args = { tag: tag,
69                  initial_buffer_creator: initial_buffer_creator };
70     var result = make_chrome_window(conkeror_chrome_uri, null);
71     result.args = args;
72     make_window_hook.run(result);
73     var close = result.close;
74     result.close = function () {
75         function attempt_close () {
76             var res = yield window_before_close_hook.run(result);
77             if (res) {
78                 window_close_hook.run(result);
79                 close.call(result);
80             }
81         }
82         co_call(attempt_close());
83     };
84     return result;
87 function get_window_by_tag (tag) {
88     var en = window_watcher.getWindowEnumerator();
89     while (en.hasMoreElements()) {
90         var w = en.getNext().QueryInterface(Ci.nsIDOMWindow);
91         if ('tag' in w && w.tag == tag)
92             return w;
93     }
94     return null;
97 /* FIXME: decide if this should include not-fully-initialized windows  */
98 function for_each_window (func) {
99     var en = window_watcher.getWindowEnumerator();
100     while (en.hasMoreElements()) {
101         var w = en.getNext().QueryInterface(Ci.nsIDOMWindow);
102         if ('conkeror' in w)
103             func(w);
104     }
107 function get_recent_conkeror_window () {
108     var wm = Cc['@mozilla.org/appshell/window-mediator;1']
109        .getService(Ci.nsIWindowMediator);
110     var window = wm.getMostRecentWindow("navigator:browser");
111     if (window && ("conkeror" in window))
112         return window;
113     var en = window_watcher.getWindowEnumerator();
114     while (en.hasMoreElements()) {
115         window = en.getNext().QueryInterface(Ci.nsIDOMWindow);
116         if ('conkeror' in window)
117             return window;
118     }
119     return null;
122 define_window_local_hook("window_initialize_early_hook");
123 define_window_local_hook("window_initialize_hook");
124 define_window_local_hook("window_initialize_late_hook");
126 var window_extra_argument_list = [];
128 define_variable("window_extra_argument_max_delay", 100);
130 function window_setup_args (window) {
131     if (window.args != null)
132         return;
134     var cur_time = Date.now();
135     var i;
136     var result = null;
137     for (i = 0; i < window_extra_argument_list.length; ++i) {
138         var a = window_extra_argument_list[i];
139         if (a.time > cur_time - window_extra_argument_max_delay) {
140             delete a.time;
141             result = a;
142             i++;
143             break;
144         }
145     }
146     window_extra_argument_list = window_extra_argument_list.slice(i);
148     if (result == null)
149         window.args = {};
150     else
151         window.args = result;
154 function window_set_extra_arguments (args) {
155     args.time = Date.now();
156     window_extra_argument_list.push(args);
159 function window_get_this_browser () {
160     return this.buffers.current.browser;
163 function window_initialize (window) {
164     window.conkeror = conkeror;
166     // Used by get_window_from_frame to get an unwrapped window reference
167     window.escape_wrapper = function (x) { x(window); };
169     window_setup_args(window);
171     // Set tag
172     var tag = null;
173     if ('tag' in window.args)
174         tag = window.args.tag;
175     window.tag = generate_new_window_tag(tag);
177     var stack = window.document.getElementById("buffer-outer-container");
178     var deck = window.document.getElementById("buffer-container");
179     window.addEventListener("resize",
180         function (e) {
181             deck.style.height = stack.clientHeight + "px";
182             deck.style.width = stack.clientWidth + "px";
183         }, true);
185     window.point = window.document.getElementById("point");
187     // Add a getBrowser() function to help certain extensions designed
188     // for Firefox work with conkeror
189     window.getBrowser = window_get_this_browser;
191     window_initialize_early_hook.run(window);
192     delete window.window_initialize_early_hook; // used only once
194     window_initialize_hook.run(window);
195     delete window.window_initialize_hook; // used only once
197     window.setTimeout(function () {
198             window_initialize_late_hook.run(window);
199             delete window.window_initialize_late_hook; // used only once
200             delete window.args; // get rid of args
201         }, 0);
203     window.addEventListener("close",
204                             function (event) {
205                                 event.preventDefault();
206                                 event.stopPropagation();
207                                 this.close();
208                             },
209                             true /* capture */);
212 define_window_local_coroutine_hook("window_before_close_hook",
213                                    RUN_HOOK_UNTIL_FAILURE);
214 define_window_local_hook("window_close_hook", RUN_HOOK);
217  * Window Modes
218  */
220 function define_global_window_mode (name, hook_name) {
221     keywords(arguments);
222     function install (window) {
223         if (name in window)
224             throw new Error(name + " already initialized for window");
225         window[name] = new conkeror[name](window);
226     }
227     function uninstall (window) {
228         if (!window[name])
229             throw new Error(name + " not initialized for window");
230         window[name].uninstall();
231         delete window[name];
232     }
233     define_global_mode(name + "_mode",
234                        function () { // enable
235                            add_hook(hook_name, install);
236                            for_each_window(install);
237                        },
238                        function () { // disable
239                            remove_hook(hook_name, install);
240                            for_each_window(uninstall);
241                        },
242                        forward_keywords(arguments));
244 ignore_function_for_get_caller_source_code_reference("define_global_window_mode");
249  * Window Title
250  */
253  * Default tile formatter.  The page url is ignored.  If there is a
254  * page_title, returns: "Page title - Conkeror".  Otherwise, it
255  * returns just: "Conkeror".
256  */
257 function default_title_formatter (window) {
258     var page_title = window.buffers.current.title;
260     if (page_title && page_title.length > 0)
261         return page_title + " - Conkeror";
262     else
263         return "Conkeror";
266 var title_format_fn = null;
268 function set_window_title (window) {
269     window.document.title = title_format_fn(window);
272 function init_window_title () {
273     title_format_fn = default_title_formatter;
275     add_hook("window_initialize_late_hook", set_window_title);
276     add_hook("current_content_buffer_location_change_hook",
277              function (buffer) {
278                  set_window_title(buffer.window);
279              });
280     add_hook("select_buffer_hook",
281              function (buffer) {
282                  set_window_title(buffer.window);
283              }, true);
284     add_hook("current_buffer_title_change_hook",
285              function (buffer) {
286                  set_window_title(buffer.window);
287              });
290 init_window_title();
293 function call_builtin_command (window, command, clear_mark) {
294     var m = window.minibuffer;
295     if (m.active && m._input_mode_enabled) {
296         m._restore_normal_state();
297         var e = m.input_element;
298         var c = e.controllers.getControllerForCommand(command);
299         try {
300             if (c && c.isCommandEnabled(command))
301                 c.doCommand(command);
302         } catch (e) {
303             // ignore errors
304         }
305         if (clear_mark)
306             m.current_state.mark_active = false;
307     } else {
308         function attempt_command (element) {
309             var c;
310             if (element.controllers
311                 && (c = element.controllers.getControllerForCommand(command)) != null
312                 && c.isCommandEnabled(command))
313             {
314                 try {
315                     c.doCommand(command);
316                 } catch (e) {
317                     // ignore errors
318                 }
319                 if (clear_mark)
320                     window.buffers.current.mark_active = false;
321                 return true;
322             }
323             return false;
324         }
325         var element = window.buffers.current.focused_element;
326         if (element && attempt_command(element, command))
327             return;
328         var win = window.buffers.current.focused_frame;
329         while (true) {
330             if (attempt_command(win, command))
331                 return;
332             if (!win.parent || win == win.parent)
333                 break;
334             win = win.parent;
335         }
336     }
341  * window_set_full_screen sets or toggles the fullScreen and hideChrome
342  * properties of the given window.  When fullscreen is a boolean, it sets
343  * it to that value.  When it is null or not given, it toggles the current
344  * value.
345  */
346 function window_set_full_screen (window, fullscreen) {
347     if (fullscreen === true || fullscreen === false) {
348         window.fullScreen = fullscreen;
349         window.hideChrome = fullscreen;
350     } else {
351         window.fullScreen = ! window.fullScreen;
352         window.hideChrome = window.fullScreen;
353     }
357 provide("window");