2
|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
2 *
|
|
3 * ***** BEGIN LICENSE BLOCK *****
|
|
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
5 *
|
|
6 * The contents of this file are subject to the Mozilla Public License Version
|
|
7 * 1.1 (the "License"); you may not use this file except in compliance with
|
|
8 * the License. You may obtain a copy of the License at
|
|
9 * http://www.mozilla.org/MPL/
|
|
10 *
|
|
11 * Software distributed under the License is distributed on an "AS IS" basis,
|
|
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
13 * for the specific language governing rights and limitations under the
|
|
14 * License.
|
|
15 *
|
|
16 * The Original Code is The JavaScript Debugger.
|
|
17 *
|
|
18 * The Initial Developer of the Original Code is
|
|
19 * Netscape Communications Corporation.
|
|
20 * Portions created by the Initial Developer are Copyright (C) 1998
|
|
21 * the Initial Developer. All Rights Reserved.
|
|
22 *
|
|
23 * Contributor(s):
|
|
24 * Robert Ginda, <rginda@netscape.com>, original author
|
|
25 * Dave Townsend, <mossop@blueprintit.co.uk>, Added cell and row property support
|
|
26 *
|
|
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.
|
|
38 *
|
|
39 * ***** END LICENSE BLOCK ***** */
|
|
40
|
|
41 /*
|
|
42 * BasicOView provides functionality of tree whose elements have no children.
|
|
43 * Usage:
|
|
44 * var myTree = new BasicOView()
|
|
45 * myTree.setColumnNames (["col 1", "col 2"]);
|
|
46 * myTree.data = [["row 1, col 1", "row 1, col 2"],
|
|
47 * ["row 2, col 1", "row 2, col 2"]];
|
|
48 * { override get*Properties, etc, as suits your purpose. }
|
|
49 *
|
|
50 * treeBoxObject.view = myTree;
|
|
51 *
|
|
52 * You'll need to make the appropriate myTree.tree.invalidate calls
|
|
53 * when myTree.data changes.
|
|
54 */
|
|
55
|
|
56 function BasicOView()
|
|
57 {
|
|
58 this.tree = null;
|
|
59 }
|
|
60
|
|
61 /* functions *you* should call to initialize and maintain the tree state */
|
|
62
|
|
63 /* scroll the line specified by |line| to the center of the tree */
|
|
64 BasicOView.prototype.centerLine =
|
|
65 function bov_ctrln (line)
|
|
66 {
|
|
67 var first = this.tree.getFirstVisibleRow();
|
|
68 var last = this.tree.getLastVisibleRow();
|
|
69 this.scrollToRow(line - (last - first + 1) / 2);
|
|
70 }
|
|
71
|
|
72 /* call this to set the association between column names and data columns */
|
|
73 BasicOView.prototype.setColumnNames =
|
|
74 function bov_setcn (aryNames)
|
|
75 {
|
|
76 this.columnNames = new Object();
|
|
77 for (var i = 0; i < aryNames.length; ++i)
|
|
78 this.columnNames[aryNames[i]] = i;
|
|
79 }
|
|
80
|
|
81 /*
|
|
82 * scroll the source so |line| is at either the top, center, or bottom
|
|
83 * of the view, depending on the value of |align|.
|
|
84 *
|
|
85 * line is the one based target line.
|
|
86 * if align is negative, the line will be scrolled to the top, if align is
|
|
87 * zero the line will be centered, and if align is greater than 0 the line
|
|
88 * will be scrolled to the bottom. 0 is the default.
|
|
89 */
|
|
90 BasicOView.prototype.scrollTo =
|
|
91 function bov_scrollto (line, align)
|
|
92 {
|
|
93 if (!this.tree)
|
|
94 return;
|
|
95
|
|
96 var headerRows = 1;
|
|
97
|
|
98 var first = this.tree.getFirstVisibleRow();
|
|
99 var last = this.tree.getLastVisibleRow();
|
|
100 var viz = last - first + 1 - headerRows; /* total number of visible rows */
|
|
101
|
|
102 /* all rows are visible, nothing to scroll */
|
|
103 if (first == 0 && last >= this.rowCount)
|
|
104 return;
|
|
105
|
|
106 /* tree lines are 0 based, we accept one based lines, deal with it */
|
|
107 --line;
|
|
108
|
|
109 /* safety clamp */
|
|
110 if (line < 0)
|
|
111 line = 0;
|
|
112 if (line >= this.rowCount)
|
|
113 line = this.rowCount - 1;
|
|
114
|
|
115 if (align < 0)
|
|
116 {
|
|
117 if (line > this.rowCount - viz) /* overscroll, can't put a row from */
|
|
118 line = this.rowCount - viz; /* last page at the top. */
|
|
119 this.tree.scrollToRow(line);
|
|
120 }
|
|
121 else if (align > 0)
|
|
122 {
|
|
123 if (line < viz) /* underscroll, can't put a row from the first page */
|
|
124 line = 0; /* at the bottom. */
|
|
125 else
|
|
126 line = line - viz + headerRows;
|
|
127
|
|
128 this.tree.scrollToRow(line);
|
|
129 }
|
|
130 else
|
|
131 {
|
|
132 var half_viz = viz / 2;
|
|
133 /* lines past this line can't be centered without causing the tree
|
|
134 * to show more rows than we have. */
|
|
135 var lastCenterable = this.rowCount - half_viz;
|
|
136 if (line > half_viz)
|
|
137 line = lastCenterable;
|
|
138 /* lines before this can't be centered without causing the tree
|
|
139 * to attempt to display negative rows. */
|
|
140 else if (line < half_viz)
|
|
141 line = half_viz;
|
|
142 else
|
|
143 /* round the vizible rows down to a whole number, or we try to end up
|
|
144 * on a N + 0.5 row! */
|
|
145 half_viz = Math.floor(half_viz);
|
|
146
|
|
147 this.tree.scrollToRow(line - half_viz);
|
|
148 }
|
|
149 }
|
|
150
|
|
151 BasicOView.prototype.__defineGetter__("selectedIndex", bov_getsel);
|
|
152 function bov_getsel()
|
|
153 {
|
|
154 if (!this.tree || this.tree.view.selection.getRangeCount() < 1)
|
|
155 return -1;
|
|
156
|
|
157 var min = new Object();
|
|
158 this.tree.view.selection.getRangeAt(0, min, {});
|
|
159 return min.value;
|
|
160 }
|
|
161
|
|
162 BasicOView.prototype.__defineSetter__("selectedIndex", bov_setsel);
|
|
163 function bov_setsel(i)
|
|
164 {
|
|
165 if (i == -1)
|
|
166 this.tree.view.selection.clearSelection();
|
|
167 else
|
|
168 this.tree.view.selection.timedSelect (i, 500);
|
|
169 return i;
|
|
170 }
|
|
171
|
|
172 /*
|
|
173 * functions the tree will call to retrieve the list state (nsITreeView.)
|
|
174 */
|
|
175
|
|
176 BasicOView.prototype.rowCount = 0;
|
|
177
|
|
178 BasicOView.prototype.getCellProperties =
|
|
179 function bov_cellprops (row, col, properties)
|
|
180 {
|
|
181 }
|
|
182
|
|
183 BasicOView.prototype.getColumnProperties =
|
|
184 function bov_colprops (col, properties)
|
|
185 {
|
|
186 }
|
|
187
|
|
188 BasicOView.prototype.getRowProperties =
|
|
189 function bov_rowprops (index, properties)
|
|
190 {
|
|
191 }
|
|
192
|
|
193 BasicOView.prototype.isContainer =
|
|
194 function bov_isctr (index)
|
|
195 {
|
|
196 return false;
|
|
197 }
|
|
198
|
|
199 BasicOView.prototype.isContainerOpen =
|
|
200 function bov_isctropen (index)
|
|
201 {
|
|
202 return false;
|
|
203 }
|
|
204
|
|
205 BasicOView.prototype.isContainerEmpty =
|
|
206 function bov_isctrempt (index)
|
|
207 {
|
|
208 return false;
|
|
209 }
|
|
210
|
|
211 BasicOView.prototype.isSeparator =
|
|
212 function bov_isseparator (index)
|
|
213 {
|
|
214 return false;
|
|
215 }
|
|
216
|
|
217 BasicOView.prototype.isSorted =
|
|
218 function bov_issorted (index)
|
|
219 {
|
|
220 return false;
|
|
221 }
|
|
222
|
|
223 BasicOView.prototype.canDrop =
|
|
224 function bov_drop (index, orientation)
|
|
225 {
|
|
226 return false;
|
|
227 }
|
|
228
|
|
229 BasicOView.prototype.drop =
|
|
230 function bov_drop (index, orientation)
|
|
231 {
|
|
232 return false;
|
|
233 }
|
|
234
|
|
235 BasicOView.prototype.getParentIndex =
|
|
236 function bov_getpi (index)
|
|
237 {
|
|
238 if (index < 0)
|
|
239 return -1;
|
|
240
|
|
241 return 0;
|
|
242 }
|
|
243
|
|
244 BasicOView.prototype.hasNextSibling =
|
|
245 function bov_hasnxtsib (rowIndex, afterIndex)
|
|
246 {
|
|
247 return (afterIndex < (this.rowCount - 1));
|
|
248 }
|
|
249
|
|
250 BasicOView.prototype.getLevel =
|
|
251 function bov_getlvl (index)
|
|
252 {
|
|
253 return 0;
|
|
254 }
|
|
255
|
|
256 BasicOView.prototype.getImageSrc =
|
|
257 function bov_getimgsrc (row, col)
|
|
258 {
|
|
259 }
|
|
260
|
|
261 BasicOView.prototype.getProgressMode =
|
|
262 function bov_getprgmode (row, col)
|
|
263 {
|
|
264 }
|
|
265
|
|
266 BasicOView.prototype.getCellValue =
|
|
267 function bov_getcellval (row, col)
|
|
268 {
|
|
269 }
|
|
270
|
|
271 BasicOView.prototype.getCellText =
|
|
272 function bov_getcelltxt (row, col)
|
|
273 {
|
|
274 if (!this.columnNames)
|
|
275 return "";
|
|
276
|
|
277 if (typeof col == "object")
|
|
278 col = col.id;
|
|
279
|
|
280 var ary = col.match (/:(.*)/);
|
|
281 if (ary)
|
|
282 col = ary[1];
|
|
283
|
|
284 var colName = this.columnNames[col];
|
|
285
|
|
286 if (typeof colName == "undefined")
|
|
287 return "";
|
|
288
|
|
289 return this.data[row][colName];
|
|
290 }
|
|
291
|
|
292 BasicOView.prototype.setTree =
|
|
293 function bov_seto (tree)
|
|
294 {
|
|
295 this.tree = tree;
|
|
296 }
|
|
297
|
|
298 BasicOView.prototype.toggleOpenState =
|
|
299 function bov_toggleopen (index)
|
|
300 {
|
|
301 }
|
|
302
|
|
303 BasicOView.prototype.cycleHeader =
|
|
304 function bov_cyclehdr (col)
|
|
305 {
|
|
306 }
|
|
307
|
|
308 BasicOView.prototype.selectionChanged =
|
|
309 function bov_selchg ()
|
|
310 {
|
|
311 }
|
|
312
|
|
313 BasicOView.prototype.cycleCell =
|
|
314 function bov_cyclecell (row, col)
|
|
315 {
|
|
316 }
|
|
317
|
|
318 BasicOView.prototype.isEditable =
|
|
319 function bov_isedit (row, col)
|
|
320 {
|
|
321 return false;
|
|
322 }
|
|
323
|
|
324 BasicOView.prototype.setCellValue =
|
|
325 function bov_setct (row, col, value)
|
|
326 {
|
|
327 }
|
|
328
|
|
329 BasicOView.prototype.setCellText =
|
|
330 function bov_setct (row, col, value)
|
|
331 {
|
|
332 }
|
|
333
|
|
334 BasicOView.prototype.onRouteFocus =
|
|
335 function bov_rfocus (event)
|
|
336 {
|
|
337 if ("onFocus" in this)
|
|
338 this.onFocus(event);
|
|
339 }
|
|
340
|
|
341 BasicOView.prototype.onRouteBlur =
|
|
342 function bov_rblur (event)
|
|
343 {
|
|
344 if ("onBlur" in this)
|
|
345 this.onBlur(event);
|
|
346 }
|
|
347
|
|
348 BasicOView.prototype.onRouteDblClick =
|
|
349 function bov_rdblclick (event)
|
|
350 {
|
|
351 if (!("onRowCommand" in this) || event.target.localName != "treechildren")
|
|
352 return;
|
|
353
|
|
354 var rowIndex = this.tree.view.selection.currentIndex;
|
|
355 if (rowIndex == -1 || rowIndex > this.rowCount)
|
|
356 return;
|
|
357 var rec = this.childData.locateChildByVisualRow(rowIndex);
|
|
358 if (!rec)
|
|
359 {
|
|
360 ASSERT (0, "bogus row index " + rowIndex);
|
|
361 return;
|
|
362 }
|
|
363
|
|
364 this.onRowCommand(rec, event);
|
|
365 }
|
|
366
|
|
367 BasicOView.prototype.onRouteKeyPress =
|
|
368 function bov_rkeypress (event)
|
|
369 {
|
|
370 var rec;
|
|
371 var rowIndex;
|
|
372
|
|
373 if ("onRowCommand" in this && (event.keyCode == 13 || event.charCode == 32))
|
|
374 {
|
|
375 if (!this.selection)
|
|
376 return;
|
|
377
|
|
378 rowIndex = this.tree.view.selection.currentIndex;
|
|
379 if (rowIndex == -1 || rowIndex > this.rowCount)
|
|
380 return;
|
|
381 rec = this.childData.locateChildByVisualRow(rowIndex);
|
|
382 if (!rec)
|
|
383 {
|
|
384 ASSERT (0, "bogus row index " + rowIndex);
|
|
385 return;
|
|
386 }
|
|
387
|
|
388 this.onRowCommand(rec, event);
|
|
389 }
|
|
390 else if ("onKeyPress" in this)
|
|
391 {
|
|
392 rowIndex = this.tree.view.selection.currentIndex;
|
|
393 if (rowIndex != -1 && rowIndex < this.rowCount)
|
|
394 {
|
|
395 rec = this.childData.locateChildByVisualRow(rowIndex);
|
|
396 if (!rec)
|
|
397 {
|
|
398 ASSERT (0, "bogus row index " + rowIndex);
|
|
399 return;
|
|
400 }
|
|
401 }
|
|
402 else
|
|
403 {
|
|
404 rec = null;
|
|
405 }
|
|
406
|
|
407 this.onKeyPress(rec, event);
|
|
408 }
|
|
409 }
|
|
410
|
|
411 BasicOView.prototype.performAction =
|
|
412 function bov_pact (action)
|
|
413 {
|
|
414 }
|
|
415
|
|
416 BasicOView.prototype.performActionOnRow =
|
|
417 function bov_pactrow (action)
|
|
418 {
|
|
419 }
|
|
420
|
|
421 BasicOView.prototype.performActionOnCell =
|
|
422 function bov_pactcell (action)
|
|
423 {
|
|
424 }
|
|
425
|
|
426 /*
|
|
427 * record for the XULTreeView. these things take care of keeping the
|
|
428 * XULTreeView properly informed of changes in value and child count. you
|
|
429 * shouldn't have to maintain tree state at all.
|
|
430 *
|
|
431 * |share| should be an otherwise empty object to store cache data.
|
|
432 * you should use the same object as the |share| for the XULTreeView that you
|
|
433 * indend to contain these records.
|
|
434 *
|
|
435 */
|
|
436 function XULTreeViewRecord(share)
|
|
437 {
|
|
438 this._share = share;
|
|
439 this.visualFootprint = 1;
|
|
440 this.isHidden = true; /* records are considered hidden until they are
|
|
441 * inserted into a live tree */
|
|
442 }
|
|
443
|
|
444 XULTreeViewRecord.prototype.properties = "";
|
|
445
|
|
446 XULTreeViewRecord.prototype.isContainerOpen = false;
|
|
447
|
|
448 /*
|
|
449 * walk the parent tree to find our tree container. return null if there is
|
|
450 * none
|
|
451 */
|
|
452 XULTreeViewRecord.prototype.findContainerTree =
|
|
453 function xtvr_gettree ()
|
|
454 {
|
|
455 if (!("parentRecord" in this))
|
|
456 return null;
|
|
457 var parent = this.parentRecord;
|
|
458
|
|
459 while (parent)
|
|
460 {
|
|
461 if ("_treeView" in parent)
|
|
462 return parent._treeView;
|
|
463 if ("parentRecord" in parent)
|
|
464 parent = parent.parentRecord;
|
|
465 else
|
|
466 parent = null;
|
|
467 }
|
|
468
|
|
469 return null;
|
|
470 }
|
|
471
|
|
472 XULTreeViewRecord.prototype.__defineGetter__("childIndex", xtvr_getChildIndex);
|
|
473 function xtvr_getChildIndex ()
|
|
474 {
|
|
475 //dd ("getChildIndex {");
|
|
476
|
|
477 if (!("parentRecord" in this))
|
|
478 {
|
|
479 delete this._childIndex;
|
|
480 //dd ("} -1");
|
|
481 return -1;
|
|
482 }
|
|
483
|
|
484 if ("_childIndex" in this)
|
|
485 {
|
|
486 if ("childData" in this && this._childIndex in this.childData &&
|
|
487 this.childData[this._childIndex] == this)
|
|
488 {
|
|
489 //dd ("} " + this._childIndex);
|
|
490 return this._childIndex;
|
|
491 }
|
|
492 }
|
|
493
|
|
494 var childData = this.parentRecord.childData;
|
|
495 var len = childData.length;
|
|
496 for (var i = 0; i < len; ++i)
|
|
497 {
|
|
498 if (childData[i] == this)
|
|
499 {
|
|
500 this._childIndex = i;
|
|
501 //dd ("} " + this._childIndex);
|
|
502 return i;
|
|
503 }
|
|
504 }
|
|
505
|
|
506 delete this._childIndex;
|
|
507 //dd ("} -1");
|
|
508 return -1;
|
|
509 }
|
|
510
|
|
511 XULTreeViewRecord.prototype.__defineSetter__("childIndex", xtvr_setChildIndex);
|
|
512 function xtvr_setChildIndex ()
|
|
513 {
|
|
514 dd("xtvr: childIndex is read only, ignore attempt to write to it\n");
|
|
515 if (typeof getStackTrace == "function")
|
|
516 dd(getStackTrace());
|
|
517 }
|
|
518
|
|
519 /* count the number of parents, not including the root node */
|
|
520 XULTreeViewRecord.prototype.__defineGetter__("level", xtvr_getLevel);
|
|
521 function xtvr_getLevel ()
|
|
522 {
|
|
523 if (!("parentRecord" in this))
|
|
524 return -1;
|
|
525
|
|
526 var rv = 0;
|
|
527 var parentRecord = this.parentRecord;
|
|
528 while ("parentRecord" in parentRecord &&
|
|
529 (parentRecord = parentRecord.parentRecord)) ++rv;
|
|
530 return rv;
|
|
531 }
|
|
532
|
|
533 /*
|
|
534 * associates a property name on this record, with a column in the tree. This
|
|
535 * method will set up a get/set pair for the property name you specify which
|
|
536 * will take care of updating the tree when the value changes. DO NOT try
|
|
537 * to change your mind later. Do not attach a different name to the same colID,
|
|
538 * and do not rename the colID. You have been warned.
|
|
539 */
|
|
540 XULTreeViewRecord.prototype.setColumnPropertyName =
|
|
541 function xtvr_setcol (colID, propertyName)
|
|
542 {
|
|
543 function xtvr_getValueShim ()
|
|
544 {
|
|
545 return this._colValues[colID];
|
|
546 }
|
|
547 function xtvr_setValueShim (newValue)
|
|
548 {
|
|
549 this._colValues[colID] = newValue;
|
|
550 return newValue;
|
|
551 }
|
|
552
|
|
553 if (!("_colValues" in this))
|
|
554 this._colValues = new Object();
|
|
555 if (!("_colProperties" in this))
|
|
556 this._colProperties = new Object();
|
|
557
|
|
558 if (typeof propertyName == "function")
|
|
559 {
|
|
560 this._colValues.__defineGetter__(colID, propertyName);
|
|
561 }
|
|
562 else
|
|
563 {
|
|
564 this.__defineGetter__(propertyName, xtvr_getValueShim);
|
|
565 this.__defineSetter__(propertyName, xtvr_setValueShim);
|
|
566 }
|
|
567 }
|
|
568
|
|
569 XULTreeViewRecord.prototype.setColumnPropertyValue =
|
|
570 function xtvr_setcolv (colID, value)
|
|
571 {
|
|
572 this._colValues[colID] = value;
|
|
573 }
|
|
574
|
|
575 XULTreeViewRecord.prototype.setColumnProperties =
|
|
576 function xtvr_setcolp (colID, value)
|
|
577 {
|
|
578 this._colProperties[colID] = value;
|
|
579 }
|
|
580
|
|
581 /*
|
|
582 * set the default sort column and reSort.
|
|
583 */
|
|
584 XULTreeViewRecord.prototype.setSortColumn =
|
|
585 function xtvr_setcol (colID, dir)
|
|
586 {
|
|
587 //dd ("setting sort column to " + colID);
|
|
588 this._share.sortColumn = colID;
|
|
589 this._share.sortDirection = (typeof dir == "undefined") ? 1 : dir;
|
|
590 this.reSort();
|
|
591 }
|
|
592
|
|
593 /*
|
|
594 * set the default sort direction. 1 is ascending, -1 is descending, 0 is no
|
|
595 * sort. setting this to 0 will *not* recover the natural insertion order,
|
|
596 * it will only affect newly added items.
|
|
597 */
|
|
598 XULTreeViewRecord.prototype.setSortDirection =
|
|
599 function xtvr_setdir (dir)
|
|
600 {
|
|
601 this._share.sortDirection = dir;
|
|
602 }
|
|
603
|
|
604 /*
|
|
605 * invalidate this row in the tree
|
|
606 */
|
|
607 XULTreeViewRecord.prototype.invalidate =
|
|
608 function xtvr_invalidate()
|
|
609 {
|
|
610 var tree = this.findContainerTree();
|
|
611 if (tree)
|
|
612 {
|
|
613 var row = this.calculateVisualRow();
|
|
614 if (row != -1)
|
|
615 tree.tree.invalidateRow(row);
|
|
616 }
|
|
617 }
|
|
618
|
|
619 /*
|
|
620 * invalidate any data in the cache.
|
|
621 */
|
|
622 XULTreeViewRecord.prototype.invalidateCache =
|
|
623 function xtvr_killcache()
|
|
624 {
|
|
625 this._share.rowCache = new Object();
|
|
626 this._share.lastComputedIndex = -1;
|
|
627 this._share.lastIndexOwner = null;
|
|
628 }
|
|
629
|
|
630 /*
|
|
631 * default comparator function for sorts. if you want a custom sort, override
|
|
632 * this method. We declare xtvr_sortcmp as a top level function, instead of
|
|
633 * a function expression so we can refer to it later.
|
|
634 */
|
|
635 XULTreeViewRecord.prototype.sortCompare = xtvr_sortcmp;
|
|
636 function xtvr_sortcmp (a, b)
|
|
637 {
|
|
638 var sc = a._share.sortColumn;
|
|
639 var sd = a._share.sortDirection;
|
|
640
|
|
641 a = a[sc];
|
|
642 b = b[sc];
|
|
643
|
|
644 if (a < b)
|
|
645 return -1 * sd;
|
|
646
|
|
647 if (a > b)
|
|
648 return 1 * sd;
|
|
649
|
|
650 return 0;
|
|
651 }
|
|
652
|
|
653 /*
|
|
654 * this method will cause all child records to be reSorted. any records
|
|
655 * with the default sortCompare method will be sorted by the colID passed to
|
|
656 * setSortColumn.
|
|
657 *
|
|
658 * the local parameter is used internally to control whether or not the
|
|
659 * sorted rows are invalidated. don't use it yourself.
|
|
660 */
|
|
661 XULTreeViewRecord.prototype.reSort =
|
|
662 function xtvr_resort (leafSort)
|
|
663 {
|
|
664 if (!("childData" in this) || this.childData.length < 1 ||
|
|
665 (this.childData[0].sortCompare == xtvr_sortcmp &&
|
|
666 !("sortColumn" in this._share) || this._share.sortDirection == 0))
|
|
667 {
|
|
668 /* if we have no children, or we have the default sort compare and no
|
|
669 * sort flags, then just exit */
|
|
670 return;
|
|
671 }
|
|
672
|
|
673 this.childData.sort(this.childData[0].sortCompare);
|
|
674
|
|
675 for (var i = 0; i < this.childData.length; ++i)
|
|
676 {
|
|
677 if ("isContainerOpen" in this.childData[i] &&
|
|
678 this.childData[i].isContainerOpen)
|
|
679 this.childData[i].reSort(true);
|
|
680 else
|
|
681 this.childData[i].sortIsInvalid = true;
|
|
682 }
|
|
683
|
|
684 if (!leafSort)
|
|
685 {
|
|
686 this.invalidateCache();
|
|
687 var tree = this.findContainerTree();
|
|
688 if (tree && tree.tree)
|
|
689 {
|
|
690 var rowIndex = this.calculateVisualRow();
|
|
691 /*
|
|
692 dd ("invalidating " + rowIndex + " - " +
|
|
693 (rowIndex + this.visualFootprint - 1));
|
|
694 */
|
|
695 tree.tree.invalidateRange (rowIndex,
|
|
696 rowIndex + this.visualFootprint - 1);
|
|
697 }
|
|
698 }
|
|
699 delete this.sortIsInvalid;
|
|
700 }
|
|
701
|
|
702 /*
|
|
703 * call this to indicate that this node may have children at one point. make
|
|
704 * sure to call it before adding your first child.
|
|
705 */
|
|
706 XULTreeViewRecord.prototype.reserveChildren =
|
|
707 function xtvr_rkids (always)
|
|
708 {
|
|
709 if (!("childData" in this))
|
|
710 this.childData = new Array();
|
|
711 if (!("isContainerOpen" in this))
|
|
712 this.isContainerOpen = false;
|
|
713 if (always)
|
|
714 this.alwaysHasChildren = true;
|
|
715 else
|
|
716 delete this.alwaysHasChildren;
|
|
717 }
|
|
718
|
|
719 /*
|
|
720 * add a child to the end of the child list for this record. takes care of
|
|
721 * updating the tree as well.
|
|
722 */
|
|
723 XULTreeViewRecord.prototype.appendChild =
|
|
724 function xtvr_appchild (child)
|
|
725 {
|
|
726 if (!(child instanceof XULTreeViewRecord))
|
|
727 throw Components.results.NS_ERROR_INVALID_ARG;
|
|
728
|
|
729 child.isHidden = false;
|
|
730 child.parentRecord = this;
|
|
731 this.childData.push(child);
|
|
732
|
|
733 if ("isContainerOpen" in this && this.isContainerOpen)
|
|
734 {
|
|
735 //dd ("appendChild: " + xtv_formatRecord(child, ""));
|
|
736 if (this.calculateVisualRow() >= 0)
|
|
737 {
|
|
738 var tree = this.findContainerTree();
|
|
739 if (tree && tree.frozen)
|
|
740 this.needsReSort = true;
|
|
741 else
|
|
742 this.reSort(true); /* reSort, don't invalidate. we're going
|
|
743 * to do that in the
|
|
744 * onVisualFootprintChanged call. */
|
|
745 }
|
|
746 this.onVisualFootprintChanged(child.calculateVisualRow(),
|
|
747 child.visualFootprint);
|
|
748 }
|
|
749 }
|
|
750
|
|
751 /*
|
|
752 * add a list of children to the end of the child list for this record.
|
|
753 * faster than multiple appendChild() calls.
|
|
754 */
|
|
755 XULTreeViewRecord.prototype.appendChildren =
|
|
756 function xtvr_appchild (children)
|
|
757 {
|
|
758 var idx = this.childData.length;
|
|
759 var delta = 0;
|
|
760 var len = children.length;
|
|
761 for (var i = 0; i < len; ++i)
|
|
762 {
|
|
763 var child = children[i];
|
|
764 child.isHidden = false;
|
|
765 child.parentRecord = this;
|
|
766 this.childData.push(child);
|
|
767 delta += child.visualFootprint;
|
|
768 }
|
|
769
|
|
770 if ("isContainerOpen" in this && this.isContainerOpen)
|
|
771 {
|
|
772 if (this.calculateVisualRow() >= 0)
|
|
773 {
|
|
774 this.reSort(true); /* reSort, don't invalidate. we're going to do
|
|
775 * that in the onVisualFootprintChanged call. */
|
|
776 }
|
|
777 this.onVisualFootprintChanged(this.childData[0].calculateVisualRow(),
|
|
778 delta);
|
|
779 }
|
|
780 }
|
|
781
|
|
782 /*
|
|
783 * remove a child from this record. updates the tree too. DON'T call this with
|
|
784 * an index not actually contained by this record.
|
|
785 */
|
|
786 XULTreeViewRecord.prototype.removeChildAtIndex =
|
|
787 function xtvr_remchild (index)
|
|
788 {
|
|
789 if (!ASSERT(this.childData.length, "removing from empty childData"))
|
|
790 return;
|
|
791
|
|
792 var orphan = this.childData[index];
|
|
793 var fpDelta = -orphan.visualFootprint;
|
|
794 var changeStart = orphan.calculateVisualRow();
|
|
795 delete orphan.parentRecord;
|
|
796 arrayRemoveAt (this.childData, index);
|
|
797
|
|
798 if (!orphan.isHidden && "isContainerOpen" in this && this.isContainerOpen)
|
|
799 {
|
|
800 this.onVisualFootprintChanged (changeStart, fpDelta);
|
|
801 }
|
|
802 }
|
|
803
|
|
804 /*
|
|
805 * hide this record and all descendants.
|
|
806 */
|
|
807 XULTreeViewRecord.prototype.hide =
|
|
808 function xtvr_hide ()
|
|
809 {
|
|
810 if (this.isHidden)
|
|
811 return;
|
|
812
|
|
813 /* get the row before hiding */
|
|
814 var row = this.calculateVisualRow();
|
|
815 this.invalidateCache();
|
|
816 this.isHidden = true;
|
|
817 /* go right to the parent so we don't muck with our own visualFootprint
|
|
818 * record, we'll need it to be correct if we're ever unHidden. */
|
|
819 if ("parentRecord" in this)
|
|
820 this.parentRecord.onVisualFootprintChanged (row, -this.visualFootprint);
|
|
821 }
|
|
822
|
|
823 /*
|
|
824 * unhide this record and all descendants.
|
|
825 */
|
|
826 XULTreeViewRecord.prototype.unHide =
|
|
827 function xtvr_uhide ()
|
|
828 {
|
|
829 if (!this.isHidden)
|
|
830 return;
|
|
831
|
|
832 this.isHidden = false;
|
|
833 this.invalidateCache();
|
|
834 var row = this.calculateVisualRow();
|
|
835 if (this.parentRecord)
|
|
836 this.parentRecord.onVisualFootprintChanged (row, this.visualFootprint);
|
|
837 }
|
|
838
|
|
839 /*
|
|
840 * open this record, exposing it's children. DON'T call this method if the
|
|
841 * record has no children.
|
|
842 */
|
|
843 XULTreeViewRecord.prototype.open =
|
|
844 function xtvr_open ()
|
|
845 {
|
|
846 if (this.isContainerOpen)
|
|
847 return;
|
|
848
|
|
849 if ("onPreOpen" in this)
|
|
850 this.onPreOpen();
|
|
851
|
|
852 this.isContainerOpen = true;
|
|
853 var delta = 0;
|
|
854 for (var i = 0; i < this.childData.length; ++i)
|
|
855 {
|
|
856 if (!this.childData[i].isHidden)
|
|
857 delta += this.childData[i].visualFootprint;
|
|
858 }
|
|
859
|
|
860 /* this reSort should only happen if the sort column changed */
|
|
861 this.reSort(true);
|
|
862 this.visualFootprint += delta;
|
|
863 if ("parentRecord" in this)
|
|
864 {
|
|
865 this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
|
|
866 0);
|
|
867 this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow() +
|
|
868 1, delta);
|
|
869 }
|
|
870 }
|
|
871
|
|
872 /*
|
|
873 * close this record, hiding it's children. DON'T call this method if the record
|
|
874 * has no children, or if it is already closed.
|
|
875 */
|
|
876 XULTreeViewRecord.prototype.close =
|
|
877 function xtvr_close ()
|
|
878 {
|
|
879 if (!this.isContainerOpen)
|
|
880 return;
|
|
881
|
|
882 this.isContainerOpen = false;
|
|
883 var delta = 1 - this.visualFootprint;
|
|
884 this.visualFootprint += delta;
|
|
885 if ("parentRecord" in this)
|
|
886 {
|
|
887 this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
|
|
888 0);
|
|
889 this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow() +
|
|
890 1, delta);
|
|
891 }
|
|
892
|
|
893 if ("onPostClose" in this)
|
|
894 this.onPostClose();
|
|
895 }
|
|
896
|
|
897 /*
|
|
898 * called when a node above this one grows or shrinks. we need to adjust
|
|
899 * our own visualFootprint to match the change, and pass the message on.
|
|
900 */
|
|
901 XULTreeViewRecord.prototype.onVisualFootprintChanged =
|
|
902 function xtvr_vpchange (start, amount)
|
|
903 {
|
|
904 /* if we're not hidden, but this notification came from a hidden node
|
|
905 * (start == -1), ignore it, it doesn't affect us. */
|
|
906 if (start == -1 && !this.isHidden)
|
|
907 {
|
|
908
|
|
909 //dd ("vfp change (" + amount + ") from hidden node ignored.");
|
|
910 return;
|
|
911 }
|
|
912
|
|
913 this.visualFootprint += amount;
|
|
914
|
|
915 if ("parentRecord" in this)
|
|
916 this.parentRecord.onVisualFootprintChanged(start, amount);
|
|
917 }
|
|
918
|
|
919 /*
|
|
920 * calculate the "visual" row for this record. If the record isn't actually
|
|
921 * visible return -1.
|
|
922 * eg.
|
|
923 * Name Visual Row
|
|
924 * node1 0
|
|
925 * node11 1
|
|
926 * node12 2
|
|
927 * node2 3
|
|
928 * node21 4
|
|
929 * node3 5
|
|
930 */
|
|
931 XULTreeViewRecord.prototype.calculateVisualRow =
|
|
932 function xtvr_calcrow ()
|
|
933 {
|
|
934 /* if this is the second time in a row that someone asked us, fetch the last
|
|
935 * result from the cache. */
|
|
936 if (this._share.lastIndexOwner == this)
|
|
937 return this._share.lastComputedIndex;
|
|
938
|
|
939 var vrow;
|
|
940
|
|
941 /* if this is an uninserted or hidden node, or... */
|
|
942 if (!("parentRecord" in this) || (this.isHidden) ||
|
|
943 /* if parent isn't open, or... */
|
|
944 (!this.parentRecord.isContainerOpen) ||
|
|
945 /* parent isn't visible */
|
|
946 ((vrow = this.parentRecord.calculateVisualRow()) == -1))
|
|
947 {
|
|
948 /* then we're not visible, return -1 */
|
|
949 //dd ("cvr: returning -1");
|
|
950 return -1;
|
|
951 }
|
|
952
|
|
953 /* parent is the root node XXX parent is not visible */
|
|
954 if (vrow == null)
|
|
955 vrow = 0;
|
|
956 else
|
|
957 /* parent is not the root node, add one for the space they take up. */
|
|
958 ++vrow;
|
|
959
|
|
960 /* add in the footprint for all of the earlier siblings */
|
|
961 var ci = this.childIndex;
|
|
962 for (var i = 0; i < ci; ++i)
|
|
963 {
|
|
964 if (!this.parentRecord.childData[i].isHidden)
|
|
965 vrow += this.parentRecord.childData[i].visualFootprint;
|
|
966 }
|
|
967
|
|
968 /* save this calculation to the cache. */
|
|
969 this._share.lastIndexOwner = this;
|
|
970 this._share.lastComputedIndex = vrow;
|
|
971
|
|
972 return vrow;
|
|
973 }
|
|
974
|
|
975 /*
|
|
976 * locates the child record for the visible row |targetRow|. DO NOT call this
|
|
977 * with a targetRow less than this record's visual row, or greater than this
|
|
978 * record's visual row + the number of visible children it has.
|
|
979 */
|
|
980 XULTreeViewRecord.prototype.locateChildByVisualRow =
|
|
981 function xtvr_find (targetRow, myRow)
|
|
982 {
|
|
983 if (targetRow in this._share.rowCache)
|
|
984 return this._share.rowCache[targetRow];
|
|
985
|
|
986 /* if this is true, we *are* the index */
|
|
987 if (targetRow == myRow)
|
|
988 return (this._share.rowCache[targetRow] = this);
|
|
989
|
|
990 /* otherwise, we've got to search the kids */
|
|
991 var childStart = myRow; /* childStart represents the starting visual row
|
|
992 * for the child we're examining. */
|
|
993 for (var i = 0; i < this.childData.length; ++i)
|
|
994 {
|
|
995 var child = this.childData[i];
|
|
996 /* ignore hidden children */
|
|
997 if (child.isHidden)
|
|
998 continue;
|
|
999 /* if this kid is the targetRow, we're done */
|
|
1000 if (childStart == targetRow)
|
|
1001 return (this._share.rowCache[targetRow] = child);
|
|
1002 /* if this kid contains the index, ask *it* to find the record */
|
|
1003 else if (targetRow <= childStart + child.visualFootprint) {
|
|
1004 /* this *has* to succeed */
|
|
1005 var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
|
|
1006 //XXXASSERT (rv, "Can't find a row that *has* to be there.");
|
|
1007 /* don't cache this, the previous call to locateChildByVisualRow
|
|
1008 * just did. */
|
|
1009 return rv;
|
|
1010 }
|
|
1011
|
|
1012 /* otherwise, get ready to ask the next kid */
|
|
1013 childStart += child.visualFootprint;
|
|
1014 }
|
|
1015
|
|
1016 return null;
|
|
1017 }
|
|
1018
|
|
1019 /* XTLabelRecords can be used to drop a label into an arbitrary place in an
|
|
1020 * arbitrary tree. normally, specializations of XULTreeViewRecord are tied to
|
|
1021 * a specific tree because of implementation details. XTLabelRecords are
|
|
1022 * specially designed (err, hacked) to work around these details. this makes
|
|
1023 * them slower, but more generic.
|
|
1024 *
|
|
1025 * we set up a getter for _share that defers to the parent object. this lets
|
|
1026 * XTLabelRecords work in any tree.
|
|
1027 */
|
|
1028 function XTLabelRecord (columnName, label, blankCols)
|
|
1029 {
|
|
1030 this.setColumnPropertyName (columnName, "label");
|
|
1031 this.label = label;
|
|
1032 this.property = null;
|
|
1033
|
|
1034 if (typeof blankCols == "object")
|
|
1035 {
|
|
1036 for (var i in blankCols)
|
|
1037 this._colValues[blankCols[i]] = "";
|
|
1038 }
|
|
1039 }
|
|
1040
|
|
1041 XTLabelRecord.prototype = new XULTreeViewRecord (null);
|
|
1042
|
|
1043 XTLabelRecord.prototype.__defineGetter__("_share", tolr_getshare);
|
|
1044 function tolr_getshare()
|
|
1045 {
|
|
1046 if ("parentRecord" in this)
|
|
1047 return this.parentRecord._share;
|
|
1048
|
|
1049 ASSERT (0, "XTLabelRecord cannot be the root of a visible tree.");
|
|
1050 return null;
|
|
1051 }
|
|
1052
|
|
1053 /* XTRootRecord is used internally by XULTreeView, you probably don't need to
|
|
1054 * make any of these */
|
|
1055 function XTRootRecord (tree, share)
|
|
1056 {
|
|
1057 this._share = share;
|
|
1058 this._treeView = tree;
|
|
1059 this.visualFootprint = 0;
|
|
1060 this.isHidden = false;
|
|
1061 this.reserveChildren();
|
|
1062 this.isContainerOpen = true;
|
|
1063 }
|
|
1064
|
|
1065 /* no cache passed in here, we set it in the XTRootRecord contructor instead. */
|
|
1066 XTRootRecord.prototype = new XULTreeViewRecord (null);
|
|
1067
|
|
1068 XTRootRecord.prototype.open =
|
|
1069 XTRootRecord.prototype.close =
|
|
1070 function torr_notimplemented()
|
|
1071 {
|
|
1072 /* don't do this on a root node */
|
|
1073 }
|
|
1074
|
|
1075 XTRootRecord.prototype.calculateVisualRow =
|
|
1076 function torr_calcrow ()
|
|
1077 {
|
|
1078 return null;
|
|
1079 }
|
|
1080
|
|
1081 XTRootRecord.prototype.reSort =
|
|
1082 function torr_resort ()
|
|
1083 {
|
|
1084 if ("_treeView" in this && this._treeView.frozen)
|
|
1085 {
|
|
1086 this._treeView.needsReSort = true;
|
|
1087 return;
|
|
1088 }
|
|
1089
|
|
1090 if (!("childData" in this) || this.childData.length < 1 ||
|
|
1091 (this.childData[0].sortCompare == xtvr_sortcmp &&
|
|
1092 !("sortColumn" in this._share) || this._share.sortDirection == 0))
|
|
1093 {
|
|
1094 /* if we have no children, or we have the default sort compare but we're
|
|
1095 * missing a sort flag, then just exit */
|
|
1096 return;
|
|
1097 }
|
|
1098
|
|
1099 this.childData.sort(this.childData[0].sortCompare);
|
|
1100
|
|
1101 for (var i = 0; i < this.childData.length; ++i)
|
|
1102 {
|
|
1103 if ("isContainerOpen" in this.childData[i] &&
|
|
1104 this.childData[i].isContainerOpen)
|
|
1105 this.childData[i].reSort(true);
|
|
1106 else
|
|
1107 this.childData[i].sortIsInvalid = true;
|
|
1108 }
|
|
1109
|
|
1110 if ("_treeView" in this && this._treeView.tree)
|
|
1111 {
|
|
1112 /*
|
|
1113 dd ("root node: invalidating 0 - " + this.visualFootprint +
|
|
1114 " for sort");
|
|
1115 */
|
|
1116 this.invalidateCache();
|
|
1117 this._treeView.tree.invalidateRange (0, this.visualFootprint);
|
|
1118 }
|
|
1119 }
|
|
1120
|
|
1121 XTRootRecord.prototype.locateChildByVisualRow =
|
|
1122 function torr_find (targetRow)
|
|
1123 {
|
|
1124 if (targetRow in this._share.rowCache)
|
|
1125 return this._share.rowCache[targetRow];
|
|
1126
|
|
1127 var childStart = -1; /* childStart represents the starting visual row
|
|
1128 * for the child we're examining. */
|
|
1129 for (var i = 0; i < this.childData.length; ++i)
|
|
1130 {
|
|
1131 var child = this.childData[i];
|
|
1132 /* ignore hidden children */
|
|
1133 if (child.isHidden)
|
|
1134 continue;
|
|
1135 /* if this kid is the targetRow, we're done */
|
|
1136 if (childStart == targetRow)
|
|
1137 return (this._share.rowCache[targetRow] = child);
|
|
1138 /* if this kid contains the index, ask *it* to find the record */
|
|
1139 else if (targetRow <= childStart + child.visualFootprint) {
|
|
1140 /* this *has* to succeed */
|
|
1141 var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
|
|
1142 //XXXASSERT (rv, "Can't find a row that *has* to be there.");
|
|
1143 /* don't cache this, the previous call to locateChildByVisualRow
|
|
1144 * just did. */
|
|
1145 return rv;
|
|
1146 }
|
|
1147
|
|
1148 /* otherwise, get ready to ask the next kid */
|
|
1149 childStart += child.visualFootprint;
|
|
1150 }
|
|
1151
|
|
1152 return null;
|
|
1153 }
|
|
1154
|
|
1155 XTRootRecord.prototype.onVisualFootprintChanged =
|
|
1156 function torr_vfpchange (start, amount)
|
|
1157 {
|
|
1158 if (!this._treeView.frozen)
|
|
1159 {
|
|
1160 this.invalidateCache();
|
|
1161 this.visualFootprint += amount;
|
|
1162 if ("_treeView" in this && "tree" in this._treeView &&
|
|
1163 this._treeView.tree)
|
|
1164 {
|
|
1165 if (amount != 0)
|
|
1166 this._treeView.tree.rowCountChanged (start, amount);
|
|
1167 else
|
|
1168 this._treeView.tree.invalidateRow (start);
|
|
1169 }
|
|
1170 }
|
|
1171 else
|
|
1172 {
|
|
1173 if ("changeAmount" in this._treeView)
|
|
1174 this._treeView.changeAmount += amount;
|
|
1175 else
|
|
1176 this._treeView.changeAmount = amount;
|
|
1177 if ("changeStart" in this._treeView)
|
|
1178 this._treeView.changeStart =
|
|
1179 Math.min (start, this._treeView.changeStart);
|
|
1180 else
|
|
1181 this._treeView.changeStart = start;
|
|
1182 }
|
|
1183 }
|
|
1184
|
|
1185 /*
|
|
1186 * XULTreeView provides functionality of tree whose elements have multiple
|
|
1187 * levels of children.
|
|
1188 */
|
|
1189
|
|
1190 function XULTreeView(share)
|
|
1191 {
|
|
1192 if (!share)
|
|
1193 share = new Object();
|
|
1194 this.childData = new XTRootRecord(this, share);
|
|
1195 this.childData.invalidateCache();
|
|
1196 this.tree = null;
|
|
1197 this.share = share;
|
|
1198 this.frozen = 0;
|
|
1199 }
|
|
1200
|
|
1201 /* functions *you* should call to initialize and maintain the tree state */
|
|
1202
|
|
1203 /*
|
|
1204 * Changes to the tree contents will not cause the tree to be invalidated
|
|
1205 * until thaw() is called. All changes will be pooled into a single invalidate
|
|
1206 * call.
|
|
1207 *
|
|
1208 * Freeze/thaws are nestable, the tree will not update until the number of
|
|
1209 * thaw() calls matches the number of freeze() calls.
|
|
1210 */
|
|
1211 XULTreeView.prototype.freeze =
|
|
1212 function xtv_freeze ()
|
|
1213 {
|
|
1214 if (++this.frozen == 1)
|
|
1215 {
|
|
1216 this.changeStart = 0;
|
|
1217 this.changeAmount = 0;
|
|
1218 }
|
|
1219 //dd ("freeze " + this.frozen);
|
|
1220 }
|
|
1221
|
|
1222 /*
|
|
1223 * Reflect any changes to the tree content since the last freeze.
|
|
1224 */
|
|
1225 XULTreeView.prototype.thaw =
|
|
1226 function xtv_thaw ()
|
|
1227 {
|
|
1228 if (this.frozen == 0)
|
|
1229 {
|
|
1230 ASSERT (0, "not frozen");
|
|
1231 return;
|
|
1232 }
|
|
1233
|
|
1234 if (--this.frozen == 0 && "changeStart" in this)
|
|
1235 {
|
|
1236 this.childData.onVisualFootprintChanged(this.changeStart,
|
|
1237 this.changeAmount);
|
|
1238 }
|
|
1239
|
|
1240 if ("needsReSort" in this) {
|
|
1241 this.childData.reSort();
|
|
1242 delete this.needsReSort;
|
|
1243 }
|
|
1244
|
|
1245
|
|
1246 delete this.changeStart;
|
|
1247 delete this.changeAmount;
|
|
1248 }
|
|
1249
|
|
1250 XULTreeView.prototype.saveBranchState =
|
|
1251 function xtv_savebranch (target, source, recurse)
|
|
1252 {
|
|
1253 var len = source.length;
|
|
1254 for (var i = 0; i < len; ++i)
|
|
1255 {
|
|
1256 if (source[i].isContainerOpen)
|
|
1257 {
|
|
1258 target[i] = new Object();
|
|
1259 target[i].name = source[i]._colValues["col-0"];
|
|
1260 if (recurse)
|
|
1261 this.saveBranchState (target[i], source[i].childData, true);
|
|
1262 }
|
|
1263 }
|
|
1264 }
|
|
1265
|
|
1266 XULTreeView.prototype.restoreBranchState =
|
|
1267 function xtv_restorebranch (target, source, recurse)
|
|
1268 {
|
|
1269 for (var i in source)
|
|
1270 {
|
|
1271 if (typeof source[i] == "object")
|
|
1272 {
|
|
1273 var name = source[i].name;
|
|
1274 var len = target.length;
|
|
1275 for (var j = 0; j < len; ++j)
|
|
1276 {
|
|
1277 if (target[j]._colValues["col-0"] == name &&
|
|
1278 "childData" in target[j])
|
|
1279 {
|
|
1280 //dd ("opening " + name);
|
|
1281 target[j].open();
|
|
1282 if (recurse)
|
|
1283 {
|
|
1284 this.restoreBranchState (target[j].childData,
|
|
1285 source[i], true);
|
|
1286 }
|
|
1287 break;
|
|
1288 }
|
|
1289 }
|
|
1290 }
|
|
1291 }
|
|
1292 }
|
|
1293
|
|
1294 /* scroll the line specified by |line| to the center of the tree */
|
|
1295 XULTreeView.prototype.centerLine =
|
|
1296 function xtv_ctrln (line)
|
|
1297 {
|
|
1298 var first = this.tree.getFirstVisibleRow();
|
|
1299 var last = this.tree.getLastVisibleRow();
|
|
1300 this.scrollToRow(line - (last - first + 1) / 2);
|
|
1301 }
|
|
1302
|
|
1303 /*
|
|
1304 * functions the tree will call to retrieve the list state (nsITreeView.)
|
|
1305 */
|
|
1306
|
|
1307 XULTreeView.prototype.__defineGetter__("rowCount", xtv_getRowCount);
|
|
1308 function xtv_getRowCount ()
|
|
1309 {
|
|
1310 if (!this.childData)
|
|
1311 return 0;
|
|
1312
|
|
1313 return this.childData.visualFootprint;
|
|
1314 }
|
|
1315
|
|
1316 XULTreeView.prototype.isContainer =
|
|
1317 function xtv_isctr (index)
|
|
1318 {
|
|
1319 var row = this.childData.locateChildByVisualRow (index);
|
|
1320
|
|
1321 return Boolean(row && ("alwaysHasChildren" in row || "childData" in row));
|
|
1322 }
|
|
1323
|
|
1324 XULTreeView.prototype.__defineGetter__("selectedIndex", xtv_getsel);
|
|
1325 function xtv_getsel()
|
|
1326 {
|
|
1327 if (!this.tree || this.tree.view.selection.getRangeCount() < 1)
|
|
1328 return -1;
|
|
1329
|
|
1330 var min = new Object();
|
|
1331 this.tree.view.selection.getRangeAt(0, min, {});
|
|
1332 return min.value;
|
|
1333 }
|
|
1334
|
|
1335 XULTreeView.prototype.__defineSetter__("selectedIndex", xtv_setsel);
|
|
1336 function xtv_setsel(i)
|
|
1337 {
|
|
1338 this.tree.view.selection.clearSelection();
|
|
1339 if (i != -1)
|
|
1340 this.tree.view.selection.timedSelect (i, 500);
|
|
1341 return i;
|
|
1342 }
|
|
1343
|
|
1344 XULTreeView.prototype.scrollTo = BasicOView.prototype.scrollTo;
|
|
1345
|
|
1346 XULTreeView.prototype.isContainerOpen =
|
|
1347 function xtv_isctropen (index)
|
|
1348 {
|
|
1349 var row = this.childData.locateChildByVisualRow (index);
|
|
1350 return row && row.isContainerOpen;
|
|
1351 }
|
|
1352
|
|
1353 XULTreeView.prototype.toggleOpenState =
|
|
1354 function xtv_toggleopen (index)
|
|
1355 {
|
|
1356 var row = this.childData.locateChildByVisualRow (index);
|
|
1357 //ASSERT(row, "bogus row");
|
|
1358 if (row)
|
|
1359 {
|
|
1360 if (row.isContainerOpen)
|
|
1361 row.close();
|
|
1362 else
|
|
1363 row.open();
|
|
1364 }
|
|
1365 }
|
|
1366
|
|
1367 XULTreeView.prototype.isContainerEmpty =
|
|
1368 function xtv_isctrempt (index)
|
|
1369 {
|
|
1370 var row = this.childData.locateChildByVisualRow (index);
|
|
1371 if ("alwaysHasChildren" in row)
|
|
1372 return false;
|
|
1373
|
|
1374 if (!row || !("childData" in row))
|
|
1375 return true;
|
|
1376
|
|
1377 return !row.childData.length;
|
|
1378 }
|
|
1379
|
|
1380 XULTreeView.prototype.isSeparator =
|
|
1381 function xtv_isseparator (index)
|
|
1382 {
|
|
1383 return false;
|
|
1384 }
|
|
1385
|
|
1386 XULTreeView.prototype.getParentIndex =
|
|
1387 function xtv_getpi (index)
|
|
1388 {
|
|
1389 if (index < 0)
|
|
1390 return -1;
|
|
1391
|
|
1392 var row = this.childData.locateChildByVisualRow (index);
|
|
1393
|
|
1394 var rv = row.parentRecord.calculateVisualRow();
|
|
1395 //dd ("getParentIndex: row " + index + " returning " + rv);
|
|
1396 return (rv != null) ? rv : -1;
|
|
1397 }
|
|
1398
|
|
1399 XULTreeView.prototype.hasNextSibling =
|
|
1400 function xtv_hasnxtsib (rowIndex, afterIndex)
|
|
1401 {
|
|
1402 var row = this.childData.locateChildByVisualRow (rowIndex);
|
|
1403 return row.childIndex < row.parentRecord.childData.length - 1;
|
|
1404 }
|
|
1405
|
|
1406 XULTreeView.prototype.getLevel =
|
|
1407 function xtv_getlvl (index)
|
|
1408 {
|
|
1409 var row = this.childData.locateChildByVisualRow (index);
|
|
1410 if (!row)
|
|
1411 return 0;
|
|
1412
|
|
1413 return row.level;
|
|
1414 }
|
|
1415
|
|
1416 XULTreeView.prototype.getImageSrc =
|
|
1417 function xtv_getimgsrc (index, col)
|
|
1418 {
|
|
1419 }
|
|
1420
|
|
1421 XULTreeView.prototype.getProgressMode =
|
|
1422 function xtv_getprgmode (index, col)
|
|
1423 {
|
|
1424 }
|
|
1425
|
|
1426 XULTreeView.prototype.getCellValue =
|
|
1427 function xtv_getcellval (index, col)
|
|
1428 {
|
|
1429 }
|
|
1430
|
|
1431 XULTreeView.prototype.getCellText =
|
|
1432 function xtv_getcelltxt (index, col)
|
|
1433 {
|
|
1434 var row = this.childData.locateChildByVisualRow (index);
|
|
1435 //ASSERT(row, "bogus row " + index);
|
|
1436
|
|
1437 if (typeof col == "object")
|
|
1438 col = col.id;
|
|
1439
|
|
1440 var ary = col.match (/:(.*)/);
|
|
1441 if (ary)
|
|
1442 col = ary[1];
|
|
1443
|
|
1444 if (row && row._colValues && col in row._colValues)
|
|
1445 return row._colValues[col];
|
|
1446 else
|
|
1447 return "";
|
|
1448 }
|
|
1449
|
|
1450 XULTreeView.prototype.getCellProperties =
|
|
1451 function xtv_cellprops (row, col, properties)
|
|
1452 {
|
|
1453 var row = this.childData.locateChildByVisualRow (row);
|
|
1454
|
|
1455 if (typeof col == "object")
|
|
1456 col = col.id;
|
|
1457
|
|
1458 var ary = col.match (/:(.*)/);
|
|
1459 if (ary)
|
|
1460 col = ary[1];
|
|
1461
|
|
1462 if (row && row._colProperties && col in row._colProperties)
|
|
1463 xtv_atomizeText(row._colProperties[col], properties);
|
|
1464 }
|
|
1465
|
|
1466 XULTreeView.prototype.getColumnProperties =
|
|
1467 function xtv_colprops (col, properties)
|
|
1468 {}
|
|
1469
|
|
1470 XULTreeView.prototype.getRowProperties =
|
|
1471 function xtv_rowprops (index, properties)
|
|
1472 {
|
|
1473 var row = this.childData.locateChildByVisualRow (index);
|
|
1474
|
|
1475 xtv_atomizeText(row.properties, properties);
|
|
1476 }
|
|
1477
|
|
1478 XULTreeView.prototype.isSorted =
|
|
1479 function xtv_issorted (index)
|
|
1480 {
|
|
1481 return false;
|
|
1482 }
|
|
1483
|
|
1484 XULTreeView.prototype.canDrop =
|
|
1485 function xtv_drop (index, orientation)
|
|
1486 {
|
|
1487 var row = this.childData.locateChildByVisualRow (index);
|
|
1488 //ASSERT(row, "bogus row " + index);
|
|
1489 return (row && ("canDrop" in row) && row.canDrop(orientation));
|
|
1490 }
|
|
1491
|
|
1492 XULTreeView.prototype.drop =
|
|
1493 function xtv_drop (index, orientation)
|
|
1494 {
|
|
1495 var row = this.childData.locateChildByVisualRow (index);
|
|
1496 //ASSERT(row, "bogus row " + index);
|
|
1497 return (row && ("drop" in row) && row.drop(orientation));
|
|
1498 }
|
|
1499
|
|
1500 XULTreeView.prototype.setTree =
|
|
1501 function xtv_seto (tree)
|
|
1502 {
|
|
1503 this.childData.invalidateCache();
|
|
1504 this.tree = tree;
|
|
1505 }
|
|
1506
|
|
1507 XULTreeView.prototype.cycleHeader =
|
|
1508 function xtv_cyclehdr (col)
|
|
1509 {
|
|
1510 }
|
|
1511
|
|
1512 XULTreeView.prototype.selectionChanged =
|
|
1513 function xtv_selchg ()
|
|
1514 {
|
|
1515 }
|
|
1516
|
|
1517 XULTreeView.prototype.cycleCell =
|
|
1518 function xtv_cyclecell (row, col)
|
|
1519 {
|
|
1520 }
|
|
1521
|
|
1522 XULTreeView.prototype.isEditable =
|
|
1523 function xtv_isedit (row, col)
|
|
1524 {
|
|
1525 return false;
|
|
1526 }
|
|
1527
|
|
1528 XULTreeView.prototype.setCellValue =
|
|
1529 function xtv_setct (row, col, value)
|
|
1530 {
|
|
1531 }
|
|
1532
|
|
1533 XULTreeView.prototype.setCellText =
|
|
1534 function xtv_setct (row, col, value)
|
|
1535 {
|
|
1536 }
|
|
1537
|
|
1538 XULTreeView.prototype.performAction =
|
|
1539 function xtv_pact (action)
|
|
1540 {
|
|
1541 }
|
|
1542
|
|
1543 XULTreeView.prototype.performActionOnRow =
|
|
1544 function xtv_pactrow (action)
|
|
1545 {
|
|
1546 }
|
|
1547
|
|
1548 XULTreeView.prototype.performActionOnCell =
|
|
1549 function xtv_pactcell (action)
|
|
1550 {
|
|
1551 }
|
|
1552
|
|
1553 XULTreeView.prototype.onRouteFocus =
|
|
1554 function xtv_rfocus (event)
|
|
1555 {
|
|
1556 if ("onFocus" in this)
|
|
1557 this.onFocus(event);
|
|
1558 }
|
|
1559
|
|
1560 XULTreeView.prototype.onRouteBlur =
|
|
1561 function xtv_rblur (event)
|
|
1562 {
|
|
1563 if ("onBlur" in this)
|
|
1564 this.onBlur(event);
|
|
1565 }
|
|
1566
|
|
1567 XULTreeView.prototype.onRouteDblClick =
|
|
1568 function xtv_rdblclick (event)
|
|
1569 {
|
|
1570 if (!("onRowCommand" in this) || event.target.localName != "treechildren")
|
|
1571 return;
|
|
1572
|
|
1573 var rowIndex = this.tree.view.selection.currentIndex;
|
|
1574 if (rowIndex == -1 || rowIndex > this.rowCount)
|
|
1575 return;
|
|
1576 var rec = this.childData.locateChildByVisualRow(rowIndex);
|
|
1577 if (!rec)
|
|
1578 {
|
|
1579 ASSERT (0, "bogus row index " + rowIndex);
|
|
1580 return;
|
|
1581 }
|
|
1582
|
|
1583 this.onRowCommand(rec, event);
|
|
1584 }
|
|
1585
|
|
1586 XULTreeView.prototype.onRouteKeyPress =
|
|
1587 function xtv_rkeypress (event)
|
|
1588 {
|
|
1589 var rec;
|
|
1590 var rowIndex;
|
|
1591
|
|
1592 if ("onRowCommand" in this && (event.keyCode == 13 || event.charCode == 32))
|
|
1593 {
|
|
1594 if (!this.selection)
|
|
1595 return;
|
|
1596
|
|
1597 rowIndex = this.tree.view.selection.currentIndex;
|
|
1598 if (rowIndex == -1 || rowIndex > this.rowCount)
|
|
1599 return;
|
|
1600 rec = this.childData.locateChildByVisualRow(rowIndex);
|
|
1601 if (!rec)
|
|
1602 {
|
|
1603 ASSERT (0, "bogus row index " + rowIndex);
|
|
1604 return;
|
|
1605 }
|
|
1606
|
|
1607 this.onRowCommand(rec, event);
|
|
1608 }
|
|
1609 else if ("onKeyPress" in this)
|
|
1610 {
|
|
1611 rowIndex = this.tree.view.selection.currentIndex;
|
|
1612 if (rowIndex != -1 && rowIndex < this.rowCount)
|
|
1613 {
|
|
1614 rec = this.childData.locateChildByVisualRow(rowIndex);
|
|
1615 if (!rec)
|
|
1616 {
|
|
1617 ASSERT (0, "bogus row index " + rowIndex);
|
|
1618 return;
|
|
1619 }
|
|
1620 }
|
|
1621 else
|
|
1622 {
|
|
1623 rec = null;
|
|
1624 }
|
|
1625
|
|
1626 this.onKeyPress(rec, event);
|
|
1627 }
|
|
1628 }
|
|
1629
|
|
1630 /******************************************************************************/
|
|
1631
|
|
1632 function xtv_atomizeText (text, array)
|
|
1633 {
|
|
1634 var atomservice = Components.classes["@mozilla.org/atom-service;1"]
|
|
1635 .getService(Components.interfaces.nsIAtomService);
|
|
1636
|
|
1637 var parts = text.split(" ");
|
|
1638 for (var i = 0; i<parts.length; i++)
|
|
1639 {
|
|
1640 var atom = atomservice.getAtom(parts[i]);
|
|
1641 array.AppendElement(atom);
|
|
1642 }
|
|
1643 }
|
|
1644
|
|
1645 function xtv_formatRecord (rec, indent)
|
|
1646 {
|
|
1647 var str = "";
|
|
1648
|
|
1649 for (var i in rec._colValues)
|
|
1650 str += rec._colValues[i] + ", ";
|
|
1651
|
|
1652 str += "[";
|
|
1653
|
|
1654 str += rec.calculateVisualRow() + ", ";
|
|
1655 str += rec.childIndex + ", ";
|
|
1656 str += rec.level + ", ";
|
|
1657 str += rec.visualFootprint + ", ";
|
|
1658 str += rec.isHidden + "]";
|
|
1659
|
|
1660 return (indent + str);
|
|
1661 }
|
|
1662
|
|
1663 function xtv_formatBranch (rec, indent, recurse)
|
|
1664 {
|
|
1665 var str = "";
|
|
1666 for (var i = 0; i < rec.childData.length; ++i)
|
|
1667 {
|
|
1668 str += xtv_formatRecord (rec.childData[i], indent) + "\n";
|
|
1669 if (recurse)
|
|
1670 {
|
|
1671 if ("childData" in rec.childData[i])
|
|
1672 str += xtv_formatBranch(rec.childData[i], indent + " ",
|
|
1673 --recurse);
|
|
1674 }
|
|
1675 }
|
|
1676
|
|
1677 return str;
|
|
1678 }
|
|
1679
|