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