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 Original Code is the Browser Search Service
.
16 # The Initial Developer
of the Original Code is
18 # Portions created by the Initial Developer are
Copyright (C
) 2005
19 # the Initial Developer
. All Rights Reserved
.
22 # Ben Goodger
<beng
@google
.com
> (Original author
)
23 # Gavin Sharp
<gavin
@gavinsharp
.com
>
24 # Pamela Greene
<pamg
.bugs
@gmail
.com
>
25 # Ryan Flint
<rflint
@dslr
.net
>
27 # Alternatively
, the contents
of this file may be used under the terms
of
28 # either the GNU General Public License Version
2 or
later (the
"GPL"), or
29 # the GNU Lesser General Public License Version
2.1 or
later (the
"LGPL"),
30 # in which
case the provisions
of the GPL or the LGPL are applicable instead
31 # of those above
. If you wish to allow
use of your version
of this file only
32 # under the terms
of either the GPL or the LGPL
, and not to allow others to
33 # use your version
of this file under the terms
of the MPL
, indicate your
34 # decision by deleting the provisions above and replace them
with the notice
35 # and other provisions required by the GPL or the LGPL
. If you
do not
delete
36 # the provisions above
, a recipient may
use your version
of this file under
37 # the terms
of any one
of the MPL
, the GPL or the LGPL
.
39 # ***** END LICENSE BLOCK
*****
41 const Ci
= Components
.interfaces
;
42 const Cc
= Components
.classes
;
44 const ENGINE_FLAVOR
= "text/x-moz-search-engine";
46 const BROWSER_SUGGEST_PREF
= "browser.search.suggest.enabled";
48 var gEngineView
= null;
50 var gEngineManagerDialog
= {
51 init
: function engineManager_init() {
52 gEngineView
= new EngineView(new EngineStore());
54 var prefService
= Cc
["@mozilla.org/preferences-service;1"].
55 getService(Ci
.nsIPrefBranch
);
56 var suggestEnabled
= prefService
.getBoolPref(BROWSER_SUGGEST_PREF
);
57 document
.getElementById("enableSuggest").checked
= suggestEnabled
;
59 var tree
= document
.getElementById("engineList");
60 tree
.view
= gEngineView
;
62 var os
= Cc
["@mozilla.org/observer-service;1"].
63 getService(Ci
.nsIObserverService
);
64 os
.addObserver(this, "browser-search-engine-modified", false);
67 observe
: function engineManager_observe(aEngine
, aTopic
, aVerb
) {
68 if (aTopic
== "browser-search-engine-modified") {
69 aEngine
.QueryInterface(Ci
.nsISearchEngine
)
72 gEngineView
._engineStore
.addEngine(aEngine
);
73 gEngineView
.rowCountChanged(gEngineView
.lastIndex
, 1);
75 case "engine-changed":
76 gEngineView
._engineStore
.reloadIcons();
78 case "engine-removed":
79 case "engine-current":
83 gEngineView
.invalidate();
87 onOK
: function engineManager_onOK() {
88 // Remove the observer
89 var os
= Cc
["@mozilla.org/observer-service;1"].
90 getService(Ci
.nsIObserverService
);
91 os
.removeObserver(this, "browser-search-engine-modified");
94 var newSuggestEnabled
= document
.getElementById("enableSuggest").checked
;
95 var prefService
= Cc
["@mozilla.org/preferences-service;1"].
96 getService(Ci
.nsIPrefBranch
);
97 prefService
.setBoolPref(BROWSER_SUGGEST_PREF
, newSuggestEnabled
);
100 gEngineView
._engineStore
.commit();
103 onCancel
: function engineManager_onCancel() {
104 // Remove the observer
105 var os
= Cc
["@mozilla.org/observer-service;1"].
106 getService(Ci
.nsIObserverService
);
107 os
.removeObserver(this, "browser-search-engine-modified");
110 onRestoreDefaults
: function engineManager_onRestoreDefaults() {
111 var num
= gEngineView
._engineStore
.restoreDefaultEngines();
112 gEngineView
.rowCountChanged(0, num
);
113 gEngineView
.invalidate();
116 showRestoreDefaults
: function engineManager_showRestoreDefaults(val
) {
117 document
.documentElement
.getButton("extra2").disabled
= !val
;
120 loadAddEngines
: function engineManager_loadAddEngines() {
122 window
.opener
.BrowserSearch
.loadAddEngines();
126 remove
: function engineManager_remove() {
127 gEngineView
._engineStore
.removeEngine(gEngineView
.selectedEngine
);
128 var index
= gEngineView
.selectedIndex
;
129 gEngineView
.rowCountChanged(index
, -1);
130 gEngineView
.invalidate();
131 gEngineView
.selection
.select(Math
.min(index
, gEngineView
.lastIndex
));
132 gEngineView
.ensureRowIsVisible(Math
.min(index
, gEngineView
.lastIndex
));
133 document
.getElementById("engineList").focus();
137 * Moves the selected engine either up or down in the engine list
139 * -1 to move the selected engine down, +1 to move it up.
141 bump
: function engineManager_move(aDir
) {
142 var selectedEngine
= gEngineView
.selectedEngine
;
143 var newIndex
= gEngineView
.selectedIndex
- aDir
;
145 gEngineView
._engineStore
.moveEngine(selectedEngine
, newIndex
);
147 gEngineView
.invalidate();
148 gEngineView
.selection
.select(newIndex
);
149 gEngineView
.ensureRowIsVisible(newIndex
);
150 this.showRestoreDefaults(true);
151 document
.getElementById("engineList").focus();
154 editKeyword
: function engineManager_editKeyword() {
155 var selectedEngine
= gEngineView
.selectedEngine
;
159 var prompt
= Cc
["@mozilla.org/embedcomp/prompt-service;1"].
160 getService(Ci
.nsIPromptService
);
161 var alias
= { value
: selectedEngine
.alias
};
162 var strings
= document
.getElementById("engineManagerBundle");
163 var title
= strings
.getString("editTitle");
164 var msg
= strings
.getFormattedString("editMsg", [selectedEngine
.name
]);
166 while (prompt
.prompt(window
, title
, msg
, alias
, null, { })) {
167 var bduplicate
= false;
168 var eduplicate
= false;
170 if (alias
.value
!= "") {
171 var searchService
= Cc
["@mozilla.org/browser/search-service;1"].
172 getService(Ci
.nsIBrowserSearchService
);
173 var engine
= searchService
.getEngineByAlias(alias
.value
);
176 if (engine
.name
!= selectedEngine
.name
)
180 var bmserv
= Cc
["@mozilla.org/browser/nav-bookmarks-service;1"].
181 getService(Ci
.nsINavBookmarksService
);
182 if (bmserv
.getURIForKeyword(alias
.value
))
186 // Check for duplicates in changes we haven't committed yet
187 var engines
= gEngineView
._engineStore
.engines
;
188 for each (var engine
in engines
) {
189 if (engine
.alias
== alias
.value
&&
190 engine
.name
!= selectedEngine
.name
) {
198 // Notify the user if they have chosen an existing engine/bookmark keyword
199 if (eduplicate
|| bduplicate
) {
200 var dtitle
= strings
.getString("duplicateTitle");
201 var bmsg
= strings
.getString("duplicateBookmarkMsg");
202 var emsg
= strings
.getFormattedString("duplicateEngineMsg",
205 prompt
.alert(window
, dtitle
, (eduplicate
) ? emsg
: bmsg
);
207 gEngineView
._engineStore
.changeEngine(selectedEngine
, "alias",
209 gEngineView
.invalidate();
215 onSelect
: function engineManager_onSelect() {
216 // buttons only work if an engine is selected and it's not the last engine
217 var disableButtons
= (gEngineView
.selectedIndex
== -1) ||
218 (gEngineView
.lastIndex
== 0);
219 var lastSelected
= (gEngineView
.selectedIndex
== gEngineView
.lastIndex
);
220 var firstSelected
= (gEngineView
.selectedIndex
== 0);
221 var noSelection
= (gEngineView
.selectedIndex
== -1);
223 document
.getElementById("cmd_remove").setAttribute("disabled",
226 document
.getElementById("cmd_moveup").setAttribute("disabled",
227 disableButtons
|| firstSelected
);
229 document
.getElementById("cmd_movedown").setAttribute("disabled",
230 disableButtons
|| lastSelected
);
231 document
.getElementById("cmd_editkeyword").setAttribute("disabled",
236 var gDragObserver
= {
237 onDragStart: function (aEvent
, aXferData
, aDragAction
) {
238 var selectedIndex
= gEngineView
.selectedIndex
;
239 if (selectedIndex
== -1)
242 aXferData
.data
= new TransferData();
243 aXferData
.data
.addDataForFlavour(ENGINE_FLAVOR
, selectedIndex
.toString());
245 aDragAction
.action
= Ci
.nsIDragService
.DRAGDROP_ACTION_MOVE
;
247 onDrop: function (aEvent
, aXferData
, aDragSession
) { },
248 onDragExit: function (aEvent
, aDragSession
) { },
249 onDragOver: function (aEvent
, aFlavour
, aDragSession
) { },
250 getSupportedFlavours: function() { return null; }
253 // "Operation" objects
254 function EngineMoveOp(aEngineClone
, aNewIndex
) {
256 throw new Error("bad args to new EngineMoveOp!");
257 this._engine
= aEngineClone
.originalEngine
;
258 this._newIndex
= aNewIndex
;
260 EngineMoveOp
.prototype = {
263 commit
: function EMO_commit() {
264 var searchService
= Cc
["@mozilla.org/browser/search-service;1"].
265 getService(Ci
.nsIBrowserSearchService
);
266 searchService
.moveEngine(this._engine
, this._newIndex
);
270 function EngineRemoveOp(aEngineClone
) {
272 throw new Error("bad args to new EngineRemoveOp!");
273 this._engine
= aEngineClone
.originalEngine
;
275 EngineRemoveOp
.prototype = {
277 commit
: function ERO_commit() {
278 var searchService
= Cc
["@mozilla.org/browser/search-service;1"].
279 getService(Ci
.nsIBrowserSearchService
);
280 searchService
.removeEngine(this._engine
);
284 function EngineUnhideOp(aEngineClone
, aNewIndex
) {
286 throw new Error("bad args to new EngineUnhideOp!");
287 this._engine
= aEngineClone
.originalEngine
;
288 this._newIndex
= aNewIndex
;
290 EngineUnhideOp
.prototype = {
293 commit
: function EUO_commit() {
294 this._engine
.hidden
= false;
295 var searchService
= Cc
["@mozilla.org/browser/search-service;1"].
296 getService(Ci
.nsIBrowserSearchService
);
297 searchService
.moveEngine(this._engine
, this._newIndex
);
301 function EngineChangeOp(aEngineClone
, aProp
, aValue
) {
303 throw new Error("bad args to new EngineChangeOp!");
305 this._engine
= aEngineClone
.originalEngine
;
307 this._newValue
= aValue
;
309 EngineChangeOp
.prototype = {
313 commit
: function ECO_commit() {
314 this._engine
[this._prop
] = this._newValue
;
318 function EngineStore() {
319 var searchService
= Cc
["@mozilla.org/browser/search-service;1"].
320 getService(Ci
.nsIBrowserSearchService
);
321 this._engines
= searchService
.getVisibleEngines({}).map(this._cloneEngine
);
322 this._defaultEngines
= searchService
.getDefaultEngines({}).map(this._cloneEngine
);
326 // check if we need to disable the restore defaults button
327 var someHidden
= this._defaultEngines
.some(function (e
) {return e
.hidden
;});
328 gEngineManagerDialog
.showRestoreDefaults(someHidden
);
330 EngineStore
.prototype = {
332 _defaultEngines
: null,
336 return this._engines
;
343 _getIndexForEngine
: function ES_getIndexForEngine(aEngine
) {
344 return this._engines
.indexOf(aEngine
);
347 _getEngineByName
: function ES_getEngineByName(aName
) {
348 for each (var engine
in this._engines
)
349 if (engine
.name
== aName
)
355 _cloneEngine
: function ES_cloneObj(aEngine
) {
357 for (var i
in aEngine
)
358 newO
[i
] = aEngine
[i
];
359 newO
.originalEngine
= aEngine
;
363 // Callback for Array's some(). A thisObj must be passed to some()
364 _isSameEngine
: function ES_isSameEngine(aEngineClone
) {
365 return aEngineClone
.originalEngine
== this.originalEngine
;
368 commit
: function ES_commit() {
369 var searchService
= Cc
["@mozilla.org/browser/search-service;1"].
370 getService(Ci
.nsIBrowserSearchService
);
371 var currentEngine
= this._cloneEngine(searchService
.currentEngine
);
372 for (var i
= 0; i
< this._ops
.length
; i
++)
373 this._ops
[i
].commit();
375 // Restore currentEngine if it is a default engine that is still visible.
376 // Needed if the user deletes currentEngine and then restores it.
377 if (this._defaultEngines
.some(this._isSameEngine
, currentEngine
) &&
378 !currentEngine
.originalEngine
.hidden
)
379 searchService
.currentEngine
= currentEngine
.originalEngine
;
382 addEngine
: function ES_addEngine(aEngine
) {
383 this._engines
.push(this._cloneEngine(aEngine
));
386 moveEngine
: function ES_moveEngine(aEngine
, aNewIndex
) {
387 if (aNewIndex
< 0 || aNewIndex
> this._engines
.length
- 1)
388 throw new Error("ES_moveEngine: invalid aNewIndex!");
389 var index
= this._getIndexForEngine(aEngine
);
391 throw new Error("ES_moveEngine: invalid engine?");
393 if (index
== aNewIndex
)
394 return; // nothing to do
396 // Move the engine in our internal store
397 var removedEngine
= this._engines
.splice(index
, 1)[0];
398 this._engines
.splice(aNewIndex
, 0, removedEngine
);
400 this._ops
.push(new EngineMoveOp(aEngine
, aNewIndex
));
403 removeEngine
: function ES_removeEngine(aEngine
) {
404 var index
= this._getIndexForEngine(aEngine
);
406 throw new Error("invalid engine?");
408 this._engines
.splice(index
, 1);
409 this._ops
.push(new EngineRemoveOp(aEngine
));
410 if (this._defaultEngines
.some(this._isSameEngine
, aEngine
))
411 gEngineManagerDialog
.showRestoreDefaults(true);
414 restoreDefaultEngines
: function ES_restoreDefaultEngines() {
417 for (var i
= 0; i
< this._defaultEngines
.length
; ++i
) {
418 var e
= this._defaultEngines
[i
];
420 // If the engine is already in the list, just move it.
421 if (this._engines
.some(this._isSameEngine
, e
)) {
422 this.moveEngine(this._getEngineByName(e
.name
), i
);
424 // Otherwise, add it back to our internal store
425 this._engines
.splice(i
, 0, e
);
426 this._ops
.push(new EngineUnhideOp(e
, i
));
430 gEngineManagerDialog
.showRestoreDefaults(false);
434 changeEngine
: function ES_changeEngine(aEngine
, aProp
, aNewValue
) {
435 var index
= this._getIndexForEngine(aEngine
);
437 throw new Error("invalid engine?");
439 this._engines
[index
][aProp
] = aNewValue
;
440 this._ops
.push(new EngineChangeOp(aEngine
, aProp
, aNewValue
));
443 reloadIcons
: function ES_reloadIcons() {
444 this._engines
.forEach(function (e
) {
445 e
.uri
= e
.originalEngine
.uri
;
450 function EngineView(aEngineStore
) {
451 this._engineStore
= aEngineStore
;
453 EngineView
.prototype = {
458 return this.rowCount
- 1;
460 get selectedIndex() {
461 var seln
= this.selection
;
462 if (seln
.getRangeCount() > 0) {
464 seln
.getRangeAt(0, min
, { });
469 get selectedEngine() {
470 return this._engineStore
.engines
[this.selectedIndex
];
474 rowCountChanged: function (index
, count
) {
475 this.tree
.rowCountChanged(index
, count
);
478 invalidate: function () {
479 this.tree
.invalidate();
482 ensureRowIsVisible: function (index
) {
483 this.tree
.ensureRowIsVisible(index
);
486 getSourceIndexFromDrag: function () {
487 var dragService
= Cc
["@mozilla.org/widget/dragservice;1"].
488 getService().QueryInterface(Ci
.nsIDragService
);
489 var dragSession
= dragService
.getCurrentSession();
490 var transfer
= Cc
["@mozilla.org/widget/transferable;1"].
491 createInstance(Ci
.nsITransferable
);
493 transfer
.addDataFlavor(ENGINE_FLAVOR
);
494 dragSession
.getData(transfer
, 0);
498 var sourceIndex
= -1;
500 transfer
.getAnyTransferData({}, dataObj
, len
);
504 sourceIndex
= dataObj
.value
.QueryInterface(Ci
.nsISupportsString
).data
;
505 sourceIndex
= parseInt(sourceIndex
.substring(0, len
.value
));
513 return this._engineStore
.engines
.length
;
516 getImageSrc: function(index
, column
) {
517 if (column
.id
== "engineName" && this._engineStore
.engines
[index
].iconURI
)
518 return this._engineStore
.engines
[index
].iconURI
.spec
;
522 getCellText: function(index
, column
) {
523 if (column
.id
== "engineName")
524 return this._engineStore
.engines
[index
].name
;
525 else if (column
.id
== "engineKeyword")
526 return this._engineStore
.engines
[index
].alias
;
530 setTree: function(tree
) {
534 canDrop: function(targetIndex
, orientation
) {
535 var sourceIndex
= this.getSourceIndexFromDrag();
536 return (sourceIndex
!= -1 &&
537 sourceIndex
!= targetIndex
&&
538 sourceIndex
!= (targetIndex
+ orientation
));
541 drop: function(dropIndex
, orientation
) {
542 var sourceIndex
= this.getSourceIndexFromDrag();
543 var sourceEngine
= this._engineStore
.engines
[sourceIndex
];
545 if (dropIndex
> sourceIndex
) {
546 if (orientation
== Ci
.nsITreeView
.DROP_BEFORE
)
549 if (orientation
== Ci
.nsITreeView
.DROP_AFTER
)
553 this._engineStore
.moveEngine(sourceEngine
, dropIndex
);
554 gEngineManagerDialog
.showRestoreDefaults(true);
556 // Redraw, and adjust selection
558 this.selection
.clearSelection();
559 this.selection
.select(dropIndex
);
563 getRowProperties: function(index
, properties
) { },
564 getCellProperties: function(index
, column
, properties
) { },
565 getColumnProperties: function(column
, properties
) { },
566 isContainer: function(index
) { return false; },
567 isContainerOpen: function(index
) { return false; },
568 isContainerEmpty: function(index
) { return false; },
569 isSeparator: function(index
) { return false; },
570 isSorted: function(index
) { return false; },
571 getParentIndex: function(index
) { return -1; },
572 hasNextSibling: function(parentIndex
, index
) { return false; },
573 getLevel: function(index
) { return 0; },
574 getProgressMode: function(index
, column
) { },
575 getCellValue: function(index
, column
) { },
576 toggleOpenState: function(index
) { },
577 cycleHeader: function(column
) { },
578 selectionChanged: function() { },
579 cycleCell: function(row
, column
) { },
580 isEditable: function(index
, column
) { return false; },
581 isSelectable: function(index
, column
) { return false; },
582 setCellValue: function(index
, column
, value
) { },
583 setCellText: function(index
, column
, value
) { },
584 performAction: function(action
) { },
585 performActionOnRow: function(action
, index
) { },
586 performActionOnCell: function(action
, index
, column
) { }