CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / os2 / nsWindow.cpp
blob505c2f44e286ef2c72ce204be7c1932d163990d0
1 /* vim: set sw=2 sts=2 et cin: */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is the Mozilla OS/2 libraries.
19 * The Initial Developer of the Original Code is
20 * John Fairhurst, <john_fairhurst@iname.com>.
21 * Portions created by the Initial Developer are Copyright (C) 1999
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Pierre Phaneuf <pp@ludusdesign.com>
26 * IBM Corp.
27 * Rich Walsh <dragtext@e-vertise.com>
28 * Dan Rosen <dr@netscape.com>
29 * Dainis Jonitis <Dainis_Jonitis@swh-t.lv>
30 * Peter Weilbacher <mozilla@Weilbacher.org>
32 * Alternatively, the contents of this file may be used under the terms of
33 * either the GNU General Public License Version 2 or later (the "GPL"), or
34 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 * in which case the provisions of the GPL or the LGPL are applicable instead
36 * of those above. If you wish to allow use of your version of this file only
37 * under the terms of either the GPL or the LGPL, and not to allow others to
38 * use your version of this file under the terms of the MPL, indicate your
39 * decision by deleting the provisions above and replace them with the notice
40 * and other provisions required by the GPL or the LGPL. If you do not delete
41 * the provisions above, a recipient may use your version of this file under
42 * the terms of any one of the MPL, the GPL or the LGPL.
44 * ***** END LICENSE BLOCK *****/
46 //=============================================================================
48 * This file is divided into the following major sections:
50 * - Macros
51 * - Variables & Forward declarations
52 * - nsWindow Create / Destroy
53 * - Standard Window Operations
54 * - Window Positioning
55 * - Plugin Operations
56 * - Top-level (frame window) Operations
57 * - Mouse Pointers
58 * - Rollup Event Handlers
59 * - nsWindow's Window Procedure
60 * - Window Message Handlers
61 * - Drag & Drop - Target methods
62 * - Keyboard Handlers
63 * - Event Dispatch
66 //=============================================================================
68 #include "nsWindow.h"
69 #include "os2FrameWindow.h"
70 #include "gfxContext.h"
71 #include "gfxOS2Surface.h"
72 #include "imgIContainer.h"
73 #include "npapi.h"
74 #include "nsDragService.h"
75 #include "nsGfxCIID.h"
76 #include "nsHashKeys.h"
77 #include "nsIDeviceContext.h"
78 #include "nsIMenuRollup.h"
79 #include "nsIPrefService.h"
80 #include "nsIRollupListener.h"
81 #include "nsIScreenManager.h"
82 #include "nsOS2Uni.h"
83 #include "nsTHashtable.h"
84 #include "nsToolkit.h"
85 #include "nsWidgetAtoms.h"
86 #include "wdgtos2rc.h"
88 //=============================================================================
89 // Macros
90 //=============================================================================
92 // Drag and Drop
94 // d&d flags - actions that might cause problems during d&d
95 #define ACTION_PAINT 1
96 #define ACTION_DRAW 2
97 #define ACTION_SCROLL 3
98 #define ACTION_SHOW 4
99 #define ACTION_PTRPOS 5
101 // d&d status - shorten these references a bit
102 #define DND_None (nsIDragSessionOS2::DND_NONE)
103 #define DND_NativeDrag (nsIDragSessionOS2::DND_NATIVEDRAG)
104 #define DND_MozDrag (nsIDragSessionOS2::DND_MOZDRAG)
105 #define DND_InDrop (nsIDragSessionOS2::DND_INDROP)
106 #define DND_DragStatus (nsIDragSessionOS2::DND_DRAGSTATUS)
107 #define DND_DispatchEnterEvent (nsIDragSessionOS2::DND_DISPATCHENTEREVENT)
108 #define DND_DispatchEvent (nsIDragSessionOS2::DND_DISPATCHEVENT)
109 #define DND_GetDragoverResult (nsIDragSessionOS2::DND_GETDRAGOVERRESULT)
110 #define DND_ExitSession (nsIDragSessionOS2::DND_EXITSESSION)
112 //-----------------------------------------------------------------------------
113 // App Command messages for IntelliMouse and Natural Keyboard Pro
115 #define WM_APPCOMMAND 0x0319
117 #define APPCOMMAND_BROWSER_BACKWARD 1
118 #define APPCOMMAND_BROWSER_FORWARD 2
119 #define APPCOMMAND_BROWSER_REFRESH 3
120 #define APPCOMMAND_BROWSER_STOP 4
122 //-----------------------------------------------------------------------------
123 // Keyboard-related macros
125 // Used for character-to-keycode translation
126 #define PMSCAN_PADMULT 0x37
127 #define PMSCAN_PAD7 0x47
128 #define PMSCAN_PAD8 0x48
129 #define PMSCAN_PAD9 0x49
130 #define PMSCAN_PADMINUS 0x4A
131 #define PMSCAN_PAD4 0x4B
132 #define PMSCAN_PAD5 0x4C
133 #define PMSCAN_PAD6 0x4D
134 #define PMSCAN_PADPLUS 0x4E
135 #define PMSCAN_PAD1 0x4F
136 #define PMSCAN_PAD2 0x50
137 #define PMSCAN_PAD3 0x51
138 #define PMSCAN_PAD0 0x52
139 #define PMSCAN_PADPERIOD 0x53
140 #define PMSCAN_PADDIV 0x5c
142 #define isNumPadScanCode(scanCode) !((scanCode < PMSCAN_PAD7) || \
143 (scanCode > PMSCAN_PADPERIOD) || \
144 (scanCode == PMSCAN_PADMULT) || \
145 (scanCode == PMSCAN_PADDIV) || \
146 (scanCode == PMSCAN_PADMINUS) || \
147 (scanCode == PMSCAN_PADPLUS))
149 #define isNumlockOn (WinGetKeyState(HWND_DESKTOP, VK_NUMLOCK) & 0x0001)
150 #define isKeyDown(vk) ((WinGetKeyState(HWND_DESKTOP,vk) & 0x8000) == 0x8000)
152 //-----------------------------------------------------------------------------
153 // Miscellanea
155 // extract X & Y from a mouse msg mparam
156 #define XFROMMP(m) (SHORT(LOUSHORT(m)))
157 #define YFROMMP(m) (SHORT(HIUSHORT(m)))
159 // make these methods seem more appropriate in context
160 #define PM2NS_PARENT NS2PM_PARENT
161 #define PM2NS NS2PM
163 // used to identify plugin widgets (copied from nsPluginNativeWindowOS2.cpp)
164 #define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION \
165 "MozillaPluginWindowPropertyAssociation"
167 // name of the window class used to clip plugins
168 #define kClipWndClass "nsClipWnd"
170 //-----------------------------------------------------------------------------
171 // Debug
173 #ifdef DEBUG_FOCUS
174 #define DEBUGFOCUS(what) fprintf(stderr, "[%8x] %8lx (%02d) "#what"\n", \
175 (int)this, mWnd, mWindowIdentifier)
176 #else
177 #define DEBUGFOCUS(what)
178 #endif
180 //=============================================================================
181 // Variables & Forward declarations
182 //=============================================================================
184 // Rollup Listener - used by nsWindow & os2FrameWindow
185 nsIRollupListener* gRollupListener = 0;
186 nsIMenuRollup* gMenuRollup = 0;
187 nsIWidget* gRollupWidget = 0;
188 PRBool gRollupConsumeRollupEvent = PR_FALSE;
190 // Miscellaneous global flags
191 PRUint32 gOS2Flags = 0;
193 // Mouse pointers
194 static HPOINTER sPtrArray[IDC_COUNT];
196 // location of last MB1 down - used for mouse-based copy/paste
197 static POINTS sLastButton1Down = {0,0};
199 // set when any nsWindow is being dragged over
200 static PRUint32 sDragStatus = 0;
202 #ifdef DEBUG_FOCUS
203 int currentWindowIdentifier = 0;
204 #endif
206 //-----------------------------------------------------------------------------
208 static PRUint32 WMChar2KeyCode(MPARAM mp1, MPARAM mp2);
210 //=============================================================================
211 // nsWindow Create / Destroy
212 //=============================================================================
214 nsWindow::nsWindow() : nsBaseWidget()
216 mWnd = 0;
217 mParent = 0;
218 mFrame = 0;
219 mWindowType = eWindowType_toplevel;
220 mBorderStyle = eBorderStyle_default;
221 mWindowState = nsWindowState_ePrecreate;
222 mOnDestroyCalled = PR_FALSE;
223 mIsDestroying = PR_FALSE;
224 mInSetFocus = PR_FALSE;
225 mNoPaint = PR_FALSE;
226 mDragHps = 0;
227 mDragStatus = 0;
228 mClipWnd = 0;
229 mCssCursorHPtr = 0;
230 mThebesSurface = 0;
232 if (!gOS2Flags) {
233 InitGlobals();
237 //-----------------------------------------------------------------------------
239 nsWindow::~nsWindow()
241 // How destruction works: A call of Destroy() destroys the PM window. This
242 // triggers an OnDestroy(), which frees resources. If not Destroy'd at
243 // delete time, Destroy() gets called anyway.
245 // NOTE: Calling virtual functions from destructors is bad; they always
246 // bind in the current object (ie. as if they weren't virtual). It
247 // may even be illegal to call them from here.
249 mIsDestroying = PR_TRUE;
251 if (mCssCursorHPtr) {
252 WinDestroyPointer(mCssCursorHPtr);
253 mCssCursorHPtr = 0;
256 // If the widget was released without calling Destroy() then
257 // the native window still exists, and we need to destroy it
258 if (!(mWindowState & nsWindowState_eDead)) {
259 mWindowState |= nsWindowState_eDoingDelete;
260 mWindowState &= ~(nsWindowState_eLive | nsWindowState_ePrecreate |
261 nsWindowState_eInCreate);
262 Destroy();
265 // Once a plugin window has been destroyed,
266 // its parent, the clipping window, can be destroyed.
267 if (mClipWnd) {
268 WinDestroyWindow(mClipWnd);
269 mClipWnd = 0;
272 // If it exists, destroy our os2FrameWindow helper object.
273 if (mFrame) {
274 delete mFrame;
275 mFrame = 0;
279 //-----------------------------------------------------------------------------
280 // Init Module-level variables.
282 // static
283 void nsWindow::InitGlobals()
285 gOS2Flags = kIsInitialized;
287 // Register the MozillaWindowClass with PM.
288 WinRegisterClass(0, kWindowClassName, fnwpNSWindow, 0, 8);
290 // Register the dummy window class used to clip plugins.
291 WinRegisterClass(0, kClipWndClass, WinDefWindowProc, 0, 4);
293 // Load the mouse pointers from the dll containing 'gOS2Flags'.
294 HMODULE hModResources = 0;
295 DosQueryModFromEIP(&hModResources, 0, 0, 0, 0, (ULONG)&gOS2Flags);
296 for (int i = 0; i < IDC_COUNT; i++) {
297 sPtrArray[i] = WinLoadPointer(HWND_DESKTOP, hModResources, IDC_BASE+i);
300 // Work out if the system is DBCS.
301 char buffer[16];
302 COUNTRYCODE cc = { 0 };
303 DosQueryDBCSEnv(sizeof(buffer), &cc, buffer);
304 if (buffer[0] || buffer[1]) {
305 gOS2Flags |= kIsDBCS;
308 // This is ugly. The Thinkpad TrackPoint driver checks to see whether
309 // or not a window actually has a scroll bar as a child before sending
310 // it scroll messages. Needless to say, no Mozilla window has real scroll
311 // bars. So if you have the "os2.trackpoint" preference set, we put an
312 // invisible scroll bar on every child window so we can scroll.
313 nsresult rv;
314 nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
315 if (NS_SUCCEEDED(rv) && prefs) {
316 PRBool isTrackPoint = PR_FALSE;
317 prefs->GetBoolPref("os2.trackpoint", &isTrackPoint);
318 if (isTrackPoint) {
319 gOS2Flags |= kIsTrackPoint;
324 //-----------------------------------------------------------------------------
325 // Release Module-level variables.
327 // static
328 void nsWindow::ReleaseGlobals()
330 for (int i = 0; i < IDC_COUNT; i++) {
331 WinDestroyPointer(sPtrArray[i]);
335 //-----------------------------------------------------------------------------
336 // Init an nsWindow & create the appropriate native window.
338 NS_METHOD nsWindow::Create(nsIWidget* aParent,
339 nsNativeWidget aNativeParent,
340 const nsIntRect& aRect,
341 EVENT_CALLBACK aHandleEventFunction,
342 nsIDeviceContext* aContext,
343 nsIAppShell* aAppShell,
344 nsIToolkit* aToolkit,
345 nsWidgetInitData* aInitData)
347 mWindowState = nsWindowState_eInCreate;
349 // Identify the parent's nsWindow & native window. Only one of these
350 // should be supplied. Note: only nsWindow saves pParent as mParent;
351 // os2FrameWindow discards it since toplevel widgets have no parent.
352 HWND hParent;
353 nsWindow* pParent;
354 if (aParent) {
355 hParent = (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW);
356 pParent = (nsWindow*)aParent;
357 } else {
358 if (aNativeParent && (HWND)aNativeParent != HWND_DESKTOP) {
359 hParent = (HWND)aNativeParent;
360 pParent = GetNSWindowPtr(hParent);
361 } else {
362 hParent = HWND_DESKTOP;
363 pParent = 0;
367 // Save the event callback function.
368 mEventCallback = aHandleEventFunction;
370 // Make sure a device context exists.
371 if (aContext) {
372 mContext = aContext;
373 NS_ADDREF(mContext);
374 } else {
375 static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID);
376 nsresult rv = CallCreateInstance(kDeviceContextCID, &mContext);
377 NS_ENSURE_SUCCESS(rv, rv);
378 mContext->Init(nsnull);
381 // XXX Toolkit is obsolete & will be removed.
382 if (!mToolkit) {
383 if (aToolkit) {
384 mToolkit = aToolkit;
385 } else if (pParent) {
386 mToolkit = pParent->GetToolkit();
387 } else {
388 mToolkit = new nsToolkit;
389 mToolkit->Init(PR_GetCurrentThread());
391 NS_ADDREF(mToolkit);
394 #ifdef DEBUG_FOCUS
395 mWindowIdentifier = currentWindowIdentifier;
396 currentWindowIdentifier++;
397 #endif
399 // Some basic initialization.
400 if (aInitData) {
401 mWindowType = aInitData->mWindowType;
402 mBorderStyle = aInitData->mBorderStyle;
404 // Suppress creation of a Thebes surface for windows that will never
405 // be painted because they're always covered by another window.
406 if (mWindowType == eWindowType_toplevel ||
407 mWindowType == eWindowType_invisible) {
408 mNoPaint = PR_TRUE;
410 // Popup windows should not have an nsWindow parent.
411 else if (mWindowType == eWindowType_popup) {
412 pParent = 0;
416 // For toplevel windows, create an instance of our helper class,
417 // then have it create a frame & client window; otherwise,
418 // call our own CreateWindow() method to create a child window.
419 if (mWindowType == eWindowType_toplevel ||
420 mWindowType == eWindowType_dialog ||
421 mWindowType == eWindowType_invisible) {
422 mFrame = new os2FrameWindow(this);
423 NS_ENSURE_TRUE(mFrame, NS_ERROR_FAILURE);
424 mWnd = mFrame->CreateFrameWindow(pParent, hParent, aRect,
425 mWindowType, mBorderStyle);
426 NS_ENSURE_TRUE(mWnd, NS_ERROR_FAILURE);
427 } else {
428 nsresult rv = CreateWindow(pParent, hParent, aRect, aInitData);
429 NS_ENSURE_SUCCESS(rv, rv);
432 // Store a pointer to this object in the window's extra bytes.
433 SetNSWindowPtr(mWnd, this);
435 // Finalize the widget creation process.
436 nsGUIEvent event(PR_TRUE, NS_CREATE, this);
437 InitEvent(event);
438 DispatchWindowEvent(&event);
440 mWindowState = nsWindowState_eLive;
441 return NS_OK;
444 //-----------------------------------------------------------------------------
445 // Create a native window for an nsWindow object.
447 nsresult nsWindow::CreateWindow(nsWindow* aParent,
448 HWND aParentWnd,
449 const nsIntRect& aRect,
450 nsWidgetInitData* aInitData)
452 // For pop-ups, the Desktop is the parent and aParentWnd is the owner.
453 HWND hOwner = 0;
454 if (mWindowType == eWindowType_popup && aParentWnd != HWND_DESKTOP) {
455 hOwner = aParentWnd;
456 aParentWnd = HWND_DESKTOP;
459 // While we comply with the clipSiblings flag, we always set
460 // clipChildren regardless of the flag for performance reasons.
461 PRUint32 style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
462 if (aInitData && !aInitData->clipSiblings) {
463 style &= ~WS_CLIPSIBLINGS;
466 // Create the window hidden; it will be resized below.
467 mWnd = WinCreateWindow(aParentWnd,
468 kWindowClassName,
470 style,
471 0, 0, 0, 0,
472 hOwner,
473 HWND_TOP,
475 0, 0);
476 NS_ENSURE_TRUE(mWnd, NS_ERROR_FAILURE);
478 // If a TrackPoint is in use, create dummy scrollbars.
479 // XXX Popups may need this also to scroll comboboxes.
480 if ((gOS2Flags & kIsTrackPoint) && mWindowType == eWindowType_child) {
481 WinCreateWindow(mWnd, WC_SCROLLBAR, 0, SBS_VERT,
482 0, 0, 0, 0, mWnd, HWND_TOP,
483 FID_VERTSCROLL, 0, 0);
486 // Store the window's dimensions, then resize accordingly.
487 mBounds = aRect;
488 nsIntRect parRect;
489 if (aParent) {
490 aParent->GetBounds(parRect);
491 } else {
492 parRect.height = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
494 WinSetWindowPos(mWnd, 0,
495 aRect.x, parRect.height - aRect.y - aRect.height,
496 aRect.width, aRect.height, SWP_SIZE | SWP_MOVE);
498 // Store the widget's parent and add it to the parent's list of children.
499 // Don't ADDREF mParent because AddChild() ADDREFs us.
500 mParent = aParent;
501 if (mParent) {
502 mParent->AddChild(this);
505 DEBUGFOCUS(Create nsWindow);
506 return NS_OK;
509 //-----------------------------------------------------------------------------
510 // Close this nsWindow.
512 NS_METHOD nsWindow::Destroy()
514 // avoid calling into other objects if we're being deleted, 'cos
515 // they must have no references to us.
516 if ((mWindowState & nsWindowState_eLive) && mParent) {
517 nsBaseWidget::Destroy();
520 // just to be safe. If we're going away and for some reason we're still
521 // the rollup widget, rollup and turn off capture.
522 if (this == gRollupWidget) {
523 if (gRollupListener) {
524 gRollupListener->Rollup(PR_UINT32_MAX, nsnull);
526 CaptureRollupEvents(nsnull, nsnull, PR_FALSE, PR_TRUE);
529 HWND hMain = GetMainWindow();
530 if (hMain) {
531 DEBUGFOCUS(Destroy);
532 if (hMain == WinQueryFocus(HWND_DESKTOP)) {
533 WinSetFocus(HWND_DESKTOP, WinQueryWindow(hMain, QW_PARENT));
535 WinDestroyWindow(hMain);
537 return NS_OK;
540 //=============================================================================
541 // Standard Window Operations
542 //=============================================================================
544 // This can't be inlined in nsWindow.h because it doesn't know about
545 // GetFrameWnd().
547 inline HWND nsWindow::GetMainWindow()
549 return mFrame ? mFrame->GetFrameWnd() : mWnd;
552 //-----------------------------------------------------------------------------
553 // Inline this here for consistency (and a cleaner looking .h).
555 // static
556 inline nsWindow* nsWindow::GetNSWindowPtr(HWND aWnd)
558 return (nsWindow*)WinQueryWindowPtr(aWnd, QWL_NSWINDOWPTR);
561 //-----------------------------------------------------------------------------
563 // static
564 inline PRBool nsWindow::SetNSWindowPtr(HWND aWnd, nsWindow* aPtr)
566 return WinSetWindowPtr(aWnd, QWL_NSWINDOWPTR, aPtr);
569 //-----------------------------------------------------------------------------
571 nsIWidget* nsWindow::GetParent()
573 // if this window isn't supposed to have a parent or it doesn't have
574 // a parent, or if it or its parent is being destroyed, return null
575 if (mFrame || mIsDestroying || mOnDestroyCalled ||
576 !mParent || mParent->mIsDestroying) {
577 return 0;
580 return mParent;
583 //-----------------------------------------------------------------------------
585 NS_METHOD nsWindow::Enable(PRBool aState)
587 HWND hMain = GetMainWindow();
588 if (hMain) {
589 WinEnableWindow(hMain, aState);
591 return NS_OK;
594 //-----------------------------------------------------------------------------
596 NS_METHOD nsWindow::IsEnabled(PRBool* aState)
598 NS_ENSURE_ARG_POINTER(aState);
599 HWND hMain = GetMainWindow();
600 *aState = !hMain || WinIsWindowEnabled(hMain);
601 return NS_OK;
604 //-----------------------------------------------------------------------------
606 NS_METHOD nsWindow::Show(PRBool aState)
608 if (mFrame) {
609 return mFrame->Show(aState);
611 if (mWnd) {
612 if (aState) {
613 // don't try to show new windows (e.g. the Bookmark menu)
614 // during a native dragover because they'll remain invisible;
615 if (CheckDragStatus(ACTION_SHOW, 0)) {
616 PRBool isVisible;
617 IsVisible(isVisible);
618 if (!isVisible) {
619 PlaceBehind(eZPlacementTop, 0, PR_FALSE);
621 WinShowWindow(mWnd, PR_TRUE);
623 } else {
624 WinShowWindow(mWnd, PR_FALSE);
628 return NS_OK;
631 //-----------------------------------------------------------------------------
633 NS_METHOD nsWindow::IsVisible(PRBool& aState)
635 aState = WinIsWindowVisible(GetMainWindow()) ? PR_TRUE : PR_FALSE;
636 return NS_OK;
639 //-----------------------------------------------------------------------------
641 NS_METHOD nsWindow::SetFocus(PRBool aRaise)
643 // for toplevel windows, this is directed to the client (i.e. mWnd)
644 if (mWnd) {
645 if (!mInSetFocus) {
646 DEBUGFOCUS(SetFocus);
647 mInSetFocus = PR_TRUE;
648 WinSetFocus(HWND_DESKTOP, mWnd);
649 mInSetFocus = PR_FALSE;
652 return NS_OK;
655 //-----------------------------------------------------------------------------
657 NS_METHOD nsWindow::Invalidate(const nsIntRect& aRect, PRBool aIsSynchronous)
659 if (mWnd) {
660 RECTL rcl = {aRect.x, aRect.y, aRect.x + aRect.width, aRect.y + aRect.height};
661 NS2PM(rcl);
662 WinInvalidateRect(mWnd, &rcl, PR_FALSE);
663 #if 0
664 if (aIsSynchronous) {
665 Update();
667 #endif
669 return NS_OK;
672 //-----------------------------------------------------------------------------
673 // Force a synchronous repaint of the window.
675 NS_IMETHODIMP nsWindow::Update()
677 if (mWnd) {
678 WinUpdateWindow(mWnd);
680 return NS_OK;
683 //-----------------------------------------------------------------------------
684 // Create a Thebes surface using the current window handle.
686 gfxASurface* nsWindow::GetThebesSurface()
688 if (mWnd && !mThebesSurface) {
689 mThebesSurface = new gfxOS2Surface(mWnd);
691 return mThebesSurface;
694 //-----------------------------------------------------------------------------
695 // Internal-only method that suppresses creation of a Thebes surface
696 // for windows that aren't supposed to be visible. If one was created
697 // by an external call to GetThebesSurface(), it will be returned.
699 gfxASurface* nsWindow::ConfirmThebesSurface()
701 if (!mThebesSurface && !mNoPaint && mWnd) {
702 mThebesSurface = new gfxOS2Surface(mWnd);
704 return mThebesSurface;
707 //-----------------------------------------------------------------------------
709 float nsWindow::GetDPI()
711 static PRInt32 sDPI = 0;
713 // Create DC compatible with the screen, then query the DPI setting.
714 // If this fails, fall back to something sensible.
715 if (!sDPI) {
716 HDC dc = DevOpenDC(0, OD_MEMORY,"*",0L, 0, 0);
717 if (dc > 0) {
718 LONG lDPI;
719 if (DevQueryCaps(dc, CAPS_VERTICAL_FONT_RES, 1, &lDPI))
720 sDPI = lDPI;
721 DevCloseDC(dc);
723 if (sDPI <= 0) {
724 sDPI = 96;
727 return sDPI;
730 //-----------------------------------------------------------------------------
731 // Return some native data according to aDataType.
733 void* nsWindow::GetNativeData(PRUint32 aDataType)
735 switch(aDataType) {
736 case NS_NATIVE_WIDGET:
737 case NS_NATIVE_WINDOW:
738 case NS_NATIVE_PLUGIN_PORT:
739 return (void*)mWnd;
741 // during a native drag over the current window or any drag
742 // originating in Moz, return a drag HPS to avoid screen corruption;
743 case NS_NATIVE_GRAPHIC: {
744 HPS hps = 0;
745 CheckDragStatus(ACTION_DRAW, &hps);
746 if (!hps) {
747 hps = WinGetPS(mWnd);
749 return (void*)hps;
753 return 0;
756 //-----------------------------------------------------------------------------
758 void nsWindow::FreeNativeData(void* data, PRUint32 aDataType)
760 // an HPS is the only native data that needs to be freed
761 if (aDataType == NS_NATIVE_GRAPHIC &&
762 data &&
763 !ReleaseIfDragHPS((HPS)data)) {
764 WinReleasePS((HPS)data);
768 //-----------------------------------------------------------------------------
770 NS_METHOD nsWindow::CaptureMouse(PRBool aCapture)
772 if (aCapture) {
773 WinSetCapture(HWND_DESKTOP, mWnd);
774 } else {
775 WinSetCapture(HWND_DESKTOP, 0);
777 return NS_OK;
780 //-----------------------------------------------------------------------------
782 PRBool nsWindow::HasPendingInputEvent()
784 return (WinQueryQueueStatus(HWND_DESKTOP) & (QS_KEY | QS_MOUSE)) != 0;
787 //=============================================================================
788 // Window Positioning
789 //=============================================================================
791 // For toplevel windows, mBounds contains the dimensions of the client
792 // window. os2FrameWindow's "override" returns the size of the frame.
794 NS_METHOD nsWindow::GetBounds(nsIntRect& aRect)
796 if (mFrame) {
797 return mFrame->GetBounds(aRect);
799 aRect = mBounds;
800 return NS_OK;
803 //-----------------------------------------------------------------------------
804 // Since mBounds contains the dimensions of the client, os2FrameWindow
805 // doesn't have to provide any special handling for this method.
807 NS_METHOD nsWindow::GetClientBounds(nsIntRect& aRect)
809 aRect.x = 0;
810 aRect.y = 0;
811 aRect.width = mBounds.width;
812 aRect.height = mBounds.height;
813 return NS_OK;
816 //-----------------------------------------------------------------------------
818 nsIntPoint nsWindow::WidgetToScreenOffset()
820 POINTL point = { 0, 0 };
821 NS2PM(point);
823 WinMapWindowPoints(mWnd, HWND_DESKTOP, &point, 1);
824 return nsIntPoint(point.x,
825 WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - point.y - 1);
828 //-----------------------------------------------------------------------------
829 // Transform Y values between PM & XP coordinate systems.
831 // ptl is in this window's space
832 void nsWindow::NS2PM(POINTL& ptl)
834 ptl.y = mBounds.height - ptl.y - 1;
837 // rcl is in this window's space
838 void nsWindow::NS2PM(RECTL& rcl)
840 LONG height = rcl.yTop - rcl.yBottom;
841 rcl.yTop = mBounds.height - rcl.yBottom;
842 rcl.yBottom = rcl.yTop - height;
845 // ptl is in parent's space
846 void nsWindow::NS2PM_PARENT(POINTL& ptl)
848 if (mParent) {
849 mParent->NS2PM(ptl);
850 } else {
851 HWND hParent = WinQueryWindow(mWnd, QW_PARENT);
852 SWP swp;
853 WinQueryWindowPos(hParent, &swp);
854 ptl.y = swp.cy - ptl.y - 1;
858 //-----------------------------------------------------------------------------
860 NS_METHOD nsWindow::Move(PRInt32 aX, PRInt32 aY)
862 if (mFrame) {
863 return mFrame->Move(aX, aY);
865 Resize(aX, aY, mBounds.width, mBounds.height, PR_FALSE);
866 return NS_OK;
869 //-----------------------------------------------------------------------------
871 NS_METHOD nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
873 if (mFrame) {
874 return mFrame->Resize(aWidth, aHeight, aRepaint);
876 Resize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint);
877 return NS_OK;
880 //-----------------------------------------------------------------------------
882 NS_METHOD nsWindow::Resize(PRInt32 aX, PRInt32 aY,
883 PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
885 if (mFrame) {
886 return mFrame->Resize(aX, aY, aWidth, aHeight, aRepaint);
889 // For mWnd & eWindowType_child set the cached values upfront, see bug 286555.
890 // For other mWnd types we defer transfer of values to mBounds to
891 // WinSetWindowPos(), see bug 391421.
893 if (!mWnd ||
894 mWindowType == eWindowType_child ||
895 mWindowType == eWindowType_plugin) {
896 mBounds.x = aX;
897 mBounds.y = aY;
898 mBounds.width = aWidth;
899 mBounds.height = aHeight;
902 // To keep top-left corner in the same place, use the new height
903 // to calculate the coordinates for the top & bottom left corners.
904 if (mWnd) {
905 POINTL ptl = { aX, aY };
906 NS2PM_PARENT(ptl);
907 ptl.y -= aHeight - 1;
909 // For popups, aX already gives the correct position.
910 if (mWindowType == eWindowType_popup) {
911 ptl.y = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - aHeight - 1 - aY;
913 else if (mParent) {
914 WinMapWindowPoints(mParent->mWnd, WinQueryWindow(mWnd, QW_PARENT),
915 &ptl, 1);
918 if (!WinSetWindowPos(mWnd, 0, ptl.x, ptl.y, aWidth, aHeight,
919 SWP_MOVE | SWP_SIZE) && aRepaint) {
920 WinInvalidateRect(mWnd, 0, FALSE);
924 return NS_OK;
927 //-----------------------------------------------------------------------------
929 NS_METHOD nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
930 nsIWidget* aWidget, PRBool aActivate)
932 HWND hBehind = HWND_TOP;
934 if (aPlacement == eZPlacementBottom) {
935 hBehind = HWND_BOTTOM;
936 } else
937 if (aPlacement == eZPlacementBelow && aWidget) {
938 hBehind = (static_cast<nsWindow*>(aWidget))->GetMainWindow();
941 PRUint32 flags = SWP_ZORDER;
942 if (aActivate) {
943 flags |= SWP_ACTIVATE;
946 WinSetWindowPos(GetMainWindow(), hBehind, 0, 0, 0, 0, flags);
947 return NS_OK;
950 //-----------------------------------------------------------------------------
951 // Set widget's position within its parent child list.
953 NS_METHOD nsWindow::SetZIndex(PRInt32 aZIndex)
955 // nsBaseWidget::SetZIndex() never has done anything sensible but
956 // has randomly placed widgets behind others (see bug 117730#c25).
957 // To get bug #353011 solved simply override it here to do nothing.
958 return NS_OK;
961 //=============================================================================
962 // Plugin Operations
963 //=============================================================================
965 // Fire an NS_PLUGIN_ACTIVATE event whenever a window associated
966 // with a plugin widget get the focus.
968 void nsWindow::ActivatePlugin(HWND aWnd)
970 // avoid acting on recursive WM_FOCUSCHANGED msgs
971 static PRBool inPluginActivate = FALSE;
972 if (inPluginActivate) {
973 return;
976 // This property is used by the plugin window to store a pointer
977 // to its plugin object. We just use it as a convenient marker.
978 if (!WinQueryProperty(mWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION)) {
979 return;
982 // Fire a plugin activation event on the plugin widget.
983 inPluginActivate = TRUE;
984 DEBUGFOCUS(NS_PLUGIN_ACTIVATE);
985 DispatchActivationEvent(NS_PLUGIN_ACTIVATE);
987 // Activating the plugin moves the focus off the child that had it,
988 // so try to restore it. If the WM_FOCUSCHANGED msg was synthesized
989 // by the plugin, then mp1 contains the child window that lost focus.
990 // Otherwise, just move it to the plugin's first child unless this
991 // is the mplayer plugin - doing so will put us into an endless loop.
992 // Since its children belong to another process, use the PID as a test.
993 HWND hFocus = 0;
994 if (WinIsChild(aWnd, mWnd)) {
995 hFocus = aWnd;
996 } else {
997 hFocus = WinQueryWindow(mWnd, QW_TOP);
998 if (hFocus) {
999 PID pidFocus, pidThis;
1000 TID tid;
1001 WinQueryWindowProcess(hFocus, &pidFocus, &tid);
1002 WinQueryWindowProcess(mWnd, &pidThis, &tid);
1003 if (pidFocus != pidThis) {
1004 hFocus = 0;
1008 if (hFocus) {
1009 WinSetFocus(HWND_DESKTOP, hFocus);
1012 inPluginActivate = FALSE;
1013 return;
1016 //-----------------------------------------------------------------------------
1017 // This is invoked on a window that has plugin widget children
1018 // to resize and clip those child windows.
1020 nsresult nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
1022 for (PRUint32 i = 0; i < aConfigurations.Length(); ++i) {
1023 const Configuration& configuration = aConfigurations[i];
1024 nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
1025 NS_ASSERTION(w->GetParent() == this,
1026 "Configured widget is not a child");
1027 w->SetPluginClipRegion(configuration);
1029 return NS_OK;
1032 //-----------------------------------------------------------------------------
1033 // This is invoked on a plugin window to resize it and set a persistent
1034 // clipping region for it. Since the latter isn't possible on OS/2, it
1035 // inserts a dummy window between the plugin widget and its parent to
1036 // act as a clipping rectangle. The dummy window's dimensions and the
1037 // plugin widget's position within the window are adjusted to correspond
1038 // to the bounding box of the supplied array of clipping rectangles.
1039 // Note: this uses PM calls rather than existing methods like Resize()
1040 // and Update() because none of them support the options needed here.
1042 void nsWindow::SetPluginClipRegion(const Configuration& aConfiguration)
1044 NS_ASSERTION((mParent && mParent->mWnd), "Child window has no parent");
1046 // If nothing has changed, exit.
1047 if (!StoreWindowClipRegion(aConfiguration.mClipRegion) &&
1048 mBounds == aConfiguration.mBounds) {
1049 return;
1052 // Set the widget's x/y to its nominal unclipped value. It doesn't
1053 // affect our calculations but other code relies on it being correct.
1054 mBounds.MoveTo(aConfiguration.mBounds.TopLeft());
1056 // Get or create the PM window we use as a clipping rectangle.
1057 HWND hClip = GetPluginClipWindow(mParent->mWnd);
1058 NS_ASSERTION(hClip, "No clipping window for plugin");
1059 if (!hClip) {
1060 return;
1063 // Create the bounding box for the clip region.
1064 const nsTArray<nsIntRect>& rects = aConfiguration.mClipRegion;
1065 nsIntRect r;
1066 for (PRUint32 i = 0; i < rects.Length(); ++i) {
1067 r.UnionRect(r, rects[i]);
1070 // Size and position hClip to match the bounding box.
1071 SWP swp;
1072 POINTL ptl;
1073 WinQueryWindowPos(hClip, &swp);
1074 ptl.x = aConfiguration.mBounds.x + r.x;
1075 ptl.y = mParent->mBounds.height
1076 - (aConfiguration.mBounds.y + r.y + r.height);
1078 ULONG clipFlags = 0;
1079 if (swp.x != ptl.x || swp.y != ptl.y) {
1080 clipFlags |= SWP_MOVE;
1082 if (swp.cx != r.width || swp.cy != r.height) {
1083 clipFlags |= SWP_SIZE;
1085 if (clipFlags) {
1086 WinSetWindowPos(hClip, 0, ptl.x, ptl.y, r.width, r.height, clipFlags);
1089 // Reducing the size of hClip clips the right & top sides of the
1090 // plugin widget. To clip the left & bottom sides, we have to move
1091 // the widget so its origin's x and/or y is negative wrt hClip.
1092 WinQueryWindowPos(mWnd, &swp);
1093 ptl.x = -r.x;
1094 ptl.y = r.height + r.y - aConfiguration.mBounds.height;
1096 ULONG wndFlags = 0;
1097 if (swp.x != ptl.x || swp.y != ptl.y) {
1098 wndFlags |= SWP_MOVE;
1100 if (mBounds.Size() != aConfiguration.mBounds.Size()) {
1101 wndFlags |= SWP_SIZE;
1103 if (wndFlags) {
1104 WinSetWindowPos(mWnd, 0, ptl.x, ptl.y,
1105 aConfiguration.mBounds.width,
1106 aConfiguration.mBounds.height, wndFlags);
1109 // Some plugins don't resize themselves when the plugin widget changes
1110 // size, so help them out by resizing the first child (usually a frame).
1111 if (wndFlags & SWP_SIZE) {
1112 HWND hChild = WinQueryWindow(mWnd, QW_TOP);
1113 if (hChild) {
1114 WinSetWindowPos(hChild, 0, 0, 0,
1115 aConfiguration.mBounds.width,
1116 aConfiguration.mBounds.height,
1117 SWP_MOVE | SWP_SIZE);
1121 // When hClip is resized, mWnd and its children may not get updated
1122 // automatically, so invalidate & repaint them
1123 if (clipFlags & SWP_SIZE) {
1124 WinInvalidateRect(mWnd, 0, TRUE);
1125 WinUpdateWindow(mWnd);
1129 //-----------------------------------------------------------------------------
1130 // This gets or creates a window that's inserted between the main window
1131 // and its plugin children. This window does nothing except act as a
1132 // clipping rectangle for the plugin widget.
1134 HWND nsWindow::GetPluginClipWindow(HWND aParentWnd)
1136 if (mClipWnd) {
1137 return mClipWnd;
1140 // Insert a new clip window in the hierarchy between mWnd & aParentWnd.
1141 mClipWnd = WinCreateWindow(aParentWnd, kClipWndClass, "",
1142 WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
1143 0, 0, 0, 0, 0, mWnd, 0, 0, 0);
1144 if (mClipWnd) {
1145 if (!WinSetParent(mWnd, mClipWnd, FALSE)) {
1146 WinDestroyWindow(mClipWnd);
1147 mClipWnd = 0;
1151 return mClipWnd;
1154 //=============================================================================
1155 // Top-level (frame window) Operations
1156 //=============================================================================
1158 // When a window gets the focus, call os2FrameWindow's version of this
1159 // method. It will fire an NS_ACTIVATE event on the top-level widget
1160 // if appropriate.
1162 void nsWindow::ActivateTopLevelWidget()
1164 if (mFrame) {
1165 mFrame->ActivateTopLevelWidget();
1166 } else {
1167 nsWindow* top = static_cast<nsWindow*>(GetTopLevelWidget());
1168 if (top && top->mFrame) {
1169 top->mFrame->ActivateTopLevelWidget();
1172 return;
1175 //-----------------------------------------------------------------------------
1176 // All of these methods are inherently toplevel-only, and are in fact
1177 // only invoked on toplevel widgets. If they're invoked on a child
1178 // window, there's an error upstream.
1180 NS_IMETHODIMP nsWindow::SetSizeMode(PRInt32 aMode)
1182 NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED);
1183 return mFrame->SetSizeMode(aMode);
1186 NS_IMETHODIMP nsWindow::HideWindowChrome(PRBool aShouldHide)
1188 NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED);
1189 return mFrame->HideWindowChrome(aShouldHide);
1192 NS_METHOD nsWindow::SetTitle(const nsAString& aTitle)
1194 NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED);
1195 return mFrame->SetTitle(aTitle);
1198 NS_METHOD nsWindow::SetIcon(const nsAString& aIconSpec)
1200 NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED);
1201 return mFrame->SetIcon(aIconSpec);
1204 NS_METHOD nsWindow::ConstrainPosition(PRBool aAllowSlop,
1205 PRInt32* aX, PRInt32* aY)
1207 NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED);
1208 return mFrame->ConstrainPosition(aAllowSlop, aX, aY);
1211 //=============================================================================
1212 // Mouse Pointers
1213 //=============================================================================
1215 // Set one of the standard mouse pointers.
1217 NS_METHOD nsWindow::SetCursor(nsCursor aCursor)
1219 HPOINTER newPointer = 0;
1221 switch (aCursor) {
1222 case eCursor_select:
1223 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_TEXT, FALSE);
1224 break;
1226 case eCursor_wait:
1227 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_WAIT, FALSE);
1228 break;
1230 case eCursor_hyperlink:
1231 newPointer = sPtrArray[IDC_SELECTANCHOR-IDC_BASE];
1232 break;
1234 case eCursor_standard:
1235 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_ARROW, FALSE);
1236 break;
1238 case eCursor_n_resize:
1239 case eCursor_s_resize:
1240 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENS, FALSE);
1241 break;
1243 case eCursor_w_resize:
1244 case eCursor_e_resize:
1245 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZEWE, FALSE);
1246 break;
1248 case eCursor_nw_resize:
1249 case eCursor_se_resize:
1250 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENWSE, FALSE);
1251 break;
1253 case eCursor_ne_resize:
1254 case eCursor_sw_resize:
1255 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENESW, FALSE);
1256 break;
1258 case eCursor_crosshair:
1259 newPointer = sPtrArray[IDC_CROSS-IDC_BASE];
1260 break;
1262 case eCursor_move:
1263 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_MOVE, FALSE);
1264 break;
1266 case eCursor_help:
1267 newPointer = sPtrArray[IDC_HELP-IDC_BASE];
1268 break;
1270 case eCursor_copy: // CSS3
1271 newPointer = sPtrArray[IDC_COPY-IDC_BASE];
1272 break;
1274 case eCursor_alias:
1275 newPointer = sPtrArray[IDC_ALIAS-IDC_BASE];
1276 break;
1278 case eCursor_cell:
1279 newPointer = sPtrArray[IDC_CELL-IDC_BASE];
1280 break;
1282 case eCursor_grab:
1283 newPointer = sPtrArray[IDC_GRAB-IDC_BASE];
1284 break;
1286 case eCursor_grabbing:
1287 newPointer = sPtrArray[IDC_GRABBING-IDC_BASE];
1288 break;
1290 case eCursor_spinning:
1291 newPointer = sPtrArray[IDC_ARROWWAIT-IDC_BASE];
1292 break;
1294 case eCursor_context_menu:
1295 // XXX this CSS3 cursor needs to be implemented
1296 break;
1298 case eCursor_zoom_in:
1299 newPointer = sPtrArray[IDC_ZOOMIN-IDC_BASE];
1300 break;
1302 case eCursor_zoom_out:
1303 newPointer = sPtrArray[IDC_ZOOMOUT-IDC_BASE];
1304 break;
1306 case eCursor_not_allowed:
1307 case eCursor_no_drop:
1308 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_ILLEGAL, FALSE);
1309 break;
1311 case eCursor_col_resize:
1312 newPointer = sPtrArray[IDC_COLRESIZE-IDC_BASE];
1313 break;
1315 case eCursor_row_resize:
1316 newPointer = sPtrArray[IDC_ROWRESIZE-IDC_BASE];
1317 break;
1319 case eCursor_vertical_text:
1320 newPointer = sPtrArray[IDC_VERTICALTEXT-IDC_BASE];
1321 break;
1323 case eCursor_all_scroll:
1324 // XXX not 100% appropriate perhaps
1325 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_MOVE, FALSE);
1326 break;
1328 case eCursor_nesw_resize:
1329 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENESW, FALSE);
1330 break;
1332 case eCursor_nwse_resize:
1333 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENWSE, FALSE);
1334 break;
1336 case eCursor_ns_resize:
1337 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENS, FALSE);
1338 break;
1340 case eCursor_ew_resize:
1341 newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZEWE, FALSE);
1342 break;
1344 case eCursor_none:
1345 newPointer = sPtrArray[IDC_NONE-IDC_BASE];
1346 break;
1348 default:
1349 NS_ERROR("Invalid cursor type");
1350 break;
1353 if (newPointer) {
1354 WinSetPointer(HWND_DESKTOP, newPointer);
1357 return NS_OK;
1360 //-----------------------------------------------------------------------------
1361 // Create a mouse pointer on the fly to support the CSS 'cursor' style.
1362 // This code is based on the Win version by C. Biesinger but has been
1363 // substantially modified to accommodate platform differences and to
1364 // improve efficiency.
1366 NS_IMETHODIMP nsWindow::SetCursor(imgIContainer* aCursor,
1367 PRUint32 aHotspotX, PRUint32 aHotspotY)
1370 // if this is the same image as last time, reuse the saved hptr;
1371 // it will be destroyed when we create a new one or when the
1372 // current window is destroyed
1373 if (mCssCursorImg == aCursor && mCssCursorHPtr) {
1374 WinSetPointer(HWND_DESKTOP, mCssCursorHPtr);
1375 return NS_OK;
1378 nsRefPtr<gfxImageSurface> frame;
1379 aCursor->CopyFrame(imgIContainer::FRAME_CURRENT,
1380 imgIContainer::FLAG_SYNC_DECODE,
1381 getter_AddRefs(frame));
1382 NS_ENSURE_TRUE(frame, NS_ERROR_NOT_AVAILABLE);
1384 // if the image is ridiculously large, exit because
1385 // it will be unrecognizable when shrunk to 32x32
1386 PRInt32 width = frame->Width();
1387 PRInt32 height = frame->Height();
1388 NS_ENSURE_TRUE(width <= 128 && height <= 128, NS_ERROR_FAILURE);
1390 PRUint8* data = frame->Data();
1392 // create the color bitmap
1393 HBITMAP hBmp = CreateBitmapRGB(data, width, height);
1394 NS_ENSURE_TRUE(hBmp, NS_ERROR_FAILURE);
1396 // create a transparency mask from the alpha bytes
1397 HBITMAP hAlpha = CreateTransparencyMask(frame->Format(), data, width, height);
1398 if (!hAlpha) {
1399 GpiDeleteBitmap(hBmp);
1400 return NS_ERROR_FAILURE;
1403 POINTERINFO info = {0};
1404 info.fPointer = TRUE;
1405 info.xHotspot = aHotspotX;
1406 info.yHotspot = height - aHotspotY - 1;
1407 info.hbmPointer = hAlpha;
1408 info.hbmColor = hBmp;
1410 // create the pointer
1411 HPOINTER cursor = WinCreatePointerIndirect(HWND_DESKTOP, &info);
1412 GpiDeleteBitmap(hBmp);
1413 GpiDeleteBitmap(hAlpha);
1414 NS_ENSURE_TRUE(cursor, NS_ERROR_FAILURE);
1416 // use it
1417 WinSetPointer(HWND_DESKTOP, cursor);
1419 // destroy the previous hptr; this has to be done after the
1420 // new pointer is set or else WinDestroyPointer() will fail
1421 if (mCssCursorHPtr) {
1422 WinDestroyPointer(mCssCursorHPtr);
1425 // save the hptr and a reference to the image for next time
1426 mCssCursorHPtr = cursor;
1427 mCssCursorImg = aCursor;
1429 return NS_OK;
1432 //-----------------------------------------------------------------------------
1433 // Render image or modified alpha data as a native bitmap.
1435 // aligned bytes per row, rounded up to next dword bounday
1436 #define ALIGNEDBPR(cx,bits) ( ( ( ((cx)*(bits)) + 31) / 32) * 4)
1438 HBITMAP nsWindow::DataToBitmap(PRUint8* aImageData, PRUint32 aWidth,
1439 PRUint32 aHeight, PRUint32 aDepth)
1441 // get a presentation space for this window
1442 HPS hps = (HPS)GetNativeData(NS_NATIVE_GRAPHIC);
1443 if (!hps) {
1444 return 0;
1447 // a handy structure that does double duty
1448 // as both BITMAPINFOHEADER2 & BITMAPINFO2
1449 struct {
1450 BITMAPINFOHEADER2 head;
1451 RGB2 black;
1452 RGB2 white;
1453 } bi;
1455 memset(&bi, 0, sizeof(bi));
1456 bi.white.bBlue = (BYTE)255;
1457 bi.white.bGreen = (BYTE)255;
1458 bi.white.bRed = (BYTE)255;
1460 // fill in the particulars
1461 bi.head.cbFix = sizeof(bi.head);
1462 bi.head.cx = aWidth;
1463 bi.head.cy = aHeight;
1464 bi.head.cPlanes = 1;
1465 bi.head.cBitCount = aDepth;
1466 bi.head.ulCompression = BCA_UNCOMP;
1467 bi.head.cbImage = ALIGNEDBPR(aWidth, aDepth) * aHeight;
1468 bi.head.cclrUsed = (aDepth == 1 ? 2 : 0);
1470 // create a bitmap from the image data
1471 HBITMAP hBmp = GpiCreateBitmap(hps, &bi.head, CBM_INIT,
1472 reinterpret_cast<BYTE*>(aImageData),
1473 (BITMAPINFO2*)&bi);
1475 // free the hps, then return the bitmap
1476 FreeNativeData((void*)hps, NS_NATIVE_GRAPHIC);
1477 return hBmp;
1480 //-----------------------------------------------------------------------------
1481 // Create an RGB24 bitmap from Cairo image data.
1483 HBITMAP nsWindow::CreateBitmapRGB(PRUint8* aImageData,
1484 PRUint32 aWidth,
1485 PRUint32 aHeight)
1487 // calc width in bytes, rounding up to a dword boundary
1488 const PRUint32 bpr = ALIGNEDBPR(aWidth, 24);
1489 PRUint8* bmp = (PRUint8*)malloc(bpr * aHeight);
1490 if (!bmp) {
1491 return 0;
1494 PRUint32* pSrc = (PRUint32*)aImageData;
1495 for (PRUint32 row = aHeight; row > 0; --row) {
1496 PRUint8* pDst = bmp + bpr * (row - 1);
1498 for (PRUint32 col = aWidth; col > 0; --col) {
1499 // In Cairo a color is encoded as ARGB in a DWORD
1500 // stored in machine endianess.
1501 PRUint32 color = *pSrc++;
1502 *pDst++ = color; // Blue
1503 *pDst++ = color >> 8; // Green
1504 *pDst++ = color >> 16; // Red
1508 // create the bitmap
1509 HBITMAP hAlpha = DataToBitmap(bmp, aWidth, aHeight, 24);
1511 // free the buffer, then return the bitmap
1512 free(bmp);
1513 return hAlpha;
1516 //-----------------------------------------------------------------------------
1517 // Create a monochrome AND/XOR bitmap from 0, 1, or 8-bit alpha data.
1519 HBITMAP nsWindow::CreateTransparencyMask(gfxASurface::gfxImageFormat format,
1520 PRUint8* aImageData,
1521 PRUint32 aWidth,
1522 PRUint32 aHeight)
1524 // calc width in bytes, rounding up to a dword boundary
1525 PRUint32 abpr = ALIGNEDBPR(aWidth, 1);
1526 PRUint32 cbData = abpr * aHeight;
1528 // alloc and clear space to hold both the AND & XOR bitmaps
1529 PRUint8* mono = (PRUint8*)calloc(cbData, 2);
1530 if (!mono) {
1531 return 0;
1534 // Non-alpha formats are already taken care of
1535 // by initializing the XOR and AND masks to zero
1536 if (format == gfxASurface::ImageFormatARGB32) {
1538 // make the AND mask the inverse of the 8-bit alpha data
1539 PRInt32* pSrc = (PRInt32*)aImageData;
1540 for (PRUint32 row = aHeight; row > 0; --row) {
1541 // Point to the right row in the AND mask
1542 PRUint8* pDst = mono + cbData + abpr * (row - 1);
1543 PRUint8 mask = 0x80;
1544 for (PRUint32 col = aWidth; col > 0; --col) {
1545 // Use the sign bit to test for transparency, as the alpha byte
1546 // is highest byte. Positive means, alpha < 128, so consider it
1547 // as transparent and set the AND mask.
1548 if (*pSrc++ >= 0) {
1549 *pDst |= mask;
1552 mask >>= 1;
1553 if (!mask) {
1554 pDst++;
1555 mask = 0x80;
1561 // create the bitmap
1562 HBITMAP hAlpha = DataToBitmap(mono, aWidth, aHeight * 2, 1);
1564 // free the buffer, then return the bitmap
1565 free(mono);
1566 return hAlpha;
1569 //=============================================================================
1570 // Rollup Event Handlers
1571 //=============================================================================
1573 NS_IMETHODIMP nsWindow::CaptureRollupEvents(nsIRollupListener* aListener,
1574 nsIMenuRollup* aMenuRollup,
1575 PRBool aDoCapture,
1576 PRBool aConsumeRollupEvent)
1578 // We haven't bothered carrying a weak reference to gRollupWidget
1579 // because we believe lifespan is properly scoped. The first
1580 // assertion helps assure that remains true.
1581 if (aDoCapture) {
1582 NS_ASSERTION(!gRollupWidget, "rollup widget reassigned before release");
1583 gRollupConsumeRollupEvent = aConsumeRollupEvent;
1584 NS_IF_RELEASE(gRollupWidget);
1585 gRollupListener = aListener;
1586 NS_IF_RELEASE(gMenuRollup);
1587 gMenuRollup = aMenuRollup;
1588 NS_IF_ADDREF(aMenuRollup);
1589 gRollupWidget = this;
1590 NS_ADDREF(this);
1591 } else {
1592 gRollupListener = nsnull;
1593 NS_IF_RELEASE(gMenuRollup);
1594 NS_IF_RELEASE(gRollupWidget);
1597 return NS_OK;
1600 //-----------------------------------------------------------------------------
1602 // static
1603 PRBool nsWindow::EventIsInsideWindow(nsWindow* aWindow)
1605 RECTL rcl;
1606 POINTL ptl;
1608 if (WinQueryMsgPos(0, &ptl)) {
1609 WinMapWindowPoints(HWND_DESKTOP, aWindow->mWnd, &ptl, 1);
1610 WinQueryWindowRect(aWindow->mWnd, &rcl);
1612 // now make sure that it wasn't one of our children
1613 if (ptl.x < rcl.xLeft || ptl.x > rcl.xRight ||
1614 ptl.y > rcl.yTop || ptl.y < rcl.yBottom) {
1615 return PR_FALSE;
1619 return PR_TRUE;
1622 //-----------------------------------------------------------------------------
1623 // Handle events that would cause a popup (combobox, menu, etc) to rollup.
1625 // static
1626 PRBool nsWindow::RollupOnButtonDown(ULONG aMsg)
1628 // Exit if the event is inside the most recent popup.
1629 if (EventIsInsideWindow((nsWindow*)gRollupWidget)) {
1630 return PR_FALSE;
1633 // See if we're dealing with a menu. If so, exit if the
1634 // event was inside a parent of the current submenu.
1635 PRUint32 popupsToRollup = PR_UINT32_MAX;
1637 if (gMenuRollup) {
1638 nsAutoTArray<nsIWidget*, 5> widgetChain;
1639 PRUint32 sameTypeCount = gMenuRollup->GetSubmenuWidgetChain(&widgetChain);
1640 for (PRUint32 i = 0; i < widgetChain.Length(); ++i) {
1641 nsIWidget* widget = widgetChain[i];
1642 if (EventIsInsideWindow((nsWindow*)widget)) {
1643 if (i < sameTypeCount) {
1644 return PR_FALSE;
1646 popupsToRollup = sameTypeCount;
1647 break;
1649 } // for each parent menu widget
1650 } // if rollup listener knows about menus
1652 // We only need to deal with the last rollup for left mouse down events.
1653 gRollupListener->Rollup(popupsToRollup,
1654 aMsg == WM_BUTTON1DOWN ? &mLastRollup : nsnull);
1656 // If true, the buttondown event won't be passed on to the wndproc.
1657 return gRollupConsumeRollupEvent;
1660 //-----------------------------------------------------------------------------
1662 // static
1663 void nsWindow::RollupOnFocusLost(HWND aFocus)
1665 HWND hRollup = ((nsWindow*)gRollupWidget)->mWnd;
1667 // Exit if focus was lost to the most recent popup.
1668 if (hRollup == aFocus) {
1669 return;
1672 // Exit if focus was lost to a parent of the current submenu.
1673 if (gMenuRollup) {
1674 nsAutoTArray<nsIWidget*, 5> widgetChain;
1675 gMenuRollup->GetSubmenuWidgetChain(&widgetChain);
1676 for (PRUint32 i = 0; i < widgetChain.Length(); ++i) {
1677 if (((nsWindow*)widgetChain[i])->mWnd == aFocus) {
1678 return;
1683 // Rollup all popups.
1684 gRollupListener->Rollup(PR_UINT32_MAX, nsnull);
1685 return;
1688 //=============================================================================
1689 // nsWindow's Window Procedure
1690 //=============================================================================
1692 // This is the actual wndproc; it does some preprocessing then passes
1693 // the msgs to the ProcessMessage() method which does most of the work.
1695 MRESULT EXPENTRY fnwpNSWindow(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1697 nsAutoRollup autoRollup;
1699 // If this window doesn't have an object ptr,
1700 // send the msg to the default wndproc.
1701 nsWindow* wnd = nsWindow::GetNSWindowPtr(hwnd);
1702 if (!wnd) {
1703 return WinDefWindowProc(hwnd, msg, mp1, mp2);
1706 // If we're not in the destructor, hold on to the object for the
1707 // life of this method, in case it gets deleted during processing.
1708 // Yes, it's a double hack since someWindow is not really an interface.
1709 nsCOMPtr<nsISupports> kungFuDeathGrip;
1710 if (!wnd->mIsDestroying) {
1711 kungFuDeathGrip = do_QueryInterface((nsBaseWidget*)wnd);
1714 // Pre-process msgs that may cause a rollup.
1715 if (gRollupListener && gRollupWidget) {
1716 switch (msg) {
1717 case WM_BUTTON1DOWN:
1718 case WM_BUTTON2DOWN:
1719 case WM_BUTTON3DOWN:
1720 if (nsWindow::RollupOnButtonDown(msg)) {
1721 return (MRESULT)PR_TRUE;
1723 break;
1725 case WM_SETFOCUS:
1726 if (!mp2) {
1727 nsWindow::RollupOnFocusLost((HWND)mp1);
1729 break;
1733 return wnd->ProcessMessage(msg, mp1, mp2);
1736 //-----------------------------------------------------------------------------
1737 // In effect, nsWindow's real wndproc.
1739 MRESULT nsWindow::ProcessMessage(ULONG msg, MPARAM mp1, MPARAM mp2)
1741 PRBool isDone = PR_FALSE;
1742 MRESULT mresult = 0;
1744 switch (msg) {
1746 // Interpret WM_QUIT as a close request so that
1747 // windows can be closed from the Window List
1748 case WM_CLOSE:
1749 case WM_QUIT: {
1750 mWindowState |= nsWindowState_eClosing;
1751 nsGUIEvent event(PR_TRUE, NS_XUL_CLOSE, this);
1752 InitEvent(event);
1753 DispatchWindowEvent(&event);
1754 // abort window closure
1755 isDone = PR_TRUE;
1756 break;
1759 case WM_DESTROY:
1760 OnDestroy();
1761 isDone = PR_TRUE;
1762 break;
1764 case WM_PAINT:
1765 isDone = OnPaint();
1766 break;
1768 case WM_TRANSLATEACCEL:
1769 isDone = OnTranslateAccelerator((PQMSG)mp1);
1770 break;
1772 case WM_CHAR:
1773 isDone = DispatchKeyEvent(mp1, mp2);
1774 break;
1776 // Mouseclicks: we don't dispatch CLICK events because they just cause
1777 // trouble: gecko seems to expect EITHER buttondown/up OR click events
1778 // and so that's what we give it.
1780 case WM_BUTTON1DOWN:
1781 WinSetCapture(HWND_DESKTOP, mWnd);
1782 DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, mp1, mp2);
1783 // there's no need to clear this on button-up
1784 sLastButton1Down.x = XFROMMP(mp1);
1785 sLastButton1Down.y = YFROMMP(mp1);
1786 break;
1788 case WM_BUTTON1UP:
1789 WinSetCapture(HWND_DESKTOP, 0);
1790 isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, mp1, mp2);
1791 break;
1793 case WM_BUTTON1DBLCLK:
1794 isDone = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, mp1, mp2);
1795 break;
1797 case WM_BUTTON2DOWN:
1798 WinSetCapture(HWND_DESKTOP, mWnd);
1799 isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, mp1, mp2, PR_FALSE,
1800 nsMouseEvent::eRightButton);
1801 break;
1803 case WM_BUTTON2UP:
1804 WinSetCapture(HWND_DESKTOP, 0);
1805 isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, mp1, mp2, PR_FALSE,
1806 nsMouseEvent::eRightButton);
1807 break;
1809 case WM_BUTTON2DBLCLK:
1810 isDone = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, mp1, mp2,
1811 PR_FALSE, nsMouseEvent::eRightButton);
1812 break;
1814 case WM_BUTTON3DOWN:
1815 WinSetCapture(HWND_DESKTOP, mWnd);
1816 isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, mp1, mp2, PR_FALSE,
1817 nsMouseEvent::eMiddleButton);
1818 break;
1820 case WM_BUTTON3UP:
1821 WinSetCapture(HWND_DESKTOP, 0);
1822 isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, mp1, mp2, PR_FALSE,
1823 nsMouseEvent::eMiddleButton);
1824 break;
1826 case WM_BUTTON3DBLCLK:
1827 isDone = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, mp1, mp2, PR_FALSE,
1828 nsMouseEvent::eMiddleButton);
1829 break;
1831 case WM_CONTEXTMENU:
1832 if (SHORT2FROMMP(mp2)) {
1833 HWND hFocus = WinQueryFocus(HWND_DESKTOP);
1834 if (hFocus != mWnd) {
1835 WinSendMsg(hFocus, msg, mp1, mp2);
1836 } else {
1837 isDone = DispatchMouseEvent(NS_CONTEXTMENU, mp1, mp2, PR_TRUE,
1838 nsMouseEvent::eLeftButton);
1840 } else {
1841 isDone = DispatchMouseEvent(NS_CONTEXTMENU, mp1, mp2, PR_FALSE,
1842 nsMouseEvent::eRightButton);
1844 break;
1846 // If MB1 & MB2 are both pressed, perform a copy or paste.
1847 case WM_CHORD:
1848 isDone = OnMouseChord(mp1, mp2);
1849 break;
1851 case WM_MOUSEMOVE: {
1852 static POINTL ptlLastPos = { -1, -1 };
1854 // If mouse has actually moved, remember the new position,
1855 // then dispatch the event.
1856 if (ptlLastPos.x != (SHORT)SHORT1FROMMP(mp1) ||
1857 ptlLastPos.y != (SHORT)SHORT2FROMMP(mp1)) {
1858 ptlLastPos.x = (SHORT)SHORT1FROMMP(mp1);
1859 ptlLastPos.y = (SHORT)SHORT2FROMMP(mp1);
1860 DispatchMouseEvent(NS_MOUSE_MOVE, mp1, mp2);
1863 // don't propagate mouse move or the OS will change the pointer
1864 isDone = PR_TRUE;
1865 break;
1868 case WM_MOUSEENTER:
1869 isDone = DispatchMouseEvent(NS_MOUSE_ENTER, mp1, mp2);
1870 break;
1872 case WM_MOUSELEAVE:
1873 isDone = DispatchMouseEvent(NS_MOUSE_EXIT, mp1, mp2);
1874 break;
1876 case WM_APPCOMMAND: {
1877 PRUint32 appCommand = SHORT2FROMMP(mp2) & 0xfff;
1879 switch (appCommand) {
1880 case APPCOMMAND_BROWSER_BACKWARD:
1881 case APPCOMMAND_BROWSER_FORWARD:
1882 case APPCOMMAND_BROWSER_REFRESH:
1883 case APPCOMMAND_BROWSER_STOP:
1884 DispatchCommandEvent(appCommand);
1885 // tell the driver that we handled the event
1886 mresult = (MRESULT)1;
1887 isDone = PR_TRUE;
1888 break;
1890 break;
1893 case WM_HSCROLL:
1894 case WM_VSCROLL:
1895 isDone = DispatchScrollEvent(msg, mp1, mp2);
1896 break;
1898 // Do not act on WM_ACTIVATE - it is handled by os2FrameWindow.
1899 // case WM_ACTIVATE:
1900 // break;
1902 // This msg is used to activate top-level and plugin widgets
1903 // after PM is done changing the focus. We're only interested
1904 // in windows gaining focus, not in those losing it.
1905 case WM_FOCUSCHANGED:
1906 DEBUGFOCUS(WM_FOCUSCHANGED);
1907 if (SHORT1FROMMP(mp2)) {
1908 ActivateTopLevelWidget();
1909 ActivatePlugin(HWNDFROMMP(mp1));
1911 break;
1913 case WM_WINDOWPOSCHANGED:
1914 isDone = OnReposition((PSWP) mp1);
1915 break;
1917 // all msgs that occur when this window is the target of a drag
1918 case DM_DRAGOVER:
1919 case DM_DRAGLEAVE:
1920 case DM_DROP:
1921 case DM_RENDERCOMPLETE:
1922 case DM_DROPHELP:
1923 OnDragDropMsg(msg, mp1, mp2, mresult);
1924 isDone = PR_TRUE;
1925 break;
1928 // If an event handler signalled that we should consume the event,
1929 // return. Otherwise, pass it on to the default wndproc.
1930 if (!isDone) {
1931 mresult = WinDefWindowProc(mWnd, msg, mp1, mp2);
1934 return mresult;
1937 //=============================================================================
1938 // Window Message Handlers
1939 //=============================================================================
1941 // WM_DESTROY has been called.
1943 void nsWindow::OnDestroy()
1945 mOnDestroyCalled = PR_TRUE;
1947 SetNSWindowPtr(mWnd, 0);
1948 mWnd = 0;
1950 // release references to context, toolkit, appshell, children
1951 nsBaseWidget::OnDestroy();
1953 // dispatching of the event may cause the reference count to drop to 0
1954 // and result in this object being deleted. To avoid that, add a
1955 // reference and then release it after dispatching the event.
1957 // It's important *not* to do this if we're being called from the
1958 // destructor -- this would result in our destructor being called *again*
1959 // from the Release() below. This is very bad...
1960 if (!(nsWindowState_eDoingDelete & mWindowState)) {
1961 AddRef();
1962 nsGUIEvent event(PR_TRUE, NS_DESTROY, this);
1963 InitEvent(event);
1964 DispatchWindowEvent(&event);
1965 Release();
1968 // dead widget
1969 mWindowState |= nsWindowState_eDead;
1970 mWindowState &= ~(nsWindowState_eLive|nsWindowState_ePrecreate|
1971 nsWindowState_eInCreate);
1974 //-----------------------------------------------------------------------------
1976 PRBool nsWindow::OnReposition(PSWP pSwp)
1978 PRBool result = PR_FALSE;
1980 if (pSwp->fl & SWP_MOVE && !(pSwp->fl & SWP_MINIMIZE)) {
1981 HWND hParent = mParent ? mParent->mWnd : WinQueryWindow(mWnd, QW_PARENT);
1983 // need screen coords.
1984 POINTL ptl = { pSwp->x, pSwp->y + pSwp->cy - 1 };
1985 // XXX - this is peculiar...
1986 WinMapWindowPoints(WinQueryWindow(mWnd, QW_PARENT), hParent, &ptl, 1);
1987 PM2NS_PARENT(ptl);
1988 mBounds.x = ptl.x;
1989 mBounds.y = ptl.y;
1990 WinMapWindowPoints(hParent, HWND_DESKTOP, &ptl, 1);
1992 result = DispatchMoveEvent(ptl.x, ptl.y);
1995 if (pSwp->fl & SWP_SIZE && !(pSwp->fl & SWP_MINIMIZE)) {
1996 mBounds.width = pSwp->cx;
1997 mBounds.height = pSwp->cy;
1999 // If the window is supposed to have a thebes surface, resize it.
2000 if (ConfirmThebesSurface()) {
2001 mThebesSurface->Resize(gfxIntSize(mBounds.width, mBounds.height));
2004 result = DispatchResizeEvent(mBounds.width, mBounds.height);
2007 return result;
2010 //-----------------------------------------------------------------------------
2012 PRBool nsWindow::OnPaint()
2014 HPS hPS;
2015 HPS hpsDrag;
2016 HRGN hrgn;
2017 nsEventStatus eventStatus = nsEventStatus_eIgnore;
2019 #ifdef DEBUG_PAINT
2020 HRGN debugPaintFlashRegion = 0;
2021 HPS debugPaintFlashPS = 0;
2023 if (debug_WantPaintFlashing()) {
2024 debugPaintFlashPS = WinGetPS(mWnd);
2025 debugPaintFlashRegion = GpiCreateRegion(debugPaintFlashPS, 0, 0);
2026 WinQueryUpdateRegion(mWnd, debugPaintFlashRegion);
2028 #endif
2030 // Use a dummy do..while(0) loop to facilitate error handling & early-outs.
2031 do {
2033 // Get the current drag status. If we're in a Moz-originated drag,
2034 // it will return a special drag HPS to pass to WinBeginPaint().
2035 // Oherwise, get a cached micro PS.
2036 CheckDragStatus(ACTION_PAINT, &hpsDrag);
2037 hPS = hpsDrag ? hpsDrag : WinGetPS(mWnd);
2039 // If we can't get an HPS, validate the window so we don't
2040 // keep getting the same WM_PAINT msg over & over again.
2041 RECTL rcl = { 0 };
2042 if (!hPS) {
2043 WinQueryWindowRect(mWnd, &rcl);
2044 WinValidateRect(mWnd, &rcl, FALSE);
2045 break;
2048 // Get the update region before WinBeginPaint() resets it.
2049 hrgn = GpiCreateRegion(hPS, 0, 0);
2050 WinQueryUpdateRegion(mWnd, hrgn);
2051 WinBeginPaint(mWnd, hPS, &rcl);
2053 // Exit if the update rect is empty.
2054 if (WinIsRectEmpty(0, &rcl)) {
2055 break;
2058 // Exit if a thebes surface can not/should not be created,
2059 // but first fill the area with the default background color
2060 // to erase any visual artifacts.
2061 if (!ConfirmThebesSurface()) {
2062 WinDrawBorder(hPS, &rcl, 0, 0, 0, 0, DB_INTERIOR | DB_AREAATTRS);
2063 break;
2066 // Even if there is no callback to update the content (unlikely)
2067 // we still want to update the screen with whatever's available.
2068 if (!mEventCallback) {
2069 mThebesSurface->Refresh(&rcl, hPS);
2070 break;
2073 // Create an event & a Thebes context.
2074 nsPaintEvent event(PR_TRUE, NS_PAINT, this);
2075 InitEvent(event);
2076 nsRefPtr<gfxContext> thebesContext = new gfxContext(mThebesSurface);
2078 // Intersect the update region with the paint rectangle to clip areas
2079 // that aren't visible (e.g. offscreen or covered by another window).
2080 HRGN hrgnPaint;
2081 hrgnPaint = GpiCreateRegion(hPS, 1, &rcl);
2082 if (hrgnPaint) {
2083 GpiCombineRegion(hPS, hrgn, hrgn, hrgnPaint, CRGN_AND);
2084 GpiDestroyRegion(hPS, hrgnPaint);
2087 // See how many rects comprise the update region. If there are 8
2088 // or fewer, update them individually. If there are more or the call
2089 // failed, update the bounding rectangle returned by WinBeginPaint().
2090 #define MAX_CLIPRECTS 8
2091 RGNRECT rgnrect = { 1, MAX_CLIPRECTS, 0, RECTDIR_LFRT_TOPBOT };
2092 RECTL arect[MAX_CLIPRECTS];
2093 RECTL* pr = arect;
2095 if (!GpiQueryRegionRects(hPS, hrgn, 0, &rgnrect, 0) ||
2096 rgnrect.crcReturned > MAX_CLIPRECTS) {
2097 rgnrect.crcReturned = 1;
2098 arect[0] = rcl;
2099 } else {
2100 GpiQueryRegionRects(hPS, hrgn, 0, &rgnrect, arect);
2103 // Create clipping regions for the event & the Thebes context.
2104 thebesContext->NewPath();
2105 for (PRUint32 i = 0; i < rgnrect.crcReturned; i++, pr++) {
2106 event.region.Or(event.region,
2107 nsIntRect(pr->xLeft,
2108 mBounds.height - pr->yTop,
2109 pr->xRight - pr->xLeft,
2110 pr->yTop - pr->yBottom));
2112 thebesContext->Rectangle(gfxRect(pr->xLeft,
2113 mBounds.height - pr->yTop,
2114 pr->xRight - pr->xLeft,
2115 pr->yTop - pr->yBottom));
2117 thebesContext->Clip();
2119 #ifdef DEBUG_PAINT
2120 debug_DumpPaintEvent(stdout, this, &event, nsCAutoString("noname"),
2121 (PRInt32)mWnd);
2122 #endif
2124 // Init the Layers manager then dispatch the event.
2125 // If it returns false there's nothing to paint, so exit.
2126 AutoLayerManagerSetup
2127 setupLayerManager(this, thebesContext, BasicLayerManager::BUFFER_NONE);
2128 if (!DispatchWindowEvent(&event, eventStatus)) {
2129 break;
2132 // Paint the surface, then use Refresh() to blit each rect to the screen.
2133 thebesContext->PopGroupToSource();
2134 thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
2135 thebesContext->Paint();
2136 pr = arect;
2137 for (PRUint32 i = 0; i < rgnrect.crcReturned; i++, pr++) {
2138 mThebesSurface->Refresh(pr, hPS);
2141 } while (0);
2143 // Cleanup.
2144 if (hPS) {
2145 WinEndPaint(hPS);
2146 if (hrgn) {
2147 GpiDestroyRegion(hPS, hrgn);
2149 if (!hpsDrag || !ReleaseIfDragHPS(hpsDrag)) {
2150 WinReleasePS(hPS);
2154 #ifdef DEBUG_PAINT
2155 if (debug_WantPaintFlashing()) {
2156 // Only flash paint events which have not ignored the paint message.
2157 // Those that ignore the paint message aren't painting anything so there
2158 // is only the overhead of the dispatching the paint event.
2159 if (eventStatus != nsEventStatus_eIgnore) {
2160 LONG CurMix = GpiQueryMix(debugPaintFlashPS);
2161 GpiSetMix(debugPaintFlashPS, FM_INVERT);
2163 GpiPaintRegion(debugPaintFlashPS, debugPaintFlashRegion);
2164 PR_Sleep(PR_MillisecondsToInterval(30));
2165 GpiPaintRegion(debugPaintFlashPS, debugPaintFlashRegion);
2166 PR_Sleep(PR_MillisecondsToInterval(30));
2168 GpiSetMix(debugPaintFlashPS, CurMix);
2170 GpiDestroyRegion(debugPaintFlashPS, debugPaintFlashRegion);
2171 WinReleasePS(debugPaintFlashPS);
2173 #endif
2175 return PR_TRUE;
2178 //-----------------------------------------------------------------------------
2179 // If MB1 & MB2 are both pressed, perform a copy or paste.
2181 PRBool nsWindow::OnMouseChord(MPARAM mp1, MPARAM mp2)
2183 if (!isKeyDown(VK_BUTTON1) || !isKeyDown(VK_BUTTON2)) {
2184 return PR_FALSE;
2187 // See how far the mouse has moved since MB1-down to determine
2188 // the operation (this really ought to look for selected content).
2189 PRBool isCopy = PR_FALSE;
2190 if (abs(XFROMMP(mp1) - sLastButton1Down.x) >
2191 (WinQuerySysValue(HWND_DESKTOP, SV_CXMOTIONSTART) / 2) ||
2192 abs(YFROMMP(mp1) - sLastButton1Down.y) >
2193 (WinQuerySysValue(HWND_DESKTOP, SV_CYMOTIONSTART) / 2)) {
2194 isCopy = PR_TRUE;
2197 nsKeyEvent event(PR_TRUE, NS_KEY_PRESS, this);
2198 nsIntPoint point(0,0);
2199 InitEvent(event, &point);
2201 event.keyCode = NS_VK_INSERT;
2202 if (isCopy) {
2203 event.isShift = PR_FALSE;
2204 event.isControl = PR_TRUE;
2205 } else {
2206 event.isShift = PR_TRUE;
2207 event.isControl = PR_FALSE;
2209 event.isAlt = PR_FALSE;
2210 event.isMeta = PR_FALSE;
2211 event.eventStructType = NS_KEY_EVENT;
2212 event.charCode = 0;
2214 // OS/2 does not set the Shift, Ctrl, or Alt on keyup
2215 if (SHORT1FROMMP(mp1) & (KC_VIRTUALKEY | KC_KEYUP | KC_LONEKEY)) {
2216 USHORT usVKey = SHORT2FROMMP(mp2);
2217 if (usVKey == VK_SHIFT) {
2218 event.isShift = PR_TRUE;
2220 if (usVKey == VK_CTRL) {
2221 event.isControl = PR_TRUE;
2223 if (usVKey == VK_ALTGRAF || usVKey == VK_ALT) {
2224 event.isAlt = PR_TRUE;
2228 return DispatchWindowEvent(&event);
2231 //=============================================================================
2232 // Drag & Drop - Target methods
2233 //=============================================================================
2235 // nsWindow knows almost nothing about d&d except that it can cause
2236 // video corruption if the screen is updated during a drag. It relies
2237 // on nsIDragSessionOS2 to handle native d&d messages and to return
2238 // the status flags it uses to control screen updates.
2240 // OnDragDropMsg() handles all of the DM_* messages messages nsWindow
2241 // should ever receive. CheckDragStatus() determines if a screen update
2242 // is safe and may return a drag HPS if doing so will avoid corruption.
2243 // As far as its author (R.Walsh) can tell, every use is required.
2245 // For Moz drags, all while-you-drag features should be fully enabled &
2246 // corruption free; for native drags, popups & scrolling are suppressed
2247 // but some niceties, e.g. moving the cursor in text fields, are enabled.
2249 //-----------------------------------------------------------------------------
2251 // This method was designed to be totally ignorant of drag and drop.
2252 // It gives nsIDragSessionOS2 (near) complete control over handling.
2254 PRBool nsWindow::OnDragDropMsg(ULONG msg, MPARAM mp1, MPARAM mp2, MRESULT& mr)
2256 nsresult rv;
2257 PRUint32 eventType = 0;
2258 PRUint32 dragFlags = 0;
2260 mr = 0;
2261 nsCOMPtr<nsIDragService> dragService =
2262 do_GetService("@mozilla.org/widget/dragservice;1", &rv);
2263 if (dragService) {
2264 nsCOMPtr<nsIDragSessionOS2> dragSession(
2265 do_QueryInterface(dragService, &rv));
2266 if (dragSession) {
2268 // handle all possible input without regard to outcome
2269 switch (msg) {
2271 case DM_DRAGOVER:
2272 dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
2273 rv = dragSession->DragOverMsg((PDRAGINFO)mp1, mr, &dragFlags);
2274 eventType = NS_DRAGDROP_OVER;
2275 break;
2277 case DM_DRAGLEAVE:
2278 rv = dragSession->DragLeaveMsg((PDRAGINFO)mp1, &dragFlags);
2279 eventType = NS_DRAGDROP_EXIT;
2280 break;
2282 case DM_DROP:
2283 rv = dragSession->DropMsg((PDRAGINFO)mp1, mWnd, &dragFlags);
2284 eventType = NS_DRAGDROP_DROP;
2285 break;
2287 case DM_DROPHELP:
2288 rv = dragSession->DropHelpMsg((PDRAGINFO)mp1, &dragFlags);
2289 eventType = NS_DRAGDROP_EXIT;
2290 break;
2292 case DM_RENDERCOMPLETE:
2293 rv = dragSession->RenderCompleteMsg((PDRAGTRANSFER)mp1,
2294 SHORT1FROMMP(mp2), &dragFlags);
2295 eventType = NS_DRAGDROP_DROP;
2296 break;
2298 default:
2299 rv = NS_ERROR_FAILURE;
2302 // handle all possible outcomes without regard to their source
2303 if (NS_SUCCEEDED(rv)) {
2304 mDragStatus = sDragStatus = (dragFlags & DND_DragStatus);
2306 if (dragFlags & DND_DispatchEnterEvent) {
2307 DispatchDragDropEvent(NS_DRAGDROP_ENTER);
2309 if (dragFlags & DND_DispatchEvent) {
2310 DispatchDragDropEvent(eventType);
2312 if (dragFlags & DND_GetDragoverResult) {
2313 dragSession->GetDragoverResult(mr);
2315 if (dragFlags & DND_ExitSession) {
2316 dragSession->ExitSession(&dragFlags);
2321 // save final drag status
2322 sDragStatus = mDragStatus = (dragFlags & DND_DragStatus);
2324 return PR_TRUE;
2327 //-----------------------------------------------------------------------------
2328 // CheckDragStatus() concentrates all the hacks needed to avoid video
2329 // corruption during d&d into one place. The caller specifies an action
2330 // that might be a problem; the method tells it whether to proceed and
2331 // provides a Drg HPS if the situation calls for one.
2333 PRBool nsWindow::CheckDragStatus(PRUint32 aAction, HPS* aHps)
2335 PRBool rtn = PR_TRUE;
2336 PRBool getHps = PR_FALSE;
2338 switch (aAction) {
2340 // OnPaint() & Scroll..() - only Moz drags get a Drg hps
2341 case ACTION_PAINT:
2342 case ACTION_SCROLL:
2343 if (sDragStatus & DND_MozDrag) {
2344 getHps = PR_TRUE;
2346 break;
2348 // GetNativeData() - Moz drags + native drags over this nsWindow
2349 case ACTION_DRAW:
2350 if ((sDragStatus & DND_MozDrag) ||
2351 (mDragStatus & DND_NativeDrag)) {
2352 getHps = PR_TRUE;
2354 break;
2356 // Show() - don't show popups during a native dragover
2357 case ACTION_SHOW:
2358 if ((sDragStatus & (DND_NativeDrag | DND_InDrop)) == DND_NativeDrag) {
2359 rtn = PR_FALSE;
2361 break;
2363 // InitEvent() - use PtrPos while in drag, MsgPos otherwise
2364 case ACTION_PTRPOS:
2365 if (!sDragStatus) {
2366 rtn = PR_FALSE;
2368 break;
2370 default:
2371 rtn = PR_FALSE;
2374 // If the caller wants an HPS, and the current drag status
2375 // calls for one, *and* a drag hps hasn't already been requested
2376 // for this window, get the hps; otherwise, return zero;
2377 // (if we provide a 2nd hps for a window, the cursor in text
2378 // fields won't be erased when it's moved to another position)
2379 if (aHps) {
2380 if (getHps && !mDragHps) {
2381 mDragHps = DrgGetPS(mWnd);
2382 *aHps = mDragHps;
2383 } else {
2384 *aHps = 0;
2388 return rtn;
2391 //-----------------------------------------------------------------------------
2392 // If there's an outstanding drag hps & it matches the one passed in,
2393 // release it.
2395 PRBool nsWindow::ReleaseIfDragHPS(HPS aHps)
2397 if (mDragHps && aHps == mDragHps) {
2398 DrgReleasePS(mDragHps);
2399 mDragHps = 0;
2400 return PR_TRUE;
2403 return PR_FALSE;
2406 //=============================================================================
2407 // Keyboard Handlers
2408 //=============================================================================
2410 // Figure out which keyboard LEDs are on.
2412 NS_IMETHODIMP nsWindow::GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState)
2414 PRUint32 vkey;
2416 NS_ENSURE_ARG_POINTER(aLEDState);
2418 switch (aKeyCode) {
2419 case NS_VK_CAPS_LOCK:
2420 vkey = VK_CAPSLOCK;
2421 break;
2422 case NS_VK_NUM_LOCK:
2423 vkey = VK_NUMLOCK;
2424 break;
2425 case NS_VK_SCROLL_LOCK:
2426 vkey = VK_SCRLLOCK;
2427 break;
2428 default:
2429 *aLEDState = PR_FALSE;
2430 return NS_OK;
2433 *aLEDState = (WinGetKeyState(HWND_DESKTOP, vkey) & 1) != 0;
2434 return NS_OK;
2437 //-----------------------------------------------------------------------------
2438 // Prevent PM from translating some keys & key-combos into accelerators.
2440 PRBool nsWindow::OnTranslateAccelerator(PQMSG pQmsg)
2442 if (pQmsg->msg != WM_CHAR) {
2443 return PR_FALSE;
2446 LONG mp1 = (LONG)pQmsg->mp1;
2447 LONG mp2 = (LONG)pQmsg->mp2;
2448 LONG sca = SHORT1FROMMP(mp1) & (KC_SHIFT | KC_CTRL | KC_ALT);
2450 if (SHORT1FROMMP(mp1) & KC_VIRTUALKEY) {
2452 // standalone F1 & F10
2453 if (SHORT2FROMMP(mp2) == VK_F1 || SHORT2FROMMP(mp2) == VK_F10) {
2454 return (!sca ? PR_TRUE : PR_FALSE);
2457 // Shift+Enter
2458 if (SHORT2FROMMP(mp2) == VK_ENTER) {
2459 return (sca == KC_SHIFT ? PR_TRUE : PR_FALSE);
2462 // Alt+Enter
2463 if (SHORT2FROMMP(mp2) == VK_NEWLINE) {
2464 return (sca == KC_ALT ? PR_TRUE : PR_FALSE);
2467 // standalone Alt & AltGraf
2468 if ((SHORT2FROMMP(mp2) == VK_ALT || SHORT2FROMMP(mp2) == VK_ALTGRAF) &&
2469 (SHORT1FROMMP(mp1) & (KC_KEYUP | KC_LONEKEY))
2470 == (KC_KEYUP | KC_LONEKEY)) {
2471 return PR_TRUE;
2475 return PR_FALSE;
2478 //-----------------------------------------------------------------------------
2479 // Key handler. Specs for the various text messages are really confused;
2480 // see other platforms for best results of how things are supposed to work.
2482 // Perhaps more importantly, the main man listening to these events
2483 // (besides random bits of javascript) is ender -- see
2484 // mozilla/editor/base/nsEditorEventListeners.cpp.
2486 PRBool nsWindow::DispatchKeyEvent(MPARAM mp1, MPARAM mp2)
2488 nsKeyEvent pressEvent(PR_TRUE, 0, nsnull);
2489 USHORT fsFlags = SHORT1FROMMP(mp1);
2490 USHORT usVKey = SHORT2FROMMP(mp2);
2491 USHORT usChar = SHORT1FROMMP(mp2);
2492 UCHAR uchScan = CHAR4FROMMP(mp1);
2494 // It appears we're not supposed to transmit shift,
2495 // control, & alt events to gecko.
2496 if (fsFlags & KC_VIRTUALKEY && !(fsFlags & KC_KEYUP) &&
2497 (usVKey == VK_SHIFT || usVKey == VK_CTRL || usVKey == VK_ALTGRAF)) {
2498 return PR_FALSE;
2501 // Workaround bug where using Alt+Esc let an Alt key creep through
2502 // Only handle alt by itself if the LONEKEY bit is set
2503 if ((fsFlags & KC_VIRTUALKEY) && (usVKey == VK_ALT) && !usChar &&
2504 (!(fsFlags & KC_LONEKEY)) && (fsFlags & KC_KEYUP)) {
2505 return PR_FALSE;
2508 // Now check if it's a dead-key
2509 if (fsFlags & KC_DEADKEY) {
2510 return PR_TRUE;
2513 // Now dispatch a keyup/keydown event. This one is *not* meant to
2514 // have the unicode charcode in.
2515 nsIntPoint point(0,0);
2516 nsKeyEvent event(PR_TRUE, (fsFlags & KC_KEYUP) ? NS_KEY_UP : NS_KEY_DOWN,
2517 this);
2518 InitEvent(event, &point);
2519 event.keyCode = WMChar2KeyCode(mp1, mp2);
2520 event.isShift = (fsFlags & KC_SHIFT) ? PR_TRUE : PR_FALSE;
2521 event.isControl = (fsFlags & KC_CTRL) ? PR_TRUE : PR_FALSE;
2522 event.isAlt = (fsFlags & KC_ALT) ? PR_TRUE : PR_FALSE;
2523 event.isMeta = PR_FALSE;
2524 event.charCode = 0;
2526 // Check for a scroll mouse event vs. a keyboard event. The way we know
2527 // this is that the repeat count is 0 and the key is not physically down.
2528 // Unfortunately, there is an exception here - if alt or ctrl are held
2529 // down, repeat count is set so we have to add special checks for them.
2530 if (((event.keyCode == NS_VK_UP) || (event.keyCode == NS_VK_DOWN)) &&
2531 !(fsFlags & KC_KEYUP) &&
2532 (!CHAR3FROMMP(mp1) || fsFlags & KC_CTRL || fsFlags & KC_ALT)) {
2533 if (!(WinGetPhysKeyState(HWND_DESKTOP, uchScan) & 0x8000)) {
2534 MPARAM mp2;
2535 if (event.keyCode == NS_VK_UP) {
2536 mp2 = MPFROM2SHORT(0, SB_LINEUP);
2537 } else {
2538 mp2 = MPFROM2SHORT(0, SB_LINEDOWN);
2540 WinSendMsg(mWnd, WM_VSCROLL, 0, mp2);
2541 return FALSE;
2545 pressEvent = event;
2546 PRBool rc = DispatchWindowEvent(&event);
2548 // Break off now if this was a key-up.
2549 if (fsFlags & KC_KEYUP) {
2550 return rc;
2553 // Break off if we've got an "invalid composition" -- that is,
2554 // the user typed a deadkey last time, but has now typed something
2555 // that doesn't make sense in that context.
2556 if (fsFlags & KC_INVALIDCOMP) {
2557 // actually, not sure whether we're supposed to abort the keypress
2558 // or process it as though the dead key has been pressed.
2559 return rc;
2562 // Now we need to dispatch a keypress event which has the unicode char.
2563 // If keydown default was prevented, do same for keypress
2564 pressEvent.message = NS_KEY_PRESS;
2565 if (rc) {
2566 pressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
2569 if (usChar) {
2570 USHORT inbuf[2];
2571 inbuf[0] = usChar;
2572 inbuf[1] = '\0';
2574 nsAutoChar16Buffer outbuf;
2575 PRInt32 bufLength;
2576 MultiByteToWideChar(0, (const char*)inbuf, 2, outbuf, bufLength);
2578 pressEvent.charCode = outbuf[0];
2580 if (pressEvent.isControl && !(fsFlags & (KC_VIRTUALKEY | KC_DEADKEY))) {
2581 if (!pressEvent.isShift && (pressEvent.charCode >= 'A' && pressEvent.charCode <= 'Z')) {
2582 pressEvent.charCode = tolower(pressEvent.charCode);
2584 if (pressEvent.isShift && (pressEvent.charCode >= 'a' && pressEvent.charCode <= 'z')) {
2585 pressEvent.charCode = toupper(pressEvent.charCode);
2587 pressEvent.keyCode = 0;
2588 } else if (!pressEvent.isControl && !pressEvent.isAlt && pressEvent.charCode != 0) {
2589 if (!(fsFlags & KC_VIRTUALKEY) || // not virtual key
2590 ((fsFlags & KC_CHAR) && !pressEvent.keyCode)) {
2591 pressEvent.keyCode = 0;
2592 } else if (usVKey == VK_SPACE) {
2593 // space key, do nothing here
2594 } else if ((fsFlags & KC_VIRTUALKEY) &&
2595 isNumPadScanCode(uchScan) && pressEvent.keyCode != 0 && isNumlockOn) {
2596 // this is NumLock+Numpad (no Alt), handle this like a normal number
2597 pressEvent.keyCode = 0;
2598 } else { // Real virtual key
2599 pressEvent.charCode = 0;
2602 rc = DispatchWindowEvent(&pressEvent);
2605 return rc;
2608 //-----------------------------------------------------------------------------
2609 // Helper function to translate from a WM_CHAR to an NS_VK_ constant.
2611 static
2612 PRUint32 WMChar2KeyCode(MPARAM mp1, MPARAM mp2)
2614 PRUint32 rc = SHORT1FROMMP(mp2); // character code
2615 PRUint32 rcmask = rc & 0x00FF; // masked character code for key up events
2616 USHORT sc = CHAR4FROMMP(mp1); // scan code
2617 USHORT flags = SHORT1FROMMP(mp1); // flag word
2619 // First check for characters.
2620 // This is complicated by keystrokes such as Ctrl+K not having the KC_CHAR
2621 // bit set, but thankfully they do have the character actually there.
2623 // Assume that `if not vkey or deadkey or valid number then char'
2624 if (!(flags & (KC_VIRTUALKEY | KC_DEADKEY)) ||
2625 (rcmask >= '0' && rcmask <= '9' && // handle keys on Numpad, too,
2626 (isNumPadScanCode(sc) ? isNumlockOn : 1))) { // if NumLock is on
2627 if (flags & KC_KEYUP) { // On OS/2 the scancode is in the upper byte of
2628 // usChar when KC_KEYUP is set so mask it off
2629 rc = rcmask;
2630 } else { // not KC_KEYUP
2631 if (!(flags & KC_CHAR)) {
2632 if ((flags & KC_ALT) || (flags & KC_CTRL)) {
2633 rc = rcmask;
2634 } else {
2635 rc = 0;
2640 if (rc < 0xFF) {
2641 if (rc >= 'a' && rc <= 'z') { // The DOM_VK are for upper case only so
2642 // if rc is lower case upper case it.
2643 rc = rc - 'a' + NS_VK_A;
2644 } else if (rc >= 'A' && rc <= 'Z') { // Upper case
2645 rc = rc - 'A' + NS_VK_A;
2646 } else if (rc >= '0' && rc <= '9') {
2647 // Number keys, including Numpad if NumLock is not set
2648 rc = rc - '0' + NS_VK_0;
2649 } else {
2650 // For some characters, map the scan code to the NS_VK value
2651 // This only happens in the char case NOT the VK case!
2652 switch (sc) {
2653 case 0x02: rc = NS_VK_1; break;
2654 case 0x03: rc = NS_VK_2; break;
2655 case 0x04: rc = NS_VK_3; break;
2656 case 0x05: rc = NS_VK_4; break;
2657 case 0x06: rc = NS_VK_5; break;
2658 case 0x07: rc = NS_VK_6; break;
2659 case 0x08: rc = NS_VK_7; break;
2660 case 0x09: rc = NS_VK_8; break;
2661 case 0x0A: rc = NS_VK_9; break;
2662 case 0x0B: rc = NS_VK_0; break;
2663 case 0x0D: rc = NS_VK_EQUALS; break;
2664 case 0x1A: rc = NS_VK_OPEN_BRACKET; break;
2665 case 0x1B: rc = NS_VK_CLOSE_BRACKET; break;
2666 case 0x27: rc = NS_VK_SEMICOLON; break;
2667 case 0x28: rc = NS_VK_QUOTE; break;
2668 case 0x29: rc = NS_VK_BACK_QUOTE; break;
2669 case 0x2B: rc = NS_VK_BACK_SLASH; break;
2670 case 0x33: rc = NS_VK_COMMA; break;
2671 case 0x34: rc = NS_VK_PERIOD; break;
2672 case 0x35: rc = NS_VK_SLASH; break;
2673 case 0x37: rc = NS_VK_MULTIPLY; break;
2674 case 0x4A: rc = NS_VK_SUBTRACT; break;
2675 case 0x4C: rc = NS_VK_CLEAR; break; // numeric case is handled above
2676 case 0x4E: rc = NS_VK_ADD; break;
2677 case 0x5C: rc = NS_VK_DIVIDE; break;
2678 default: break;
2679 } // switch
2680 } // else
2681 } // if (rc < 0xFF)
2682 } else if (flags & KC_VIRTUALKEY) {
2683 USHORT vk = SHORT2FROMMP(mp2);
2684 if (flags & KC_KEYUP) { // On OS/2 there are extraneous bits in the upper byte of
2685 // usChar when KC_KEYUP is set so mask them off
2686 rc = rcmask;
2688 if (isNumPadScanCode(sc) &&
2689 (((flags & KC_ALT) && (sc != PMSCAN_PADPERIOD)) ||
2690 ((flags & (KC_CHAR | KC_SHIFT)) == KC_CHAR) ||
2691 ((flags & KC_KEYUP) && rc != 0))) {
2692 CHAR numpadMap[] = {NS_VK_NUMPAD7, NS_VK_NUMPAD8, NS_VK_NUMPAD9, 0,
2693 NS_VK_NUMPAD4, NS_VK_NUMPAD5, NS_VK_NUMPAD6, 0,
2694 NS_VK_NUMPAD1, NS_VK_NUMPAD2, NS_VK_NUMPAD3,
2695 NS_VK_NUMPAD0, NS_VK_DECIMAL};
2696 // If this is the Numpad must not return VK for ALT+Numpad or ALT+NumLock+Numpad
2697 // NumLock+Numpad is OK
2698 if (numpadMap[sc - PMSCAN_PAD7] != 0) { // not plus or minus on Numpad
2699 if (flags & KC_ALT) { // do not react on Alt plus ASCII-code sequences
2700 rc = 0;
2701 } else {
2702 rc = numpadMap[sc - PMSCAN_PAD7];
2704 } else { // plus or minus of Numpad
2705 rc = 0; // No virtual key for Alt+Numpad or NumLock+Numpad
2707 } else if (!(flags & KC_CHAR) || isNumPadScanCode(sc) ||
2708 (vk == VK_BACKSPACE) || (vk == VK_TAB) || (vk == VK_BACKTAB) ||
2709 (vk == VK_ENTER) || (vk == VK_NEWLINE) || (vk == VK_SPACE)) {
2710 if (vk >= VK_F1 && vk <= VK_F24) {
2711 rc = NS_VK_F1 + (vk - VK_F1);
2713 else switch (vk) {
2714 case VK_NUMLOCK: rc = NS_VK_NUM_LOCK; break;
2715 case VK_SCRLLOCK: rc = NS_VK_SCROLL_LOCK; break;
2716 case VK_ESC: rc = NS_VK_ESCAPE; break; // NS_VK_CANCEL
2717 case VK_BACKSPACE: rc = NS_VK_BACK; break;
2718 case VK_TAB: rc = NS_VK_TAB; break;
2719 case VK_BACKTAB: rc = NS_VK_TAB; break; // layout tests for isShift
2720 case VK_CLEAR: rc = NS_VK_CLEAR; break;
2721 case VK_NEWLINE: rc = NS_VK_RETURN; break;
2722 case VK_ENTER: rc = NS_VK_RETURN; break;
2723 case VK_SHIFT: rc = NS_VK_SHIFT; break;
2724 case VK_CTRL: rc = NS_VK_CONTROL; break;
2725 case VK_ALT: rc = NS_VK_ALT; break;
2726 case VK_PAUSE: rc = NS_VK_PAUSE; break;
2727 case VK_CAPSLOCK: rc = NS_VK_CAPS_LOCK; break;
2728 case VK_SPACE: rc = NS_VK_SPACE; break;
2729 case VK_PAGEUP: rc = NS_VK_PAGE_UP; break;
2730 case VK_PAGEDOWN: rc = NS_VK_PAGE_DOWN; break;
2731 case VK_END: rc = NS_VK_END; break;
2732 case VK_HOME: rc = NS_VK_HOME; break;
2733 case VK_LEFT: rc = NS_VK_LEFT; break;
2734 case VK_UP: rc = NS_VK_UP; break;
2735 case VK_RIGHT: rc = NS_VK_RIGHT; break;
2736 case VK_DOWN: rc = NS_VK_DOWN; break;
2737 case VK_PRINTSCRN: rc = NS_VK_PRINTSCREEN; break;
2738 case VK_INSERT: rc = NS_VK_INSERT; break;
2739 case VK_DELETE: rc = NS_VK_DELETE; break;
2740 } // switch
2742 } // KC_VIRTUALKEY
2744 return rc;
2747 //=============================================================================
2748 // Event Dispatch
2749 //=============================================================================
2751 // Initialize an event to dispatch.
2753 void nsWindow::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint)
2755 // if no point was supplied, calculate it
2756 if (!aPoint) {
2757 // for most events, get the message position; for drag events,
2758 // msg position may be incorrect, so get the current position instead
2759 POINTL ptl;
2760 if (CheckDragStatus(ACTION_PTRPOS, 0)) {
2761 WinQueryPointerPos(HWND_DESKTOP, &ptl);
2762 } else {
2763 WinQueryMsgPos(0, &ptl);
2766 WinMapWindowPoints(HWND_DESKTOP, mWnd, &ptl, 1);
2767 PM2NS(ptl);
2768 event.refPoint.x = ptl.x;
2769 event.refPoint.y = ptl.y;
2770 } else {
2771 // use the point override if provided
2772 event.refPoint.x = aPoint->x;
2773 event.refPoint.y = aPoint->y;
2776 event.time = WinQueryMsgTime(0);
2777 return;
2780 //-----------------------------------------------------------------------------
2781 // Invoke the Event Listener object's callback.
2783 NS_IMETHODIMP nsWindow::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
2785 aStatus = nsEventStatus_eIgnore;
2787 if (!mEventCallback) {
2788 return NS_OK;
2791 // if state is eInCreate, only send out NS_CREATE
2792 // if state is eDoingDelete, don't send out anything
2793 if ((mWindowState & nsWindowState_eLive) ||
2794 (mWindowState == nsWindowState_eInCreate && event->message == NS_CREATE)) {
2795 aStatus = (*mEventCallback)(event);
2797 return NS_OK;
2800 //-----------------------------------------------------------------------------
2802 NS_IMETHODIMP nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
2804 NS_PRECONDITION(aNewParent, "");
2805 return NS_ERROR_NOT_IMPLEMENTED;
2808 //-----------------------------------------------------------------------------
2810 PRBool nsWindow::DispatchWindowEvent(nsGUIEvent* event)
2812 nsEventStatus status;
2813 DispatchEvent(event, status);
2814 return (status == nsEventStatus_eConsumeNoDefault);
2817 PRBool nsWindow::DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus) {
2818 DispatchEvent(event, aStatus);
2819 return (aStatus == nsEventStatus_eConsumeNoDefault);
2822 //-----------------------------------------------------------------------------
2824 PRBool nsWindow::DispatchCommandEvent(PRUint32 aEventCommand)
2826 nsCOMPtr<nsIAtom> command;
2828 switch (aEventCommand) {
2829 case APPCOMMAND_BROWSER_BACKWARD:
2830 command = nsWidgetAtoms::Back;
2831 break;
2832 case APPCOMMAND_BROWSER_FORWARD:
2833 command = nsWidgetAtoms::Forward;
2834 break;
2835 case APPCOMMAND_BROWSER_REFRESH:
2836 command = nsWidgetAtoms::Reload;
2837 break;
2838 case APPCOMMAND_BROWSER_STOP:
2839 command = nsWidgetAtoms::Stop;
2840 break;
2841 default:
2842 return PR_FALSE;
2845 nsCommandEvent event(PR_TRUE, nsWidgetAtoms::onAppCommand, command, this);
2846 InitEvent(event);
2847 return DispatchWindowEvent(&event);
2850 //-----------------------------------------------------------------------------
2852 PRBool nsWindow::DispatchDragDropEvent(PRUint32 aMsg)
2854 nsDragEvent event(PR_TRUE, aMsg, this);
2855 InitEvent(event);
2857 event.isShift = isKeyDown(VK_SHIFT);
2858 event.isControl = isKeyDown(VK_CTRL);
2859 event.isAlt = isKeyDown(VK_ALT) || isKeyDown(VK_ALTGRAF);
2860 event.isMeta = PR_FALSE;
2862 return DispatchWindowEvent(&event);
2865 //-----------------------------------------------------------------------------
2867 PRBool nsWindow::DispatchMoveEvent(PRInt32 aX, PRInt32 aY)
2869 // Params here are in XP-space for the desktop
2870 nsGUIEvent event(PR_TRUE, NS_MOVE, this);
2871 nsIntPoint point(aX, aY);
2872 InitEvent(event, &point);
2873 return DispatchWindowEvent(&event);
2876 //-----------------------------------------------------------------------------
2878 PRBool nsWindow::DispatchResizeEvent(PRInt32 aX, PRInt32 aY)
2880 nsSizeEvent event(PR_TRUE, NS_SIZE, this);
2881 nsIntRect rect(0, 0, aX, aY);
2883 InitEvent(event);
2884 event.windowSize = &rect; // this is the *client* rectangle
2885 event.mWinWidth = mBounds.width;
2886 event.mWinHeight = mBounds.height;
2888 return DispatchWindowEvent(&event);
2891 //-----------------------------------------------------------------------------
2892 // Deal with all sorts of mouse events.
2894 PRBool nsWindow::DispatchMouseEvent(PRUint32 aEventType, MPARAM mp1, MPARAM mp2,
2895 PRBool aIsContextMenuKey, PRInt16 aButton)
2897 NS_ENSURE_TRUE(aEventType, PR_FALSE);
2899 nsMouseEvent event(PR_TRUE, aEventType, this, nsMouseEvent::eReal,
2900 aIsContextMenuKey
2901 ? nsMouseEvent::eContextMenuKey
2902 : nsMouseEvent::eNormal);
2903 event.button = aButton;
2905 if (aEventType == NS_MOUSE_ENTER || aEventType == NS_MOUSE_EXIT) {
2906 // Ignore enter/leave msgs forwarded from the frame to FID_CLIENT
2907 // because we're only interested msgs involving the content area.
2908 if (HWNDFROMMP(mp1) != mWnd) {
2909 return FALSE;
2912 // If the mouse has exited the content area and entered either an
2913 // unrelated window or what Windows would call the nonclient area
2914 // (i.e. frame, titlebar, etc.), mark this as a toplevel exit.
2915 // Note: exits to and from menus will also be marked toplevel.
2916 if (aEventType == NS_MOUSE_EXIT) {
2917 HWND hTop = 0;
2918 HWND hCur = mWnd;
2919 HWND hDesk = WinQueryDesktopWindow(0, 0);
2920 while (hCur && hCur != hDesk) {
2921 hTop = hCur;
2922 hCur = WinQueryWindow(hCur, QW_PARENT);
2925 // event.exit was init'ed to eChild, so we don't need an 'else'
2926 hTop = WinWindowFromID(hTop, FID_CLIENT);
2927 if (!hTop || !WinIsChild(HWNDFROMMP(mp2), hTop)) {
2928 event.exit = nsMouseEvent::eTopLevel;
2932 InitEvent(event, nsnull);
2933 event.isShift = isKeyDown(VK_SHIFT);
2934 event.isControl = isKeyDown(VK_CTRL);
2935 event.isAlt = isKeyDown(VK_ALT) || isKeyDown(VK_ALTGRAF);
2936 } else {
2937 POINTL ptl;
2938 if (aEventType == NS_CONTEXTMENU && aIsContextMenuKey) {
2939 WinQueryPointerPos(HWND_DESKTOP, &ptl);
2940 WinMapWindowPoints(HWND_DESKTOP, mWnd, &ptl, 1);
2941 } else {
2942 ptl.x = (SHORT)SHORT1FROMMP(mp1);
2943 ptl.y = (SHORT)SHORT2FROMMP(mp1);
2945 PM2NS(ptl);
2946 nsIntPoint pt(ptl.x, ptl.y);
2947 InitEvent(event, &pt);
2949 USHORT usFlags = SHORT2FROMMP(mp2);
2950 event.isShift = (usFlags & KC_SHIFT) ? PR_TRUE : PR_FALSE;
2951 event.isControl = (usFlags & KC_CTRL) ? PR_TRUE : PR_FALSE;
2952 event.isAlt = (usFlags & KC_ALT) ? PR_TRUE : PR_FALSE;
2954 event.isMeta = PR_FALSE;
2956 // Dblclicks are used to set the click count, then changed to mousedowns
2957 if (aEventType == NS_MOUSE_DOUBLECLICK &&
2958 (aButton == nsMouseEvent::eLeftButton ||
2959 aButton == nsMouseEvent::eRightButton)) {
2960 event.message = NS_MOUSE_BUTTON_DOWN;
2961 event.button = (aButton == nsMouseEvent::eLeftButton) ?
2962 nsMouseEvent::eLeftButton : nsMouseEvent::eRightButton;
2963 event.clickCount = 2;
2964 } else {
2965 event.clickCount = 1;
2968 NPEvent pluginEvent;
2969 switch (aEventType) {
2971 case NS_MOUSE_BUTTON_DOWN:
2972 switch (aButton) {
2973 case nsMouseEvent::eLeftButton:
2974 pluginEvent.event = WM_BUTTON1DOWN;
2975 break;
2976 case nsMouseEvent::eMiddleButton:
2977 pluginEvent.event = WM_BUTTON3DOWN;
2978 break;
2979 case nsMouseEvent::eRightButton:
2980 pluginEvent.event = WM_BUTTON2DOWN;
2981 break;
2982 default:
2983 break;
2985 break;
2987 case NS_MOUSE_BUTTON_UP:
2988 switch (aButton) {
2989 case nsMouseEvent::eLeftButton:
2990 pluginEvent.event = WM_BUTTON1UP;
2991 break;
2992 case nsMouseEvent::eMiddleButton:
2993 pluginEvent.event = WM_BUTTON3UP;
2994 break;
2995 case nsMouseEvent::eRightButton:
2996 pluginEvent.event = WM_BUTTON2UP;
2997 break;
2998 default:
2999 break;
3001 break;
3003 case NS_MOUSE_DOUBLECLICK:
3004 switch (aButton) {
3005 case nsMouseEvent::eLeftButton:
3006 pluginEvent.event = WM_BUTTON1DBLCLK;
3007 break;
3008 case nsMouseEvent::eMiddleButton:
3009 pluginEvent.event = WM_BUTTON3DBLCLK;
3010 break;
3011 case nsMouseEvent::eRightButton:
3012 pluginEvent.event = WM_BUTTON2DBLCLK;
3013 break;
3014 default:
3015 break;
3017 break;
3019 case NS_MOUSE_MOVE:
3020 pluginEvent.event = WM_MOUSEMOVE;
3021 break;
3024 pluginEvent.wParam = 0;
3025 pluginEvent.lParam = MAKELONG(event.refPoint.x, event.refPoint.y);
3027 event.pluginEvent = (void*)&pluginEvent;
3029 return DispatchWindowEvent(&event);
3032 //-----------------------------------------------------------------------------
3033 // Signal plugin & top-level window activation.
3035 PRBool nsWindow::DispatchActivationEvent(PRUint32 aEventType)
3037 nsGUIEvent event(PR_TRUE, aEventType, this);
3039 // These events should go to their base widget location,
3040 // not current mouse position.
3041 nsIntPoint point(0, 0);
3042 InitEvent(event, &point);
3044 NPEvent pluginEvent;
3045 switch (aEventType) {
3046 case NS_ACTIVATE:
3047 pluginEvent.event = WM_SETFOCUS;
3048 break;
3049 case NS_DEACTIVATE:
3050 pluginEvent.event = WM_FOCUSCHANGED;
3051 break;
3052 case NS_PLUGIN_ACTIVATE:
3053 pluginEvent.event = WM_FOCUSCHANGED;
3054 break;
3056 event.pluginEvent = (void*)&pluginEvent;
3058 return DispatchWindowEvent(&event);
3061 //-----------------------------------------------------------------------------
3063 PRBool nsWindow::DispatchScrollEvent(ULONG msg, MPARAM mp1, MPARAM mp2)
3065 nsMouseScrollEvent scrollEvent(PR_TRUE, NS_MOUSE_SCROLL, this);
3066 InitEvent(scrollEvent);
3068 scrollEvent.isShift = isKeyDown(VK_SHIFT);
3069 scrollEvent.isControl = isKeyDown(VK_CTRL);
3070 scrollEvent.isAlt = isKeyDown(VK_ALT) || isKeyDown(VK_ALTGRAF);
3071 scrollEvent.isMeta = PR_FALSE;
3072 scrollEvent.scrollFlags = (msg == WM_HSCROLL) ?
3073 nsMouseScrollEvent::kIsHorizontal :
3074 nsMouseScrollEvent::kIsVertical;
3076 // The SB_* constants for analogous vertical & horizontal ops have the
3077 // the same values, so only use the verticals to avoid compiler errors.
3078 switch (SHORT2FROMMP(mp2)) {
3079 case SB_LINEUP:
3080 // SB_LINELEFT:
3081 scrollEvent.delta = -1;
3082 break;
3084 case SB_LINEDOWN:
3085 // SB_LINERIGHT:
3086 scrollEvent.delta = 1;
3087 break;
3089 case SB_PAGEUP:
3090 // SB_PAGELEFT:
3091 scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage;
3092 scrollEvent.delta = -1;
3093 break;
3095 case SB_PAGEDOWN:
3096 // SB_PAGERIGHT:
3097 scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage;
3098 scrollEvent.delta = 1;
3099 break;
3101 default:
3102 scrollEvent.delta = 0;
3103 break;
3105 DispatchWindowEvent(&scrollEvent);
3107 return PR_FALSE;
3110 //=============================================================================