/* vim: set sw=2 sts=2 et cin: */ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This Original Code has been modified by IBM Corporation. * Modifications made by IBM are * Copyright (c) International Business Machines Corporation, 2000 * */ //============================================================================= #include "nsWindow.h" #include "os2FrameWindow.h" #include "nsIRollupListener.h" #include "nsIScreenManager.h" #include "nsOS2Uni.h" //----------------------------------------------------------------------------- // External Variables (in nsWindow.cpp) extern uint32_t gOS2Flags; #ifdef DEBUG_FOCUS extern int currentWindowIdentifier; #endif //----------------------------------------------------------------------------- // Debug #ifdef DEBUG_FOCUS #define DEBUGFOCUS(what) fprintf(stderr, "[%8x] %8lx (%02d) "#what"\n", \ (int)this, mFrameWnd, mWindowIdentifier) #else #define DEBUGFOCUS(what) #endif //============================================================================= // os2FrameWindow Setup //============================================================================= os2FrameWindow::os2FrameWindow(nsWindow* aOwner) { mOwner = aOwner; mFrameWnd = 0; mTitleBar = 0; mSysMenu = 0; mMinMax = 0; mSavedStyle = 0; mFrameIcon = 0; mChromeHidden = false; mNeedActivation = false; mPrevFrameProc = 0; mFrameBounds = nsIntRect(0, 0, 0, 0); } //----------------------------------------------------------------------------- os2FrameWindow::~os2FrameWindow() { if (mFrameIcon) { WinFreeFileIcon(mFrameIcon); mFrameIcon = 0; } } //----------------------------------------------------------------------------- // Create frame & client windows for an os2FrameWindow object; return the // handle of the client window. This is the only method that manipulates // the client window - all other operations on it are handled by nsWindow. HWND os2FrameWindow::CreateFrameWindow(nsWindow* aParent, HWND aParentWnd, const nsIntRect& aRect, nsWindowType aWindowType, nsBorderStyle aBorderStyle) { // Create a frame window with a MozillaWindowClass window as its client. HWND hClient; uint32_t fcfFlags = GetFCFlags(aWindowType, aBorderStyle); mFrameWnd = WinCreateStdWindow(HWND_DESKTOP, 0, (ULONG*)&fcfFlags, kWindowClassName, "Title", WS_CLIPCHILDREN, NULLHANDLE, 0, &hClient); if (!mFrameWnd) { return 0; } // Hide from the Window List until shown. SetWindowListVisibility(false); // This prevents a modal dialog from being covered by its disabled parent. if (aParentWnd != HWND_DESKTOP) { WinSetOwner(mFrameWnd, aParentWnd); } // Get the frame control HWNDs for use by fullscreen mode. mTitleBar = WinWindowFromID(mFrameWnd, FID_TITLEBAR); mSysMenu = WinWindowFromID(mFrameWnd, FID_SYSMENU); mMinMax = WinWindowFromID(mFrameWnd, FID_MINMAX); // Calc the size of a frame needed to contain a client area of the // specified width & height. Without this, eWindowType_dialog windows // will be truncated (toplevel windows will still display correctly). RECTL rcl = {0, 0, aRect.width, aRect.height}; WinCalcFrameRect(mFrameWnd, &rcl, FALSE); mFrameBounds = nsIntRect(aRect.x, aRect.y, rcl.xRight-rcl.xLeft, rcl.yTop-rcl.yBottom); // Move & resize the frame. int32_t pmY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - mFrameBounds.y - mFrameBounds.height; WinSetWindowPos(mFrameWnd, 0, mFrameBounds.x, pmY, mFrameBounds.width, mFrameBounds.height, SWP_SIZE | SWP_MOVE); // Store the client's bounds. For windows with resizable borders, // the width includes the width of the frame controls (minmax, etc.). SWP swp; WinQueryWindowPos(hClient, &swp); mOwner->SetBounds(nsIntRect(swp.x, mFrameBounds.height - swp.y - swp.cy, swp.cx, swp.cy)); // Subclass the frame. mPrevFrameProc = WinSubclassWindow(mFrameWnd, fnwpFrame); WinSetWindowPtr(mFrameWnd, QWL_USER, this); DEBUGFOCUS(Create os2FrameWindow); return hClient; } //----------------------------------------------------------------------------- uint32_t os2FrameWindow::GetFCFlags(nsWindowType aWindowType, nsBorderStyle aBorderStyle) { uint32_t style = FCF_TITLEBAR | FCF_SYSMENU | FCF_TASKLIST | FCF_CLOSEBUTTON | FCF_NOBYTEALIGN | FCF_AUTOICON; if (aWindowType == eWindowType_dialog) { if (aBorderStyle == eBorderStyle_default) { style |= FCF_DLGBORDER; } else { style |= FCF_SIZEBORDER | FCF_MINMAX; } } else { style |= FCF_SIZEBORDER | FCF_MINMAX; } if (gOS2Flags & kIsDBCS) { style |= FCF_DBE_APPSTAT; } if (aWindowType == eWindowType_invisible) { style &= ~FCF_TASKLIST; } if (aBorderStyle != eBorderStyle_default && aBorderStyle != eBorderStyle_all) { if (aBorderStyle == eBorderStyle_none || !(aBorderStyle & eBorderStyle_resizeh)) { style &= ~FCF_SIZEBORDER; style |= FCF_DLGBORDER; } if (aBorderStyle == eBorderStyle_none || !(aBorderStyle & eBorderStyle_border)) { style &= ~(FCF_DLGBORDER | FCF_SIZEBORDER); } if (aBorderStyle == eBorderStyle_none || !(aBorderStyle & eBorderStyle_title)) { style &= ~(FCF_TITLEBAR | FCF_TASKLIST); } if (aBorderStyle == eBorderStyle_none || !(aBorderStyle & eBorderStyle_close)) { style &= ~FCF_CLOSEBUTTON; } if (aBorderStyle == eBorderStyle_none || !(aBorderStyle & (eBorderStyle_menu | eBorderStyle_close))) { style &= ~FCF_SYSMENU; } // Looks like getting rid of the system menu also does away // with the close box. So, we only get rid of the system menu // if you want neither it nor the close box. if (aBorderStyle == eBorderStyle_none || !(aBorderStyle & eBorderStyle_minimize)) { style &= ~FCF_MINBUTTON; } if (aBorderStyle == eBorderStyle_none || !(aBorderStyle & eBorderStyle_maximize)) { style &= ~FCF_MAXBUTTON; } } return style; } //============================================================================= // Window Operations //============================================================================= // For frame windows, 'Show' is equivalent to 'Show & Activate'. nsresult os2FrameWindow::Show(bool aState) { uint32_t ulFlags; if (!aState) { ulFlags = SWP_HIDE | SWP_DEACTIVATE; } else { ulFlags = SWP_SHOW | SWP_ACTIVATE; uint32_t ulStyle = WinQueryWindowULong(mFrameWnd, QWL_STYLE); int32_t sizeMode = mOwner->SizeMode(); if (!(ulStyle & WS_VISIBLE)) { if (sizeMode == nsSizeMode_Maximized) { ulFlags |= SWP_MAXIMIZE; } else if (sizeMode == nsSizeMode_Minimized) { ulFlags |= SWP_MINIMIZE; } else { ulFlags |= SWP_RESTORE; } } else if (ulStyle & WS_MINIMIZED) { if (sizeMode == nsSizeMode_Maximized) { ulFlags |= SWP_MAXIMIZE; } else { ulFlags |= SWP_RESTORE; } } } WinSetWindowPos(mFrameWnd, 0, 0, 0, 0, 0, ulFlags); SetWindowListVisibility(aState); return NS_OK; } //----------------------------------------------------------------------------- void os2FrameWindow::SetWindowListVisibility(bool aState) { HSWITCH hswitch = WinQuerySwitchHandle(mFrameWnd, 0); if (hswitch) { SWCNTRL swctl; WinQuerySwitchEntry(hswitch, &swctl); swctl.uchVisibility = aState ? SWL_VISIBLE : SWL_INVISIBLE; swctl.fbJump = aState ? SWL_JUMPABLE : SWL_NOTJUMPABLE; WinChangeSwitchEntry(hswitch, &swctl); } } //============================================================================= // Window Positioning //============================================================================= nsresult os2FrameWindow::GetBounds(nsIntRect& aRect) { aRect = mFrameBounds; return NS_OK; } //----------------------------------------------------------------------------- nsresult os2FrameWindow::Move(int32_t aX, int32_t aY) { aY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - mFrameBounds.height - aY; WinSetWindowPos(mFrameWnd, 0, aX, aY, 0, 0, SWP_MOVE); return NS_OK; } //----------------------------------------------------------------------------- nsresult os2FrameWindow::Resize(int32_t aWidth, int32_t aHeight, bool aRepaint) { // When resizing, the coordinates of the window's bottom-left corner have to // be adjusted to ensure the position of the top-left corner doesn't change. Resize(mFrameBounds.x, mFrameBounds.y, aWidth, aHeight, aRepaint); return NS_OK; } //----------------------------------------------------------------------------- nsresult os2FrameWindow::Resize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, bool aRepaint) { aY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - aY - aHeight; WinSetWindowPos(mFrameWnd, 0, aX, aY, aWidth, aHeight, SWP_MOVE | SWP_SIZE); return NS_OK; } //============================================================================= // Top-level (frame window) Operations //============================================================================= // When WM_ACTIVATE is received with the "gaining activation" flag set, // the frame's wndproc sets mNeedActivation. Later, when an nsWindow // gets a WM_FOCUSCHANGED msg with the "gaining focus" flag set, it // invokes this method on os2FrameWindow to fire an NS_ACTIVATE event. void os2FrameWindow::ActivateTopLevelWidget() { // Don't fire event if we're minimized or else the window will // be restored as soon as the user clicks on it. When the user // explicitly restores it, SetSizeMode() will call this method. if (mNeedActivation) { if (mOwner->SizeMode() != nsSizeMode_Minimized) { mNeedActivation = false; DEBUGFOCUS(NS_ACTIVATE); mOwner->DispatchActivationEvent(NS_ACTIVATE); } } return; } //----------------------------------------------------------------------------- // Maximize, minimize or restore the window. When the frame has its // controls, this method is usually advisory because min/max/restore has // already occurred. It only performs these actions when the frame is in // fullscreen mode or saved window positions are being restored at startup. nsresult os2FrameWindow::SetSizeMode(int32_t aMode) { int32_t previousMode = mOwner->SizeMode(); // save the new state nsresult rv = mOwner->nsBaseWidget::SetSizeMode(aMode); if (!NS_SUCCEEDED(rv)) { return rv; } // Minimized windows would get restored involuntarily if we fired an // NS_ACTIVATE when the user clicks on them. Instead, we defer the // activation event until the window has explicitly been restored. if (previousMode == nsSizeMode_Minimized && previousMode != aMode) { DEBUGFOCUS(deferred NS_ACTIVATE); ActivateTopLevelWidget(); } ULONG ulStyle = WinQueryWindowULong(mFrameWnd, QWL_STYLE); switch (aMode) { case nsSizeMode_Normal: if (ulStyle & (WS_MAXIMIZED | WS_MINIMIZED)) { WinSetWindowPos(mFrameWnd, 0, 0, 0, 0, 0, SWP_RESTORE); } break; case nsSizeMode_Minimized: if (!(ulStyle & WS_MINIMIZED)) { WinSetWindowPos(mFrameWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_MINIMIZE | SWP_ZORDER | SWP_DEACTIVATE); } break; case nsSizeMode_Maximized: // Don't permit the window to be maximized when in // fullscreen mode because it won't be restored correctly. if (!(ulStyle & WS_MAXIMIZED) && !mChromeHidden) { WinSetWindowPos(mFrameWnd, HWND_TOP, 0, 0, 0, 0, SWP_MAXIMIZE | SWP_ZORDER); } break; // 'nsSizeMode_Fullscreen' is defined but isn't used (as of v1.9.3) case nsSizeMode_Fullscreen: default: break; } return NS_OK; } //----------------------------------------------------------------------------- // Hide or show the frame & its controls (titlebar, minmax, etc.). nsresult os2FrameWindow::HideWindowChrome(bool aShouldHide) { // Putting a maximized window into fullscreen mode causes multiple // problems if it's later minimized & then restored. To avoid them, // restore maximized windows before putting them in fullscreen mode. if (WinQueryWindowULong(mFrameWnd, QWL_STYLE) & WS_MAXIMIZED) { WinSetWindowPos(mFrameWnd, 0, 0, 0, 0, 0, SWP_RESTORE | SWP_NOREDRAW); } HWND hParent; if (aShouldHide) { hParent = HWND_OBJECT; mChromeHidden = true; } else { hParent = mFrameWnd; mChromeHidden = false; } // Hide or show the frame controls. WinSetParent(mTitleBar, hParent, FALSE); WinSetParent(mSysMenu, hParent, FALSE); WinSetParent(mMinMax, hParent, FALSE); // Modify the frame style, then advise it of the changes. if (aShouldHide) { mSavedStyle = WinQueryWindowULong(mFrameWnd, QWL_STYLE); WinSetWindowULong(mFrameWnd, QWL_STYLE, mSavedStyle & ~FS_SIZEBORDER); WinSendMsg(mFrameWnd, WM_UPDATEFRAME, 0, 0); } else { WinSetWindowULong(mFrameWnd, QWL_STYLE, mSavedStyle); WinSendMsg(mFrameWnd, WM_UPDATEFRAME, MPFROMLONG(FCF_TITLEBAR | FCF_SYSMENU | FCF_MINMAX), 0); } return NS_OK; } //----------------------------------------------------------------------------- // On OS/2, if you pass a titlebar > 512 characters, it doesn't display at all. // We are going to limit our titlebars to 256 just to be on the safe side. #define MAX_TITLEBAR_LENGTH 256 nsresult os2FrameWindow::SetTitle(const nsAString& aTitle) { PRUnichar* uchtemp = ToNewUnicode(aTitle); for (uint32_t i = 0; i < aTitle.Length(); i++) { switch (uchtemp[i]) { case 0x2018: case 0x2019: uchtemp[i] = 0x0027; break; case 0x201C: case 0x201D: uchtemp[i] = 0x0022; break; case 0x2014: uchtemp[i] = 0x002D; break; } } nsAutoCharBuffer title; int32_t titleLength; WideCharToMultiByte(0, uchtemp, aTitle.Length(), title, titleLength); if (titleLength > MAX_TITLEBAR_LENGTH) { title[MAX_TITLEBAR_LENGTH] = '\0'; } WinSetWindowText(mFrameWnd, title.Elements()); // If the chrome is hidden, set the text of the titlebar directly if (mChromeHidden) { WinSetWindowText(mTitleBar, title.Elements()); } nsMemory::Free(uchtemp); return NS_OK; } //----------------------------------------------------------------------------- // This implementation guarantees that sysmenus & minimized windows // will always have some icon other than the sysmenu default. nsresult os2FrameWindow::SetIcon(const nsAString& aIconSpec) { static HPOINTER hDefaultIcon = 0; HPOINTER hWorkingIcon = 0; // Assume the given string is a local identifier for an icon file. nsCOMPtr iconFile; mOwner->ResolveIconName(aIconSpec, NS_LITERAL_STRING(".ico"), getter_AddRefs(iconFile)); // if the file was found, try to use it if (iconFile) { nsAutoCString path; iconFile->GetNativePath(path); if (mFrameIcon) { WinFreeFileIcon(mFrameIcon); } mFrameIcon = WinLoadFileIcon(path.get(), FALSE); hWorkingIcon = mFrameIcon; } // if that doesn't work, use the app's icon (let's hope it can be // loaded because nobody should have to look at SPTR_APPICON) if (!hWorkingIcon) { if (!hDefaultIcon) { hDefaultIcon = WinLoadPointer(HWND_DESKTOP, 0, 1); if (!hDefaultIcon) { hDefaultIcon = WinQuerySysPointer(HWND_DESKTOP, SPTR_APPICON, FALSE); } } hWorkingIcon = hDefaultIcon; } WinSendMsg(mFrameWnd, WM_SETICON, (MPARAM)hWorkingIcon, (MPARAM)0); return NS_OK; } //----------------------------------------------------------------------------- // Constrain a potential move to fit onscreen. nsresult os2FrameWindow::ConstrainPosition(bool aAllowSlop, int32_t* aX, int32_t* aY) { // do we have enough info to do anything bool doConstrain = false; // get our playing field. use the current screen, or failing // that for any reason, use device caps for the default screen. RECTL screenRect; nsCOMPtr screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); if (screenmgr) { nsCOMPtr screen; int32_t left, top, width, height; // zero size rects confuse the screen manager width = mFrameBounds.width > 0 ? mFrameBounds.width : 1; height = mFrameBounds.height > 0 ? mFrameBounds.height : 1; screenmgr->ScreenForRect(*aX, *aY, width, height, getter_AddRefs(screen)); if (screen) { screen->GetAvailRect(&left, &top, &width, &height); screenRect.xLeft = left; screenRect.xRight = left+width; screenRect.yTop = top; screenRect.yBottom = top+height; doConstrain = true; } } #define kWindowPositionSlop 100 if (doConstrain) { if (aAllowSlop) { if (*aX < screenRect.xLeft - mFrameBounds.width + kWindowPositionSlop) { *aX = screenRect.xLeft - mFrameBounds.width + kWindowPositionSlop; } else if (*aX >= screenRect.xRight - kWindowPositionSlop) { *aX = screenRect.xRight - kWindowPositionSlop; } if (*aY < screenRect.yTop) { *aY = screenRect.yTop; } else if (*aY >= screenRect.yBottom - kWindowPositionSlop) { *aY = screenRect.yBottom - kWindowPositionSlop; } } else { if (*aX < screenRect.xLeft) { *aX = screenRect.xLeft; } else if (*aX >= screenRect.xRight - mFrameBounds.width) { *aX = screenRect.xRight - mFrameBounds.width; } if (*aY < screenRect.yTop) { *aY = screenRect.yTop; } else if (*aY >= screenRect.yBottom - mFrameBounds.height) { *aY = screenRect.yBottom - mFrameBounds.height; } } } return NS_OK; } //============================================================================= // os2FrameWindow's Window Procedure //============================================================================= // Subclass for frame window MRESULT EXPENTRY fnwpFrame(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { // check to see if we have a rollup listener registered nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); if (rollupListener) { nsCOMPtr rollupWidget = rollupListener->GetRollupWidget(); if (msg == WM_TRACKFRAME || msg == WM_MINMAXFRAME || msg == WM_BUTTON1DOWN || msg == WM_BUTTON2DOWN || msg == WM_BUTTON3DOWN) { // Rollup if the event is outside the popup if (!nsWindow::EventIsInsideWindow((nsWindow*)rollupWidget)) { rollupListener->Rollup(UINT32_MAX); } } } os2FrameWindow* pFrame = (os2FrameWindow*)WinQueryWindowPtr(hwnd, QWL_USER); return pFrame->ProcessFrameMessage(msg, mp1, mp2); } //----------------------------------------------------------------------------- // Process messages from the frame MRESULT os2FrameWindow::ProcessFrameMessage(ULONG msg, MPARAM mp1, MPARAM mp2) { MRESULT mresult = 0; bool isDone = false; switch (msg) { case WM_WINDOWPOSCHANGED: { PSWP pSwp = (PSWP)mp1; // Don't save the new position or size of a minimized // window, or else it won't be restored correctly. if (pSwp->fl & SWP_MOVE && !(pSwp->fl & SWP_MINIMIZE)) { POINTL ptl = { pSwp->x, pSwp->y + pSwp->cy }; ptl.y = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - ptl.y; mFrameBounds.x = ptl.x; mFrameBounds.y = ptl.y; mOwner->DispatchMoveEvent(ptl.x, ptl.y); } // Save the frame's bounds, then call the default wndproc // so the client can handle its WM_WINDOWPOSCHANGED msg now. if (pSwp->fl & SWP_SIZE && !(pSwp->fl & SWP_MINIMIZE)) { mFrameBounds.width = pSwp->cx; mFrameBounds.height = pSwp->cy; mresult = (*mPrevFrameProc)(mFrameWnd, msg, mp1, mp2); isDone = true; } if (pSwp->fl & (SWP_MAXIMIZE | SWP_MINIMIZE | SWP_RESTORE)) { nsSizeModeEvent event(true, NS_SIZEMODE, mOwner); if (pSwp->fl & SWP_MAXIMIZE) { event.mSizeMode = nsSizeMode_Maximized; } else if (pSwp->fl & SWP_MINIMIZE) { event.mSizeMode = nsSizeMode_Minimized; } else { event.mSizeMode = nsSizeMode_Normal; } mOwner->InitEvent(event); mOwner->DispatchWindowEvent(&event); } break; } // A frame window in kiosk/fullscreen mode must have its frame // controls reattached before it's minimized & detached after it's // restored. If this doesn't happen at the correct times, clicking // on the icon won't restore it, the sysmenu will have the wrong // items, and/or the minmax button will have the wrong buttons. case WM_ADJUSTWINDOWPOS: if (mChromeHidden && ((PSWP)mp1)->fl & SWP_MINIMIZE) { WinSetParent(mTitleBar, mFrameWnd, TRUE); WinSetParent(mSysMenu, mFrameWnd, TRUE); WinSetParent(mMinMax, mFrameWnd, TRUE); } break; case WM_ADJUSTFRAMEPOS: if (mChromeHidden && ((PSWP)mp1)->fl & SWP_RESTORE) { WinSetParent(mTitleBar, HWND_OBJECT, TRUE); WinSetParent(mSysMenu, HWND_OBJECT, TRUE); WinSetParent(mMinMax, HWND_OBJECT, TRUE); } break; case WM_DESTROY: DEBUGFOCUS(frame WM_DESTROY); WinSubclassWindow(mFrameWnd, mPrevFrameProc); WinSetWindowPtr(mFrameWnd, QWL_USER, 0); break; case WM_INITMENU: // If we are in fullscreen/kiosk mode, disable maximize menu item. if (mChromeHidden && SHORT1FROMMP(mp1) == SC_SYSMENU && WinQueryWindowULong(mFrameWnd, QWL_STYLE) & WS_MINIMIZED) { MENUITEM menuitem; WinSendMsg(WinWindowFromID(mFrameWnd, FID_SYSMENU), MM_QUERYITEM, MPFROM2SHORT(SC_SYSMENU, FALSE), MPARAM(&menuitem)); mresult = (*mPrevFrameProc)(mFrameWnd, msg, mp1, mp2); WinEnableMenuItem(menuitem.hwndSubMenu, SC_MAXIMIZE, FALSE); isDone = true; } break; case WM_SYSCOMMAND: // If we are in fullscreen/kiosk mode, don't honor maximize requests. if (mChromeHidden && SHORT1FROMMP(mp1) == SC_MAXIMIZE && WinQueryWindowULong(mFrameWnd, QWL_STYLE) & WS_MINIMIZED) { isDone = true; } break; // When the frame is activated, set a flag to be acted on after // PM has finished changing focus. When deactivated, dispatch // the event immediately because it doesn't affect the focus. case WM_ACTIVATE: DEBUGFOCUS(WM_ACTIVATE); if (mp1) { mNeedActivation = true; } else { mNeedActivation = false; DEBUGFOCUS(NS_DEACTIVATE); mOwner->DispatchActivationEvent(NS_DEACTIVATE); // Prevent the frame from automatically focusing any window // when it's reactivated. Let moz set the focus to avoid // having non-widget children of plugins focused in error. WinSetWindowULong(mFrameWnd, QWL_HWNDFOCUSSAVE, 0); } break; } if (!isDone) { mresult = (*mPrevFrameProc)(mFrameWnd, msg, mp1, mp2); } return mresult; } //=============================================================================