--- /dev/null
+{\r
+ "info" : \r
+ {\r
+ "name" : "org.argeo.ria",\r
+\r
+ "summary" : "Slc Webui",\r
+ "description" : "Argeo Rich Internet Application",\r
+ \r
+ "homepage" : "http://www.argeo.org/",\r
+\r
+ "license" : "LGPL",\r
+ "authors" : \r
+ [\r
+ {\r
+ "name" : "Charles du Jeu",\r
+ "email" : "charles.dujeu@gmail.com"\r
+ }\r
+ ],\r
+\r
+ "version" : "trunk",\r
+ "qooxdoo-versions": ["0.8"]\r
+ },\r
+ \r
+ "provides" : \r
+ {\r
+ "namespace" : "org.argeo.ria",\r
+ "encoding" : "utf-8",\r
+ "class" : "class",\r
+ "resource" : "resource",\r
+ "translation" : "translation",\r
+ "type" : "application"\r
+ }\r
+}\r
+\r
--- /dev/null
+/* ************************************************************************\r
+\r
+ Copyright: 2008 Argeo\r
+\r
+ License: LGPL\r
+\r
+ Authors: Charles du Jeu\r
+\r
+************************************************************************ */\r
+\r
+/* ************************************************************************\r
+\r
+#asset(slc/*)\r
+\r
+************************************************************************ */\r
+\r
+/**\r
+ * This is the main application class of an Argeo RIA.\r
+ */\r
+qx.Class.define("org.argeo.ria.Application",\r
+{\r
+ extend : qx.application.Standalone,\r
+ \r
+ statics : {\r
+ INSTANCE : null \r
+ },\r
+ \r
+ properties : {\r
+ /**\r
+ * Available perspective detected in the current compilation.\r
+ */\r
+ perspectives : {\r
+ check : "Map",\r
+ init : {}\r
+ },\r
+ /**\r
+ * Currently layouted perspective label\r
+ */\r
+ activePerspectiveName : {\r
+ check : "String",\r
+ init : ""\r
+ },\r
+ /**\r
+ * Currently layouted perspective.\r
+ */\r
+ activePerspective : {\r
+ init : null\r
+ },\r
+ /**\r
+ * Basic command associated to the application, applicable to all perspectives.\r
+ */\r
+ commandsDefinitions : {\r
+ init : {\r
+ "stop" : {\r
+ label : "Stop", \r
+ icon : "resource/slc/process-stop.png",\r
+ shortcut : "Control+s",\r
+ enabled : false,\r
+ menu : null,\r
+ toolbar : "list",\r
+ callback : function(e){},\r
+ command : null\r
+ },\r
+ "switchperspective" : {\r
+ label : "Switch Perspective", \r
+ icon : "resource/slc/view-pane-tree.png",\r
+ shortcut : "",\r
+ enabled : true,\r
+ menu : "View",\r
+ toolbar : false,\r
+ submenu : [],\r
+ submenuCallback : function(commandId){\r
+ // Defer execution to assure that the submenu is closed \r
+ // before it is rebuilt.\r
+ qx.event.Timer.once(function(){\r
+ org.argeo.ria.Application.INSTANCE.loadPerspective(commandId);\r
+ }, this, 10); \r
+ },\r
+ callback : function(e){},\r
+ command : null \r
+ }, \r
+ "log" : {\r
+ label : "Show Console", \r
+ icon : "resource/slc/help-contents.png",\r
+ shortcut : "",\r
+ enabled : true,\r
+ menu : "View",\r
+ menuPosition: "last",\r
+ toolbar : false,\r
+ callback : function(e){ \r
+ org.argeo.ria.components.Logger.getInstance().toggle();\r
+ }, \r
+ command : null\r
+ },\r
+ "help" : {\r
+ label : "About...", \r
+ icon : "resource/slc/help-about.png",\r
+ shortcut : "Control+h",\r
+ enabled : true,\r
+ menu : "View",\r
+ toolbar : false,\r
+ callback : function(e){\r
+ var win = new org.argeo.ria.components.Modal("About SLC", null, "SLC is a product from Argeo.");\r
+ win.attachAndShow();\r
+ }, \r
+ command : null\r
+ }\r
+ }\r
+ }\r
+ },\r
+\r
+ members :\r
+ {\r
+ /**\r
+ * This method contains the initial application code and gets called \r
+ * during startup of the application\r
+ */\r
+ main : function()\r
+ {\r
+ // Call super class\r
+ this.base(arguments);\r
+ this.self(arguments).INSTANCE = this;\r
+ this.views = {};\r
+ \r
+ var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
+ viewsManager.setApplicationRoot(this.getRoot());\r
+ \r
+ /*\r
+ var appli = this;\r
+ qx.bom.Event.addNativeListener(window, "unload", function(){\r
+ // TODO : Close perspective if one is open. \r
+ if(appli.getActivePerspective()){\r
+ alert(appli.getActivePerspective());\r
+ appli.getActivePerspective().remove(org.argeo.ria.components.ViewsManager.getInstance());\r
+ } \r
+ });\r
+ */\r
+ // Enable logging in debug variant\r
+ if (qx.core.Variant.isSet("qx.debug", "on"))\r
+ {\r
+ qx.log.appender.Native;\r
+ qx.log.appender.Console;\r
+ }\r
+ var winLogger = org.argeo.ria.components.Logger.getInstance();\r
+ this.getRoot().add(winLogger);\r
+ qx.log.Logger.register(winLogger);\r
+\r
+ // Main layout\r
+ var layout = new qx.ui.layout.VBox();\r
+ var container = new qx.ui.container.Composite(layout);\r
+ viewsManager.setViewPanesContainer(container);\r
+ // Document is the application root \r
+ this.getRoot().add(container, {left:0,right:0,top:0,bottom:0});\r
+ \r
+ // Find available perspectives\r
+ var allPerspectives = {};\r
+ for(var key in qx.Bootstrap.$$registry){\r
+ if(qx.Class.hasInterface(qx.Bootstrap.$$registry[key], org.argeo.ria.components.IPerspective)){\r
+ allPerspectives[key] = qx.Bootstrap.$$registry[key];\r
+ }\r
+ }\r
+ var perspectiveNumber = qx.lang.Object.getLength(allPerspectives);\r
+ if(!perspectiveNumber){\r
+ this.error("Cannot find a perspective for startup!");\r
+ return;\r
+ }\r
+ this.setPerspectives(allPerspectives);\r
+ // Choose startup perspective, delete switch menu if only one perspective.\r
+ if(perspectiveNumber <= 1){\r
+ delete this.getCommandsDefinitions()["switchperspective"];\r
+ this.setActivePerspectiveName(qx.lang.Object.getKeys(allPerspectives)[0]);\r
+ }\r
+ else{\r
+ var startupSetting;\r
+ try{\r
+ startupSetting = qx.core.Setting.get("ria.StartupPerspective");\r
+ }catch(e){}\r
+ if(startupSetting && allPerspectives[startupSetting]){\r
+ this.setActivePerspectiveName(startupSetting);\r
+ }else{\r
+ this.setActivePerspectiveName(qx.lang.Object.getKeys(allPerspectives)[0]);\r
+ }\r
+ this.rebuildPerspectiveMenus();\r
+ }\r
+ \r
+ var menuBar = new qx.ui.menubar.MenuBar();\r
+ var toolbar = new qx.ui.toolbar.ToolBar();\r
+ var commandManager = org.argeo.ria.event.CommandsManager.getInstance();\r
+ commandManager.init(this.getCommandsDefinitions());\r
+ commandManager.createCommands();\r
+ commandManager.registerMenuBar(menuBar);\r
+ commandManager.registerToolBar(toolbar);\r
+ toolbar.setShow("both");\r
+ commandManager.addToolbarContextMenu(toolbar);\r
+\r
+ var stopCommand = commandManager.getCommandById("stop");\r
+ var serviceManager = org.argeo.ria.remote.RequestManager.getInstance();\r
+ serviceManager.setStopCommand(stopCommand);\r
+\r
+ container.add(menuBar);\r
+ container.add(toolbar); \r
+\r
+ this.loadPerspective();\r
+ },\r
+ \r
+ /**\r
+ * Load a given perspective by its name.\r
+ * @param perspectiveName {String} Perspective to load\r
+ */\r
+ loadPerspective : function(perspectiveName){\r
+ if(perspectiveName){\r
+ this.setActivePerspectiveName(perspectiveName);\r
+ this.rebuildPerspectiveMenus();\r
+ }else{\r
+ perspectiveName = this.getActivePerspectiveName();\r
+ }\r
+ var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
+ if(this.getActivePerspective()){\r
+ this.getActivePerspective().remove(viewsManager);\r
+ }\r
+ var allPerspectives = this.getPerspectives(); \r
+ var perspectiveClass = allPerspectives[perspectiveName];\r
+ if(!perspectiveClass){\r
+ this.error("Cannot find class for startup perspective : "+perspectiveName);\r
+ return;\r
+ }\r
+ var perspective = new perspectiveClass;\r
+ perspective.initViewPanes(viewsManager);\r
+ perspective.initViews(viewsManager);\r
+ this.setActivePerspective(perspective);\r
+ },\r
+ \r
+ /**\r
+ * After switching perspective, call this function to rebuild menu with the right selected.\r
+ */\r
+ rebuildPerspectiveMenus : function(){\r
+ var switchCommand = this.getCommandsDefinitions()["switchperspective"];\r
+ switchCommand.submenu = [];\r
+ var allPerspectives = this.getPerspectives();\r
+ for(var key in allPerspectives){\r
+ switchCommand.submenu.push({\r
+ "label":(allPerspectives[key].LABEL || key)+(key==this.getActivePerspectiveName()?" (current)":""),\r
+ "icon" :(allPerspectives[key].ICON || null),\r
+ "commandId":key,\r
+ "disabled" : (key==this.getActivePerspectiveName()?true:false)\r
+ });\r
+ }\r
+ if(switchCommand.command){ // Command already created : force reload\r
+ switchCommand.command.clearMenus();\r
+ switchCommand.command.setMenu(switchCommand.submenu);\r
+ }\r
+ },\r
+\r
+ /**\r
+ * Specific action of calling an external URL without triggering the "close()" method\r
+ * of Application.\r
+ * @param hrefValue {String} A download url that should reply with specific "attachment" header to avoid leaving the application.\r
+ */\r
+ javascriptDownloadLocation: function(hrefValue){\r
+ this.interruptClose = true;\r
+ document.location.href = hrefValue;\r
+ this.interruptClose = false;\r
+ },\r
+ \r
+ /**\r
+ * Called at Application ending (closing the browser).\r
+ */\r
+ close : function(){\r
+ if(this.interruptClose) return ; \r
+ if(this.getActivePerspective()){\r
+ this.getActivePerspective().remove(org.argeo.ria.components.ViewsManager.getInstance());\r
+ } \r
+ this.base(arguments);\r
+\r
+ }\r
+ \r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * Generic package for the Argeo Rich Internet Application based on qooxdoo (www.qooxdoo.org).\r
+ * This is an empty application in itself, since it requires at least a basic implementation to be useful.\r
+ * \r
+ * \r
+ * The skeleton of an ArgeoRIA is the following : \r
+ * \r
+ * + GUI Application containing a menubar, a toolbar, and an empty space for custom Perspective.\r
+ * \r
+ * + Various managers : org.argeo.ria.components.ViewsManager for manipulating IView (VIEW), org.argeo.ria.event.CommandsManager \r
+ * for automatically wiring commands inside the application (CONTROLLER), and org.argeo.ria.remote.RequestManager for accessing the data (MODEL).\r
+ * \r
+ * + A simple configuration will allow to start your own Perspective inside the application.\r
+ *\r
+ */
\ No newline at end of file
--- /dev/null
+/**\r
+ * A "dynamic" implementation of the standard TreeFolder class. \r
+ *\r
+ */\r
+qx.Class.define("org.argeo.ria.components.DynamicTreeFolder", {\r
+ extend : qx.ui.tree.TreeFolder,\r
+ \r
+ properties : {\r
+ /**\r
+ * The current state of the folder, usually "empty" => "loading" => "loaded"\r
+ */\r
+ "state" : {\r
+ check : "String",\r
+ init : "empty",\r
+ apply : "_applyState"\r
+ },\r
+ /**\r
+ * String to display as a child node during loading\r
+ */\r
+ "loadingString" : {\r
+ check : "String",\r
+ init : "Loading..."\r
+ },\r
+ /**\r
+ * Function that will load the children of this folder\r
+ */\r
+ "loader" : {\r
+ check : "Function",\r
+ init : function(treeFolder){treeFolder.setLoaded();}\r
+ },\r
+ /**\r
+ * Optionnal data describing the "drag" behaviour of the created children.\r
+ * First level is "file" or "folder", and for each of them, supported keys are "type" and "action". \r
+ */\r
+ "dragData": {\r
+ check : "Map",\r
+ init : {}\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Creates a new instance of DynamicTreeFolder\r
+ * @param label {String} Label of the folder\r
+ * @param loader {Function} Function that will load the children \r
+ * @param loadingString {String} String to display as a child node during loading\r
+ * @param dragData {Map} Optionnal data describing the "drag" behaviour of the created children.\r
+ */\r
+ construct : function(label, loader, loadingString, dragData){\r
+ this.base(arguments, label);\r
+ if(loader) this.setLoader(loader);\r
+ if(loadingString) this.setLoadingString(loadingString);\r
+ if(dragData) this.setDragData(dragData);\r
+ this.addListener("changeOpen", function(e){\r
+ if(e.getData() && this.getState() == "loading"){\r
+ this.load();\r
+ }\r
+ }, this);\r
+ this.setState("loading");\r
+ },\r
+ \r
+ members : {\r
+ /**\r
+ * Add an item to the folder \r
+ * @param varargs {Mixed} One or many children to add\r
+ */\r
+ add : function(varargs){\r
+ this.base(arguments, varargs);\r
+ for (var i=0, l=arguments.length; i<l; i++)\r
+ { \r
+ var treeItem = arguments[i];\r
+ if(treeItem == this.loadingChild) continue;\r
+ this.appendDragData(treeItem);\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * If there is dragData set, init the drag behaviour of a child\r
+ * @param treeItem {qx.ui.tree.AbstractTreeItem} Newly created child\r
+ */\r
+ appendDragData : function(treeItem){\r
+ var dragData = this.getDragData();\r
+ var nodeTypeDetected = false;\r
+ if(qx.Class.isSubClassOf(qx.Class.getByName(treeItem.classname), qx.ui.tree.TreeFile) && dragData["file"]){\r
+ nodeTypeDetected = "file";\r
+ }\r
+ if(qx.Class.isSubClassOf(qx.Class.getByName(treeItem.classname), qx.ui.tree.TreeFolder) && dragData["folder"]){\r
+ nodeTypeDetected = "folder";\r
+ } \r
+ if(!nodeTypeDetected) return;\r
+ \r
+ treeItem.setDraggable(true);\r
+ treeItem.addListener("dragstart", function(e){ \r
+ if(dragData[nodeTypeDetected]["type"]){\r
+ for(var j=0;j<dragData[nodeTypeDetected]["type"].length;j++){\r
+ e.addType(dragData[nodeTypeDetected]["type"][j]);\r
+ }\r
+ }\r
+ if(dragData[nodeTypeDetected]["action"]){\r
+ for(var j=0;j<dragData[nodeTypeDetected]["action"].length;j++){\r
+ e.addAction(dragData[nodeTypeDetected]["action"][j]);\r
+ }\r
+ }\r
+ }); \r
+ \r
+ },\r
+ \r
+ /**\r
+ * Set the state to "loaded"\r
+ */\r
+ setLoaded : function(){\r
+ this.setState("loaded");\r
+ },\r
+ /**\r
+ * Called when "state" is set to a new value\r
+ * @param state {String} the new state\r
+ */\r
+ _applyState : function(state){\r
+ if(state == "loaded"){\r
+ if(this.loadingChild){\r
+ this.remove(this.loadingChild);\r
+ delete this.loadingChild;\r
+ }\r
+ }else if(state == "loading" && !this.loadingChild){\r
+ this.addLoadingChild();\r
+ }\r
+ },\r
+ /**\r
+ * Create a temporary child with the loadingString label and add it.\r
+ */\r
+ addLoadingChild : function(){\r
+ this.loadingChild = new qx.ui.tree.TreeFile(this.getLoadingString());\r
+ this.add(this.loadingChild);\r
+ },\r
+ /**\r
+ * Call loader function.\r
+ */\r
+ load : function(){ \r
+ var loaderFunc = this.getLoader();\r
+ loaderFunc(this);\r
+ },\r
+ /**\r
+ * Empty then call loader function.\r
+ */\r
+ reload : function(){\r
+ this.removeAll();\r
+ this.setState("loading");\r
+ this.load();\r
+ }\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * \r
+ * Any component implementing this interface will generally be a user-interface indicating \r
+ * a "loading" status (button enabled/disabled, animated loading gif, etc...).\r
+ * The RequestManager can handle such an array of ILoadStatusable at the beginning/end of a Request.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Interface.define("org.argeo.ria.components.ILoadStatusable", {\r
+ \r
+ members : {\r
+ /**\r
+ * Sets the current status of the component.\r
+ * @param status {boolean} load status\r
+ * @return {Boolean}\r
+ */\r
+ setOnLoad : function(status){return true;}\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * This interface defines the main methods of an application Perpective.\r
+ * See the org.argeo.ria package documentation for more info on how to build an Application\r
+ * with this framework. \r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Interface.define("org.argeo.ria.components.IPerspective", {\r
+ \r
+ statics : {\r
+ /**\r
+ * The human readable name of the perspective\r
+ */\r
+ LABEL : "",\r
+ /**\r
+ * An image resource associated to the perspective\r
+ */\r
+ ICON : ""\r
+ },\r
+ \r
+ members : {\r
+ /**\r
+ * Initialize the available zones that will later contain IView implementations.\r
+ * This method is <b>in charge</b> of your panel to the main application zone \r
+ * (just below the toolbar).\r
+ * \r
+ * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
+ * \r
+ */\r
+ initViewPanes : function(viewsManager){return true;},\r
+ /**\r
+ * Once the zones are available and initialized, initialize the views here\r
+ * and add them to viewPanes. Trigger initial data loading, etc.\r
+ * \r
+ * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
+ * \r
+ */\r
+ initViews : function(viewsManager){return true},\r
+ /**\r
+ * Remove and destroy the perspective\r
+ * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
+ */\r
+ remove : function(viewsManager){return true}\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * Interface for a standard 'view' of an argeo RIA. A view is an independant applet that \r
+ * will be integrated inside a ViewPane. \r
+ * If this view is to implement a selection (a list, a tree, etc) that will trigger changes on commands, \r
+ * it must trigger a viewSelection#changeSelection event.\r
+ * \r
+ * The typical lifecycle of an IView will be the following :\r
+ * <br>+ init(viewPane) : initialize basic GUI in the viewPane\r
+ * <br>+ getCommands() : wire the commands and add them to the toolbars/menubars\r
+ * <br>+ load(data) : loads the data itself.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Interface.define("org.argeo.ria.components.IView", {\r
+ \r
+ properties : {\r
+ /**\r
+ * The commands definition Map that will be automatically added and wired to the menubar and toolbar.\r
+ * See {@link org.argeo.ria.event.CommandsManager#definitions} for the keys to use for defining commands.\r
+ */\r
+ commands : {},\r
+ viewSelection : {\r
+ nullable:false, \r
+ check:"org.argeo.ria.components.ViewSelection"\r
+ },\r
+ instanceId : {init:""},\r
+ instanceLabel : {init:""}\r
+ },\r
+ \r
+ members : {\r
+ /**\r
+ * The implementation should contain the GUI initialisation.\r
+ * This is the role of the manager to actually add the graphical component to the pane, \r
+ * so it's not necessary to do it here. \r
+ * @param viewPane {org.argeo.ria.components.ViewPane} The pane manager\r
+ * @param data {Mixed} Any object or data passed by the initiator of the view\r
+ * @return {Boolean}\r
+ */\r
+ init : function(viewPane, data){return true;},\r
+ /**\r
+ * The implementation should contain the real data loading (i.o. query...)\r
+ * @return {Boolean}\r
+ */\r
+ load : function(){return true;},\r
+ /**\r
+ * Whether this component is already contained in a scroller (return false) or not (return true).\r
+ * @return {Boolean}\r
+ */\r
+ addScroll : function(){return true;},\r
+ /**\r
+ * Called at destruction time\r
+ * Perform all the clean operations (stopping polling queries, etc.) \r
+ */\r
+ close : function(){return true;}\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * A modal window like console for the logs. \r
+ * Also opens a small alert window (qooxdoo, not native) on errors.\r
+ * \r
+ * @author : Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.Logger",\r
+{\r
+ type : "singleton",\r
+ extend : qx.ui.window.Window,\r
+ \r
+ construct : function(){\r
+ this.base(arguments, "Logs", "resource/slc/help-contents.png");\r
+ this.set({\r
+ showMaximize : true,\r
+ showMinimize : false,\r
+ width: 550,\r
+ height: 300\r
+ });\r
+ this.setLayout(new qx.ui.layout.Dock(0,5));\r
+ var buttonPane = new qx.ui.container.Composite(new qx.ui.layout.Canvas());\r
+ var closeButton = new qx.ui.form.Button("Close"); \r
+ closeButton.addListener("execute", function(e){\r
+ this.hide(); \r
+ }, this);\r
+ buttonPane.add(closeButton, {width:'20%',left:'40%'});\r
+ this.add(buttonPane, {edge:'south'});\r
+ this.setModal(false);\r
+ \r
+ var layout = new qx.ui.layout.VBox(2); \r
+ this._logPane = new qx.ui.container.Composite(layout);\r
+ var deco = new qx.ui.decoration.Single(1, 'solid', '#000000');\r
+ deco.setBackgroundColor("#ffffff")\r
+ var scroller = new qx.ui.container.Scroll(this._logPane);\r
+ scroller.setDecorator(deco);\r
+ this.add(scroller, {edge:'center', width:'100%', height:'100%'});\r
+ // Build style sheet content\r
+ var style =\r
+ [\r
+ '.messages{font-size:0.9em}',\r
+ '.messages div{padding:0px 4px;}',\r
+ '.messages .offset{font-weight:bold;}',\r
+ '.messages .object{font-style:italic;}',\r
+ \r
+ '.messages .user-command{color:blue}',\r
+ '.messages .user-result{background:white}',\r
+ '.messages .user-error{background:#FFE2D5}',\r
+ '.messages .level-debug{background:white}',\r
+ '.messages .level-info{background:#DEEDFA}',\r
+ '.messages .level-warn{background:#FFF7D5}',\r
+ '.messages .level-error{background:#FFE2D5}',\r
+ '.messages .level-user{background:#E3EFE9}',\r
+ '.messages .type-string{color:black;font-weight:normal;}',\r
+ '.messages .type-number{color:#155791;font-weight:normal;}',\r
+ '.messages .type-boolean{color:#15BC91;font-weight:normal;}',\r
+ '.messages .type-array{color:#CC3E8A;font-weight:bold;}',\r
+ '.messages .type-map{color:#CC3E8A;font-weight:bold;}',\r
+ '.messages .type-key{color:#565656;font-style:italic}',\r
+ '.messages .type-class{color:#5F3E8A;font-weight:bold}',\r
+ '.messages .type-instance{color:#565656;font-weight:bold}',\r
+ '.messages .type-stringify{color:#565656;font-weight:bold}'\r
+ ]; \r
+ // Include stylesheet\r
+ qx.bom.Stylesheet.createElement(style.join(""));\r
+ \r
+ },\r
+ \r
+ members : {\r
+ /**\r
+ * Adds a log in the GUI component.\r
+ * @param entry {Map} A log entry\r
+ */\r
+ process : function(entry){\r
+ var wrapper = qx.log.appender.Util.toHtml(entry);\r
+ var label = new qx.ui.basic.Label('<div class="messages"><div class="'+wrapper.className+'">'+wrapper.innerHTML.replace(",","<br/>")+'</div></div>'); \r
+ label.setRich(true);\r
+ if(entry.level == "error"){\r
+ if(!this.alert){\r
+ this.alert = new org.argeo.ria.components.Modal("Error");\r
+ this.alert.setPersistent(true);\r
+ this.alert.addCloseButton();\r
+ }\r
+ this.alert.addCenter(label.clone()); \r
+ this.alert.attachAndShow();\r
+ }else if(entry.level == "info"){\r
+ this.showLogAsPopup(label.clone());\r
+ }\r
+ this._logPane.addAt(label, 0);\r
+ },\r
+ /**\r
+ * Shows the GUI console and center it.\r
+ */\r
+ toggle : function(){\r
+ this.show();\r
+ this.center();\r
+ },\r
+ \r
+ /**\r
+ * Show a given info log in a small popup right-top aligned. \r
+ * The popup will disappear after 5 seconds.\r
+ * @param content {qx.ui.basic.Label} The content of the popup to display \r
+ */\r
+ showLogAsPopup:function(content){\r
+ if(!this.popup){\r
+ this.popup = new qx.ui.popup.Popup(new qx.ui.layout.Canvas()).set({\r
+ backgroundColor: "#DFFAD3",\r
+ padding: [2, 4],\r
+ width: 350,\r
+ offset:0,\r
+ position: "right-top"\r
+ });\r
+ }\r
+ this.popup.removeAll();\r
+ this.popup.add(content);\r
+ var appRoot = org.argeo.ria.components.ViewsManager.getInstance().getApplicationRoot(); \r
+ appRoot.add(this.popup);\r
+ this.popup.show();\r
+ this.popup.moveTo((qx.bom.Viewport.getWidth()-350), 0);\r
+ qx.event.Timer.once(function(){this.popup.hide();}, this, 5000);\r
+ }\r
+ },\r
+\r
+ destruct : function()\r
+ {\r
+ qx.log.Logger.unregister(this);\r
+ }\r
+ \r
+});\r
--- /dev/null
+/**\r
+ * Generic modal popup window.\r
+ * It is layed out with a dock layout. When adding components to it, they are added as "center".\r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.Modal",\r
+{\r
+ extend : qx.ui.window.Window,\r
+ \r
+ properties : {\r
+ persistent : {\r
+ check : "Boolean",\r
+ init : false\r
+ }\r
+ },\r
+ \r
+ events : {\r
+ /**\r
+ * Triggered when the user clicks the "ok" button. \r
+ */\r
+ "ok" : "qx.event.type.Event"\r
+ },\r
+ /**\r
+ * \r
+ * @param caption {String} Title of the window\r
+ * @param icon {String} Icon of the window\r
+ * @param text {String} Default content of the window.\r
+ */\r
+ construct : function(caption, icon, text){\r
+ this.base(arguments, caption, icon);\r
+ this.set({\r
+ showMaximize : false,\r
+ showMinimize : false,\r
+ width: 200,\r
+ height: 150\r
+ });\r
+ this.setLayout(new qx.ui.layout.Dock());\r
+ this.setModal(true);\r
+ this.center();\r
+ if(text){\r
+ this.addLabel(text);\r
+ }\r
+ },\r
+ \r
+ members : {\r
+ \r
+ addCenter : function(component){\r
+ if(!this.getPersistent()){\r
+ this.add(component, {edge : 'center', width:'100%'});\r
+ }else{\r
+ if(!this.centerScroller){\r
+ this.centerScroller = new qx.ui.container.Composite(new qx.ui.layout.VBox(1)); \r
+ this.add(new qx.ui.container.Scroll(this.centerScroller), {edge : 'center', width:'100%', height:'100%'});\r
+ }\r
+ this.centerScroller.add(component);\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Display text inside the popup\r
+ * @param text {String} A string content for the popup\r
+ */\r
+ addLabel:function(text){\r
+ var label = new qx.ui.basic.Label(text);\r
+ label.setRich(true);\r
+ label.setTextAlign("center");\r
+ this.addCenter(label);\r
+ this.addCloseButton();\r
+ },\r
+ /**\r
+ * Add a question and ok / cancel buttons\r
+ * @param text {String} The question to ask to the user\r
+ */\r
+ addConfirm : function(text){\r
+ var label = new qx.ui.basic.Label(text);\r
+ label.setRich(true);\r
+ label.setTextAlign("center");\r
+ this.addCenter(label);\r
+ this.addOkCancel();\r
+ },\r
+ /**\r
+ * Display a component (panel) in the center of the popup\r
+ * @param panel {qx.ui.core.Widget} A gui component (will be set at width 100%).\r
+ */\r
+ addContent: function(panel){\r
+ this.addCenter(panel);\r
+ this.addCloseButton();\r
+ },\r
+ /**\r
+ * Automatically attach to the application root, then show.\r
+ */\r
+ attachAndShow:function(){\r
+ if(!this.attached){\r
+ org.argeo.ria.components.ViewsManager.getInstance().getApplicationRoot().add(this);\r
+ this.attached = true;\r
+ }\r
+ this.show();\r
+ },\r
+ /**\r
+ * Adds a close button bottom-center aligned to the popup\r
+ */\r
+ addCloseButton : function(){\r
+ this.closeButton = new qx.ui.form.Button("Close");\r
+ this.closeButton.addListener("execute", this._closeAndDestroy, this);\r
+ this.add(this.closeButton, {edge:'south'}); \r
+ },\r
+ /**\r
+ * Adds two buttons bottom-center aligned (Ok and Cancel). \r
+ * Ok button has no listener by default, Cancel will close and destroy the popup.\r
+ */\r
+ addOkCancel : function(){\r
+ var buttonPane = new qx.ui.container.Composite(new qx.ui.layout.HBox(5, 'right'));\r
+ buttonPane.setAlignX("center");\r
+ this.add(buttonPane, {edge:"south"});\r
+ this.okButton = new qx.ui.form.Button("Ok");\r
+ this.okButton.addListener("execute", function(e){\r
+ this.fireEvent("ok");\r
+ this._closeAndDestroy();\r
+ }, this);\r
+ this.cancelButton = new qx.ui.form.Button("Cancel");\r
+ this.cancelButton.addListener("execute", this._closeAndDestroy, this);\r
+ buttonPane.add(this.okButton);\r
+ buttonPane.add(this.cancelButton);\r
+ },\r
+ /**\r
+ * Adds a prompt form to the popup : a question, followed by a text input.\r
+ * @param questionString {String} The question to ask to the user \r
+ * @param validationCallback {Function} Callback to apply : takes the text input value as unique argument.\r
+ * @param callbackContext {Object} Context for the callback, optional.\r
+ */\r
+ makePromptForm:function(questionString, validationCallback, callbackContext){\r
+ var label = new qx.ui.basic.Label(questionString);\r
+ label.setRich(true);\r
+ label.setTextAlign("center");\r
+ this.add(label, {edge:'north'});\r
+ var textField = new qx.ui.form.TextField();\r
+ textField.setMarginTop(10);\r
+ textField.setMarginBottom(10);\r
+ this.add(textField, {edge:'center'});\r
+ this.addOkCancel();\r
+ if(callbackContext){\r
+ validationCallback = qx.lang.Function.bind(validationCallback, callbackContext);\r
+ }\r
+ this.okButton.addListener("execute", function(e){\r
+ var valid = validationCallback(textField.getValue());\r
+ if(valid) this._closeAndDestroy();\r
+ }, this);\r
+ },\r
+ /**\r
+ * Close this modal window and destroy it.\r
+ */\r
+ _closeAndDestroy : function(){\r
+ this.hide();\r
+ if(!this.getPersistent()){\r
+ this.destroy();\r
+ }else{\r
+ if(this.centerScroller) this.centerScroller.removeAll();\r
+ }\r
+ }\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * A more elaborate views container than ViewPane, as it can handle multiple contents\r
+ * at once via a TabView.\r
+ * See {@link org.argeo.ria.components.ViewPane}.\r
+ */\r
+qx.Class.define("org.argeo.ria.components.TabbedViewPane",\r
+{\r
+ extend : qx.ui.container.Composite,\r
+ implement : [org.argeo.ria.components.ILoadStatusable],\r
+\r
+ /**\r
+ * @param viewId {String} Unique id of this viewPane\r
+ * @param viewTitle {String} Readable Title of this viewPane\r
+ */\r
+ construct : function(viewId, viewTitle){\r
+ this.base(arguments);\r
+ this.setViewId(viewId);\r
+ this._defaultViewTitle = viewTitle; \r
+ this.setLayout(new qx.ui.layout.Canvas());\r
+ this.blurredDecorator = new qx.ui.decoration.Uniform(1, "solid", "#000");\r
+ this.blurredDecorator.setBackgroundImage("decoration/app-header.png");\r
+ this.blurredDecorator.setBackgroundRepeat("scale");\r
+ this.setDecorator(this.blurredDecorator);\r
+\r
+ this.focusedDecorator = new qx.ui.decoration.Uniform(1, "solid", "#065fb2");\r
+ this.focusedDecorator.setBackgroundImage("decoration/app-header.png");\r
+ this.focusedDecorator.setBackgroundRepeat("scale");\r
+ \r
+ this.tabView = new qx.ui.tabview.TabView();\r
+ this.tabView.setAppearance("widget");\r
+ // Empty mode\r
+ this.add(this.tabView, {top: 7, width:"100%", bottom:0});\r
+ this.tabView.setBackgroundColor("#fff");\r
+ this.tabView.setMarginTop(27);\r
+ \r
+ this.tabView.addListener("changeSelected", function(){\r
+ this.fireEvent("changeSelection");\r
+ }, this);\r
+ \r
+ \r
+ this.setFocusable(true);\r
+ this.addListener("click", function(e){\r
+ this.fireDataEvent("changeFocus", this);\r
+ }, this); \r
+ \r
+ this.pageIds = {};\r
+ },\r
+\r
+ properties : {\r
+ /**\r
+ * Unique id of the pane\r
+ */\r
+ viewId : {init:""},\r
+ /**\r
+ * Human-readable title for this view\r
+ */\r
+ viewTitle : {init:"", event:"changeViewTitle"},\r
+ /**\r
+ * Has its own scrollable content \r
+ */\r
+ ownScrollable : {init: false, check:"Boolean"},\r
+ /**\r
+ * Map of commands definition\r
+ * @see org.argeo.ria.event.Command \r
+ */\r
+ commands : {init : null, nullable:true, check:"Map"}\r
+ \r
+ },\r
+ \r
+ members : {\r
+ /**\r
+ * Checks if the pane already contains a given view, identified by its instance id\r
+ * @param contentId {Mixed} The instance id to check\r
+ * @return {Boolean}\r
+ */\r
+ contentExists : function(contentId){\r
+ if(this.pageIds[contentId]){\r
+ this.tabView.setSelected(this.pageIds[contentId]);\r
+ return this.pageIds[contentId].getUserData("argeoria.iview");\r
+ } \r
+ },\r
+ /**\r
+ * Sets a new instance in the tabbed pane.\r
+ * @param content {org.argeo.ria.components.IView} The applet to add.\r
+ */\r
+ setContent : function(content){\r
+ if(!this.tabView.getChildren().length){\r
+ this.tabView.setBackgroundColor("transparent");\r
+ this.tabView.setMarginTop(0); \r
+ }\r
+ var contentId = content.getInstanceId();\r
+ var page = new qx.ui.tabview.Page(content.getInstanceLabel());\r
+ this.pageIds[contentId] = page;\r
+ page.setPadding(0);\r
+ page.setLayout(new qx.ui.layout.Canvas());\r
+ page.add(content, {width:"100%", top:0, bottom:0});\r
+ this.tabView.add(page); \r
+ page.setUserData("argeoria.iview", content); \r
+ content.getViewSelection().addListener("changeSelection", function(e){\r
+ this.fireEvent("changeSelection");\r
+ }, this);\r
+ this.tabView.setSelected(page);\r
+ },\r
+ /**\r
+ * Get the currently selected tab content, if any.\r
+ * @return {org.argeo.ria.components.IView} The currently selected view.\r
+ */\r
+ getContent : function(){\r
+ if(this._getCrtPage()){\r
+ return this._getCrtPage().getUserData("argeoria.iview");\r
+ }\r
+ return null;\r
+ },\r
+ /**\r
+ * Get the currently selected tab ViewSelection object.\r
+ * @return {org.argeo.ria.components.ViewSelection} The view selection object of the currently selected view.\r
+ */\r
+ getViewSelection : function(){\r
+ if(!this.getContent()) return null;\r
+ return this.getContent().getViewSelection();\r
+ },\r
+ /**\r
+ * Return the currently selected tab Page.\r
+ * @return {qx.ui.tabview.Page} The page\r
+ */\r
+ _getCrtPage : function(){\r
+ return this.tabView.getSelected();\r
+ },\r
+ /**\r
+ * Closes the currently selected view and remove all tabs components (button, page).\r
+ */\r
+ closeCurrent : function(){\r
+ var crtPage = this._getCrtPage();\r
+ if(!crtPage) return;\r
+ var iView = crtPage.getUserData("argeoria.iview");\r
+ var iViewInstance = iView.getInstanceId();\r
+ iView.close(); \r
+ this.tabView.remove(crtPage);\r
+ delete(this.pageIds[iViewInstance]); \r
+ if(!this.tabView.getChildren().length){ // No more tabs : remove commands!\r
+ if(this.getCommands()){\r
+ org.argeo.ria.event.CommandsManager.getInstance().removeCommands(this.getCommands(), this.getViewId());\r
+ this.setCommands(null);\r
+ } \r
+ this.tabView.setBackgroundColor("#fff");\r
+ this.tabView.setMarginTop(27);\r
+ } \r
+ },\r
+ /**\r
+ * Call closeCurrent() recursively until there is no more page.\r
+ */\r
+ empty : function(){\r
+ var crtPage = this._getCrtPage();\r
+ while(crtPage){\r
+ this.closeCurrent();\r
+ crtPage = this._getCrtPage();\r
+ }\r
+ },\r
+ /**\r
+ * Sets the tabView on "load" state. Nothing is done at the moment.\r
+ * @param load {Boolean} Load status\r
+ */\r
+ setOnLoad : function(load){\r
+ \r
+ },\r
+ /**\r
+ * Sets a graphical indicator that this pane has the focus. A blue border.\r
+ */\r
+ focus : function(){\r
+ if(this.hasFocus) return;\r
+ this.fireEvent("changeSelection");\r
+ this.setDecorator(this.focusedDecorator);\r
+ this.hasFocus = true;\r
+ }, \r
+ /**\r
+ * Remove a graphical focus indicator on this pane.\r
+ */\r
+ blur : function(){\r
+ this.hasFocus = false;\r
+ this.setDecorator(this.blurredDecorator);\r
+ }\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**
+ * A standard view container, referenced in the application by its unique id.
+ * It is managed by the ViewsManager singleton that works as the "View" part of an MVC model.
+ * @see org.argeo.ria.components.ViewsManager
+ * @author Charles
+ */
+qx.Class.define("org.argeo.ria.components.ViewPane",
+{
+ extend : qx.ui.container.Composite,
+ implement : [org.argeo.ria.components.ILoadStatusable],
+
+ /**
+ * @param viewId {String} Unique id of this viewPane
+ * @param viewTitle {String} Readable Title of this viewPane
+ * @param splitPaneData {Map} Additionnal data to be used by splitpanes implementations.
+ */
+ construct : function(viewId, viewTitle, splitPaneData){
+ this.base(arguments);
+ this.setViewId(viewId);
+ this._defaultViewTitle = viewTitle;
+ this.setViewTitle(viewTitle);
+ if(splitPaneData){
+ this.setSplitPaneData(splitPaneData);
+ }
+ this.setFocusable(true);
+ this.addListener("click", function(e){
+ this.fireDataEvent("changeFocus", this);
+ }, this);
+ this.createGui();
+ },
+
+ events : {
+ /**
+ * Trigger when the focus is changing
+ */
+ "changeFocus" : "qx.event.type.Data",
+ /**
+ * Triggered when selection of content has changed.
+ */
+ "changeSelection" : "qx.event.type.Event"
+ },
+
+ properties :
+ {
+ /**
+ * Unique id of the pane
+ */
+ viewId : {init:""},
+ /**
+ * Human-readable title for this view
+ */
+ viewTitle : {init:"", event:"changeViewTitle"},
+ /**
+ * Has its own scrollable content
+ */
+ ownScrollable : {init: false, check:"Boolean"},
+ /**
+ * Data concerning the split pane
+ */
+ splitPaneData : {init : null, check:"Map"},
+ /**
+ * Map of commands definition
+ * @see org.argeo.ria.event.Command
+ */
+ commands : {init : null, nullable:true, check:"Map"},
+ /**
+ * The real business content.
+ */
+ content : {
+ init: null,
+ nullable : true,
+ check : "org.argeo.ria.components.IView",
+ apply : "_applyContent"
+ }
+ },
+
+ /*
+ *****************************************************************************
+ MEMBERS
+ *****************************************************************************
+ */
+
+ members :
+ {
+ /**
+ * Creates a standard GUI for the viewPane, including a container for an IView.
+ */
+ createGui : function(){
+ this.setLayout(new qx.ui.layout.VBox());
+ this.header = new qx.ui.container.Composite();
+ this.header.setLayout(new qx.ui.layout.Dock());
+ this.loadImage = new qx.ui.basic.Image('resource/slc/ajax-loader.gif');
+ this.header.set({appearance:"app-header", height:34});
+ this.headerLabel = new qx.ui.basic.Label(this.getViewTitle());
+ this.header.add(this.headerLabel, {edge:"west"});
+ this.addListener("changeViewTitle", function(e){
+ var newTitle = e.getData();
+ if(newTitle != ""){
+ this.headerLabel.setContent(newTitle);
+ if(e.getOldData() == ""){
+ this.header.add(this.headerLabel, {edge:"west"});
+ }
+ }else{
+ this.header.remove(this.headerLabel);
+ }
+ }, this);
+ this.add(this.header);
+ this.setDecorator(new qx.ui.decoration.Single(1,"solid","#000"));
+ /*
+ // Open close button of splitPane, not very useful at the moment.
+ if(this.getSplitPaneData()){
+ var data = this.getSplitPaneData();
+ var imgName = (data.orientation=="horizontal"?"go-left":"go-bottom");
+ var image = new qx.ui.basic.Image("resource/slc/"+imgName+".png");
+ image.addListener("click", function(e){
+ var image = e.getTarget();
+ var data = this.getSplitPaneData();
+ var functionDim = (data.orientation=="horizontal"?"Width":"Height");
+ var objectToResize = data.object || this;
+ var crtDim = objectToResize["get"+functionDim]();
+ var minimize = (data.orientation=="horizontal"?"go-right":"go-top");
+ var maximize = (data.orientation=="horizontal"?"go-left":"go-bottom");
+ if(crtDim > data.min){
+ objectToResize["set"+functionDim](data.min);
+ image.setSource("resource/slc/"+minimize+".png");
+ this.origDimension = crtDim;
+ }else{
+ if(this.origDimension){
+ objectToResize["set"+functionDim](this.origDimension);
+ image.setSource("resource/slc/"+maximize+".png");
+ }
+ }
+ }, this);
+ this.header.add(image,{edge:"east"});
+ }
+ */
+ },
+
+ /**
+ * Get the content ViewSelection object.
+ * @return {org.argeo.ria.components.ViewSelection} The view selection
+ */
+ getViewSelection : function(){
+ if(this.getContent()){
+ return this.getContent().getViewSelection();
+ }
+ return null;
+ },
+ /**
+ * Checks if the pane already contains a given view, identified by its instance id
+ * @param iViewId {Mixed} The instance id to check
+ * @return {Boolean}
+ */
+ contentExists : function(iViewId){
+ if(this.getContent()){
+ this.empty();
+ }
+ return false;
+ },
+
+ /**
+ * Sets the content of this pane.
+ * @param content {org.argeo.ria.components.IView} An IView implementation
+ */
+ _applyContent : function(content){
+ if(content == null) return;
+ var addScrollable = (content.addScroll?content.addScroll():false);
+ if(addScrollable){
+ this.setOwnScrollable(true);
+ this.scrollable = new qx.ui.container.Scroll(content);
+ this.add(this.scrollable, {flex: 1});
+ }else{
+ this.guiContent = content;
+ this.add(this.guiContent, {flex:1});
+ }
+ content.getViewSelection().addListener("changeSelection", function(e){
+ this.fireEvent("changeSelection");
+ }, this);
+ },
+ /**
+ * Adds a graphical component too the header of the view pane.
+ * It is added as "center" in the dock layout, and will override the view pane title label.
+ * For example, you can add your own title, or add a switch, or buttons, etc.
+ * @param component {qx.ui.core.Widget} The graphical component to add.
+ */
+ addHeaderComponent : function(component){
+ this.header.setPadding(4);
+ this.header.add(component, {edge:"center"});
+ component.setTextColor("#1a1a1a");
+ this.loadImage.setMargin(4);
+ },
+
+ /**
+ * Implementation of the ILoadStatusable interface.
+ * @see org.argeo.ria.components.ILoadStatusable
+ * @param load {Boolean} The loading status
+ */
+ setOnLoad : function(load){
+ if(load){
+ this.header.add(this.loadImage, {edge:"east"});
+ }else{
+ this.header.remove(this.loadImage);
+ }
+ },
+
+ /**
+ * Call empty() method, since this pane can only handle one view.
+ */
+ closeCurrent : function(){
+ this.empty();
+ },
+
+ /**
+ * Removes and destroy the IView content of this viewPane.
+ */
+ empty: function(){
+ if(this.getOwnScrollable() && this.scrollable){
+ this.remove(this.scrollable);
+ }else if(this.guiContent){
+ this.remove(this.guiContent);
+ }
+ if(this.getCommands()){
+ org.argeo.ria.event.CommandsManager.getInstance().removeCommands(this.getCommands());
+ this.setCommands(null);
+ }
+ if(this.getContent()){
+ this.getContent().close();
+ }
+ this.setViewTitle(this._defaultViewTitle);
+ this.setContent(null);
+ },
+ /**
+ * Sets a graphical indicator that this pane has the focus. A blue border.
+ */
+ focus : function(){
+ if(this.hasFocus) return;
+ this.setDecorator(new qx.ui.decoration.Single(1,"solid","#065fb2"));
+ this.fireEvent("changeSelection");
+ this.hasFocus = true;
+ },
+ /**
+ * Remove a graphical focus indicator on this pane.
+ */
+ blur : function(){
+ this.hasFocus = false;
+ this.setDecorator(new qx.ui.decoration.Single(1,"solid","#000"));
+ }
+
+ }
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * Generic selection model associated to an IView content opened in a given ViewPane.\r
+ * It contains in an array any row/data/node, and triggers changeSelection data events.\r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.ViewSelection",\r
+{\r
+ extend : qx.core.Object,\r
+ \r
+ /**\r
+ * @param viewId {String} The ViewPane unique id\r
+ */\r
+ construct : function(viewId){\r
+ this.base(arguments);\r
+ this.nodes = [];\r
+ this.setViewId(viewId);\r
+ },\r
+\r
+ properties : {\r
+ /**\r
+ * The viewPane unique id \r
+ */\r
+ viewId : {\r
+ check : "String",\r
+ nullable: false\r
+ }\r
+ },\r
+ \r
+ events : {\r
+ /**\r
+ * Triggered each time the selection changes.\r
+ */\r
+ "changeSelection" : "qx.event.type.Data"\r
+ },\r
+ \r
+ /*\r
+ *****************************************************************************\r
+ MEMBERS\r
+ *****************************************************************************\r
+ */\r
+\r
+ members :\r
+ {\r
+ /**\r
+ * Empty the selection\r
+ */\r
+ clear : function(){\r
+ this.nodes = [];\r
+ this.triggerEvent();\r
+ },\r
+ \r
+ /**\r
+ * Add a row or xml node or whatever\r
+ * @param node {mixed} Data to add to the selection\r
+ */\r
+ addNode : function(node) {\r
+ this.nodes.push(node);\r
+ this.triggerEvent();\r
+ },\r
+ \r
+ /**\r
+ * The number of rows/nodes selected\r
+ * @return {Integer}\r
+ */\r
+ getCount : function() {\r
+ return this.nodes.length;\r
+ },\r
+ \r
+ /**\r
+ * Returns the content of the selection \r
+ * @return {Array}\r
+ */\r
+ getNodes : function(){\r
+ return this.nodes;\r
+ },\r
+ \r
+ /**\r
+ * Creates and fire a data event changeSelection\r
+ */\r
+ triggerEvent : function(){\r
+ this.fireDataEvent("changeSelection", this);\r
+ }\r
+ \r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * The main "view" manager (in a standard MVC conception) of the application.\r
+ * It register various containers org.argeo.ria.components.viewPane and feed them with org.argeo.ria.components.IView implementations.\r
+ * It is a singleton and can thus be called by any part of the application.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.ViewsManager",\r
+{\r
+ type : "singleton",\r
+ extend : qx.core.Object,\r
+\r
+ properties : {\r
+ /**\r
+ * The application root (like Application.getRoot()), used to attach and show modal windows.\r
+ */\r
+ applicationRoot : {init : null},\r
+ /**\r
+ * The main container for the org.argeo.ria.components.ViewPane instances. \r
+ */\r
+ viewPanesContainer : {init: null},\r
+ /**\r
+ * Keeps the currently focused viewPane. \r
+ */\r
+ currentFocus : {init :null}\r
+ },\r
+ construct : function(){\r
+ this.views = {};\r
+ },\r
+ members : {\r
+ /**\r
+ * Initialize and load a given IView implementation into a viewPane.\r
+ * The IView itself is returned.\r
+ * \r
+ * @param classObj {Clazz} The class object to instantiate\r
+ * @param viewPaneId {String} The unique ID of the view pane\r
+ * @param data {Mixed} Any data provided by the opener.\r
+ * @return {org.argeo.ria.components.IView}\r
+ */\r
+ initIViewClass: function(classObj, viewPaneId, data){\r
+ var viewPane = this.getViewPaneById(viewPaneId); \r
+ var iView = new classObj;\r
+ iView.init(viewPane, data);\r
+ var existingView = viewPane.contentExists(iView.getInstanceId()); \r
+ if(existingView){\r
+ delete iView;\r
+ return existingView;\r
+ }\r
+ var commands = iView.getCommands();\r
+ //viewPane.empty();\r
+ if(commands){\r
+ viewPane.setCommands(commands);\r
+ org.argeo.ria.event.CommandsManager.getInstance().addCommands(commands, "view:"+viewPaneId, viewPaneId);\r
+ }\r
+ viewPane.setContent(iView);\r
+ this.setViewPaneFocus(viewPane);\r
+ return iView;\r
+ },\r
+ \r
+ /**\r
+ * Registers a new viewPane\r
+ * @param viewPane {org.argeo.ria.components.ViewPane} The new ViewPane instance\r
+ */\r
+ registerViewPane : function(viewPane){\r
+ this.views[viewPane.getViewId()] = viewPane;\r
+ viewPane.addListener("changeSelection", function(e){\r
+ var viewSelection = e.getTarget().getViewSelection();\r
+ if(!viewSelection) return;\r
+ org.argeo.ria.event.CommandsManager.getInstance().refreshCommands(viewSelection);\r
+ });\r
+ viewPane.addListener("changeFocus", function(e){\r
+ this.setViewPaneFocus(e.getTarget());\r
+ }, this);\r
+ },\r
+ /**\r
+ * Sets a given viewPane as the currently focused one. Blur the others.\r
+ * @param viewPane {org.argeo.ria.components.ViewPane} The viewPane (or TabbedViewPane) to focus on.\r
+ */\r
+ setViewPaneFocus : function(viewPane){\r
+ for(var key in this.views){\r
+ this.views[key].blur();\r
+ }\r
+ this.setCurrentFocus(viewPane);\r
+ viewPane.focus(); \r
+ },\r
+ /**\r
+ * Returns a viewPane by its unique id.\r
+ * @param viewPaneId {String} The unique id\r
+ * @return {org.argeo.ria.components.ViewPane}\r
+ */\r
+ getViewPaneById : function(viewPaneId){\r
+ if(this.views[viewPaneId]) return this.views[viewPaneId];\r
+ throw new Error("Cannot find view '"+viewPaneId+"'"); \r
+ },\r
+ /**\r
+ * Returns a viewPane current viewSelection object\r
+ * @param viewPaneId {String} The unique id. \r
+ * @return {org.argeo.ria.components.ViewSelection}\r
+ */\r
+ getViewPaneSelection : function(viewPaneId){\r
+ return this.getViewPaneById(viewPaneId).getViewSelection();\r
+ },\r
+ /**\r
+ * Changes a viewPane title dynamically.\r
+ * @param viewPaneId {String} ViewPane unique Id. \r
+ * @param viewTitle {String} the new title for this viewPane.\r
+ */\r
+ setViewPaneTitle : function(viewPaneId, viewTitle){\r
+ this.getViewPaneById(viewPaneId).setViewTitle(viewTitle);\r
+ }\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * Package containing GUI components and main interfaces for GUI components to \r
+ * be used by any argeo RIA application.\r
+ *\r
+ */
\ No newline at end of file
--- /dev/null
+/**\r
+ * The standard command for all actions. It registers keyboard shortcuts, centralizes \r
+ * command state, callback, etc. It is defined by command definitions that can be found \r
+ * in the CommandsManager. \r
+ */\r
+ qx.Class.define("org.argeo.ria.event.Command",\r
+{\r
+ extend : qx.event.Command,\r
+ implement : [org.argeo.ria.components.ILoadStatusable],\r
+\r
+ properties : {\r
+ /**\r
+ * Unique id of the command \r
+ */\r
+ id : {init:""},\r
+ /**\r
+ * Label of the command \r
+ */\r
+ label : {init:""},\r
+ /**\r
+ * Icon of the command \r
+ */\r
+ icon : {init:""},\r
+ /**\r
+ * Weather this command is a true/false state \r
+ */\r
+ toggle : {init:false},\r
+ /**\r
+ * It toggle button, initial state \r
+ */\r
+ toggleInitialState : {init : false},\r
+ /**\r
+ * Sub menu if needed \r
+ */\r
+ menu : {\r
+ nullable: true,\r
+ event : "changeMenu"\r
+ },\r
+ /**\r
+ * Callback associated to the submenu of the command \r
+ */\r
+ menuCallback : {nullable:true},\r
+ /**\r
+ * Context used when triggering menuCallback \r
+ */\r
+ menuContext : {nullable:true}\r
+ },\r
+ \r
+ /**\r
+ * @param id {String} Id of the command\r
+ * @param label {String} Label of the command\r
+ * @param icon {String} Icon of the command\r
+ * @param shortcut {String} Keyboard Shortcut (like alt+o, ctrl+z, etc..)\r
+ */\r
+ construct : function(id, label, icon, shortcut){\r
+ this.base(arguments, shortcut);\r
+ this.setId(id);\r
+ this.setLabel(label);\r
+ this.setIcon(icon); \r
+ this.menuClones = [];\r
+ this.callbacks = {};\r
+ },\r
+ \r
+ members :\r
+ {\r
+ /**\r
+ * Create a Button that suits a qx.ui.menu.MenuBar linked to this command\r
+ * @return {qx.ui.menu.Button}\r
+ */\r
+ getMenuButton : function(){\r
+ if(this.getToggle()){\r
+ button = new qx.ui.menu.CheckBox(this.getLabel());\r
+ this._registerToggleButtonListeners(button);\r
+ }else{\r
+ var button = new qx.ui.menu.Button(\r
+ this.getLabel(), \r
+ this.getIcon(), \r
+ this, \r
+ this.getMenuClone()\r
+ );\r
+ if(this.getMenu()){\r
+ this.addListener("changeMenu", function(event){\r
+ button.setMenu(this.getMenuClone());\r
+ }, this); \r
+ }\r
+ }\r
+ this.addTooltip(button);\r
+ return button;\r
+ },\r
+ \r
+ /**\r
+ * Create a Button that suits a qx.ui.toolbar.Toolbar part linked to this command.\r
+ * @return {qx.ui.toolbar.MenuButton}\r
+ */\r
+ getToolbarButton : function(){\r
+ var button;\r
+ if(this.getMenu()){\r
+ button = new qx.ui.toolbar.MenuButton(\r
+ this.getLabel(),\r
+ this.getIcon(), \r
+ this.getMenuClone()\r
+ );\r
+ this.addListener("changeMenu", function(event){\r
+ button.setMenu(this.getMenuClone());\r
+ }, this);\r
+ this.addListener("changeEnabled", function(e){\r
+ this.setEnabled(e.getData());\r
+ }, button);\r
+ button.setEnabled(this.getEnabled());\r
+ }else if(this.getToggle()){\r
+ button = new qx.ui.toolbar.CheckBox(this.getLabel(), this.getIcon());\r
+ if(this.getToggleInitialState()){\r
+ button.setChecked(true);\r
+ }\r
+ this._registerToggleButtonListeners(button);\r
+ }else{\r
+ button = new qx.ui.toolbar.Button(\r
+ this.getLabel(),\r
+ this.getIcon(),\r
+ this\r
+ );\r
+ }\r
+ this.addTooltip(button);\r
+ return button;\r
+ },\r
+ \r
+ /**\r
+ * Register a given callback to be shared by one or more focusable part.\r
+ * @param callback {Function} A callback function\r
+ * @param focusablePartId {String} A string identifiing a focusable part. At the moment, it can only be "view:viewId"\r
+ */\r
+ registerCallback : function(callback, focusablePartId){\r
+ this.callbacks[focusablePartId] = callback;\r
+ },\r
+ /**\r
+ * Return all the registered callbacks for this command.\r
+ * @return {Map} A map of callback, viewId => callBack.\r
+ */\r
+ getCallbacks : function(){\r
+ return this.callbacks;\r
+ },\r
+ /**\r
+ * Remove a callback for a given focusable part.\r
+ * @param focusablePartId {String} A id like "view:viewId".\r
+ */\r
+ removeCallback : function(focusablePartId){\r
+ if(this.callbacks[focusablePartId]){\r
+ delete this.callbacks[focusablePartId];\r
+ }\r
+ }, \r
+ \r
+ /**\r
+ * Special tricks using UserData to enable/disable listeners to avoid loops...\r
+ * @param button {qx.ui.core.Widget} toolbar Checkbox or menu Checkbox button.\r
+ */\r
+ _registerToggleButtonListeners : function(button){\r
+ button.addListener("changeChecked", function(event){\r
+ if(button.getUserData("disableListener")) return;\r
+ this.setUserData("slc.command.toggleState", event.getData());\r
+ this.setUserData("slc.command.toggleStateSource", button);\r
+ this.fireEvent("execute");\r
+ }, this);\r
+ this.addListener("execute", function(event){\r
+ if(this.getUserData("slc.command.toggleStateSource") == button) return;\r
+ button.setUserData("disableListener", true);\r
+ button.setChecked(this.getUserData("slc.command.toggleState"));\r
+ button.setUserData("disableListener", false);\r
+ }, this); \r
+ },\r
+ \r
+ /**\r
+ * Clones the command menu\r
+ * @return {qx.ui.menu.Menu}\r
+ */\r
+ getMenuClone : function(){\r
+ var menuClone = new qx.ui.menu.Menu();\r
+ menuClone.setMinWidth(100);\r
+ var submenus = this.getMenu();\r
+ if(!submenus) return;\r
+ for(var i=0;i<submenus.length;i++){\r
+ if(submenus[i].separator){\r
+ menuClone.add(new qx.ui.menu.Separator());\r
+ }else{\r
+ var button = new qx.ui.menu.Button(submenus[i].label, submenus[i].icon);\r
+ if(submenus[i].disabled){\r
+ button.setEnabled(false);\r
+ }\r
+ button.setUserData("commandId", submenus[i].commandId);\r
+ button.addListener("execute", this.executeSubMenuCallback, this);\r
+ menuClone.add(button);\r
+ }\r
+ }\r
+ this.menuClones.push(menuClone);\r
+ return menuClone;\r
+ },\r
+ \r
+ /**\r
+ * Remove all existing menus and their clones.\r
+ */\r
+ clearMenus : function(){\r
+ if(!this.getMenu()) return;\r
+ for(var i=0;i<this.menuClones.length;i++){\r
+ this.menuClones[i].destroy();\r
+ }\r
+ this.menuClones = [];\r
+ },\r
+ \r
+ /**\r
+ * Triggers the menuCallback property in the right context.\r
+ * @param event {qx.event.type.Event} The firing event.\r
+ */\r
+ executeSubMenuCallback : function(event){\r
+ var button = event.getTarget();\r
+ var callback = this.getMenuCallback();\r
+ var context;\r
+ if(this.getMenuContext()){\r
+ if(typeof(this.getMenuContext()) == "object") context = this.getMenuContext();\r
+ else if(typeof(this.getMenuContext()) == "string"){\r
+ if(this.getMenuContext().split(":")[0] == "view"){\r
+ var viewId = this.getMenuContext().split(":")[1];\r
+ context = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
+ }\r
+ }\r
+ }\r
+ callback = qx.lang.Function.bind(callback, context || this);\r
+ callback(button.getUserData("commandId")); \r
+ }, \r
+ /**\r
+ * Adds a tooltip to a button.\r
+ * @param element {qx.ui.core.Widget} The element to which the command tooltip is added. \r
+ */\r
+ addTooltip : function(element){\r
+ if(this.getShortcut() != null){\r
+ element.setToolTip(new qx.ui.tooltip.ToolTip(this.getShortcut()));\r
+ } \r
+ },\r
+ \r
+ /**\r
+ * Implementation of the ILoadStatusable interface.\r
+ * Sets the whole command enabled if not loading and disabled if loading.\r
+ * @param status {Boolean} The loading status of the button. \r
+ */\r
+ setOnLoad : function(status){\r
+ this.setEnabled(!status);\r
+ }\r
+ \r
+ }\r
+});\r
--- /dev/null
+/**\r
+ * The main controller (in a standard MVC point of view) of the application. It is a singleton\r
+ * thus can be called by any part of the application.\r
+ * This will wire all the commands that can be defined dynamically by any IView, and add their\r
+ * corresponding buttons to the application menubar and toolbars.\r
+ * See the "definitions" property documentation below for more info on how to define new commands.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.event.CommandsManager",\r
+{\r
+ type : "singleton",\r
+ extend : qx.core.Object,\r
+\r
+ construct : function(){\r
+ this.base(arguments);\r
+ this.setInitialDefinitions(qx.lang.Object.copy(this.getDefinitions()));\r
+ this.addListener("changedCommands", this.createCommands, this);\r
+ },\r
+\r
+ properties : \r
+ {\r
+ /**\r
+ * The commands definitions is a Map described as below\r
+ * <pre>\r
+ * {\r
+ * <b>label : "",</b> \r
+ * | The label of the action\r
+ * \r
+ * <b>icon : "",</b> \r
+ * | The icon image\r
+ * \r
+ * <b>shortcut : "",</b>\r
+ * | The keyboard shortcut, as defined in qooxdoo (Control+s, Alt+k, etc.). Warning, the letter must be lowercase.\r
+ * \r
+ * <b>enabled : true,</b>\r
+ * | Whether it is enabled or disabled at creation\r
+ * \r
+ * <b>menu : ""|null,</b>\r
+ * | The menu group to which the command will be added. If null, will not appear in the menus.\r
+ * \r
+ * <b>menuPosition : "first"|"last"</b>\r
+ * | Optional : force the menu group to be first or last in the menubar.\r
+ * \r
+ * <b>toolbar : ""|null,</b>\r
+ * | The toolbar group to which the command will be added. If null, will not appear in the toolbars.\r
+ * \r
+ * <b>init : function(){},</b>\r
+ * | Optional function called at command creation.\r
+ * | Function context : the command itself\r
+ * \r
+ * <b>callback : function(e){},</b>\r
+ * | The main callback to be triggered when command is executed.\r
+ * | Function context : the current class (not the command!)\r
+ * \r
+ * <b>selectionChange : function(viewPaneId, xmlNodes){},</b>\r
+ * | Optional function called each time a selectionChange is detected in one of the active viewPane.\r
+ * | The origin viewPaneId and the new selection as a map of nodes are passed as arguments.\r
+ * | Function context : the command itself.\r
+ * \r
+ * <b>submenu : [{label:"", icon:"", commandId:""}, ...],</b>\r
+ * | If set, the command will create a submenu, being in a menu or in the toolbar.\r
+ * | The submenu is created with the various array entries, and the submenuCallback function\r
+ * | will be called with the 'commandId' parameter when a submenu entry is selected.\r
+ * \r
+ * <b>submenuCallback : function(commandId){},</b>\r
+ * | Callback if command is a submenu (cf. above).\r
+ * | Function context : the current class/\r
+ * \r
+ * <b>command : null</b>\r
+ * | For internal use only, caching the actual org.argeo.ria.event.Command object.\r
+ * }\r
+ * </pre>\r
+ * @see org.argeo.ria.event.Command for the definition Map details. \r
+ */\r
+ definitions : {\r
+ init : {},\r
+ check : "Map"\r
+ },\r
+ /**\r
+ * For internal use \r
+ */\r
+ initialDefinitions : {\r
+ init : {},\r
+ check : "Map"\r
+ },\r
+ /**\r
+ * Special command definitions that are shared between focusable parts. \r
+ */\r
+ sharedDefinitions : {\r
+ init: {},\r
+ check: "Map"\r
+ }\r
+ },\r
+\r
+ events : {\r
+ /**\r
+ * Triggered when the whole commands list is changed. Mainly used internally by the manager.\r
+ */\r
+ "changedCommands" : "qx.event.type.Event"\r
+ },\r
+ \r
+ /*\r
+ *****************************************************************************\r
+ MEMBERS\r
+ *****************************************************************************\r
+ */\r
+\r
+ members :\r
+ {\r
+ /**\r
+ * Initialize the manager with basic definitions.\r
+ * @param initDefinitions {Map} A map of commands definitions.\r
+ */\r
+ init : function(initDefinitions){\r
+ this.setDefinitions(initDefinitions);\r
+ this.setInitialDefinitions(qx.lang.Object.copy(initDefinitions));\r
+ },\r
+ \r
+ /**\r
+ * Creates all the objects (if they are not already existing) from the definitions maps.\r
+ */\r
+ createCommands : function(){\r
+ this.menus = {};\r
+ this.toolbars = {};\r
+ var defs = this.getDefinitions();\r
+ var shared = this.getSharedDefinitions();\r
+ for(var key in defs){\r
+ var definition = defs[key];\r
+ var command;\r
+ if(!definition.command){\r
+ command = new org.argeo.ria.event.Command(key, definition.label, definition.icon, definition.shortcut);\r
+ if(definition.submenu){\r
+ command.setMenu(definition.submenu);\r
+ if(definition.submenuCallback){\r
+ command.setMenuCallback(definition.submenuCallback);\r
+ command.setMenuContext((definition.callbackContext?definition.callbackContext:null));\r
+ }\r
+ }\r
+ command.setEnabled(definition.enabled);\r
+ if(definition.toggle){\r
+ command.setToggle(true);\r
+ if(definition.toggleInitialState){\r
+ command.setToggleInitialState(definition.toggleInitialState);\r
+ }\r
+ }\r
+ this._attachListener(command, definition.callback, definition.callbackContext);\r
+ if(definition.init){\r
+ var binded = qx.lang.Function.bind(definition.init, command);\r
+ binded();\r
+ }\r
+ definition.command = command;\r
+ }else{\r
+ command = definition.command;\r
+ if(shared[key]){\r
+ \r
+ for(var focusPartId in shared[key]){\r
+ var sharedCommand = shared[key][focusPartId];\r
+ if(sharedCommand.callback){\r
+ var split = sharedCommand.callbackContext.split(":");\r
+ var focusPart = split[0];\r
+ var viewId = split[1]; \r
+ command.registerCallback(sharedCommand.callback, split[1]); \r
+ //this._attachListener(command, sharedCommand.callback, sharedCommand.callbackContext);\r
+ } \r
+ }\r
+ \r
+ }\r
+ }\r
+ if(definition.menu){\r
+ if(!this.menus[definition.menu]) this.menus[definition.menu] = [];\r
+ this.menus[definition.menu].push(definition);\r
+ }\r
+ if(definition.toolbar){\r
+ if(!this.toolbars[definition.toolbar]) this.toolbars[definition.toolbar] = [];\r
+ this.toolbars[definition.toolbar].push(command);\r
+ }\r
+ }\r
+ this.setDefinitions(defs);\r
+ },\r
+ \r
+ /**\r
+ * Refresh the current commands status depending on the viewSelection.\r
+ * @param viewSelection {org.argeo.ria.components.ViewSelection} The current ViewSelection\r
+ */\r
+ refreshCommands : function(viewSelection){\r
+ var defs = this.getDefinitions();\r
+ var shared = this.getSharedDefinitions();\r
+ var xmlNodes = null;\r
+ if(viewSelection.getCount() > 0){\r
+ var xmlNodes = viewSelection.getNodes();\r
+ }\r
+ for(var key in defs){\r
+ var definition = defs[key];\r
+ if(!definition.selectionChange) continue;\r
+ if(shared[key]){\r
+ var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
+ //this.debug(currentFocus);\r
+ if(!currentFocus) continue;\r
+ var sharedComm = shared[key][currentFocus.getViewId()];\r
+ if(sharedComm && sharedComm.selectionChange){\r
+ var binded = qx.lang.Function.bind(sharedComm.selectionChange, definition.command);\r
+ binded(viewSelection.getViewId(), xmlNodes);\r
+ }\r
+ }\r
+ var binded = qx.lang.Function.bind(definition.selectionChange, definition.command);\r
+ binded(viewSelection.getViewId(), xmlNodes);\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Record a menubar for the application\r
+ * @param menuBar {qx.ui.menubar.MenuBar} The application menubar\r
+ */\r
+ registerMenuBar : function(menuBar){\r
+ this.addListener("changedCommands", function(){\r
+ this.createMenuButtons(menuBar);\r
+ }, this);\r
+ this.createMenuButtons(menuBar);\r
+ },\r
+\r
+ /**\r
+ * Record a toolbar for the application\r
+ * @param toolBar {qx.ui.toolbar.ToolBar} The application toolbar\r
+ */\r
+ registerToolBar : function(toolBar){\r
+ this.addListener("changedCommands", function(){\r
+ this.createToolbarParts(toolBar);\r
+ }, this);\r
+ this.createToolbarParts(toolBar);\r
+ }, \r
+ \r
+ /**\r
+ * Creates the real buttons and add them to the passed menuBar. \r
+ * @param menuBar {qx.ui.menubar.MenuBar} The application menubar\r
+ */\r
+ createMenuButtons : function(menuBar){\r
+ menuBar.removeAll();\r
+ var anchors = {};\r
+ for(var key in this.menus){\r
+ var menu = new qx.ui.menu.Menu();\r
+ var button = new qx.ui.menubar.Button(key, null, menu);\r
+ var anchorDetected = false;\r
+ for(var i=0; i<this.menus[key].length;i++){\r
+ var def = this.menus[key][i]; \r
+ menu.add(def.command.getMenuButton());\r
+ if(!anchorDetected && def.menuPosition){\r
+ anchorDetected = true;\r
+ anchors[def.menuPosition] = button;\r
+ }\r
+ }\r
+ if(!anchorDetected){\r
+ menuBar.add(button);\r
+ }\r
+ }\r
+ // Add specific anchored buttons\r
+ if(anchors.first) menuBar.addAt(anchors.first, 0);\r
+ else if(anchors.last){\r
+ menuBar.add(anchors.last);\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Creates the real buttons and add them to the passed toolbar. \r
+ * @param toolbar {qx.ui.toolbar.ToolBar} The application toolbar\r
+ */\r
+ createToolbarParts : function(toolbar){\r
+ toolbar.removeAll();\r
+ for(var key in this.toolbars){\r
+ var tPart = new qx.ui.toolbar.Part();\r
+ toolbar.add(tPart);\r
+ this.toolbars[key].map(function(command){\r
+ tPart.add(command.getToolbarButton());\r
+ });\r
+ }\r
+ },\r
+ /**\r
+ * Creates a context menu from an array of commands ids.\r
+ * @param commandIdsArray {Array} An array of string\r
+ * @return {qx.ui.menu.Menu}\r
+ */\r
+ createMenuFromIds : function(commandIdsArray){\r
+ var defs = this.getDefinitions();\r
+ var contextMenu = new qx.ui.menu.Menu();\r
+ for(var i=0;i<commandIdsArray.length;i++){\r
+ var definition = defs[commandIdsArray[i]];\r
+ if(definition){\r
+ var command = definition.command;\r
+ contextMenu.add(command.getMenuButton());\r
+ }\r
+ }\r
+ return contextMenu;\r
+ },\r
+ /**\r
+ * Add a new set of commands definitions. See the definitions property of this class.\r
+ * @param definitions {Map} a set of commands definitions.\r
+ * @param callbackContext {qx.ui.core.Object} The context used inside the commands callbacks.\r
+ * @param focusablePartId {String} A string identifying the associated focusable part, like "view:viewId". \r
+ */\r
+ addCommands : function(definitions, callbackContext, focusablePartId){\r
+ var crtDefs = this.getDefinitions(); \r
+ for(var key in definitions){\r
+ if(callbackContext) definitions[key]['callbackContext'] = callbackContext;\r
+ if(crtDefs[key] && definitions[key]['shared']){\r
+ if(focusablePartId) {\r
+ definitions[key]['focusablePartId'] = focusablePartId;\r
+ if(!this.getSharedDefinitions()[key]){\r
+ this.getSharedDefinitions()[key] = {};\r
+ }\r
+ this.getSharedDefinitions()[key][focusablePartId] = definitions[key];\r
+ }\r
+ \r
+ }else{\r
+ crtDefs[key] = definitions[key];\r
+ }\r
+ }\r
+ this.setDefinitions(crtDefs);\r
+ this.fireEvent("changedCommands");\r
+ },\r
+ /**\r
+ * Removes a whole set of commands by their definitions maps.\r
+ * @param definitions {Map} a set of commands definitions\r
+ * @param focusablePartId {String} A string identifying the associated focusable part, like "view:viewId". \r
+ */\r
+ removeCommands : function(definitions, focusablePartId){\r
+ var crtDefs = this.getDefinitions();\r
+ var initDefs = this.getInitialDefinitions();\r
+ var sharedDefs = this.getSharedDefinitions();\r
+ for(var key in definitions){\r
+ if(!crtDefs[key]) continue;\r
+ if(initDefs[key]){\r
+ crtDefs[key] = initDefs[key];\r
+ }else{\r
+ if(sharedDefs[key] && sharedDefs[key][focusablePartId]){\r
+ crtDefs[key].command.removeCallback(focusablePartId);\r
+ delete sharedDefs[key][focusablePartId];\r
+ }else{\r
+ delete crtDefs[key];\r
+ }\r
+ }\r
+ }\r
+ this.setDefinitions(crtDefs);\r
+ this.fireEvent("changedCommands");\r
+ },\r
+ /**\r
+ * Executes a command by its id.\r
+ * @param commandId {String} The command id.\r
+ */\r
+ executeCommand : function(commandId){\r
+ var defs = this.getDefinitions();\r
+ if(defs[commandId] && defs[commandId].command.getEnabled()){\r
+ defs[commandId].command.execute();\r
+ }\r
+ },\r
+ /**\r
+ * Retrieves a command by its id.\r
+ * @param commandId {String} The command id.\r
+ */\r
+ getCommandById : function(commandId){\r
+ var defs = this.getDefinitions();\r
+ if(defs[commandId] && defs[commandId].command){\r
+ return defs[commandId].command;\r
+ } \r
+ },\r
+ /**\r
+ * Add a standard context menu to a toolbar for button look and feel (show icon, text, both).\r
+ * @param toolbar {qx.ui.toolbar.ToolBar} The toolbar\r
+ */\r
+ addToolbarContextMenu : function(toolbar){\r
+ var menu = new qx.ui.menu.Menu();\r
+ var icon = new qx.ui.menu.RadioButton("Show Icons");\r
+ icon.setValue("icon");\r
+ var text = new qx.ui.menu.RadioButton("Show Text");\r
+ text.setValue("label");\r
+ var both = new qx.ui.menu.RadioButton("Show Both");\r
+ both.setValue("both");\r
+ var mgr = new qx.ui.form.RadioGroup(icon, text, both);\r
+ menu.add(icon);\r
+ menu.add(text);\r
+ menu.add(both);\r
+ mgr.setSelected(both);\r
+ toolbar.setContextMenu(menu); \r
+ mgr.addListener("changeValue", function(e){\r
+ this.setShow(e.getData());\r
+ }, toolbar);\r
+ \r
+ },\r
+ /**\r
+ * Attach a listener to a command, with a context.\r
+ * The context can be an object, a string like "view:viewId" or null. \r
+ * If a string, the viewPaneId content will be retrieved at runtime. If null, "this" will be used\r
+ * as default context.\r
+ * @param command {org.argeo.ria.event.Command} The command\r
+ * @param callback {Function} The function to execute\r
+ * @param callbackContext {Object|String} The context in which the function will be executed. \r
+ */\r
+ _attachListener:function(command, callback, callbackContext){ \r
+ if(!callbackContext){\r
+ command.addListener("execute", callback, this);\r
+ return;\r
+ }\r
+ if(typeof(callbackContext) == "object"){\r
+ command.addListener("execute", callback, callbackContext);\r
+ return;\r
+ } \r
+ if(typeof(callbackContext) == "string"){\r
+ \r
+ var split = callbackContext.split(":");\r
+ var focusPart = split[0];\r
+ var viewId = split[1];\r
+ if(command.getCallbacks()[viewId]) return;\r
+ command.registerCallback(callback, split[1]);\r
+ command.addListener("execute", function(event){\r
+ var target = event.getTarget();\r
+ var callbacks = target.getCallbacks();\r
+ if(qx.lang.Object.getLength(callbacks) == 0) return;\r
+ var view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
+ if(qx.lang.Object.getLength(callbacks) == 1){\r
+ var binded = qx.lang.Function.bind(callbacks[qx.lang.Object.getKeys(callbacks)[0]], view);\r
+ binded(event);\r
+ return;\r
+ }\r
+ var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
+ if(currentFocus && currentFocus.getViewId() && callbacks[currentFocus.getViewId()]){\r
+ var currentViewId = currentFocus.getViewId();\r
+ view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(currentViewId).getContent();\r
+ var binded = qx.lang.Function.bind(callbacks[currentFocus.getViewId()], view);\r
+ binded(event);\r
+ return;\r
+ }\r
+ });\r
+ \r
+ \r
+ /*\r
+ if(callbackContext.split(":")[0] == "view"){\r
+ var viewId = callbackContext.split(":")[1];\r
+ command.addListener("execute", function(event){\r
+ if(event.getTarget().getCheckFocusAtCallback()){\r
+ var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
+ if(currentFocus.getViewId() != viewId) return;\r
+ }\r
+ var view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
+ var binded = qx.lang.Function.bind(callback, view);\r
+ binded(event);\r
+ });\r
+ }else{\r
+ command.addListener("execute", callback, callbackContext);\r
+ }\r
+ */\r
+ }\r
+ }\r
+ }\r
+});\r
--- /dev/null
+/**\r
+ * This can be triggered at the end of a IO Request. In that case, it contains\r
+ * a data type as an identifier, and the request response itself.\r
+ * Can be used this way to listen for data changes from various parts of the application.\r
+ */\r
+qx.Class.define("org.argeo.ria.event.ReloadEvent",\r
+{\r
+ extend : qx.event.type.Event,\r
+ \r
+ construct : function(){\r
+ this.base(arguments);\r
+ },\r
+ members : {\r
+ /**\r
+ * Basic initialisation of event\r
+ * @param dataType {String} a unique data identifier\r
+ * @param content {mixed} the retrieved data\r
+ */\r
+ init: function(dataType, content){\r
+ this.setDataType(dataType);\r
+ this.setContent(content); \r
+ }\r
+ },\r
+ properties :{\r
+ /**\r
+ * A unique data identifier \r
+ */\r
+ dataType : {init:null, check:"String"},\r
+ /**\r
+ * The new data content \r
+ */\r
+ content : {init:null}\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * Event utilities, this package is the "controller" part of the application (in an classic MVC view).\r
+ * The commandsManager singleton is in charge of wiring all the commands inside the application.\r
+ *\r
+ */
\ No newline at end of file
--- /dev/null
+/**\r
+ * A standard client for sending/receiving JMS message.\r
+ * It is based on ActiveMQ Ajax implementation.\r
+ */\r
+qx.Class.define("org.argeo.ria.remote.JmsClient", {\r
+\r
+ type : "singleton",\r
+ extend : qx.core.Object,\r
+ \r
+ construct : function(){\r
+ this.base(arguments);\r
+ },\r
+ members : {\r
+ // The URI of the MessageListenerServlet\r
+ uri : '../amq', \r
+\r
+ // Polling. Set to true (default) if waiting poll for messages is needed\r
+ poll : true,\r
+ pollTimeout : 25,\r
+ interrupt : false,\r
+\r
+ // Poll delay. if set to positive integer, this is the time to wait in ms before\r
+ // sending the next poll after the last completes.\r
+ _pollDelay : 0,\r
+\r
+ _first : true,\r
+ /**\r
+ * Trigger at each poll event.\r
+ * @param first {Boolean} Whether it is the first event to be triggered. \r
+ */\r
+ _pollEvent : function(first) {},\r
+ _handlers : new Array(),\r
+\r
+ /**\r
+ * Parses the XML response to a message POST.\r
+ * @param response {qx.io.remote.Response} The query response\r
+ */\r
+ _messageHandler : function(response) {\r
+ var doc = response.getContent(); \r
+ var messages = org.argeo.ria.util.Element.selectNodes(doc, "//response");\r
+ for(var i=0;i<messages.length;i++){\r
+ var id = messages[i].getAttribute("id");\r
+ if(id && this._handlers[id]){\r
+ this._handlers[id](messages[i]);\r
+ }\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Parses the empty response of a poll GET query.\r
+ * @param response {qx.io.remote.Response} The query response\r
+ */\r
+ _pollHandler : function(response) {\r
+ try {\r
+ this._messageHandler(response);\r
+ this._pollEvent(this._first);\r
+ this._first = false;\r
+ } catch (e) {\r
+ alert(e);\r
+ }\r
+\r
+ if (this._pollDelay > 0)\r
+ qx.event.Timer.once(this._sendPoll, this, this._pollDelay);\r
+ else\r
+ this._sendPoll();\r
+ },\r
+\r
+ /**\r
+ * Send a poll query : GET query and no paramter at all. \r
+ * @param request {qx.io.remote.Request} A request object\r
+ */\r
+ _sendPoll : function(request) {\r
+ if(this.interrupt) return;\r
+ var request = new qx.io.remote.Request(this.uri, "GET", "application/xml");\r
+ request.setTimeout(this.pollTimeout*1000+5000);\r
+ request.addListener("completed", this._pollHandler, this);\r
+ request.send();\r
+ },\r
+\r
+ /**\r
+ * Add a function that gets called on every poll response, after all received\r
+ * messages have been handled. The poll handler is past a boolean that indicates\r
+ * if this is the first poll for the page.\r
+ * \r
+ * @param func {Function} The handler to be called. \r
+ */\r
+ addPollHandler : function(func) {\r
+ var old = this._pollEvent;\r
+ this._pollEvent = function(first) {\r
+ old(first);\r
+ func(first);\r
+ }\r
+ },\r
+\r
+ /**\r
+ * Send a JMS message to a destination (eg topic://MY.TOPIC). \r
+ * Message should be xml or encoded xml content.\r
+ * \r
+ * @param destination {String} The topic destination\r
+ * @param message {String} XML encoded message\r
+ * @param properties {Map} A map of additional parameters to add to the query.\r
+ */\r
+ sendMessage : function(destination, message, properties) {\r
+ this._sendMessage(destination, message, 'send', properties);\r
+ },\r
+\r
+ /**\r
+ * Listen on a channel or topic. handler must be a function taking a message arguement\r
+ * @param id {String} A unique identifier for this handler\r
+ * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
+ * @param handler {Function} The handler to trigger when receiving a message \r
+ * @param context {Object} An object to bind on the handler.\r
+ */\r
+ addListener : function(id, destination, handler, context) {\r
+ this._handlers[id] = qx.lang.Function.bind(handler, context);\r
+ this._sendMessage(destination, id, 'listen');\r
+ },\r
+\r
+ /**\r
+ * Remove Listener from channel or topic.\r
+ * @param id {String} identifier of the handler to remove.\r
+ * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
+ */ \r
+ removeListener : function(id, destination) {\r
+ this._handlers[id] = null;\r
+ this._sendMessage(destination, id, 'unlisten');\r
+ },\r
+ \r
+ /**\r
+ * Send a message of a given type.\r
+ * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
+ * @param message {String} XML encoded message\r
+ * @param type {String} The JMS-Type of message (listen, unlisten, send).\r
+ * @param properties {Map} A map of additional parameters to add to the query.\r
+ */\r
+ _sendMessage : function(destination, message, type, properties) {\r
+ var req = new qx.io.remote.Request(this.uri, "POST", "text/plain");\r
+ if(!properties) properties = {}; \r
+ properties["destination"] = destination;\r
+ properties["message"] = message;\r
+ properties["type"] = type;\r
+ var vParametersList = [];\r
+ \r
+ for (var vId in properties)\r
+ {\r
+ var value = properties[vId];\r
+ if (value instanceof Array)\r
+ {\r
+ for (var i=0; i<value.length; i++)\r
+ {\r
+ vParametersList.push(encodeURIComponent(vId) +\r
+ "=" +\r
+ encodeURIComponent(value[i]));\r
+ }\r
+ }\r
+ else\r
+ {\r
+ vParametersList.push(encodeURIComponent(vId) +\r
+ "=" +\r
+ encodeURIComponent(value));\r
+ }\r
+ } \r
+ if (vParametersList.length > 0)\r
+ {\r
+ req.setData(vParametersList.join("&"));\r
+ }\r
+ \r
+ //req.addListener("completed", this.endBatch, this);\r
+ req.send();\r
+ },\r
+\r
+ /**\r
+ * Starts a poll on the JMS server.\r
+ */\r
+ startPolling : function() {\r
+ if (this.poll){\r
+ this.interrupt = false;\r
+ var req = new qx.io.remote.Request(this.uri, "GET", "application/xml");\r
+ req.setParameter("timeout", "10");\r
+ req.addListener("completed", this._pollHandler, this);\r
+ req.send();\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Stops polling the JMS server.\r
+ */\r
+ stopPolling : function(){\r
+ this.interrupt = true;\r
+ }\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * A management class for all request sent to the server\r
+ * Basically, to access the server, always get a new Request object from this class.\r
+ * It will then trigger various user-interface events during the Request lifecycle. \r
+ * \r
+ * For the moment, it's about the "Stop" button command, handling any passed ILoadStatusable states, \r
+ * and logging the Request status/errors.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.remote.RequestManager",\r
+{\r
+ type : "singleton",\r
+ extend : qx.core.Object,\r
+ \r
+ events : {\r
+ /**\r
+ * Triggered on the user demand at the end of the Request \r
+ */\r
+ "reload" : "org.argeo.ria.event.ReloadEvent"\r
+ },\r
+ \r
+ construct : function(){\r
+ this.base(arguments); \r
+ },\r
+ \r
+ members : {\r
+ /**\r
+ * Sets the unique "stop" command of the application.\r
+ * @param stopCommand {org.argeo.ria.event.Command} The command\r
+ */\r
+ setStopCommand : function(stopCommand){\r
+ this.command = stopCommand;\r
+ },\r
+ \r
+ /**\r
+ * Creates a Request and handle various parts of its lifecycle.\r
+ * @see org.argeo.ria.event.ReloadEvent\r
+ * @see org.argeo.ria.components.ILoadStatusable\r
+ * \r
+ * @param url {String} The server url\r
+ * @param method {String} Connexion method (POST, GET, etc.)\r
+ * @param responseType {String} Expected response mime type (application/xml, etc...).\r
+ * @param fireReloadEventType {String} On user-demand, if this parameter is not null, a org.argeo.ria.event.ReloadEvent will be triggered when the request is completed. \r
+ * @param iLoadStatusables {Array} An array of ILoadStatusable implementations that need to be updated by the Request state (loading/ended).\r
+ * @return {qx.io.remote.Request}\r
+ */\r
+ getRequest : function(url, method, responseType, fireReloadEventType, iLoadStatusables){\r
+ var request = new qx.io.remote.Request(url, method, responseType);\r
+ if(iLoadStatusables){\r
+ request.setUserData("iLoadStatusables", iLoadStatusables);\r
+ }\r
+ if(fireReloadEventType){\r
+ request.addListener("completed", function(response){\r
+ this.fireReloadEvent(fireReloadEventType, response.getContent());\r
+ }, this);\r
+ }\r
+ this.enableCommand(request);\r
+ request.addListener("timeout", this.requestTerminated, this);\r
+ request.addListener("failed", this.requestTerminated, this);\r
+ request.addListener("aborted", this.requestTerminated, this);\r
+ request.addListener("completed", this.requestCompleted, this); \r
+ return request;\r
+ }, \r
+ \r
+ /**\r
+ * Creates a ReloadEvent and fire it.\r
+ * @param dataType {String} The data type \r
+ * @param content {mixed} The content of the request response.\r
+ */\r
+ fireReloadEvent : function(dataType, content){\r
+ this.fireEvent("reload", org.argeo.ria.event.ReloadEvent, [dataType, content]); \r
+ },\r
+ \r
+ /**\r
+ * Triggered when request is created\r
+ * @param e {qx.event.type.Event} The event\r
+ */\r
+ requestCreated : function(e){\r
+ var request = e.getTarget();\r
+ this.enableCommand(request);\r
+ },\r
+ \r
+ /**\r
+ * Triggered when request is completed normally\r
+ * @param e {qx.event.type.Event} The event\r
+ */\r
+ requestCompleted : function(e){\r
+ var request = e.getTarget();\r
+ this.disableCommand(request);\r
+ },\r
+ \r
+ /**\r
+ * Triggered when request is completed abnormally\r
+ * @param e {qx.event.type.Event} The event\r
+ */\r
+ requestTerminated : function(e){\r
+ var request = e.getTarget();\r
+ var errorType = e.getType();\r
+ this.disableCommand(request);\r
+ var message = "";\r
+ if(errorType == "aborted"){\r
+ message = "Request aborted by user";\r
+ }else if(errorType == "failed"){\r
+ message = "Request failed!";\r
+ }else if(errorType == "timeout"){\r
+ message = "Request timed out!";\r
+ }\r
+ this.error(message);\r
+ },\r
+ \r
+ /**\r
+ * Triggered by a request creation. Update the GUI parts according to its status. \r
+ * @param request {qx.io.remote.Request} The current Request \r
+ */\r
+ disableCommand : function(request){\r
+ this.command.setEnabled(false);\r
+ if(request.getUserData("iLoadStatusables")){\r
+ this.updateGuiParts(request.getUserData("iLoadStatusables"), false);\r
+ }\r
+ var listener = request.getUserData("listener");\r
+ if(listener){\r
+ this.command.removeListener("execute", listener);\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Triggered by a request ending. Update the GUI parts according to its status. \r
+ * @param request {qx.io.remote.Request} The current Request \r
+ */\r
+ enableCommand : function(request){\r
+ this.command.setEnabled(true);\r
+ if(request.getUserData("iLoadStatusables")){\r
+ this.updateGuiParts(request.getUserData("iLoadStatusables"), true);\r
+ }\r
+ qx.ui.core.queue.Manager.flush();\r
+ var listener = request.abort;\r
+ request.setUserData("listener", listener);\r
+ this.command.addListener("execute", listener, request);\r
+ },\r
+ \r
+ /**\r
+ * Update the ILoadStatusable implementations\r
+ * @param iLoadStatusables {Array} An array of ILoadStatusable \r
+ * @param loadStatus {Boolean} The current status of a request \r
+ */\r
+ updateGuiParts : function(iLoadStatusables, loadStatus){\r
+ for(var i=0;i<iLoadStatusables.length;i++){\r
+ if(qx.Class.implementsInterface(qx.Class.getByName(iLoadStatusables[i].classname), org.argeo.ria.components.ILoadStatusable)){\r
+ iLoadStatusables[i].setOnLoad(loadStatus);\r
+ }else{\r
+ this.debug("Does not implement the ILoadStatusable interface! GUIPART type : "+ iLoadStatusables[i].classname);\r
+ }\r
+ }\r
+ }\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * Communication with server package. \r
+ *\r
+ */
\ No newline at end of file
--- /dev/null
+/* ************************************************************************\r
+\r
+ Copyright:\r
+\r
+ License:\r
+\r
+ Authors:\r
+\r
+************************************************************************ */\r
+\r
+/**\r
+ * This class demonstrates how to define unit tests for your application.\r
+ *\r
+ * Execute <code>generate.py test</code> to generate a testrunner application \r
+ * and open it from <tt>test/index.html</tt>\r
+ *\r
+ * The methods that contain the tests are instance methods with a \r
+ * <code>test</code> prefix. You can create an arbitrary number of test \r
+ * classes like this one. They can be organized in a regular class hierarchy, \r
+ * i.e. using deeper namespaces and a corresponding file structure within the \r
+ * <tt>test</tt> folder.\r
+ */\r
+qx.Class.define("org.argeo.ria.test.DemoTest",\r
+{\r
+ extend : qx.dev.unit.TestCase,\r
+\r
+ members :\r
+ {\r
+ /*\r
+ ---------------------------------------------------------------------------\r
+ TESTS\r
+ ---------------------------------------------------------------------------\r
+ */\r
+ \r
+ /**\r
+ * Here are some simple tests\r
+ */\r
+ testSimple : function()\r
+ {\r
+ this.assertEquals(4, 3+1, "This should never fail!");\r
+ this.assertFalse(false, "Can false be true?!");\r
+ },\r
+\r
+ /**\r
+ * Here are some more advanced tests\r
+ */\r
+ testAdvanced: function () \r
+ {\r
+ var a = 3;\r
+ var b = a;\r
+ this.assertIdentical(a, b, "A rose by any other name is still a rose");\r
+ this.assertInRange(3, 1, 10, "You must be kidding, 3 can never be outside [1,10]!");\r
+ }\r
+ }\r
+});\r
--- /dev/null
+/**\r
+ * Unit tests to be executed by the TestRunner application.\r
+ *\r
+ */
\ No newline at end of file
--- /dev/null
+/**\r
+ * Cross browser XML Element API\r
+ * \r
+ * Overrides the Qooxdoo qx.xml.Element to handle the namespace prefixes\r
+ *\r
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/81f3de54-3b79-46dc-8e01-73ca2d94cdb5.asp\r
+ * http://developer.mozilla.org/en/docs/Parsing_and_serializing_XML\r
+ */\r
+qx.Class.define("org.argeo.ria.util.Element",\r
+{\r
+ \r
+ statics :\r
+ {\r
+ \r
+ DEFAULT_NAMESPACE_MAP : null,\r
+ \r
+ /**\r
+ * Selects the first XmlNode that matches the XPath expression.\r
+ *\r
+ * @param element {Element | Document} root element for the search\r
+ * @param query {String} XPath query\r
+ * @param NSMap (Object) A map matching namespace prefixes to namespace URIS;\r
+ * @return {Element} first matching element\r
+ * @signature function(element, query, NSMap)\r
+ */\r
+ selectSingleNode : qx.core.Variant.select("qx.client",\r
+ {\r
+ "mshtml|opera": function(element, query, NSMap) {\r
+ NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+ if(NSMap){ \r
+ var namespaces = [];\r
+ var i=0;\r
+ for(var prefix in NSMap){ \r
+ namespaces[i] = 'xmlns:'+prefix+'="'+NSMap[prefix]+'"';\r
+ i++;\r
+ }\r
+ var doc = element.ownerDocument || element;\r
+ doc.setProperty('SelectionNamespaces', namespaces.join(" "));\r
+ }\r
+ try{\r
+ return element.selectSingleNode(query);\r
+ }catch(err){}\r
+ },\r
+\r
+ "default": function(element, query, NSMap)\r
+ {\r
+ NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+ if(!this.__xpe) {\r
+ this.__xpe = new XPathEvaluator();\r
+ }\r
+\r
+ var xpe = this.__xpe;\r
+\r
+ try {\r
+ var resolver;\r
+ if(NSMap){\r
+ resolver = function(prefix){\r
+ return NSMap[prefix] || null;\r
+ }\r
+ }else{\r
+ resolver = xpe.createNSResolver(element);\r
+ }\r
+ //return xpe.evaluate(query, element, xpe.createNSResolver(element), XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\r
+ return xpe.evaluate(query, element, resolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\r
+ } catch(err) {\r
+ throw new Error("selectSingleNode: query: " + query + ", element: " + element + ", error: " + err);\r
+ }\r
+ }\r
+ }),\r
+\r
+\r
+ /**\r
+ * Selects a list of nodes matching the XPath expression.\r
+ *\r
+ * @param element {Element | Document} root element for the search\r
+ * @param query {String} XPath query\r
+ * @param NSMap {Map} Mapping between namespaces prefixes and URI.\r
+ * @return {Element[]} List of matching elements\r
+ * @signature function(element, query, NSMap)\r
+ */\r
+ selectNodes : qx.core.Variant.select("qx.client",\r
+ {\r
+ "mshtml|opera": function(element, query, NSMap) {\r
+ NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+ if(NSMap){\r
+ var namespaces = [];\r
+ var i=0;\r
+ for(var prefix in NSMap){ \r
+ namespaces[i] = 'xmlns:'+prefix+'="'+NSMap[prefix]+'"';\r
+ i++;\r
+ }\r
+ var doc = element.ownerDocument || element;\r
+ doc.setProperty('SelectionNamespaces', namespaces.join(" "));\r
+ } \r
+ return element.selectNodes(query);\r
+ },\r
+\r
+ "default": function(element, query, NSMap)\r
+ {\r
+ NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+ var xpe = this.__xpe;\r
+\r
+ if(!xpe) {\r
+ this.__xpe = xpe = new XPathEvaluator();\r
+ }\r
+\r
+ try {\r
+ var resolver;\r
+ if(NSMap){\r
+ resolver = function(prefix){\r
+ return NSMap[prefix] || null;\r
+ }\r
+ }else{\r
+ resolver = xpe.createNSResolver(element);\r
+ } \r
+ //var result = xpe.evaluate(query, element, xpe.createNSResolver(element), XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\r
+ var result = xpe.evaluate(query, element, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\r
+ } catch(err) {\r
+ throw new Error("selectNodes: query: " + query + ", element: " + element + ", error: " + err);\r
+ }\r
+\r
+ var nodes = [];\r
+ for (var i=0; i<result.snapshotLength; i++) {\r
+ nodes[i] = result.snapshotItem(i);\r
+ }\r
+\r
+ return nodes;\r
+ }\r
+ }),\r
+\r
+\r
+ /**\r
+ * Returns a list of elements with the given tag name belonging to the given namespace (http://developer.mozilla.org/en/docs/DOM:element.getElementsByTagNameNS).\r
+ *\r
+ * @param element {Element | Document} the element from where the search should start.\r
+ * Note that only the descendants of this element are included in the search, not the node itself.\r
+ * @param namespaceURI {var} is the namespace URI of elements to look for . For example, if you need to look\r
+ * for XHTML elements, use the XHTML namespace URI, <tt>http://www.w3.org/1999/xhtml</tt>.\r
+ * @param tagname {String} the tagname to look for\r
+ * @return {Element[]} a list of found elements in the order they appear in the tree.\r
+ * @signature function(element, namespaceURI, tagname)\r
+ */\r
+ getElementsByTagNameNS : qx.core.Variant.select("qx.client",\r
+ { \r
+ "mshtml": function(element, namespaceURI, tagname)\r
+ {\r
+ var doc = element.ownerDocument || element;\r
+\r
+ doc.setProperty("SelectionLanguage", "XPath");\r
+ doc.setProperty("SelectionNamespaces", "xmlns:ns='" + namespaceURI + "'");\r
+\r
+ return qx.xml.Element.selectNodes(element, 'descendant-or-self::ns:' + tagname);\r
+ },\r
+\r
+ "default": function(element, namespaceURI, tagname) {\r
+ return element.getElementsByTagNameNS(namespaceURI, tagname);\r
+ }\r
+ }),\r
+\r
+\r
+ /**\r
+ * Selects the first XmlNode that matches the XPath expression and returns the text content of the element\r
+ *\r
+ * @param element {Element|Document} root element for the search\r
+ * @param query {String} XPath query\r
+ * @param NSMap {Object} Mapping between NS prefix / uri\r
+ * @return {String} the joined text content of the found element or null if not appropriate.\r
+ * @signature function(element, query)\r
+ */\r
+ getSingleNodeText : function(element, query, NSMap)\r
+ {\r
+ NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+ var node = org.argeo.ria.util.Element.selectSingleNode(element, query, NSMap);\r
+ return qx.dom.Node.getText(node);\r
+ }\r
+ }\r
+});\r
--- /dev/null
+/* ************************************************************************\r
+\r
+ qooxdoo - the new era of web development\r
+\r
+ http://qooxdoo.org\r
+\r
+ Copyright:\r
+ 2007 Derrell Lipman\r
+\r
+ License:\r
+ LGPL: http://www.gnu.org/licenses/lgpl.html\r
+ EPL: http://www.eclipse.org/org/documents/epl-v10.php\r
+ See the LICENSE file in the project's top-level directory for details.\r
+\r
+ Authors:\r
+ * Derrell Lipman (derrell)\r
+ * David Perez Carmona (david-perez)\r
+\r
+************************************************************************ */\r
+\r
+/* ************************************************************************\r
+\r
+#require(qx.theme.Modern)\r
+#require(qx.theme.Classic)\r
+#require(qx.log.Logger)\r
+\r
+************************************************************************ */\r
+\r
+/**\r
+ * A data cell renderer for the tree column of a simple tree\r
+ */\r
+qx.Class.define("org.argeo.ria.util.TreeDataCellRenderer",\r
+{\r
+ extend : qx.ui.treevirtual.SimpleTreeDataCellRenderer,\r
+\r
+\r
+ construct : function()\r
+ {\r
+ this.base(arguments);\r
+\r
+ this.__am = qx.util.AliasManager.getInstance();\r
+ this.__rm = qx.util.ResourceManager;\r
+ this.__tm = qx.theme.manager.Appearance.getInstance();\r
+\r
+ // Base URL used for indentation\r
+ this.BLANK = this.__rm.toUri(this.__am.resolve("static/blank.gif"));\r
+ },\r
+\r
+\r
+ statics :\r
+ {\r
+ __icon : { }\r
+ },\r
+\r
+\r
+\r
+\r
+ /*\r
+ *****************************************************************************\r
+ MEMBERS\r
+ *****************************************************************************\r
+ */\r
+\r
+ members :\r
+ {\r
+ // overridden\r
+ _getCellStyle : function(cellInfo)\r
+ {\r
+ var node = cellInfo.value;\r
+\r
+ // Return the style for the div for the cell. If there's cell-specific\r
+ // style information provided, append it.\r
+ var html =\r
+ this.base(arguments, cellInfo) +\r
+ (node.cellStyle ? node.cellStyle + ";" : "");\r
+ return html;\r
+ },\r
+\r
+ // overridden\r
+ _getContentHtml : function(cellInfo)\r
+ {\r
+ var html = "";\r
+\r
+ // Horizontal position\r
+ var pos = 0;\r
+\r
+ // If needed, add extra content before indentation\r
+ var extra = this._addExtraContentBeforeIndentation(cellInfo, pos);\r
+ html += extra.html;\r
+ pos = extra.pos;\r
+\r
+ // Add the indentation (optionally with tree lines)\r
+ var indentation = this._addIndentation(cellInfo, pos);\r
+ html += indentation.html\r
+ pos = indentation.pos;\r
+\r
+ // If needed, add extra content before icon\r
+ extra = this._addExtraContentBeforeIcon(cellInfo, pos);\r
+ html += extra.html;\r
+ pos = extra.pos;\r
+\r
+ // Add the node icon\r
+ var icon = this._addIcon(cellInfo, pos);\r
+ html += icon.html;\r
+ pos = icon.pos;\r
+\r
+ // If needed, add extra content before label\r
+ extra = this._addExtraContentBeforeLabel(cellInfo, pos);\r
+ html += extra.html;\r
+ pos = extra.pos;\r
+\r
+ // Add the node's label\r
+ html += this._addLabel(cellInfo, pos);\r
+\r
+ return html;\r
+ },\r
+\r
+ /**\r
+ * Add an image to the tree. This might be a visible icon or it may be\r
+ * part of the indentation.\r
+ *\r
+ * @param imageInfo {Map}\r
+ * How to display the image. It optionally includes any of the\r
+ * following:\r
+ * <dl>\r
+ * <dt>position {Map}</dt>\r
+ * <dd>\r
+ * If provided, a div is created to hold the image. The div's top,\r
+ * right, bottom, left, width, and/or height may be specified with\r
+ * members of this map. Each is expected to be an integer value.\r
+ * </dd>\r
+ * <dt>imageWidth, imageHeight</dt>\r
+ * <dd>\r
+ * The image's width and height. These are used only if both are\r
+ * specified.\r
+ * </dd>\r
+ * </dl>\r
+ *\r
+ * @return {String}\r
+ * The html for this image, possibly with a surrounding div (see\r
+ * 'position', above).\r
+ */\r
+ _addImage : function(imageInfo)\r
+ {\r
+ var html = [];\r
+\r
+ // Resolve the URI\r
+ var source = this.__rm.toUri(this.__am.resolve(imageInfo.url));\r
+\r
+ // If we've been given positioning attributes, enclose image in a div\r
+ if (imageInfo.position)\r
+ {\r
+ var pos = imageInfo.position;\r
+\r
+ html.push('<div style="position:absolute;');\r
+\r
+ if (!qx.core.Variant.isSet("qx.client", "mshtml"))\r
+ {\r
+ html.push(qx.bom.element.BoxSizing.compile("content-box"));\r
+ }\r
+\r
+ if (pos.top !== undefined)\r
+ {\r
+ html.push('top:' + pos.top + 'px;');\r
+ }\r
+\r
+ if (pos.right !== undefined)\r
+ {\r
+ html.push('right:' + pos.right + 'px;');\r
+ }\r
+\r
+ if (pos.bottom !== undefined)\r
+ {\r
+ html.push('bottom:' + pos.bottom + 'px;');\r
+ }\r
+\r
+ if (pos.left !== undefined)\r
+ {\r
+ html.push('left:' + pos.left + 'px;');\r
+ }\r
+\r
+ if (pos.width !== undefined)\r
+ {\r
+ html.push('width:' + pos.width + 'px;');\r
+ }\r
+\r
+ if (pos.height !== undefined)\r
+ {\r
+ html.push('height:' + pos.height + 'px;');\r
+ }\r
+\r
+ html.push('">');\r
+ }\r
+\r
+ // Don't use an image tag. They render differently in Firefox and IE7\r
+ // even if both are enclosed in a div specified as content box. Instead,\r
+ // add the image as the background image of a div.\r
+ html.push('<div style="');\r
+ html.push('background-image:url(' + source + ');');\r
+ html.push('background-repeat:no-repeat;');\r
+\r
+ if (imageInfo.imageWidth && imageInfo.imageHeight)\r
+ {\r
+ html.push(\r
+ ';width:' +\r
+ imageInfo.imageWidth +\r
+ 'px' +\r
+ ';height:' +\r
+ imageInfo.imageHeight +\r
+ 'px');\r
+ }\r
+\r
+ var tooltip = imageInfo.tooltip;\r
+\r
+ if (tooltip != null)\r
+ {\r
+ html.push('" title="' + tooltip);\r
+ }\r
+\r
+ html.push('"> </div>');\r
+\r
+ if (imageInfo.position)\r
+ {\r
+ html.push('</div>');\r
+ }\r
+\r
+ return html.join("");\r
+ },\r
+\r
+\r
+ /**\r
+ * Add the indentation for this node of the tree.\r
+ *\r
+ * The indentation optionally includes tree lines. Whether tree lines are\r
+ * used depends on (a) the properties 'useTreeLines' and\r
+ * 'excludeFirstLevelTreelines' within this class; and (b) the widget\r
+ * theme in use (some themes don't support tree lines).\r
+ *\r
+ * @param cellInfo {Map} The information about the cell.\r
+ * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+ *\r
+ * @param pos {Integer}\r
+ * The position from the left edge of the column at which to render this\r
+ * item.\r
+ *\r
+ * @return {Map}\r
+ * The returned map contains an 'html' member which contains the html for\r
+ * the indentation, and a 'pos' member which is the starting position\r
+ * plus the width of the indentation.\r
+ */\r
+ _addIndentation : function(cellInfo, pos)\r
+ {\r
+ var node = cellInfo.value;\r
+ var imageData;\r
+ var html = "";\r
+\r
+ // Generate the indentation. Obtain icon determination values once\r
+ // rather than each time through the loop.\r
+ var bUseTreeLines = this.getUseTreeLines();\r
+ var bExcludeFirstLevelTreeLines = this.getExcludeFirstLevelTreeLines();\r
+ var bAlwaysShowOpenCloseSymbol = this.getAlwaysShowOpenCloseSymbol();\r
+\r
+ for (var i=0; i<node.level; i++)\r
+ {\r
+ imageData = this._getIndentSymbol(i, node, bUseTreeLines,\r
+ bAlwaysShowOpenCloseSymbol,\r
+ bExcludeFirstLevelTreeLines);\r
+ \r
+ html += this._addImage(\r
+ {\r
+ url : imageData.icon,\r
+ position :\r
+ {\r
+ top : 0 + (imageData.paddingTop || 5),\r
+ left : pos + (imageData.paddingLeft || 3),\r
+ width : 19,\r
+ height : 16\r
+ }\r
+ });\r
+ pos += 19;\r
+ }\r
+\r
+ return (\r
+ {\r
+ html : html,\r
+ pos : pos\r
+ });\r
+ },\r
+\r
+ /**\r
+ * Add the icon for this node of the tree.\r
+ *\r
+ * @param cellInfo {Map} The information about the cell.\r
+ * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+ *\r
+ * @param pos {Integer}\r
+ * The position from the left edge of the column at which to render this\r
+ * item.\r
+ *\r
+ * @return {Map}\r
+ * The returned map contains an 'html' member which contains the html for\r
+ * the icon, and a 'pos' member which is the starting position plus the\r
+ * width of the icon.\r
+ */\r
+ _addIcon : function(cellInfo, pos)\r
+ {\r
+ var node = cellInfo.value;\r
+\r
+ // Add the node's icon\r
+ var imageUrl = (node.bSelected ? node.iconSelected : node.icon);\r
+\r
+ if (!imageUrl)\r
+ {\r
+ if (node.type == qx.ui.treevirtual.SimpleTreeDataModel.Type.LEAF)\r
+ {\r
+ var o = this.__tm.styleFrom("treevirtual-file");\r
+ }\r
+ else\r
+ {\r
+ var states = { opened : node.bOpened };\r
+ var o = this.__tm.styleFrom("treevirtual-folder", states);\r
+ }\r
+\r
+ imageUrl = o.icon;\r
+ }\r
+\r
+ var html = this._addImage(\r
+ {\r
+ url : imageUrl,\r
+ position :\r
+ {\r
+ top : 0,\r
+ left : pos,\r
+ width : 19,\r
+ height : 16\r
+ }\r
+ });\r
+\r
+ return (\r
+ {\r
+ html : html,\r
+ pos : pos + 19\r
+ });\r
+ },\r
+\r
+ /**\r
+ * Add the label for this node of the tree.\r
+ *\r
+ * @param cellInfo {Map} The information about the cell.\r
+ * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+ * Additionally, if defined, the labelSpanStyle member is used to apply\r
+ * style to the span containing the label. (This member is for use by\r
+ * subclasses; it's not otherwise used by this class.)\r
+ *\r
+ * @param pos {Integer}\r
+ * The position from the left edge of the column at which to render this\r
+ * item.\r
+ *\r
+ * @return {String}\r
+ * The html for the label.\r
+ */\r
+ _addLabel : function(cellInfo, pos)\r
+ {\r
+ var node = cellInfo.value;\r
+\r
+ // Add the node's label. We calculate the "left" property with: each\r
+ // tree line (indentation) icon is 19 pixels wide; the folder icon is 16\r
+ // pixels wide, there are two pixels of padding at the left, and we want\r
+ // 2 pixels between the folder icon and the label\r
+ var html =\r
+ '<div style="position:absolute;' +\r
+ 'left:' + pos + 'px;' +\r
+ 'top:0;' +\r
+ (node.labelStyle ? node.labelStyle + ";" : "") +\r
+ '">' +\r
+ '<span' + (cellInfo.labelSpanStyle\r
+ ? 'style="' + cellInfo.labelSpanStyle + ';"'\r
+ : "") + '>' +\r
+ node.label +\r
+ '</span>' +\r
+ '</div>';\r
+\r
+ return html;\r
+ },\r
+\r
+ /**\r
+ * Adds extra content just before the indentation.\r
+ *\r
+ * @param cellInfo {Map} The information about the cell.\r
+ * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+ *\r
+ * @param pos {Integer}\r
+ * The position from the left edge of the column at which to render this\r
+ * item.\r
+ *\r
+ * @return {Map}\r
+ * The returned map contains an 'html' member which contains the html for\r
+ * the indentation, and a 'pos' member which is the starting position\r
+ * plus the width of the indentation.\r
+ */\r
+ _addExtraContentBeforeIndentation : function(cellInfo, pos)\r
+ {\r
+ return { html: '', pos: pos };\r
+ },\r
+\r
+ /**\r
+ * Adds extra content just before the icon.\r
+ *\r
+ * @param cellInfo {Map} The information about the cell.\r
+ * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+ *\r
+ * @param pos {Integer}\r
+ * The position from the left edge of the column at which to render this\r
+ * item.\r
+ *\r
+ * @return {Map}\r
+ * The returned map contains an 'html' member which contains the html for\r
+ * the indentation, and a 'pos' member which is the starting position\r
+ * plus the width of the indentation.\r
+ */\r
+ _addExtraContentBeforeIcon : function(cellInfo, pos)\r
+ {\r
+ return { html: '', pos: pos };\r
+ },\r
+\r
+ /**\r
+ * Adds extra content just before the label.\r
+ *\r
+ * @param cellInfo {Map} The information about the cell.\r
+ * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+ *\r
+ * @param pos {Integer}\r
+ * The position from the left edge of the column at which to render this\r
+ * item.\r
+ *\r
+ * @return {Map}\r
+ * The returned map contains an 'html' member which contains the html for\r
+ * the indentation, and a 'pos' member which is the starting position\r
+ * plus the width of the indentation.\r
+ */\r
+ _addExtraContentBeforeLabel : function(cellInfo, pos)\r
+ {\r
+ return { html: '', pos: pos };\r
+ },\r
+\r
+\r
+ /**\r
+ * Determine the symbol to use for indentation of a tree row, at a\r
+ * particular column. The indentation to use may be just white space or\r
+ * may be a tree line. Tree lines come in numerous varieties, so the\r
+ * appropriate one is selected.\r
+ *\r
+ * @type member\r
+ *\r
+ * @param column {Integer}\r
+ * The column of indentation being requested, zero-relative\r
+ *\r
+ * @param node {Node}\r
+ * The node being displayed in the row. The properties of a node are\r
+ * described in {@link qx.ui.treevirtual.SimpleTreeDataModel}\r
+ *\r
+ * @param bUseTreeLines {Boolean}\r
+ * Whether to find an appropriate tree line icon, or simply provide\r
+ * white space.\r
+ *\r
+ * @param bAlwaysShowOpenCloseSymbol {Boolean}\r
+ * Whether to display the open/close icon for a node even if it has no\r
+ * children.\r
+ *\r
+ * @param bExcludeFirstLevelTreeLines {Boolean}\r
+ * If bUseTreeLines is enabled, then further filtering of the left-most\r
+ * tree line may be specified here. If <i>true</i> then the left-most\r
+ * tree line, between top-level siblings, will not be displayed.\r
+ * If <i>false</i>, then the left-most tree line wiill be displayed\r
+ * just like all of the other tree lines.\r
+ *\r
+ * @return {var} TODOC\r
+ */\r
+ _getIndentSymbol : function(column,\r
+ node,\r
+ bUseTreeLines,\r
+ bAlwaysShowOpenCloseSymbol,\r
+ bExcludeFirstLevelTreeLines)\r
+ {\r
+ var STDCR = org.argeo.ria.util.TreeDataCellRenderer;\r
+\r
+ // If we're in column 0 and excludeFirstLevelTreeLines is enabled, then\r
+ // we treat this as if no tree lines were requested.\r
+ if (column == 0 && bExcludeFirstLevelTreeLines)\r
+ {\r
+ bUseTreeLines = false;\r
+ }\r
+\r
+ // If we're not on the final column...\r
+ if (column < node.level - 1)\r
+ {\r
+ // then return either a line or a blank icon, depending on\r
+ // bUseTreeLines\r
+ return (bUseTreeLines && ! node.lastChild[column]\r
+ ? STDCR.__icon.line\r
+ : { icon : this.BLANK });\r
+ }\r
+\r
+ var bLastChild = node.lastChild[node.lastChild.length - 1];\r
+\r
+ // Is this a branch node that does not have the open/close button hidden?\r
+ if (node.type == qx.ui.treevirtual.SimpleTreeDataModel.Type.BRANCH &&\r
+ ! node.bHideOpenClose)\r
+ {\r
+ // Does this node have any children, or do we always want the\r
+ // open/close symbol to be shown?\r
+ if (node.children.length > 0 || bAlwaysShowOpenCloseSymbol)\r
+ {\r
+ // If we're not showing tree lines...\r
+ if (!bUseTreeLines)\r
+ {\r
+ // ... then just use a expand or contract\r
+ return (node.bOpened\r
+ ? STDCR.__icon.contract\r
+ : STDCR.__icon.expand);\r
+ }\r
+\r
+ // Are we looking at a top-level, first child of its parent?\r
+ if (column == 0 && node.bFirstChild)\r
+ {\r
+ // Yup. If it's also a last child...\r
+ if (bLastChild)\r
+ {\r
+ // ... then use no tree lines.\r
+ return (node.bOpened\r
+ ? STDCR.__icon.onlyContract\r
+ : STDCR.__icon.onlyExpand);\r
+ }\r
+ else\r
+ {\r
+ // otherwise, use descender lines but no ascender.\r
+ return (node.bOpened\r
+ ? STDCR.__icon.startContract\r
+ : STDCR.__icon.startExpand);\r
+ }\r
+ }\r
+\r
+ // It's not a top-level, first child. Is this the last child of its\r
+ // parent?\r
+ if (bLastChild)\r
+ {\r
+ // Yup. Return an ending expand or contract.\r
+ return (node.bOpened\r
+ ? STDCR.__icon.endContract\r
+ : STDCR.__icon.endExpand);\r
+ }\r
+\r
+ // Otherwise, return a crossing expand or contract.\r
+ return (node.bOpened\r
+ ? STDCR.__icon.crossContract\r
+ : STDCR.__icon.crossExpand);\r
+ }\r
+ }\r
+\r
+ // This node does not have any children. Return an end or cross, if\r
+ // we're using tree lines.\r
+ if (bUseTreeLines)\r
+ {\r
+ // If this is a child of the root node...\r
+ if (node.parentNodeId == 0)\r
+ {\r
+ // If this is the only child...\r
+ if (bLastChild && node.bFirstChild)\r
+ {\r
+ // ... then return a blank.\r
+ return { icon : this.BLANK };\r
+ }\r
+\r
+ // Otherwise, if this is the last child...\r
+ if (bLastChild)\r
+ {\r
+ // ... then return an end line.\r
+ return STDCR.__icon.end;\r
+ }\r
+\r
+ // Otherwise if this is the first child...\r
+ if (node.bFirstChild)\r
+ {\r
+ // ... then return a start line.\r
+ return STDCR.__icon.startContract;\r
+ }\r
+ }\r
+\r
+ // If this is a last child, return and ending line; otherwise cross.\r
+ return (bLastChild\r
+ ? STDCR.__icon.end\r
+ : STDCR.__icon.cross);\r
+ }\r
+\r
+ return { icon : this.BLANK };\r
+ }\r
+ },\r
+\r
+ defer : function()\r
+ {\r
+ // Ensure that the theme is initialized\r
+ qx.theme.manager.Meta.getInstance().initialize();\r
+\r
+ var STDCR = org.argeo.ria.util.TreeDataCellRenderer;\r
+\r
+ var ImageLoader = qx.io2.ImageLoader;\r
+\r
+ var am = qx.util.AliasManager.getInstance();\r
+ var rm = qx.util.ResourceManager;\r
+ var tm = qx.theme.manager.Appearance.getInstance();\r
+\r
+ var loadImage = function(f)\r
+ {\r
+ ImageLoader.load(rm.toUri(am.resolve(f)));\r
+ };\r
+\r
+ STDCR.__icon.line = tm.styleFrom("treevirtual-line");\r
+ loadImage(STDCR.__icon.line.icon);\r
+\r
+ STDCR.__icon.contract = tm.styleFrom("treevirtual-contract");\r
+ loadImage(STDCR.__icon.contract.icon);\r
+\r
+ STDCR.__icon.expand = tm.styleFrom("treevirtual-expand");\r
+ loadImage(STDCR.__icon.expand.icon);\r
+\r
+ STDCR.__icon.onlyContract = tm.styleFrom("treevirtual-only-contract");\r
+ loadImage(STDCR.__icon.onlyContract.icon);\r
+\r
+ STDCR.__icon.onlyExpand = tm.styleFrom("treevirtual-only-expand");\r
+ loadImage(STDCR.__icon.onlyExpand.icon);\r
+\r
+ STDCR.__icon.startContract = tm.styleFrom("treevirtual-start-contract");\r
+ loadImage(STDCR.__icon.startContract.icon);\r
+\r
+ STDCR.__icon.startExpand = tm.styleFrom("treevirtual-start-expand");\r
+ loadImage(STDCR.__icon.startExpand.icon);\r
+\r
+ STDCR.__icon.endContract = tm.styleFrom("treevirtual-end-contract");\r
+ loadImage(STDCR.__icon.endContract.icon);\r
+\r
+ STDCR.__icon.endExpand = tm.styleFrom("treevirtual-end-expand");\r
+ loadImage(STDCR.__icon.endExpand.icon);\r
+\r
+ STDCR.__icon.crossContract = tm.styleFrom("treevirtual-cross-contract");\r
+ loadImage(STDCR.__icon.crossContract.icon);\r
+\r
+ STDCR.__icon.crossExpand = tm.styleFrom("treevirtual-cross-expand");\r
+ loadImage(STDCR.__icon.crossExpand.icon);\r
+\r
+ STDCR.__icon.end = tm.styleFrom("treevirtual-end");\r
+ loadImage(STDCR.__icon.end.icon);\r
+\r
+ STDCR.__icon.cross = tm.styleFrom("treevirtual-cross");\r
+ loadImage(STDCR.__icon.cross.icon);\r
+ },\r
+\r
+ destruct : function()\r
+ {\r
+ this._disposeFields(\r
+ "__am",\r
+ "__rm",\r
+ "__tm",\r
+ "BLANK");\r
+ }\r
+});
\ No newline at end of file
--- /dev/null
+/**\r
+ * Various utilitary classes, especially qooxdoo framework extensions \r
+ * to fix various bugs or missing features.\r
+ *\r
+ */
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
+<head>\r
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+ <title>Slc Webui</title>\r
+ <script type="text/javascript" src="script/org.argeo.ria.js"></script>\r
+</head>\r
+</html>\r
--- /dev/null
+This directory will contain translation (.po) files once you run the
+'translation' job in your project.
+
+++ /dev/null
-{\r
- "info" : \r
- {\r
- "name" : "org.argeo.ria",\r
-\r
- "summary" : "Slc Webui",\r
- "description" : "Argeo Rich Internet Application",\r
- \r
- "homepage" : "http://www.argeo.org/",\r
-\r
- "license" : "LGPL",\r
- "authors" : \r
- [\r
- {\r
- "name" : "Charles du Jeu",\r
- "email" : "charles.dujeu@gmail.com"\r
- }\r
- ],\r
-\r
- "version" : "trunk",\r
- "qooxdoo-versions": ["0.8"]\r
- },\r
- \r
- "provides" : \r
- {\r
- "namespace" : "org.argeo.ria",\r
- "encoding" : "utf-8",\r
- "class" : "class",\r
- "resource" : "resource",\r
- "translation" : "translation",\r
- "type" : "application"\r
- }\r
-}\r
-\r
+++ /dev/null
-/* ************************************************************************\r
-\r
- Copyright: 2008 Argeo\r
-\r
- License: LGPL\r
-\r
- Authors: Charles du Jeu\r
-\r
-************************************************************************ */\r
-\r
-/* ************************************************************************\r
-\r
-#asset(slc/*)\r
-\r
-************************************************************************ */\r
-\r
-/**\r
- * This is the main application class of an Argeo RIA.\r
- */\r
-qx.Class.define("org.argeo.ria.Application",\r
-{\r
- extend : qx.application.Standalone,\r
- \r
- statics : {\r
- INSTANCE : null \r
- },\r
- \r
- properties : {\r
- /**\r
- * Available perspective detected in the current compilation.\r
- */\r
- perspectives : {\r
- check : "Map",\r
- init : {}\r
- },\r
- /**\r
- * Currently layouted perspective label\r
- */\r
- activePerspectiveName : {\r
- check : "String",\r
- init : ""\r
- },\r
- /**\r
- * Currently layouted perspective.\r
- */\r
- activePerspective : {\r
- init : null\r
- },\r
- /**\r
- * Basic command associated to the application, applicable to all perspectives.\r
- */\r
- commandsDefinitions : {\r
- init : {\r
- "stop" : {\r
- label : "Stop", \r
- icon : "resource/slc/process-stop.png",\r
- shortcut : "Control+s",\r
- enabled : false,\r
- menu : null,\r
- toolbar : "list",\r
- callback : function(e){},\r
- command : null\r
- },\r
- "switchperspective" : {\r
- label : "Switch Perspective", \r
- icon : "resource/slc/view-pane-tree.png",\r
- shortcut : "",\r
- enabled : true,\r
- menu : "View",\r
- toolbar : false,\r
- submenu : [],\r
- submenuCallback : function(commandId){\r
- // Defer execution to assure that the submenu is closed \r
- // before it is rebuilt.\r
- qx.event.Timer.once(function(){\r
- org.argeo.ria.Application.INSTANCE.loadPerspective(commandId);\r
- }, this, 10); \r
- },\r
- callback : function(e){},\r
- command : null \r
- }, \r
- "log" : {\r
- label : "Show Console", \r
- icon : "resource/slc/help-contents.png",\r
- shortcut : "",\r
- enabled : true,\r
- menu : "View",\r
- menuPosition: "last",\r
- toolbar : false,\r
- callback : function(e){ \r
- org.argeo.ria.components.Logger.getInstance().toggle();\r
- }, \r
- command : null\r
- },\r
- "help" : {\r
- label : "About...", \r
- icon : "resource/slc/help-about.png",\r
- shortcut : "Control+h",\r
- enabled : true,\r
- menu : "View",\r
- toolbar : false,\r
- callback : function(e){\r
- var win = new org.argeo.ria.components.Modal("About SLC", null, "SLC is a product from Argeo.");\r
- win.attachAndShow();\r
- }, \r
- command : null\r
- }\r
- }\r
- }\r
- },\r
-\r
- members :\r
- {\r
- /**\r
- * This method contains the initial application code and gets called \r
- * during startup of the application\r
- */\r
- main : function()\r
- {\r
- // Call super class\r
- this.base(arguments);\r
- this.self(arguments).INSTANCE = this;\r
- this.views = {};\r
- \r
- var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
- viewsManager.setApplicationRoot(this.getRoot());\r
- \r
- /*\r
- var appli = this;\r
- qx.bom.Event.addNativeListener(window, "unload", function(){\r
- // TODO : Close perspective if one is open. \r
- if(appli.getActivePerspective()){\r
- alert(appli.getActivePerspective());\r
- appli.getActivePerspective().remove(org.argeo.ria.components.ViewsManager.getInstance());\r
- } \r
- });\r
- */\r
- // Enable logging in debug variant\r
- if (qx.core.Variant.isSet("qx.debug", "on"))\r
- {\r
- qx.log.appender.Native;\r
- qx.log.appender.Console;\r
- }\r
- var winLogger = org.argeo.ria.components.Logger.getInstance();\r
- this.getRoot().add(winLogger);\r
- qx.log.Logger.register(winLogger);\r
-\r
- // Main layout\r
- var layout = new qx.ui.layout.VBox();\r
- var container = new qx.ui.container.Composite(layout);\r
- viewsManager.setViewPanesContainer(container);\r
- // Document is the application root \r
- this.getRoot().add(container, {left:0,right:0,top:0,bottom:0});\r
- \r
- // Find available perspectives\r
- var allPerspectives = {};\r
- for(var key in qx.Bootstrap.$$registry){\r
- if(qx.Class.hasInterface(qx.Bootstrap.$$registry[key], org.argeo.ria.components.IPerspective)){\r
- allPerspectives[key] = qx.Bootstrap.$$registry[key];\r
- }\r
- }\r
- var perspectiveNumber = qx.lang.Object.getLength(allPerspectives);\r
- if(!perspectiveNumber){\r
- this.error("Cannot find a perspective for startup!");\r
- return;\r
- }\r
- this.setPerspectives(allPerspectives);\r
- // Choose startup perspective, delete switch menu if only one perspective.\r
- if(perspectiveNumber <= 1){\r
- delete this.getCommandsDefinitions()["switchperspective"];\r
- this.setActivePerspectiveName(qx.lang.Object.getKeys(allPerspectives)[0]);\r
- }\r
- else{\r
- var startupSetting;\r
- try{\r
- startupSetting = qx.core.Setting.get("ria.StartupPerspective");\r
- }catch(e){}\r
- if(startupSetting && allPerspectives[startupSetting]){\r
- this.setActivePerspectiveName(startupSetting);\r
- }else{\r
- this.setActivePerspectiveName(qx.lang.Object.getKeys(allPerspectives)[0]);\r
- }\r
- this.rebuildPerspectiveMenus();\r
- }\r
- \r
- var menuBar = new qx.ui.menubar.MenuBar();\r
- var toolbar = new qx.ui.toolbar.ToolBar();\r
- var commandManager = org.argeo.ria.event.CommandsManager.getInstance();\r
- commandManager.init(this.getCommandsDefinitions());\r
- commandManager.createCommands();\r
- commandManager.registerMenuBar(menuBar);\r
- commandManager.registerToolBar(toolbar);\r
- toolbar.setShow("both");\r
- commandManager.addToolbarContextMenu(toolbar);\r
-\r
- var stopCommand = commandManager.getCommandById("stop");\r
- var serviceManager = org.argeo.ria.remote.RequestManager.getInstance();\r
- serviceManager.setStopCommand(stopCommand);\r
-\r
- container.add(menuBar);\r
- container.add(toolbar); \r
-\r
- this.loadPerspective();\r
- },\r
- \r
- /**\r
- * Load a given perspective by its name.\r
- * @param perspectiveName {String} Perspective to load\r
- */\r
- loadPerspective : function(perspectiveName){\r
- if(perspectiveName){\r
- this.setActivePerspectiveName(perspectiveName);\r
- this.rebuildPerspectiveMenus();\r
- }else{\r
- perspectiveName = this.getActivePerspectiveName();\r
- }\r
- var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
- if(this.getActivePerspective()){\r
- this.getActivePerspective().remove(viewsManager);\r
- }\r
- var allPerspectives = this.getPerspectives(); \r
- var perspectiveClass = allPerspectives[perspectiveName];\r
- if(!perspectiveClass){\r
- this.error("Cannot find class for startup perspective : "+perspectiveName);\r
- return;\r
- }\r
- var perspective = new perspectiveClass;\r
- perspective.initViewPanes(viewsManager);\r
- perspective.initViews(viewsManager);\r
- this.setActivePerspective(perspective);\r
- },\r
- \r
- /**\r
- * After switching perspective, call this function to rebuild menu with the right selected.\r
- */\r
- rebuildPerspectiveMenus : function(){\r
- var switchCommand = this.getCommandsDefinitions()["switchperspective"];\r
- switchCommand.submenu = [];\r
- var allPerspectives = this.getPerspectives();\r
- for(var key in allPerspectives){\r
- switchCommand.submenu.push({\r
- "label":(allPerspectives[key].LABEL || key)+(key==this.getActivePerspectiveName()?" (current)":""),\r
- "icon" :(allPerspectives[key].ICON || null),\r
- "commandId":key,\r
- "disabled" : (key==this.getActivePerspectiveName()?true:false)\r
- });\r
- }\r
- if(switchCommand.command){ // Command already created : force reload\r
- switchCommand.command.clearMenus();\r
- switchCommand.command.setMenu(switchCommand.submenu);\r
- }\r
- },\r
-\r
- /**\r
- * Specific action of calling an external URL without triggering the "close()" method\r
- * of Application.\r
- * @param hrefValue {String} A download url that should reply with specific "attachment" header to avoid leaving the application.\r
- */\r
- javascriptDownloadLocation: function(hrefValue){\r
- this.interruptClose = true;\r
- document.location.href = hrefValue;\r
- this.interruptClose = false;\r
- },\r
- \r
- /**\r
- * Called at Application ending (closing the browser).\r
- */\r
- close : function(){\r
- if(this.interruptClose) return ; \r
- if(this.getActivePerspective()){\r
- this.getActivePerspective().remove(org.argeo.ria.components.ViewsManager.getInstance());\r
- } \r
- this.base(arguments);\r
-\r
- }\r
- \r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * Generic package for the Argeo Rich Internet Application based on qooxdoo (www.qooxdoo.org).\r
- * This is an empty application in itself, since it requires at least a basic implementation to be useful.\r
- * \r
- * \r
- * The skeleton of an ArgeoRIA is the following : \r
- * \r
- * + GUI Application containing a menubar, a toolbar, and an empty space for custom Perspective.\r
- * \r
- * + Various managers : org.argeo.ria.components.ViewsManager for manipulating IView (VIEW), org.argeo.ria.event.CommandsManager \r
- * for automatically wiring commands inside the application (CONTROLLER), and org.argeo.ria.remote.RequestManager for accessing the data (MODEL).\r
- * \r
- * + A simple configuration will allow to start your own Perspective inside the application.\r
- *\r
- */
\ No newline at end of file
+++ /dev/null
-/**\r
- * A "dynamic" implementation of the standard TreeFolder class. \r
- *\r
- */\r
-qx.Class.define("org.argeo.ria.components.DynamicTreeFolder", {\r
- extend : qx.ui.tree.TreeFolder,\r
- \r
- properties : {\r
- /**\r
- * The current state of the folder, usually "empty" => "loading" => "loaded"\r
- */\r
- "state" : {\r
- check : "String",\r
- init : "empty",\r
- apply : "_applyState"\r
- },\r
- /**\r
- * String to display as a child node during loading\r
- */\r
- "loadingString" : {\r
- check : "String",\r
- init : "Loading..."\r
- },\r
- /**\r
- * Function that will load the children of this folder\r
- */\r
- "loader" : {\r
- check : "Function",\r
- init : function(treeFolder){treeFolder.setLoaded();}\r
- },\r
- /**\r
- * Optionnal data describing the "drag" behaviour of the created children.\r
- * First level is "file" or "folder", and for each of them, supported keys are "type" and "action". \r
- */\r
- "dragData": {\r
- check : "Map",\r
- init : {}\r
- }\r
- },\r
- \r
- /**\r
- * Creates a new instance of DynamicTreeFolder\r
- * @param label {String} Label of the folder\r
- * @param loader {Function} Function that will load the children \r
- * @param loadingString {String} String to display as a child node during loading\r
- * @param dragData {Map} Optionnal data describing the "drag" behaviour of the created children.\r
- */\r
- construct : function(label, loader, loadingString, dragData){\r
- this.base(arguments, label);\r
- if(loader) this.setLoader(loader);\r
- if(loadingString) this.setLoadingString(loadingString);\r
- if(dragData) this.setDragData(dragData);\r
- this.addListener("changeOpen", function(e){\r
- if(e.getData() && this.getState() == "loading"){\r
- this.load();\r
- }\r
- }, this);\r
- this.setState("loading");\r
- },\r
- \r
- members : {\r
- /**\r
- * Add an item to the folder \r
- * @param varargs {Mixed} One or many children to add\r
- */\r
- add : function(varargs){\r
- this.base(arguments, varargs);\r
- for (var i=0, l=arguments.length; i<l; i++)\r
- { \r
- var treeItem = arguments[i];\r
- if(treeItem == this.loadingChild) continue;\r
- this.appendDragData(treeItem);\r
- }\r
- },\r
- \r
- /**\r
- * If there is dragData set, init the drag behaviour of a child\r
- * @param treeItem {qx.ui.tree.AbstractTreeItem} Newly created child\r
- */\r
- appendDragData : function(treeItem){\r
- var dragData = this.getDragData();\r
- var nodeTypeDetected = false;\r
- if(qx.Class.isSubClassOf(qx.Class.getByName(treeItem.classname), qx.ui.tree.TreeFile) && dragData["file"]){\r
- nodeTypeDetected = "file";\r
- }\r
- if(qx.Class.isSubClassOf(qx.Class.getByName(treeItem.classname), qx.ui.tree.TreeFolder) && dragData["folder"]){\r
- nodeTypeDetected = "folder";\r
- } \r
- if(!nodeTypeDetected) return;\r
- \r
- treeItem.setDraggable(true);\r
- treeItem.addListener("dragstart", function(e){ \r
- if(dragData[nodeTypeDetected]["type"]){\r
- for(var j=0;j<dragData[nodeTypeDetected]["type"].length;j++){\r
- e.addType(dragData[nodeTypeDetected]["type"][j]);\r
- }\r
- }\r
- if(dragData[nodeTypeDetected]["action"]){\r
- for(var j=0;j<dragData[nodeTypeDetected]["action"].length;j++){\r
- e.addAction(dragData[nodeTypeDetected]["action"][j]);\r
- }\r
- }\r
- }); \r
- \r
- },\r
- \r
- /**\r
- * Set the state to "loaded"\r
- */\r
- setLoaded : function(){\r
- this.setState("loaded");\r
- },\r
- /**\r
- * Called when "state" is set to a new value\r
- * @param state {String} the new state\r
- */\r
- _applyState : function(state){\r
- if(state == "loaded"){\r
- if(this.loadingChild){\r
- this.remove(this.loadingChild);\r
- delete this.loadingChild;\r
- }\r
- }else if(state == "loading" && !this.loadingChild){\r
- this.addLoadingChild();\r
- }\r
- },\r
- /**\r
- * Create a temporary child with the loadingString label and add it.\r
- */\r
- addLoadingChild : function(){\r
- this.loadingChild = new qx.ui.tree.TreeFile(this.getLoadingString());\r
- this.add(this.loadingChild);\r
- },\r
- /**\r
- * Call loader function.\r
- */\r
- load : function(){ \r
- var loaderFunc = this.getLoader();\r
- loaderFunc(this);\r
- },\r
- /**\r
- * Empty then call loader function.\r
- */\r
- reload : function(){\r
- this.removeAll();\r
- this.setState("loading");\r
- this.load();\r
- }\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * \r
- * Any component implementing this interface will generally be a user-interface indicating \r
- * a "loading" status (button enabled/disabled, animated loading gif, etc...).\r
- * The RequestManager can handle such an array of ILoadStatusable at the beginning/end of a Request.\r
- * \r
- * @author Charles du Jeu\r
- */\r
-qx.Interface.define("org.argeo.ria.components.ILoadStatusable", {\r
- \r
- members : {\r
- /**\r
- * Sets the current status of the component.\r
- * @param status {boolean} load status\r
- * @return {Boolean}\r
- */\r
- setOnLoad : function(status){return true;}\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * This interface defines the main methods of an application Perpective.\r
- * See the org.argeo.ria package documentation for more info on how to build an Application\r
- * with this framework. \r
- * \r
- * @author Charles du Jeu\r
- */\r
-qx.Interface.define("org.argeo.ria.components.IPerspective", {\r
- \r
- statics : {\r
- /**\r
- * The human readable name of the perspective\r
- */\r
- LABEL : "",\r
- /**\r
- * An image resource associated to the perspective\r
- */\r
- ICON : ""\r
- },\r
- \r
- members : {\r
- /**\r
- * Initialize the available zones that will later contain IView implementations.\r
- * This method is <b>in charge</b> of your panel to the main application zone \r
- * (just below the toolbar).\r
- * \r
- * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
- * \r
- */\r
- initViewPanes : function(viewsManager){return true;},\r
- /**\r
- * Once the zones are available and initialized, initialize the views here\r
- * and add them to viewPanes. Trigger initial data loading, etc.\r
- * \r
- * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
- * \r
- */\r
- initViews : function(viewsManager){return true},\r
- /**\r
- * Remove and destroy the perspective\r
- * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
- */\r
- remove : function(viewsManager){return true}\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * Interface for a standard 'view' of an argeo RIA. A view is an independant applet that \r
- * will be integrated inside a ViewPane. \r
- * If this view is to implement a selection (a list, a tree, etc) that will trigger changes on commands, \r
- * it must trigger a viewSelection#changeSelection event.\r
- * \r
- * The typical lifecycle of an IView will be the following :\r
- * <br>+ init(viewPane) : initialize basic GUI in the viewPane\r
- * <br>+ getCommands() : wire the commands and add them to the toolbars/menubars\r
- * <br>+ load(data) : loads the data itself.\r
- * \r
- * @author Charles du Jeu\r
- */\r
-qx.Interface.define("org.argeo.ria.components.IView", {\r
- \r
- properties : {\r
- /**\r
- * The commands definition Map that will be automatically added and wired to the menubar and toolbar.\r
- * See {@link org.argeo.ria.event.CommandsManager#definitions} for the keys to use for defining commands.\r
- */\r
- commands : {},\r
- viewSelection : {\r
- nullable:false, \r
- check:"org.argeo.ria.components.ViewSelection"\r
- },\r
- instanceId : {init:""},\r
- instanceLabel : {init:""}\r
- },\r
- \r
- members : {\r
- /**\r
- * The implementation should contain the GUI initialisation.\r
- * This is the role of the manager to actually add the graphical component to the pane, \r
- * so it's not necessary to do it here. \r
- * @param viewPane {org.argeo.ria.components.ViewPane} The pane manager\r
- * @param data {Mixed} Any object or data passed by the initiator of the view\r
- * @return {Boolean}\r
- */\r
- init : function(viewPane, data){return true;},\r
- /**\r
- * The implementation should contain the real data loading (i.o. query...)\r
- * @return {Boolean}\r
- */\r
- load : function(){return true;},\r
- /**\r
- * Whether this component is already contained in a scroller (return false) or not (return true).\r
- * @return {Boolean}\r
- */\r
- addScroll : function(){return true;},\r
- /**\r
- * Called at destruction time\r
- * Perform all the clean operations (stopping polling queries, etc.) \r
- */\r
- close : function(){return true;}\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * A modal window like console for the logs. \r
- * Also opens a small alert window (qooxdoo, not native) on errors.\r
- * \r
- * @author : Charles du Jeu\r
- */\r
-qx.Class.define("org.argeo.ria.components.Logger",\r
-{\r
- type : "singleton",\r
- extend : qx.ui.window.Window,\r
- \r
- construct : function(){\r
- this.base(arguments, "Logs", "resource/slc/help-contents.png");\r
- this.set({\r
- showMaximize : true,\r
- showMinimize : false,\r
- width: 550,\r
- height: 300\r
- });\r
- this.setLayout(new qx.ui.layout.Dock(0,5));\r
- var buttonPane = new qx.ui.container.Composite(new qx.ui.layout.Canvas());\r
- var closeButton = new qx.ui.form.Button("Close"); \r
- closeButton.addListener("execute", function(e){\r
- this.hide(); \r
- }, this);\r
- buttonPane.add(closeButton, {width:'20%',left:'40%'});\r
- this.add(buttonPane, {edge:'south'});\r
- this.setModal(false);\r
- \r
- var layout = new qx.ui.layout.VBox(2); \r
- this._logPane = new qx.ui.container.Composite(layout);\r
- var deco = new qx.ui.decoration.Single(1, 'solid', '#000000');\r
- deco.setBackgroundColor("#ffffff")\r
- var scroller = new qx.ui.container.Scroll(this._logPane);\r
- scroller.setDecorator(deco);\r
- this.add(scroller, {edge:'center', width:'100%', height:'100%'});\r
- // Build style sheet content\r
- var style =\r
- [\r
- '.messages{font-size:0.9em}',\r
- '.messages div{padding:0px 4px;}',\r
- '.messages .offset{font-weight:bold;}',\r
- '.messages .object{font-style:italic;}',\r
- \r
- '.messages .user-command{color:blue}',\r
- '.messages .user-result{background:white}',\r
- '.messages .user-error{background:#FFE2D5}',\r
- '.messages .level-debug{background:white}',\r
- '.messages .level-info{background:#DEEDFA}',\r
- '.messages .level-warn{background:#FFF7D5}',\r
- '.messages .level-error{background:#FFE2D5}',\r
- '.messages .level-user{background:#E3EFE9}',\r
- '.messages .type-string{color:black;font-weight:normal;}',\r
- '.messages .type-number{color:#155791;font-weight:normal;}',\r
- '.messages .type-boolean{color:#15BC91;font-weight:normal;}',\r
- '.messages .type-array{color:#CC3E8A;font-weight:bold;}',\r
- '.messages .type-map{color:#CC3E8A;font-weight:bold;}',\r
- '.messages .type-key{color:#565656;font-style:italic}',\r
- '.messages .type-class{color:#5F3E8A;font-weight:bold}',\r
- '.messages .type-instance{color:#565656;font-weight:bold}',\r
- '.messages .type-stringify{color:#565656;font-weight:bold}'\r
- ]; \r
- // Include stylesheet\r
- qx.bom.Stylesheet.createElement(style.join(""));\r
- \r
- },\r
- \r
- members : {\r
- /**\r
- * Adds a log in the GUI component.\r
- * @param entry {Map} A log entry\r
- */\r
- process : function(entry){\r
- var wrapper = qx.log.appender.Util.toHtml(entry);\r
- var label = new qx.ui.basic.Label('<div class="messages"><div class="'+wrapper.className+'">'+wrapper.innerHTML.replace(",","<br/>")+'</div></div>'); \r
- label.setRich(true);\r
- if(entry.level == "error"){\r
- if(!this.alert){\r
- this.alert = new org.argeo.ria.components.Modal("Error");\r
- this.alert.setPersistent(true);\r
- this.alert.addCloseButton();\r
- }\r
- this.alert.addCenter(label.clone()); \r
- this.alert.attachAndShow();\r
- }else if(entry.level == "info"){\r
- this.showLogAsPopup(label.clone());\r
- }\r
- this._logPane.addAt(label, 0);\r
- },\r
- /**\r
- * Shows the GUI console and center it.\r
- */\r
- toggle : function(){\r
- this.show();\r
- this.center();\r
- },\r
- \r
- /**\r
- * Show a given info log in a small popup right-top aligned. \r
- * The popup will disappear after 5 seconds.\r
- * @param content {qx.ui.basic.Label} The content of the popup to display \r
- */\r
- showLogAsPopup:function(content){\r
- if(!this.popup){\r
- this.popup = new qx.ui.popup.Popup(new qx.ui.layout.Canvas()).set({\r
- backgroundColor: "#DFFAD3",\r
- padding: [2, 4],\r
- width: 350,\r
- offset:0,\r
- position: "right-top"\r
- });\r
- }\r
- this.popup.removeAll();\r
- this.popup.add(content);\r
- var appRoot = org.argeo.ria.components.ViewsManager.getInstance().getApplicationRoot(); \r
- appRoot.add(this.popup);\r
- this.popup.show();\r
- this.popup.moveTo((qx.bom.Viewport.getWidth()-350), 0);\r
- qx.event.Timer.once(function(){this.popup.hide();}, this, 5000);\r
- }\r
- },\r
-\r
- destruct : function()\r
- {\r
- qx.log.Logger.unregister(this);\r
- }\r
- \r
-});\r
+++ /dev/null
-/**\r
- * Generic modal popup window.\r
- * It is layed out with a dock layout. When adding components to it, they are added as "center".\r
- * @author Charles du Jeu\r
- */\r
-qx.Class.define("org.argeo.ria.components.Modal",\r
-{\r
- extend : qx.ui.window.Window,\r
- \r
- properties : {\r
- persistent : {\r
- check : "Boolean",\r
- init : false\r
- }\r
- },\r
- \r
- events : {\r
- /**\r
- * Triggered when the user clicks the "ok" button. \r
- */\r
- "ok" : "qx.event.type.Event"\r
- },\r
- /**\r
- * \r
- * @param caption {String} Title of the window\r
- * @param icon {String} Icon of the window\r
- * @param text {String} Default content of the window.\r
- */\r
- construct : function(caption, icon, text){\r
- this.base(arguments, caption, icon);\r
- this.set({\r
- showMaximize : false,\r
- showMinimize : false,\r
- width: 200,\r
- height: 150\r
- });\r
- this.setLayout(new qx.ui.layout.Dock());\r
- this.setModal(true);\r
- this.center();\r
- if(text){\r
- this.addLabel(text);\r
- }\r
- },\r
- \r
- members : {\r
- \r
- addCenter : function(component){\r
- if(!this.getPersistent()){\r
- this.add(component, {edge : 'center', width:'100%'});\r
- }else{\r
- if(!this.centerScroller){\r
- this.centerScroller = new qx.ui.container.Composite(new qx.ui.layout.VBox(1)); \r
- this.add(new qx.ui.container.Scroll(this.centerScroller), {edge : 'center', width:'100%', height:'100%'});\r
- }\r
- this.centerScroller.add(component);\r
- }\r
- },\r
- \r
- /**\r
- * Display text inside the popup\r
- * @param text {String} A string content for the popup\r
- */\r
- addLabel:function(text){\r
- var label = new qx.ui.basic.Label(text);\r
- label.setRich(true);\r
- label.setTextAlign("center");\r
- this.addCenter(label);\r
- this.addCloseButton();\r
- },\r
- /**\r
- * Add a question and ok / cancel buttons\r
- * @param text {String} The question to ask to the user\r
- */\r
- addConfirm : function(text){\r
- var label = new qx.ui.basic.Label(text);\r
- label.setRich(true);\r
- label.setTextAlign("center");\r
- this.addCenter(label);\r
- this.addOkCancel();\r
- },\r
- /**\r
- * Display a component (panel) in the center of the popup\r
- * @param panel {qx.ui.core.Widget} A gui component (will be set at width 100%).\r
- */\r
- addContent: function(panel){\r
- this.addCenter(panel);\r
- this.addCloseButton();\r
- },\r
- /**\r
- * Automatically attach to the application root, then show.\r
- */\r
- attachAndShow:function(){\r
- if(!this.attached){\r
- org.argeo.ria.components.ViewsManager.getInstance().getApplicationRoot().add(this);\r
- this.attached = true;\r
- }\r
- this.show();\r
- },\r
- /**\r
- * Adds a close button bottom-center aligned to the popup\r
- */\r
- addCloseButton : function(){\r
- this.closeButton = new qx.ui.form.Button("Close");\r
- this.closeButton.addListener("execute", this._closeAndDestroy, this);\r
- this.add(this.closeButton, {edge:'south'}); \r
- },\r
- /**\r
- * Adds two buttons bottom-center aligned (Ok and Cancel). \r
- * Ok button has no listener by default, Cancel will close and destroy the popup.\r
- */\r
- addOkCancel : function(){\r
- var buttonPane = new qx.ui.container.Composite(new qx.ui.layout.HBox(5, 'right'));\r
- buttonPane.setAlignX("center");\r
- this.add(buttonPane, {edge:"south"});\r
- this.okButton = new qx.ui.form.Button("Ok");\r
- this.okButton.addListener("execute", function(e){\r
- this.fireEvent("ok");\r
- this._closeAndDestroy();\r
- }, this);\r
- this.cancelButton = new qx.ui.form.Button("Cancel");\r
- this.cancelButton.addListener("execute", this._closeAndDestroy, this);\r
- buttonPane.add(this.okButton);\r
- buttonPane.add(this.cancelButton);\r
- },\r
- /**\r
- * Adds a prompt form to the popup : a question, followed by a text input.\r
- * @param questionString {String} The question to ask to the user \r
- * @param validationCallback {Function} Callback to apply : takes the text input value as unique argument.\r
- * @param callbackContext {Object} Context for the callback, optional.\r
- */\r
- makePromptForm:function(questionString, validationCallback, callbackContext){\r
- var label = new qx.ui.basic.Label(questionString);\r
- label.setRich(true);\r
- label.setTextAlign("center");\r
- this.add(label, {edge:'north'});\r
- var textField = new qx.ui.form.TextField();\r
- textField.setMarginTop(10);\r
- textField.setMarginBottom(10);\r
- this.add(textField, {edge:'center'});\r
- this.addOkCancel();\r
- if(callbackContext){\r
- validationCallback = qx.lang.Function.bind(validationCallback, callbackContext);\r
- }\r
- this.okButton.addListener("execute", function(e){\r
- var valid = validationCallback(textField.getValue());\r
- if(valid) this._closeAndDestroy();\r
- }, this);\r
- },\r
- /**\r
- * Close this modal window and destroy it.\r
- */\r
- _closeAndDestroy : function(){\r
- this.hide();\r
- if(!this.getPersistent()){\r
- this.destroy();\r
- }else{\r
- if(this.centerScroller) this.centerScroller.removeAll();\r
- }\r
- }\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * A more elaborate views container than ViewPane, as it can handle multiple contents\r
- * at once via a TabView.\r
- * See {@link org.argeo.ria.components.ViewPane}.\r
- */\r
-qx.Class.define("org.argeo.ria.components.TabbedViewPane",\r
-{\r
- extend : qx.ui.container.Composite,\r
- implement : [org.argeo.ria.components.ILoadStatusable],\r
-\r
- /**\r
- * @param viewId {String} Unique id of this viewPane\r
- * @param viewTitle {String} Readable Title of this viewPane\r
- */\r
- construct : function(viewId, viewTitle){\r
- this.base(arguments);\r
- this.setViewId(viewId);\r
- this._defaultViewTitle = viewTitle; \r
- this.setLayout(new qx.ui.layout.Canvas());\r
- this.blurredDecorator = new qx.ui.decoration.Uniform(1, "solid", "#000");\r
- this.blurredDecorator.setBackgroundImage("decoration/app-header.png");\r
- this.blurredDecorator.setBackgroundRepeat("scale");\r
- this.setDecorator(this.blurredDecorator);\r
-\r
- this.focusedDecorator = new qx.ui.decoration.Uniform(1, "solid", "#065fb2");\r
- this.focusedDecorator.setBackgroundImage("decoration/app-header.png");\r
- this.focusedDecorator.setBackgroundRepeat("scale");\r
- \r
- this.tabView = new qx.ui.tabview.TabView();\r
- this.tabView.setAppearance("widget");\r
- // Empty mode\r
- this.add(this.tabView, {top: 7, width:"100%", bottom:0});\r
- this.tabView.setBackgroundColor("#fff");\r
- this.tabView.setMarginTop(27);\r
- \r
- this.tabView.addListener("changeSelected", function(){\r
- this.fireEvent("changeSelection");\r
- }, this);\r
- \r
- \r
- this.setFocusable(true);\r
- this.addListener("click", function(e){\r
- this.fireDataEvent("changeFocus", this);\r
- }, this); \r
- \r
- this.pageIds = {};\r
- },\r
-\r
- properties : {\r
- /**\r
- * Unique id of the pane\r
- */\r
- viewId : {init:""},\r
- /**\r
- * Human-readable title for this view\r
- */\r
- viewTitle : {init:"", event:"changeViewTitle"},\r
- /**\r
- * Has its own scrollable content \r
- */\r
- ownScrollable : {init: false, check:"Boolean"},\r
- /**\r
- * Map of commands definition\r
- * @see org.argeo.ria.event.Command \r
- */\r
- commands : {init : null, nullable:true, check:"Map"}\r
- \r
- },\r
- \r
- members : {\r
- /**\r
- * Checks if the pane already contains a given view, identified by its instance id\r
- * @param contentId {Mixed} The instance id to check\r
- * @return {Boolean}\r
- */\r
- contentExists : function(contentId){\r
- if(this.pageIds[contentId]){\r
- this.tabView.setSelected(this.pageIds[contentId]);\r
- return this.pageIds[contentId].getUserData("argeoria.iview");\r
- } \r
- },\r
- /**\r
- * Sets a new instance in the tabbed pane.\r
- * @param content {org.argeo.ria.components.IView} The applet to add.\r
- */\r
- setContent : function(content){\r
- if(!this.tabView.getChildren().length){\r
- this.tabView.setBackgroundColor("transparent");\r
- this.tabView.setMarginTop(0); \r
- }\r
- var contentId = content.getInstanceId();\r
- var page = new qx.ui.tabview.Page(content.getInstanceLabel());\r
- this.pageIds[contentId] = page;\r
- page.setPadding(0);\r
- page.setLayout(new qx.ui.layout.Canvas());\r
- page.add(content, {width:"100%", top:0, bottom:0});\r
- this.tabView.add(page); \r
- page.setUserData("argeoria.iview", content); \r
- content.getViewSelection().addListener("changeSelection", function(e){\r
- this.fireEvent("changeSelection");\r
- }, this);\r
- this.tabView.setSelected(page);\r
- },\r
- /**\r
- * Get the currently selected tab content, if any.\r
- * @return {org.argeo.ria.components.IView} The currently selected view.\r
- */\r
- getContent : function(){\r
- if(this._getCrtPage()){\r
- return this._getCrtPage().getUserData("argeoria.iview");\r
- }\r
- return null;\r
- },\r
- /**\r
- * Get the currently selected tab ViewSelection object.\r
- * @return {org.argeo.ria.components.ViewSelection} The view selection object of the currently selected view.\r
- */\r
- getViewSelection : function(){\r
- if(!this.getContent()) return null;\r
- return this.getContent().getViewSelection();\r
- },\r
- /**\r
- * Return the currently selected tab Page.\r
- * @return {qx.ui.tabview.Page} The page\r
- */\r
- _getCrtPage : function(){\r
- return this.tabView.getSelected();\r
- },\r
- /**\r
- * Closes the currently selected view and remove all tabs components (button, page).\r
- */\r
- closeCurrent : function(){\r
- var crtPage = this._getCrtPage();\r
- if(!crtPage) return;\r
- var iView = crtPage.getUserData("argeoria.iview");\r
- var iViewInstance = iView.getInstanceId();\r
- iView.close(); \r
- this.tabView.remove(crtPage);\r
- delete(this.pageIds[iViewInstance]); \r
- if(!this.tabView.getChildren().length){ // No more tabs : remove commands!\r
- if(this.getCommands()){\r
- org.argeo.ria.event.CommandsManager.getInstance().removeCommands(this.getCommands(), this.getViewId());\r
- this.setCommands(null);\r
- } \r
- this.tabView.setBackgroundColor("#fff");\r
- this.tabView.setMarginTop(27);\r
- } \r
- },\r
- /**\r
- * Call closeCurrent() recursively until there is no more page.\r
- */\r
- empty : function(){\r
- var crtPage = this._getCrtPage();\r
- while(crtPage){\r
- this.closeCurrent();\r
- crtPage = this._getCrtPage();\r
- }\r
- },\r
- /**\r
- * Sets the tabView on "load" state. Nothing is done at the moment.\r
- * @param load {Boolean} Load status\r
- */\r
- setOnLoad : function(load){\r
- \r
- },\r
- /**\r
- * Sets a graphical indicator that this pane has the focus. A blue border.\r
- */\r
- focus : function(){\r
- if(this.hasFocus) return;\r
- this.fireEvent("changeSelection");\r
- this.setDecorator(this.focusedDecorator);\r
- this.hasFocus = true;\r
- }, \r
- /**\r
- * Remove a graphical focus indicator on this pane.\r
- */\r
- blur : function(){\r
- this.hasFocus = false;\r
- this.setDecorator(this.blurredDecorator);\r
- }\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**
- * A standard view container, referenced in the application by its unique id.
- * It is managed by the ViewsManager singleton that works as the "View" part of an MVC model.
- * @see org.argeo.ria.components.ViewsManager
- * @author Charles
- */
-qx.Class.define("org.argeo.ria.components.ViewPane",
-{
- extend : qx.ui.container.Composite,
- implement : [org.argeo.ria.components.ILoadStatusable],
-
- /**
- * @param viewId {String} Unique id of this viewPane
- * @param viewTitle {String} Readable Title of this viewPane
- * @param splitPaneData {Map} Additionnal data to be used by splitpanes implementations.
- */
- construct : function(viewId, viewTitle, splitPaneData){
- this.base(arguments);
- this.setViewId(viewId);
- this._defaultViewTitle = viewTitle;
- this.setViewTitle(viewTitle);
- if(splitPaneData){
- this.setSplitPaneData(splitPaneData);
- }
- this.setFocusable(true);
- this.addListener("click", function(e){
- this.fireDataEvent("changeFocus", this);
- }, this);
- this.createGui();
- },
-
- events : {
- /**
- * Trigger when the focus is changing
- */
- "changeFocus" : "qx.event.type.Data",
- /**
- * Triggered when selection of content has changed.
- */
- "changeSelection" : "qx.event.type.Event"
- },
-
- properties :
- {
- /**
- * Unique id of the pane
- */
- viewId : {init:""},
- /**
- * Human-readable title for this view
- */
- viewTitle : {init:"", event:"changeViewTitle"},
- /**
- * Has its own scrollable content
- */
- ownScrollable : {init: false, check:"Boolean"},
- /**
- * Data concerning the split pane
- */
- splitPaneData : {init : null, check:"Map"},
- /**
- * Map of commands definition
- * @see org.argeo.ria.event.Command
- */
- commands : {init : null, nullable:true, check:"Map"},
- /**
- * The real business content.
- */
- content : {
- init: null,
- nullable : true,
- check : "org.argeo.ria.components.IView",
- apply : "_applyContent"
- }
- },
-
- /*
- *****************************************************************************
- MEMBERS
- *****************************************************************************
- */
-
- members :
- {
- /**
- * Creates a standard GUI for the viewPane, including a container for an IView.
- */
- createGui : function(){
- this.setLayout(new qx.ui.layout.VBox());
- this.header = new qx.ui.container.Composite();
- this.header.setLayout(new qx.ui.layout.Dock());
- this.loadImage = new qx.ui.basic.Image('resource/slc/ajax-loader.gif');
- this.header.set({appearance:"app-header", height:34});
- this.headerLabel = new qx.ui.basic.Label(this.getViewTitle());
- this.header.add(this.headerLabel, {edge:"west"});
- this.addListener("changeViewTitle", function(e){
- var newTitle = e.getData();
- if(newTitle != ""){
- this.headerLabel.setContent(newTitle);
- if(e.getOldData() == ""){
- this.header.add(this.headerLabel, {edge:"west"});
- }
- }else{
- this.header.remove(this.headerLabel);
- }
- }, this);
- this.add(this.header);
- this.setDecorator(new qx.ui.decoration.Single(1,"solid","#000"));
- /*
- // Open close button of splitPane, not very useful at the moment.
- if(this.getSplitPaneData()){
- var data = this.getSplitPaneData();
- var imgName = (data.orientation=="horizontal"?"go-left":"go-bottom");
- var image = new qx.ui.basic.Image("resource/slc/"+imgName+".png");
- image.addListener("click", function(e){
- var image = e.getTarget();
- var data = this.getSplitPaneData();
- var functionDim = (data.orientation=="horizontal"?"Width":"Height");
- var objectToResize = data.object || this;
- var crtDim = objectToResize["get"+functionDim]();
- var minimize = (data.orientation=="horizontal"?"go-right":"go-top");
- var maximize = (data.orientation=="horizontal"?"go-left":"go-bottom");
- if(crtDim > data.min){
- objectToResize["set"+functionDim](data.min);
- image.setSource("resource/slc/"+minimize+".png");
- this.origDimension = crtDim;
- }else{
- if(this.origDimension){
- objectToResize["set"+functionDim](this.origDimension);
- image.setSource("resource/slc/"+maximize+".png");
- }
- }
- }, this);
- this.header.add(image,{edge:"east"});
- }
- */
- },
-
- /**
- * Get the content ViewSelection object.
- * @return {org.argeo.ria.components.ViewSelection} The view selection
- */
- getViewSelection : function(){
- if(this.getContent()){
- return this.getContent().getViewSelection();
- }
- return null;
- },
- /**
- * Checks if the pane already contains a given view, identified by its instance id
- * @param iViewId {Mixed} The instance id to check
- * @return {Boolean}
- */
- contentExists : function(iViewId){
- if(this.getContent()){
- this.empty();
- }
- return false;
- },
-
- /**
- * Sets the content of this pane.
- * @param content {org.argeo.ria.components.IView} An IView implementation
- */
- _applyContent : function(content){
- if(content == null) return;
- var addScrollable = (content.addScroll?content.addScroll():false);
- if(addScrollable){
- this.setOwnScrollable(true);
- this.scrollable = new qx.ui.container.Scroll(content);
- this.add(this.scrollable, {flex: 1});
- }else{
- this.guiContent = content;
- this.add(this.guiContent, {flex:1});
- }
- content.getViewSelection().addListener("changeSelection", function(e){
- this.fireEvent("changeSelection");
- }, this);
- },
- /**
- * Adds a graphical component too the header of the view pane.
- * It is added as "center" in the dock layout, and will override the view pane title label.
- * For example, you can add your own title, or add a switch, or buttons, etc.
- * @param component {qx.ui.core.Widget} The graphical component to add.
- */
- addHeaderComponent : function(component){
- this.header.setPadding(4);
- this.header.add(component, {edge:"center"});
- component.setTextColor("#1a1a1a");
- this.loadImage.setMargin(4);
- },
-
- /**
- * Implementation of the ILoadStatusable interface.
- * @see org.argeo.ria.components.ILoadStatusable
- * @param load {Boolean} The loading status
- */
- setOnLoad : function(load){
- if(load){
- this.header.add(this.loadImage, {edge:"east"});
- }else{
- this.header.remove(this.loadImage);
- }
- },
-
- /**
- * Call empty() method, since this pane can only handle one view.
- */
- closeCurrent : function(){
- this.empty();
- },
-
- /**
- * Removes and destroy the IView content of this viewPane.
- */
- empty: function(){
- if(this.getOwnScrollable() && this.scrollable){
- this.remove(this.scrollable);
- }else if(this.guiContent){
- this.remove(this.guiContent);
- }
- if(this.getCommands()){
- org.argeo.ria.event.CommandsManager.getInstance().removeCommands(this.getCommands());
- this.setCommands(null);
- }
- if(this.getContent()){
- this.getContent().close();
- }
- this.setViewTitle(this._defaultViewTitle);
- this.setContent(null);
- },
- /**
- * Sets a graphical indicator that this pane has the focus. A blue border.
- */
- focus : function(){
- if(this.hasFocus) return;
- this.setDecorator(new qx.ui.decoration.Single(1,"solid","#065fb2"));
- this.fireEvent("changeSelection");
- this.hasFocus = true;
- },
- /**
- * Remove a graphical focus indicator on this pane.
- */
- blur : function(){
- this.hasFocus = false;
- this.setDecorator(new qx.ui.decoration.Single(1,"solid","#000"));
- }
-
- }
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * Generic selection model associated to an IView content opened in a given ViewPane.\r
- * It contains in an array any row/data/node, and triggers changeSelection data events.\r
- * @author Charles du Jeu\r
- */\r
-qx.Class.define("org.argeo.ria.components.ViewSelection",\r
-{\r
- extend : qx.core.Object,\r
- \r
- /**\r
- * @param viewId {String} The ViewPane unique id\r
- */\r
- construct : function(viewId){\r
- this.base(arguments);\r
- this.nodes = [];\r
- this.setViewId(viewId);\r
- },\r
-\r
- properties : {\r
- /**\r
- * The viewPane unique id \r
- */\r
- viewId : {\r
- check : "String",\r
- nullable: false\r
- }\r
- },\r
- \r
- events : {\r
- /**\r
- * Triggered each time the selection changes.\r
- */\r
- "changeSelection" : "qx.event.type.Data"\r
- },\r
- \r
- /*\r
- *****************************************************************************\r
- MEMBERS\r
- *****************************************************************************\r
- */\r
-\r
- members :\r
- {\r
- /**\r
- * Empty the selection\r
- */\r
- clear : function(){\r
- this.nodes = [];\r
- this.triggerEvent();\r
- },\r
- \r
- /**\r
- * Add a row or xml node or whatever\r
- * @param node {mixed} Data to add to the selection\r
- */\r
- addNode : function(node) {\r
- this.nodes.push(node);\r
- this.triggerEvent();\r
- },\r
- \r
- /**\r
- * The number of rows/nodes selected\r
- * @return {Integer}\r
- */\r
- getCount : function() {\r
- return this.nodes.length;\r
- },\r
- \r
- /**\r
- * Returns the content of the selection \r
- * @return {Array}\r
- */\r
- getNodes : function(){\r
- return this.nodes;\r
- },\r
- \r
- /**\r
- * Creates and fire a data event changeSelection\r
- */\r
- triggerEvent : function(){\r
- this.fireDataEvent("changeSelection", this);\r
- }\r
- \r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * The main "view" manager (in a standard MVC conception) of the application.\r
- * It register various containers org.argeo.ria.components.viewPane and feed them with org.argeo.ria.components.IView implementations.\r
- * It is a singleton and can thus be called by any part of the application.\r
- * \r
- * @author Charles du Jeu\r
- */\r
-qx.Class.define("org.argeo.ria.components.ViewsManager",\r
-{\r
- type : "singleton",\r
- extend : qx.core.Object,\r
-\r
- properties : {\r
- /**\r
- * The application root (like Application.getRoot()), used to attach and show modal windows.\r
- */\r
- applicationRoot : {init : null},\r
- /**\r
- * The main container for the org.argeo.ria.components.ViewPane instances. \r
- */\r
- viewPanesContainer : {init: null},\r
- /**\r
- * Keeps the currently focused viewPane. \r
- */\r
- currentFocus : {init :null}\r
- },\r
- construct : function(){\r
- this.views = {};\r
- },\r
- members : {\r
- /**\r
- * Initialize and load a given IView implementation into a viewPane.\r
- * The IView itself is returned.\r
- * \r
- * @param classObj {Clazz} The class object to instantiate\r
- * @param viewPaneId {String} The unique ID of the view pane\r
- * @param data {Mixed} Any data provided by the opener.\r
- * @return {org.argeo.ria.components.IView}\r
- */\r
- initIViewClass: function(classObj, viewPaneId, data){\r
- var viewPane = this.getViewPaneById(viewPaneId); \r
- var iView = new classObj;\r
- iView.init(viewPane, data);\r
- var existingView = viewPane.contentExists(iView.getInstanceId()); \r
- if(existingView){\r
- delete iView;\r
- return existingView;\r
- }\r
- var commands = iView.getCommands();\r
- //viewPane.empty();\r
- if(commands){\r
- viewPane.setCommands(commands);\r
- org.argeo.ria.event.CommandsManager.getInstance().addCommands(commands, "view:"+viewPaneId, viewPaneId);\r
- }\r
- viewPane.setContent(iView);\r
- this.setViewPaneFocus(viewPane);\r
- return iView;\r
- },\r
- \r
- /**\r
- * Registers a new viewPane\r
- * @param viewPane {org.argeo.ria.components.ViewPane} The new ViewPane instance\r
- */\r
- registerViewPane : function(viewPane){\r
- this.views[viewPane.getViewId()] = viewPane;\r
- viewPane.addListener("changeSelection", function(e){\r
- var viewSelection = e.getTarget().getViewSelection();\r
- if(!viewSelection) return;\r
- org.argeo.ria.event.CommandsManager.getInstance().refreshCommands(viewSelection);\r
- });\r
- viewPane.addListener("changeFocus", function(e){\r
- this.setViewPaneFocus(e.getTarget());\r
- }, this);\r
- },\r
- /**\r
- * Sets a given viewPane as the currently focused one. Blur the others.\r
- * @param viewPane {org.argeo.ria.components.ViewPane} The viewPane (or TabbedViewPane) to focus on.\r
- */\r
- setViewPaneFocus : function(viewPane){\r
- for(var key in this.views){\r
- this.views[key].blur();\r
- }\r
- this.setCurrentFocus(viewPane);\r
- viewPane.focus(); \r
- },\r
- /**\r
- * Returns a viewPane by its unique id.\r
- * @param viewPaneId {String} The unique id\r
- * @return {org.argeo.ria.components.ViewPane}\r
- */\r
- getViewPaneById : function(viewPaneId){\r
- if(this.views[viewPaneId]) return this.views[viewPaneId];\r
- throw new Error("Cannot find view '"+viewPaneId+"'"); \r
- },\r
- /**\r
- * Returns a viewPane current viewSelection object\r
- * @param viewPaneId {String} The unique id. \r
- * @return {org.argeo.ria.components.ViewSelection}\r
- */\r
- getViewPaneSelection : function(viewPaneId){\r
- return this.getViewPaneById(viewPaneId).getViewSelection();\r
- },\r
- /**\r
- * Changes a viewPane title dynamically.\r
- * @param viewPaneId {String} ViewPane unique Id. \r
- * @param viewTitle {String} the new title for this viewPane.\r
- */\r
- setViewPaneTitle : function(viewPaneId, viewTitle){\r
- this.getViewPaneById(viewPaneId).setViewTitle(viewTitle);\r
- }\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * Package containing GUI components and main interfaces for GUI components to \r
- * be used by any argeo RIA application.\r
- *\r
- */
\ No newline at end of file
+++ /dev/null
-/**\r
- * The standard command for all actions. It registers keyboard shortcuts, centralizes \r
- * command state, callback, etc. It is defined by command definitions that can be found \r
- * in the CommandsManager. \r
- */\r
- qx.Class.define("org.argeo.ria.event.Command",\r
-{\r
- extend : qx.event.Command,\r
- implement : [org.argeo.ria.components.ILoadStatusable],\r
-\r
- properties : {\r
- /**\r
- * Unique id of the command \r
- */\r
- id : {init:""},\r
- /**\r
- * Label of the command \r
- */\r
- label : {init:""},\r
- /**\r
- * Icon of the command \r
- */\r
- icon : {init:""},\r
- /**\r
- * Weather this command is a true/false state \r
- */\r
- toggle : {init:false},\r
- /**\r
- * It toggle button, initial state \r
- */\r
- toggleInitialState : {init : false},\r
- /**\r
- * Sub menu if needed \r
- */\r
- menu : {\r
- nullable: true,\r
- event : "changeMenu"\r
- },\r
- /**\r
- * Callback associated to the submenu of the command \r
- */\r
- menuCallback : {nullable:true},\r
- /**\r
- * Context used when triggering menuCallback \r
- */\r
- menuContext : {nullable:true}\r
- },\r
- \r
- /**\r
- * @param id {String} Id of the command\r
- * @param label {String} Label of the command\r
- * @param icon {String} Icon of the command\r
- * @param shortcut {String} Keyboard Shortcut (like alt+o, ctrl+z, etc..)\r
- */\r
- construct : function(id, label, icon, shortcut){\r
- this.base(arguments, shortcut);\r
- this.setId(id);\r
- this.setLabel(label);\r
- this.setIcon(icon); \r
- this.menuClones = [];\r
- this.callbacks = {};\r
- },\r
- \r
- members :\r
- {\r
- /**\r
- * Create a Button that suits a qx.ui.menu.MenuBar linked to this command\r
- * @return {qx.ui.menu.Button}\r
- */\r
- getMenuButton : function(){\r
- if(this.getToggle()){\r
- button = new qx.ui.menu.CheckBox(this.getLabel());\r
- this._registerToggleButtonListeners(button);\r
- }else{\r
- var button = new qx.ui.menu.Button(\r
- this.getLabel(), \r
- this.getIcon(), \r
- this, \r
- this.getMenuClone()\r
- );\r
- if(this.getMenu()){\r
- this.addListener("changeMenu", function(event){\r
- button.setMenu(this.getMenuClone());\r
- }, this); \r
- }\r
- }\r
- this.addTooltip(button);\r
- return button;\r
- },\r
- \r
- /**\r
- * Create a Button that suits a qx.ui.toolbar.Toolbar part linked to this command.\r
- * @return {qx.ui.toolbar.MenuButton}\r
- */\r
- getToolbarButton : function(){\r
- var button;\r
- if(this.getMenu()){\r
- button = new qx.ui.toolbar.MenuButton(\r
- this.getLabel(),\r
- this.getIcon(), \r
- this.getMenuClone()\r
- );\r
- this.addListener("changeMenu", function(event){\r
- button.setMenu(this.getMenuClone());\r
- }, this);\r
- this.addListener("changeEnabled", function(e){\r
- this.setEnabled(e.getData());\r
- }, button);\r
- button.setEnabled(this.getEnabled());\r
- }else if(this.getToggle()){\r
- button = new qx.ui.toolbar.CheckBox(this.getLabel(), this.getIcon());\r
- if(this.getToggleInitialState()){\r
- button.setChecked(true);\r
- }\r
- this._registerToggleButtonListeners(button);\r
- }else{\r
- button = new qx.ui.toolbar.Button(\r
- this.getLabel(),\r
- this.getIcon(),\r
- this\r
- );\r
- }\r
- this.addTooltip(button);\r
- return button;\r
- },\r
- \r
- /**\r
- * Register a given callback to be shared by one or more focusable part.\r
- * @param callback {Function} A callback function\r
- * @param focusablePartId {String} A string identifiing a focusable part. At the moment, it can only be "view:viewId"\r
- */\r
- registerCallback : function(callback, focusablePartId){\r
- this.callbacks[focusablePartId] = callback;\r
- },\r
- /**\r
- * Return all the registered callbacks for this command.\r
- * @return {Map} A map of callback, viewId => callBack.\r
- */\r
- getCallbacks : function(){\r
- return this.callbacks;\r
- },\r
- /**\r
- * Remove a callback for a given focusable part.\r
- * @param focusablePartId {String} A id like "view:viewId".\r
- */\r
- removeCallback : function(focusablePartId){\r
- if(this.callbacks[focusablePartId]){\r
- delete this.callbacks[focusablePartId];\r
- }\r
- }, \r
- \r
- /**\r
- * Special tricks using UserData to enable/disable listeners to avoid loops...\r
- * @param button {qx.ui.core.Widget} toolbar Checkbox or menu Checkbox button.\r
- */\r
- _registerToggleButtonListeners : function(button){\r
- button.addListener("changeChecked", function(event){\r
- if(button.getUserData("disableListener")) return;\r
- this.setUserData("slc.command.toggleState", event.getData());\r
- this.setUserData("slc.command.toggleStateSource", button);\r
- this.fireEvent("execute");\r
- }, this);\r
- this.addListener("execute", function(event){\r
- if(this.getUserData("slc.command.toggleStateSource") == button) return;\r
- button.setUserData("disableListener", true);\r
- button.setChecked(this.getUserData("slc.command.toggleState"));\r
- button.setUserData("disableListener", false);\r
- }, this); \r
- },\r
- \r
- /**\r
- * Clones the command menu\r
- * @return {qx.ui.menu.Menu}\r
- */\r
- getMenuClone : function(){\r
- var menuClone = new qx.ui.menu.Menu();\r
- menuClone.setMinWidth(100);\r
- var submenus = this.getMenu();\r
- if(!submenus) return;\r
- for(var i=0;i<submenus.length;i++){\r
- if(submenus[i].separator){\r
- menuClone.add(new qx.ui.menu.Separator());\r
- }else{\r
- var button = new qx.ui.menu.Button(submenus[i].label, submenus[i].icon);\r
- if(submenus[i].disabled){\r
- button.setEnabled(false);\r
- }\r
- button.setUserData("commandId", submenus[i].commandId);\r
- button.addListener("execute", this.executeSubMenuCallback, this);\r
- menuClone.add(button);\r
- }\r
- }\r
- this.menuClones.push(menuClone);\r
- return menuClone;\r
- },\r
- \r
- /**\r
- * Remove all existing menus and their clones.\r
- */\r
- clearMenus : function(){\r
- if(!this.getMenu()) return;\r
- for(var i=0;i<this.menuClones.length;i++){\r
- this.menuClones[i].destroy();\r
- }\r
- this.menuClones = [];\r
- },\r
- \r
- /**\r
- * Triggers the menuCallback property in the right context.\r
- * @param event {qx.event.type.Event} The firing event.\r
- */\r
- executeSubMenuCallback : function(event){\r
- var button = event.getTarget();\r
- var callback = this.getMenuCallback();\r
- var context;\r
- if(this.getMenuContext()){\r
- if(typeof(this.getMenuContext()) == "object") context = this.getMenuContext();\r
- else if(typeof(this.getMenuContext()) == "string"){\r
- if(this.getMenuContext().split(":")[0] == "view"){\r
- var viewId = this.getMenuContext().split(":")[1];\r
- context = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
- }\r
- }\r
- }\r
- callback = qx.lang.Function.bind(callback, context || this);\r
- callback(button.getUserData("commandId")); \r
- }, \r
- /**\r
- * Adds a tooltip to a button.\r
- * @param element {qx.ui.core.Widget} The element to which the command tooltip is added. \r
- */\r
- addTooltip : function(element){\r
- if(this.getShortcut() != null){\r
- element.setToolTip(new qx.ui.tooltip.ToolTip(this.getShortcut()));\r
- } \r
- },\r
- \r
- /**\r
- * Implementation of the ILoadStatusable interface.\r
- * Sets the whole command enabled if not loading and disabled if loading.\r
- * @param status {Boolean} The loading status of the button. \r
- */\r
- setOnLoad : function(status){\r
- this.setEnabled(!status);\r
- }\r
- \r
- }\r
-});\r
+++ /dev/null
-/**\r
- * The main controller (in a standard MVC point of view) of the application. It is a singleton\r
- * thus can be called by any part of the application.\r
- * This will wire all the commands that can be defined dynamically by any IView, and add their\r
- * corresponding buttons to the application menubar and toolbars.\r
- * See the "definitions" property documentation below for more info on how to define new commands.\r
- * \r
- * @author Charles du Jeu\r
- */\r
-qx.Class.define("org.argeo.ria.event.CommandsManager",\r
-{\r
- type : "singleton",\r
- extend : qx.core.Object,\r
-\r
- construct : function(){\r
- this.base(arguments);\r
- this.setInitialDefinitions(qx.lang.Object.copy(this.getDefinitions()));\r
- this.addListener("changedCommands", this.createCommands, this);\r
- },\r
-\r
- properties : \r
- {\r
- /**\r
- * The commands definitions is a Map described as below\r
- * <pre>\r
- * {\r
- * <b>label : "",</b> \r
- * | The label of the action\r
- * \r
- * <b>icon : "",</b> \r
- * | The icon image\r
- * \r
- * <b>shortcut : "",</b>\r
- * | The keyboard shortcut, as defined in qooxdoo (Control+s, Alt+k, etc.). Warning, the letter must be lowercase.\r
- * \r
- * <b>enabled : true,</b>\r
- * | Whether it is enabled or disabled at creation\r
- * \r
- * <b>menu : ""|null,</b>\r
- * | The menu group to which the command will be added. If null, will not appear in the menus.\r
- * \r
- * <b>menuPosition : "first"|"last"</b>\r
- * | Optional : force the menu group to be first or last in the menubar.\r
- * \r
- * <b>toolbar : ""|null,</b>\r
- * | The toolbar group to which the command will be added. If null, will not appear in the toolbars.\r
- * \r
- * <b>init : function(){},</b>\r
- * | Optional function called at command creation.\r
- * | Function context : the command itself\r
- * \r
- * <b>callback : function(e){},</b>\r
- * | The main callback to be triggered when command is executed.\r
- * | Function context : the current class (not the command!)\r
- * \r
- * <b>selectionChange : function(viewPaneId, xmlNodes){},</b>\r
- * | Optional function called each time a selectionChange is detected in one of the active viewPane.\r
- * | The origin viewPaneId and the new selection as a map of nodes are passed as arguments.\r
- * | Function context : the command itself.\r
- * \r
- * <b>submenu : [{label:"", icon:"", commandId:""}, ...],</b>\r
- * | If set, the command will create a submenu, being in a menu or in the toolbar.\r
- * | The submenu is created with the various array entries, and the submenuCallback function\r
- * | will be called with the 'commandId' parameter when a submenu entry is selected.\r
- * \r
- * <b>submenuCallback : function(commandId){},</b>\r
- * | Callback if command is a submenu (cf. above).\r
- * | Function context : the current class/\r
- * \r
- * <b>command : null</b>\r
- * | For internal use only, caching the actual org.argeo.ria.event.Command object.\r
- * }\r
- * </pre>\r
- * @see org.argeo.ria.event.Command for the definition Map details. \r
- */\r
- definitions : {\r
- init : {},\r
- check : "Map"\r
- },\r
- /**\r
- * For internal use \r
- */\r
- initialDefinitions : {\r
- init : {},\r
- check : "Map"\r
- },\r
- /**\r
- * Special command definitions that are shared between focusable parts. \r
- */\r
- sharedDefinitions : {\r
- init: {},\r
- check: "Map"\r
- }\r
- },\r
-\r
- events : {\r
- /**\r
- * Triggered when the whole commands list is changed. Mainly used internally by the manager.\r
- */\r
- "changedCommands" : "qx.event.type.Event"\r
- },\r
- \r
- /*\r
- *****************************************************************************\r
- MEMBERS\r
- *****************************************************************************\r
- */\r
-\r
- members :\r
- {\r
- /**\r
- * Initialize the manager with basic definitions.\r
- * @param initDefinitions {Map} A map of commands definitions.\r
- */\r
- init : function(initDefinitions){\r
- this.setDefinitions(initDefinitions);\r
- this.setInitialDefinitions(qx.lang.Object.copy(initDefinitions));\r
- },\r
- \r
- /**\r
- * Creates all the objects (if they are not already existing) from the definitions maps.\r
- */\r
- createCommands : function(){\r
- this.menus = {};\r
- this.toolbars = {};\r
- var defs = this.getDefinitions();\r
- var shared = this.getSharedDefinitions();\r
- for(var key in defs){\r
- var definition = defs[key];\r
- var command;\r
- if(!definition.command){\r
- command = new org.argeo.ria.event.Command(key, definition.label, definition.icon, definition.shortcut);\r
- if(definition.submenu){\r
- command.setMenu(definition.submenu);\r
- if(definition.submenuCallback){\r
- command.setMenuCallback(definition.submenuCallback);\r
- command.setMenuContext((definition.callbackContext?definition.callbackContext:null));\r
- }\r
- }\r
- command.setEnabled(definition.enabled);\r
- if(definition.toggle){\r
- command.setToggle(true);\r
- if(definition.toggleInitialState){\r
- command.setToggleInitialState(definition.toggleInitialState);\r
- }\r
- }\r
- this._attachListener(command, definition.callback, definition.callbackContext);\r
- if(definition.init){\r
- var binded = qx.lang.Function.bind(definition.init, command);\r
- binded();\r
- }\r
- definition.command = command;\r
- }else{\r
- command = definition.command;\r
- if(shared[key]){\r
- \r
- for(var focusPartId in shared[key]){\r
- var sharedCommand = shared[key][focusPartId];\r
- if(sharedCommand.callback){\r
- var split = sharedCommand.callbackContext.split(":");\r
- var focusPart = split[0];\r
- var viewId = split[1]; \r
- command.registerCallback(sharedCommand.callback, split[1]); \r
- //this._attachListener(command, sharedCommand.callback, sharedCommand.callbackContext);\r
- } \r
- }\r
- \r
- }\r
- }\r
- if(definition.menu){\r
- if(!this.menus[definition.menu]) this.menus[definition.menu] = [];\r
- this.menus[definition.menu].push(definition);\r
- }\r
- if(definition.toolbar){\r
- if(!this.toolbars[definition.toolbar]) this.toolbars[definition.toolbar] = [];\r
- this.toolbars[definition.toolbar].push(command);\r
- }\r
- }\r
- this.setDefinitions(defs);\r
- },\r
- \r
- /**\r
- * Refresh the current commands status depending on the viewSelection.\r
- * @param viewSelection {org.argeo.ria.components.ViewSelection} The current ViewSelection\r
- */\r
- refreshCommands : function(viewSelection){\r
- var defs = this.getDefinitions();\r
- var shared = this.getSharedDefinitions();\r
- var xmlNodes = null;\r
- if(viewSelection.getCount() > 0){\r
- var xmlNodes = viewSelection.getNodes();\r
- }\r
- for(var key in defs){\r
- var definition = defs[key];\r
- if(!definition.selectionChange) continue;\r
- if(shared[key]){\r
- var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
- //this.debug(currentFocus);\r
- if(!currentFocus) continue;\r
- var sharedComm = shared[key][currentFocus.getViewId()];\r
- if(sharedComm && sharedComm.selectionChange){\r
- var binded = qx.lang.Function.bind(sharedComm.selectionChange, definition.command);\r
- binded(viewSelection.getViewId(), xmlNodes);\r
- }\r
- }\r
- var binded = qx.lang.Function.bind(definition.selectionChange, definition.command);\r
- binded(viewSelection.getViewId(), xmlNodes);\r
- }\r
- },\r
- \r
- /**\r
- * Record a menubar for the application\r
- * @param menuBar {qx.ui.menubar.MenuBar} The application menubar\r
- */\r
- registerMenuBar : function(menuBar){\r
- this.addListener("changedCommands", function(){\r
- this.createMenuButtons(menuBar);\r
- }, this);\r
- this.createMenuButtons(menuBar);\r
- },\r
-\r
- /**\r
- * Record a toolbar for the application\r
- * @param toolBar {qx.ui.toolbar.ToolBar} The application toolbar\r
- */\r
- registerToolBar : function(toolBar){\r
- this.addListener("changedCommands", function(){\r
- this.createToolbarParts(toolBar);\r
- }, this);\r
- this.createToolbarParts(toolBar);\r
- }, \r
- \r
- /**\r
- * Creates the real buttons and add them to the passed menuBar. \r
- * @param menuBar {qx.ui.menubar.MenuBar} The application menubar\r
- */\r
- createMenuButtons : function(menuBar){\r
- menuBar.removeAll();\r
- var anchors = {};\r
- for(var key in this.menus){\r
- var menu = new qx.ui.menu.Menu();\r
- var button = new qx.ui.menubar.Button(key, null, menu);\r
- var anchorDetected = false;\r
- for(var i=0; i<this.menus[key].length;i++){\r
- var def = this.menus[key][i]; \r
- menu.add(def.command.getMenuButton());\r
- if(!anchorDetected && def.menuPosition){\r
- anchorDetected = true;\r
- anchors[def.menuPosition] = button;\r
- }\r
- }\r
- if(!anchorDetected){\r
- menuBar.add(button);\r
- }\r
- }\r
- // Add specific anchored buttons\r
- if(anchors.first) menuBar.addAt(anchors.first, 0);\r
- else if(anchors.last){\r
- menuBar.add(anchors.last);\r
- }\r
- },\r
- \r
- /**\r
- * Creates the real buttons and add them to the passed toolbar. \r
- * @param toolbar {qx.ui.toolbar.ToolBar} The application toolbar\r
- */\r
- createToolbarParts : function(toolbar){\r
- toolbar.removeAll();\r
- for(var key in this.toolbars){\r
- var tPart = new qx.ui.toolbar.Part();\r
- toolbar.add(tPart);\r
- this.toolbars[key].map(function(command){\r
- tPart.add(command.getToolbarButton());\r
- });\r
- }\r
- },\r
- /**\r
- * Creates a context menu from an array of commands ids.\r
- * @param commandIdsArray {Array} An array of string\r
- * @return {qx.ui.menu.Menu}\r
- */\r
- createMenuFromIds : function(commandIdsArray){\r
- var defs = this.getDefinitions();\r
- var contextMenu = new qx.ui.menu.Menu();\r
- for(var i=0;i<commandIdsArray.length;i++){\r
- var definition = defs[commandIdsArray[i]];\r
- if(definition){\r
- var command = definition.command;\r
- contextMenu.add(command.getMenuButton());\r
- }\r
- }\r
- return contextMenu;\r
- },\r
- /**\r
- * Add a new set of commands definitions. See the definitions property of this class.\r
- * @param definitions {Map} a set of commands definitions.\r
- * @param callbackContext {qx.ui.core.Object} The context used inside the commands callbacks.\r
- * @param focusablePartId {String} A string identifying the associated focusable part, like "view:viewId". \r
- */\r
- addCommands : function(definitions, callbackContext, focusablePartId){\r
- var crtDefs = this.getDefinitions(); \r
- for(var key in definitions){\r
- if(callbackContext) definitions[key]['callbackContext'] = callbackContext;\r
- if(crtDefs[key] && definitions[key]['shared']){\r
- if(focusablePartId) {\r
- definitions[key]['focusablePartId'] = focusablePartId;\r
- if(!this.getSharedDefinitions()[key]){\r
- this.getSharedDefinitions()[key] = {};\r
- }\r
- this.getSharedDefinitions()[key][focusablePartId] = definitions[key];\r
- }\r
- \r
- }else{\r
- crtDefs[key] = definitions[key];\r
- }\r
- }\r
- this.setDefinitions(crtDefs);\r
- this.fireEvent("changedCommands");\r
- },\r
- /**\r
- * Removes a whole set of commands by their definitions maps.\r
- * @param definitions {Map} a set of commands definitions\r
- * @param focusablePartId {String} A string identifying the associated focusable part, like "view:viewId". \r
- */\r
- removeCommands : function(definitions, focusablePartId){\r
- var crtDefs = this.getDefinitions();\r
- var initDefs = this.getInitialDefinitions();\r
- var sharedDefs = this.getSharedDefinitions();\r
- for(var key in definitions){\r
- if(!crtDefs[key]) continue;\r
- if(initDefs[key]){\r
- crtDefs[key] = initDefs[key];\r
- }else{\r
- if(sharedDefs[key] && sharedDefs[key][focusablePartId]){\r
- crtDefs[key].command.removeCallback(focusablePartId);\r
- delete sharedDefs[key][focusablePartId];\r
- }else{\r
- delete crtDefs[key];\r
- }\r
- }\r
- }\r
- this.setDefinitions(crtDefs);\r
- this.fireEvent("changedCommands");\r
- },\r
- /**\r
- * Executes a command by its id.\r
- * @param commandId {String} The command id.\r
- */\r
- executeCommand : function(commandId){\r
- var defs = this.getDefinitions();\r
- if(defs[commandId] && defs[commandId].command.getEnabled()){\r
- defs[commandId].command.execute();\r
- }\r
- },\r
- /**\r
- * Retrieves a command by its id.\r
- * @param commandId {String} The command id.\r
- */\r
- getCommandById : function(commandId){\r
- var defs = this.getDefinitions();\r
- if(defs[commandId] && defs[commandId].command){\r
- return defs[commandId].command;\r
- } \r
- },\r
- /**\r
- * Add a standard context menu to a toolbar for button look and feel (show icon, text, both).\r
- * @param toolbar {qx.ui.toolbar.ToolBar} The toolbar\r
- */\r
- addToolbarContextMenu : function(toolbar){\r
- var menu = new qx.ui.menu.Menu();\r
- var icon = new qx.ui.menu.RadioButton("Show Icons");\r
- icon.setValue("icon");\r
- var text = new qx.ui.menu.RadioButton("Show Text");\r
- text.setValue("label");\r
- var both = new qx.ui.menu.RadioButton("Show Both");\r
- both.setValue("both");\r
- var mgr = new qx.ui.form.RadioGroup(icon, text, both);\r
- menu.add(icon);\r
- menu.add(text);\r
- menu.add(both);\r
- mgr.setSelected(both);\r
- toolbar.setContextMenu(menu); \r
- mgr.addListener("changeValue", function(e){\r
- this.setShow(e.getData());\r
- }, toolbar);\r
- \r
- },\r
- /**\r
- * Attach a listener to a command, with a context.\r
- * The context can be an object, a string like "view:viewId" or null. \r
- * If a string, the viewPaneId content will be retrieved at runtime. If null, "this" will be used\r
- * as default context.\r
- * @param command {org.argeo.ria.event.Command} The command\r
- * @param callback {Function} The function to execute\r
- * @param callbackContext {Object|String} The context in which the function will be executed. \r
- */\r
- _attachListener:function(command, callback, callbackContext){ \r
- if(!callbackContext){\r
- command.addListener("execute", callback, this);\r
- return;\r
- }\r
- if(typeof(callbackContext) == "object"){\r
- command.addListener("execute", callback, callbackContext);\r
- return;\r
- } \r
- if(typeof(callbackContext) == "string"){\r
- \r
- var split = callbackContext.split(":");\r
- var focusPart = split[0];\r
- var viewId = split[1];\r
- if(command.getCallbacks()[viewId]) return;\r
- command.registerCallback(callback, split[1]);\r
- command.addListener("execute", function(event){\r
- var target = event.getTarget();\r
- var callbacks = target.getCallbacks();\r
- if(qx.lang.Object.getLength(callbacks) == 0) return;\r
- var view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
- if(qx.lang.Object.getLength(callbacks) == 1){\r
- var binded = qx.lang.Function.bind(callbacks[qx.lang.Object.getKeys(callbacks)[0]], view);\r
- binded(event);\r
- return;\r
- }\r
- var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
- if(currentFocus && currentFocus.getViewId() && callbacks[currentFocus.getViewId()]){\r
- var currentViewId = currentFocus.getViewId();\r
- view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(currentViewId).getContent();\r
- var binded = qx.lang.Function.bind(callbacks[currentFocus.getViewId()], view);\r
- binded(event);\r
- return;\r
- }\r
- });\r
- \r
- \r
- /*\r
- if(callbackContext.split(":")[0] == "view"){\r
- var viewId = callbackContext.split(":")[1];\r
- command.addListener("execute", function(event){\r
- if(event.getTarget().getCheckFocusAtCallback()){\r
- var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
- if(currentFocus.getViewId() != viewId) return;\r
- }\r
- var view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
- var binded = qx.lang.Function.bind(callback, view);\r
- binded(event);\r
- });\r
- }else{\r
- command.addListener("execute", callback, callbackContext);\r
- }\r
- */\r
- }\r
- }\r
- }\r
-});\r
+++ /dev/null
-/**\r
- * This can be triggered at the end of a IO Request. In that case, it contains\r
- * a data type as an identifier, and the request response itself.\r
- * Can be used this way to listen for data changes from various parts of the application.\r
- */\r
-qx.Class.define("org.argeo.ria.event.ReloadEvent",\r
-{\r
- extend : qx.event.type.Event,\r
- \r
- construct : function(){\r
- this.base(arguments);\r
- },\r
- members : {\r
- /**\r
- * Basic initialisation of event\r
- * @param dataType {String} a unique data identifier\r
- * @param content {mixed} the retrieved data\r
- */\r
- init: function(dataType, content){\r
- this.setDataType(dataType);\r
- this.setContent(content); \r
- }\r
- },\r
- properties :{\r
- /**\r
- * A unique data identifier \r
- */\r
- dataType : {init:null, check:"String"},\r
- /**\r
- * The new data content \r
- */\r
- content : {init:null}\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * Event utilities, this package is the "controller" part of the application (in an classic MVC view).\r
- * The commandsManager singleton is in charge of wiring all the commands inside the application.\r
- *\r
- */
\ No newline at end of file
+++ /dev/null
-/**\r
- * A standard client for sending/receiving JMS message.\r
- * It is based on ActiveMQ Ajax implementation.\r
- */\r
-qx.Class.define("org.argeo.ria.remote.JmsClient", {\r
-\r
- type : "singleton",\r
- extend : qx.core.Object,\r
- \r
- construct : function(){\r
- this.base(arguments);\r
- },\r
- members : {\r
- // The URI of the MessageListenerServlet\r
- uri : '../amq', \r
-\r
- // Polling. Set to true (default) if waiting poll for messages is needed\r
- poll : true,\r
- pollTimeout : 25,\r
- interrupt : false,\r
-\r
- // Poll delay. if set to positive integer, this is the time to wait in ms before\r
- // sending the next poll after the last completes.\r
- _pollDelay : 0,\r
-\r
- _first : true,\r
- /**\r
- * Trigger at each poll event.\r
- * @param first {Boolean} Whether it is the first event to be triggered. \r
- */\r
- _pollEvent : function(first) {},\r
- _handlers : new Array(),\r
-\r
- /**\r
- * Parses the XML response to a message POST.\r
- * @param response {qx.io.remote.Response} The query response\r
- */\r
- _messageHandler : function(response) {\r
- var doc = response.getContent(); \r
- var messages = org.argeo.ria.util.Element.selectNodes(doc, "//response");\r
- for(var i=0;i<messages.length;i++){\r
- var id = messages[i].getAttribute("id");\r
- if(id && this._handlers[id]){\r
- this._handlers[id](messages[i]);\r
- }\r
- }\r
- },\r
- \r
- /**\r
- * Parses the empty response of a poll GET query.\r
- * @param response {qx.io.remote.Response} The query response\r
- */\r
- _pollHandler : function(response) {\r
- try {\r
- this._messageHandler(response);\r
- this._pollEvent(this._first);\r
- this._first = false;\r
- } catch (e) {\r
- alert(e);\r
- }\r
-\r
- if (this._pollDelay > 0)\r
- qx.event.Timer.once(this._sendPoll, this, this._pollDelay);\r
- else\r
- this._sendPoll();\r
- },\r
-\r
- /**\r
- * Send a poll query : GET query and no paramter at all. \r
- * @param request {qx.io.remote.Request} A request object\r
- */\r
- _sendPoll : function(request) {\r
- if(this.interrupt) return;\r
- var request = new qx.io.remote.Request(this.uri, "GET", "application/xml");\r
- request.setTimeout(this.pollTimeout*1000+5000);\r
- request.addListener("completed", this._pollHandler, this);\r
- request.send();\r
- },\r
-\r
- /**\r
- * Add a function that gets called on every poll response, after all received\r
- * messages have been handled. The poll handler is past a boolean that indicates\r
- * if this is the first poll for the page.\r
- * \r
- * @param func {Function} The handler to be called. \r
- */\r
- addPollHandler : function(func) {\r
- var old = this._pollEvent;\r
- this._pollEvent = function(first) {\r
- old(first);\r
- func(first);\r
- }\r
- },\r
-\r
- /**\r
- * Send a JMS message to a destination (eg topic://MY.TOPIC). \r
- * Message should be xml or encoded xml content.\r
- * \r
- * @param destination {String} The topic destination\r
- * @param message {String} XML encoded message\r
- * @param properties {Map} A map of additional parameters to add to the query.\r
- */\r
- sendMessage : function(destination, message, properties) {\r
- this._sendMessage(destination, message, 'send', properties);\r
- },\r
-\r
- /**\r
- * Listen on a channel or topic. handler must be a function taking a message arguement\r
- * @param id {String} A unique identifier for this handler\r
- * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
- * @param handler {Function} The handler to trigger when receiving a message \r
- * @param context {Object} An object to bind on the handler.\r
- */\r
- addListener : function(id, destination, handler, context) {\r
- this._handlers[id] = qx.lang.Function.bind(handler, context);\r
- this._sendMessage(destination, id, 'listen');\r
- },\r
-\r
- /**\r
- * Remove Listener from channel or topic.\r
- * @param id {String} identifier of the handler to remove.\r
- * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
- */ \r
- removeListener : function(id, destination) {\r
- this._handlers[id] = null;\r
- this._sendMessage(destination, id, 'unlisten');\r
- },\r
- \r
- /**\r
- * Send a message of a given type.\r
- * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
- * @param message {String} XML encoded message\r
- * @param type {String} The JMS-Type of message (listen, unlisten, send).\r
- * @param properties {Map} A map of additional parameters to add to the query.\r
- */\r
- _sendMessage : function(destination, message, type, properties) {\r
- var req = new qx.io.remote.Request(this.uri, "POST", "text/plain");\r
- if(!properties) properties = {}; \r
- properties["destination"] = destination;\r
- properties["message"] = message;\r
- properties["type"] = type;\r
- var vParametersList = [];\r
- \r
- for (var vId in properties)\r
- {\r
- var value = properties[vId];\r
- if (value instanceof Array)\r
- {\r
- for (var i=0; i<value.length; i++)\r
- {\r
- vParametersList.push(encodeURIComponent(vId) +\r
- "=" +\r
- encodeURIComponent(value[i]));\r
- }\r
- }\r
- else\r
- {\r
- vParametersList.push(encodeURIComponent(vId) +\r
- "=" +\r
- encodeURIComponent(value));\r
- }\r
- } \r
- if (vParametersList.length > 0)\r
- {\r
- req.setData(vParametersList.join("&"));\r
- }\r
- \r
- //req.addListener("completed", this.endBatch, this);\r
- req.send();\r
- },\r
-\r
- /**\r
- * Starts a poll on the JMS server.\r
- */\r
- startPolling : function() {\r
- if (this.poll){\r
- this.interrupt = false;\r
- var req = new qx.io.remote.Request(this.uri, "GET", "application/xml");\r
- req.setParameter("timeout", "10");\r
- req.addListener("completed", this._pollHandler, this);\r
- req.send();\r
- }\r
- },\r
- \r
- /**\r
- * Stops polling the JMS server.\r
- */\r
- stopPolling : function(){\r
- this.interrupt = true;\r
- }\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * A management class for all request sent to the server\r
- * Basically, to access the server, always get a new Request object from this class.\r
- * It will then trigger various user-interface events during the Request lifecycle. \r
- * \r
- * For the moment, it's about the "Stop" button command, handling any passed ILoadStatusable states, \r
- * and logging the Request status/errors.\r
- * \r
- * @author Charles du Jeu\r
- */\r
-qx.Class.define("org.argeo.ria.remote.RequestManager",\r
-{\r
- type : "singleton",\r
- extend : qx.core.Object,\r
- \r
- events : {\r
- /**\r
- * Triggered on the user demand at the end of the Request \r
- */\r
- "reload" : "org.argeo.ria.event.ReloadEvent"\r
- },\r
- \r
- construct : function(){\r
- this.base(arguments); \r
- },\r
- \r
- members : {\r
- /**\r
- * Sets the unique "stop" command of the application.\r
- * @param stopCommand {org.argeo.ria.event.Command} The command\r
- */\r
- setStopCommand : function(stopCommand){\r
- this.command = stopCommand;\r
- },\r
- \r
- /**\r
- * Creates a Request and handle various parts of its lifecycle.\r
- * @see org.argeo.ria.event.ReloadEvent\r
- * @see org.argeo.ria.components.ILoadStatusable\r
- * \r
- * @param url {String} The server url\r
- * @param method {String} Connexion method (POST, GET, etc.)\r
- * @param responseType {String} Expected response mime type (application/xml, etc...).\r
- * @param fireReloadEventType {String} On user-demand, if this parameter is not null, a org.argeo.ria.event.ReloadEvent will be triggered when the request is completed. \r
- * @param iLoadStatusables {Array} An array of ILoadStatusable implementations that need to be updated by the Request state (loading/ended).\r
- * @return {qx.io.remote.Request}\r
- */\r
- getRequest : function(url, method, responseType, fireReloadEventType, iLoadStatusables){\r
- var request = new qx.io.remote.Request(url, method, responseType);\r
- if(iLoadStatusables){\r
- request.setUserData("iLoadStatusables", iLoadStatusables);\r
- }\r
- if(fireReloadEventType){\r
- request.addListener("completed", function(response){\r
- this.fireReloadEvent(fireReloadEventType, response.getContent());\r
- }, this);\r
- }\r
- this.enableCommand(request);\r
- request.addListener("timeout", this.requestTerminated, this);\r
- request.addListener("failed", this.requestTerminated, this);\r
- request.addListener("aborted", this.requestTerminated, this);\r
- request.addListener("completed", this.requestCompleted, this); \r
- return request;\r
- }, \r
- \r
- /**\r
- * Creates a ReloadEvent and fire it.\r
- * @param dataType {String} The data type \r
- * @param content {mixed} The content of the request response.\r
- */\r
- fireReloadEvent : function(dataType, content){\r
- this.fireEvent("reload", org.argeo.ria.event.ReloadEvent, [dataType, content]); \r
- },\r
- \r
- /**\r
- * Triggered when request is created\r
- * @param e {qx.event.type.Event} The event\r
- */\r
- requestCreated : function(e){\r
- var request = e.getTarget();\r
- this.enableCommand(request);\r
- },\r
- \r
- /**\r
- * Triggered when request is completed normally\r
- * @param e {qx.event.type.Event} The event\r
- */\r
- requestCompleted : function(e){\r
- var request = e.getTarget();\r
- this.disableCommand(request);\r
- },\r
- \r
- /**\r
- * Triggered when request is completed abnormally\r
- * @param e {qx.event.type.Event} The event\r
- */\r
- requestTerminated : function(e){\r
- var request = e.getTarget();\r
- var errorType = e.getType();\r
- this.disableCommand(request);\r
- var message = "";\r
- if(errorType == "aborted"){\r
- message = "Request aborted by user";\r
- }else if(errorType == "failed"){\r
- message = "Request failed!";\r
- }else if(errorType == "timeout"){\r
- message = "Request timed out!";\r
- }\r
- this.error(message);\r
- },\r
- \r
- /**\r
- * Triggered by a request creation. Update the GUI parts according to its status. \r
- * @param request {qx.io.remote.Request} The current Request \r
- */\r
- disableCommand : function(request){\r
- this.command.setEnabled(false);\r
- if(request.getUserData("iLoadStatusables")){\r
- this.updateGuiParts(request.getUserData("iLoadStatusables"), false);\r
- }\r
- var listener = request.getUserData("listener");\r
- if(listener){\r
- this.command.removeListener("execute", listener);\r
- }\r
- },\r
- \r
- /**\r
- * Triggered by a request ending. Update the GUI parts according to its status. \r
- * @param request {qx.io.remote.Request} The current Request \r
- */\r
- enableCommand : function(request){\r
- this.command.setEnabled(true);\r
- if(request.getUserData("iLoadStatusables")){\r
- this.updateGuiParts(request.getUserData("iLoadStatusables"), true);\r
- }\r
- qx.ui.core.queue.Manager.flush();\r
- var listener = request.abort;\r
- request.setUserData("listener", listener);\r
- this.command.addListener("execute", listener, request);\r
- },\r
- \r
- /**\r
- * Update the ILoadStatusable implementations\r
- * @param iLoadStatusables {Array} An array of ILoadStatusable \r
- * @param loadStatus {Boolean} The current status of a request \r
- */\r
- updateGuiParts : function(iLoadStatusables, loadStatus){\r
- for(var i=0;i<iLoadStatusables.length;i++){\r
- if(qx.Class.implementsInterface(qx.Class.getByName(iLoadStatusables[i].classname), org.argeo.ria.components.ILoadStatusable)){\r
- iLoadStatusables[i].setOnLoad(loadStatus);\r
- }else{\r
- this.debug("Does not implement the ILoadStatusable interface! GUIPART type : "+ iLoadStatusables[i].classname);\r
- }\r
- }\r
- }\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * Communication with server package. \r
- *\r
- */
\ No newline at end of file
+++ /dev/null
-/* ************************************************************************\r
-\r
- Copyright:\r
-\r
- License:\r
-\r
- Authors:\r
-\r
-************************************************************************ */\r
-\r
-/**\r
- * This class demonstrates how to define unit tests for your application.\r
- *\r
- * Execute <code>generate.py test</code> to generate a testrunner application \r
- * and open it from <tt>test/index.html</tt>\r
- *\r
- * The methods that contain the tests are instance methods with a \r
- * <code>test</code> prefix. You can create an arbitrary number of test \r
- * classes like this one. They can be organized in a regular class hierarchy, \r
- * i.e. using deeper namespaces and a corresponding file structure within the \r
- * <tt>test</tt> folder.\r
- */\r
-qx.Class.define("org.argeo.ria.test.DemoTest",\r
-{\r
- extend : qx.dev.unit.TestCase,\r
-\r
- members :\r
- {\r
- /*\r
- ---------------------------------------------------------------------------\r
- TESTS\r
- ---------------------------------------------------------------------------\r
- */\r
- \r
- /**\r
- * Here are some simple tests\r
- */\r
- testSimple : function()\r
- {\r
- this.assertEquals(4, 3+1, "This should never fail!");\r
- this.assertFalse(false, "Can false be true?!");\r
- },\r
-\r
- /**\r
- * Here are some more advanced tests\r
- */\r
- testAdvanced: function () \r
- {\r
- var a = 3;\r
- var b = a;\r
- this.assertIdentical(a, b, "A rose by any other name is still a rose");\r
- this.assertInRange(3, 1, 10, "You must be kidding, 3 can never be outside [1,10]!");\r
- }\r
- }\r
-});\r
+++ /dev/null
-/**\r
- * Unit tests to be executed by the TestRunner application.\r
- *\r
- */
\ No newline at end of file
+++ /dev/null
-/**\r
- * Cross browser XML Element API\r
- * \r
- * Overrides the Qooxdoo qx.xml.Element to handle the namespace prefixes\r
- *\r
- * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/81f3de54-3b79-46dc-8e01-73ca2d94cdb5.asp\r
- * http://developer.mozilla.org/en/docs/Parsing_and_serializing_XML\r
- */\r
-qx.Class.define("org.argeo.ria.util.Element",\r
-{\r
- \r
- statics :\r
- {\r
- \r
- DEFAULT_NAMESPACE_MAP : null,\r
- \r
- /**\r
- * Selects the first XmlNode that matches the XPath expression.\r
- *\r
- * @param element {Element | Document} root element for the search\r
- * @param query {String} XPath query\r
- * @param NSMap (Object) A map matching namespace prefixes to namespace URIS;\r
- * @return {Element} first matching element\r
- * @signature function(element, query, NSMap)\r
- */\r
- selectSingleNode : qx.core.Variant.select("qx.client",\r
- {\r
- "mshtml|opera": function(element, query, NSMap) {\r
- NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
- if(NSMap){ \r
- var namespaces = [];\r
- var i=0;\r
- for(var prefix in NSMap){ \r
- namespaces[i] = 'xmlns:'+prefix+'="'+NSMap[prefix]+'"';\r
- i++;\r
- }\r
- var doc = element.ownerDocument || element;\r
- doc.setProperty('SelectionNamespaces', namespaces.join(" "));\r
- }\r
- try{\r
- return element.selectSingleNode(query);\r
- }catch(err){}\r
- },\r
-\r
- "default": function(element, query, NSMap)\r
- {\r
- NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
- if(!this.__xpe) {\r
- this.__xpe = new XPathEvaluator();\r
- }\r
-\r
- var xpe = this.__xpe;\r
-\r
- try {\r
- var resolver;\r
- if(NSMap){\r
- resolver = function(prefix){\r
- return NSMap[prefix] || null;\r
- }\r
- }else{\r
- resolver = xpe.createNSResolver(element);\r
- }\r
- //return xpe.evaluate(query, element, xpe.createNSResolver(element), XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\r
- return xpe.evaluate(query, element, resolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\r
- } catch(err) {\r
- throw new Error("selectSingleNode: query: " + query + ", element: " + element + ", error: " + err);\r
- }\r
- }\r
- }),\r
-\r
-\r
- /**\r
- * Selects a list of nodes matching the XPath expression.\r
- *\r
- * @param element {Element | Document} root element for the search\r
- * @param query {String} XPath query\r
- * @param NSMap {Map} Mapping between namespaces prefixes and URI.\r
- * @return {Element[]} List of matching elements\r
- * @signature function(element, query, NSMap)\r
- */\r
- selectNodes : qx.core.Variant.select("qx.client",\r
- {\r
- "mshtml|opera": function(element, query, NSMap) {\r
- NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
- if(NSMap){\r
- var namespaces = [];\r
- var i=0;\r
- for(var prefix in NSMap){ \r
- namespaces[i] = 'xmlns:'+prefix+'="'+NSMap[prefix]+'"';\r
- i++;\r
- }\r
- var doc = element.ownerDocument || element;\r
- doc.setProperty('SelectionNamespaces', namespaces.join(" "));\r
- } \r
- return element.selectNodes(query);\r
- },\r
-\r
- "default": function(element, query, NSMap)\r
- {\r
- NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
- var xpe = this.__xpe;\r
-\r
- if(!xpe) {\r
- this.__xpe = xpe = new XPathEvaluator();\r
- }\r
-\r
- try {\r
- var resolver;\r
- if(NSMap){\r
- resolver = function(prefix){\r
- return NSMap[prefix] || null;\r
- }\r
- }else{\r
- resolver = xpe.createNSResolver(element);\r
- } \r
- //var result = xpe.evaluate(query, element, xpe.createNSResolver(element), XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\r
- var result = xpe.evaluate(query, element, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\r
- } catch(err) {\r
- throw new Error("selectNodes: query: " + query + ", element: " + element + ", error: " + err);\r
- }\r
-\r
- var nodes = [];\r
- for (var i=0; i<result.snapshotLength; i++) {\r
- nodes[i] = result.snapshotItem(i);\r
- }\r
-\r
- return nodes;\r
- }\r
- }),\r
-\r
-\r
- /**\r
- * Returns a list of elements with the given tag name belonging to the given namespace (http://developer.mozilla.org/en/docs/DOM:element.getElementsByTagNameNS).\r
- *\r
- * @param element {Element | Document} the element from where the search should start.\r
- * Note that only the descendants of this element are included in the search, not the node itself.\r
- * @param namespaceURI {var} is the namespace URI of elements to look for . For example, if you need to look\r
- * for XHTML elements, use the XHTML namespace URI, <tt>http://www.w3.org/1999/xhtml</tt>.\r
- * @param tagname {String} the tagname to look for\r
- * @return {Element[]} a list of found elements in the order they appear in the tree.\r
- * @signature function(element, namespaceURI, tagname)\r
- */\r
- getElementsByTagNameNS : qx.core.Variant.select("qx.client",\r
- { \r
- "mshtml": function(element, namespaceURI, tagname)\r
- {\r
- var doc = element.ownerDocument || element;\r
-\r
- doc.setProperty("SelectionLanguage", "XPath");\r
- doc.setProperty("SelectionNamespaces", "xmlns:ns='" + namespaceURI + "'");\r
-\r
- return qx.xml.Element.selectNodes(element, 'descendant-or-self::ns:' + tagname);\r
- },\r
-\r
- "default": function(element, namespaceURI, tagname) {\r
- return element.getElementsByTagNameNS(namespaceURI, tagname);\r
- }\r
- }),\r
-\r
-\r
- /**\r
- * Selects the first XmlNode that matches the XPath expression and returns the text content of the element\r
- *\r
- * @param element {Element|Document} root element for the search\r
- * @param query {String} XPath query\r
- * @param NSMap {Object} Mapping between NS prefix / uri\r
- * @return {String} the joined text content of the found element or null if not appropriate.\r
- * @signature function(element, query)\r
- */\r
- getSingleNodeText : function(element, query, NSMap)\r
- {\r
- NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
- var node = org.argeo.ria.util.Element.selectSingleNode(element, query, NSMap);\r
- return qx.dom.Node.getText(node);\r
- }\r
- }\r
-});\r
+++ /dev/null
-/* ************************************************************************\r
-\r
- qooxdoo - the new era of web development\r
-\r
- http://qooxdoo.org\r
-\r
- Copyright:\r
- 2007 Derrell Lipman\r
-\r
- License:\r
- LGPL: http://www.gnu.org/licenses/lgpl.html\r
- EPL: http://www.eclipse.org/org/documents/epl-v10.php\r
- See the LICENSE file in the project's top-level directory for details.\r
-\r
- Authors:\r
- * Derrell Lipman (derrell)\r
- * David Perez Carmona (david-perez)\r
-\r
-************************************************************************ */\r
-\r
-/* ************************************************************************\r
-\r
-#require(qx.theme.Modern)\r
-#require(qx.theme.Classic)\r
-#require(qx.log.Logger)\r
-\r
-************************************************************************ */\r
-\r
-/**\r
- * A data cell renderer for the tree column of a simple tree\r
- */\r
-qx.Class.define("org.argeo.ria.util.TreeDataCellRenderer",\r
-{\r
- extend : qx.ui.treevirtual.SimpleTreeDataCellRenderer,\r
-\r
-\r
- construct : function()\r
- {\r
- this.base(arguments);\r
-\r
- this.__am = qx.util.AliasManager.getInstance();\r
- this.__rm = qx.util.ResourceManager;\r
- this.__tm = qx.theme.manager.Appearance.getInstance();\r
-\r
- // Base URL used for indentation\r
- this.BLANK = this.__rm.toUri(this.__am.resolve("static/blank.gif"));\r
- },\r
-\r
-\r
- statics :\r
- {\r
- __icon : { }\r
- },\r
-\r
-\r
-\r
-\r
- /*\r
- *****************************************************************************\r
- MEMBERS\r
- *****************************************************************************\r
- */\r
-\r
- members :\r
- {\r
- // overridden\r
- _getCellStyle : function(cellInfo)\r
- {\r
- var node = cellInfo.value;\r
-\r
- // Return the style for the div for the cell. If there's cell-specific\r
- // style information provided, append it.\r
- var html =\r
- this.base(arguments, cellInfo) +\r
- (node.cellStyle ? node.cellStyle + ";" : "");\r
- return html;\r
- },\r
-\r
- // overridden\r
- _getContentHtml : function(cellInfo)\r
- {\r
- var html = "";\r
-\r
- // Horizontal position\r
- var pos = 0;\r
-\r
- // If needed, add extra content before indentation\r
- var extra = this._addExtraContentBeforeIndentation(cellInfo, pos);\r
- html += extra.html;\r
- pos = extra.pos;\r
-\r
- // Add the indentation (optionally with tree lines)\r
- var indentation = this._addIndentation(cellInfo, pos);\r
- html += indentation.html\r
- pos = indentation.pos;\r
-\r
- // If needed, add extra content before icon\r
- extra = this._addExtraContentBeforeIcon(cellInfo, pos);\r
- html += extra.html;\r
- pos = extra.pos;\r
-\r
- // Add the node icon\r
- var icon = this._addIcon(cellInfo, pos);\r
- html += icon.html;\r
- pos = icon.pos;\r
-\r
- // If needed, add extra content before label\r
- extra = this._addExtraContentBeforeLabel(cellInfo, pos);\r
- html += extra.html;\r
- pos = extra.pos;\r
-\r
- // Add the node's label\r
- html += this._addLabel(cellInfo, pos);\r
-\r
- return html;\r
- },\r
-\r
- /**\r
- * Add an image to the tree. This might be a visible icon or it may be\r
- * part of the indentation.\r
- *\r
- * @param imageInfo {Map}\r
- * How to display the image. It optionally includes any of the\r
- * following:\r
- * <dl>\r
- * <dt>position {Map}</dt>\r
- * <dd>\r
- * If provided, a div is created to hold the image. The div's top,\r
- * right, bottom, left, width, and/or height may be specified with\r
- * members of this map. Each is expected to be an integer value.\r
- * </dd>\r
- * <dt>imageWidth, imageHeight</dt>\r
- * <dd>\r
- * The image's width and height. These are used only if both are\r
- * specified.\r
- * </dd>\r
- * </dl>\r
- *\r
- * @return {String}\r
- * The html for this image, possibly with a surrounding div (see\r
- * 'position', above).\r
- */\r
- _addImage : function(imageInfo)\r
- {\r
- var html = [];\r
-\r
- // Resolve the URI\r
- var source = this.__rm.toUri(this.__am.resolve(imageInfo.url));\r
-\r
- // If we've been given positioning attributes, enclose image in a div\r
- if (imageInfo.position)\r
- {\r
- var pos = imageInfo.position;\r
-\r
- html.push('<div style="position:absolute;');\r
-\r
- if (!qx.core.Variant.isSet("qx.client", "mshtml"))\r
- {\r
- html.push(qx.bom.element.BoxSizing.compile("content-box"));\r
- }\r
-\r
- if (pos.top !== undefined)\r
- {\r
- html.push('top:' + pos.top + 'px;');\r
- }\r
-\r
- if (pos.right !== undefined)\r
- {\r
- html.push('right:' + pos.right + 'px;');\r
- }\r
-\r
- if (pos.bottom !== undefined)\r
- {\r
- html.push('bottom:' + pos.bottom + 'px;');\r
- }\r
-\r
- if (pos.left !== undefined)\r
- {\r
- html.push('left:' + pos.left + 'px;');\r
- }\r
-\r
- if (pos.width !== undefined)\r
- {\r
- html.push('width:' + pos.width + 'px;');\r
- }\r
-\r
- if (pos.height !== undefined)\r
- {\r
- html.push('height:' + pos.height + 'px;');\r
- }\r
-\r
- html.push('">');\r
- }\r
-\r
- // Don't use an image tag. They render differently in Firefox and IE7\r
- // even if both are enclosed in a div specified as content box. Instead,\r
- // add the image as the background image of a div.\r
- html.push('<div style="');\r
- html.push('background-image:url(' + source + ');');\r
- html.push('background-repeat:no-repeat;');\r
-\r
- if (imageInfo.imageWidth && imageInfo.imageHeight)\r
- {\r
- html.push(\r
- ';width:' +\r
- imageInfo.imageWidth +\r
- 'px' +\r
- ';height:' +\r
- imageInfo.imageHeight +\r
- 'px');\r
- }\r
-\r
- var tooltip = imageInfo.tooltip;\r
-\r
- if (tooltip != null)\r
- {\r
- html.push('" title="' + tooltip);\r
- }\r
-\r
- html.push('"> </div>');\r
-\r
- if (imageInfo.position)\r
- {\r
- html.push('</div>');\r
- }\r
-\r
- return html.join("");\r
- },\r
-\r
-\r
- /**\r
- * Add the indentation for this node of the tree.\r
- *\r
- * The indentation optionally includes tree lines. Whether tree lines are\r
- * used depends on (a) the properties 'useTreeLines' and\r
- * 'excludeFirstLevelTreelines' within this class; and (b) the widget\r
- * theme in use (some themes don't support tree lines).\r
- *\r
- * @param cellInfo {Map} The information about the cell.\r
- * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
- *\r
- * @param pos {Integer}\r
- * The position from the left edge of the column at which to render this\r
- * item.\r
- *\r
- * @return {Map}\r
- * The returned map contains an 'html' member which contains the html for\r
- * the indentation, and a 'pos' member which is the starting position\r
- * plus the width of the indentation.\r
- */\r
- _addIndentation : function(cellInfo, pos)\r
- {\r
- var node = cellInfo.value;\r
- var imageData;\r
- var html = "";\r
-\r
- // Generate the indentation. Obtain icon determination values once\r
- // rather than each time through the loop.\r
- var bUseTreeLines = this.getUseTreeLines();\r
- var bExcludeFirstLevelTreeLines = this.getExcludeFirstLevelTreeLines();\r
- var bAlwaysShowOpenCloseSymbol = this.getAlwaysShowOpenCloseSymbol();\r
-\r
- for (var i=0; i<node.level; i++)\r
- {\r
- imageData = this._getIndentSymbol(i, node, bUseTreeLines,\r
- bAlwaysShowOpenCloseSymbol,\r
- bExcludeFirstLevelTreeLines);\r
- \r
- html += this._addImage(\r
- {\r
- url : imageData.icon,\r
- position :\r
- {\r
- top : 0 + (imageData.paddingTop || 5),\r
- left : pos + (imageData.paddingLeft || 3),\r
- width : 19,\r
- height : 16\r
- }\r
- });\r
- pos += 19;\r
- }\r
-\r
- return (\r
- {\r
- html : html,\r
- pos : pos\r
- });\r
- },\r
-\r
- /**\r
- * Add the icon for this node of the tree.\r
- *\r
- * @param cellInfo {Map} The information about the cell.\r
- * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
- *\r
- * @param pos {Integer}\r
- * The position from the left edge of the column at which to render this\r
- * item.\r
- *\r
- * @return {Map}\r
- * The returned map contains an 'html' member which contains the html for\r
- * the icon, and a 'pos' member which is the starting position plus the\r
- * width of the icon.\r
- */\r
- _addIcon : function(cellInfo, pos)\r
- {\r
- var node = cellInfo.value;\r
-\r
- // Add the node's icon\r
- var imageUrl = (node.bSelected ? node.iconSelected : node.icon);\r
-\r
- if (!imageUrl)\r
- {\r
- if (node.type == qx.ui.treevirtual.SimpleTreeDataModel.Type.LEAF)\r
- {\r
- var o = this.__tm.styleFrom("treevirtual-file");\r
- }\r
- else\r
- {\r
- var states = { opened : node.bOpened };\r
- var o = this.__tm.styleFrom("treevirtual-folder", states);\r
- }\r
-\r
- imageUrl = o.icon;\r
- }\r
-\r
- var html = this._addImage(\r
- {\r
- url : imageUrl,\r
- position :\r
- {\r
- top : 0,\r
- left : pos,\r
- width : 19,\r
- height : 16\r
- }\r
- });\r
-\r
- return (\r
- {\r
- html : html,\r
- pos : pos + 19\r
- });\r
- },\r
-\r
- /**\r
- * Add the label for this node of the tree.\r
- *\r
- * @param cellInfo {Map} The information about the cell.\r
- * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
- * Additionally, if defined, the labelSpanStyle member is used to apply\r
- * style to the span containing the label. (This member is for use by\r
- * subclasses; it's not otherwise used by this class.)\r
- *\r
- * @param pos {Integer}\r
- * The position from the left edge of the column at which to render this\r
- * item.\r
- *\r
- * @return {String}\r
- * The html for the label.\r
- */\r
- _addLabel : function(cellInfo, pos)\r
- {\r
- var node = cellInfo.value;\r
-\r
- // Add the node's label. We calculate the "left" property with: each\r
- // tree line (indentation) icon is 19 pixels wide; the folder icon is 16\r
- // pixels wide, there are two pixels of padding at the left, and we want\r
- // 2 pixels between the folder icon and the label\r
- var html =\r
- '<div style="position:absolute;' +\r
- 'left:' + pos + 'px;' +\r
- 'top:0;' +\r
- (node.labelStyle ? node.labelStyle + ";" : "") +\r
- '">' +\r
- '<span' + (cellInfo.labelSpanStyle\r
- ? 'style="' + cellInfo.labelSpanStyle + ';"'\r
- : "") + '>' +\r
- node.label +\r
- '</span>' +\r
- '</div>';\r
-\r
- return html;\r
- },\r
-\r
- /**\r
- * Adds extra content just before the indentation.\r
- *\r
- * @param cellInfo {Map} The information about the cell.\r
- * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
- *\r
- * @param pos {Integer}\r
- * The position from the left edge of the column at which to render this\r
- * item.\r
- *\r
- * @return {Map}\r
- * The returned map contains an 'html' member which contains the html for\r
- * the indentation, and a 'pos' member which is the starting position\r
- * plus the width of the indentation.\r
- */\r
- _addExtraContentBeforeIndentation : function(cellInfo, pos)\r
- {\r
- return { html: '', pos: pos };\r
- },\r
-\r
- /**\r
- * Adds extra content just before the icon.\r
- *\r
- * @param cellInfo {Map} The information about the cell.\r
- * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
- *\r
- * @param pos {Integer}\r
- * The position from the left edge of the column at which to render this\r
- * item.\r
- *\r
- * @return {Map}\r
- * The returned map contains an 'html' member which contains the html for\r
- * the indentation, and a 'pos' member which is the starting position\r
- * plus the width of the indentation.\r
- */\r
- _addExtraContentBeforeIcon : function(cellInfo, pos)\r
- {\r
- return { html: '', pos: pos };\r
- },\r
-\r
- /**\r
- * Adds extra content just before the label.\r
- *\r
- * @param cellInfo {Map} The information about the cell.\r
- * See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
- *\r
- * @param pos {Integer}\r
- * The position from the left edge of the column at which to render this\r
- * item.\r
- *\r
- * @return {Map}\r
- * The returned map contains an 'html' member which contains the html for\r
- * the indentation, and a 'pos' member which is the starting position\r
- * plus the width of the indentation.\r
- */\r
- _addExtraContentBeforeLabel : function(cellInfo, pos)\r
- {\r
- return { html: '', pos: pos };\r
- },\r
-\r
-\r
- /**\r
- * Determine the symbol to use for indentation of a tree row, at a\r
- * particular column. The indentation to use may be just white space or\r
- * may be a tree line. Tree lines come in numerous varieties, so the\r
- * appropriate one is selected.\r
- *\r
- * @type member\r
- *\r
- * @param column {Integer}\r
- * The column of indentation being requested, zero-relative\r
- *\r
- * @param node {Node}\r
- * The node being displayed in the row. The properties of a node are\r
- * described in {@link qx.ui.treevirtual.SimpleTreeDataModel}\r
- *\r
- * @param bUseTreeLines {Boolean}\r
- * Whether to find an appropriate tree line icon, or simply provide\r
- * white space.\r
- *\r
- * @param bAlwaysShowOpenCloseSymbol {Boolean}\r
- * Whether to display the open/close icon for a node even if it has no\r
- * children.\r
- *\r
- * @param bExcludeFirstLevelTreeLines {Boolean}\r
- * If bUseTreeLines is enabled, then further filtering of the left-most\r
- * tree line may be specified here. If <i>true</i> then the left-most\r
- * tree line, between top-level siblings, will not be displayed.\r
- * If <i>false</i>, then the left-most tree line wiill be displayed\r
- * just like all of the other tree lines.\r
- *\r
- * @return {var} TODOC\r
- */\r
- _getIndentSymbol : function(column,\r
- node,\r
- bUseTreeLines,\r
- bAlwaysShowOpenCloseSymbol,\r
- bExcludeFirstLevelTreeLines)\r
- {\r
- var STDCR = org.argeo.ria.util.TreeDataCellRenderer;\r
-\r
- // If we're in column 0 and excludeFirstLevelTreeLines is enabled, then\r
- // we treat this as if no tree lines were requested.\r
- if (column == 0 && bExcludeFirstLevelTreeLines)\r
- {\r
- bUseTreeLines = false;\r
- }\r
-\r
- // If we're not on the final column...\r
- if (column < node.level - 1)\r
- {\r
- // then return either a line or a blank icon, depending on\r
- // bUseTreeLines\r
- return (bUseTreeLines && ! node.lastChild[column]\r
- ? STDCR.__icon.line\r
- : { icon : this.BLANK });\r
- }\r
-\r
- var bLastChild = node.lastChild[node.lastChild.length - 1];\r
-\r
- // Is this a branch node that does not have the open/close button hidden?\r
- if (node.type == qx.ui.treevirtual.SimpleTreeDataModel.Type.BRANCH &&\r
- ! node.bHideOpenClose)\r
- {\r
- // Does this node have any children, or do we always want the\r
- // open/close symbol to be shown?\r
- if (node.children.length > 0 || bAlwaysShowOpenCloseSymbol)\r
- {\r
- // If we're not showing tree lines...\r
- if (!bUseTreeLines)\r
- {\r
- // ... then just use a expand or contract\r
- return (node.bOpened\r
- ? STDCR.__icon.contract\r
- : STDCR.__icon.expand);\r
- }\r
-\r
- // Are we looking at a top-level, first child of its parent?\r
- if (column == 0 && node.bFirstChild)\r
- {\r
- // Yup. If it's also a last child...\r
- if (bLastChild)\r
- {\r
- // ... then use no tree lines.\r
- return (node.bOpened\r
- ? STDCR.__icon.onlyContract\r
- : STDCR.__icon.onlyExpand);\r
- }\r
- else\r
- {\r
- // otherwise, use descender lines but no ascender.\r
- return (node.bOpened\r
- ? STDCR.__icon.startContract\r
- : STDCR.__icon.startExpand);\r
- }\r
- }\r
-\r
- // It's not a top-level, first child. Is this the last child of its\r
- // parent?\r
- if (bLastChild)\r
- {\r
- // Yup. Return an ending expand or contract.\r
- return (node.bOpened\r
- ? STDCR.__icon.endContract\r
- : STDCR.__icon.endExpand);\r
- }\r
-\r
- // Otherwise, return a crossing expand or contract.\r
- return (node.bOpened\r
- ? STDCR.__icon.crossContract\r
- : STDCR.__icon.crossExpand);\r
- }\r
- }\r
-\r
- // This node does not have any children. Return an end or cross, if\r
- // we're using tree lines.\r
- if (bUseTreeLines)\r
- {\r
- // If this is a child of the root node...\r
- if (node.parentNodeId == 0)\r
- {\r
- // If this is the only child...\r
- if (bLastChild && node.bFirstChild)\r
- {\r
- // ... then return a blank.\r
- return { icon : this.BLANK };\r
- }\r
-\r
- // Otherwise, if this is the last child...\r
- if (bLastChild)\r
- {\r
- // ... then return an end line.\r
- return STDCR.__icon.end;\r
- }\r
-\r
- // Otherwise if this is the first child...\r
- if (node.bFirstChild)\r
- {\r
- // ... then return a start line.\r
- return STDCR.__icon.startContract;\r
- }\r
- }\r
-\r
- // If this is a last child, return and ending line; otherwise cross.\r
- return (bLastChild\r
- ? STDCR.__icon.end\r
- : STDCR.__icon.cross);\r
- }\r
-\r
- return { icon : this.BLANK };\r
- }\r
- },\r
-\r
- defer : function()\r
- {\r
- // Ensure that the theme is initialized\r
- qx.theme.manager.Meta.getInstance().initialize();\r
-\r
- var STDCR = org.argeo.ria.util.TreeDataCellRenderer;\r
-\r
- var ImageLoader = qx.io2.ImageLoader;\r
-\r
- var am = qx.util.AliasManager.getInstance();\r
- var rm = qx.util.ResourceManager;\r
- var tm = qx.theme.manager.Appearance.getInstance();\r
-\r
- var loadImage = function(f)\r
- {\r
- ImageLoader.load(rm.toUri(am.resolve(f)));\r
- };\r
-\r
- STDCR.__icon.line = tm.styleFrom("treevirtual-line");\r
- loadImage(STDCR.__icon.line.icon);\r
-\r
- STDCR.__icon.contract = tm.styleFrom("treevirtual-contract");\r
- loadImage(STDCR.__icon.contract.icon);\r
-\r
- STDCR.__icon.expand = tm.styleFrom("treevirtual-expand");\r
- loadImage(STDCR.__icon.expand.icon);\r
-\r
- STDCR.__icon.onlyContract = tm.styleFrom("treevirtual-only-contract");\r
- loadImage(STDCR.__icon.onlyContract.icon);\r
-\r
- STDCR.__icon.onlyExpand = tm.styleFrom("treevirtual-only-expand");\r
- loadImage(STDCR.__icon.onlyExpand.icon);\r
-\r
- STDCR.__icon.startContract = tm.styleFrom("treevirtual-start-contract");\r
- loadImage(STDCR.__icon.startContract.icon);\r
-\r
- STDCR.__icon.startExpand = tm.styleFrom("treevirtual-start-expand");\r
- loadImage(STDCR.__icon.startExpand.icon);\r
-\r
- STDCR.__icon.endContract = tm.styleFrom("treevirtual-end-contract");\r
- loadImage(STDCR.__icon.endContract.icon);\r
-\r
- STDCR.__icon.endExpand = tm.styleFrom("treevirtual-end-expand");\r
- loadImage(STDCR.__icon.endExpand.icon);\r
-\r
- STDCR.__icon.crossContract = tm.styleFrom("treevirtual-cross-contract");\r
- loadImage(STDCR.__icon.crossContract.icon);\r
-\r
- STDCR.__icon.crossExpand = tm.styleFrom("treevirtual-cross-expand");\r
- loadImage(STDCR.__icon.crossExpand.icon);\r
-\r
- STDCR.__icon.end = tm.styleFrom("treevirtual-end");\r
- loadImage(STDCR.__icon.end.icon);\r
-\r
- STDCR.__icon.cross = tm.styleFrom("treevirtual-cross");\r
- loadImage(STDCR.__icon.cross.icon);\r
- },\r
-\r
- destruct : function()\r
- {\r
- this._disposeFields(\r
- "__am",\r
- "__rm",\r
- "__tm",\r
- "BLANK");\r
- }\r
-});
\ No newline at end of file
+++ /dev/null
-/**\r
- * Various utilitary classes, especially qooxdoo framework extensions \r
- * to fix various bugs or missing features.\r
- *\r
- */
\ No newline at end of file
+++ /dev/null
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<head>\r
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
- <title>Slc Webui</title>\r
- <script type="text/javascript" src="script/org.argeo.ria.js"></script>\r
-</head>\r
-</html>\r
+++ /dev/null
-This directory will contain translation (.po) files once you run the
-'translation' job in your project.
-