xpfe/components/autocomplete/resources/content/autocomplete.xml

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 <?xml version="1.0"?>
michael@0 2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 - License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
michael@0 5
michael@0 6
michael@0 7 <bindings id="autocompleteBindings"
michael@0 8 xmlns="http://www.mozilla.org/xbl"
michael@0 9 xmlns:html="http://www.w3.org/1999/xhtml"
michael@0 10 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
michael@0 11 xmlns:xbl="http://www.mozilla.org/xbl">
michael@0 12
michael@0 13 <binding id="autocomplete" role="xul:combobox"
michael@0 14 extends="chrome://global/content/bindings/textbox.xml#textbox">
michael@0 15 <resources>
michael@0 16 <stylesheet src="chrome://global/content/autocomplete.css"/>
michael@0 17 <stylesheet src="chrome://global/skin/autocomplete.css"/>
michael@0 18 </resources>
michael@0 19
michael@0 20 <content>
michael@0 21 <children includes="menupopup"/>
michael@0 22
michael@0 23 <xul:hbox class="autocomplete-textbox-container" flex="1" align="center">
michael@0 24 <children includes="image|deck|stack|box">
michael@0 25 <xul:image class="autocomplete-icon" allowevents="true"/>
michael@0 26 </children>
michael@0 27
michael@0 28 <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,tooltiptext=inputtooltiptext">
michael@0 29 <children/>
michael@0 30 <html:input anonid="input" class="autocomplete-textbox textbox-input"
michael@0 31 allowevents="true"
michael@0 32 xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,userAction"/>
michael@0 33 </xul:hbox>
michael@0 34 <children includes="hbox"/>
michael@0 35 </xul:hbox>
michael@0 36
michael@0 37 <xul:dropmarker class="autocomplete-history-dropmarker" allowevents="true"
michael@0 38 xbl:inherits="open,enablehistory" anonid="historydropmarker"/>
michael@0 39
michael@0 40 <xul:popupset>
michael@0 41 <xul:panel type="autocomplete" anonid="popup"
michael@0 42 ignorekeys="true" noautofocus="true" level="top"
michael@0 43 xbl:inherits="for=id,nomatch"/>
michael@0 44 </xul:popupset>
michael@0 45 </content>
michael@0 46
michael@0 47 <implementation implements="nsIDOMXULMenuListElement">
michael@0 48
michael@0 49 <constructor><![CDATA[
michael@0 50 // XXX bug 90337 band-aid until we figure out what's going on here
michael@0 51 if (this.value != this.mInputElt.value)
michael@0 52 this.mInputElt.value = this.value;
michael@0 53 delete this.value;
michael@0 54
michael@0 55 // listen for pastes
michael@0 56 this.mInputElt.controllers.insertControllerAt(0, this.mPasteController);
michael@0 57
michael@0 58 // listen for menubar activation
michael@0 59 window.top.addEventListener("DOMMenuBarActive", this.mMenuBarListener, true);
michael@0 60
michael@0 61 // set default property values
michael@0 62 this.ifSetAttribute("timeout", 50);
michael@0 63 this.ifSetAttribute("pastetimeout", 1000);
michael@0 64 this.ifSetAttribute("maxrows", 5);
michael@0 65 this.ifSetAttribute("showpopup", true);
michael@0 66 this.ifSetAttribute("disableKeyNavigation", true);
michael@0 67
michael@0 68 // initialize the search sessions
michael@0 69 if (this.hasAttribute("autocompletesearch"))
michael@0 70 this.initAutoCompleteSearch();
michael@0 71
michael@0 72 // hack to work around lack of bottom-up constructor calling
michael@0 73 if ("initialize" in this.popup)
michael@0 74 this.popup.initialize();
michael@0 75 ]]></constructor>
michael@0 76
michael@0 77 <destructor><![CDATA[
michael@0 78 this.clearResults(false);
michael@0 79 window.top.removeEventListener("DOMMenuBarActive", this.mMenuBarListener, true);
michael@0 80 this.mInputElt.controllers.removeController(this.mPasteController);
michael@0 81 ]]></destructor>
michael@0 82
michael@0 83 <!-- =================== nsIAutoCompleteInput =================== -->
michael@0 84 <!-- XXX: This implementation is currently incomplete. -->
michael@0 85
michael@0 86 <!-- reference to the results popup element -->
michael@0 87 <field name="popup"><![CDATA[
michael@0 88 document.getAnonymousElementByAttribute(this, "anonid", "popup");
michael@0 89 ]]></field>
michael@0 90
michael@0 91 <property name="popupOpen"
michael@0 92 onget="return this.mMenuOpen;"
michael@0 93 onset="if (val) this.openPopup(); else this.closePopup(); return val;"/>
michael@0 94
michael@0 95 <!-- option to turn off autocomplete -->
michael@0 96 <property name="disableAutoComplete"
michael@0 97 onset="this.setAttribute('disableautocomplete', val); return val;"
michael@0 98 onget="return this.getAttribute('disableautocomplete') == 'true';"/>
michael@0 99
michael@0 100 <!-- if the resulting match string is not at the beginning of the typed string,
michael@0 101 this will optionally autofill like this "bar |>> foobar|" -->
michael@0 102 <property name="completeDefaultIndex"
michael@0 103 onset="this.setAttribute('completedefaultindex', val); return val;"
michael@0 104 onget="return this.getAttribute('completedefaultindex') == 'true';"/>
michael@0 105
michael@0 106 <!-- option for completing to the default result whenever the user hits
michael@0 107 enter or the textbox loses focus -->
michael@0 108 <property name="forceComplete"
michael@0 109 onset="this.setAttribute('forcecomplete', val); return val;"
michael@0 110 onget="return this.getAttribute('forcecomplete') == 'true';"/>
michael@0 111
michael@0 112 <property name="minResultsForPopup"
michael@0 113 onset="this.setAttribute('minresultsforpopup', val); return val;"
michael@0 114 onget="var t = this.getAttribute('minresultsforpopup'); return t ? parseInt(t) : 1;"/>
michael@0 115
michael@0 116 <!-- maximum number of rows to display -->
michael@0 117 <property name="maxRows"
michael@0 118 onset="this.setAttribute('maxrows', val); return val;"
michael@0 119 onget="return parseInt(this.getAttribute('maxrows')) || 0;"/>
michael@0 120
michael@0 121 <!-- toggles a second column in the results list which contains
michael@0 122 the string in the comment field of each autocomplete result -->
michael@0 123 <property name="showCommentColumn"
michael@0 124 onget="return this.getAttribute('showcommentcolumn') == 'true';">
michael@0 125 <setter><![CDATA[
michael@0 126 this.popup.showCommentColumn = val;
michael@0 127 this.setAttribute('showcommentcolumn', val);
michael@0 128 return val;
michael@0 129 ]]></setter>
michael@0 130 </property>
michael@0 131
michael@0 132 <!-- number of milliseconds after a keystroke before a search begins -->
michael@0 133 <property name="timeout"
michael@0 134 onset="this.setAttribute('timeout', val); return val;"
michael@0 135 onget="return parseInt(this.getAttribute('timeout')) || 0;"/>
michael@0 136
michael@0 137 <property name="searchParam"
michael@0 138 onget="return this.getAttribute('autocompletesearchparam') || '';"
michael@0 139 onset="this.setAttribute('autocompletesearchparam', val); return val;"/>
michael@0 140
michael@0 141 <property name="searchCount" readonly="true"
michael@0 142 onget="return this.sessionCount;"/>
michael@0 143
michael@0 144 <method name="getSearchAt">
michael@0 145 <parameter name="aIndex"/>
michael@0 146 <body><![CDATA[
michael@0 147 var idx = -1;
michael@0 148 for (var name in this.mSessions)
michael@0 149 if (++idx == aIndex)
michael@0 150 return name;
michael@0 151
michael@0 152 return null;
michael@0 153 ]]></body>
michael@0 154 </method>
michael@0 155
michael@0 156 <property name="textValue"
michael@0 157 onget="return this.value;"
michael@0 158 onset="this.setTextValue(val); return val;"/>
michael@0 159
michael@0 160 <method name="onSearchBegin">
michael@0 161 <body><![CDATA[
michael@0 162 this._fireEvent("searchbegin");
michael@0 163 ]]></body>
michael@0 164 </method>
michael@0 165
michael@0 166 <method name="onSearchComplete">
michael@0 167 <body><![CDATA[
michael@0 168 if (this.noMatch)
michael@0 169 this.setAttribute("nomatch", "true");
michael@0 170 else
michael@0 171 this.removeAttribute("nomatch");
michael@0 172
michael@0 173 this._fireEvent("searchcomplete");
michael@0 174 ]]></body>
michael@0 175 </method>
michael@0 176
michael@0 177 <method name="onTextReverted">
michael@0 178 <body><![CDATA[
michael@0 179 return this._fireEvent("textreverted");
michael@0 180 ]]></body>
michael@0 181 </method>
michael@0 182
michael@0 183 <!-- =================== nsIDOMXULMenuListElement =================== -->
michael@0 184
michael@0 185 <property name="editable" readonly="true"
michael@0 186 onget="return true;" />
michael@0 187
michael@0 188 <property name="crop"
michael@0 189 onset="this.setAttribute('crop', val); return val;"
michael@0 190 onget="return this.getAttribute('crop');"/>
michael@0 191
michael@0 192 <property name="label" readonly="true"
michael@0 193 onget="return this.mInputElt.value;"/>
michael@0 194
michael@0 195 <property name="open"
michael@0 196 onget="return this.getAttribute('open') == 'true';">
michael@0 197 <setter>
michael@0 198 <![CDATA[
michael@0 199 var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker");
michael@0 200 if (val) {
michael@0 201 this.setAttribute('open', true);
michael@0 202 historyPopup.showPopup();
michael@0 203 } else {
michael@0 204 this.removeAttribute('open');
michael@0 205 historyPopup.hidePopup();
michael@0 206 }
michael@0 207 ]]>
michael@0 208 </setter>
michael@0 209 </property>
michael@0 210
michael@0 211 <!-- =================== PUBLIC PROPERTIES =================== -->
michael@0 212
michael@0 213 <property name="value"
michael@0 214 onget="return this.mInputElt.value;">
michael@0 215 <setter><![CDATA[
michael@0 216 this.ignoreInputEvent = true;
michael@0 217 this.mInputElt.value = val;
michael@0 218 this.ignoreInputEvent = false;
michael@0 219 var event = document.createEvent('Events');
michael@0 220 event.initEvent('ValueChange', true, true);
michael@0 221 this.mInputElt.dispatchEvent(event);
michael@0 222 return val;
michael@0 223 ]]></setter>
michael@0 224 </property>
michael@0 225
michael@0 226 <property name="focused"
michael@0 227 onget="return this.getAttribute('focused') == 'true';"/>
michael@0 228
michael@0 229 <method name="initAutoCompleteSearch">
michael@0 230 <body><![CDATA[
michael@0 231 var list = this.getAttribute("autocompletesearch").split(" ");
michael@0 232 for (var i = 0; i < list.length; i++) {
michael@0 233 var name = list[i];
michael@0 234 var contractid = "@mozilla.org/autocomplete/search;1?name=" + name;
michael@0 235 if (contractid in Components.classes) {
michael@0 236 try {
michael@0 237 this.mSessions[name] =
michael@0 238 Components.classes[contractid].getService(Components.interfaces.nsIAutoCompleteSearch);
michael@0 239 this.mLastResults[name] = null;
michael@0 240 this.mLastRows[name] = 0;
michael@0 241 ++this.sessionCount;
michael@0 242 } catch (e) {
michael@0 243 dump("### ERROR - unable to create search \"" + name + "\".\n");
michael@0 244 }
michael@0 245 } else {
michael@0 246 dump("search \"" + name + "\" not found - skipping.\n");
michael@0 247 }
michael@0 248 }
michael@0 249 ]]></body>
michael@0 250 </method>
michael@0 251
michael@0 252 <!-- the number of sessions currently in use -->
michael@0 253 <field name="sessionCount">0</field>
michael@0 254
michael@0 255 <!-- number of milliseconds after a paste before a search begins -->
michael@0 256 <property name="pasteTimeout"
michael@0 257 onset="this.setAttribute('pastetimeout', val); return val;"
michael@0 258 onget="var t = parseInt(this.getAttribute('pastetimeout')); return t ? t : 0;"/>
michael@0 259
michael@0 260 <!-- option for filling the textbox with the best match while typing
michael@0 261 and selecting the difference -->
michael@0 262 <property name="autoFill"
michael@0 263 onset="this.setAttribute('autofill', val); return val;"
michael@0 264 onget="return this.getAttribute('autofill') == 'true';"/>
michael@0 265
michael@0 266 <!-- if this attribute is set, allow different style for
michael@0 267 non auto-completed lines -->
michael@0 268 <property name="highlightNonMatches"
michael@0 269 onset="this.setAttribute('highlightnonmatches', val); return val;"
michael@0 270 onget="return this.getAttribute('highlightnonmatches') == 'true';"/>
michael@0 271
michael@0 272 <!-- option to show the popup containing the results -->
michael@0 273 <property name="showPopup"
michael@0 274 onset="this.setAttribute('showpopup', val); return val;"
michael@0 275 onget="return this.getAttribute('showpopup') == 'true';"/>
michael@0 276
michael@0 277 <!-- option to allow scrolling through the list via the tab key, rather than
michael@0 278 tab moving focus out of the textbox -->
michael@0 279 <property name="tabScrolling"
michael@0 280 onset="return this.setAttribute('tabscrolling', val); return val;"
michael@0 281 onget="return this.getAttribute('tabscrolling') == 'true';"/>
michael@0 282
michael@0 283 <!-- option to completely ignore any blur events while
michael@0 284 searches are still going on. This is useful so that nothing
michael@0 285 gets autopicked if the window is required to lose focus for
michael@0 286 some reason (eg in LDAP autocomplete, another window may be
michael@0 287 brought up so that the user can enter a password to authenticate
michael@0 288 to an LDAP server). -->
michael@0 289 <property name="ignoreBlurWhileSearching"
michael@0 290 onset="this.setAttribute('ignoreblurwhilesearching', val); return val;"
michael@0 291 onget="return this.getAttribute('ignoreblurwhilesearching') == 'true';"/>
michael@0 292
michael@0 293 <!-- state which indicates the current action being performed by the user.
michael@0 294 Possible values are : none, typing, scrolling -->
michael@0 295 <property name="userAction"
michael@0 296 onset="this.setAttribute('userAction', val); return val;"
michael@0 297 onget="return this.getAttribute('userAction');"/>
michael@0 298
michael@0 299 <!-- state which indicates if the last search had no matches -->
michael@0 300 <field name="noMatch">true</field>
michael@0 301
michael@0 302 <!-- state which indicates a search is currently happening -->
michael@0 303 <field name="isSearching">false</field>
michael@0 304
michael@0 305 <!-- state which indicates a search timeout is current waiting -->
michael@0 306 <property name="isWaiting"
michael@0 307 onget="return this.mAutoCompleteTimer != 0;"/>
michael@0 308
michael@0 309 <!-- =================== PRIVATE PROPERTIES =================== -->
michael@0 310
michael@0 311 <field name="mSessions">({})</field>
michael@0 312 <field name="mLastResults">({})</field>
michael@0 313 <field name="mLastRows">({})</field>
michael@0 314 <field name="mLastKeyCode">null</field>
michael@0 315 <field name="mAutoCompleteTimer">0</field>
michael@0 316 <field name="mMenuOpen">false</field>
michael@0 317 <field name="mFireAfterSearch">false</field>
michael@0 318 <field name="mFinishAfterSearch">false</field>
michael@0 319 <field name="mNeedToFinish">false</field>
michael@0 320 <field name="mNeedToComplete">false</field>
michael@0 321 <field name="mTransientValue">false</field>
michael@0 322 <field name="mView">null</field>
michael@0 323 <field name="currentSearchString">""</field>
michael@0 324 <field name="ignoreInputEvent">false</field>
michael@0 325 <field name="oninit">null</field>
michael@0 326 <field name="mDefaultMatchFilled">false</field>
michael@0 327 <field name="mFirstReturn">true</field>
michael@0 328 <field name="mIsPasting">false</field>
michael@0 329
michael@0 330 <field name="mPasteController"><![CDATA[
michael@0 331 ({
michael@0 332 self: this,
michael@0 333 kGlobalClipboard: Components.interfaces.nsIClipboard.kGlobalClipboard,
michael@0 334 supportsCommand: function(aCommand) {
michael@0 335 return aCommand == "cmd_paste";
michael@0 336 },
michael@0 337 isCommandEnabled: function(aCommand) {
michael@0 338 return aCommand == "cmd_paste" &&
michael@0 339 this.self.editor.isSelectionEditable &&
michael@0 340 this.self.editor.canPaste(this.kGlobalClipboard);
michael@0 341 },
michael@0 342 doCommand: function(aCommand) {
michael@0 343 if (aCommand == "cmd_paste") {
michael@0 344 this.self.mIsPasting = true;
michael@0 345 this.self.editor.paste(this.kGlobalClipboard);
michael@0 346 this.self.mIsPasting = false;
michael@0 347 }
michael@0 348 },
michael@0 349 onEvent: function() {}
michael@0 350 })
michael@0 351 ]]></field>
michael@0 352
michael@0 353 <field name="mMenuBarListener"><![CDATA[
michael@0 354 ({
michael@0 355 self: this,
michael@0 356 handleEvent: function(aEvent) {
michael@0 357 try {
michael@0 358 this.self.finishAutoComplete(false, false, aEvent);
michael@0 359 this.self.clearTimer();
michael@0 360 this.self.closePopup();
michael@0 361 } catch (e) {
michael@0 362 window.top.removeEventListener("DOMMenuBarActive", this, true);
michael@0 363 }
michael@0 364 }
michael@0 365 })
michael@0 366 ]]></field>
michael@0 367
michael@0 368 <field name="mAutoCompleteObserver"><![CDATA[
michael@0 369 ({
michael@0 370 self: this,
michael@0 371 onSearchResult: function(aSearch, aResult) {
michael@0 372 for (var name in this.self.mSessions)
michael@0 373 if (this.self.mSessions[name] == aSearch)
michael@0 374 this.self.processResults(name, aResult);
michael@0 375 }
michael@0 376 })
michael@0 377 ]]></field>
michael@0 378
michael@0 379 <field name="mInputElt"><![CDATA[
michael@0 380 document.getAnonymousElementByAttribute(this, "anonid", "input");
michael@0 381 ]]></field>
michael@0 382
michael@0 383 <field name="mMenuAccessKey"><![CDATA[
michael@0 384 Components.classes["@mozilla.org/preferences-service;1"]
michael@0 385 .getService(Components.interfaces.nsIPrefBranch)
michael@0 386 .getIntPref("ui.key.menuAccessKey");
michael@0 387 ]]></field>
michael@0 388
michael@0 389 <!-- =================== PUBLIC METHODS =================== -->
michael@0 390
michael@0 391 <method name="getErrorAt">
michael@0 392 <parameter name="aIndex"/>
michael@0 393 <body><![CDATA[
michael@0 394 var obj = aIndex < 0 ? null : this.convertIndexToSession(aIndex);
michael@0 395 return obj && this.mLastResults[obj.session] &&
michael@0 396 this.mLastResults[obj.session].errorDescription;
michael@0 397 ]]></body>
michael@0 398 </method>
michael@0 399
michael@0 400 <!-- get a value from the autocomplete results as a string via an absolute index-->
michael@0 401 <method name="getResultValueAt">
michael@0 402 <parameter name="aIndex"/>
michael@0 403 <body><![CDATA[
michael@0 404 var obj = this.convertIndexToSession(aIndex);
michael@0 405 return obj ? this.getSessionValueAt(obj.session, obj.index) : null;
michael@0 406 ]]></body>
michael@0 407 </method>
michael@0 408
michael@0 409 <!-- get a value from the autocomplete results as a string from a specific session -->
michael@0 410 <method name="getSessionValueAt">
michael@0 411 <parameter name="aSession"/>
michael@0 412 <parameter name="aIndex"/>
michael@0 413 <body><![CDATA[
michael@0 414 var result = this.mLastResults[aSession];
michael@0 415 return result.errorDescription || result.getValueAt(aIndex);
michael@0 416 ]]></body>
michael@0 417 </method>
michael@0 418
michael@0 419 <!-- get the total number of results overall -->
michael@0 420 <method name="getResultCount">
michael@0 421 <body><![CDATA[
michael@0 422 return this.view.rowCount;
michael@0 423 ]]></body>
michael@0 424 </method>
michael@0 425
michael@0 426 <!-- get the first session that has results -->
michael@0 427 <method name="getDefaultSession">
michael@0 428 <body><![CDATA[
michael@0 429 for (var name in this.mLastResults) {
michael@0 430 var results = this.mLastResults[name];
michael@0 431 if (results && results.matchCount > 0 && !results.errorDescription)
michael@0 432 return name;
michael@0 433 }
michael@0 434 return null;
michael@0 435 ]]></body>
michael@0 436 </method>
michael@0 437
michael@0 438 <!-- empty the cached result data and empty the results popup -->
michael@0 439 <method name="clearResults">
michael@0 440 <parameter name="aInvalidate"/>
michael@0 441 <body><![CDATA[
michael@0 442 this.clearResultData();
michael@0 443 this.clearResultElements(aInvalidate);
michael@0 444 ]]></body>
michael@0 445 </method>
michael@0 446
michael@0 447 <!-- =================== PRIVATE METHODS =================== -->
michael@0 448
michael@0 449 <!-- ::::::::::::: session searching ::::::::::::: -->
michael@0 450
michael@0 451 <!-- -->
michael@0 452 <method name="callListener">
michael@0 453 <parameter name="me"/>
michael@0 454 <parameter name="aAction"/>
michael@0 455 <body><![CDATA[
michael@0 456 // bail if the binding was detached or the element removed from
michael@0 457 // document during the timeout
michael@0 458 if (!("startLookup" in me) || !me.ownerDocument || !me.parentNode)
michael@0 459 return;
michael@0 460
michael@0 461 me.clearTimer();
michael@0 462
michael@0 463 if (me.disableAutoComplete)
michael@0 464 return;
michael@0 465
michael@0 466 switch (aAction) {
michael@0 467 case "startLookup":
michael@0 468 me.startLookup();
michael@0 469 break;
michael@0 470
michael@0 471 case "stopLookup":
michael@0 472 me.stopLookup();
michael@0 473 break;
michael@0 474 }
michael@0 475 ]]></body>
michael@0 476 </method>
michael@0 477
michael@0 478 <!-- -->
michael@0 479 <method name="startLookup">
michael@0 480 <body><![CDATA[
michael@0 481 var str = this.currentSearchString;
michael@0 482 if (!str) {
michael@0 483 this.clearResults(false);
michael@0 484 this.closePopup();
michael@0 485 return;
michael@0 486 }
michael@0 487
michael@0 488 this.isSearching = true;
michael@0 489 this.mFirstReturn = true;
michael@0 490 this.mSessionReturns = this.sessionCount;
michael@0 491 this.mFailureItems = 0;
michael@0 492 this.mDefaultMatchFilled = false; // clear out our prefill state.
michael@0 493
michael@0 494 // Notify the input that the search is beginning.
michael@0 495 this.onSearchBegin();
michael@0 496
michael@0 497 // tell each session to start searching...
michael@0 498 for (var name in this.mSessions)
michael@0 499 try {
michael@0 500 this.mSessions[name].startSearch(str, this.searchParam, this.mLastResults[name], this.mAutoCompleteObserver);
michael@0 501 } catch (e) {
michael@0 502 --this.mSessionReturns;
michael@0 503 this.searchFailed();
michael@0 504 }
michael@0 505 ]]></body>
michael@0 506 </method>
michael@0 507
michael@0 508 <!-- -->
michael@0 509 <method name="stopLookup">
michael@0 510 <body><![CDATA[
michael@0 511 for (var name in this.mSessions)
michael@0 512 this.mSessions[name].stopSearch();
michael@0 513 ]]></body>
michael@0 514 </method>
michael@0 515
michael@0 516 <!-- -->
michael@0 517 <method name="processResults">
michael@0 518 <parameter name="aSessionName"/>
michael@0 519 <parameter name="aResults"/>
michael@0 520 <body><![CDATA[
michael@0 521 if (this.disableAutoComplete)
michael@0 522 return;
michael@0 523
michael@0 524 const ACR = Components.interfaces.nsIAutoCompleteResult;
michael@0 525 var status = aResults.searchResult;
michael@0 526 if (status != ACR.RESULT_NOMATCH_ONGOING &&
michael@0 527 status != ACR.RESULT_SUCCESS_ONGOING)
michael@0 528 --this.mSessionReturns;
michael@0 529
michael@0 530 // check the many criteria for failure
michael@0 531 if (aResults.errorDescription)
michael@0 532 ++this.mFailureItems;
michael@0 533 else if (status == ACR.RESULT_IGNORED ||
michael@0 534 status == ACR.RESULT_FAILURE ||
michael@0 535 status == ACR.RESULT_NOMATCH ||
michael@0 536 status == ACR.RESULT_NOMATCH_ONGOING ||
michael@0 537 aResults.matchCount == 0 ||
michael@0 538 aResults.searchString != this.currentSearchString)
michael@0 539 {
michael@0 540 this.mLastResults[aSessionName] = null;
michael@0 541 if (this.mFirstReturn)
michael@0 542 this.clearResultElements(false);
michael@0 543 this.mFirstReturn = false;
michael@0 544 this.searchFailed();
michael@0 545 return;
michael@0 546 }
michael@0 547
michael@0 548 if (this.mFirstReturn) {
michael@0 549 if (this.view.mTree)
michael@0 550 this.view.mTree.beginUpdateBatch();
michael@0 551 this.clearResultElements(false); // clear results, but don't repaint yet
michael@0 552 }
michael@0 553
michael@0 554 // always call openPopup...we may not have opened it
michael@0 555 // if a previous search session didn't return enough search results.
michael@0 556 // it's smart and doesn't try to open itself multiple times...
michael@0 557 // be sure to add our result elements before calling openResultPopuup as we need
michael@0 558 // to know the total # of results found so far.
michael@0 559 this.addResultElements(aSessionName, aResults);
michael@0 560
michael@0 561 this.autoFillInput(aSessionName, aResults, false);
michael@0 562 if (this.mFirstReturn && this.view.mTree)
michael@0 563 this.view.mTree.endUpdateBatch();
michael@0 564 this.openPopup();
michael@0 565 this.mFirstReturn = false;
michael@0 566
michael@0 567 // if this is the last session to return...
michael@0 568 if (this.mSessionReturns == 0)
michael@0 569 this.postSearchCleanup();
michael@0 570
michael@0 571 if (this.mFinishAfterSearch)
michael@0 572 this.finishAutoComplete(false, this.mFireAfterSearch, null);
michael@0 573 ]]></body>
michael@0 574 </method>
michael@0 575
michael@0 576 <!-- called each time a search fails, except when failure items need
michael@0 577 to be displayed. If all searches have failed, clear the list
michael@0 578 and close the popup -->
michael@0 579 <method name="searchFailed">
michael@0 580 <body><![CDATA[
michael@0 581 // if all searches are done and they all failed...
michael@0 582 if (this.mSessionReturns == 0 && this.getResultCount() == 0) {
michael@0 583 if (this.minResultsForPopup == 0) {
michael@0 584 this.clearResults(true); // clear data and repaint empty
michael@0 585 this.openPopup();
michael@0 586 } else {
michael@0 587 this.closePopup();
michael@0 588 }
michael@0 589 }
michael@0 590
michael@0 591 // if it's the last session to return, time to clean up...
michael@0 592 if (this.mSessionReturns == 0)
michael@0 593 this.postSearchCleanup();
michael@0 594 ]]></body>
michael@0 595 </method>
michael@0 596
michael@0 597 <!-- does some stuff after a search is done (success or failure) -->
michael@0 598 <method name="postSearchCleanup">
michael@0 599 <body><![CDATA[
michael@0 600 this.isSearching = false;
michael@0 601
michael@0 602 // figure out if there are no matches in all search sessions
michael@0 603 var failed = true;
michael@0 604 for (var name in this.mSessions) {
michael@0 605 if (this.mLastResults[name])
michael@0 606 failed = this.mLastResults[name].errorDescription ||
michael@0 607 this.mLastResults[name].matchCount == 0;
michael@0 608 if (!failed)
michael@0 609 break;
michael@0 610 }
michael@0 611 this.noMatch = failed;
michael@0 612
michael@0 613 // if we have processed all of our searches, and none of them gave us a default index,
michael@0 614 // then we should try to auto fill the input field with the first match.
michael@0 615 // note: autoFillInput is smart enough to kick out if we've already prefilled something...
michael@0 616 if (!this.noMatch) {
michael@0 617 var defaultSession = this.getDefaultSession();
michael@0 618 if (defaultSession)
michael@0 619 this.autoFillInput(defaultSession, this.mLastResults[defaultSession], true);
michael@0 620 }
michael@0 621
michael@0 622 // Notify the input that the search is complete.
michael@0 623 this.onSearchComplete();
michael@0 624 ]]></body>
michael@0 625 </method>
michael@0 626
michael@0 627 <!-- when the focus exits the widget or user hits return,
michael@0 628 determine what value to leave in the textbox -->
michael@0 629 <method name="finishAutoComplete">
michael@0 630 <parameter name="aForceComplete"/>
michael@0 631 <parameter name="aFireTextCommand"/>
michael@0 632 <parameter name="aTriggeringEvent"/>
michael@0 633 <body><![CDATA[
michael@0 634 this.mFinishAfterSearch = false;
michael@0 635 this.mFireAfterSearch = false;
michael@0 636 if (this.mNeedToFinish && !this.disableAutoComplete) {
michael@0 637 // set textbox value to either override value, or default search result
michael@0 638 var val = this.popup.overrideValue;
michael@0 639 if (val) {
michael@0 640 this.setTextValue(val);
michael@0 641 this.mNeedToFinish = false;
michael@0 642 } else if (this.mTransientValue ||
michael@0 643 !(this.forceComplete ||
michael@0 644 (aForceComplete &&
michael@0 645 this.mDefaultMatchFilled &&
michael@0 646 this.mNeedToComplete))) {
michael@0 647 this.mNeedToFinish = false;
michael@0 648 } else if (this.isWaiting) {
michael@0 649 // if the user typed, the search results are out of date, so let
michael@0 650 // the search finish, and tell it to come back here when it's done
michael@0 651 this.mFinishAfterSearch = true;
michael@0 652 this.mFireAfterSearch = aFireTextCommand;
michael@0 653 return;
michael@0 654 } else {
michael@0 655 // we want to use the default item index for the first session which gave us a valid
michael@0 656 // default item index...
michael@0 657 for (var name in this.mLastResults) {
michael@0 658 var results = this.mLastResults[name];
michael@0 659 if (results && results.matchCount > 0 &&
michael@0 660 !results.errorDescription && results.defaultIndex != -1)
michael@0 661 {
michael@0 662 val = results.getValueAt(results.defaultIndex);
michael@0 663 this.setTextValue(val);
michael@0 664 this.mDefaultMatchFilled = true;
michael@0 665 this.mNeedToFinish = false;
michael@0 666 break;
michael@0 667 }
michael@0 668 }
michael@0 669
michael@0 670 if (this.mNeedToFinish) {
michael@0 671 // if a search is happening at this juncture, bail out of this function
michael@0 672 // and let the search finish, and tell it to come back here when it's done
michael@0 673 if (this.isSearching) {
michael@0 674 this.mFinishAfterSearch = true;
michael@0 675 this.mFireAfterSearch = aFireTextCommand;
michael@0 676 return;
michael@0 677 }
michael@0 678
michael@0 679 this.mNeedToFinish = false;
michael@0 680 var defaultSession = this.getDefaultSession();
michael@0 681 if (defaultSession)
michael@0 682 {
michael@0 683 // preselect the first one
michael@0 684 var first = this.getSessionValueAt(defaultSession, 0);
michael@0 685 this.setTextValue(first);
michael@0 686 this.mDefaultMatchFilled = true;
michael@0 687 }
michael@0 688 }
michael@0 689 }
michael@0 690
michael@0 691 this.stopLookup();
michael@0 692
michael@0 693 this.closePopup();
michael@0 694 }
michael@0 695
michael@0 696 this.mNeedToComplete = false;
michael@0 697 this.clearTimer();
michael@0 698
michael@0 699 if (aFireTextCommand)
michael@0 700 this._fireEvent("textentered", this.userAction, aTriggeringEvent);
michael@0 701 ]]></body>
michael@0 702 </method>
michael@0 703
michael@0 704 <!-- when the user clicks an entry in the autocomplete popup -->
michael@0 705 <method name="onResultClick">
michael@0 706 <body><![CDATA[
michael@0 707 // set textbox value to either override value, or the clicked result
michael@0 708 var errItem = this.getErrorAt(this.popup.selectedIndex);
michael@0 709 var val = this.popup.overrideValue;
michael@0 710 if (val)
michael@0 711 this.setTextValue(val);
michael@0 712 else if (this.popup.selectedIndex != -1) {
michael@0 713 if (errItem) {
michael@0 714 this.setTextValue(this.currentSearchString);
michael@0 715 this.mTransientValue = true;
michael@0 716 } else {
michael@0 717 this.setTextValue(this.getResultValueAt(
michael@0 718 this.popup.selectedIndex));
michael@0 719 }
michael@0 720 }
michael@0 721
michael@0 722 this.mNeedToFinish = false;
michael@0 723 this.mNeedToComplete = false;
michael@0 724
michael@0 725 this.closePopup();
michael@0 726
michael@0 727 this.currentSearchString = "";
michael@0 728
michael@0 729 if (errItem)
michael@0 730 this._fireEvent("errorcommand", errItem);
michael@0 731 this._fireEvent("textentered", "clicking");
michael@0 732 ]]></body>
michael@0 733 </method>
michael@0 734
michael@0 735 <!-- when the user hits escape, revert the previously typed value in the textbox -->
michael@0 736 <method name="undoAutoComplete">
michael@0 737 <body><![CDATA[
michael@0 738 var val = this.currentSearchString;
michael@0 739
michael@0 740 var ok = this.onTextReverted();
michael@0 741 if ((ok || ok == undefined) && val)
michael@0 742 this.setTextValue(val);
michael@0 743
michael@0 744 this.userAction = "typing";
michael@0 745
michael@0 746 this.currentSearchString = this.value;
michael@0 747 this.mNeedToComplete = false;
michael@0 748 ]]></body>
michael@0 749 </method>
michael@0 750
michael@0 751 <!-- convert an absolute result index into a session name/index pair -->
michael@0 752 <method name="convertIndexToSession">
michael@0 753 <parameter name="aIndex"/>
michael@0 754 <body><![CDATA[
michael@0 755 for (var name in this.mLastRows) {
michael@0 756 if (aIndex < this.mLastRows[name])
michael@0 757 return { session: name, index: aIndex };
michael@0 758 aIndex -= this.mLastRows[name];
michael@0 759 }
michael@0 760 return null;
michael@0 761 ]]></body>
michael@0 762 </method>
michael@0 763
michael@0 764 <!-- ::::::::::::: user input handling ::::::::::::: -->
michael@0 765
michael@0 766 <!-- -->
michael@0 767 <method name="processInput">
michael@0 768 <body><![CDATA[
michael@0 769 // stop current lookup in case it's async.
michael@0 770 this.stopLookup();
michael@0 771 // stop the queued up lookup on a timer
michael@0 772 this.clearTimer();
michael@0 773
michael@0 774 if (this.disableAutoComplete)
michael@0 775 return;
michael@0 776
michael@0 777 this.userAction = "typing";
michael@0 778 this.mFinishAfterSearch = false;
michael@0 779 this.mNeedToFinish = true;
michael@0 780 this.mTransientValue = false;
michael@0 781 this.mNeedToComplete = true;
michael@0 782 var str = this.value;
michael@0 783 this.currentSearchString = str;
michael@0 784 this.popup.clearSelection();
michael@0 785
michael@0 786 var timeout = this.mIsPasting ? this.pasteTimeout : this.timeout;
michael@0 787 this.mAutoCompleteTimer = setTimeout(this.callListener, timeout, this, "startLookup");
michael@0 788 ]]></body>
michael@0 789 </method>
michael@0 790
michael@0 791 <!-- -->
michael@0 792 <method name="processKeyPress">
michael@0 793 <parameter name="aEvent"/>
michael@0 794 <body><![CDATA[
michael@0 795 this.mLastKeyCode = aEvent.keyCode;
michael@0 796
michael@0 797 var killEvent = false;
michael@0 798
michael@0 799 switch (aEvent.keyCode) {
michael@0 800 case KeyEvent.DOM_VK_TAB:
michael@0 801 if (this.tabScrolling) {
michael@0 802 // don't kill this event if alt-tab or ctrl-tab is hit
michael@0 803 if (!aEvent.altKey && !aEvent.ctrlKey) {
michael@0 804 killEvent = this.mMenuOpen;
michael@0 805 if (killEvent)
michael@0 806 this.keyNavigation(aEvent);
michael@0 807 }
michael@0 808 }
michael@0 809 break;
michael@0 810
michael@0 811 case KeyEvent.DOM_VK_RETURN:
michael@0 812
michael@0 813 // if this is a failure item, save it for fireErrorCommand
michael@0 814 var errItem = this.getErrorAt(this.popup.selectedIndex);
michael@0 815
michael@0 816 killEvent = this.mMenuOpen;
michael@0 817 this.finishAutoComplete(true, true, aEvent);
michael@0 818 this.closePopup();
michael@0 819 if (errItem) {
michael@0 820 this._fireEvent("errorcommand", errItem);
michael@0 821 }
michael@0 822 break;
michael@0 823
michael@0 824 case KeyEvent.DOM_VK_ESCAPE:
michael@0 825 this.clearTimer();
michael@0 826 killEvent = this.mMenuOpen;
michael@0 827 this.undoAutoComplete();
michael@0 828 this.closePopup();
michael@0 829 break;
michael@0 830
michael@0 831 case KeyEvent.DOM_VK_LEFT:
michael@0 832 case KeyEvent.DOM_VK_RIGHT:
michael@0 833 case KeyEvent.DOM_VK_HOME:
michael@0 834 case KeyEvent.DOM_VK_END:
michael@0 835 this.finishAutoComplete(true, false, aEvent);
michael@0 836 this.clearTimer();
michael@0 837 this.closePopup();
michael@0 838 break;
michael@0 839
michael@0 840 case KeyEvent.DOM_VK_DOWN:
michael@0 841 if (!aEvent.altKey) {
michael@0 842 this.clearTimer();
michael@0 843 killEvent = this.keyNavigation(aEvent);
michael@0 844 break;
michael@0 845 }
michael@0 846 // Alt+Down falls through to history popup toggling code
michael@0 847
michael@0 848 case KeyEvent.DOM_VK_F4:
michael@0 849 if (!aEvent.ctrlKey && !aEvent.shiftKey && this.getAttribute("enablehistory") == "true") {
michael@0 850 var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker");
michael@0 851 if (historyPopup)
michael@0 852 historyPopup.showPopup();
michael@0 853 else
michael@0 854 historyPopup.hidePopup();
michael@0 855 }
michael@0 856 break;
michael@0 857 case KeyEvent.DOM_VK_PAGE_UP:
michael@0 858 case KeyEvent.DOM_VK_PAGE_DOWN:
michael@0 859 case KeyEvent.DOM_VK_UP:
michael@0 860 if (!aEvent.ctrlKey && !aEvent.metaKey) {
michael@0 861 this.clearTimer();
michael@0 862 killEvent = this.keyNavigation(aEvent);
michael@0 863 }
michael@0 864 break;
michael@0 865
michael@0 866 case KeyEvent.DOM_VK_BACK_SPACE:
michael@0 867 if (!aEvent.ctrlKey && !aEvent.altKey && !aEvent.shiftKey &&
michael@0 868 this.selectionStart == this.currentSearchString.length &&
michael@0 869 this.selectionEnd == this.value.length &&
michael@0 870 this.mDefaultMatchFilled) {
michael@0 871 this.mDefaultMatchFilled = false;
michael@0 872 this.value = this.currentSearchString;
michael@0 873 }
michael@0 874
michael@0 875 if (!/Mac/.test(navigator.platform))
michael@0 876 break;
michael@0 877 case KeyEvent.DOM_VK_DELETE:
michael@0 878 if (/Mac/.test(navigator.platform) && !aEvent.shiftKey)
michael@0 879 break;
michael@0 880
michael@0 881 if (this.mMenuOpen && this.popup.selectedIndex != -1) {
michael@0 882 var obj = this.convertIndexToSession(this.popup.selectedIndex);
michael@0 883 if (obj) {
michael@0 884 var result = this.mLastResults[obj.session];
michael@0 885 if (!result.errorDescription) {
michael@0 886 var count = result.matchCount;
michael@0 887 result.removeValueAt(obj.index, true);
michael@0 888 this.view.updateResults(this.popup.selectedIndex, result.matchCount - count);
michael@0 889 killEvent = true;
michael@0 890 }
michael@0 891 }
michael@0 892 }
michael@0 893 break;
michael@0 894 }
michael@0 895
michael@0 896 if (killEvent) {
michael@0 897 aEvent.preventDefault();
michael@0 898 aEvent.stopPropagation();
michael@0 899 }
michael@0 900
michael@0 901 return true;
michael@0 902 ]]></body>
michael@0 903 </method>
michael@0 904
michael@0 905 <!-- -->
michael@0 906 <method name="processStartComposition">
michael@0 907 <body><![CDATA[
michael@0 908 this.finishAutoComplete(false, false, null);
michael@0 909 this.clearTimer();
michael@0 910 this.closePopup();
michael@0 911 ]]></body>
michael@0 912 </method>
michael@0 913
michael@0 914 <!-- -->
michael@0 915 <method name="keyNavigation">
michael@0 916 <parameter name="aEvent"/>
michael@0 917 <body><![CDATA[
michael@0 918 var k = aEvent.keyCode;
michael@0 919 if (k == KeyEvent.DOM_VK_TAB ||
michael@0 920 k == KeyEvent.DOM_VK_UP || k == KeyEvent.DOM_VK_DOWN ||
michael@0 921 k == KeyEvent.DOM_VK_PAGE_UP || k == KeyEvent.DOM_VK_PAGE_DOWN)
michael@0 922 {
michael@0 923 if (!this.mMenuOpen) {
michael@0 924 // Original xpfe style was to allow the up and down keys to have
michael@0 925 // their default Mac action if the popup could not be opened.
michael@0 926 // For compatibility for toolkit we now have to predict which
michael@0 927 // keys have a default action that we can always allow to fire.
michael@0 928 if (/Mac/.test(navigator.platform) &&
michael@0 929 ((k == KeyEvent.DOM_VK_UP &&
michael@0 930 (this.selectionStart != 0 ||
michael@0 931 this.selectionEnd != 0)) ||
michael@0 932 (k == KeyEvent.DOM_VK_DOWN &&
michael@0 933 (this.selectionStart != this.value.length ||
michael@0 934 this.selectionEnd != this.value.length))))
michael@0 935 return false;
michael@0 936 if (this.currentSearchString != this.value) {
michael@0 937 this.processInput();
michael@0 938 return true;
michael@0 939 }
michael@0 940 if (this.view.rowCount < this.minResultsForPopup)
michael@0 941 return true; // used to be false, see above
michael@0 942
michael@0 943 this.mNeedToFinish = true;
michael@0 944 this.openPopup();
michael@0 945 return true;
michael@0 946 }
michael@0 947
michael@0 948 this.userAction = "scrolling";
michael@0 949 this.mNeedToComplete = false;
michael@0 950
michael@0 951 var reverse = k == KeyEvent.DOM_VK_TAB && aEvent.shiftKey ||
michael@0 952 k == KeyEvent.DOM_VK_UP ||
michael@0 953 k == KeyEvent.DOM_VK_PAGE_UP;
michael@0 954 var page = k == KeyEvent.DOM_VK_PAGE_UP ||
michael@0 955 k == KeyEvent.DOM_VK_PAGE_DOWN;
michael@0 956 var selected = this.popup.selectBy(reverse, page);
michael@0 957
michael@0 958 // determine which value to place in the textbox
michael@0 959 this.ignoreInputEvent = true;
michael@0 960 if (selected != -1) {
michael@0 961 if (this.getErrorAt(selected)) {
michael@0 962 if (this.currentSearchString)
michael@0 963 this.setTextValue(this.currentSearchString);
michael@0 964 } else {
michael@0 965 this.setTextValue(this.getResultValueAt(selected));
michael@0 966 }
michael@0 967 this.mTransientValue = true;
michael@0 968 } else {
michael@0 969 if (this.currentSearchString)
michael@0 970 this.setTextValue(this.currentSearchString);
michael@0 971 this.mTransientValue = false;
michael@0 972 }
michael@0 973
michael@0 974 // move cursor to the end
michael@0 975 this.mInputElt.setSelectionRange(this.value.length, this.value.length);
michael@0 976 this.ignoreInputEvent = false;
michael@0 977 }
michael@0 978 return true;
michael@0 979 ]]></body>
michael@0 980 </method>
michael@0 981
michael@0 982 <!-- while the user is typing, fill the textbox with the "default" value
michael@0 983 if one can be assumed, and select the end of the text -->
michael@0 984 <method name="autoFillInput">
michael@0 985 <parameter name="aSessionName"/>
michael@0 986 <parameter name="aResults"/>
michael@0 987 <parameter name="aUseFirstMatchIfNoDefault"/>
michael@0 988 <body><![CDATA[
michael@0 989 if (this.mInputElt.selectionEnd < this.currentSearchString.length ||
michael@0 990 this.mDefaultMatchFilled)
michael@0 991 return;
michael@0 992
michael@0 993 if (!this.mFinishAfterSearch &&
michael@0 994 (this.autoFill || this.completeDefaultIndex) &&
michael@0 995 this.mLastKeyCode != KeyEvent.DOM_VK_BACK_SPACE &&
michael@0 996 this.mLastKeyCode != KeyEvent.DOM_VK_DELETE) {
michael@0 997 var indexToUse = aResults.defaultIndex;
michael@0 998 if (aUseFirstMatchIfNoDefault && indexToUse == -1)
michael@0 999 indexToUse = 0;
michael@0 1000
michael@0 1001 if (indexToUse != -1) {
michael@0 1002 var resultValue = this.getSessionValueAt(aSessionName, indexToUse);
michael@0 1003 var match = resultValue.toLowerCase();
michael@0 1004 var entry = this.currentSearchString.toLowerCase();
michael@0 1005 this.ignoreInputEvent = true;
michael@0 1006 if (match.indexOf(entry) == 0) {
michael@0 1007 var endPoint = this.value.length;
michael@0 1008 this.setTextValue(this.value + resultValue.substr(endPoint));
michael@0 1009 this.mInputElt.setSelectionRange(endPoint, this.value.length);
michael@0 1010 } else {
michael@0 1011 if (this.completeDefaultIndex) {
michael@0 1012 this.setTextValue(this.value + " >> " + resultValue);
michael@0 1013 this.mInputElt.setSelectionRange(entry.length, this.value.length);
michael@0 1014 } else {
michael@0 1015 var postIndex = resultValue.indexOf(this.value);
michael@0 1016 if (postIndex >= 0) {
michael@0 1017 var startPt = this.value.length;
michael@0 1018 this.setTextValue(this.value +
michael@0 1019 resultValue.substr(startPt+postIndex));
michael@0 1020 this.mInputElt.setSelectionRange(startPt, this.value.length);
michael@0 1021 }
michael@0 1022 }
michael@0 1023 }
michael@0 1024 this.mNeedToComplete = true;
michael@0 1025 this.ignoreInputEvent = false;
michael@0 1026 this.mDefaultMatchFilled = true;
michael@0 1027 }
michael@0 1028 }
michael@0 1029 ]]></body>
michael@0 1030 </method>
michael@0 1031
michael@0 1032 <!-- ::::::::::::: popup and tree ::::::::::::: -->
michael@0 1033
michael@0 1034 <!-- -->
michael@0 1035 <method name="openPopup">
michael@0 1036 <body><![CDATA[
michael@0 1037 if (!this.mMenuOpen && this.focused &&
michael@0 1038 (this.getResultCount() >= this.minResultsForPopup ||
michael@0 1039 this.mFailureItems)) {
michael@0 1040 var w = this.boxObject.width;
michael@0 1041 if (w != this.popup.boxObject.width)
michael@0 1042 this.popup.setAttribute("width", w);
michael@0 1043 this.popup.showPopup(this, -1, -1, "popup", "bottomleft", "topleft");
michael@0 1044 this.mMenuOpen = true;
michael@0 1045 }
michael@0 1046 ]]></body>
michael@0 1047 </method>
michael@0 1048
michael@0 1049 <!-- -->
michael@0 1050 <method name="closePopup">
michael@0 1051 <body><![CDATA[
michael@0 1052 if (this.popup && this.mMenuOpen) {
michael@0 1053 this.popup.hidePopup();
michael@0 1054 this.mMenuOpen = false;
michael@0 1055 }
michael@0 1056 ]]></body>
michael@0 1057 </method>
michael@0 1058
michael@0 1059 <!-- -->
michael@0 1060 <method name="addResultElements">
michael@0 1061 <parameter name="aSession"/>
michael@0 1062 <parameter name="aResults"/>
michael@0 1063 <body><![CDATA[
michael@0 1064 var count = aResults.errorDescription ? 1 : aResults.matchCount;
michael@0 1065 if (this.focused && this.showPopup) {
michael@0 1066 var row = 0;
michael@0 1067 for (var name in this.mSessions) {
michael@0 1068 row += this.mLastRows[name];
michael@0 1069 if (name == aSession)
michael@0 1070 break;
michael@0 1071 }
michael@0 1072 this.view.updateResults(row, count - this.mLastRows[name]);
michael@0 1073 this.popup.adjustHeight();
michael@0 1074 }
michael@0 1075 this.mLastResults[aSession] = aResults;
michael@0 1076 this.mLastRows[aSession] = count;
michael@0 1077 ]]></body>
michael@0 1078 </method>
michael@0 1079
michael@0 1080 <!-- -->
michael@0 1081 <method name="clearResultElements">
michael@0 1082 <parameter name="aInvalidate"/>
michael@0 1083 <body><![CDATA[
michael@0 1084 for (var name in this.mSessions)
michael@0 1085 this.mLastRows[name] = 0;
michael@0 1086 this.view.clearResults();
michael@0 1087 if (aInvalidate)
michael@0 1088 this.popup.adjustHeight();
michael@0 1089
michael@0 1090 this.noMatch = true;
michael@0 1091 ]]></body>
michael@0 1092 </method>
michael@0 1093
michael@0 1094 <!-- -->
michael@0 1095 <method name="setTextValue">
michael@0 1096 <parameter name="aValue"/>
michael@0 1097 <body><![CDATA[
michael@0 1098 this.value = aValue;
michael@0 1099
michael@0 1100 // Completing a result should simulate the user typing the result,
michael@0 1101 // so fire an input event.
michael@0 1102 var evt = document.createEvent("UIEvents");
michael@0 1103 evt.initUIEvent("input", true, false, window, 0);
michael@0 1104 var oldIgnoreInput = this.ignoreInputEvent;
michael@0 1105 this.ignoreInputEvent = true;
michael@0 1106 this.dispatchEvent(evt);
michael@0 1107 this.ignoreInputEvent = oldIgnoreInput;
michael@0 1108 ]]></body>
michael@0 1109 </method>
michael@0 1110
michael@0 1111 <!-- -->
michael@0 1112 <method name="clearResultData">
michael@0 1113 <body><![CDATA[
michael@0 1114 for (var name in this.mSessions)
michael@0 1115 this.mLastResults[name] = null;
michael@0 1116 ]]></body>
michael@0 1117 </method>
michael@0 1118
michael@0 1119 <!-- ::::::::::::: miscellaneous ::::::::::::: -->
michael@0 1120
michael@0 1121 <!-- -->
michael@0 1122 <method name="ifSetAttribute">
michael@0 1123 <parameter name="aAttr"/>
michael@0 1124 <parameter name="aVal"/>
michael@0 1125 <body><![CDATA[
michael@0 1126 if (!this.hasAttribute(aAttr))
michael@0 1127 this.setAttribute(aAttr, aVal);
michael@0 1128 ]]></body>
michael@0 1129 </method>
michael@0 1130
michael@0 1131 <!-- -->
michael@0 1132 <method name="clearTimer">
michael@0 1133 <body><![CDATA[
michael@0 1134 if (this.mAutoCompleteTimer) {
michael@0 1135 clearTimeout(this.mAutoCompleteTimer);
michael@0 1136 this.mAutoCompleteTimer = 0;
michael@0 1137 }
michael@0 1138 ]]></body>
michael@0 1139 </method>
michael@0 1140
michael@0 1141 <!-- ::::::::::::: event dispatching ::::::::::::: -->
michael@0 1142
michael@0 1143 <method name="_fireEvent">
michael@0 1144 <parameter name="aEventType"/>
michael@0 1145 <parameter name="aEventParam"/>
michael@0 1146 <parameter name="aTriggeringEvent"/>
michael@0 1147 <body>
michael@0 1148 <![CDATA[
michael@0 1149 var noCancel = true;
michael@0 1150 // handle any xml attribute event handlers
michael@0 1151 var handler = this.getAttribute("on"+aEventType);
michael@0 1152 if (handler) {
michael@0 1153 var fn = new Function("eventParam", "domEvent", handler);
michael@0 1154 var returned = fn.apply(this, [aEventParam, aTriggeringEvent]);
michael@0 1155 if (returned == false)
michael@0 1156 noCancel = false;
michael@0 1157 }
michael@0 1158
michael@0 1159 return noCancel;
michael@0 1160 ]]>
michael@0 1161 </body>
michael@0 1162 </method>
michael@0 1163
michael@0 1164 <!-- =================== TREE VIEW =================== -->
michael@0 1165
michael@0 1166 <field name="view"><![CDATA[
michael@0 1167 ({
michael@0 1168 mTextbox: this,
michael@0 1169 mTree: null,
michael@0 1170 mSelection: null,
michael@0 1171 mRowCount: 0,
michael@0 1172
michael@0 1173 clearResults: function()
michael@0 1174 {
michael@0 1175 var oldCount = this.mRowCount;
michael@0 1176 this.mRowCount = 0;
michael@0 1177
michael@0 1178 if (this.mTree) {
michael@0 1179 this.mTree.rowCountChanged(0, -oldCount);
michael@0 1180 this.mTree.scrollToRow(0);
michael@0 1181 }
michael@0 1182 },
michael@0 1183
michael@0 1184 updateResults: function(aRow, aCount)
michael@0 1185 {
michael@0 1186 this.mRowCount += aCount;
michael@0 1187
michael@0 1188 if (this.mTree)
michael@0 1189 this.mTree.rowCountChanged(aRow, aCount);
michael@0 1190 },
michael@0 1191
michael@0 1192 //////////////////////////////////////////////////////////
michael@0 1193 // nsIAutoCompleteController interface
michael@0 1194
michael@0 1195 // this is the only method required by the treebody mouseup handler
michael@0 1196 handleEnter: function(aIsPopupSelection) {
michael@0 1197 this.mTextbox.onResultClick();
michael@0 1198 },
michael@0 1199
michael@0 1200 //////////////////////////////////////////////////////////
michael@0 1201 // nsITreeView interface
michael@0 1202
michael@0 1203 get rowCount() {
michael@0 1204 return this.mRowCount;
michael@0 1205 },
michael@0 1206
michael@0 1207 get selection() {
michael@0 1208 return this.mSelection;
michael@0 1209 },
michael@0 1210
michael@0 1211 set selection(aVal) {
michael@0 1212 return this.mSelection = aVal;
michael@0 1213 },
michael@0 1214
michael@0 1215 setTree: function(aTree)
michael@0 1216 {
michael@0 1217 this.mTree = aTree;
michael@0 1218 },
michael@0 1219
michael@0 1220 getCellText: function(aRow, aCol)
michael@0 1221 {
michael@0 1222 for (var name in this.mTextbox.mSessions) {
michael@0 1223 if (aRow < this.mTextbox.mLastRows[name]) {
michael@0 1224 var result = this.mTextbox.mLastResults[name];
michael@0 1225 switch (aCol.id) {
michael@0 1226 case "treecolAutoCompleteValue":
michael@0 1227 return result.errorDescription || result.getLabelAt(aRow);
michael@0 1228 case "treecolAutoCompleteComment":
michael@0 1229 if (!result.errorDescription)
michael@0 1230 return result.getCommentAt(aRow);
michael@0 1231 default:
michael@0 1232 return "";
michael@0 1233 }
michael@0 1234 }
michael@0 1235 aRow -= this.mTextbox.mLastRows[name];
michael@0 1236 }
michael@0 1237 return "";
michael@0 1238 },
michael@0 1239
michael@0 1240 getRowProperties: function(aIndex)
michael@0 1241 {
michael@0 1242 return "";
michael@0 1243 },
michael@0 1244
michael@0 1245 getCellProperties: function(aIndex, aCol)
michael@0 1246 {
michael@0 1247 // for the value column, append nsIAutoCompleteItem::className
michael@0 1248 // to the property list so that we can style this column
michael@0 1249 // using that property
michael@0 1250 if (aCol.id == "treecolAutoCompleteValue") {
michael@0 1251 for (var name in this.mTextbox.mSessions) {
michael@0 1252 if (aIndex < this.mTextbox.mLastRows[name]) {
michael@0 1253 var result = this.mTextbox.mLastResults[name];
michael@0 1254 if (result.errorDescription)
michael@0 1255 return "";
michael@0 1256 return result.getStyleAt(aIndex);
michael@0 1257 }
michael@0 1258 aIndex -= this.mTextbox.mLastRows[name];
michael@0 1259 }
michael@0 1260 }
michael@0 1261 return "";
michael@0 1262 },
michael@0 1263
michael@0 1264 getColumnProperties: function(aCol)
michael@0 1265 {
michael@0 1266 return "";
michael@0 1267 },
michael@0 1268
michael@0 1269 getImageSrc: function(aRow, aCol)
michael@0 1270 {
michael@0 1271 if (aCol.id == "treecolAutoCompleteValue") {
michael@0 1272 for (var name in this.mTextbox.mSessions) {
michael@0 1273 if (aRow < this.mTextbox.mLastRows[name]) {
michael@0 1274 var result = this.mTextbox.mLastResults[name];
michael@0 1275 if (result.errorDescription)
michael@0 1276 return "";
michael@0 1277 return result.getImageAt(aRow);
michael@0 1278 }
michael@0 1279 aRow -= this.mTextbox.mLastRows[name];
michael@0 1280 }
michael@0 1281 }
michael@0 1282 return "";
michael@0 1283 },
michael@0 1284
michael@0 1285 getParentIndex: function(aRowIndex) { },
michael@0 1286 hasNextSibling: function(aRowIndex, aAfterIndex) { },
michael@0 1287 getLevel: function(aIndex) {},
michael@0 1288 getProgressMode: function(aRow, aCol) {},
michael@0 1289 getCellValue: function(aRow, aCol) {},
michael@0 1290 isContainer: function(aIndex) {},
michael@0 1291 isContainerOpen: function(aIndex) {},
michael@0 1292 isContainerEmpty: function(aIndex) {},
michael@0 1293 isSeparator: function(aIndex) {},
michael@0 1294 isSorted: function() {},
michael@0 1295 toggleOpenState: function(aIndex) {},
michael@0 1296 selectionChanged: function() {},
michael@0 1297 cycleHeader: function(aCol) {},
michael@0 1298 cycleCell: function(aRow, aCol) {},
michael@0 1299 isEditable: function(aRow, aCol) {},
michael@0 1300 isSelectable: function(aRow, aCol) {},
michael@0 1301 setCellValue: function(aRow, aCol, aValue) {},
michael@0 1302 setCellText: function(aRow, aCol, aValue) {},
michael@0 1303 performAction: function(aAction) {},
michael@0 1304 performActionOnRow: function(aAction, aRow) {},
michael@0 1305 performActionOnCell: function(aAction, aRow, aCol) {}
michael@0 1306 });
michael@0 1307 ]]></field>
michael@0 1308
michael@0 1309 </implementation>
michael@0 1310
michael@0 1311 <handlers>
michael@0 1312 <handler event="input"
michael@0 1313 action="if (!this.ignoreInputEvent) this.processInput();"/>
michael@0 1314
michael@0 1315 <handler event="keypress" phase="capturing"
michael@0 1316 action="return this.processKeyPress(event);"/>
michael@0 1317
michael@0 1318 <handler event="compositionstart" phase="capturing"
michael@0 1319 action="this.processStartComposition();"/>
michael@0 1320
michael@0 1321 <handler event="focus" phase="capturing"
michael@0 1322 action="this.userAction = 'typing';"/>
michael@0 1323
michael@0 1324 <handler event="blur" phase="capturing"
michael@0 1325 action="if ( !(this.ignoreBlurWhileSearching &amp;&amp; this.isSearching) ) {this.userAction = 'none'; this.finishAutoComplete(false, false, event);}"/>
michael@0 1326
michael@0 1327 <handler event="mousedown" phase="capturing"
michael@0 1328 action="if ( !this.mMenuOpen ) this.finishAutoComplete(false, false, event);"/>
michael@0 1329 </handlers>
michael@0 1330 </binding>
michael@0 1331
michael@0 1332 <binding id="autocomplete-result-popup" extends="chrome://global/content/bindings/popup.xml#popup">
michael@0 1333 <resources>
michael@0 1334 <stylesheet src="chrome://global/content/autocomplete.css"/>
michael@0 1335 <stylesheet src="chrome://global/skin/autocomplete.css"/>
michael@0 1336 </resources>
michael@0 1337
michael@0 1338 <content ignorekeys="true" level="top">
michael@0 1339 <xul:tree anonid="tree" class="autocomplete-tree plain" flex="1">
michael@0 1340 <xul:treecols anonid="treecols">
michael@0 1341 <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteValue" flex="2"/>
michael@0 1342 <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteComment" flex="1" hidden="true"/>
michael@0 1343 </xul:treecols>
michael@0 1344 <xul:treechildren anonid="treebody" class="autocomplete-treebody"/>
michael@0 1345 </xul:tree>
michael@0 1346 </content>
michael@0 1347
michael@0 1348 <implementation implements="nsIAutoCompletePopup">
michael@0 1349 <constructor><![CDATA[
michael@0 1350 if (this.textbox && this.textbox.view)
michael@0 1351 this.initialize();
michael@0 1352 ]]></constructor>
michael@0 1353
michael@0 1354 <destructor><![CDATA[
michael@0 1355 if (this.view)
michael@0 1356 this.tree.view = null;
michael@0 1357 ]]></destructor>
michael@0 1358
michael@0 1359 <field name="textbox">
michael@0 1360 document.getBindingParent(this);
michael@0 1361 </field>
michael@0 1362
michael@0 1363 <field name="tree">
michael@0 1364 document.getAnonymousElementByAttribute(this, "anonid", "tree");
michael@0 1365 </field>
michael@0 1366
michael@0 1367 <field name="treecols">
michael@0 1368 document.getAnonymousElementByAttribute(this, "anonid", "treecols");
michael@0 1369 </field>
michael@0 1370
michael@0 1371 <field name="treebody">
michael@0 1372 document.getAnonymousElementByAttribute(this, "anonid", "treebody");
michael@0 1373 </field>
michael@0 1374
michael@0 1375 <field name="view">
michael@0 1376 null
michael@0 1377 </field>
michael@0 1378
michael@0 1379 <!-- Setting tree.view doesn't always immediately create a selection,
michael@0 1380 so we ensure the selection by asking the tree for the view. Note:
michael@0 1381 this.view.selection is quicker if we know the selection exists. -->
michael@0 1382 <property name="selection" onget="return this.tree.view.selection;"/>
michael@0 1383
michael@0 1384 <property name="pageCount"
michael@0 1385 onget="return this.tree.treeBoxObject.getPageLength();"/>
michael@0 1386
michael@0 1387 <field name="maxRows">0</field>
michael@0 1388 <field name="mLastRows">0</field>
michael@0 1389
michael@0 1390 <method name="initialize">
michael@0 1391 <body><![CDATA[
michael@0 1392 this.showCommentColumn = this.textbox.showCommentColumn;
michael@0 1393 this.tree.view = this.textbox.view;
michael@0 1394 this.view = this.textbox.view;
michael@0 1395 this.maxRows = this.textbox.maxRows;
michael@0 1396 ]]></body>
michael@0 1397 </method>
michael@0 1398
michael@0 1399 <property name="showCommentColumn"
michael@0 1400 onget="return !this.treecols.lastChild.hidden;"
michael@0 1401 onset="this.treecols.lastChild.hidden = !val; return val;"/>
michael@0 1402
michael@0 1403 <method name="adjustHeight">
michael@0 1404 <body><![CDATA[
michael@0 1405 // detect the desired height of the tree
michael@0 1406 var bx = this.tree.treeBoxObject;
michael@0 1407 var view = this.view;
michael@0 1408 var rows = this.maxRows || 6;
michael@0 1409 if (!view.rowCount || (rows && view.rowCount < rows))
michael@0 1410 rows = view.rowCount;
michael@0 1411
michael@0 1412 var height = rows * bx.rowHeight;
michael@0 1413
michael@0 1414 if (height == 0)
michael@0 1415 this.tree.setAttribute("collapsed", "true");
michael@0 1416 else {
michael@0 1417 if (this.tree.hasAttribute("collapsed"))
michael@0 1418 this.tree.removeAttribute("collapsed");
michael@0 1419 this.tree.setAttribute("height", height);
michael@0 1420 }
michael@0 1421 ]]></body>
michael@0 1422 </method>
michael@0 1423
michael@0 1424 <method name="clearSelection">
michael@0 1425 <body>
michael@0 1426 this.selection.clearSelection();
michael@0 1427 </body>
michael@0 1428 </method>
michael@0 1429
michael@0 1430 <method name="getNextIndex">
michael@0 1431 <parameter name="aReverse"/>
michael@0 1432 <parameter name="aPage"/>
michael@0 1433 <parameter name="aIndex"/>
michael@0 1434 <parameter name="aMaxRow"/>
michael@0 1435 <body><![CDATA[
michael@0 1436 if (aMaxRow < 0)
michael@0 1437 return -1;
michael@0 1438
michael@0 1439 if (aIndex == -1)
michael@0 1440 return aReverse ? aMaxRow : 0;
michael@0 1441 if (aIndex == (aReverse ? 0 : aMaxRow))
michael@0 1442 return -1;
michael@0 1443
michael@0 1444 var amount = aPage ? this.pageCount - 1 : 1;
michael@0 1445 aIndex = aReverse ? aIndex - amount : aIndex + amount;
michael@0 1446 if (aIndex > aMaxRow)
michael@0 1447 return aMaxRow;
michael@0 1448 if (aIndex < 0)
michael@0 1449 return 0;
michael@0 1450 return aIndex;
michael@0 1451 ]]></body>
michael@0 1452 </method>
michael@0 1453
michael@0 1454 <!-- =================== nsIAutoCompletePopup =================== -->
michael@0 1455
michael@0 1456 <field name="input">
michael@0 1457 null
michael@0 1458 </field>
michael@0 1459
michael@0 1460 <!-- This property is meant to be overriden by bindings extending
michael@0 1461 this one. When the user selects an item from the list by
michael@0 1462 hitting enter or clicking, this method can set the value
michael@0 1463 of the textbox to a different value if it wants to. -->
michael@0 1464 <property name="overrideValue" readonly="true" onget="return null;"/>
michael@0 1465
michael@0 1466 <property name="selectedIndex">
michael@0 1467 <getter>
michael@0 1468 if (!this.view || !this.selection.count)
michael@0 1469 return -1;
michael@0 1470 var start = {}, end = {};
michael@0 1471 this.view.selection.getRangeAt(0, start, end);
michael@0 1472 return start.value;
michael@0 1473 </getter>
michael@0 1474 <setter>
michael@0 1475 if (this.view) {
michael@0 1476 this.selection.select(val);
michael@0 1477 if (val >= 0) {
michael@0 1478 this.view.selection.currentIndex = -1;
michael@0 1479 this.tree.treeBoxObject.ensureRowIsVisible(val);
michael@0 1480 }
michael@0 1481 }
michael@0 1482 return val;
michael@0 1483 </setter>
michael@0 1484 </property>
michael@0 1485
michael@0 1486 <property name="popupOpen" onget="return !!this.input;" readonly="true"/>
michael@0 1487
michael@0 1488 <method name="openAutocompletePopup">
michael@0 1489 <parameter name="aInput"/>
michael@0 1490 <parameter name="aElement"/>
michael@0 1491 <body><![CDATA[
michael@0 1492 if (!this.input) {
michael@0 1493 this.tree.view = aInput.controller;
michael@0 1494 this.view = this.tree.view;
michael@0 1495 this.showCommentColumn = aInput.showCommentColumn;
michael@0 1496 this.maxRows = aInput.maxRows;
michael@0 1497 this.invalidate();
michael@0 1498
michael@0 1499 var viewer = aElement
michael@0 1500 .ownerDocument
michael@0 1501 .defaultView
michael@0 1502 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
michael@0 1503 .getInterface(Components.interfaces.nsIWebNavigation)
michael@0 1504 .QueryInterface(Components.interfaces.nsIDocShell)
michael@0 1505 .contentViewer
michael@0 1506 .QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);
michael@0 1507 var rect = aElement.getBoundingClientRect();
michael@0 1508 var width = Math.round((rect.right - rect.left) * viewer.fullZoom);
michael@0 1509 this.setAttribute("width", width > 100 ? width : 100);
michael@0 1510 // Adjust the direction (which is not inherited) of the autocomplete
michael@0 1511 // popup list, based on the textbox direction. (Bug 707039)
michael@0 1512 this.style.direction = aElement.ownerDocument.defaultView
michael@0 1513 .getComputedStyle(aElement)
michael@0 1514 .direction;
michael@0 1515 const nsIPopupBoxObject = Components.interfaces.nsIPopupBoxObject;
michael@0 1516 this.popupBoxObject.setConsumeRollupEvent(aInput.consumeRollupEvent
michael@0 1517 ? nsIPopupBoxObject.ROLLUP_CONSUME
michael@0 1518 : nsIPopupBoxObject.ROLLUP_NO_CONSUME);
michael@0 1519 this.openPopup(aElement, "after_start", 0, 0, false, false);
michael@0 1520 if (this.state != "closed")
michael@0 1521 this.input = aInput;
michael@0 1522 }
michael@0 1523 ]]></body>
michael@0 1524 </method>
michael@0 1525
michael@0 1526 <method name="closePopup">
michael@0 1527 <body>
michael@0 1528 this.hidePopup();
michael@0 1529 </body>
michael@0 1530 </method>
michael@0 1531
michael@0 1532 <method name="invalidate">
michael@0 1533 <body>
michael@0 1534 if (this.view)
michael@0 1535 this.adjustHeight();
michael@0 1536 this.tree.treeBoxObject.invalidate();
michael@0 1537 </body>
michael@0 1538 </method>
michael@0 1539
michael@0 1540 <method name="selectBy">
michael@0 1541 <parameter name="aReverse"/>
michael@0 1542 <parameter name="aPage"/>
michael@0 1543 <body><![CDATA[
michael@0 1544 try {
michael@0 1545 return this.selectedIndex = this.getNextIndex(aReverse, aPage, this.selectedIndex, this.view.rowCount - 1);
michael@0 1546 } catch (ex) {
michael@0 1547 // do nothing - occasionally timer-related js errors happen here
michael@0 1548 // e.g. "this.selectedIndex has no properties", when you type fast and hit a
michael@0 1549 // navigation key before this popup has opened
michael@0 1550 return -1;
michael@0 1551 }
michael@0 1552 ]]></body>
michael@0 1553 </method>
michael@0 1554 </implementation>
michael@0 1555
michael@0 1556 <handlers>
michael@0 1557 <handler event="popupshowing">
michael@0 1558 if (this.textbox)
michael@0 1559 this.textbox.mMenuOpen = true;
michael@0 1560 </handler>
michael@0 1561
michael@0 1562 <handler event="popuphiding">
michael@0 1563 if (this.textbox)
michael@0 1564 this.textbox.mMenuOpen = false;
michael@0 1565 this.clearSelection();
michael@0 1566 this.input = null;
michael@0 1567 </handler>
michael@0 1568 </handlers>
michael@0 1569 </binding>
michael@0 1570
michael@0 1571 <binding id="autocomplete-treebody">
michael@0 1572 <implementation>
michael@0 1573 <field name="popup">document.getBindingParent(this);</field>
michael@0 1574
michael@0 1575 <field name="mLastMoveTime">Date.now()</field>
michael@0 1576 </implementation>
michael@0 1577
michael@0 1578 <handlers>
michael@0 1579 <handler event="mouseout" action="this.popup.selectedIndex = -1;"/>
michael@0 1580
michael@0 1581 <handler event="mouseup"><![CDATA[
michael@0 1582 var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
michael@0 1583 if (rc != -1) {
michael@0 1584 this.popup.selectedIndex = rc;
michael@0 1585 this.popup.view.handleEnter(true);
michael@0 1586 }
michael@0 1587 ]]></handler>
michael@0 1588
michael@0 1589 <handler event="mousemove"><![CDATA[
michael@0 1590 if (Date.now() - this.mLastMoveTime > 30) {
michael@0 1591 var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
michael@0 1592 if (rc != -1 && rc != this.popup.selectedIndex)
michael@0 1593 this.popup.selectedIndex = rc;
michael@0 1594 this.mLastMoveTime = Date.now();
michael@0 1595 }
michael@0 1596 ]]></handler>
michael@0 1597 </handlers>
michael@0 1598 </binding>
michael@0 1599
michael@0 1600 <binding id="autocomplete-history-popup"
michael@0 1601 extends="chrome://global/content/bindings/popup.xml#popup-scrollbars">
michael@0 1602 <resources>
michael@0 1603 <stylesheet src="chrome://global/content/autocomplete.css"/>
michael@0 1604 <stylesheet src="chrome://global/skin/autocomplete.css"/>
michael@0 1605 </resources>
michael@0 1606
michael@0 1607 <implementation>
michael@0 1608 <method name="removeOpenAttribute">
michael@0 1609 <parameter name="parentNode"/>
michael@0 1610 <body><![CDATA[
michael@0 1611 parentNode.removeAttribute("open");
michael@0 1612 ]]></body>
michael@0 1613 </method>
michael@0 1614 </implementation>
michael@0 1615
michael@0 1616 <handlers>
michael@0 1617 <handler event="popuphiding"><![CDATA[
michael@0 1618 setTimeout(this.removeOpenAttribute, 0, this.parentNode);
michael@0 1619 ]]></handler>
michael@0 1620 </handlers>
michael@0 1621 </binding>
michael@0 1622
michael@0 1623 <binding id="history-dropmarker" extends="chrome://global/content/bindings/general.xml#dropmarker">
michael@0 1624
michael@0 1625 <implementation>
michael@0 1626 <method name="showPopup">
michael@0 1627 <body><![CDATA[
michael@0 1628 var textbox = document.getBindingParent(this);
michael@0 1629 var kids = textbox.getElementsByClassName("autocomplete-history-popup");
michael@0 1630 if (kids.item(0) && textbox.getAttribute("open") != "true") { // Open history popup
michael@0 1631 var w = textbox.boxObject.width;
michael@0 1632 if (w != kids[0].boxObject.width)
michael@0 1633 kids[0].width = w;
michael@0 1634 kids[0].showPopup(textbox, -1, -1, "popup", "bottomleft", "topleft");
michael@0 1635 textbox.setAttribute("open", "true");
michael@0 1636 }
michael@0 1637 ]]></body>
michael@0 1638 </method>
michael@0 1639 </implementation>
michael@0 1640
michael@0 1641 <handlers>
michael@0 1642 <handler event="mousedown"><![CDATA[
michael@0 1643 this.showPopup();
michael@0 1644 ]]></handler>
michael@0 1645 </handlers>
michael@0 1646 </binding>
michael@0 1647
michael@0 1648 </bindings>
michael@0 1649

mercurial