/* vim: set sw=2 sts=2 et cin: */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Mozilla OS/2 libraries.
 *
 * The Initial Developer of the Original Code is
 * John Fairhurst, <john_fairhurst@iname.com>.
 * Portions created by the Initial Developer are Copyright (C) 1999
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Rich Walsh <dragtext@e-vertise.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK *****
 *
 * 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 nsIRollupListener*  gRollupListener;
extern nsIWidget*          gRollupWidget;
extern PRBool              gRollupConsumeRollupEvent;
extern PRUint32            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     = PR_FALSE;
  mNeedActivation   = PR_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;
  PRUint32 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(PR_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.
  PRInt32 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;
}

//-----------------------------------------------------------------------------

PRUint32 os2FrameWindow::GetFCFlags(nsWindowType aWindowType,
                                    nsBorderStyle aBorderStyle)
{
  PRUint32 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(PRBool aState)
{
  PRUint32 ulFlags;
  if (!aState) {
    ulFlags = SWP_HIDE | SWP_DEACTIVATE;
  } else {
    ulFlags = SWP_SHOW | SWP_ACTIVATE;

    PRUint32 ulStyle = WinQueryWindowULong(mFrameWnd, QWL_STYLE);
    PRInt32 sizeMode;
    mOwner->GetSizeMode(&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(PRBool 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(PRInt32 aX, PRInt32 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(PRInt32 aWidth, PRInt32 aHeight,
                                PRBool 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(PRInt32 aX, PRInt32 aY,
                                PRInt32 aWidth, PRInt32 aHeight,
                                PRBool 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) {
    PRInt32 sizeMode;
    mOwner->GetSizeMode(&sizeMode);
    if (sizeMode != nsSizeMode_Minimized) {
      mNeedActivation = PR_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(PRInt32 aMode)
{
  PRInt32 previousMode;
  mOwner->GetSizeMode(&previousMode);

  // 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(PRBool 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 = PR_TRUE;
  } else {
    hParent = mFrameWnd;
    mChromeHidden = PR_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 (PRUint32 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;
  PRInt32 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<nsILocalFile> iconFile;
  mOwner->ResolveIconName(aIconSpec, NS_LITERAL_STRING(".ico"),
                          getter_AddRefs(iconFile));

  // if the file was found, try to use it
  if (iconFile) {
    nsCAutoString 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(PRBool aAllowSlop,
                                      PRInt32* aX, PRInt32* aY)
{
  // do we have enough info to do anything
  PRBool doConstrain = PR_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<nsIScreenManager> screenmgr =
    do_GetService("@mozilla.org/gfx/screenmanager;1");
  if (screenmgr) {
    nsCOMPtr<nsIScreen> screen;
    PRInt32 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 = PR_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
  if (gRollupListener && gRollupWidget) {
    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*)gRollupWidget)) {
        gRollupListener->Rollup(PR_UINT32_MAX, nsnull);
      }
    }
  }

  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;
  PRBool  isDone = PR_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 = PR_TRUE;
      }

      if (pSwp->fl & (SWP_MAXIMIZE | SWP_MINIMIZE | SWP_RESTORE)) {
        nsSizeModeEvent event(PR_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 = PR_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 = PR_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 = PR_TRUE;
      } else {
        mNeedActivation = PR_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;
}

//=============================================================================

