browser_object_follow: do not give $opener for following non-element/window in new...
[conkeror.git] / modules / mode-line.js
blob7eb8630fe3c9e40c8e0d2e5572f1c58483817fda
1 /**
2  * (C) Copyright 2005 Shawn Betts
3  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
4  * (C) Copyright 2010 John J. Foerch
5  *
6  * Use, modification, and distribution are subject to the terms specified in the
7  * COPYING file.
8 **/
10 in_module(null);
12 require("mode.js");
14 define_window_local_hook("mode_line_hook");
16 define_keywords("$flex", "$align", "$class", "$crop");
17 function generic_element_widget_container (window, container) {
18     this.window = window;
19     this.container = container;
21 generic_element_widget_container.prototype = {
22     constructor: generic_element_widget_container,
23     add_text_widget: function (widget) {
24         keywords(arguments, $flex = widget.flex,
25                  $class = widget.class_name, $crop = widget.crop);
26         var flex = arguments.$flex;
27         var class_name = arguments.$class;
28         var align = arguments.$align;
29         var crop = arguments.$crop;
30         var element = create_XUL(this.window, "label");
31         if (flex)
32             element.setAttribute("flex", flex);
33         if (align)
34             element.setAttribute("align", align);
35         if (class_name)
36             element.setAttribute("class", class_name);
37         if (crop)
38             element.setAttribute("crop", crop);
39         return this.add_widget(widget, element);
40     },
41     add_widget: function (widget, element) {
42         element.conkeror_widget = new generic_widget_element(element, widget);
43         this.container.appendChild(element);
44         return element.conkeror_widget;
45     },
46     destroy: function () {
47         var children = this.container.childNodes;
48         for (var i = 0, nchildren = children.length; i < nchildren; ++i)
49             children.item(i).conkeror_widget.destroy();
50     }
53 function mode_line (window) {
54     var element = create_XUL(window, "hbox");
55     element.setAttribute("class", "mode-line");
56     /* FIXME: this will need to changed to be buffer-local */
57     var insert_before = window.document.getElementById("minibuffer");
58     insert_before.parentNode.insertBefore(element, insert_before);
59     window.mode_line = this;
60     generic_element_widget_container.call(this, window, element);
61     mode_line_hook.run(window, this);
63 mode_line.prototype = {
64     constructor: mode_line,
65     __proto__: generic_element_widget_container.prototype,
67     uninstall: function () {
68         this.container.parentNode.removeChild(this.window.mode_line.container);
69         generic_element_widget_container.prototype.destroy.call(this);
70     }
74 function generic_widget_element (element, widget) {
75     this.element = element;
76     this.widget = widget;
77     widget.attach(this);
79 generic_widget_element.prototype = {
80     constructor: generic_widget_element,
81     get text () {
82         return this.element.getAttribute("value");
83     },
85     set text (v) {
86         this.element.setAttribute("value", v);
87     },
89     destroy: function () {
90         this.widget.destroy();
91     },
93     remove: function () {
94         this.element.parentNode.removeChild(this.element);
95         this.destroy();
96     }
100 function text_widget (window) {
101     this.window_hooks = [];
102     this.window = window;
104 text_widget.prototype = {
105     constructor: text_widget,
106     add_hook: function (hook_name, handler) {
107         var obj = this;
108         if (handler == null)
109             handler = function () { obj.update(); };
110         add_hook.call(this.window, hook_name, handler);
111         this.window_hooks.push([hook_name, handler]);
112     },
114     view: null,
116     attach: function (view) {
117         this.view = view;
118         this.update();
119     },
121     update: function () {},
123     destroy: function () {
124         for each (let i in this.window_hooks) {
125             remove_hook.call(this.window, i[0], i[1]);
126         }
127     },
129     remove: function () {
130         this.view.remove();
131     }
135 define_global_window_mode("mode_line", "window_initialize_early_hook");
137 function current_buffer_name_widget (window) {
138     this.class_name = "current-buffer-name-widget";
139     text_widget.call(this, window);
140     this.flex = "1";
141     this.crop = "end";
142     this.add_hook("current_content_buffer_location_change_hook");
143     this.add_hook("select_buffer_hook");
145 current_buffer_name_widget.prototype.__proto__ = text_widget.prototype;
146 current_buffer_name_widget.prototype.update = function () {
147     this.view.text = this.window.buffers.current.description;
150 function current_buffer_scroll_position_widget (window) {
151     this.class_name = "current-buffer-scroll-position-widget";
152     text_widget.call(this, window);
153     this.add_hook("current_buffer_scroll_hook");
154     this.add_hook("select_buffer_hook");
155     this.add_hook("current_content_buffer_location_change_hook");
156     this.add_hook("current_content_buffer_focus_change_hook");
157     this.add_hook("current_special_buffer_generated_hook");
159 current_buffer_scroll_position_widget.prototype.__proto__ = text_widget.prototype;
160 current_buffer_scroll_position_widget.prototype.update = function () {
161     var b = this.window.buffers.current;
162     var scrollX, scrollY, scrollMaxX, scrollMaxY;
163     var w = b.focused_frame;
164     scrollX = w.scrollX;
165     scrollY = w.scrollY;
166     scrollMaxX = w.scrollMaxX;
167     scrollMaxY = w.scrollMaxY;
168     var x = scrollMaxX == 0 ? 100 : Math.round(scrollX / scrollMaxX * 100);
169     var y = scrollMaxY == 0 ? 100 : Math.round(scrollY / scrollMaxY * 100);
170     this.view.text = "(" + x + ", " + y + ")";
174 define_variable("clock_time_format", "%R",
175     "Format string for the mode-line clock widget.\n"+
176     "It takes the same format as strftime() in C. "+
177     "See http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html "+
178     "for details.");
180 function clock_widget (window) {
181     this.class_name = "clock-widget";
182     text_widget.call(this, window);
183     var obj = this;
184     this.do_update = function () { obj.update(); };
185     // todo: use one timer for multiple clock widgets
186     this.timer_ID = window.setTimeout(this.do_update, 0);
187     this.timer_timeout = true;
189 clock_widget.prototype.__proto__ = text_widget.prototype;
190 clock_widget.prototype.update = function () {
191     var time = new Date();
192     this.view.text = time.toLocaleFormat(clock_time_format);
193     if (time.getSeconds() > 0 || time.getMilliseconds() > 100) {
194         this.window.clearTimeout(this.timer_ID);
195         var time = time.getSeconds() * 1000 + time.getMilliseconds();
196         time = 60000 - time;
197         this.timer_ID = this.window.setTimeout(this.do_update, time);
198         this.timer_timeout = true;
199     } else if (this.timer_timeout) {
200         this.window.clearTimeout(this.timer_ID);
201         this.timer_ID = this.window.setInterval(this.do_update, 60000);
202         this.timer_timeout = false;
203     }
205 clock_widget.prototype.destroy = function () {
206     this.window.clearTimeout(this.timer_ID);
209 function buffer_count_widget (window) {
210     this.class_name = "buffer-count-widget";
211     text_widget.call(this, window);
212     this.add_hook("select_buffer_hook");
213     this.add_hook("create_buffer_hook");
214     this.add_hook("kill_buffer_hook");
216 buffer_count_widget.prototype.__proto__ = text_widget.prototype;
217 buffer_count_widget.prototype.update = function () {
218     this.view.text = ("[" + (this.window.buffers.selected_index+1) + "/" +
219                       this.window.buffers.count + "]");
222 function loading_count_widget (window) {
223     this.class_name = "loading-count-widget";
224     text_widget.call(this, window);
225     var obj = this;
226     this.add_hook("content_buffer_started_loading_hook");
227     this.add_hook("content_buffer_finished_loading_hook");
228     this.add_hook("kill_buffer_hook");
230 loading_count_widget.prototype.__proto__ = text_widget.prototype;
231 loading_count_widget.prototype.update = function () {
232     var count = 0;
233     for_each_buffer(function (b) { if (b.loading) count++; });
234     if (count)
235         this.view.text = "(" + count + " loading)";
236     else
237         this.view.text = "";
242  * buffer_icon_widget shows the icon for the current buffer, if any, in
243  * the mode-line.
244  */
245 function buffer_icon_widget (window) {
246     this.class_name = "buffer-icon-widget";
247     text_widget.call(this, window);
248     this.add_hook("current_buffer_icon_change_hook");
249     this.add_hook("select_buffer_hook");
251 buffer_icon_widget.prototype.__proto__ = text_widget.prototype;
252 buffer_icon_widget.prototype.update = function () {
253     var buffer = this.window.buffers.current;
254     if (buffer.icon)
255         this.view.element.setAttribute("src", buffer.icon);
256     else
257         this.view.element.removeAttribute("src");
259 buffer_icon_widget.mode_line_adder = function (window) {
260     var element = create_XUL(window, "image");
261     element.setAttribute("class", "buffer-icon-widget");
262     element.setAttribute("width", "16");
263     element.setAttribute("height", "16");
264     window.mode_line.add_widget(new buffer_icon_widget(window), element);
268 function mode_line_adder (widget_constructor) {
269     if (!('mode_line_adder' in widget_constructor))
270         widget_constructor.mode_line_adder = function (window) {
271             window.mode_line.add_text_widget(new widget_constructor(window));
272         };
273     return widget_constructor.mode_line_adder;
276 add_hook("mode_line_hook", mode_line_adder(current_buffer_name_widget));
277 add_hook("mode_line_hook", mode_line_adder(clock_widget));
278 add_hook("mode_line_hook", mode_line_adder(current_buffer_scroll_position_widget));
280 mode_line_mode(true);
282 provide("mode-line");