]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.webapp/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/CommandsManager.js
573c11ff642fe80da8643aba2bf25336ab8689a6
[gpl/argeo-slc.git] / org.argeo.slc.webapp / src / main / webapp / argeo-ria-src / class / org / argeo / ria / event / CommandsManager.js
1 /**
2 * The main controller (in a standard MVC point of view) of the application. It is a singleton
3 * thus can be called by any part of the application.
4 * This will wire all the commands that can be defined dynamically by any IView, and add their
5 * corresponding buttons to the application menubar and toolbars.
6 * See the "definitions" property documentation below for more info on how to define new commands.
7 *
8 * @author Charles du Jeu
9 */
10 qx.Class.define("org.argeo.ria.event.CommandsManager",
11 {
12 type : "singleton",
13 extend : qx.core.Object,
14
15 construct : function(){
16 this.base(arguments);
17 this.setInitialDefinitions(qx.lang.Object.copy(this.getDefinitions()));
18 this.addListener("changedCommands", this.createCommands, this);
19 },
20
21 properties :
22 {
23 /**
24 * The commands definitions is a Map described as below
25 * <pre>
26 * {
27 * <b>label : "",</b>
28 * | The label of the action
29 *
30 * <b>icon : "",</b>
31 * | The icon image
32 *
33 * <b>shortcut : "",</b>
34 * | The keyboard shortcut, as defined in qooxdoo (Control+s, Alt+k, etc.). Warning, the letter must be lowercase.
35 *
36 * <b>enabled : true,</b>
37 * | Whether it is enabled or disabled at creation
38 *
39 * <b>menu : ""|null,</b>
40 * | The menu group to which the command will be added. If null, will not appear in the menus.
41 *
42 * <b>menuPosition : "first"|"last"</b>
43 * | Optional : force the menu group to be first or last in the menubar.
44 *
45 * <b>toolbar : ""|null,</b>
46 * | The toolbar group to which the command will be added. If null, will not appear in the toolbars.
47 *
48 * <b>init : function(){},</b>
49 * | Optional function called at command creation.
50 * | Function context : the command itself
51 *
52 * <b>callback : function(e){},</b>
53 * | The main callback to be triggered when command is executed.
54 * | Function context : the current class (not the command!)
55 *
56 * <b>selectionChange : function(viewPaneId, xmlNodes){},</b>
57 * | Optional function called each time a selectionChange is detected in one of the active viewPane.
58 * | The origin viewPaneId and the new selection as a map of nodes are passed as arguments.
59 * | Function context : the command itself.
60 *
61 * <b>submenu : [{label:"", icon:"", commandId:""}, ...],</b>
62 * | If set, the command will create a submenu, being in a menu or in the toolbar.
63 * | The submenu is created with the various array entries, and the submenuCallback function
64 * | will be called with the 'commandId' parameter when a submenu entry is selected.
65 *
66 * <b>submenuCallback : function(commandId){},</b>
67 * | Callback if command is a submenu (cf. above).
68 * | Function context : the current class/
69 *
70 * <b>command : null</b>
71 * | For internal use only, caching the actual org.argeo.ria.event.Command object.
72 * }
73 * </pre>
74 * @see org.argeo.ria.event.Command for the definition Map details.
75 */
76 definitions : {
77 init : {},
78 check : "Map"
79 },
80 /**
81 * For internal use
82 */
83 initialDefinitions : {
84 init : {},
85 check : "Map"
86 }
87 },
88
89 events : {
90 /**
91 * Triggered when the whole commands list is changed. Mainly used internally by the manager.
92 */
93 "changedCommands" : "qx.event.type.Event"
94 },
95
96 /*
97 *****************************************************************************
98 MEMBERS
99 *****************************************************************************
100 */
101
102 members :
103 {
104 init : function(initDefinitions){
105 this.setDefinitions(initDefinitions);
106 this.setInitialDefinitions(qx.lang.Object.copy(initDefinitions));
107 },
108
109 /**
110 * Creates all the objects (if they are not already existing) from the definitions maps.
111 */
112 createCommands : function(){
113 this.menus = {};
114 this.toolbars = {};
115 var defs = this.getDefinitions();
116 for(var key in defs){
117 var definition = defs[key];
118 var command;
119 if(!definition.command){
120 command = new org.argeo.ria.event.Command(key, definition.label, definition.icon, definition.shortcut);
121 if(definition.submenu){
122 command.setMenu(definition.submenu);
123 if(definition.submenuCallback){
124 command.setMenuCallback(definition.submenuCallback);
125 command.setMenuContext((definition.callbackContext?definition.callbackContext:null));
126 }
127 }
128 command.setEnabled(definition.enabled);
129 if(definition.toggle){
130 command.setToggle(true);
131 }
132 this._attachListener(command, definition.callback, definition.callbackContext);
133 if(definition.init){
134 var binded = qx.lang.Function.bind(definition.init, command);
135 binded();
136 }
137 definition.command = command;
138 }else{
139 command = definition.command;
140 }
141 if(definition.menu){
142 if(!this.menus[definition.menu]) this.menus[definition.menu] = [];
143 this.menus[definition.menu].push(definition);
144 }
145 if(definition.toolbar){
146 if(!this.toolbars[definition.toolbar]) this.toolbars[definition.toolbar] = [];
147 this.toolbars[definition.toolbar].push(command);
148 }
149 }
150 this.setDefinitions(defs);
151 },
152
153 /**
154 * Refresh the current commands status depending on the viewSelection.
155 * @param viewSelection {org.argeo.ria.components.ViewSelection} The current ViewSelection
156 */
157 refreshCommands : function(viewSelection){
158 var defs = this.getDefinitions();
159 var xmlNodes = null;
160 if(viewSelection.getCount() > 0){
161 var xmlNodes = viewSelection.getNodes();
162 }
163 for(var key in defs){
164 var definition = defs[key];
165 if(!definition.selectionChange) continue;
166 var binded = qx.lang.Function.bind(definition.selectionChange, definition.command);
167 binded(viewSelection.getViewId(), xmlNodes);
168 }
169 },
170
171 /**
172 * Record a menubar for the application
173 * @param menuBar {qx.ui.menubar.MenuBar} The application menubar
174 */
175 registerMenuBar : function(menuBar){
176 this.addListener("changedCommands", function(){
177 this.createMenuButtons(menuBar);
178 }, this);
179 this.createMenuButtons(menuBar);
180 },
181
182 /**
183 * Record a toolbar for the application
184 * @param toolBar {qx.ui.toolbar.ToolBar} The application toolbar
185 */
186 registerToolBar : function(toolBar){
187 this.addListener("changedCommands", function(){
188 this.createToolbarParts(toolBar);
189 }, this);
190 this.createToolbarParts(toolBar);
191 },
192
193 /**
194 * Creates the real buttons and add them to the passed menuBar.
195 * @param menuBar {qx.ui.menubar.MenuBar} The application menubar
196 */
197 createMenuButtons : function(menuBar){
198 menuBar.removeAll();
199 var anchors = {};
200 for(var key in this.menus){
201 var menu = new qx.ui.menu.Menu();
202 var button = new qx.ui.menubar.Button(key, null, menu);
203 var anchorDetected = false;
204 for(var i=0; i<this.menus[key].length;i++){
205 var def = this.menus[key][i];
206 menu.add(def.command.getMenuButton());
207 if(!anchorDetected && def.menuPosition){
208 anchorDetected = true;
209 anchors[def.menuPosition] = button;
210 }
211 }
212 if(!anchorDetected){
213 menuBar.add(button);
214 }
215 }
216 // Add specific anchored buttons
217 if(anchors.first) menuBar.addAt(anchors.first, 0);
218 else if(anchors.last){
219 menuBar.add(anchors.last);
220 }
221 },
222
223 /**
224 * Creates the real buttons and add them to the passed toolbar.
225 * @param toolbar {qx.ui.toolbar.ToolBar} The application toolbar
226 */
227 createToolbarParts : function(toolbar){
228 toolbar.removeAll();
229 for(var key in this.toolbars){
230 var tPart = new qx.ui.toolbar.Part();
231 toolbar.add(tPart);
232 this.toolbars[key].map(function(command){
233 tPart.add(command.getToolbarButton());
234 });
235 }
236 },
237 /**
238 * Creates a context menu from an array of commands ids.
239 * @param commandIdsArray {Array} An array of string
240 * @return {qx.ui.menu.Menu}
241 */
242 createMenuFromIds : function(commandIdsArray){
243 var defs = this.getDefinitions();
244 var contextMenu = new qx.ui.menu.Menu();
245 for(var i=0;i<commandIdsArray.length;i++){
246 var definition = defs[commandIdsArray[i]];
247 if(definition){
248 var command = definition.command;
249 contextMenu.add(command.getMenuButton());
250 }
251 }
252 return contextMenu;
253 },
254 /**
255 * Add a new set of commands definitions. See the definitions property of this class.
256 * @param definitions {Map} a set of commands definitions.
257 * @param callbackContext {qx.ui.core.Object} The context used inside the commands callbacks.
258 */
259 addCommands : function(definitions, callbackContext){
260 var crtDefs = this.getDefinitions();
261 for(var key in definitions){
262 if(callbackContext) definitions[key]['callbackContext'] = callbackContext;
263 crtDefs[key] = definitions[key];
264 }
265 this.setDefinitions(crtDefs);
266 this.fireEvent("changedCommands");
267 },
268 /**
269 * Removes a whole set of commands by their definitions maps.
270 * @param definitions {Map} a set of commands definitions
271 */
272 removeCommands : function(definitions){
273 var crtDefs = this.getDefinitions();
274 var initDefs = this.getInitialDefinitions();
275 for(var key in definitions){
276 if(!crtDefs[key]) continue;
277 if(initDefs[key]){
278 crtDefs[key] = initDefs[key];
279 }else{
280 delete crtDefs[key];
281 }
282 }
283 this.setDefinitions(crtDefs);
284 this.fireEvent("changedCommands");
285 },
286 /**
287 * Executes a command by its id.
288 * @param commandId {String} The command id.
289 */
290 executeCommand : function(commandId){
291 var defs = this.getDefinitions();
292 if(defs[commandId] && defs[commandId].command.getEnabled()){
293 defs[commandId].command.execute();
294 }
295 },
296 /**
297 * Retrieves a command by its id.
298 * @param commandId {String} The command id.
299 */
300 getCommandById : function(commandId){
301 var defs = this.getDefinitions();
302 if(defs[commandId] && defs[commandId].command){
303 return defs[commandId].command;
304 }
305 },
306 /**
307 * Add a standard context menu to a toolbar for button look and feel (show icon, text, both).
308 * @param toolbar {qx.ui.toolbar.ToolBar} The toolbar
309 */
310 addToolbarContextMenu : function(toolbar){
311 var menu = new qx.ui.menu.Menu();
312 var icon = new qx.ui.menu.RadioButton("Show Icons");
313 icon.setValue("icon");
314 var text = new qx.ui.menu.RadioButton("Show Text");
315 text.setValue("label");
316 var both = new qx.ui.menu.RadioButton("Show Both");
317 both.setValue("both");
318 var mgr = new qx.ui.form.RadioGroup(icon, text, both);
319 menu.add(icon);
320 menu.add(text);
321 menu.add(both);
322 mgr.setSelected(both);
323 toolbar.setContextMenu(menu);
324 mgr.addListener("changeValue", function(e){
325 this.setShow(e.getData());
326 }, toolbar);
327
328 },
329 /**
330 * Attach a listener to a command, with a context.
331 * The context can be an object, a string like "view:viewId" or null.
332 * If a string, the viewPaneId content will be retrieved at runtime. If null, "this" will be used
333 * as default context.
334 * @param command {org.argeo.ria.event.Command} The command
335 * @param callback {Function} The function to execute
336 * @param callbackContext {Object|String} The context in which the function will be executed.
337 */
338 _attachListener:function(command, callback, callbackContext){
339 if(!callbackContext){
340 command.addListener("execute", callback, this);
341 return;
342 }
343 if(typeof(callbackContext) == "object"){
344 command.addListener("execute", callback, callbackContext);
345 return;
346 }
347 if(typeof(callbackContext) == "string"){
348 if(callbackContext.split(":")[0] == "view"){
349 var viewId = callbackContext.split(":")[1];
350 command.addListener("execute", function(event){
351 var view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();
352 var binded = qx.lang.Function.bind(callback, view);
353 binded(event);
354 });
355 }else{
356 command.addListener("execute", callback, callbackContext);
357 }
358 }
359 }
360 }
361 });