2 * (C) Copyright 2004-2005 Shawn Betts
3 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
5 * Use, modification, and distribution are subject to the terms specified in the
9 // turn on the selection in all frames
10 function getFocusedSelCtrl(window)
12 var ds = window.buffers.current.doc_shell;
13 var dsEnum = ds.getDocShellEnumerator(Components.interfaces.nsIDocShellTreeItem.typeContent,
14 Components.interfaces.nsIDocShell.ENUMERATE_FORWARDS);
15 while (dsEnum.hasMoreElements()) {
16 ds = dsEnum.getNext().QueryInterface(Components.interfaces.nsIDocShell);
19 var display = ds.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
20 .getInterface(Components.interfaces.nsISelectionDisplay);
23 return display.QueryInterface(Components.interfaces.nsISelectionController);
28 return window.buffers.current.doc_shell
29 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
30 .getInterface(Components.interfaces.nsISelectionDisplay)
31 .QueryInterface(Components.interfaces.nsISelectionController);
35 function initial_isearch_state(frame, forward)
37 this.screenx = frame.scrollX;
38 this.screeny = frame.scrollY;
42 this.range = frame.document.createRange();
43 this.selection = null;
44 this.direction = forward;
47 function isearch_session(window, forward)
50 this.frame = window.buffers.current.focused_frame;
51 this.sel_ctrl = getFocusedSelCtrl(window);
52 this.sel_ctrl.setDisplaySelection(Components.interfaces.nsISelectionController.SELECTION_ATTENTION);
53 this.sel_ctrl.repaintSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
54 this.states.push(new initial_isearch_state(this.frame, forward));
57 minibuffer_input_state.call(this, isearch_keymap, "");
59 isearch_session.prototype = {
60 constructor : isearch_session,
61 __proto__ : minibuffer_input_state.prototype, // inherit from minibuffer_state
64 return this.states[this.states.length - 1];
66 _set_selection : function (range)
69 const selctrlcomp = Components.interfaces.nsISelectionController;
70 var sel = this.sel_ctrl.getSelection(selctrlcomp.SELECTION_NORMAL);
71 sel.removeAllRanges();
72 sel.addRange(range.cloneRange());
73 this.sel_ctrl.scrollSelectionIntoView(selctrlcomp.SELECTION_NORMAL,
74 selctrlcomp.SELECTION_FOCUS_REGION,
76 } catch(e) {/*FIXME:figure out if/why this is needed*/ dumpln("setSelection: " + e);}
78 _clear_selection : function ()
80 const selctrlcomp = Components.interfaces.nsISelectionController;
81 var sel = this.sel_ctrl.getSelection(selctrlcomp.SELECTION_NORMAL);
82 sel.removeAllRanges();
84 restore_state: function () {
85 var m = this.window.minibuffer;
87 m._input_text = s.search_str;
89 this._set_selection(s.selection);
91 this._clear_selection();
92 this.frame.scrollTo(s.screenx, s.screeny);
93 m.prompt = ((s.wrapped ? "Wrapped ":"")
94 + (s.range ? "" : "Failing ")
95 + "I-Search" + (s.direction? "": " backward") + ":");
97 _highlight_find : function (str, wrapped, dir, pt) {
99 var doc = this.frame.document;
100 var finder = (Components.classes["@mozilla.org/embedcomp/rangefind;1"].createInstance()
101 .QueryInterface(Components.interfaces.nsIFind));
105 var body = doc.documentElement;
107 finder.findBackwards = !dir;
109 searchRange = doc.createRange();
110 startPt = doc.createRange();
111 endPt = doc.createRange();
113 var count = body.childNodes.length;
115 // Search range in the doc
116 searchRange.setStart(body,0);
117 searchRange.setEnd(body, count);
121 startPt.setStart(body, count);
122 startPt.setEnd(body, count);
124 startPt.setStart(pt.startContainer, pt.startOffset);
125 startPt.setEnd(pt.startContainer, pt.startOffset);
127 endPt.setStart(body, 0);
128 endPt.setEnd(body, 0);
131 startPt.setStart(body, 0);
132 startPt.setEnd(body, 0);
134 startPt.setStart(pt.endContainer, pt.endOffset);
135 startPt.setEnd(pt.endContainer, pt.endOffset);
137 endPt.setStart(body, count);
138 endPt.setEnd(body, count);
142 var selectionRange = null;
147 retRange = finder.Find(str, searchRange, startPt, endPt);
148 var keepSearching = false;
150 var sc = retRange.startContainer;
151 var ec = retRange.endContainer;
152 var scp = sc.parentNode;
153 var ecp = ec.parentNode;
154 var sy1 = abs_point(scp).y;
155 var ey2 = abs_point(ecp).y + ecp.offsetHeight;
157 startPt = retRange.startContainer.ownerDocument.createRange();
159 startPt.setStart(retRange.startContainer, retRange.startOffset);
160 startPt.setEnd(retRange.startContainer, retRange.startOffset);
162 startPt.setStart(retRange.endContainer, retRange.endOffset);
163 startPt.setEnd(retRange.endContainer, retRange.endOffset);
165 // We want to find a match that is completely
166 // visible, otherwise the view will scroll just a
167 // bit to fit the selection in completely.
168 keepSearching = (dir && sy1 < this.frame.scrollY)
169 || (!dir && ey2 >= this.frame.scrollY + this.frame.innerHeight);
171 } while (retRange && keepSearching);
173 retRange = finder.Find(str, searchRange, startPt, endPt);
177 this._set_selection(retRange);
178 selectionRange = retRange.cloneRange();
183 return selectionRange;
184 } catch(e) { /* FIXME: figure out why this is needed*/ this.window.alert(e); }
188 find : function (str, dir, pt) {
191 if (str == null || str.length == 0)
194 // Should we wrap this time?
195 var wrapped = s.wrapped;
197 if (s.wrapped == false && s.range == null && s.search_str == str && s.direction == dir) {
202 var match_range = this._highlight_find(str, wrapped, dir, point);
205 screenx: this.frame.scrollX, screeny: this.frame.scrollY,
206 search_str: str, wrapped: wrapped, point: point,
208 selection: match_range ? match_range : s.selection,
211 this.states.push(new_state);
214 focus_link : function ()
216 var sel = this.frame.getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
219 var node = sel.focusNode;
224 if (node.localName == "A") {
225 if (node.hasAttributes && node.attributes.getNamedItem("href")) {
226 /* FIXME: This shouldn't be needed anymore, due to
227 * better xpc wrappers */
228 Components.lookupMethod(node, "focus").call(node);
232 } while ((node = node.parentNode));
235 handle_input : function (m) {
237 this.find(m._input_text, this.top.direction, this.top.point);
238 this.restore_state();
243 destroy : function () {
245 this.frame.scrollTo(this.states[0].screenx, this.states[0].screeny);
246 this._clear_selection();
251 function isearch_continue(window, direction) {
252 var s = window.minibuffer.current_state;
253 if (!(s instanceof isearch_session))
254 throw "Invalid minibuffer state";
255 if (s.states.length == 1 && window.isearch_last_search)
256 s.find(window.isearch_last_search, direction, s.top.point);
258 s.find(s.top.search_str, direction, s.top.range);
261 interactive("isearch-continue-forward", function (I) {isearch_continue(I.window, true);});
262 interactive("isearch-continue-backward", function (I) {isearch_continue(I.window, false);});
264 function isearch_start (window, direction)
266 var s = new isearch_session(window, direction);
267 window.minibuffer.push_state(s);
270 interactive("isearch-forward", function (I) {isearch_start(I.window, true);});
271 interactive("isearch-backward", function (I) {isearch_start(I.window, false);});
273 function isearch_backspace (window)
275 var s = window.minibuffer.current_state;
276 if (!(s instanceof isearch_session))
277 throw "Invalid minibuffer state";
278 if (s.states.length > 1)
282 interactive("isearch-backspace", function (I) {isearch_backspace(I.window);});
284 function isearch_done (window)
286 var s = window.minibuffer.current_state;
287 if (!(s instanceof isearch_session))
288 throw "Invalid minibuffer state";
289 s.sel_ctrl.setDisplaySelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL);
291 // Prevent focus from being reverted
292 window.minibuffer.saved_focused_element = null;
293 window.minibuffer.saved_focused_window = null;
297 window.minibuffer.pop_state();
298 window.isearch_last_search = s.top.search_str;
300 s._clear_selection();
302 interactive("isearch-done", function (I) {isearch_done(I.window);});