2 * (C) Copyright 2005 Shawn Betts
3 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
4 * (C) Copyright 2010-2012 John J. Foerch
6 * Use, modification, and distribution are subject to the terms specified in the
12 define_window_local_hook("mode_line_hook");
14 define_keywords("$flex", "$align", "$class", "$crop");
15 function generic_element_widget_container (window, container) {
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");
30 element.setAttribute("flex", flex);
32 element.setAttribute("align", align);
34 element.setAttribute("class", class_name);
36 element.setAttribute("crop", crop);
37 return this.add_widget(widget, element);
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;
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();
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);
73 function generic_widget_element (element, widget) {
74 this.element = element;
78 generic_widget_element.prototype = {
79 constructor: generic_widget_element,
81 return this.element.getAttribute("value");
85 this.element.setAttribute("value", v);
88 destroy: function () {
89 this.widget.destroy();
93 this.element.parentNode.removeChild(this.element);
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) {
108 handler = function () { obj.update(); };
109 add_hook.call(this.window, hook_name, handler);
110 this.window_hooks.push([hook_name, handler]);
115 attach: function (view) {
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]);
128 remove: function () {
134 define_global_window_mode("mode_line", "window_initialize_early_hook");
138 * current_buffer_name_widget shows the name of the current buffer.
140 function current_buffer_name_widget (window) {
141 this.class_name = "current-buffer-name-widget";
142 text_widget.call(this, window);
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;
158 * current_buffer_scroll_position_widget shows the vertical and horizontal
159 * scroll position of the current buffer.
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;
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 + ")";
190 * clock_widget shows a clock.
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 "+
198 function clock_widget (window) {
199 this.class_name = "clock-widget";
200 text_widget.call(this, window);
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();
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;
225 destroy: function () {
226 this.window.clearTimeout(this.timer_ID);
232 * buffer_count_widget shows the number of buffers in the window.
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 + "]");
253 * loading_count_widget shows how many buffers in the current window are
256 function loading_count_widget (window) {
257 this.class_name = "loading-count-widget";
258 text_widget.call(this, window);
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 () {
269 for_each_buffer(function (b) { if (b.loading) count++; });
271 this.view.text = "(" + count + " loading)";
279 * buffer_icon_widget shows the icon for the current buffer, if any, in
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;
294 this.view.element.setAttribute("src", buffer.icon);
296 this.view.element.removeAttribute("src");
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.
311 function downloads_status_widget (window) {
312 text_widget.call(this, window);
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;
325 destroy: function () {
326 remove_hook("download_progress_change_hook", this.updater);
327 remove_hook("download_state_change_hook", this.updater);
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)
353 str = t + "%/" + f + "%";
355 var doc = buffer.document.QueryInterface(Ci.nsIImageDocument);
356 if (doc.imageIsResized) {
357 if (t == 100 && f == 100)
360 str += "/zoom-to-fit";
363 this.view.text = str;
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));
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");