1 jls.loader.provide('jls.win32.WindowElement');
  2 
  3 jls.loader.require('jls.win32.Window');
  4 jls.loader.require('jls.gui.Element');
  5 jls.loader.require('jls.gui.Event');
  6 jls.loader.require('jls.io.File');
  7 
  8 jls.win32.WindowElement = jls.lang.Class.create(jls.gui.Element,
  9 {
 10     initialize : function($super, parameters, parent) {
 11         this._window = null;
 12         this._windowId = null;
 13         this._windowUid = 1;
 14         this._bounds = [null, null, null, null];
 15         this._icon = null;
 16         this._menu = null;
 17         this._popupMenu = null;
 18         
 19         $super(parameters, parent);
 20         if (this._icon) {
 21             this.setIcon(this._icon);
 22         }
 23         jls.logger.trace('parent: "' + parent + '"');
 24         if ((typeof parent == 'undefined') || (parent == null)) {
 25             jls.win32.WindowElement._onCreateTopLevelWindow(this);
 26             this.observe('unload', jls.win32.WindowElement._onDestroyTopLevelWindow);
 27         }
 28         // TODO Remove
 29         //this._window.show(jls.win32.Window.SW_SHOW);
 30         //this.observe('load', this._window.update.bind(this));
 31         //this._window.update();
 32     },
 33     onCreate : jls.lang.Class.abstractMethod,
 34     onStyleChange : function(key, oldValue, newValue) {
 35         if (this._window == null) {
 36             return;
 37         }
 38         switch (key) {
 39         case 'fontFamily':
 40         case 'fontSize':
 41             var font = jls.win32.WindowElement.getFont(this.getStyle().getPropertyValue('fontFamily'),
 42             		this.getStyle().getPropertyValue('fontSize'));
 43             this._window.sendMessage(jls.win32.Window.WM_SETFONT, font, 1 /* force redraw */);
 44             break;
 45         case 'visibility':
 46         	//this._element._window.isWindowVisible() ? 'visible' : 'hidden';
 47     		//jls.lang.System.out.println('onStyleChange(' + key + ', ' + oldValue + ', ' + newValue + ')');
 48             this._window.show(newValue == 'visible' ? jls.win32.Window.SW_SHOW : jls.win32.Window.SW_HIDE);
 49             break;
 50         default:
 51             return;
 52         }
 53     },
 54     addChild : function($super, child) {
 55     	/*if ((typeof child == 'object') &&
 56     			(child instanceof jls.win32.WindowElement) &&
 57     			(child.getWindowStyle() & jls.win32.Window.WS_CHILD == jls.win32.Window.WS_CHILD)) {
 58     		return;
 59     	}*/
 60     	if ((typeof child == 'object') && (child instanceof jls.win32.Frame)) {
 61     		return child;
 62     	}
 63     	return $super(child);
 64     },
 65     onDestroy : function() {
 66         this._window.close();
 67     },
 68     getParentWindow : function(native) {
 69         var parent = this.getParent();
 70         // Looking for the first window type parent
 71         while (parent) {
 72             if (parent instanceof jls.win32.WindowElement) {
 73             	if (native) {
 74                     return parent._window;
 75             	} else {
 76                     return parent;
 77             	}
 78             }
 79             parent = parent.getParent();
 80         }
 81         return null;
 82     },
 83     getWindowOffset : function() {
 84         var parent = this.getParent();
 85         var x = 0;
 86         var y = 0;
 87         // Looking for the first window type parent
 88         while (parent) {
 89         	var cOffset = parent.getClientOffset();
 90             x += cOffset[0];
 91             y += cOffset[1];
 92             if (parent instanceof jls.win32.WindowElement) {
 93                 return [x, y];
 94             }
 95             x += parent._x;
 96             y += parent._y;
 97             parent = parent.getParent();
 98         }
 99         throw new jls.lang.Exception('No WindowElement parent found');
100     },
101     getOwnedWindows : function() {
102         return [];
103     },
104     observe: function($super, type, handler) {
105     	$super(type, handler);
106     	if (type == 'mouseout') {
107     		//this._window.trackMouseEvent(jls.win32.Window.TME_LEAVE);
108     		this._window.setCapture();
109     	}
110     },
111     unobserve: function($super, type, handler) {
112     	$super(type, handler);
113     	if (type == 'mouseout') {
114         	//jls.logger.warn('releaseCapture()');
115     		jls.win32.Window.releaseCapture();
116     	}
117     },
118     onWindowMessage: function(message, wParam, lParam) {
119     	//jls.logger.warn('onWindowMessage(' + jls.win32.Window.getMessageName(message) + ', ' + wParam + ', ' + lParam + ')');
120         switch(message) {
121         case jls.win32.Window.WM_CREATE:
122             this.dispatch(new jls.gui.Event('load', this));
123             break;
124         case jls.win32.Window.WM_DESTROY:
125             this.dispatch(new jls.gui.Event('unload', this));
126             break;
127         case jls.win32.Window.WM_INITMENU: // a menu is about to become active.
128             // wParam: A handle to the menu to be initialized.
129             var menu = null;
130             if ((this._popupMenu != null) && (this._popupMenu._menu.handle() == wParam)) {
131             	menu = this._popupMenu;
132             }
133             if ((this._menu != null) && (this._menu._menu.handle() == wParam)) {
134             	menu = this._menu;
135             }
136             if (menu != null) {
137             	menu.dispatch(new jls.gui.Event('init', this));
138             }
139             break;
140         case jls.win32.Window.WM_INITMENUPOPUP: // a drop-down menu or submenu is about to become active
141             break;
142         case jls.win32.Window.WM_COMMAND:
143         	var src = jls.win32.Window.hiWord(wParam);
144         	var id = jls.win32.Window.loWord(wParam);
145         	//jls.logger.warn('WM_COMMAND wParam: ' + wParam + ' (src: ' + src + ', id: ' + id + '), lParam: ' + lParam);
146         	if (src == 0) { // Menu
147         		var item = null;
148                 if (this._menu != null) {
149                 	item = this._menu.getItemById(id);
150                 }
151                 if ((item == null) && (this._popupMenu != null)) {
152                 	item = this._popupMenu.getItemById(id);
153                 }
154         		if ((item != null) && (item._event != null)) {
155         	        var event = new jls.gui.Event(item._event, this);
156         	        event.menuItem = item;
157         	        item.getRootMenu().dispatch(event);
158         		}
159         	} else if (src == 1) { // Accelerator
160         	}/* else { // Control
161         		// lParam contains the control handle
162                 var childCount = this.getChildCount();
163                 for (var i = 0; i < childCount; i++) {
164                     var child = this.getChild(i);
165                     if ((child instanceof jls.win32.WindowElement) &&
166                     		((child._windowId == id) || (child._window.handle() == lParam)) &&
167                     		('onWindowCommand' in child))
168                     {
169                         child.onWindowCommand(src);
170                         break;
171                     }
172                 }
173         	}*/ // Control notifications are routed natively on the control window via WM_CTLNOTIFY
174             break;
175         case jls.win32.Window.WM_SIZE:
176             var event = new jls.gui.Event('resize', this);
177             event.width = jls.win32.Window.loWord(lParam);
178             event.height = jls.win32.Window.hiWord(lParam);
179             //jls.logger.trace('WM_SIZE - wParam: ' + wParam + ', lParam: ' + lParam + ' (' + event.width + 'x' + event.height + ')');
180             this.dispatch(event);
181             break;
182         case jls.win32.Window.WM_SETFOCUS:
183             this.dispatch(new jls.gui.Event('focus', this));
184             break;
185         case jls.win32.Window.WM_LBUTTONDOWN:
186             this.dispatch(jls.win32.WindowElement.createMouseEvent('mousedown', this, jls.gui.Event.MOUSE_BUTTON_LEFT, wParam, lParam));
187             break;
188         case jls.win32.Window.WM_LBUTTONUP:
189             this.dispatch(jls.win32.WindowElement.createMouseEvent('mouseup', this, jls.gui.Event.MOUSE_BUTTON_LEFT, wParam, lParam));
190             this.dispatch(jls.win32.WindowElement.createMouseEvent('click', this, jls.gui.Event.MOUSE_BUTTON_LEFT, wParam, lParam));
191             break;
192         case jls.win32.Window.WM_LBUTTONDBLCLK:
193             this.dispatch(jls.win32.WindowElement.createMouseEvent('dblclick', this, jls.gui.Event.MOUSE_BUTTON_LEFT, wParam, lParam));
194             break;
195         case jls.win32.Window.WM_MBUTTONDOWN:
196             this.dispatch(jls.win32.WindowElement.createMouseEvent('mousedown', this, jls.gui.Event.MOUSE_BUTTON_MIDDLE, wParam, lParam));
197             break;
198         case jls.win32.Window.WM_MBUTTONUP:
199             this.dispatch(jls.win32.WindowElement.createMouseEvent('mouseup', this, jls.gui.Event.MOUSE_BUTTON_MIDDLE, wParam, lParam));
200             this.dispatch(jls.win32.WindowElement.createMouseEvent('click', this, jls.gui.Event.MOUSE_BUTTON_MIDDLE, wParam, lParam));
201             break;
202         case jls.win32.Window.WM_MBUTTONDBLCLK:
203             this.dispatch(jls.win32.WindowElement.createMouseEvent('dblclick', this, jls.gui.Event.MOUSE_BUTTON_MIDDLE, wParam, lParam));
204             break;
205         case jls.win32.Window.WM_RBUTTONDOWN:
206             this.dispatch(jls.win32.WindowElement.createMouseEvent('mousedown', this, jls.gui.Event.MOUSE_BUTTON_RIGHT, wParam, lParam));
207             break;
208         case jls.win32.Window.WM_RBUTTONUP:
209             this.dispatch(jls.win32.WindowElement.createMouseEvent('mouseup', this, jls.gui.Event.MOUSE_BUTTON_RIGHT, wParam, lParam));
210             this.dispatch(jls.win32.WindowElement.createMouseEvent('click', this, jls.gui.Event.MOUSE_BUTTON_RIGHT, wParam, lParam));
211             break;
212         case jls.win32.Window.WM_RBUTTONDBLCLK:
213             this.dispatch(jls.win32.WindowElement.createMouseEvent('dblclick', this, jls.gui.Event.MOUSE_BUTTON_RIGHT, wParam, lParam));
214             break;
215         // TODO mouseover
216         case jls.win32.Window.WM_MOUSEMOVE:
217             this.dispatch(jls.win32.WindowElement.createMouseEvent('mousemove', this, -1, wParam, lParam));
218             break;
219         case jls.win32.Window.WM_MOUSELEAVE: // TODO Remove
220         case jls.win32.Window.WM_CAPTURECHANGED:
221             this.dispatch(jls.gui.Event.createKeyEvent('mouseout', this, ' '));
222             break;
223         case jls.win32.Window.WM_KEYDOWN:
224             this.dispatch(jls.gui.Event.createKeyEvent('keydown', this, ' '));
225             break;
226         case jls.win32.Window.WM_KEYUP:
227             this.dispatch(jls.gui.Event.createKeyEvent('keyup', this, ' '));
228             break;
229         }
230     },
231     onStartObserving: function() {
232         this._window.setWindowFunction(this.onWindowMessage.bind(this));
233     },
234     onStopObserving: function() {
235         this._window.setWindowFunction(null);
236     },
237     setMenu : function(menu) {
238     	if (! (menu instanceof jls.win32.MenuItem)) {
239     		throw new jls.lang.Exception('Invalid menu argument');
240     	}
241         this._menu = menu;
242         this._window.setMenu(menu._menu);
243     },
244     trackPopup : function(popupMenu, x, y) {
245     	if (! (popupMenu instanceof jls.win32.MenuItem)) {
246     		throw new jls.lang.Exception('Invalid popupMenu argument');
247     	}
248         this._popupMenu = popupMenu;
249         return popupMenu._menu.trackPopup(this._window, x, y);
250     },
251     getWindowExStyle : function() {
252         var exs = 0;
253         // TODO 
254     	/*if (this.getStyle().getPropertyValue('border') != null) {
255             exs |= jls.win32.Window.WS_EX_CLIENTEDGE
256         }*/
257         return exs;
258     },
259     getWindowStyle : function() {
260     	var ws = jls.win32.Window.WS_CHILD;
261     	if (this.getStyle().getPropertyValue('visibility') == 'visible') {
262     		ws |= jls.win32.Window.WS_VISIBLE;
263     	}
264         // TODO Fix
265     	if (this.getStyle().getPropertyValue('border') != null) {
266     		ws |= jls.win32.Window.WS_BORDER;
267     	}
268     	if (this.getStyle().getPropertyValue('overflow') == 'scroll') {
269     		ws |= jls.win32.Window.WS_HSCROLL | jls.win32.Window.WS_VSCROLL;
270     	}
271         return ws;
272     },
273     getWindowId : function(create) {
274     	if (create) {
275     		var pw = this.getParentWindow();
276     		if ((pw != null)) {
277     			this._windowId = pw._windowUid++;
278     		}
279     	}
280         return this._windowId;
281     },
282     setWindowId : function(id) {
283         this._windowId = id;
284         return this;
285     },
286     /*setWindowExStyle : function(exStyle) {
287         this._windowExStyle = exStyle;
288         return this;
289     },
290     setWindowStyle : function(style) {
291         this._windowStyle = style;
292         return this;
293     },*/
294     // TODO setWindowIcon ?
295     getIcon : function() {
296         return this._icon;
297     },
298     setIcon : function(name) {
299         var icon = jls.win32.WindowElement.getIcon(name);
300         if (icon) {
301             this._icon = name;
302             if (this._window) {
303                 this._window.setIcon(icon);
304             }
305         }
306         return this;
307     },
308     /*setWindowFont : function(font) {
309         this.sendMessage(jls.win32.Window.WM_SETFONT, font, 0);
310         return this;
311     },*/
312     onWindowUpdate : function() {
313     	var offset = this.getWindowOffset();
314         var bounds = this.getBounds();
315         var locationChange = (offset[0] + bounds[0] != this._bounds[0]) || (offset[1] + bounds[1] != this._bounds[1]);
316         var sizeChange = (bounds[2] != this._bounds[2]) || (bounds[3] != this._bounds[3]);
317         if (locationChange || sizeChange) {
318             this._setPos(locationChange == sizeChange ? 0 : (locationChange ? jls.win32.Window.SWP_NOSIZE : jls.win32.Window.SWP_NOMOVE));
319         }
320     },
321     update : function($super) {
322     	this.onWindowUpdate();
323         $super();
324         return this;
325     },
326     _setPos : function(flag, offset) {
327         if (this._window) {
328         	offset = offset || this.getWindowOffset();
329             this._window.setPosition(offset[0] + this.getX(), offset[1] + this.getY(), this.getW(), this.getH(), flag || 0);
330             this._bounds = [offset[0] + this.getX(), offset[1] + this.getY(), this.getW(), this.getH()];
331         }
332     },
333     _getBounds : function() {
334         var rect = this._window.rect(true);
335         return [rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1]];
336     },
337     _getClientRect : function() {
338         return this._window.rect(true);
339     }
340 });
341 
342 Object.extend(jls.win32.WindowElement,
343 {
344     _topLevelWindowCount : 0,
345     _topLevelWindows : [],
346     _fontCache : {},
347     _iconCache : {},
348     getFont : function(family, size) {
349         var key = family + '#' + size;
350         //jls.logger.warn('key: "' + key + '"');
351         if (! (key in jls.win32.WindowElement._fontCache)) {
352             jls.win32.WindowElement._fontCache[key] = new jls.win32.Font(family, size);
353         }
354         return jls.win32.WindowElement._fontCache[key];
355     },
356     getIcon : function(iconFilename) {
357         if (! (iconFilename in jls.win32.WindowElement._iconCache)) {
358             if (new jls.io.File(iconFilename).exists()) {
359                 jls.win32.WindowElement._iconCache[iconFilename] = new jls.win32.Image(iconFilename, jls.win32.Image.IMAGE_ICON);
360             } else {
361                 jls.logger.debug('File not found ' + iconFilename);
362                 //throw new jls.lang.Exception('File not found ' + iconFilename);
363             }
364         }
365         return jls.win32.WindowElement._iconCache[iconFilename];
366     },
367     createMouseEvent : function(type, target, button, wParam, lParam) {
368         var event = new jls.gui.Event(type, target);
369         event.setMouse(button, jls.win32.Window.loWord(lParam), jls.win32.Window.hiWord(lParam), 0, 0);
370         event.control = (wParam & jls.win32.Window.MK_CONTROL) != 0;
371         event.shift = (wParam & jls.win32.Window.MK_SHIFT) != 0;
372         return event;
373     },
374     getWindows : function() {
375         // TODO make a copy
376         return jls.win32.WindowElement._topLevelWindows;
377     },
378     _onCreateTopLevelWindow : function(wnd) {
379         jls.win32.WindowElement._topLevelWindowCount++;
380         jls.logger.trace('add top-level window (' + jls.win32.WindowElement._topLevelWindowCount + ')');
381     },
382     _onDestroyTopLevelWindow : function(event) {
383         
384         jls.win32.WindowElement._topLevelWindowCount--;
385         jls.logger.trace('remove top-level window (' + jls.win32.WindowElement._topLevelWindowCount + ')');
386         if (jls.win32.WindowElement._topLevelWindowCount == 0) {
387             jls.win32.Window.destroyThreadWindow();
388         }
389     }
390 });
391 
392 
393 /*
394 mouseover: The mouseover event occurs when the pointing device is moved onto an element. This event is valid for most elements.
395 mouseout: The mouseout event occurs when the pointing device is moved away from an element. This event is valid for most elements..
396 abort: The abort event occurs when page loading is stopped before an image has been allowed to completely load. This event applies to OBJECT elements.
397 ?error: The error event occurs when an image does not load properly or when an error occurs during script execution. This event is valid for OBJECT elements, BODY elements, and FRAMESET element.
398 ?select: The select event occurs when a user selects some text in a text field. This event is valid for INPUT and TEXTAREA elements.
399 change: The change event occurs when a control loses the input focus and its value has been modified since gaining focus. This event is valid for INPUT, SELECT, and TEXTAREA. element.
400 ?submit: The submit event occurs when a form is submitted. This event only applies to the FORM element.
401 ?reset: The reset event occurs when a form is reset. This event only applies to the FORM element.
402 focus: The focus event occurs when an element receives focus either via a pointing device or by tabbing navigation. This event is valid for the following elements: LABEL, INPUT, SELECT, TEXTAREA, and BUTTON.
403 blur: The blur event occurs when an element loses focus either via the pointing device or by tabbing navigation. This event is valid for the following elements: LABEL, INPUT, SELECT, TEXTAREA, and BUTTON.
404 ?scroll: The scroll event occurs when a document view is scrolled.
405  */
406