new module to enable editing and deleting of bookmarks
[conkeror/arlinius.git] / modules / mode-line.js
bloba0255e4fede0226040e041be4e1d05557d19b9db
1 /**
2  * (C) Copyright 2005 Shawn Betts
3  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
4  * (C) Copyright 2010-2012 John J. Foerch
5  *
6  * Use, modification, and distribution are subject to the terms specified in the
7  * COPYING file.
8 **/
10 require("mode.js");
12 define_window_local_hook("mode_line_hook");
14 define_keywords("$flex", "$align", "$class", "$crop");
15 function generic_element_widget_container (window, container) {
16     this.window = window;
17     this.container = container;
19 generic_element_widget_container.prototype = {
20     constructor: generic_element_widget_container,
21     add_text_widget: function (widget) {
22         keywords(arguments, $flex = widget.flex,
23                  $class = widget.class_name, $crop = widget.crop);
24         var flex = arguments.$flex;
25         var class_name = arguments.$class;
26         var align = arguments.$align;
27         var crop = arguments.$crop;
28         var element = create_XUL(this.window, "label");
29         if (flex)
30             element.setAttribute("flex", flex);
31         if (align)
32             element.setAttribute("align", align);
33         if (class_name)
34             element.setAttribute("class", class_name);
35         if (crop)
36             element.setAttribute("crop", crop);
37         return this.add_widget(widget, element);
38     },
39     add_widget: function (widget, element) {
40         element.conkeror_widget = new generic_widget_element(element, widget);
41         this.container.appendChild(element);
42         return element.conkeror_widget;
43     },
44     destroy: function () {
45         var children = this.container.childNodes;
46         for (var i = 0, nchildren = children.length; i < nchildren; ++i)
47             children.item(i).conkeror_widget.destroy();
48     }
51 function mode_line (window) {
52     var element = create_XUL(window, "hbox");
53     element.setAttribute("class", "mode-line");
54     /* FIXME: this will need to changed to be buffer-local */
55     var insert_before = window.document.getElementById("minibuffer");
56     insert_before.parentNode.insertBefore(element, insert_before);
57     window.mode_line = this;
58     generic_element_widget_container.call(this, window, element);
59     mode_line_hook.run(window, this);
61 mode_line.prototype = {
62     constructor: mode_line,
63     __proto__: generic_element_widget_container.prototype,
64     toString: function () "#<mode_line>",
66     uninstall: function () {
67         this.container.parentNode.removeChild(this.window.mode_line.container);
68         generic_element_widget_container.prototype.destroy.call(this);
69     }
73 function generic_widget_element (element, widget) {
74     this.element = element;
75     this.widget = widget;
76     widget.attach(this);
78 generic_widget_element.prototype = {
79     constructor: generic_widget_element,
80     get text () {
81         return this.element.getAttribute("value");
82     },
84     set text (v) {
85         this.element.setAttribute("value", v);
86     },
88     destroy: function () {
89         this.widget.destroy();
90     },
92     remove: function () {
93         this.element.parentNode.removeChild(this.element);
94         this.destroy();
95     }
99 function text_widget (window) {
100     this.window_hooks = [];
101     this.window = window;
103 text_widget.prototype = {
104     constructor: text_widget,
105     add_hook: function (hook_name, handler) {
106         var obj = this;
107         if (handler == null)
108             handler = function () { obj.update(); };
109         add_hook.call(this.window, hook_name, handler);
110         this.window_hooks.push([hook_name, handler]);
111     },
113     view: null,
115     attach: function (view) {
116         this.view = view;
117         this.update();
118     },
120     update: function () {},
122     destroy: function () {
123         for each (let i in this.window_hooks) {
124             remove_hook.call(this.window, i[0], i[1]);
125         }
126     },
128     remove: function () {
129         this.view.remove();
130     }
134 define_global_window_mode("mode_line", "window_initialize_early_hook");
138  * current_buffer_name_widget shows the name of the current buffer.
139  */
140 function current_buffer_name_widget (window) {
141     this.class_name = "current-buffer-name-widget";
142     text_widget.call(this, window);
143     this.flex = "1";
144     this.crop = "end";
145     this.add_hook("current_content_buffer_location_change_hook");
146     this.add_hook("select_buffer_hook");
148 current_buffer_name_widget.prototype = {
149     constructor: current_buffer_name_widget,
150     __proto__: text_widget.prototype,
151     update: function () {
152         this.view.text = this.window.buffers.current.description;
153     }
158  * current_buffer_scroll_position_widget shows the vertical and horizontal
159  * scroll position of the current buffer.
160  */
161 function current_buffer_scroll_position_widget (window) {
162     this.class_name = "current-buffer-scroll-position-widget";
163     text_widget.call(this, window);
164     this.add_hook("current_buffer_scroll_hook");
165     this.add_hook("select_buffer_hook");
166     this.add_hook("current_content_buffer_location_change_hook");
167     this.add_hook("current_content_buffer_focus_change_hook");
168     this.add_hook("current_special_buffer_generated_hook");
169     this.add_hook("current_content_buffer_finished_loading_hook");
171 current_buffer_scroll_position_widget.prototype = {
172     constructor: current_buffer_scroll_position_widget,
173     __proto__: text_widget.prototype,
174     update: function () {
175         var b = this.window.buffers.current;
176         var scrollX, scrollY, scrollMaxX, scrollMaxY;
177         var w = b.focused_frame;
178         scrollX = w.scrollX;
179         scrollY = w.scrollY;
180         scrollMaxX = w.scrollMaxX;
181         scrollMaxY = w.scrollMaxY;
182         var x = scrollMaxX == 0 ? 100 : Math.round(scrollX / scrollMaxX * 100);
183         var y = scrollMaxY == 0 ? 100 : Math.round(scrollY / scrollMaxY * 100);
184         this.view.text = "(" + x + ", " + y + ")";
185     }
190  * clock_widget shows a clock.
191  */
192 define_variable("clock_time_format", "%R",
193     "Format string for the mode-line clock widget.\n"+
194     "It takes the same format as strftime() in C. "+
195     "See http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html "+
196     "for details.");
198 function clock_widget (window) {
199     this.class_name = "clock-widget";
200     text_widget.call(this, window);
201     var obj = this;
202     this.do_update = function () { obj.update(); };
203     // todo: use one timer for multiple clock widgets
204     this.timer_ID = window.setTimeout(this.do_update, 0);
205     this.timer_timeout = true;
207 clock_widget.prototype = {
208     constructor: clock_widget,
209     __proto__: text_widget.prototype,
210     update: function () {
211         var time = new Date();
212         this.view.text = time.toLocaleFormat(clock_time_format);
213         if (time.getSeconds() > 0 || time.getMilliseconds() > 100) {
214             this.window.clearTimeout(this.timer_ID);
215             var time = time.getSeconds() * 1000 + time.getMilliseconds();
216             time = 60000 - time;
217             this.timer_ID = this.window.setTimeout(this.do_update, time);
218             this.timer_timeout = true;
219         } else if (this.timer_timeout) {
220             this.window.clearTimeout(this.timer_ID);
221             this.timer_ID = this.window.setInterval(this.do_update, 60000);
222             this.timer_timeout = false;
223         }
224     },
225     destroy: function () {
226         this.window.clearTimeout(this.timer_ID);
227     }
232  * buffer_count_widget shows the number of buffers in the window.
233  */
234 function buffer_count_widget (window) {
235     this.class_name = "buffer-count-widget";
236     text_widget.call(this, window);
237     this.add_hook("select_buffer_hook");
238     this.add_hook("create_buffer_hook");
239     this.add_hook("kill_buffer_hook");
240     this.add_hook("move_buffer_hook");
242 buffer_count_widget.prototype = {
243     constructor: buffer_count_widget,
244     __proto__: text_widget.prototype,
245     update: function () {
246         this.view.text = ("[" + (this.window.buffers.selected_index+1) + "/" +
247                           this.window.buffers.count + "]");
248     }
253  * loading_count_widget shows how many buffers in the current window are
254  * loading.
255  */
256 function loading_count_widget (window) {
257     this.class_name = "loading-count-widget";
258     text_widget.call(this, window);
259     var obj = this;
260     this.add_hook("content_buffer_started_loading_hook");
261     this.add_hook("content_buffer_finished_loading_hook");
262     this.add_hook("kill_buffer_hook");
264 loading_count_widget.prototype = {
265     constructor: loading_count_widget,
266     __proto__: text_widget.prototype,
267     update: function () {
268         var count = 0;
269         for_each_buffer(function (b) { if (b.loading) count++; });
270         if (count)
271             this.view.text = "(" + count + " loading)";
272         else
273             this.view.text = "";
274     }
279  * buffer_icon_widget shows the icon for the current buffer, if any, in
280  * the mode-line.
281  */
282 function buffer_icon_widget (window) {
283     this.class_name = "buffer-icon-widget";
284     text_widget.call(this, window);
285     this.add_hook("current_buffer_icon_change_hook");
286     this.add_hook("select_buffer_hook");
288 buffer_icon_widget.prototype = {
289     constructor: buffer_icon_widget,
290     __proto__: text_widget.prototype,
291     update: function () {
292         var buffer = this.window.buffers.current;
293         if (buffer.icon)
294             this.view.element.setAttribute("src", buffer.icon);
295         else
296             this.view.element.removeAttribute("src");
297     }
299 buffer_icon_widget.mode_line_adder = function (window) {
300     var element = create_XUL(window, "image");
301     element.setAttribute("class", "buffer-icon-widget");
302     element.setAttribute("width", "16");
303     element.setAttribute("height", "16");
304     window.mode_line.add_widget(new buffer_icon_widget(window), element);
309  * downloads_status_widget shows the number of active downloads.
310  */
311 function downloads_status_widget (window) {
312     text_widget.call(this, window);
313     var obj = this;
314     this.updater = function () { obj.update(); };
315     add_hook("download_progress_change_hook", this.updater);
316     add_hook("download_state_change_hook", this.updater);
318 downloads_status_widget.prototype = {
319     constructor: downloads_status_widget,
320     __proto__: text_widget.prototype,
321     class_name: "downloads-status-widget",
322     update: function (info) {
323         this.view.text = download_manager_service.activeDownloadCount;
324     },
325     destroy: function () {
326         remove_hook("download_progress_change_hook", this.updater);
327         remove_hook("download_state_change_hook", this.updater);
328     }
333  * zoom_widget
334  */
335 function zoom_widget (window) {
336     text_widget.call(this, window);
337     this.add_hook("select_buffer_hook");
338     this.add_hook("current_buffer_zoom_hook");
339     this.add_hook("current_content_buffer_location_change_hook");
340     this.add_hook("current_content_buffer_finished_loading_hook");
342 zoom_widget.prototype = {
343     constructor: zoom_widget,
344     __proto__: text_widget.prototype,
345     class_name: "zoom-widget",
346     update: function () {
347         var buffer = this.window.buffers.current;
348         var t = Math.round(buffer.markup_document_viewer.textZoom * 100);
349         var f = Math.round(buffer.markup_document_viewer.fullZoom * 100);
350         if (t == 100 && f == 100)
351             var str = "";
352         else
353             str = t + "%/" + f + "%";
354         try {
355             var doc = buffer.document.QueryInterface(Ci.nsIImageDocument);
356             if (doc.imageIsResized) {
357                 if (t == 100 && f == 100)
358                     str = "zoom-to-fit";
359                 else
360                     str += "/zoom-to-fit";
361             }
362         } catch (e) {}
363         this.view.text = str;
364     }
368 function mode_line_adder (widget_constructor) {
369     if (!('mode_line_adder' in widget_constructor))
370         widget_constructor.mode_line_adder = function (window) {
371             window.mode_line.add_text_widget(new widget_constructor(window));
372         };
373     return widget_constructor.mode_line_adder;
376 add_hook("mode_line_hook", mode_line_adder(current_buffer_name_widget));
377 add_hook("mode_line_hook", mode_line_adder(clock_widget));
378 add_hook("mode_line_hook", mode_line_adder(current_buffer_scroll_position_widget));
380 mode_line_mode(true);
382 provide("mode-line");