1 /***** BEGIN LICENSE BLOCK *****
2 Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 The contents of this file are subject to the Mozilla Public License Version
5 1.1 (the "License"); you may not use this file except in compliance with
6 the License. You may obtain a copy of the License at
7 http://www.mozilla.org/MPL/
9 Software distributed under the License is distributed on an "AS IS" basis,
10 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 for the specific language governing rights and limitations under the
14 The Initial Developer of the Original Code is Shawn Betts.
15 Portions created by the Initial Developer are Copyright (C) 2004,2005
16 by the Initial Developer. All Rights Reserved.
18 Alternatively, the contents of this file may be used under the terms of
19 either the GNU General Public License Version 2 or later (the "GPL"), or
20 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
21 in which case the provisions of the GPL or the LGPL are applicable instead
22 of those above. If you wish to allow use of your version of this file only
23 under the terms of either the GPL or the LGPL, and not to allow others to
24 use your version of this file under the terms of the MPL, indicate your
25 decision by deleting the provisions above and replace them with the notice
26 and other provisions required by the GPL or the LGPL. If you do not delete
27 the provisions above, a recipient may use your version of this file under
28 the terms of any one of the MPL, the GPL or the LGPL.
29 ***** END LICENSE BLOCK *****/
33 // turn on the selection in all frames
34 function getFocusedSelCtrl(window)
36 var ds = window.buffers.current.doc_shell;
37 var dsEnum = ds.getDocShellEnumerator(Components.interfaces.nsIDocShellTreeItem.typeContent,
38 Components.interfaces.nsIDocShell.ENUMERATE_FORWARDS);
39 while (dsEnum.hasMoreElements()) {
40 ds = dsEnum.getNext().QueryInterface(Components.interfaces.nsIDocShell);
43 var display = ds.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
44 .getInterface(Components.interfaces.nsISelectionDisplay);
47 return display.QueryInterface(Components.interfaces.nsISelectionController);
52 return window.buffers.current.doc_shell
53 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
54 .getInterface(Components.interfaces.nsISelectionDisplay)
55 .QueryInterface(Components.interfaces.nsISelectionController);
59 function initial_isearch_state(frame, forward)
61 this.screenx = frame.scrollX;
62 this.screeny = frame.scrollY;
66 this.range = frame.document.createRange();
67 this.selection = null;
68 this.direction = forward;
71 function isearch_session(window, forward)
74 this.frame = window.buffers.current.focused_frame;
75 this.sel_ctrl = getFocusedSelCtrl(window);
76 this.sel_ctrl.setDisplaySelection(Components.interfaces.nsISelectionController.SELECTION_ATTENTION);
77 this.sel_ctrl.repaintSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
78 this.states.push(new initial_isearch_state(this.frame, forward));
81 minibuffer_state.call(this, isearch_keymap, "");
83 isearch_session.prototype = {
84 constructor : isearch_session,
85 __proto__ : minibuffer_state.prototype, // inherit from minibuffer_state
88 return this.states[this.states.length - 1];
90 _set_selection : function (range)
93 const selctrlcomp = Components.interfaces.nsISelectionController;
94 var sel = this.sel_ctrl.getSelection(selctrlcomp.SELECTION_NORMAL);
95 sel.removeAllRanges();
96 sel.addRange(range.cloneRange());
97 this.sel_ctrl.scrollSelectionIntoView(selctrlcomp.SELECTION_NORMAL,
98 selctrlcomp.SELECTION_FOCUS_REGION,
100 } catch(e) {/*FIXME:figure out if/why this is needed*/ dumpln("setSelection: " + e);}
102 _clear_selection : function ()
104 const selctrlcomp = Components.interfaces.nsISelectionController;
105 var sel = this.sel_ctrl.getSelection(selctrlcomp.SELECTION_NORMAL);
106 sel.removeAllRanges();
108 restore_state: function () {
109 var m = this.window.minibuffer;
111 m._input_text = s.search_str;
113 this._set_selection(s.selection);
115 this._clear_selection();
116 this.frame.scrollTo(s.screenx, s.screeny);
117 m.prompt = ((s.wrapped ? "Wrapped ":"")
118 + (s.range ? "" : "Failing ")
119 + "I-Search" + (s.direction? "": " backward") + ":");
121 _highlight_find : function (str, wrapped, dir, pt) {
123 var doc = this.frame.document;
124 var finder = (Components.classes["@mozilla.org/embedcomp/rangefind;1"].createInstance()
125 .QueryInterface(Components.interfaces.nsIFind));
129 var body = doc.documentElement;
131 finder.findBackwards = !dir;
133 searchRange = doc.createRange();
134 startPt = doc.createRange();
135 endPt = doc.createRange();
137 var count = body.childNodes.length;
139 // Search range in the doc
140 searchRange.setStart(body,0);
141 searchRange.setEnd(body, count);
145 startPt.setStart(body, count);
146 startPt.setEnd(body, count);
148 startPt.setStart(pt.startContainer, pt.startOffset);
149 startPt.setEnd(pt.startContainer, pt.startOffset);
151 endPt.setStart(body, 0);
152 endPt.setEnd(body, 0);
155 startPt.setStart(body, 0);
156 startPt.setEnd(body, 0);
158 startPt.setStart(pt.endContainer, pt.endOffset);
159 startPt.setEnd(pt.endContainer, pt.endOffset);
161 endPt.setStart(body, count);
162 endPt.setEnd(body, count);
166 var selectionRange = null;
171 retRange = finder.Find(str, searchRange, startPt, endPt);
172 var keepSearching = false;
174 var sc = retRange.startContainer;
175 var ec = retRange.endContainer;
176 var scp = sc.parentNode;
177 var ecp = ec.parentNode;
178 var sy1 = abs_point(scp).y;
179 var ey2 = abs_point(ecp).y + ecp.offsetHeight;
181 startPt = retRange.startContainer.ownerDocument.createRange();
183 startPt.setStart(retRange.startContainer, retRange.startOffset);
184 startPt.setEnd(retRange.startContainer, retRange.startOffset);
186 startPt.setStart(retRange.endContainer, retRange.endOffset);
187 startPt.setEnd(retRange.endContainer, retRange.endOffset);
189 // We want to find a match that is completely
190 // visible, otherwise the view will scroll just a
191 // bit to fit the selection in completely.
192 keepSearching = (dir && sy1 < this.frame.scrollY)
193 || (!dir && ey2 >= this.frame.scrollY + this.frame.innerHeight);
195 } while (retRange && keepSearching);
197 retRange = finder.Find(str, searchRange, startPt, endPt);
201 this._set_selection(retRange);
202 selectionRange = retRange.cloneRange();
207 return selectionRange;
208 } catch(e) { /* FIXME: figure out why this is needed*/ this.window.alert(e); }
212 find : function (str, dir, pt) {
215 if (str == null || str.length == 0)
218 // Should we wrap this time?
219 var wrapped = s.wrapped;
221 if (s.wrapped == false && s.range == null && s.search_str == str && s.direction == dir) {
226 var match_range = this._highlight_find(str, wrapped, dir, point);
229 screenx: this.frame.scrollX, screeny: this.frame.scrollY,
230 search_str: str, wrapped: wrapped, point: point,
232 selection: match_range ? match_range : s.selection,
235 this.states.push(new_state);
238 focus_link : function ()
240 var sel = this.frame.getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
243 var node = sel.focusNode;
248 if (node.localName == "A") {
249 if (node.hasAttributes && node.attributes.getNamedItem("href")) {
250 /* FIXME: This shouldn't be needed anymore, due to
251 * better xpc wrappers */
252 Components.lookupMethod(node, "focus").call(node);
256 } while ((node = node.parentNode));
260 function isearch_continue(window, direction) {
261 var s = window.minibuffer.current_state;
262 if (!(s instanceof isearch_session))
263 throw "Invalid minibuffer state";
264 if (s.states.length == 1 && window.isearch_last_search)
265 s.find(window.isearch_last_search, direction, s.top.point);
267 s.find(s.top.search_str, direction, s.top.range);
270 interactive("isearch-continue-forward", function (I) {isearch_continue(I.window, true);});
271 interactive("isearch-continue-backward", function (I) {isearch_continue(I.window, false);});
273 function isearch_start (window, direction)
275 var s = new isearch_session(window, direction);
276 window.minibuffer.push_state(s);
279 interactive("isearch-forward", function (I) {isearch_start(I.window, true);});
280 interactive("isearch-backward", function (I) {isearch_start(I.window, false);});
282 function isearch_backspace (window)
284 var s = window.minibuffer.current_state;
285 if (!(s instanceof isearch_session))
286 throw "Invalid minibuffer state";
287 if (s.states.length > 1)
291 interactive("isearch-backspace", function (I) {isearch_backspace(I.window);});
293 /* FIXME: do this stuff in .destroy instead */
294 function isearch_abort (window)
296 var s = window.minibuffer.current_state;
297 if (!(s instanceof isearch_session))
298 throw "Invalid minibuffer state";
299 window.minibuffer.pop_state();
300 s.frame.scrollTo(s.states[0].screenx, s.states[0].screeny);
301 s._clear_selection();
303 interactive("isearch-abort", function(I) {isearch_abort(I.window);});
306 function isearch_add_character (window, event)
308 var s = window.minibuffer.current_state;
309 if (!(s instanceof isearch_session))
310 throw "Invalid minibuffer state";
311 var str = s.top.search_str;
312 str += String.fromCharCode(event.charCode);
313 s.find(str, s.top.direction, s.top.point);
316 interactive("isearch-add-character", function (I) {isearch_add_character(I.window, I.event);});
318 function isearch_done (window)
320 var s = window.minibuffer.current_state;
321 if (!(s instanceof isearch_session))
322 throw "Invalid minibuffer state";
323 s.sel_ctrl.setDisplaySelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
324 window.minibuffer.pop_state(false /* don't restore focus */);
325 window.isearch_last_search = s.top.search_str;
327 s._clear_selection();
329 interactive("isearch-done", function (I) {isearch_done(I.window);});