Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / examples / desktop / js / Desktop.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /*!
16  * Ext JS Library 4.0
17  * Copyright(c) 2006-2011 Sencha Inc.
18  * licensing@sencha.com
19  * http://www.sencha.com/license
20  */
21
22 /**
23  * @class Ext.ux.desktop.Desktop
24  * @extends Ext.panel.Panel
25  * <p>This class manages the wallpaper, shortcuts and taskbar.</p>
26  */
27 Ext.define('Ext.ux.desktop.Desktop', {
28     extend: 'Ext.panel.Panel',
29
30     alias: 'widget.desktop',
31
32     uses: [
33         'Ext.util.MixedCollection',
34         'Ext.menu.Menu',
35         'Ext.view.View', // dataview
36         'Ext.window.Window',
37
38         'Ext.ux.desktop.TaskBar',
39         'Ext.ux.desktop.Wallpaper',
40         'Ext.ux.desktop.FitAllLayout'
41     ],
42
43     activeWindowCls: 'ux-desktop-active-win',
44     inactiveWindowCls: 'ux-desktop-inactive-win',
45     lastActiveWindow: null,
46
47     border: false,
48     html: '&#160;',
49     layout: 'fitall',
50
51     xTickSize: 1,
52     yTickSize: 1,
53
54     app: null,
55
56     /**
57      * @cfg {Array|Store} shortcuts
58      * The items to add to the DataView. This can be a {@link Ext.data.Store Store} or a
59      * simple array. Items should minimally provide the fields in the
60      * {@link Ext.ux.desktop.ShorcutModel ShortcutModel}.
61      */
62     shortcuts: null,
63
64     /**
65      * @cfg {String} shortcutItemSelector
66      * This property is passed to the DataView for the desktop to select shortcut items.
67      * If the {@link #shortcutTpl} is modified, this will probably need to be modified as
68      * well.
69      */
70     shortcutItemSelector: 'div.ux-desktop-shortcut',
71
72     /**
73      * @cfg {String} shortcutTpl
74      * This XTemplate is used to render items in the DataView. If this is changed, the
75      * {@link shortcutItemSelect} will probably also need to changed.
76      */
77     shortcutTpl: [
78         '<tpl for=".">',
79             '<div class="ux-desktop-shortcut" id="{name}-shortcut">',
80                 '<div class="ux-desktop-shortcut-icon {iconCls}">',
81                     '<img src="',Ext.BLANK_IMAGE_URL,'" title="{name}">',
82                 '</div>',
83                 '<span class="ux-desktop-shortcut-text">{name}</span>',
84             '</div>',
85         '</tpl>',
86         '<div class="x-clear"></div>'
87     ],
88
89     /**
90      * @cfg {Object} taskbarConfig
91      * The config object for the TaskBar.
92      */
93     taskbarConfig: null,
94
95     windowMenu: null,
96
97     initComponent: function () {
98         var me = this;
99
100         me.windowMenu = new Ext.menu.Menu(me.createWindowMenu());
101
102         me.bbar = me.taskbar = new Ext.ux.desktop.TaskBar(me.taskbarConfig);
103         me.taskbar.windowMenu = me.windowMenu;
104
105         me.windows = new Ext.util.MixedCollection();
106
107         me.contextMenu = new Ext.menu.Menu(me.createDesktopMenu());
108
109         me.items = [
110             { xtype: 'wallpaper', id: me.id+'_wallpaper' },
111             me.createDataView()
112         ];
113
114         me.callParent();
115
116         me.shortcutsView = me.items.getAt(1);
117         me.shortcutsView.on('itemclick', me.onShortcutItemClick, me);
118
119         var wallpaper = me.wallpaper;
120         me.wallpaper = me.items.getAt(0);
121         if (wallpaper) {
122             me.setWallpaper(wallpaper, me.wallpaperStretch);
123         }
124     },
125
126     afterRender: function () {
127         var me = this;
128         me.callParent();
129         me.el.on('contextmenu', me.onDesktopMenu, me);
130     },
131
132     //------------------------------------------------------
133     // Overrideable configuration creation methods
134
135     createDataView: function () {
136         var me = this;
137         return {
138             xtype: 'dataview',
139             overItemCls: 'x-view-over',
140             trackOver: true,
141             itemSelector: me.shortcutItemSelector,
142             store: me.shortcuts,
143             tpl: new Ext.XTemplate(me.shortcutTpl)
144         };
145     },
146
147     createDesktopMenu: function () {
148         var me = this, ret = {
149             items: me.contextMenuItems || []
150         };
151
152         if (ret.items.length) {
153             ret.items.push('-');
154         }
155
156         ret.items.push(
157                 { text: 'Tile', handler: me.tileWindows, scope: me, minWindows: 1 },
158                 { text: 'Cascade', handler: me.cascadeWindows, scope: me, minWindows: 1 })
159
160         return ret;
161     },
162
163     createWindowMenu: function () {
164         var me = this;
165         return {
166             defaultAlign: 'br-tr',
167             items: [
168                 { text: 'Restore', handler: me.onWindowMenuRestore, scope: me },
169                 { text: 'Minimize', handler: me.onWindowMenuMinimize, scope: me },
170                 { text: 'Maximize', handler: me.onWindowMenuMaximize, scope: me },
171                 '-',
172                 { text: 'Close', handler: me.onWindowMenuClose, scope: me }
173             ],
174             listeners: {
175                 beforeshow: me.onWindowMenuBeforeShow,
176                 hide: me.onWindowMenuHide,
177                 scope: me
178             }
179         };
180     },
181
182     //------------------------------------------------------
183     // Event handler methods
184
185     onDesktopMenu: function (e) {
186         var me = this, menu = me.contextMenu;
187         e.stopEvent();
188         if (!menu.rendered) {
189             menu.on('beforeshow', me.onDesktopMenuBeforeShow, me);
190         }
191         menu.showAt(e.getXY());
192         menu.doConstrain();
193     },
194
195     onDesktopMenuBeforeShow: function (menu) {
196         var me = this, count = me.windows.getCount();
197
198         menu.items.each(function (item) {
199             var min = item.minWindows || 0;
200             item.setDisabled(count < min);
201         });
202     },
203
204     onShortcutItemClick: function (dataView, record) {
205         var me = this, module = me.app.getModule(record.data.module),
206             win = module && module.createWindow();
207
208         if (win) {
209             me.restoreWindow(win);
210         }
211     },
212
213     onWindowClose: function(win) {
214         var me = this;
215         me.windows.remove(win);
216         me.taskbar.removeTaskButton(win.taskButton);
217         me.updateActiveWindow();
218     },
219
220     //------------------------------------------------------
221     // Window context menu handlers
222
223     onWindowMenuBeforeShow: function (menu) {
224         var items = menu.items.items, win = menu.theWin;
225         items[0].setDisabled(win.maximized !== true && win.hidden !== true); // Restore
226         items[1].setDisabled(win.minimized === true); // Minimize
227         items[2].setDisabled(win.maximized === true || win.hidden === true); // Maximize
228     },
229
230     onWindowMenuClose: function () {
231         var me = this, win = me.windowMenu.theWin;
232
233         win.close();
234     },
235
236     onWindowMenuHide: function (menu) {
237         menu.theWin = null;
238     },
239
240     onWindowMenuMaximize: function () {
241         var me = this, win = me.windowMenu.theWin;
242
243         win.maximize();
244     },
245
246     onWindowMenuMinimize: function () {
247         var me = this, win = me.windowMenu.theWin;
248
249         win.minimize();
250     },
251
252     onWindowMenuRestore: function () {
253         var me = this, win = me.windowMenu.theWin;
254
255         me.restoreWindow(win);
256     },
257
258     //------------------------------------------------------
259     // Dynamic (re)configuration methods
260
261     getWallpaper: function () {
262         return this.wallpaper.wallpaper;
263     },
264
265     setTickSize: function(xTickSize, yTickSize) {
266         var me = this,
267             xt = me.xTickSize = xTickSize,
268             yt = me.yTickSize = (arguments.length > 1) ? yTickSize : xt;
269
270         me.windows.each(function(win) {
271             var dd = win.dd, resizer = win.resizer;
272             dd.xTickSize = xt;
273             dd.yTickSize = yt;
274             resizer.widthIncrement = xt;
275             resizer.heightIncrement = yt;
276         });
277     },
278
279     setWallpaper: function (wallpaper, stretch) {
280         this.wallpaper.setWallpaper(wallpaper, stretch);
281         return this;
282     },
283
284     //------------------------------------------------------
285     // Window management methods
286
287     cascadeWindows: function() {
288         var x = 0, y = 0,
289             zmgr = this.getDesktopZIndexManager();
290
291         zmgr.eachBottomUp(function(win) {
292             if (win.isWindow && win.isVisible() && !win.maximized) {
293                 win.setPosition(x, y);
294                 x += 20;
295                 y += 20;
296             }
297         });
298     },
299
300     createWindow: function(config, cls) {
301         var me = this, win, cfg = Ext.applyIf(config || {}, {
302                 stateful: false,
303                 isWindow: true,
304                 constrainHeader: true,
305                 minimizable: true,
306                 maximizable: true
307             });
308
309         cls = cls || Ext.window.Window;
310         win = me.add(new cls(cfg));
311
312         me.windows.add(win);
313
314         win.taskButton = me.taskbar.addTaskButton(win);
315         win.animateTarget = win.taskButton.el;
316
317         win.on({
318             activate: me.updateActiveWindow,
319             beforeshow: me.updateActiveWindow,
320             deactivate: me.updateActiveWindow,
321             minimize: me.minimizeWindow,
322             destroy: me.onWindowClose,
323             scope: me
324         });
325
326         win.on({
327             afterrender: function () {
328                 win.dd.xTickSize = me.xTickSize;
329                 win.dd.yTickSize = me.yTickSize;
330
331                 if (win.resizer) {
332                     win.resizer.widthIncrement = me.xTickSize;
333                     win.resizer.heightIncrement = me.yTickSize;
334                 }
335             },
336             single: true
337         });
338
339         // replace normal window close w/fadeOut animation:
340         win.doClose = function ()  {
341             win.doClose = Ext.emptyFn; // dblclick can call again...
342             win.el.disableShadow();
343             win.el.fadeOut({
344                 listeners: {
345                     afteranimate: function () {
346                         win.destroy();
347                     }
348                 }
349             });
350         };
351
352         return win;
353     },
354
355     getActiveWindow: function () {
356         var win = null,
357             zmgr = this.getDesktopZIndexManager();
358
359         if (zmgr) {
360             // We cannot rely on activate/deactive because that fires against non-Window
361             // components in the stack.
362
363             zmgr.eachTopDown(function (comp) {
364                 if (comp.isWindow && !comp.hidden) {
365                     win = comp;
366                     return false;
367                 }
368                 return true;
369             });
370         }
371
372         return win;
373     },
374
375     getDesktopZIndexManager: function () {
376         var windows = this.windows;
377         // TODO - there has to be a better way to get this...
378         return (windows.getCount() && windows.getAt(0).zIndexManager) || null;
379     },
380
381     getWindow: function(id) {
382         return this.windows.get(id);
383     },
384
385     minimizeWindow: function(win) {
386         win.minimized = true;
387         win.hide();
388     },
389
390     restoreWindow: function (win) {
391         if (win.isVisible()) {
392             win.restore();
393             win.toFront();
394         } else {
395             win.show();
396         }
397         return win;
398     },
399
400     tileWindows: function() {
401         var me = this, availWidth = me.body.getWidth(true);
402         var x = me.xTickSize, y = me.yTickSize, nextY = y;
403
404         me.windows.each(function(win) {
405             if (win.isVisible() && !win.maximized) {
406                 var w = win.el.getWidth();
407
408                 // Wrap to next row if we are not at the line start and this Window will
409                 // go off the end
410                 if (x > me.xTickSize && x + w > availWidth) {
411                     x = me.xTickSize;
412                     y = nextY;
413                 }
414
415                 win.setPosition(x, y);
416                 x += w + me.xTickSize;
417                 nextY = Math.max(nextY, y + win.el.getHeight() + me.yTickSize);
418             }
419         });
420     },
421
422     updateActiveWindow: function () {
423         var me = this, activeWindow = me.getActiveWindow(), last = me.lastActiveWindow;
424         if (activeWindow === last) {
425             return;
426         }
427
428         if (last) {
429             if (last.el.dom) {
430                 last.addCls(me.inactiveWindowCls);
431                 last.removeCls(me.activeWindowCls);
432             }
433             last.active = false;
434         }
435
436         me.lastActiveWindow = activeWindow;
437
438         if (activeWindow) {
439             activeWindow.addCls(me.activeWindowCls);
440             activeWindow.removeCls(me.inactiveWindowCls);
441             activeWindow.minimized = false;
442             activeWindow.active = true;
443         }
444
445         me.taskbar.setActiveButton(activeWindow && activeWindow.taskButton);
446     }
447 });
448