3 * (C) 2006 by Derrell Lipman
7 * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
11 * Swat LDB Browser class graphical user interface
13 qx.OO.defineClass("swat.module.ldbbrowse.Gui", qx.core.Object,
16 qx.core.Object.call(this);
21 * Build the raw graphical user interface.
23 qx.Proto.buildGui = function(module)
28 // We need a horizontal box layout for the database name
29 var hlayout = new qx.ui.layout.HorizontalBoxLayout();
37 // Create a label for the database name
38 o = new qx.ui.basic.Atom("Database:");
40 o.setHorizontalChildrenAlign("right");
42 // Add the label to the horizontal layout
45 // Create a combo box for for the database name
46 o = new qx.ui.form.ComboBox();
47 o.getField().setWidth("100%");
50 // Add our global database name (the only option, for now)
51 var item = new qx.ui.form.ListItem(module.dbFile);
54 // We want to be notified if the selection changes
55 o.addEventListener("changeSelection", fsm.eventListener, fsm);
57 // Save the database name object so we can react to changes
58 fsm.addObject("dbName", o);
60 // Add the combo box to the horizontal layout
63 // Add the database name selection to the canvas
64 module.canvas.add(hlayout);
66 // Create and position the tabview
67 var tabView_ = new qx.ui.pageview.tabview.TabView;
75 // Create each of the tabs
77 new qx.ui.pageview.tabview.Button("Search");
79 new qx.ui.pageview.tabview.Button("Browse");
81 // Specify the initially-selected tab
82 tabView_Search.setChecked(true);
84 // Add each of the tabs to the tabview
85 tabView_.getBar().add(tabView_Search, tabView_Browse);
87 // Create the pages to display when each tab is selected
88 var tabViewPage_Search =
89 new qx.ui.pageview.tabview.Page(tabView_Search);
90 var tabViewPage_Browse =
91 new qx.ui.pageview.tabview.Page(tabView_Browse);
93 // Build the search page
94 this._buildPageSearch(module, tabViewPage_Search);
96 // Build the browse page
97 this._buildPageBrowse(module, tabViewPage_Browse);
99 // Add the pages to the tabview
100 tabView_.getPane().add(tabViewPage_Search, tabViewPage_Browse);
102 // Add the tabview to our canvas
103 module.canvas.add(tabView_);
108 * Populate the graphical user interface with the specified data
110 * @param module {swat.module.Module}
111 * The module descriptor for the module.
114 * The result returned by SAMBA to our request. We display the data
115 * provided by this result.
117 qx.Proto.displayData = function(module, request)
119 var gui = module.gui;
120 var fsm = module.fsm;
121 var result = request.getUserData("result")
122 var requestType = request.getUserData("requestType");
124 // Did the request fail?
125 if (result.type == "failed")
127 // Yup. We're not going to do anything particularly elegant...
128 alert("Async(" + result.id + ") exception: " + result.data);
132 // Dispatch to the appropriate handler, depending on the request type
136 this._displayFindResults(module, request);
140 this._displayTreeOpenResults(module, request);
143 case "tree_selection_changed":
144 this._displayTreeSelectionChangedResults(module, request);
147 case "database_name_changed":
148 this._clearAllFields(module, request);
152 throw new Error("Unexpected request type: " + requestType);
155 // Force flushing of pending DOM updates. This is actually a
156 // work-around for a bug. Without this, occasionally, updates to the
157 // gui aren't displayed until some 'action' takes place, e.g. key
158 // press or mouse movement.
159 qx.ui.core.Widget.flushGlobalQueues();
163 qx.Proto._setAppearances = function()
165 // Modify the default appearance of a ComboBox for use in Search tab:
166 // use more of the available width.
168 // If we had multiple uses, we'd create a new appearance which didn't
169 // contain a width. That way, we'd be able to assign a specific width to
170 // each ComboBox instance. Since we don't have multiple of them, we can
171 // just modify this default appearance.
173 // See http://qooxdoo.org/documentation/user_manual/appearance for an
174 // explanation of what's going on here. The missing significant point in
175 // the discussion is that in the current qooxdoo appearance
176 // implementation, it's not possible to override a specific widget's
177 // appearance with explicit settings just for that widget (stupid!). I
178 // expect that to change in a future version.
179 var appMgr = qx.manager.object.AppearanceManager.getInstance();
180 var theme = appMgr.getAppearanceTheme();
181 var appearance = theme._appearances["combo-box"];
186 var oldInitial = appearance.initial;
187 appearance.initial = function(vTheme)
189 var res = oldInitial ? oldInitial.apply(this, arguments) : {};
196 qx.Proto._buildPageSearch = function(module, page)
198 var fsm = module.fsm;
200 // We need a vertical box layout for the various input fields
201 var vlayout = new qx.ui.layout.VerticalBoxLayout();
202 vlayout.setWidth("100%");
204 // We need a horizontal box layout for the search combo box and its label
205 var hlayout = new qx.ui.layout.HorizontalBoxLayout();
206 hlayout.setWidth("100%");
207 hlayout.setHeight(25);
209 // Create a label for the list of required attributes
210 var label = new qx.ui.basic.Atom("Search Expression:");
212 label.setHorizontalChildrenAlign("right");
214 // Add the label to the horizontal layout
217 // Create a combo box for entry of the search expression
218 var search = new qx.ui.form.ComboBox();
219 search.getField().setWidth("100%");
220 search.setEditable(true);
221 fsm.addObject("searchExpr", search);
223 // Add the combo box to the horizontal layout
226 // Add the horizontal layout to the vertical layout
227 vlayout.add(hlayout);
229 // We need a horizontal box layout for the base combo box and its label
230 hlayout = new qx.ui.layout.HorizontalBoxLayout();
231 hlayout.setWidth("100%");
232 hlayout.setHeight(25);
234 // Create a label for the list of required attributes
235 var label = new qx.ui.basic.Atom("Base:");
237 label.setHorizontalChildrenAlign("right");
239 // Add the label to the horizontal layout
242 // Create a combo box for entry of the search expression
243 var base = new qx.ui.form.ComboBox();
244 base.getField().setWidth("100%");
245 base.setEditable(true);
246 fsm.addObject("baseDN", base);
248 // Add the combo box to the horizontal layout
251 // Add the horizontal layout to the vertical layout
252 vlayout.add(hlayout);
254 // We need a horizontal box layout for scope radio buttons
255 hlayout = new qx.ui.layout.HorizontalBoxLayout();
256 hlayout.setWidth("100%");
257 hlayout.setHeight(25);
259 // Create a label for the list of required attributes
260 var label = new qx.ui.basic.Atom("Scope:");
262 label.setHorizontalChildrenAlign("right");
264 // Add the label to the horizontal layout
267 // Create a radio button for each scope
268 var rbDefault = new qx.ui.form.RadioButton("Default", "default");
269 var rbBase = new qx.ui.form.RadioButton("Base", "base");
270 var rbOne = new qx.ui.form.RadioButton("One Level", "one");
271 var rbSubtree = new qx.ui.form.RadioButton("Subtree", "subtree");
273 // Use a default of "Default"
274 rbBase.setChecked(true);
276 // Add the radio buttons to the horizontal layout
277 hlayout.add(rbDefault, rbBase, rbOne, rbSubtree);
279 // Group the radio buttons so only one in the group may be selected
280 var scope = new qx.manager.selection.RadioManager("scope",
287 fsm.addObject("scope", scope);
289 // Right-justify the 'Find' button
290 var spacer = new qx.ui.basic.HorizontalSpacer;
293 // Create the 'Find' button
294 var find = new qx.ui.form.Button('Find');
296 find.addEventListener("execute", fsm.eventListener, fsm);
298 // We'll be receiving events on the find object, so save its friendly name
299 fsm.addObject("find", find, "swat.module.fsmUtils.disable_during_rpc");
303 // Add the Find button line to the vertical layout
304 vlayout.add(hlayout);
306 // Add the horizontal box layout to the page
309 // Create a simple table model
310 var tableModel = new qx.ui.table.SimpleTableModel();
311 tableModel.setColumns([ "Distinguished Name", "Attribute", "Value" ]);
313 tableModel.setColumnEditable(0, false);
314 tableModel.setColumnEditable(1, false);
315 tableModel.setColumnEditable(2, false);
316 fsm.addObject("tableModel:search", tableModel);
319 var table = new qx.ui.table.Table(tableModel);
325 statusBarVisible: false,
326 columnVisibilityButtonVisible: false
328 table.setColumnWidth(0, 300);
329 table.setColumnWidth(1, 180);
330 table.setColumnWidth(2, 240);
331 table.setMetaColumnCounts([ 1, -1 ]);// h-scroll attribute and value together
332 fsm.addObject("table:search", table);
337 qx.Proto._buildPageBrowse = function(module, page)
339 var fsm = module.fsm;
341 // Create a vertical splitpane for tree (top) and table (bottom)
342 var splitpane = new qx.ui.splitpane.VerticalSplitPane("1*", "2*");
343 splitpane.setEdge(0);
345 // Create a tree row structure for the tree root
346 var trsInstance = qx.ui.treefullcontrol.TreeRowStructure.getInstance();
347 var trs = trsInstance.standard(module.dbFile);
349 // Create the tree and set its characteristics
350 var tree = new qx.ui.treefullcontrol.Tree(trs);
352 backgroundColor: 255,
353 border: qx.renderer.border.BorderPresets.getInstance().inset,
361 alwaysShowPlusMinusSymbol: true
364 // All subtrees will use this root node's event listeners. Create an event
365 // listener for an open while empty.
366 tree.addEventListener("treeOpenWhileEmpty", fsm.eventListener, fsm);
368 // All subtrees will use this root node's event listeners. Create an event
369 // listener for selection changed, to populate attribute/value table
370 tree.getManager().addEventListener("changeSelection",
374 // We'll be receiving events on the tree object, so save its friendly name
375 fsm.addObject("tree", tree);
376 fsm.addObject("tree:manager", tree.getManager());
378 // Add the tree to the page.
379 splitpane.addTop(tree);
381 // Create a simple table model
382 var tableModel = new qx.ui.table.SimpleTableModel();
383 tableModel.setColumns([ "Attribute", "Value" ]);
385 tableModel.setColumnEditable(0, false);
386 tableModel.setColumnEditable(1, false);
387 fsm.addObject("tableModel:browse", tableModel);
390 var table = new qx.ui.table.Table(tableModel);
396 statusBarVisible: false,
397 columnVisibilityButtonVisible: false
399 table.setColumnWidth(0, 200);
400 table.setColumnWidth(1, 440);
401 table.setMetaColumnCounts([1, -1]);
402 fsm.addObject("table:browse", table);
404 // Add the table to the bottom portion of the splitpane
405 splitpane.addBottom(table);
407 // Add the first splitpane to the page
412 qx.Proto._displayFindResults = function(module, request)
415 var fsm = module.fsm;
417 // Track the maximum length of the attribute values
420 // Obtain the result object
421 result = request.getUserData("result").data;
423 if (result && result["length"])
425 len = result["length"];
426 for (var i = 0; i < result["length"]; i++)
429 if (typeof(o) != "object")
431 alert("Found unexpected result, type " +
440 // skip dn and distinguishedName fields;
441 // they're shown in each row anyway.
442 if (field == "dn" || field == "distinguishedName")
447 // If it's multi-valued (type is an array)...
448 if (typeof(o[field]) == "object")
450 // ... then add each value with same name
452 for (var i = 0; i < a.length; i++)
454 if (a[i].length > maxLen)
456 maxLen = a[i].length;
465 else // single-valued
467 // ... add its name and value to the table
469 if (o[field].length > maxLen)
471 maxLen = o[field].length;
481 // Obtain the table and tableModel objects
482 var table = fsm.getObject("table:search");
483 var tableModel = fsm.getObject("tableModel:search");
485 // Adjust the width of the value column based on
487 table.setColumnWidth(2, maxLen * 7);
489 // Tell the table to use the new data
490 tableModel.setData(rowData);
495 alert("No rows returned.");
500 qx.Proto._displayTreeOpenResults = function(module, request)
506 // Obtain the result object
507 var result = request.getUserData("result").data;
509 // We also need some of the original parameters passed to the request
510 var parent = request.getUserData("parent");
511 var attributes = request.getUserData("attributes");
514 if (! result || result["length"] == 0)
516 // Nope. Allow parent's expand/contract button to be removed
517 parent.setAlwaysShowPlusMinusSymbol(false);
521 for (var i = 0; i < result.length; i++)
527 // Determine name for new tree row. If first level, use entire
528 // DN. Otherwise, strip off first additional component.
529 if (attributes == "defaultNamingContext")
531 name = child["defaultNamingContext"];
535 name = child["dn"].split(",")[0];
538 // Build a standard tree row
539 trs = qx.ui.treefullcontrol.TreeRowStructure.getInstance().standard(name);
541 // This row is a "folder" (it can have children)
542 t = new qx.ui.treefullcontrol.TreeFolder(trs);
543 t.setAlwaysShowPlusMinusSymbol(true);
545 // Add this row to its parent
551 qx.Proto._displayTreeSelectionChangedResults = function(module, request)
553 var fsm = module.fsm;
555 // Obtain the result object
556 var result = request.getUserData("result").data;
558 // If we received an empty list, ...
561 // ... then just clear the attribute/value table.
562 tableModel.setData([ ]);
566 // Start with an empty table dataset
569 // The result contains a single object: attributes
570 var attributes = result[0];
572 // Track the maximum length of the attribute values
575 // For each attribute we received...
576 for (var attr in attributes)
578 // If it's multi-valued (type is an array)...
579 if (typeof(attributes[attr]) == "object")
581 // ... then add each value with same name
582 var a = attributes[attr];
583 for (var i = 0; i < a.length; i++)
585 if (a[i].length > maxLen)
587 maxLen = a[i].length;
589 rowData.push([ attr, a[i] ]);
592 else // single-valued
594 // ... add its name and value to the table dataset
595 if (attributes[attr].length > maxLen)
597 maxLen = attributes[attr].length;
599 rowData.push([ attr, attributes[attr] ]);
603 // Obtain the table and tableModel objects
604 var table = fsm.getObject("table:browse");
605 var tableModel = fsm.getObject("tableModel:browse");
607 // Adjust the width of the value column based on maxLen
608 table.setColumnWidth(1, maxLen * 7);
610 // Add the dataset to the table
611 tableModel.setData(rowData);
615 qx.Proto._clearAllFields = function(module, request)
617 // Obtain the result object
618 var result = request.getUserData("result").data;
620 // Retrieve the database handle
621 module.dbHandle = result;
623 // In the future, when we support more than one database, we'll want to
624 // clear all fields here. For now, there's no need.
630 * Singleton Instance Getter
632 qx.Class.getInstance = qx.util.Return.returnInstance;