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