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(frame)
36 var ds = frame.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 frame.buffers.current.doc_shell
53 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
54 .getInterface(Components.interfaces.nsISelectionDisplay)
55 .QueryInterface(Components.interfaces.nsISelectionController);
59 function initial_isearch_state(window, forward)
61 this.screenx = window.scrollX;
62 this.screeny = window.scrollY;
66 this.range = window.document.createRange();
67 this.selection = null;
68 this.direction = forward;
71 function isearch_session(frame, forward)
74 this.window = frame.buffers.current.focused_window();
75 this.sel_ctrl = getFocusedSelCtrl(frame);
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.window, forward));
81 minibuffer_state.call(this, isearch_kmap, "");
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.frame.minibuffer;
111 m._input_text = s.search_str;
113 this._set_selection(s.selection);
115 this._clear_selection();
116 this.window.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.window.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.window.scrollY)
193 || (!dir && ey2 >= this.window.scrollY + this.window.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.frame.alert(e); }
212 find : function (str, dir, pt) {
215 // Should we wrap this time?
216 var wrapped = s.wrapped;
218 if (s.wrapped == false && s.range == null && s.search_str == str && s.direction == dir) {
223 var match_range = this._highlight_find(str, wrapped, dir, point);
226 screenx: this.window.scrollX, screeny: this.window.scrollY,
227 search_str: str, wrapped: wrapped, point: point,
229 selection: match_range ? match_range : s.selection,
232 this.states.push(new_state);
235 focus_link : function ()
237 var sel = this.window.getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
240 var node = sel.focusNode;
245 if (node.localName == "A") {
246 if (node.hasAttributes && node.attributes.getNamedItem("href")) {
247 /* FIXME: This shouldn't be needed anymore, due to
248 * better xpc wrappers */
249 Components.lookupMethod(node, "focus").call(node);
253 } while ((node = node.parentNode));
257 function isearch_continue(frame, direction) {
258 var s = frame.minibuffer.current_state;
259 if (!(s instanceof isearch_session))
260 throw "Invalid minibuffer state";
261 if (s.states.length == 1 && frame.isearch_last_search)
262 s.find(frame.isearch_last_search, direction, s.top.point);
264 s.find(s.top.search_str, direction, s.top.range);
267 interactive("isearch-continue-forward", isearch_continue, I.current_frame, true);
268 interactive("isearch-continue-backward", isearch_continue, I.current_frame, false);
270 function isearch_start (frame, direction)
272 var s = new isearch_session(frame, direction);
273 frame.minibuffer.push_state(s);
276 interactive("isearch-forward", isearch_start, I.current_frame, true);
277 interactive("isearch-backward", isearch_start, I.current_frame, false);
279 function isearch_backspace (frame)
281 var s = frame.minibuffer.current_state;
282 if (!(s instanceof isearch_session))
283 throw "Invalid minibuffer state";
284 if (s.states.length > 1)
288 interactive("isearch-backspace", isearch_backspace, I.current_frame);
290 function isearch_abort (frame)
292 var s = frame.minibuffer.current_state;
293 if (!(s instanceof isearch_session))
294 throw "Invalid minibuffer state";
295 frame.minibuffer.pop_state();
296 s.window.scrollTo(s.states[0].screenx, s.states[0].screeny);
297 s._clear_selection();
299 interactive("isearch-abort", isearch_abort, I.current_frame);
302 function isearch_add_character (frame, event)
304 var s = frame.minibuffer.current_state;
305 if (!(s instanceof isearch_session))
306 throw "Invalid minibuffer state";
307 var str = s.top.search_str;
308 str += String.fromCharCode(event.charCode);
309 s.find(str, s.top.direction, s.top.point);
312 interactive("isearch-add-character", isearch_add_character, I.current_frame, I.e);
314 function isearch_done (frame)
316 var s = frame.minibuffer.current_state;
317 if (!(s instanceof isearch_session))
318 throw "Invalid minibuffer state";
319 s.sel_ctrl.setDisplaySelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
320 frame.minibuffer.pop_state(false /* don't restore focus */);
321 frame.isearch_last_search = s.top.search_str;
323 s._clear_selection();
325 interactive("isearch-done", isearch_done, I.current_frame);