2001-09-28 20:14:13 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
2004-04-18 14:30:37 +00:00
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
1999-07-18 06:23:45 +00:00
|
|
|
*
|
2004-04-18 14:30:37 +00:00
|
|
|
* 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/
|
1999-07-18 06:23:45 +00:00
|
|
|
*
|
2001-09-28 20:14:13 +00:00
|
|
|
* 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.
|
1999-07-18 06:23:45 +00:00
|
|
|
*
|
1999-11-06 03:40:37 +00:00
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
2004-04-18 14:30:37 +00:00
|
|
|
* The Initial Developer of the Original Code is
|
2001-09-28 20:14:13 +00:00
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
1999-11-06 03:40:37 +00:00
|
|
|
*
|
2001-09-28 20:14:13 +00:00
|
|
|
* Contributor(s):
|
2004-04-18 14:30:37 +00:00
|
|
|
* Original Author: David W. Hyatt (hyatt@netscape.com)
|
2000-03-23 21:10:09 +00:00
|
|
|
* Mike Pinkerton (pinkerton@netscape.com)
|
2000-03-01 03:12:51 +00:00
|
|
|
* Dean Tessman <dean_tessman@hotmail.com>
|
2001-06-04 23:03:24 +00:00
|
|
|
* Ben Goodger <ben@netscape.com>
|
2001-09-28 20:14:13 +00:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
2004-04-18 14:30:37 +00:00
|
|
|
* either of 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"),
|
2001-09-28 20:14:13 +00:00
|
|
|
* 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
|
2004-04-18 14:30:37 +00:00
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
2001-09-28 20:14:13 +00:00
|
|
|
* 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
|
2004-04-18 14:30:37 +00:00
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
2001-09-28 20:14:13 +00:00
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
1999-07-18 06:23:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
#include "nsMenuPopupFrame.h"
|
1999-07-20 07:28:39 +00:00
|
|
|
#include "nsXULAtoms.h"
|
1999-07-20 09:50:48 +00:00
|
|
|
#include "nsHTMLAtoms.h"
|
1999-07-18 06:23:45 +00:00
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "prtypes.h"
|
|
|
|
#include "nsIAtom.h"
|
|
|
|
#include "nsIPresContext.h"
|
2003-02-22 00:32:13 +00:00
|
|
|
#include "nsStyleContext.h"
|
1999-07-18 06:23:45 +00:00
|
|
|
#include "nsCSSRendering.h"
|
|
|
|
#include "nsINameSpaceManager.h"
|
1999-07-20 08:37:03 +00:00
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsWidgetsCID.h"
|
1999-07-22 09:01:55 +00:00
|
|
|
#include "nsMenuFrame.h"
|
1999-09-21 01:03:00 +00:00
|
|
|
#include "nsIPopupSetFrame.h"
|
2000-09-01 01:54:35 +00:00
|
|
|
#include "nsIDOMWindowInternal.h"
|
1999-11-23 03:02:01 +00:00
|
|
|
#include "nsIDOMScreen.h"
|
|
|
|
#include "nsIScriptGlobalObject.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDeviceContext.h"
|
|
|
|
#include "nsRect.h"
|
2000-02-08 09:30:15 +00:00
|
|
|
#include "nsIDOMXULDocument.h"
|
2000-02-13 08:33:39 +00:00
|
|
|
#include "nsILookAndFeel.h"
|
|
|
|
#include "nsIComponentManager.h"
|
2000-05-15 04:12:31 +00:00
|
|
|
#include "nsBoxLayoutState.h"
|
2000-11-13 21:25:07 +00:00
|
|
|
#include "nsIScrollableView.h"
|
2001-07-16 02:40:48 +00:00
|
|
|
#include "nsGUIEvent.h"
|
2001-08-18 01:04:47 +00:00
|
|
|
#include "nsIRootBox.h"
|
2002-08-01 23:12:38 +00:00
|
|
|
#include "nsIDocShellTreeItem.h"
|
2002-09-07 05:38:16 +00:00
|
|
|
#include "nsITimerInternal.h"
|
2003-07-14 07:37:39 +00:00
|
|
|
#include "nsReadableUtils.h"
|
2003-03-05 01:43:25 +00:00
|
|
|
#include "nsUnicharUtils.h"
|
2004-01-21 23:05:10 +00:00
|
|
|
#include "nsCSSFrameConstructor.h"
|
2002-02-15 03:53:26 +00:00
|
|
|
#ifdef XP_WIN
|
|
|
|
#include "nsISound.h"
|
|
|
|
#endif
|
1999-07-20 08:37:03 +00:00
|
|
|
|
|
|
|
const PRInt32 kMaxZ = 0x7fffffff; //XXX: Shouldn't there be a define somewhere for MaxInt for PRInt32
|
1999-07-18 06:23:45 +00:00
|
|
|
|
|
|
|
|
2001-08-18 01:04:47 +00:00
|
|
|
static nsIPopupSetFrame*
|
|
|
|
GetPopupSetFrame(nsIPresContext* aPresContext)
|
|
|
|
{
|
|
|
|
nsIFrame* rootFrame;
|
2003-12-21 05:36:36 +00:00
|
|
|
aPresContext->PresShell()->GetRootFrame(&rootFrame);
|
2001-08-18 01:04:47 +00:00
|
|
|
if (!rootFrame)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
if (rootFrame)
|
2004-01-09 14:20:53 +00:00
|
|
|
rootFrame = rootFrame->GetFirstChild(nsnull);
|
2001-08-18 01:04:47 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIRootBox> rootBox(do_QueryInterface(rootFrame));
|
|
|
|
if (!rootBox)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsIFrame* popupSetFrame;
|
|
|
|
rootBox->GetPopupSetFrame(&popupSetFrame);
|
|
|
|
if (!popupSetFrame)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPopupSetFrame> popupSet(do_QueryInterface(popupSetFrame));
|
|
|
|
return popupSet;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-07-18 06:23:45 +00:00
|
|
|
// NS_NewMenuPopupFrame
|
|
|
|
//
|
|
|
|
// Wrapper for creating a new menu popup container
|
|
|
|
//
|
|
|
|
nsresult
|
1999-12-04 23:49:50 +00:00
|
|
|
NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
1999-07-18 06:23:45 +00:00
|
|
|
{
|
|
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
|
|
if (nsnull == aNewFrame) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
2000-03-02 03:01:30 +00:00
|
|
|
nsMenuPopupFrame* it = new (aPresShell) nsMenuPopupFrame (aPresShell);
|
1999-07-18 06:23:45 +00:00
|
|
|
if ( !it )
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
*aNewFrame = it;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-07-21 07:42:16 +00:00
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
|
|
nsMenuPopupFrame::AddRef(void)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
|
|
nsMenuPopupFrame::Release(void)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-12-22 01:57:29 +00:00
|
|
|
//
|
|
|
|
// QueryInterface
|
|
|
|
//
|
1999-12-21 19:28:15 +00:00
|
|
|
NS_INTERFACE_MAP_BEGIN(nsMenuPopupFrame)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIMenuParent)
|
2000-02-13 08:33:39 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
2000-01-26 22:35:53 +00:00
|
|
|
NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
|
|
|
|
|
1999-07-18 06:23:45 +00:00
|
|
|
|
|
|
|
//
|
2000-03-01 03:12:51 +00:00
|
|
|
// nsMenuPopupFrame ctor
|
1999-07-18 06:23:45 +00:00
|
|
|
//
|
2000-03-23 21:10:09 +00:00
|
|
|
nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell)
|
|
|
|
:nsBoxFrame(aShell), mCurrentMenu(nsnull), mTimerMenu(nsnull), mCloseTimer(nsnull),
|
2001-09-10 07:34:54 +00:00
|
|
|
mMenuCanOverlapOSBar(PR_FALSE), mShouldAutoPosition(PR_TRUE), mShouldRollup(PR_TRUE)
|
1999-07-18 06:23:45 +00:00
|
|
|
{
|
2000-03-01 03:12:51 +00:00
|
|
|
SetIsContextMenu(PR_FALSE); // we're not a context menu by default
|
2001-03-12 04:19:10 +00:00
|
|
|
// Don't allow container frames to automatically position
|
|
|
|
// the popup because they will put it in the wrong position.
|
2003-08-04 12:39:51 +00:00
|
|
|
RemoveStateBits(NS_FRAME_SYNC_FRAME_AND_VIEW);
|
2000-03-01 03:12:51 +00:00
|
|
|
} // ctor
|
1999-07-18 06:23:45 +00:00
|
|
|
|
|
|
|
|
1999-07-20 07:28:39 +00:00
|
|
|
NS_IMETHODIMP
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMenuPopupFrame::Init(nsIPresContext* aPresContext,
|
1999-07-20 07:28:39 +00:00
|
|
|
nsIContent* aContent,
|
|
|
|
nsIFrame* aParent,
|
2003-02-22 00:32:13 +00:00
|
|
|
nsStyleContext* aContext,
|
1999-07-20 07:28:39 +00:00
|
|
|
nsIFrame* aPrevInFlow)
|
|
|
|
{
|
1999-07-21 02:56:23 +00:00
|
|
|
nsresult rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
|
1999-07-20 09:50:48 +00:00
|
|
|
|
2000-03-23 21:10:09 +00:00
|
|
|
// lookup if we're allowed to overlap the OS bar (menubar/taskbar) from the
|
|
|
|
// look&feel object
|
2004-01-21 09:35:59 +00:00
|
|
|
PRBool tempBool;
|
|
|
|
aPresContext->LookAndFeel()->
|
|
|
|
GetMetric(nsILookAndFeel::eMetric_MenusCanOverlapOSBar, tempBool);
|
|
|
|
mMenuCanOverlapOSBar = tempBool;
|
2001-12-14 23:13:08 +00:00
|
|
|
|
1999-10-26 04:44:41 +00:00
|
|
|
// XXX Hack
|
1999-11-24 06:03:41 +00:00
|
|
|
mPresContext = aPresContext;
|
1999-10-26 04:44:41 +00:00
|
|
|
|
1999-07-20 10:35:24 +00:00
|
|
|
CreateViewForFrame(aPresContext, this, aContext, PR_TRUE);
|
1999-07-20 08:37:03 +00:00
|
|
|
|
|
|
|
// Now that we've made a view, remove it and insert it at the correct
|
|
|
|
// position in the view hierarchy (as the root view). We do this so that we
|
|
|
|
// can draw the menus outside the confines of the window.
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIView* ourView = GetView();
|
|
|
|
nsIViewManager* viewManager = ourView->GetViewManager();
|
1999-07-20 08:37:03 +00:00
|
|
|
|
|
|
|
// Remove the view from its old position.
|
2001-12-01 14:31:45 +00:00
|
|
|
viewManager->RemoveChild(ourView);
|
1999-07-20 08:37:03 +00:00
|
|
|
|
|
|
|
// Reinsert ourselves as the root view with a maximum z-index.
|
2004-05-06 03:17:14 +00:00
|
|
|
nsIView* rootView = viewManager->RootView();
|
2001-12-01 14:31:45 +00:00
|
|
|
viewManager->SetViewZIndex(ourView, PR_FALSE, kMaxZ);
|
|
|
|
viewManager->InsertChild(rootView, ourView, nsnull, PR_TRUE);
|
1999-07-20 08:37:03 +00:00
|
|
|
|
2000-01-25 01:53:34 +00:00
|
|
|
// XXX Hack. The menu's view should float above all other views,
|
|
|
|
// so we use the nsIView::SetFloating() to tell the view manager
|
|
|
|
// about that constraint.
|
2001-12-01 14:31:45 +00:00
|
|
|
viewManager->SetViewFloating(ourView, PR_TRUE);
|
2000-01-25 01:53:34 +00:00
|
|
|
|
1999-07-31 01:43:33 +00:00
|
|
|
// XXX Hack. Change our transparency to be non-transparent
|
|
|
|
// until the bug related to update of transparency on show/hide
|
|
|
|
// is fixed.
|
|
|
|
viewManager->SetViewContentTransparency(ourView, PR_FALSE);
|
|
|
|
|
1999-07-20 08:37:03 +00:00
|
|
|
// Create a widget for ourselves.
|
|
|
|
nsWidgetInitData widgetData;
|
1999-07-27 04:27:17 +00:00
|
|
|
widgetData.mWindowType = eWindowType_popup;
|
|
|
|
widgetData.mBorderStyle = eBorderStyle_default;
|
2002-08-21 13:12:30 +00:00
|
|
|
widgetData.clipSiblings = PR_TRUE;
|
1999-09-26 22:45:35 +00:00
|
|
|
|
2003-07-28 21:25:13 +00:00
|
|
|
nsIContent* parentContent = aContent->GetParent();
|
2003-11-19 01:20:56 +00:00
|
|
|
nsIAtom *tag = nsnull;
|
2002-01-05 23:50:52 +00:00
|
|
|
if (parentContent)
|
2003-11-19 01:20:56 +00:00
|
|
|
tag = parentContent->Tag();
|
2002-01-05 23:50:52 +00:00
|
|
|
widgetData.mDropShadow = !(tag && tag == nsXULAtoms::menulist);
|
|
|
|
|
1999-09-28 23:25:48 +00:00
|
|
|
// XXX make sure we are hidden (shouldn't this be done automatically?)
|
2001-12-01 14:31:45 +00:00
|
|
|
viewManager->SetViewVisibility(ourView, nsViewVisibility_kHide);
|
2001-11-10 23:30:13 +00:00
|
|
|
#if defined(XP_MAC) || defined(XP_MACOSX)
|
2002-01-05 23:50:52 +00:00
|
|
|
#ifdef DEBUG
|
2000-10-28 22:17:53 +00:00
|
|
|
printf("XP Popups: This is a nag to indicate that an inconsistent hack is being done on the Mac for popups.\n");
|
2002-01-05 23:50:52 +00:00
|
|
|
#endif
|
1999-09-26 22:45:35 +00:00
|
|
|
static NS_DEFINE_IID(kCPopupCID, NS_POPUP_CID);
|
2003-03-06 23:07:00 +00:00
|
|
|
ourView->CreateWidget(kCPopupCID, &widgetData, nsnull, PR_TRUE, PR_TRUE,
|
|
|
|
eContentTypeUI);
|
1999-09-26 22:45:35 +00:00
|
|
|
#else
|
1999-07-20 08:37:03 +00:00
|
|
|
static NS_DEFINE_IID(kCChildCID, NS_CHILD_CID);
|
2003-03-06 23:07:00 +00:00
|
|
|
ourView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE,
|
|
|
|
eContentTypeUI);
|
1999-09-26 22:45:35 +00:00
|
|
|
#endif
|
|
|
|
|
2001-09-10 07:34:54 +00:00
|
|
|
MoveToAttributePosition();
|
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
return rv;
|
1999-07-20 07:28:39 +00:00
|
|
|
}
|
1999-07-20 09:35:35 +00:00
|
|
|
|
2001-08-18 01:04:47 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::MarkStyleChange(nsBoxLayoutState& aState)
|
|
|
|
{
|
|
|
|
NeedsRecalc();
|
|
|
|
|
|
|
|
if (HasStyleChange())
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// iterate through all children making them dirty
|
|
|
|
MarkChildrenStyleChange();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIBoxLayout> layout;
|
|
|
|
GetLayoutManager(getter_AddRefs(layout));
|
|
|
|
if (layout)
|
|
|
|
layout->BecameDirty(this, aState);
|
|
|
|
|
|
|
|
nsIBox* parent = nsnull;
|
|
|
|
GetParentBox(&parent);
|
2001-10-19 06:27:07 +00:00
|
|
|
nsIMenuFrame* menuFrame = nsnull;
|
|
|
|
CallQueryInterface(parent, &menuFrame);
|
|
|
|
|
|
|
|
if (menuFrame)
|
2001-08-18 01:04:47 +00:00
|
|
|
return parent->RelayoutDirtyChild(aState, this);
|
|
|
|
else {
|
|
|
|
nsIPopupSetFrame* popupSet = GetPopupSetFrame(mPresContext);
|
2002-08-06 12:48:28 +00:00
|
|
|
nsIBox *box;
|
|
|
|
if (popupSet && NS_SUCCEEDED(CallQueryInterface(popupSet, &box))) {
|
2001-08-18 01:04:47 +00:00
|
|
|
nsBoxLayoutState state(mPresContext);
|
|
|
|
box->MarkDirtyChildren(state); // Mark the popupset as dirty.
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsIFrame* frame = nsnull;
|
|
|
|
GetFrame(&frame);
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
|
|
aState.GetPresShell(getter_AddRefs(shell));
|
2003-08-04 12:39:51 +00:00
|
|
|
return frame->GetParent()->ReflowDirtyChild(shell, frame);
|
2001-08-18 01:04:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::MarkDirty(nsBoxLayoutState& aState)
|
|
|
|
{
|
|
|
|
NeedsRecalc();
|
|
|
|
|
|
|
|
nsIFrame* frame;
|
|
|
|
GetFrame(&frame);
|
|
|
|
|
|
|
|
// only reflow if we aren't already dirty.
|
2003-08-04 12:39:51 +00:00
|
|
|
if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) {
|
2001-08-18 01:04:47 +00:00
|
|
|
#ifdef DEBUG_COELESCED
|
|
|
|
Coelesced();
|
|
|
|
#endif
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
frame->AddStateBits(NS_FRAME_IS_DIRTY);
|
2001-08-18 01:04:47 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIBoxLayout> layout;
|
|
|
|
GetLayoutManager(getter_AddRefs(layout));
|
|
|
|
if (layout)
|
|
|
|
layout->BecameDirty(this, aState);
|
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
if (frame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) {
|
2001-08-18 01:04:47 +00:00
|
|
|
#ifdef DEBUG_COELESCED
|
|
|
|
Coelesced();
|
|
|
|
#endif
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIBox* parent = nsnull;
|
|
|
|
GetParentBox(&parent);
|
2001-10-19 06:27:07 +00:00
|
|
|
nsIMenuFrame* menuFrame = nsnull;
|
|
|
|
CallQueryInterface(parent, &menuFrame);
|
|
|
|
|
|
|
|
if (menuFrame)
|
2001-08-18 01:04:47 +00:00
|
|
|
return parent->RelayoutDirtyChild(aState, this);
|
|
|
|
else {
|
|
|
|
nsIPopupSetFrame* popupSet = GetPopupSetFrame(mPresContext);
|
2002-08-06 12:48:28 +00:00
|
|
|
nsIBox *box;
|
|
|
|
if (popupSet && NS_SUCCEEDED(CallQueryInterface(popupSet, &box))) {
|
2001-08-18 01:04:47 +00:00
|
|
|
nsBoxLayoutState state(mPresContext);
|
|
|
|
box->MarkDirtyChildren(state); // Mark the popupset as dirty.
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
|
|
aState.GetPresShell(getter_AddRefs(shell));
|
2003-08-04 12:39:51 +00:00
|
|
|
return frame->GetParent()->ReflowDirtyChild(shell, frame);
|
2001-08-18 01:04:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::RelayoutDirtyChild(nsBoxLayoutState& aState, nsIBox* aChild)
|
|
|
|
{
|
|
|
|
nsIFrame* frame;
|
|
|
|
GetFrame(&frame);
|
|
|
|
|
|
|
|
if (aChild != nsnull) {
|
|
|
|
nsCOMPtr<nsIBoxLayout> layout;
|
|
|
|
GetLayoutManager(getter_AddRefs(layout));
|
|
|
|
if (layout)
|
|
|
|
layout->ChildBecameDirty(this, aState, aChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we are not dirty mark ourselves dirty and tell our parent we are dirty too.
|
2003-08-04 12:39:51 +00:00
|
|
|
if (!(frame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
|
2001-08-18 01:04:47 +00:00
|
|
|
// Mark yourself as dirty and needing to be recalculated
|
2003-08-04 12:39:51 +00:00
|
|
|
frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
2001-08-18 01:04:47 +00:00
|
|
|
NeedsRecalc();
|
|
|
|
|
|
|
|
nsIBox* parentBox = nsnull;
|
|
|
|
GetParentBox(&parentBox);
|
2001-10-19 06:27:07 +00:00
|
|
|
nsIMenuFrame* menuFrame = nsnull;
|
|
|
|
CallQueryInterface(parentBox, &menuFrame);
|
|
|
|
|
|
|
|
if (menuFrame)
|
2001-08-18 01:04:47 +00:00
|
|
|
return parentBox->RelayoutDirtyChild(aState, this);
|
|
|
|
else {
|
|
|
|
nsIPopupSetFrame* popupSet = GetPopupSetFrame(mPresContext);
|
2002-08-06 12:48:28 +00:00
|
|
|
nsIBox *box;
|
|
|
|
if (popupSet && NS_SUCCEEDED(CallQueryInterface(popupSet, &box))) {
|
2001-08-18 01:04:47 +00:00
|
|
|
nsBoxLayoutState state(mPresContext);
|
|
|
|
box->MarkDirtyChildren(state); // Mark the popupset as dirty.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return nsBox::RelayoutDirtyChild(aState, aChild);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-04-25 07:10:48 +00:00
|
|
|
void
|
|
|
|
nsMenuPopupFrame::GetLayoutFlags(PRUint32& aFlags)
|
|
|
|
{
|
2000-05-18 00:37:27 +00:00
|
|
|
aFlags = NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_VISIBILITY;
|
2000-04-25 07:10:48 +00:00
|
|
|
}
|
|
|
|
|
2001-06-04 23:03:24 +00:00
|
|
|
PRBool ParentIsScrollableView(nsIView* aStartView);
|
|
|
|
PRBool ParentIsScrollableView(nsIView* aStartView)
|
|
|
|
{
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIView* scrollportView = aStartView->GetParent();
|
2001-06-04 23:03:24 +00:00
|
|
|
nsIScrollableView* scrollableView = nsnull;
|
|
|
|
if (scrollportView)
|
|
|
|
scrollportView->QueryInterface(NS_GET_IID(nsIScrollableView), (void**) &scrollableView);
|
|
|
|
return scrollableView != nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetViewOffset
|
|
|
|
// Retrieves the offset of the given view with the root view, in the
|
|
|
|
// coordinate system of the root view.
|
1999-07-20 09:35:35 +00:00
|
|
|
void
|
2001-06-04 23:03:24 +00:00
|
|
|
nsMenuPopupFrame::GetViewOffset(nsIView* aView, nsPoint& aPoint)
|
1999-07-20 09:35:35 +00:00
|
|
|
{
|
2001-06-04 23:03:24 +00:00
|
|
|
// Notes:
|
|
|
|
// 1) The root view is the client area of the toplevel window that
|
|
|
|
// this popup is anchored to.
|
|
|
|
// 2) Each menupopup is a child of the root view (see
|
|
|
|
// nsMenuPopupFrame::Init())
|
|
|
|
// 3) The coordinates that we return are the total distance between
|
|
|
|
// the top left of the start view and the origin of the root view.
|
|
|
|
// Note that for extremely tall menus there can be negative bounds
|
|
|
|
// as the menupopup may fall north of the client area (e.g. above
|
|
|
|
// the titlebar). We must take this into account for correct positioning,
|
|
|
|
// however, negative bounds due to views that are the canvas in a
|
|
|
|
// ScrollPortView must be ignored (as this has no bearing on
|
|
|
|
// view offset), hence the call to ParentIsScrollableView.
|
|
|
|
//
|
|
|
|
|
1999-07-20 09:35:35 +00:00
|
|
|
aPoint.x = 0;
|
|
|
|
aPoint.y = 0;
|
|
|
|
|
2002-04-10 09:27:11 +00:00
|
|
|
// Keep track of the root view so that we know to stop there
|
2004-05-06 03:17:14 +00:00
|
|
|
nsIView* rootView = aView->GetViewManager()->RootView();
|
2002-04-10 09:27:11 +00:00
|
|
|
|
1999-07-20 09:35:35 +00:00
|
|
|
nsIView *parent;
|
|
|
|
|
|
|
|
parent = aView;
|
2001-06-04 23:03:24 +00:00
|
|
|
while (parent) {
|
2003-08-04 12:39:51 +00:00
|
|
|
nsRect bounds = parent->GetBounds();
|
2001-06-04 23:03:24 +00:00
|
|
|
if ((bounds.y >= 0 && bounds.x >= 0) || !ParentIsScrollableView(parent)) {
|
|
|
|
//
|
|
|
|
// The Extremely Tall Menu:
|
|
|
|
// +----+---------------------------------------
|
|
|
|
// | | +--------------------+ - -
|
|
|
|
// | | | (Decoration) | | <-(4) | <-(3)
|
|
|
|
// (0, 0) -> +----+-------------+ | + |
|
|
|
|
// | _File _Edit +--------------------+ | +
|
|
|
|
// +------------------| (ScrollPortView) | | |
|
|
|
|
// | | | | |
|
|
|
|
// | | | | <-(1) | <-(2)
|
|
|
|
// | | | | |
|
|
|
|
// |<---------------->| | | |
|
|
|
|
// | (5) |====================| - -
|
|
|
|
// | | MenuFrame >|
|
|
|
|
// | |====================|
|
|
|
|
//
|
|
|
|
// Typically, we want to ignore negative view bounds as these imply
|
|
|
|
// a canvas view inside a scrollport view. However in other cases this
|
|
|
|
// means the view falls outside the positive quadrant of the root view,
|
|
|
|
// and has negative y-axis bounds. We still want to add this negative
|
|
|
|
// bounds as when positioning the menu, the following calculation can
|
|
|
|
// be performed:
|
|
|
|
//
|
|
|
|
// (1) = (2) + (3) + (4)
|
|
|
|
//
|
|
|
|
// (1) - the position on the y-axis in the coordinate system of the root
|
|
|
|
// view at which to position the popup
|
|
|
|
// (2) - the offset of the invoking MenuFrame from the top of the scroll-
|
|
|
|
// port view (adjusted for canvas area scrolled out of view)
|
|
|
|
// (3) - the bounds of the scrollport view with respect to the popup's
|
|
|
|
// view (at this point, aPoint.y)
|
|
|
|
// (4) - the bounds of the popup's view with respect to the root view
|
|
|
|
// (a negative value for extremely tall popups)
|
|
|
|
//
|
|
|
|
// (5) - the position on the x-axis in the coordinate system of the root
|
|
|
|
// view at which to position the popup.
|
|
|
|
|
2000-11-29 06:15:43 +00:00
|
|
|
aPoint.y += bounds.y;
|
2001-06-04 23:03:24 +00:00
|
|
|
aPoint.x += bounds.x;
|
2000-11-29 06:15:43 +00:00
|
|
|
}
|
2002-04-10 09:27:11 +00:00
|
|
|
if (parent == rootView)
|
|
|
|
break;
|
2003-08-04 12:39:51 +00:00
|
|
|
parent = parent->GetParent();
|
1999-07-20 09:35:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-06-04 23:03:24 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetRootViewForPopup
|
|
|
|
// Retrieves the view for the popup widget that contains the given frame.
|
2003-01-08 22:40:14 +00:00
|
|
|
// If the given frame is not contained by a popup widget, return the
|
|
|
|
// root view. This is the root view of the pres context's
|
|
|
|
// viewmanager if aStopAtViewManagerRoot is true; otherwise it's the
|
|
|
|
// root view of the root viewmanager.
|
1999-09-10 08:47:12 +00:00
|
|
|
void
|
2001-06-04 23:03:24 +00:00
|
|
|
nsMenuPopupFrame::GetRootViewForPopup(nsIPresContext* aPresContext,
|
2003-01-08 22:40:14 +00:00
|
|
|
nsIFrame* aStartFrame,
|
|
|
|
PRBool aStopAtViewManagerRoot,
|
|
|
|
nsIView** aResult)
|
1999-09-10 08:47:12 +00:00
|
|
|
{
|
|
|
|
*aResult = nsnull;
|
2001-06-04 23:03:24 +00:00
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIView* view = aStartFrame->GetClosestView();
|
|
|
|
NS_ASSERTION(view, "frame must have a closest view!");
|
|
|
|
if (view) {
|
2003-01-08 22:40:14 +00:00
|
|
|
nsIView* rootView = nsnull;
|
|
|
|
if (aStopAtViewManagerRoot) {
|
2004-05-06 03:17:14 +00:00
|
|
|
rootView = view->GetViewManager()->RootView();
|
2003-01-08 22:40:14 +00:00
|
|
|
}
|
|
|
|
|
2001-06-04 23:03:24 +00:00
|
|
|
while (view) {
|
|
|
|
// Walk up the view hierachy looking for a view whose widget has a
|
|
|
|
// window type of eWindowType_popup - in other words a popup window
|
|
|
|
// widget. If we find one, this is the view we want.
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIWidget* widget = view->GetWidget();
|
2001-06-04 23:03:24 +00:00
|
|
|
if (widget) {
|
|
|
|
nsWindowType wtype;
|
|
|
|
widget->GetWindowType(wtype);
|
|
|
|
if (wtype == eWindowType_popup) {
|
|
|
|
*aResult = view;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-01-08 22:40:14 +00:00
|
|
|
if (aStopAtViewManagerRoot && view == rootView) {
|
|
|
|
*aResult = view;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIView* temp = view->GetParent();
|
2001-06-04 23:03:24 +00:00
|
|
|
if (!temp) {
|
|
|
|
// Otherwise, we've walked all the way up to the root view and not
|
|
|
|
// found a view for a popup window widget. Just return the root view.
|
|
|
|
*aResult = view;
|
|
|
|
}
|
|
|
|
view = temp;
|
|
|
|
}
|
1999-09-10 08:47:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-02-04 07:44:43 +00:00
|
|
|
|
|
|
|
void GetWidgetForView(nsIView *aView, nsIWidget *&aWidget);
|
|
|
|
void GetWidgetForView(nsIView *aView, nsIWidget *&aWidget)
|
|
|
|
{
|
|
|
|
aWidget = nsnull;
|
|
|
|
nsIView *view = aView;
|
|
|
|
while (!aWidget && view)
|
|
|
|
{
|
2003-08-04 12:39:51 +00:00
|
|
|
aWidget = view->GetWidget();
|
2000-02-04 07:44:43 +00:00
|
|
|
if (!aWidget)
|
2003-08-04 12:39:51 +00:00
|
|
|
view = view->GetParent();
|
|
|
|
else
|
|
|
|
NS_ADDREF(aWidget);
|
2000-02-04 07:44:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-02-08 09:30:15 +00:00
|
|
|
//
|
|
|
|
// AdjustClientXYForNestedDocuments
|
|
|
|
//
|
|
|
|
// almost certainly, the document where the mouse was clicked is not
|
|
|
|
// the document that contains the popup, especially if we're viewing a page
|
|
|
|
// with frames. Thus we need to make adjustments to the client coordinates to
|
|
|
|
// take this into account and get them back into the relative coordinates of
|
|
|
|
// this document.
|
|
|
|
//
|
|
|
|
void
|
2001-09-10 07:34:54 +00:00
|
|
|
nsMenuPopupFrame::AdjustClientXYForNestedDocuments ( nsIDOMXULDocument* inPopupDoc, nsIPresShell* inPopupShell,
|
2000-02-08 09:30:15 +00:00
|
|
|
PRInt32 inClientX, PRInt32 inClientY,
|
|
|
|
PRInt32* outAdjX, PRInt32* outAdjY )
|
|
|
|
{
|
|
|
|
if ( !inPopupDoc || !outAdjX || !outAdjY )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Find the widget associated with the popup's document
|
2004-01-30 03:20:41 +00:00
|
|
|
nsIWidget* popupDocumentWidget = nsnull;
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIViewManager* viewManager = inPopupShell->GetViewManager();
|
2000-05-12 03:16:07 +00:00
|
|
|
if ( viewManager ) {
|
2004-05-06 03:17:14 +00:00
|
|
|
nsIView* rootView = viewManager->RootView();
|
2004-01-30 03:20:41 +00:00
|
|
|
popupDocumentWidget = rootView->GetNearestWidget(nsnull);
|
2000-05-12 03:16:07 +00:00
|
|
|
}
|
2000-02-08 09:30:15 +00:00
|
|
|
NS_WARN_IF_FALSE(popupDocumentWidget, "ACK, BAD WIDGET");
|
|
|
|
|
2002-05-18 03:55:16 +00:00
|
|
|
// Find the widget associated with the target's document.
|
|
|
|
// For tooltips, we check the document's tooltipNode (which is set by
|
|
|
|
// nsXULTooltipListener). For regular popups, use popupNode (set by
|
|
|
|
// nsXULPopupListener).
|
|
|
|
|
2000-02-08 09:30:15 +00:00
|
|
|
nsCOMPtr<nsIDOMNode> targetNode;
|
2003-11-19 01:20:56 +00:00
|
|
|
if (mContent->Tag() == nsXULAtoms::tooltip)
|
2002-05-18 03:55:16 +00:00
|
|
|
inPopupDoc->GetTooltipNode(getter_AddRefs(targetNode));
|
|
|
|
else
|
|
|
|
inPopupDoc->GetPopupNode(getter_AddRefs(targetNode));
|
|
|
|
|
2002-09-29 22:21:41 +00:00
|
|
|
//NS_WARN_IF_FALSE(targetNode, "no popup/tooltip node on document!");
|
2000-02-08 09:30:15 +00:00
|
|
|
nsCOMPtr<nsIContent> targetAsContent ( do_QueryInterface(targetNode) );
|
|
|
|
nsCOMPtr<nsIWidget> targetDocumentWidget;
|
|
|
|
if ( targetAsContent ) {
|
2003-07-28 21:25:13 +00:00
|
|
|
nsCOMPtr<nsIDocument> targetDocument = targetAsContent->GetDocument();
|
2000-04-25 01:44:53 +00:00
|
|
|
if (targetDocument) {
|
2003-09-27 04:18:26 +00:00
|
|
|
nsIPresShell *shell = targetDocument->GetShellAt(0);
|
2000-05-12 03:16:07 +00:00
|
|
|
if ( shell ) {
|
2002-10-04 00:56:43 +00:00
|
|
|
// We might be inside a popup widget. If so, we need to use that widget and
|
|
|
|
// not the root view's widget.
|
|
|
|
nsIFrame* targetFrame;
|
|
|
|
shell->GetPrimaryFrameFor(targetAsContent, &targetFrame);
|
|
|
|
nsIView* parentView = nsnull;
|
2002-10-22 00:23:35 +00:00
|
|
|
if (targetFrame) {
|
2002-12-09 15:02:18 +00:00
|
|
|
nsCOMPtr<nsIPresContext> targetContext;
|
|
|
|
shell->GetPresContext(getter_AddRefs(targetContext));
|
|
|
|
if (targetContext) {
|
2003-01-08 22:40:14 +00:00
|
|
|
GetRootViewForPopup(targetContext, targetFrame, PR_TRUE, &parentView);
|
2002-12-09 15:02:18 +00:00
|
|
|
GetWidgetForView(parentView, *getter_AddRefs(targetDocumentWidget));
|
|
|
|
}
|
2002-10-22 00:23:35 +00:00
|
|
|
}
|
2002-10-04 00:56:43 +00:00
|
|
|
if (!targetDocumentWidget) {
|
|
|
|
// We aren't inside a popup. This means we should use the root view's
|
|
|
|
// widget.
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIViewManager* viewManagerTarget = shell->GetViewManager();
|
2002-10-04 00:56:43 +00:00
|
|
|
if ( viewManagerTarget ) {
|
2004-05-06 03:17:14 +00:00
|
|
|
nsIView* rootViewTarget = viewManagerTarget->RootView();
|
2004-01-30 03:20:41 +00:00
|
|
|
targetDocumentWidget = rootViewTarget->GetNearestWidget(nsnull);
|
2000-05-12 03:16:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-04-25 01:44:53 +00:00
|
|
|
}
|
2000-02-08 09:30:15 +00:00
|
|
|
}
|
2002-09-29 22:21:41 +00:00
|
|
|
//NS_WARN_IF_FALSE(targetDocumentWidget, "ACK, BAD TARGET");
|
2000-02-08 09:30:15 +00:00
|
|
|
|
|
|
|
// the offset we need is the difference between the upper left corner of the two widgets. Use
|
|
|
|
// screen coordinates to find the global offset between them.
|
|
|
|
nsRect popupDocTopLeft;
|
|
|
|
if ( popupDocumentWidget ) {
|
|
|
|
nsRect topLeftClient ( 0, 0, 10, 10 );
|
|
|
|
popupDocumentWidget->WidgetToScreen ( topLeftClient, popupDocTopLeft );
|
|
|
|
}
|
|
|
|
nsRect targetDocTopLeft;
|
|
|
|
if ( targetDocumentWidget ) {
|
|
|
|
nsRect topLeftClient ( 0, 0, 10, 10 );
|
|
|
|
targetDocumentWidget->WidgetToScreen ( topLeftClient, targetDocTopLeft );
|
|
|
|
}
|
|
|
|
nsPoint pixelOffset ( targetDocTopLeft.x - popupDocTopLeft.x, targetDocTopLeft.y - popupDocTopLeft.y );
|
|
|
|
|
|
|
|
*outAdjX = inClientX + pixelOffset.x;
|
|
|
|
*outAdjY = inClientY + pixelOffset.y;
|
|
|
|
|
|
|
|
} // AdjustClientXYForNestedDocuments
|
|
|
|
|
|
|
|
|
2000-03-02 04:08:04 +00:00
|
|
|
//
|
|
|
|
// AdjustPositionForAnchorAlign
|
|
|
|
//
|
|
|
|
// Uses the |popupanchor| and |popupalign| attributes on the popup to move the popup around and
|
|
|
|
// anchor it to its parent. |outFlushWithTopBottom| will be TRUE if the popup is flush with either
|
|
|
|
// the top or bottom edge of its parent, and FALSE if it is flush with the left or right edge of
|
|
|
|
// the parent.
|
|
|
|
//
|
|
|
|
void
|
2001-09-10 07:34:54 +00:00
|
|
|
nsMenuPopupFrame::AdjustPositionForAnchorAlign ( PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect,
|
2000-03-02 04:08:04 +00:00
|
|
|
const nsString& aPopupAnchor, const nsString& aPopupAlign,
|
|
|
|
PRBool* outFlushWithTopBottom )
|
|
|
|
{
|
2001-08-15 04:09:41 +00:00
|
|
|
nsAutoString popupAnchor(aPopupAnchor);
|
|
|
|
nsAutoString popupAlign(aPopupAlign);
|
|
|
|
|
2003-05-15 03:42:21 +00:00
|
|
|
if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
|
2004-04-30 13:23:43 +00:00
|
|
|
if (popupAnchor.EqualsLiteral("topright"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAnchor.Assign(NS_LITERAL_STRING("topleft"));
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("topleft"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAnchor.Assign(NS_LITERAL_STRING("topright"));
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("bottomleft"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAnchor.Assign(NS_LITERAL_STRING("bottomright"));
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("bottomright"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAnchor.Assign(NS_LITERAL_STRING("bottomleft"));
|
2001-08-15 04:09:41 +00:00
|
|
|
|
2004-04-30 13:23:43 +00:00
|
|
|
if (popupAlign.EqualsLiteral("topright"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAlign.Assign(NS_LITERAL_STRING("topleft"));
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAlign.EqualsLiteral("topleft"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAlign.Assign(NS_LITERAL_STRING("topright"));
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAlign.EqualsLiteral("bottomleft"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAlign.Assign(NS_LITERAL_STRING("bottomright"));
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("bottomright"))
|
2001-12-16 11:58:03 +00:00
|
|
|
popupAlign.Assign(NS_LITERAL_STRING("bottomleft"));
|
2001-08-15 04:09:41 +00:00
|
|
|
}
|
|
|
|
|
2001-10-02 00:38:34 +00:00
|
|
|
// Adjust position for margins at the aligned corner
|
|
|
|
nsMargin margin;
|
2003-05-15 03:42:21 +00:00
|
|
|
GetStyleMargin()->GetMargin(margin);
|
2004-04-30 13:23:43 +00:00
|
|
|
if (popupAlign.EqualsLiteral("topleft")) {
|
2001-10-02 00:38:34 +00:00
|
|
|
*ioXPos += margin.left;
|
|
|
|
*ioYPos += margin.top;
|
2004-04-30 13:23:43 +00:00
|
|
|
} else if (popupAlign.EqualsLiteral("topright")) {
|
2001-10-02 00:38:34 +00:00
|
|
|
*ioXPos += margin.right;
|
|
|
|
*ioYPos += margin.top;
|
2004-04-30 13:23:43 +00:00
|
|
|
} else if (popupAlign.EqualsLiteral("bottomleft")) {
|
2001-10-02 00:38:34 +00:00
|
|
|
*ioXPos += margin.left;
|
|
|
|
*ioYPos += margin.bottom;
|
2004-04-30 13:23:43 +00:00
|
|
|
} else if (popupAlign.EqualsLiteral("bottomright")) {
|
2001-10-02 00:38:34 +00:00
|
|
|
*ioXPos += margin.right;
|
|
|
|
*ioYPos += margin.bottom;
|
|
|
|
}
|
|
|
|
|
2004-04-30 13:23:43 +00:00
|
|
|
if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("topleft")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioXPos += inParentRect.width;
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topleft")) {
|
2001-08-08 01:38:05 +00:00
|
|
|
*outFlushWithTopBottom = PR_TRUE;
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("bottomright")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioXPos -= (mRect.width - inParentRect.width);
|
|
|
|
*ioYPos -= mRect.height;
|
|
|
|
*outFlushWithTopBottom = PR_TRUE;
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("bottomleft")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioXPos += inParentRect.width;
|
|
|
|
*ioYPos -= (mRect.height - inParentRect.height);
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("topright")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioXPos -= (mRect.width - inParentRect.width);
|
|
|
|
*ioYPos += inParentRect.height;
|
|
|
|
*outFlushWithTopBottom = PR_TRUE;
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topright")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioXPos -= mRect.width;
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("bottomleft")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioYPos -= mRect.height;
|
|
|
|
*outFlushWithTopBottom = PR_TRUE;
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("bottomright")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioXPos -= mRect.width;
|
|
|
|
*ioYPos -= (mRect.height - inParentRect.height);
|
|
|
|
}
|
2004-04-30 13:23:43 +00:00
|
|
|
else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("topleft")) {
|
2000-03-02 04:08:04 +00:00
|
|
|
*ioYPos += inParentRect.height;
|
|
|
|
*outFlushWithTopBottom = PR_TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
NS_WARNING ( "Hmmm, looks like you've hit a anchor/align case we weren't setup for." );
|
|
|
|
|
|
|
|
} // AdjustPositionForAnchorAlign
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// IsMoreRoomOnOtherSideOfParent
|
|
|
|
//
|
|
|
|
// Determine if there is more room on the screen for the popup to live if it was positioned
|
|
|
|
// on the flip side of the parent from the side it is flush against (ie, if it's top edge was
|
|
|
|
// flush against the bottom, is there more room if its bottom edge were flush against the top)
|
|
|
|
//
|
|
|
|
PRBool
|
2001-09-10 07:34:54 +00:00
|
|
|
nsMenuPopupFrame::IsMoreRoomOnOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt32 inScreenViewLocX, PRInt32 inScreenViewLocY,
|
2000-03-02 04:08:04 +00:00
|
|
|
const nsRect & inScreenParentFrameRect, PRInt32 inScreenTopTwips, PRInt32 inScreenLeftTwips,
|
|
|
|
PRInt32 inScreenBottomTwips, PRInt32 inScreenRightTwips )
|
|
|
|
{
|
|
|
|
PRBool switchSides = PR_FALSE;
|
|
|
|
if ( inFlushAboveBelow ) {
|
|
|
|
PRInt32 availAbove = inScreenParentFrameRect.y - inScreenTopTwips;
|
|
|
|
PRInt32 availBelow = inScreenBottomTwips - (inScreenParentFrameRect.y + inScreenParentFrameRect.height) ;
|
|
|
|
if ( inScreenViewLocY > inScreenParentFrameRect.y ) // view is now below parent
|
|
|
|
switchSides = availAbove > availBelow;
|
|
|
|
else
|
|
|
|
switchSides = availBelow > availAbove;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PRInt32 availLeft = inScreenParentFrameRect.x - inScreenLeftTwips;
|
|
|
|
PRInt32 availRight = inScreenRightTwips - (inScreenParentFrameRect.x + inScreenParentFrameRect.width) ;
|
|
|
|
if ( inScreenViewLocX > inScreenParentFrameRect.x ) // view is now to the right of parent
|
|
|
|
switchSides = availLeft > availRight;
|
|
|
|
else
|
|
|
|
switchSides = availRight > availLeft;
|
|
|
|
}
|
|
|
|
|
|
|
|
return switchSides;
|
|
|
|
|
|
|
|
} // IsMoreRoomOnOtherSideOfParent
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// MovePopupToOtherSideOfParent
|
|
|
|
//
|
|
|
|
// Move the popup to the other side of the parent (ie, if it the popup's top edge is flush against the
|
|
|
|
// bottom of its parent, move the popup so that its bottom edge is now flush against the top of its
|
|
|
|
// parent...same idea for left/right).
|
|
|
|
//
|
|
|
|
// NOTE: In moving the popup, it may need to change size in order to stay on the screen. This will
|
|
|
|
// have the side effect of touching |mRect|.
|
|
|
|
//
|
|
|
|
void
|
2001-09-10 07:34:54 +00:00
|
|
|
nsMenuPopupFrame::MovePopupToOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt32* ioXPos, PRInt32* ioYPos,
|
|
|
|
PRInt32* ioScreenViewLocX, PRInt32* ioScreenViewLocY,
|
|
|
|
const nsRect & inScreenParentFrameRect, PRInt32 inScreenTopTwips, PRInt32 inScreenLeftTwips,
|
|
|
|
PRInt32 inScreenBottomTwips, PRInt32 inScreenRightTwips )
|
2000-03-02 04:08:04 +00:00
|
|
|
{
|
|
|
|
if ( inFlushAboveBelow ) {
|
|
|
|
if ( *ioScreenViewLocY > inScreenParentFrameRect.y ) { // view is currently below parent
|
|
|
|
// move it above.
|
|
|
|
PRInt32 shiftDistY = inScreenParentFrameRect.height + mRect.height;
|
|
|
|
*ioYPos -= shiftDistY;
|
|
|
|
*ioScreenViewLocY -= shiftDistY;
|
|
|
|
// trim it to fit.
|
|
|
|
if ( *ioScreenViewLocY < inScreenTopTwips ) {
|
|
|
|
PRInt32 trimY = inScreenTopTwips - *ioScreenViewLocY;
|
|
|
|
*ioYPos += trimY;
|
|
|
|
*ioScreenViewLocY += trimY;
|
|
|
|
mRect.height -= trimY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // view is currently above parent
|
|
|
|
// move it below
|
|
|
|
PRInt32 shiftDistY = inScreenParentFrameRect.height + mRect.height;
|
|
|
|
*ioYPos += shiftDistY;
|
|
|
|
*ioScreenViewLocY += shiftDistY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ( *ioScreenViewLocX > inScreenParentFrameRect.x ) { // view is currently to the right of the parent
|
|
|
|
// move it to the left.
|
|
|
|
PRInt32 shiftDistX = inScreenParentFrameRect.width + mRect.width;
|
|
|
|
*ioXPos -= shiftDistX;
|
|
|
|
*ioScreenViewLocX -= shiftDistX;
|
|
|
|
// trim it to fit.
|
|
|
|
if ( *ioScreenViewLocX < inScreenLeftTwips ) {
|
|
|
|
PRInt32 trimX = inScreenLeftTwips - *ioScreenViewLocX;
|
|
|
|
*ioXPos += trimX;
|
|
|
|
*ioScreenViewLocX += trimX;
|
|
|
|
mRect.width -= trimX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // view is currently to the right of the parent
|
|
|
|
// move it to the right
|
|
|
|
PRInt32 shiftDistX = inScreenParentFrameRect.width + mRect.width;
|
|
|
|
*ioXPos += shiftDistX;
|
|
|
|
*ioScreenViewLocX += shiftDistX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // MovePopupToOtherSideOfParent
|
|
|
|
|
|
|
|
|
2000-02-08 09:30:15 +00:00
|
|
|
|
1999-07-20 09:35:35 +00:00
|
|
|
nsresult
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMenuPopupFrame::SyncViewWithFrame(nsIPresContext* aPresContext,
|
1999-12-08 11:30:47 +00:00
|
|
|
const nsString& aPopupAnchor,
|
|
|
|
const nsString& aPopupAlign,
|
1999-09-10 08:47:12 +00:00
|
|
|
nsIFrame* aFrame,
|
|
|
|
PRInt32 aXPos, PRInt32 aYPos)
|
1999-07-20 09:35:35 +00:00
|
|
|
{
|
1999-11-24 06:03:41 +00:00
|
|
|
NS_ENSURE_ARG(aPresContext);
|
2000-05-12 02:58:21 +00:00
|
|
|
NS_ENSURE_ARG(aFrame);
|
2001-01-24 08:38:09 +00:00
|
|
|
|
2001-09-10 07:34:54 +00:00
|
|
|
if (!mShouldAutoPosition)
|
|
|
|
return NS_OK;
|
|
|
|
|
2001-06-04 23:03:24 +00:00
|
|
|
// |containingView|
|
|
|
|
// The view that contains the frame that is invoking this popup. This is
|
|
|
|
// the canvas view inside the scrollport view. It can have negative bounds
|
|
|
|
// if the canvas is scrolled so that part is off screen.
|
|
|
|
nsIView* containingView = nsnull;
|
|
|
|
nsPoint offset;
|
2002-08-10 10:16:39 +00:00
|
|
|
nsMargin margin;
|
2001-06-04 23:03:24 +00:00
|
|
|
aFrame->GetOffsetFromView(aPresContext, offset, &containingView);
|
|
|
|
if (!containingView)
|
1999-09-10 08:47:12 +00:00
|
|
|
return NS_OK;
|
1999-07-20 09:35:35 +00:00
|
|
|
|
2001-06-04 23:03:24 +00:00
|
|
|
// |view|
|
|
|
|
// The root view for the popup window widget associated with this frame,
|
|
|
|
// or, the view associated with this frame.
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIView* view = GetView();
|
1999-07-20 09:35:35 +00:00
|
|
|
|
2001-01-24 08:38:09 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// (0,-y) +- - - - - - - - - - - - - - - - - - - - - -+ _ _
|
|
|
|
// | (part of canvas scrolled off the top) | | bounds.y |
|
|
|
|
// (0, 0) -> +-------------------------------------------+ + |
|
|
|
|
// | | | | offset
|
|
|
|
// | (part of canvas visible through parent | | dY |
|
|
|
|
// | nsIScrollableView (nsScrollPortView) ) | | |
|
|
|
|
// | | | |
|
|
|
|
// |===========================================| - -
|
|
|
|
// | aFrame > |
|
|
|
|
// |===========================================|
|
|
|
|
// | |
|
|
|
|
// +-------------------------------------------+
|
|
|
|
// | |
|
|
|
|
// +- - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
//
|
|
|
|
// Explanation:
|
|
|
|
//
|
|
|
|
// If the frame we're trying to align this popup to is on a canvas inside
|
|
|
|
// a scrolled viewport (that is, the containing view of the frame is
|
|
|
|
// the child of a scrolling view, nsIScrollableView) the offset y
|
|
|
|
// dimension of that view contains matter that is not onscreen (scrolled
|
|
|
|
// up out of view) and must be adjusted when positioning the popup.
|
|
|
|
// The y dimension on the bounds of the containing view is negative if
|
|
|
|
// any content is offscreen, and the size of this dimension represents
|
|
|
|
// the amount we must adjust the offset by. For most submenus this is 0, and
|
|
|
|
// so the offset is unchanged. For toplevel menus whose containing view is
|
|
|
|
// a window or other view, whose bounds should not be taken into account.
|
|
|
|
//
|
2001-06-04 23:03:24 +00:00
|
|
|
if (ParentIsScrollableView(containingView)) {
|
2003-08-04 12:39:51 +00:00
|
|
|
nsRect bounds = containingView->GetBounds();
|
2001-06-04 23:03:24 +00:00
|
|
|
offset += nsPoint(bounds.x, bounds.y);
|
2001-01-24 08:38:09 +00:00
|
|
|
}
|
1999-07-20 09:35:35 +00:00
|
|
|
|
2001-06-04 23:03:24 +00:00
|
|
|
// |parentPos|
|
|
|
|
// The distance between the containingView and the root view. This provides
|
|
|
|
// a hint as to where to position the menu relative to the window.
|
|
|
|
nsPoint parentPos;
|
|
|
|
GetViewOffset(containingView, parentPos);
|
|
|
|
|
|
|
|
// |parentRect|
|
|
|
|
// The dimensions of the frame invoking the popup.
|
2003-08-04 12:39:51 +00:00
|
|
|
nsRect parentRect = aFrame->GetRect();
|
1999-07-20 09:35:35 +00:00
|
|
|
|
1999-11-23 03:02:01 +00:00
|
|
|
float p2t, t2p;
|
1999-11-24 06:03:41 +00:00
|
|
|
aPresContext->GetScaledPixelsToTwips(&p2t);
|
1999-11-23 03:02:01 +00:00
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIViewManager* viewManager = containingView->GetViewManager();
|
2001-06-04 23:03:24 +00:00
|
|
|
|
1999-11-23 03:02:01 +00:00
|
|
|
nsCOMPtr<nsIDeviceContext> dx;
|
|
|
|
viewManager->GetDeviceContext(*getter_AddRefs(dx));
|
2004-02-05 01:57:10 +00:00
|
|
|
t2p = dx->AppUnitsToDevUnits();
|
2000-02-08 09:30:15 +00:00
|
|
|
|
|
|
|
// get the document and the global script object
|
2003-12-21 05:36:36 +00:00
|
|
|
nsIPresShell *presShell = aPresContext->PresShell();
|
2000-02-08 09:30:15 +00:00
|
|
|
nsCOMPtr<nsIDocument> document;
|
|
|
|
presShell->GetDocument(getter_AddRefs(document));
|
2003-11-19 01:20:56 +00:00
|
|
|
|
|
|
|
PRBool sizedToPopup = (mContent->Tag() != nsXULAtoms::tooltip) &&
|
|
|
|
(nsMenuFrame::IsSizedToPopup(aFrame->GetContent(), PR_FALSE));
|
|
|
|
|
2002-08-06 01:03:27 +00:00
|
|
|
// If we stick to our parent's width, set it here before we move the
|
|
|
|
// window around, because moving is done with respect to the width...
|
|
|
|
if (sizedToPopup) {
|
|
|
|
mRect.width = parentRect.width;
|
|
|
|
}
|
|
|
|
|
2000-02-04 07:27:30 +00:00
|
|
|
// |xpos| and |ypos| hold the x and y positions of where the popup will be moved to,
|
|
|
|
// in _twips_, in the coordinate system of the _parent view_.
|
|
|
|
PRInt32 xpos = 0, ypos = 0;
|
|
|
|
|
2000-03-02 04:08:04 +00:00
|
|
|
// if we are anchored to our parent, there are certain things we don't want to do
|
|
|
|
// when repositioning the view to fit on the screen, such as end up positioned over
|
|
|
|
// the parent. When doing this reposition, we want to move the popup to the side with
|
|
|
|
// the most room. The combination of anchor and alignment dictate if we readjst
|
|
|
|
// above/below or to the left/right.
|
|
|
|
PRBool anchoredToParent = PR_FALSE;
|
|
|
|
PRBool readjustAboveBelow = PR_FALSE;
|
|
|
|
|
|
|
|
if ( aXPos != -1 || aYPos != -1 ) {
|
2000-02-08 09:30:15 +00:00
|
|
|
|
|
|
|
// for this case, we've been handed a specific x/y location (in client coordinates) for
|
|
|
|
// the popup. However, we may be deeply nested in a frameset, etc and so the client coordinates
|
|
|
|
// need some adjusting.
|
|
|
|
nsCOMPtr<nsIDOMXULDocument> xulDoc ( do_QueryInterface(document) );
|
|
|
|
PRInt32 newXPos = 0, newYPos = 0;
|
|
|
|
AdjustClientXYForNestedDocuments ( xulDoc, presShell, aXPos, aYPos, &newXPos, &newYPos );
|
|
|
|
|
|
|
|
xpos = NSIntPixelsToTwips(newXPos, p2t);
|
|
|
|
ypos = NSIntPixelsToTwips(newYPos, p2t);
|
2001-10-02 00:38:34 +00:00
|
|
|
|
|
|
|
// Add in the top and left margins
|
2003-05-15 03:42:21 +00:00
|
|
|
GetStyleMargin()->GetMargin(margin);
|
2001-10-02 00:38:34 +00:00
|
|
|
|
|
|
|
xpos += margin.left;
|
|
|
|
ypos += margin.top;
|
1999-12-08 11:30:47 +00:00
|
|
|
}
|
|
|
|
else {
|
2000-03-02 04:08:04 +00:00
|
|
|
anchoredToParent = PR_TRUE;
|
|
|
|
|
1999-11-23 03:02:01 +00:00
|
|
|
xpos = parentPos.x + offset.x;
|
|
|
|
ypos = parentPos.y + offset.y;
|
2000-03-02 04:08:04 +00:00
|
|
|
|
|
|
|
// move the popup according to the anchor/alignment attributes. This will also tell us
|
|
|
|
// which axis the popup is flush against in case we have to move it around later.
|
|
|
|
AdjustPositionForAnchorAlign ( &xpos, &ypos, parentRect, aPopupAnchor, aPopupAlign, &readjustAboveBelow );
|
1999-09-10 08:47:12 +00:00
|
|
|
}
|
1999-12-08 11:30:47 +00:00
|
|
|
|
2000-04-21 06:55:13 +00:00
|
|
|
// Compute info about the screen dimensions. Because of multiple monitor systems,
|
|
|
|
// the left or top sides of the screen may be in negative space (main monitor is on the
|
|
|
|
// right, etc). We need to be sure to do the right thing.
|
2003-10-22 06:09:48 +00:00
|
|
|
nsCOMPtr<nsIDOMWindowInternal> window(do_QueryInterface(document->GetScriptGlobalObject()));
|
1999-11-23 03:02:01 +00:00
|
|
|
nsCOMPtr<nsIDOMScreen> screen;
|
|
|
|
window->GetScreen(getter_AddRefs(screen));
|
2000-04-21 06:55:13 +00:00
|
|
|
PRInt32 screenWidth = 0, screenHeight = 0;
|
|
|
|
PRInt32 screenLeft = 0, screenTop = 0;
|
|
|
|
PRInt32 screenRight = 0, screenBottom = 0;
|
2000-03-23 21:10:09 +00:00
|
|
|
if ( mMenuCanOverlapOSBar ) {
|
2000-04-21 06:55:13 +00:00
|
|
|
screen->GetLeft(&screenLeft);
|
|
|
|
screen->GetTop(&screenTop);
|
2000-03-23 21:10:09 +00:00
|
|
|
screen->GetWidth(&screenWidth);
|
|
|
|
screen->GetHeight(&screenHeight);
|
|
|
|
}
|
|
|
|
else {
|
2000-04-21 06:55:13 +00:00
|
|
|
screen->GetAvailLeft(&screenLeft);
|
|
|
|
screen->GetAvailTop(&screenTop);
|
2000-03-23 21:10:09 +00:00
|
|
|
screen->GetAvailWidth(&screenWidth);
|
|
|
|
screen->GetAvailHeight(&screenHeight);
|
|
|
|
}
|
2000-03-16 05:13:15 +00:00
|
|
|
screenRight = screenLeft + screenWidth;
|
|
|
|
screenBottom = screenTop + screenHeight;
|
|
|
|
|
|
|
|
// inset the screen by 5px so that menus don't butt up against the side
|
|
|
|
const PRInt32 kTrimMargin = 5;
|
|
|
|
screenLeft += kTrimMargin;
|
|
|
|
screenTop += kTrimMargin;
|
|
|
|
screenRight -= kTrimMargin;
|
|
|
|
screenBottom -= kTrimMargin;
|
|
|
|
screenWidth -= 2 * kTrimMargin;
|
|
|
|
screenHeight -= 2 * kTrimMargin;
|
|
|
|
|
2000-03-02 04:08:04 +00:00
|
|
|
PRInt32 screenTopTwips = NSIntPixelsToTwips(screenTop, p2t);
|
|
|
|
PRInt32 screenLeftTwips = NSIntPixelsToTwips(screenLeft, p2t);
|
2000-02-04 07:27:30 +00:00
|
|
|
PRInt32 screenWidthTwips = NSIntPixelsToTwips(screenWidth, p2t);
|
|
|
|
PRInt32 screenHeightTwips = NSIntPixelsToTwips(screenHeight, p2t);
|
|
|
|
PRInt32 screenRightTwips = NSIntPixelsToTwips(screenRight, p2t);
|
|
|
|
PRInt32 screenBottomTwips = NSIntPixelsToTwips(screenBottom, p2t);
|
1999-11-23 03:02:01 +00:00
|
|
|
|
2000-02-04 07:27:30 +00:00
|
|
|
// Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In
|
|
|
|
// order to determine the screen coordinates of where our view will end up, we
|
|
|
|
// need to find the x/y position of the parent view in screen coords. That is done
|
|
|
|
// by getting the widget associated with the parent view and determining the offset
|
|
|
|
// based on converting (0,0) in its coordinate space to screen coords. We then
|
2000-03-02 04:08:04 +00:00
|
|
|
// offset that point by (|xpos|,|ypos|) to get the true screen coordinates of
|
2000-02-04 07:27:30 +00:00
|
|
|
// the view. *whew*
|
2001-06-04 23:03:24 +00:00
|
|
|
|
|
|
|
// |parentView|
|
|
|
|
// The root view for the window that contains the frame, for frames inside
|
|
|
|
// menupopups this is the first view inside the popup window widget, for
|
|
|
|
// frames inside a toplevel window, this is the root view of the toplevel
|
|
|
|
// window.
|
|
|
|
nsIView* parentView = nsnull;
|
2003-01-08 22:40:14 +00:00
|
|
|
GetRootViewForPopup(aPresContext, aFrame, PR_FALSE, &parentView);
|
2001-06-04 23:03:24 +00:00
|
|
|
if (!parentView)
|
|
|
|
return NS_OK;
|
|
|
|
|
2002-05-20 09:04:25 +00:00
|
|
|
// Use containingView instead of parentView, to account for the scrollarrows
|
|
|
|
// that a parent menu might have.
|
|
|
|
|
2000-02-04 07:27:30 +00:00
|
|
|
nsCOMPtr<nsIWidget> parentViewWidget;
|
2002-05-20 09:04:25 +00:00
|
|
|
GetWidgetForView ( containingView, *getter_AddRefs(parentViewWidget) );
|
2000-03-02 04:08:04 +00:00
|
|
|
nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect;
|
|
|
|
parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect );
|
|
|
|
PRInt32 screenViewLocX = NSIntPixelsToTwips(screenParentWidgetRect.x,p2t) + (xpos - parentPos.x);
|
|
|
|
PRInt32 screenViewLocY = NSIntPixelsToTwips(screenParentWidgetRect.y,p2t) + (ypos - parentPos.y);
|
|
|
|
|
|
|
|
if ( anchoredToParent ) {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Popup is anchored to the parent, guarantee that it does not cover the parent. We
|
|
|
|
// shouldn't do anything funky if it will already fit on the screen as is.
|
|
|
|
//
|
|
|
|
|
2002-05-20 09:04:25 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// +------------------------+
|
|
|
|
// | /\ |
|
|
|
|
// parentPos -> - +------------------------+
|
|
|
|
// | | |
|
|
|
|
// offset | | |
|
|
|
|
// | | |
|
|
|
|
// | | | (screenViewLocX,screenViewLocY)
|
|
|
|
// - |========================|+--------------
|
|
|
|
// | parentRect > ||
|
|
|
|
// |========================||
|
|
|
|
// | || Submenu
|
|
|
|
// +------------------------+| ( = mRect )
|
|
|
|
// | \/ ||
|
|
|
|
// +------------------------+
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-03-02 04:08:04 +00:00
|
|
|
// compute screen coordinates of parent frame so we can play with it. Make sure we put it
|
|
|
|
// into twips as everything else is as well.
|
|
|
|
nsRect screenParentFrameRect ( NSTwipsToIntPixels(offset.x,t2p), NSTwipsToIntPixels(offset.y,t2p),
|
|
|
|
parentRect.width, parentRect.height );
|
|
|
|
parentViewWidget->WidgetToScreen ( screenParentFrameRect, screenParentFrameRect );
|
|
|
|
screenParentFrameRect.x = NSIntPixelsToTwips(screenParentFrameRect.x, p2t);
|
|
|
|
screenParentFrameRect.y = NSIntPixelsToTwips(screenParentFrameRect.y, p2t);
|
2002-05-17 21:23:36 +00:00
|
|
|
|
2002-07-05 23:12:51 +00:00
|
|
|
// Don't let it spill off the screen to the top
|
2002-05-17 21:23:36 +00:00
|
|
|
if (screenViewLocY < screenTopTwips) {
|
|
|
|
PRInt32 moveDist = screenTopTwips - screenViewLocY;
|
|
|
|
screenViewLocY = screenTopTwips;
|
|
|
|
ypos += moveDist;
|
|
|
|
}
|
2000-03-02 04:08:04 +00:00
|
|
|
|
|
|
|
// if it doesn't fit on the screen, do our magic.
|
|
|
|
if ( (screenViewLocX + mRect.width) > screenRightTwips ||
|
2002-07-05 23:12:51 +00:00
|
|
|
screenViewLocX < screenLeftTwips ||
|
2000-03-02 04:08:04 +00:00
|
|
|
(screenViewLocY + mRect.height) > screenBottomTwips ) {
|
|
|
|
|
|
|
|
// figure out which side of the parent has the most free space so we can move/resize
|
|
|
|
// the popup there. This should still work if the parent frame is partially screen.
|
|
|
|
PRBool switchSides = IsMoreRoomOnOtherSideOfParent ( readjustAboveBelow, screenViewLocX, screenViewLocY,
|
|
|
|
screenParentFrameRect, screenTopTwips, screenLeftTwips,
|
|
|
|
screenBottomTwips, screenRightTwips );
|
|
|
|
|
|
|
|
// move the popup to the correct side, if necessary. Note that MovePopupToOtherSideOfParent()
|
|
|
|
// can change width/height of |mRect|.
|
|
|
|
if ( switchSides )
|
|
|
|
MovePopupToOtherSideOfParent ( readjustAboveBelow, &xpos, &ypos, &screenViewLocX, &screenViewLocY,
|
|
|
|
screenParentFrameRect, screenTopTwips, screenLeftTwips,
|
|
|
|
screenBottomTwips, screenRightTwips );
|
|
|
|
|
|
|
|
// We are allowed to move the popup along the axis to which we're not anchored to the parent
|
|
|
|
// in order to get it to not spill off the screen.
|
|
|
|
if ( readjustAboveBelow ) {
|
2000-03-16 05:13:15 +00:00
|
|
|
// move left to be on screen, but don't let it go off the screen at the left
|
2000-03-02 04:08:04 +00:00
|
|
|
if ( (screenViewLocX + mRect.width) > screenRightTwips ) {
|
|
|
|
PRInt32 moveDistX = (screenViewLocX + mRect.width) - screenRightTwips;
|
2000-03-16 05:13:15 +00:00
|
|
|
if ( screenViewLocX - moveDistX < screenLeftTwips )
|
|
|
|
moveDistX = screenViewLocX - screenLeftTwips;
|
2000-03-02 04:08:04 +00:00
|
|
|
screenViewLocX -= moveDistX;
|
|
|
|
xpos -= moveDistX;
|
2002-07-05 23:12:51 +00:00
|
|
|
} else if (screenViewLocX < screenLeftTwips) {
|
|
|
|
// move right to be on screen, but don't let it go off the screen at the right
|
|
|
|
PRInt32 moveDistX = screenLeftTwips - screenViewLocX;
|
|
|
|
if ( (screenViewLocX + mRect.width + moveDistX) > screenRightTwips )
|
|
|
|
moveDistX = screenRightTwips - screenViewLocX - mRect.width;
|
|
|
|
screenViewLocX += moveDistX;
|
|
|
|
xpos += moveDistX;
|
2000-03-02 04:08:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2000-03-16 05:13:15 +00:00
|
|
|
// move it up to be on screen, but don't let it go off the screen at the top
|
2003-10-30 02:09:52 +00:00
|
|
|
/*
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* |+---- screenViewLocY
|
|
|
|
* ||
|
|
|
|
* || Submenu ( = mRect )
|
|
|
|
* -+|
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* - - - - - - - - - - screenBottomTwips (bottom of the screen)
|
|
|
|
* | \
|
|
|
|
* | } moveDistY
|
|
|
|
* | /
|
|
|
|
* +---- screenViewLocY + mRect.height
|
|
|
|
*/
|
2002-05-20 09:04:25 +00:00
|
|
|
|
2000-03-02 04:08:04 +00:00
|
|
|
if ( (screenViewLocY + mRect.height) > screenBottomTwips ) {
|
2002-05-20 09:04:25 +00:00
|
|
|
// XXX Bug 84121 comment 48 says the next line has to use screenHeightTwips, why not screenBottomTwips?
|
2002-01-14 05:45:53 +00:00
|
|
|
PRInt32 moveDistY = (screenViewLocY + mRect.height) - screenHeightTwips;
|
2000-03-16 05:13:15 +00:00
|
|
|
if ( screenViewLocY - moveDistY < screenTopTwips )
|
|
|
|
moveDistY = screenViewLocY - screenTopTwips;
|
2000-03-02 04:08:04 +00:00
|
|
|
screenViewLocY -= moveDistY;
|
|
|
|
ypos -= moveDistY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resize it to fit on the screen. By this point, we've given the popup as much
|
|
|
|
// room as we can w/out covering the parent. If it still can't be as big
|
|
|
|
// as it wants to be, well, it just has to suck up and deal.
|
2002-05-20 09:04:25 +00:00
|
|
|
//
|
|
|
|
// ySpillage is calculated the same way as moveDistY above. see picture there.
|
|
|
|
|
2000-03-02 04:08:04 +00:00
|
|
|
PRInt32 xSpillage = (screenViewLocX + mRect.width) - screenRightTwips;
|
|
|
|
if ( xSpillage > 0 )
|
|
|
|
mRect.width -= xSpillage;
|
2002-05-20 09:04:25 +00:00
|
|
|
PRInt32 ySpillage = (screenViewLocY + mRect.height) - screenBottomTwips;
|
2000-03-02 04:08:04 +00:00
|
|
|
if ( ySpillage > 0 )
|
|
|
|
mRect.height -= ySpillage;
|
|
|
|
|
2002-01-14 05:45:53 +00:00
|
|
|
// shrink to fit onto the screen, vertically and horizontally
|
|
|
|
if(mRect.width > screenWidthTwips)
|
|
|
|
mRect.width = screenWidthTwips;
|
|
|
|
if(mRect.height > screenHeightTwips)
|
|
|
|
mRect.height = screenHeightTwips;
|
|
|
|
|
2000-03-02 04:08:04 +00:00
|
|
|
} // if it doesn't fit on screen
|
|
|
|
} // if anchored to parent
|
|
|
|
else {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Popup not anchored to anything, just make sure it's on the screen by any
|
|
|
|
// means necessary
|
|
|
|
//
|
|
|
|
|
|
|
|
// shrink to fit onto the screen, vertically and horizontally
|
|
|
|
if(mRect.width > screenWidthTwips)
|
|
|
|
mRect.width = screenWidthTwips;
|
|
|
|
if(mRect.height > screenHeightTwips)
|
|
|
|
mRect.height = screenHeightTwips;
|
|
|
|
|
|
|
|
// we now know where the view is...make sure that it's still onscreen at all!
|
|
|
|
if ( screenViewLocX < screenLeftTwips ) {
|
|
|
|
PRInt32 moveDistX = screenLeftTwips - screenViewLocX;
|
|
|
|
xpos += moveDistX;
|
|
|
|
screenViewLocX += moveDistX;
|
|
|
|
}
|
|
|
|
if ( screenViewLocY < screenTopTwips ) {
|
|
|
|
PRInt32 moveDistY = screenTopTwips - screenViewLocY;
|
|
|
|
ypos += moveDistY;
|
|
|
|
screenViewLocY += moveDistY;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure it is not even partially offscreen.
|
2003-08-01 13:10:31 +00:00
|
|
|
if ( (screenViewLocX + mRect.width) > screenRightTwips )
|
2003-08-01 13:26:34 +00:00
|
|
|
xpos -= (screenViewLocX + mRect.width) - screenRightTwips;
|
2000-03-02 04:08:04 +00:00
|
|
|
if ( (screenViewLocY + mRect.height) > screenBottomTwips )
|
2002-08-10 10:16:39 +00:00
|
|
|
ypos -= (mRect.height + margin.top + margin.bottom);
|
2000-03-02 04:08:04 +00:00
|
|
|
|
|
|
|
}
|
2000-02-04 07:27:30 +00:00
|
|
|
|
|
|
|
viewManager->MoveViewTo(view, xpos, ypos);
|
2001-12-19 12:11:12 +00:00
|
|
|
|
2002-03-06 01:52:06 +00:00
|
|
|
// Now that we've positioned the view, sync up the frame's origin.
|
2003-08-04 12:39:51 +00:00
|
|
|
nsPoint frameOrigin = GetPosition();
|
|
|
|
nsPoint offsetToView;
|
2002-03-06 01:52:06 +00:00
|
|
|
GetOriginToViewOffset(aPresContext, offsetToView, nsnull);
|
|
|
|
frameOrigin -= offsetToView;
|
2003-08-04 12:39:51 +00:00
|
|
|
nsBoxFrame::SetPosition(frameOrigin);
|
2002-03-06 01:52:06 +00:00
|
|
|
|
2002-05-17 21:23:36 +00:00
|
|
|
if (sizedToPopup) {
|
2002-01-12 01:59:50 +00:00
|
|
|
nsBoxLayoutState state(mPresContext);
|
|
|
|
SetBounds(state, nsRect(mRect.x, mRect.y, parentRect.width, mRect.height));
|
2001-12-19 12:11:12 +00:00
|
|
|
}
|
2002-01-12 01:59:50 +00:00
|
|
|
|
2000-03-03 01:50:43 +00:00
|
|
|
nsAutoString shouldDisplay, menuActive;
|
2001-08-17 08:14:14 +00:00
|
|
|
mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, menuActive);
|
2004-04-30 13:23:43 +00:00
|
|
|
if (!menuActive.EqualsLiteral("true")) {
|
2001-08-17 08:14:14 +00:00
|
|
|
mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed, shouldDisplay);
|
2004-04-30 13:23:43 +00:00
|
|
|
if ( shouldDisplay.EqualsLiteral("true") )
|
2001-08-17 08:14:14 +00:00
|
|
|
mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, NS_LITERAL_STRING("true"), PR_TRUE);
|
2000-03-03 01:50:43 +00:00
|
|
|
}
|
2000-02-15 17:14:59 +00:00
|
|
|
|
1999-07-20 09:35:35 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-11-27 22:00:33 +00:00
|
|
|
static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
|
|
|
|
nsIFrame** aResult)
|
|
|
|
{
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIContent* child = nsnull;
|
2001-06-04 23:23:35 +00:00
|
|
|
if (aChild)
|
2003-08-04 12:39:51 +00:00
|
|
|
child = aChild->GetContent();
|
2004-01-07 22:30:53 +00:00
|
|
|
aShell->FrameConstructor()->GetInsertionPoint(aShell, aFrame,
|
|
|
|
child, aResult);
|
2000-11-27 22:00:33 +00:00
|
|
|
}
|
1999-07-21 07:42:16 +00:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-09-21 01:03:00 +00:00
|
|
|
nsMenuPopupFrame::GetNextMenuItem(nsIMenuFrame* aStart, nsIMenuFrame** aResult)
|
1999-07-21 07:42:16 +00:00
|
|
|
{
|
2000-04-04 06:43:02 +00:00
|
|
|
nsIFrame* immediateParent = nsnull;
|
2003-12-21 05:36:36 +00:00
|
|
|
GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
|
2000-04-04 06:43:02 +00:00
|
|
|
if (!immediateParent)
|
|
|
|
immediateParent = this;
|
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
nsIFrame* currFrame = nsnull;
|
|
|
|
nsIFrame* startFrame = nsnull;
|
1999-07-22 09:01:55 +00:00
|
|
|
if (aStart) {
|
2001-01-04 20:44:42 +00:00
|
|
|
aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame);
|
1999-09-21 01:03:00 +00:00
|
|
|
if (currFrame) {
|
|
|
|
startFrame = currFrame;
|
2003-08-04 12:39:51 +00:00
|
|
|
currFrame = currFrame->GetNextSibling();
|
1999-09-21 01:03:00 +00:00
|
|
|
}
|
1999-07-22 09:01:55 +00:00
|
|
|
}
|
2000-04-04 06:43:02 +00:00
|
|
|
else
|
2004-01-09 14:20:53 +00:00
|
|
|
currFrame = immediateParent->GetFirstChild(nsnull);
|
1999-07-22 09:01:55 +00:00
|
|
|
|
1999-07-22 01:59:09 +00:00
|
|
|
while (currFrame) {
|
1999-07-21 07:42:16 +00:00
|
|
|
// See if it's a menu item.
|
2003-08-04 12:39:51 +00:00
|
|
|
if (IsValidItem(currFrame->GetContent())) {
|
1999-09-21 01:03:00 +00:00
|
|
|
nsCOMPtr<nsIMenuFrame> menuFrame = do_QueryInterface(currFrame);
|
|
|
|
*aResult = menuFrame.get();
|
|
|
|
NS_IF_ADDREF(*aResult);
|
1999-07-21 07:42:16 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2003-08-04 12:39:51 +00:00
|
|
|
currFrame = currFrame->GetNextSibling();
|
1999-07-21 07:42:16 +00:00
|
|
|
}
|
|
|
|
|
2004-01-09 14:20:53 +00:00
|
|
|
currFrame = immediateParent->GetFirstChild(nsnull);
|
1999-07-22 01:59:09 +00:00
|
|
|
|
1999-07-21 07:42:16 +00:00
|
|
|
// Still don't have anything. Try cycling from the beginning.
|
1999-09-21 01:03:00 +00:00
|
|
|
while (currFrame && currFrame != startFrame) {
|
1999-07-21 07:42:16 +00:00
|
|
|
// See if it's a menu item.
|
2003-08-04 12:39:51 +00:00
|
|
|
if (IsValidItem(currFrame->GetContent())) {
|
1999-09-21 01:03:00 +00:00
|
|
|
nsCOMPtr<nsIMenuFrame> menuFrame = do_QueryInterface(currFrame);
|
|
|
|
*aResult = menuFrame.get();
|
|
|
|
NS_IF_ADDREF(*aResult);
|
1999-07-21 07:42:16 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-07-22 01:59:09 +00:00
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
currFrame = currFrame->GetNextSibling();
|
1999-07-21 07:42:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// No luck. Just return our start value.
|
|
|
|
*aResult = aStart;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-09-21 01:03:00 +00:00
|
|
|
nsMenuPopupFrame::GetPreviousMenuItem(nsIMenuFrame* aStart, nsIMenuFrame** aResult)
|
1999-07-21 07:42:16 +00:00
|
|
|
{
|
2000-04-04 06:43:02 +00:00
|
|
|
nsIFrame* immediateParent = nsnull;
|
2003-12-21 05:36:36 +00:00
|
|
|
GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
|
2000-04-04 06:43:02 +00:00
|
|
|
if (!immediateParent)
|
|
|
|
immediateParent = this;
|
|
|
|
|
2004-01-09 14:20:53 +00:00
|
|
|
nsFrameList frames(immediateParent->GetFirstChild(nsnull));
|
2000-04-04 06:43:02 +00:00
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
nsIFrame* currFrame = nsnull;
|
|
|
|
nsIFrame* startFrame = nsnull;
|
1999-07-22 09:01:55 +00:00
|
|
|
if (aStart) {
|
2001-01-04 20:44:42 +00:00
|
|
|
aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame);
|
1999-09-21 01:03:00 +00:00
|
|
|
if (currFrame) {
|
|
|
|
startFrame = currFrame;
|
2000-04-04 06:43:02 +00:00
|
|
|
currFrame = frames.GetPrevSiblingFor(currFrame);
|
1999-09-21 01:03:00 +00:00
|
|
|
}
|
1999-07-22 09:01:55 +00:00
|
|
|
}
|
2000-04-04 06:43:02 +00:00
|
|
|
else currFrame = frames.LastChild();
|
1999-07-22 09:01:55 +00:00
|
|
|
|
1999-07-22 01:59:09 +00:00
|
|
|
while (currFrame) {
|
1999-07-21 07:42:16 +00:00
|
|
|
// See if it's a menu item.
|
2003-08-04 12:39:51 +00:00
|
|
|
if (IsValidItem(currFrame->GetContent())) {
|
1999-09-21 01:03:00 +00:00
|
|
|
nsCOMPtr<nsIMenuFrame> menuFrame = do_QueryInterface(currFrame);
|
|
|
|
*aResult = menuFrame.get();
|
|
|
|
NS_IF_ADDREF(*aResult);
|
1999-07-21 07:42:16 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-04-04 06:43:02 +00:00
|
|
|
currFrame = frames.GetPrevSiblingFor(currFrame);
|
1999-07-21 07:42:16 +00:00
|
|
|
}
|
|
|
|
|
2000-04-04 06:43:02 +00:00
|
|
|
currFrame = frames.LastChild();
|
1999-07-22 01:59:09 +00:00
|
|
|
|
|
|
|
// Still don't have anything. Try cycling from the end.
|
1999-09-21 01:03:00 +00:00
|
|
|
while (currFrame && currFrame != startFrame) {
|
1999-07-21 07:42:16 +00:00
|
|
|
// See if it's a menu item.
|
2003-08-04 12:39:51 +00:00
|
|
|
if (IsValidItem(currFrame->GetContent())) {
|
1999-09-21 01:03:00 +00:00
|
|
|
nsCOMPtr<nsIMenuFrame> menuFrame = do_QueryInterface(currFrame);
|
|
|
|
*aResult = menuFrame.get();
|
|
|
|
NS_IF_ADDREF(*aResult);
|
1999-07-21 07:42:16 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-07-22 01:59:09 +00:00
|
|
|
|
2000-04-04 06:43:02 +00:00
|
|
|
currFrame = frames.GetPrevSiblingFor(currFrame);
|
1999-07-21 07:42:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// No luck. Just return our start value.
|
|
|
|
*aResult = aStart;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-02-13 08:33:39 +00:00
|
|
|
NS_IMETHODIMP nsMenuPopupFrame::GetCurrentMenuItem(nsIMenuFrame** aResult)
|
|
|
|
{
|
|
|
|
*aResult = mCurrentMenu;
|
|
|
|
NS_IF_ADDREF(*aResult);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2002-11-06 02:27:57 +00:00
|
|
|
NS_IMETHODIMP nsMenuPopupFrame::ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* When this popup is open, should clicks outside of it be consumed?
|
|
|
|
* Return PR_TRUE if the popup hould rollup on an outside click,
|
|
|
|
* but consume that click so it can't be used for anything else.
|
|
|
|
* Return PR_FALSE to allow clicks outside the popup to activate content
|
|
|
|
* even when the popup is open.
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Should clicks outside of a popup be eaten?
|
|
|
|
*
|
2003-05-12 22:47:46 +00:00
|
|
|
* Menus Autocomplete Comboboxes
|
|
|
|
* Mac Eat No Eat
|
|
|
|
* Win No No Eat
|
|
|
|
* Unix Eat No Eat
|
2002-11-06 02:27:57 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
aConsumeOutsideClicks = PR_TRUE;
|
|
|
|
|
2003-07-28 21:25:13 +00:00
|
|
|
nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
|
2002-11-06 02:27:57 +00:00
|
|
|
|
|
|
|
if (parentContent) {
|
2003-11-19 01:20:56 +00:00
|
|
|
nsIAtom *parentTag = parentContent->Tag();
|
2003-05-12 22:47:46 +00:00
|
|
|
if (parentTag == nsXULAtoms::menulist)
|
|
|
|
return NS_OK; // Consume outside clicks for combo boxes on all platforms
|
|
|
|
if (parentTag == nsXULAtoms::menu || parentTag == nsXULAtoms::popupset) {
|
2004-01-09 19:34:54 +00:00
|
|
|
#if defined(XP_WIN) || defined(XP_OS2)
|
2003-05-12 22:47:46 +00:00
|
|
|
// Don't consume outside clicks for menus in Windows
|
|
|
|
aConsumeOutsideClicks = PR_FALSE;
|
|
|
|
#endif
|
|
|
|
return NS_OK;
|
2002-11-06 02:27:57 +00:00
|
|
|
}
|
|
|
|
if (parentTag == nsXULAtoms::textbox) {
|
2003-05-12 22:47:46 +00:00
|
|
|
// Don't consume outside clicks for autocomplete widget
|
2002-11-06 02:27:57 +00:00
|
|
|
nsAutoString typeString;
|
|
|
|
parentContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::type, typeString);
|
2004-04-30 13:23:43 +00:00
|
|
|
if (typeString.EqualsLiteral("autocomplete"))
|
2002-11-06 02:27:57 +00:00
|
|
|
aConsumeOutsideClicks = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-11-13 21:25:07 +00:00
|
|
|
nsIScrollableView* nsMenuPopupFrame::GetScrollableView(nsIFrame* aStart)
|
|
|
|
{
|
|
|
|
if ( ! aStart )
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsIFrame* currFrame;
|
|
|
|
nsIScrollableView* scrollableView=nsnull;
|
|
|
|
|
|
|
|
// try start frame and siblings
|
|
|
|
currFrame=aStart;
|
|
|
|
do {
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIView* view = currFrame->GetView();
|
2000-11-13 21:25:07 +00:00
|
|
|
if ( view )
|
2003-06-19 23:44:01 +00:00
|
|
|
CallQueryInterface(view, &scrollableView);
|
2000-11-13 21:25:07 +00:00
|
|
|
if ( scrollableView )
|
|
|
|
return scrollableView;
|
2003-08-04 12:39:51 +00:00
|
|
|
currFrame = currFrame->GetNextSibling();
|
2000-11-13 21:25:07 +00:00
|
|
|
} while ( currFrame );
|
|
|
|
|
|
|
|
// try children
|
|
|
|
nsIFrame* childFrame;
|
|
|
|
currFrame=aStart;
|
|
|
|
do {
|
2004-01-09 14:20:53 +00:00
|
|
|
childFrame = currFrame->GetFirstChild(nsnull);
|
2000-11-13 21:25:07 +00:00
|
|
|
scrollableView=GetScrollableView(childFrame);
|
|
|
|
if ( scrollableView )
|
|
|
|
return scrollableView;
|
2003-08-04 12:39:51 +00:00
|
|
|
currFrame = currFrame->GetNextSibling();
|
2000-11-13 21:25:07 +00:00
|
|
|
} while ( currFrame );
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsIMenuFrame* aMenuItem)
|
|
|
|
{
|
|
|
|
nsIFrame* frame=nsnull;
|
2001-01-04 20:44:42 +00:00
|
|
|
aMenuItem->QueryInterface(NS_GET_IID(nsIFrame), (void**)&frame);
|
2000-11-13 21:25:07 +00:00
|
|
|
if ( frame ) {
|
|
|
|
nsIFrame* childFrame=nsnull;
|
2004-01-09 14:20:53 +00:00
|
|
|
childFrame = GetFirstChild(nsnull);
|
2000-11-13 21:25:07 +00:00
|
|
|
nsIScrollableView *scrollableView;
|
|
|
|
scrollableView=GetScrollableView(childFrame);
|
|
|
|
if ( scrollableView ) {
|
|
|
|
nsIView* view=nsnull;
|
2001-01-04 20:44:42 +00:00
|
|
|
scrollableView->QueryInterface(NS_GET_IID(nsIView), (void**)&view);
|
2000-11-13 21:25:07 +00:00
|
|
|
if ( view ) {
|
|
|
|
nscoord scrollX, scrollY;
|
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nsRect viewRect = view->GetBounds();
|
|
|
|
nsRect itemRect = frame->GetRect();
|
2000-11-13 21:25:07 +00:00
|
|
|
scrollableView->GetScrollPosition(scrollX, scrollY);
|
|
|
|
|
|
|
|
// scroll down
|
|
|
|
if ( itemRect.y + itemRect.height > scrollY + viewRect.height )
|
|
|
|
scrollableView->ScrollTo(scrollX, itemRect.y + itemRect.height - viewRect.height, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
|
|
|
|
|
|
|
|
// scroll up
|
|
|
|
else if ( itemRect.y < scrollY )
|
|
|
|
scrollableView->ScrollTo(scrollX, itemRect.y, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem)
|
1999-07-21 07:42:16 +00:00
|
|
|
{
|
2002-10-04 00:56:43 +00:00
|
|
|
// When a context menu is open, the current menu is locked, and no change
|
|
|
|
// to the menu is allowed.
|
|
|
|
nsCOMPtr<nsIMenuParent> contextMenu;
|
|
|
|
GetContextMenu(getter_AddRefs(contextMenu));
|
|
|
|
if (contextMenu)
|
|
|
|
return NS_OK;
|
|
|
|
|
1999-07-21 07:42:16 +00:00
|
|
|
if (mCurrentMenu == aMenuItem)
|
|
|
|
return NS_OK;
|
1999-09-21 01:03:00 +00:00
|
|
|
|
1999-07-22 09:01:55 +00:00
|
|
|
// Unset the current child.
|
1999-07-21 08:51:41 +00:00
|
|
|
if (mCurrentMenu) {
|
1999-09-21 01:03:00 +00:00
|
|
|
PRBool isOpen = PR_FALSE;
|
|
|
|
mCurrentMenu->MenuIsOpen(isOpen);
|
1999-09-25 03:39:35 +00:00
|
|
|
mCurrentMenu->SelectMenu(PR_FALSE);
|
2000-02-13 08:33:39 +00:00
|
|
|
if (isOpen) {
|
|
|
|
// Don't close up immediately.
|
|
|
|
// Kick off a close timer.
|
2000-02-13 08:44:31 +00:00
|
|
|
KillCloseTimer(); // Ensure we don't have another stray waiting closure.
|
2000-02-13 08:33:39 +00:00
|
|
|
PRInt32 menuDelay = 300; // ms
|
|
|
|
|
2004-01-21 09:35:59 +00:00
|
|
|
mPresContext->LookAndFeel()->
|
|
|
|
GetMetric(nsILookAndFeel::eMetric_SubmenuDelay, menuDelay);
|
1999-09-25 03:39:35 +00:00
|
|
|
|
2000-02-13 08:33:39 +00:00
|
|
|
// Kick off the timer.
|
2000-09-13 23:57:52 +00:00
|
|
|
mCloseTimer = do_CreateInstance("@mozilla.org/timer;1");
|
2002-09-07 05:38:16 +00:00
|
|
|
nsCOMPtr<nsITimerInternal> ti = do_QueryInterface(mCloseTimer);
|
2002-09-20 22:32:32 +00:00
|
|
|
ti->SetIdle(PR_FALSE);
|
2002-09-07 05:38:16 +00:00
|
|
|
mCloseTimer->InitWithCallback(this, menuDelay, nsITimer::TYPE_ONE_SHOT);
|
2000-02-13 08:33:39 +00:00
|
|
|
mTimerMenu = mCurrentMenu;
|
|
|
|
}
|
1999-07-21 08:51:41 +00:00
|
|
|
}
|
|
|
|
|
1999-07-21 07:42:16 +00:00
|
|
|
// Set the new child.
|
1999-07-21 08:51:41 +00:00
|
|
|
if (aMenuItem) {
|
2000-11-13 21:25:07 +00:00
|
|
|
EnsureMenuItemIsVisible(aMenuItem);
|
1999-09-21 01:03:00 +00:00
|
|
|
aMenuItem->SelectMenu(PR_TRUE);
|
1999-07-21 08:51:41 +00:00
|
|
|
}
|
1999-07-22 09:01:55 +00:00
|
|
|
|
1999-07-21 07:42:16 +00:00
|
|
|
mCurrentMenu = aMenuItem;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-07-21 08:51:41 +00:00
|
|
|
|
|
|
|
|
2000-02-09 09:34:35 +00:00
|
|
|
NS_IMETHODIMP
|
1999-07-23 08:36:39 +00:00
|
|
|
nsMenuPopupFrame::Escape(PRBool& aHandledFlag)
|
1999-07-23 07:49:43 +00:00
|
|
|
{
|
2003-03-05 01:43:25 +00:00
|
|
|
mIncrementalString.Truncate();
|
2002-04-24 07:15:35 +00:00
|
|
|
|
2002-10-04 00:56:43 +00:00
|
|
|
// See if we have a context menu open.
|
|
|
|
nsCOMPtr<nsIMenuParent> contextMenu;
|
|
|
|
GetContextMenu(getter_AddRefs(contextMenu));
|
|
|
|
if (contextMenu) {
|
|
|
|
// Get the context menu parent.
|
|
|
|
nsIFrame* childFrame;
|
|
|
|
CallQueryInterface(contextMenu, &childFrame);
|
|
|
|
nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(mPresContext);
|
|
|
|
if (popupSetFrame)
|
|
|
|
// Destroy the popup.
|
|
|
|
popupSetFrame->DestroyPopup(childFrame, PR_FALSE);
|
|
|
|
aHandledFlag = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-07-23 08:36:39 +00:00
|
|
|
if (!mCurrentMenu)
|
2000-02-09 09:34:35 +00:00
|
|
|
return NS_OK;
|
1999-07-23 07:49:43 +00:00
|
|
|
|
1999-07-23 08:36:39 +00:00
|
|
|
// See if our menu is open.
|
1999-09-21 01:03:00 +00:00
|
|
|
PRBool isOpen = PR_FALSE;
|
|
|
|
mCurrentMenu->MenuIsOpen(isOpen);
|
|
|
|
if (isOpen) {
|
1999-07-23 08:36:39 +00:00
|
|
|
// Let the child menu handle this.
|
1999-09-21 01:03:00 +00:00
|
|
|
mCurrentMenu->Escape(aHandledFlag);
|
1999-07-23 08:36:39 +00:00
|
|
|
if (!aHandledFlag) {
|
|
|
|
// We should close up.
|
1999-09-21 01:03:00 +00:00
|
|
|
mCurrentMenu->OpenMenu(PR_FALSE);
|
1999-07-23 08:36:39 +00:00
|
|
|
aHandledFlag = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
2000-02-09 09:34:35 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
1999-07-23 07:49:43 +00:00
|
|
|
}
|
|
|
|
|
2000-02-09 09:34:35 +00:00
|
|
|
NS_IMETHODIMP
|
1999-07-24 22:02:23 +00:00
|
|
|
nsMenuPopupFrame::Enter()
|
|
|
|
{
|
2003-03-05 01:43:25 +00:00
|
|
|
mIncrementalString.Truncate();
|
2002-04-24 07:15:35 +00:00
|
|
|
|
2002-10-04 00:56:43 +00:00
|
|
|
// See if we have a context menu open.
|
|
|
|
nsCOMPtr<nsIMenuParent> contextMenu;
|
|
|
|
GetContextMenu(getter_AddRefs(contextMenu));
|
|
|
|
if (contextMenu)
|
|
|
|
return contextMenu->Enter();
|
|
|
|
|
1999-07-24 22:02:23 +00:00
|
|
|
// Give it to the child.
|
1999-09-21 01:03:00 +00:00
|
|
|
if (mCurrentMenu)
|
|
|
|
mCurrentMenu->Enter();
|
2000-02-09 09:34:35 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
1999-07-24 22:02:23 +00:00
|
|
|
}
|
|
|
|
|
2002-10-04 00:56:43 +00:00
|
|
|
void
|
|
|
|
nsMenuPopupFrame::GetContextMenu(nsIMenuParent** aContextMenu)
|
|
|
|
{
|
|
|
|
*aContextMenu = nsnull;
|
|
|
|
if (mIsContextMenu || !nsMenuFrame::sDismissalListener)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIMenuParent> menuParent;
|
|
|
|
nsMenuFrame::sDismissalListener->GetCurrentMenuParent(getter_AddRefs(menuParent));
|
|
|
|
if (!menuParent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PRBool isContextMenu;
|
|
|
|
menuParent->GetIsContextMenu(isContextMenu);
|
|
|
|
if (isContextMenu) {
|
|
|
|
*aContextMenu = menuParent;
|
|
|
|
NS_ADDREF(*aContextMenu);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
nsIMenuFrame*
|
2002-04-24 07:15:35 +00:00
|
|
|
nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction)
|
1999-07-23 07:49:43 +00:00
|
|
|
{
|
2002-04-24 07:15:35 +00:00
|
|
|
PRUint32 charCode, keyCode;
|
|
|
|
aKeyEvent->GetCharCode(&charCode);
|
|
|
|
aKeyEvent->GetKeyCode(&keyCode);
|
|
|
|
|
|
|
|
doAction = PR_FALSE;
|
2000-04-04 06:43:02 +00:00
|
|
|
|
1999-07-23 07:49:43 +00:00
|
|
|
// Enumerate over our list of frames.
|
2000-04-04 06:43:02 +00:00
|
|
|
nsIFrame* immediateParent = nsnull;
|
2003-12-21 05:36:36 +00:00
|
|
|
GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
|
2000-04-04 06:43:02 +00:00
|
|
|
if (!immediateParent)
|
|
|
|
immediateParent = this;
|
|
|
|
|
2002-04-24 07:15:35 +00:00
|
|
|
PRUint32 matchCount = 0, matchShortcutCount = 0;
|
|
|
|
PRBool foundActive = PR_FALSE;
|
|
|
|
PRBool isShortcut;
|
|
|
|
nsIMenuFrame* frameBefore = nsnull;
|
|
|
|
nsIMenuFrame* frameAfter = nsnull;
|
|
|
|
nsIMenuFrame* frameShortcut = nsnull;
|
|
|
|
|
2003-07-28 21:25:13 +00:00
|
|
|
nsIContent* parentContent = mContent->GetParent();
|
2003-11-19 01:20:56 +00:00
|
|
|
|
|
|
|
PRBool isMenu =
|
|
|
|
parentContent && parentContent->Tag() != nsXULAtoms::menulist;
|
2002-04-24 07:15:35 +00:00
|
|
|
|
|
|
|
static DOMTimeStamp lastKeyTime = 0;
|
|
|
|
DOMTimeStamp keyTime;
|
|
|
|
aKeyEvent->GetTimeStamp(&keyTime);
|
|
|
|
|
|
|
|
if (charCode == 0) {
|
|
|
|
if (keyCode == NS_VK_BACK) {
|
|
|
|
if (!isMenu && !mIncrementalString.IsEmpty()) {
|
|
|
|
mIncrementalString.SetLength(mIncrementalString.Length() - 1);
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
#ifdef XP_WIN
|
|
|
|
nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
|
|
|
|
if (soundInterface)
|
|
|
|
soundInterface->Beep();
|
|
|
|
#endif // #ifdef XP_WIN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
else {
|
2003-03-05 01:43:25 +00:00
|
|
|
PRUnichar uniChar = ToLowerCase(NS_STATIC_CAST(PRUnichar, charCode));
|
2002-04-24 07:15:35 +00:00
|
|
|
if (isMenu || // Menu supports only first-letter navigation
|
|
|
|
keyTime - lastKeyTime > INC_TYP_INTERVAL) // Interval too long, treat as new typing
|
2003-03-05 01:43:25 +00:00
|
|
|
mIncrementalString = uniChar;
|
2002-04-24 07:15:35 +00:00
|
|
|
else {
|
2003-03-05 01:43:25 +00:00
|
|
|
mIncrementalString.Append(uniChar);
|
2002-04-24 07:15:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-05 01:43:25 +00:00
|
|
|
// See bug 188199 & 192346, if all letters in incremental string are same, just try to match the first one
|
|
|
|
nsAutoString incrementalString(mIncrementalString);
|
|
|
|
PRUint32 charIndex = 1, stringLength = incrementalString.Length();
|
|
|
|
while (charIndex < stringLength && incrementalString[charIndex] == incrementalString[charIndex - 1]) {
|
|
|
|
charIndex++;
|
|
|
|
}
|
|
|
|
if (charIndex == stringLength) {
|
|
|
|
incrementalString.Truncate(1);
|
|
|
|
stringLength = 1;
|
|
|
|
}
|
|
|
|
|
2002-04-24 07:15:35 +00:00
|
|
|
lastKeyTime = keyTime;
|
|
|
|
|
2000-04-04 06:43:02 +00:00
|
|
|
nsIFrame* currFrame;
|
2002-03-19 03:23:31 +00:00
|
|
|
// NOTE: If you crashed here due to a bogus |immediateParent| it is
|
|
|
|
// possible that the menu whose shortcut is being looked up has
|
|
|
|
// been destroyed already. One strategy would be to
|
|
|
|
// setTimeout(<func>,0) as detailed in:
|
|
|
|
// <http://bugzilla.mozilla.org/show_bug.cgi?id=126675#c32>
|
2004-01-09 14:20:53 +00:00
|
|
|
currFrame = immediateParent->GetFirstChild(nsnull);
|
2000-04-04 06:43:02 +00:00
|
|
|
|
2002-04-24 07:15:35 +00:00
|
|
|
// We start searching from first child. This process is divided into two parts
|
|
|
|
// -- before current and after current -- by the current item
|
1999-07-23 07:49:43 +00:00
|
|
|
while (currFrame) {
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIContent* current = currFrame->GetContent();
|
1999-07-23 07:49:43 +00:00
|
|
|
|
|
|
|
// See if it's a menu item.
|
1999-07-26 01:35:39 +00:00
|
|
|
if (IsValidItem(current)) {
|
2002-04-24 07:15:35 +00:00
|
|
|
nsAutoString activeKey, textKey;
|
1999-07-23 07:49:43 +00:00
|
|
|
// Get the shortcut attribute.
|
2002-04-24 07:15:35 +00:00
|
|
|
current->GetAttr(kNameSpaceID_None, nsXULAtoms::accesskey, textKey);
|
|
|
|
if (textKey.IsEmpty()) { // No shortcut, try first letter
|
|
|
|
isShortcut = PR_FALSE;
|
|
|
|
current->GetAttr(kNameSpaceID_None, nsXULAtoms::label, textKey);
|
|
|
|
if (textKey.IsEmpty()) // No label, try another attribute (value)
|
|
|
|
current->GetAttr(kNameSpaceID_None, nsXULAtoms::value, textKey);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
isShortcut = PR_TRUE;
|
|
|
|
|
2003-07-14 07:37:39 +00:00
|
|
|
if (StringBeginsWith(textKey, incrementalString,
|
|
|
|
nsCaseInsensitiveStringComparator())) {
|
2002-04-24 07:15:35 +00:00
|
|
|
// mIncrementalString is a prefix of textKey
|
|
|
|
nsCOMPtr<nsIMenuFrame> menuFrame = do_QueryInterface(currFrame);
|
|
|
|
if (menuFrame) {
|
|
|
|
// There is one match
|
|
|
|
matchCount++;
|
|
|
|
if (isShortcut) {
|
|
|
|
// There is one shortcut-key match
|
|
|
|
matchShortcutCount++;
|
|
|
|
// Record the matched item. If there is only one matched shortcut item, do it
|
|
|
|
frameShortcut = menuFrame.get();
|
|
|
|
}
|
|
|
|
if (!foundActive) {
|
|
|
|
// It's a first candidate item located before/on the current item
|
|
|
|
if (!frameBefore)
|
|
|
|
frameBefore = menuFrame.get();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// It's a first candidate item located after the current item
|
|
|
|
if (!frameAfter)
|
|
|
|
frameAfter = menuFrame.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
1999-09-21 01:03:00 +00:00
|
|
|
return nsnull;
|
2002-04-24 07:15:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the active status
|
|
|
|
current->GetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, activeKey);
|
2004-04-30 13:23:43 +00:00
|
|
|
if (activeKey.EqualsLiteral("true")) {
|
2002-04-24 07:15:35 +00:00
|
|
|
foundActive = PR_TRUE;
|
2003-03-05 01:43:25 +00:00
|
|
|
if (stringLength > 1) {
|
2002-04-24 07:15:35 +00:00
|
|
|
// If there is more than one char typed, the current item has highest priority,
|
|
|
|
// otherwise the item next to current has highest priority
|
|
|
|
nsCOMPtr<nsIMenuFrame> menuFrame = do_QueryInterface(currFrame);
|
|
|
|
if (menuFrame && menuFrame.get() == frameBefore) {
|
|
|
|
return frameBefore;
|
|
|
|
}
|
1999-07-23 07:49:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-08-04 12:39:51 +00:00
|
|
|
currFrame = currFrame->GetNextSibling();
|
1999-07-23 07:49:43 +00:00
|
|
|
}
|
2002-02-15 03:53:26 +00:00
|
|
|
|
2002-04-24 07:15:35 +00:00
|
|
|
doAction = (isMenu && (matchCount == 1 || matchShortcutCount == 1));
|
|
|
|
|
|
|
|
if (matchShortcutCount == 1) // We have one matched shortcut item
|
|
|
|
return frameShortcut;
|
|
|
|
if (frameAfter) // If we have matched item after the current, use it
|
|
|
|
return frameAfter;
|
|
|
|
else if (frameBefore) // If we haven't, use the item before the current
|
|
|
|
return frameBefore;
|
|
|
|
|
|
|
|
// If we don't match anything, rollback the last typing
|
|
|
|
mIncrementalString.SetLength(mIncrementalString.Length() - 1);
|
|
|
|
|
2002-02-15 03:53:26 +00:00
|
|
|
// didn't find a matching menu item
|
|
|
|
#ifdef XP_WIN
|
2002-04-24 07:15:35 +00:00
|
|
|
// behavior on Windows - this item is in a menu popup off of the
|
|
|
|
// menu bar, so beep and do nothing else
|
|
|
|
if (isMenu) {
|
|
|
|
nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
|
|
|
|
if (soundInterface)
|
|
|
|
soundInterface->Beep();
|
|
|
|
}
|
2002-02-15 03:53:26 +00:00
|
|
|
#endif // #ifdef XP_WIN
|
|
|
|
|
1999-07-23 07:49:43 +00:00
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
2000-02-09 09:34:35 +00:00
|
|
|
NS_IMETHODIMP
|
2002-04-24 07:15:35 +00:00
|
|
|
nsMenuPopupFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag)
|
1999-07-23 05:47:43 +00:00
|
|
|
{
|
1999-07-23 07:56:27 +00:00
|
|
|
if (mCurrentMenu) {
|
1999-09-21 01:03:00 +00:00
|
|
|
PRBool isOpen = PR_FALSE;
|
|
|
|
mCurrentMenu->MenuIsOpen(isOpen);
|
|
|
|
if (isOpen) {
|
1999-07-23 07:56:27 +00:00
|
|
|
// No way this applies to us. Give it to our child.
|
2002-04-24 07:15:35 +00:00
|
|
|
mCurrentMenu->ShortcutNavigation(aKeyEvent, aHandledFlag);
|
2000-02-09 09:34:35 +00:00
|
|
|
return NS_OK;
|
1999-07-23 07:56:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This applies to us. Let's see if one of the shortcuts applies
|
2002-04-24 07:15:35 +00:00
|
|
|
PRBool action;
|
|
|
|
nsIMenuFrame* result = FindMenuWithShortcut(aKeyEvent, action);
|
1999-07-23 07:56:27 +00:00
|
|
|
if (result) {
|
|
|
|
// We got one!
|
|
|
|
aHandledFlag = PR_TRUE;
|
|
|
|
SetCurrentMenuItem(result);
|
2002-04-24 07:15:35 +00:00
|
|
|
if (action)
|
|
|
|
result->Enter();
|
1999-07-23 07:56:27 +00:00
|
|
|
}
|
2000-02-09 09:34:35 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
1999-07-23 05:47:43 +00:00
|
|
|
}
|
|
|
|
|
2000-02-09 09:34:35 +00:00
|
|
|
NS_IMETHODIMP
|
2002-08-06 23:47:01 +00:00
|
|
|
nsMenuPopupFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag)
|
1999-07-22 09:01:55 +00:00
|
|
|
{
|
2002-10-04 00:56:43 +00:00
|
|
|
// See if we have a context menu open.
|
|
|
|
nsCOMPtr<nsIMenuParent> contextMenu;
|
|
|
|
GetContextMenu(getter_AddRefs(contextMenu));
|
|
|
|
if (contextMenu)
|
|
|
|
return contextMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
|
|
|
|
|
2002-08-06 23:47:01 +00:00
|
|
|
nsNavigationDirection theDirection;
|
|
|
|
NS_DIRECTION_FROM_KEY_CODE(theDirection, aKeyCode);
|
|
|
|
|
2003-03-05 01:43:25 +00:00
|
|
|
mIncrementalString.Truncate();
|
2002-04-24 07:15:35 +00:00
|
|
|
|
1999-07-22 09:01:55 +00:00
|
|
|
// This method only gets called if we're open.
|
2002-08-06 23:47:01 +00:00
|
|
|
if (!mCurrentMenu && NS_DIRECTION_IS_INLINE(theDirection)) {
|
1999-07-22 09:01:55 +00:00
|
|
|
// We've been opened, but we haven't had anything selected.
|
2002-08-06 23:47:01 +00:00
|
|
|
// We can handle End, but our parent handles Start.
|
|
|
|
if (theDirection == eNavigationDirection_End) {
|
1999-09-21 01:03:00 +00:00
|
|
|
nsIMenuFrame* nextItem;
|
1999-07-26 06:29:48 +00:00
|
|
|
GetNextMenuItem(nsnull, &nextItem);
|
|
|
|
if (nextItem) {
|
|
|
|
aHandledFlag = PR_TRUE;
|
|
|
|
SetCurrentMenuItem(nextItem);
|
|
|
|
}
|
|
|
|
}
|
2000-02-09 09:34:35 +00:00
|
|
|
return NS_OK;
|
1999-07-22 09:01:55 +00:00
|
|
|
}
|
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
PRBool isContainer = PR_FALSE;
|
|
|
|
PRBool isOpen = PR_FALSE;
|
2000-08-03 22:07:49 +00:00
|
|
|
PRBool isDisabled = PR_FALSE;
|
1999-09-21 01:03:00 +00:00
|
|
|
if (mCurrentMenu) {
|
|
|
|
mCurrentMenu->MenuIsContainer(isContainer);
|
|
|
|
mCurrentMenu->MenuIsOpen(isOpen);
|
2000-08-03 22:07:49 +00:00
|
|
|
mCurrentMenu->MenuIsDisabled(isDisabled);
|
1999-09-21 01:03:00 +00:00
|
|
|
|
|
|
|
if (isOpen) {
|
1999-07-25 00:16:11 +00:00
|
|
|
// Give our child a shot.
|
2002-08-06 23:47:01 +00:00
|
|
|
mCurrentMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
|
1999-07-25 00:16:11 +00:00
|
|
|
}
|
2002-08-06 23:47:01 +00:00
|
|
|
else if (theDirection == eNavigationDirection_End &&
|
|
|
|
isContainer && !isDisabled) {
|
1999-07-25 00:16:11 +00:00
|
|
|
// The menu is not yet open. Open it and select the first item.
|
|
|
|
aHandledFlag = PR_TRUE;
|
1999-09-21 01:03:00 +00:00
|
|
|
mCurrentMenu->OpenMenu(PR_TRUE);
|
|
|
|
mCurrentMenu->SelectFirstItem();
|
1999-07-25 00:16:11 +00:00
|
|
|
}
|
1999-07-22 09:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aHandledFlag)
|
2000-02-09 09:34:35 +00:00
|
|
|
return NS_OK; // The child menu took it for us.
|
1999-07-22 09:01:55 +00:00
|
|
|
|
2002-08-06 23:47:01 +00:00
|
|
|
// For block progression, we can move in either direction
|
|
|
|
if (NS_DIRECTION_IS_BLOCK(theDirection) ||
|
|
|
|
NS_DIRECTION_IS_BLOCK_TO_EDGE(theDirection)) {
|
2000-11-13 21:25:07 +00:00
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
nsIMenuFrame* nextItem;
|
1999-07-22 09:01:55 +00:00
|
|
|
|
2002-08-06 23:47:01 +00:00
|
|
|
if (theDirection == eNavigationDirection_Before)
|
2001-03-24 00:12:47 +00:00
|
|
|
GetPreviousMenuItem(mCurrentMenu, &nextItem);
|
2002-08-06 23:47:01 +00:00
|
|
|
else if (theDirection == eNavigationDirection_After)
|
1999-07-22 09:01:55 +00:00
|
|
|
GetNextMenuItem(mCurrentMenu, &nextItem);
|
2002-08-06 23:47:01 +00:00
|
|
|
else if (theDirection == eNavigationDirection_First)
|
2001-03-24 00:12:47 +00:00
|
|
|
GetNextMenuItem(nsnull, &nextItem);
|
|
|
|
else
|
2001-03-27 22:58:50 +00:00
|
|
|
GetPreviousMenuItem(nsnull, &nextItem);
|
1999-07-22 09:01:55 +00:00
|
|
|
|
|
|
|
SetCurrentMenuItem(nextItem);
|
|
|
|
|
|
|
|
aHandledFlag = PR_TRUE;
|
|
|
|
}
|
1999-09-21 01:03:00 +00:00
|
|
|
else if (mCurrentMenu && isContainer && isOpen) {
|
2002-08-06 23:47:01 +00:00
|
|
|
if (theDirection == eNavigationDirection_Start) {
|
1999-07-26 04:38:28 +00:00
|
|
|
// Close it up.
|
1999-09-21 01:03:00 +00:00
|
|
|
mCurrentMenu->OpenMenu(PR_FALSE);
|
1999-07-26 06:29:48 +00:00
|
|
|
aHandledFlag = PR_TRUE;
|
1999-07-26 04:38:28 +00:00
|
|
|
}
|
1999-07-25 01:14:43 +00:00
|
|
|
}
|
2000-02-09 09:34:35 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
1999-07-24 01:59:32 +00:00
|
|
|
}
|
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::GetParentPopup(nsIMenuParent** aMenuParent)
|
|
|
|
{
|
|
|
|
*aMenuParent = nsnull;
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIFrame* frame = GetParent();
|
1999-09-21 01:03:00 +00:00
|
|
|
if (frame) {
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIFrame* grandparent = frame->GetParent();
|
1999-09-21 01:03:00 +00:00
|
|
|
if (grandparent) {
|
|
|
|
nsCOMPtr<nsIMenuParent> menuParent = do_QueryInterface(grandparent);
|
|
|
|
if (menuParent) {
|
|
|
|
*aMenuParent = menuParent.get();
|
|
|
|
NS_ADDREF(*aMenuParent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-08-12 20:45:47 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::HideChain()
|
|
|
|
{
|
2001-09-10 07:34:54 +00:00
|
|
|
if (!mShouldRollup)
|
|
|
|
return NS_OK;
|
|
|
|
|
1999-10-05 23:31:06 +00:00
|
|
|
// Stop capturing rollups
|
|
|
|
// (must do this during Hide, which happens before the menu item is executed,
|
|
|
|
// since this reinstates normal event handling.)
|
2001-09-10 07:34:54 +00:00
|
|
|
if (nsMenuFrame::sDismissalListener)
|
|
|
|
nsMenuFrame::sDismissalListener->Unregister();
|
1999-10-05 23:31:06 +00:00
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIFrame* frame = GetParent();
|
1999-08-12 20:45:47 +00:00
|
|
|
if (frame) {
|
1999-09-21 01:03:00 +00:00
|
|
|
nsCOMPtr<nsIMenuFrame> menuFrame = do_QueryInterface(frame);
|
2001-08-18 01:04:47 +00:00
|
|
|
if (!menuFrame) {
|
|
|
|
nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(mPresContext);
|
|
|
|
if (popupSetFrame)
|
|
|
|
// Hide the popup.
|
|
|
|
popupSetFrame->HidePopup(this);
|
1999-09-21 01:03:00 +00:00
|
|
|
return NS_OK;
|
2001-08-18 01:04:47 +00:00
|
|
|
}
|
|
|
|
|
1999-08-12 20:45:47 +00:00
|
|
|
menuFrame->ActivateMenu(PR_FALSE);
|
|
|
|
menuFrame->SelectMenu(PR_FALSE);
|
|
|
|
|
|
|
|
// Get the parent.
|
|
|
|
nsCOMPtr<nsIMenuParent> menuParent;
|
|
|
|
menuFrame->GetMenuParent(getter_AddRefs(menuParent));
|
|
|
|
if (menuParent)
|
|
|
|
menuParent->HideChain();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-07-24 01:59:32 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::DismissChain()
|
|
|
|
{
|
2001-09-10 07:34:54 +00:00
|
|
|
if (!mShouldRollup)
|
|
|
|
return NS_OK;
|
|
|
|
|
1999-09-25 03:39:35 +00:00
|
|
|
// Stop capturing rollups
|
2001-09-10 07:34:54 +00:00
|
|
|
if (nsMenuFrame::sDismissalListener)
|
|
|
|
nsMenuFrame::sDismissalListener->Unregister();
|
1999-09-25 03:39:35 +00:00
|
|
|
|
1999-07-24 01:59:32 +00:00
|
|
|
// Get our menu parent.
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIFrame* frame = GetParent();
|
1999-07-24 01:59:32 +00:00
|
|
|
if (frame) {
|
2002-08-06 12:48:28 +00:00
|
|
|
nsIMenuFrame *menuFrame = nsnull;
|
|
|
|
CallQueryInterface(frame, &menuFrame);
|
2001-08-18 01:04:47 +00:00
|
|
|
if (!menuFrame) {
|
|
|
|
nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(mPresContext);
|
2001-12-12 05:11:16 +00:00
|
|
|
if (popupSetFrame) {
|
|
|
|
// make sure the menu is not highlighted
|
2003-11-22 23:35:52 +00:00
|
|
|
if (mCurrentMenu) {
|
|
|
|
PRBool wasOpen;
|
|
|
|
mCurrentMenu->MenuIsOpen(wasOpen);
|
|
|
|
if (wasOpen)
|
|
|
|
mCurrentMenu->OpenMenu(PR_FALSE);
|
2001-12-12 05:11:16 +00:00
|
|
|
mCurrentMenu->SelectMenu(PR_FALSE);
|
2003-11-22 23:35:52 +00:00
|
|
|
}
|
2001-08-18 01:04:47 +00:00
|
|
|
// Destroy the popup.
|
2002-10-04 00:56:43 +00:00
|
|
|
popupSetFrame->DestroyPopup(this, PR_TRUE);
|
2001-12-12 05:11:16 +00:00
|
|
|
}
|
1999-09-21 01:03:00 +00:00
|
|
|
return NS_OK;
|
2001-08-18 01:04:47 +00:00
|
|
|
}
|
|
|
|
|
1999-07-24 01:59:32 +00:00
|
|
|
menuFrame->OpenMenu(PR_FALSE);
|
|
|
|
|
|
|
|
// Get the parent.
|
2002-08-06 12:48:28 +00:00
|
|
|
nsIMenuParent* menuParent;
|
|
|
|
menuFrame->GetMenuParent(&menuParent);
|
1999-07-24 01:59:32 +00:00
|
|
|
if (menuParent)
|
|
|
|
menuParent->DismissChain();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-07-26 01:35:39 +00:00
|
|
|
|
1999-09-25 03:39:35 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::GetWidget(nsIWidget **aWidget)
|
|
|
|
{
|
|
|
|
// Get parent view
|
|
|
|
nsIView * view = nsnull;
|
2003-01-08 22:40:14 +00:00
|
|
|
// XXX should this be passing PR_FALSE or PR_TRUE for aStopAtViewManagerRoot?
|
|
|
|
nsMenuPopupFrame::GetRootViewForPopup(mPresContext, this, PR_FALSE, &view);
|
1999-09-25 03:39:35 +00:00
|
|
|
if (!view)
|
|
|
|
return NS_OK;
|
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
*aWidget = view->GetWidget();
|
|
|
|
NS_IF_ADDREF(*aWidget);
|
1999-09-25 03:39:35 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-09-21 01:03:00 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::CreateDismissalListener()
|
|
|
|
{
|
1999-10-04 02:27:09 +00:00
|
|
|
nsMenuDismissalListener *listener = new nsMenuDismissalListener();
|
|
|
|
if (!listener) return NS_ERROR_OUT_OF_MEMORY;
|
2001-09-10 07:34:54 +00:00
|
|
|
nsMenuFrame::sDismissalListener = listener;
|
1999-10-04 02:27:09 +00:00
|
|
|
NS_ADDREF(listener);
|
1999-09-21 01:03:00 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-02-09 09:34:35 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::InstallKeyboardNavigator()
|
|
|
|
{
|
2000-02-18 10:22:20 +00:00
|
|
|
if (mKeyboardNavigator)
|
|
|
|
return NS_OK;
|
|
|
|
|
2003-07-28 21:25:13 +00:00
|
|
|
nsCOMPtr<nsIDOMEventReceiver> target = do_QueryInterface(mContent->GetDocument());
|
2000-02-09 09:34:35 +00:00
|
|
|
|
|
|
|
mTarget = target;
|
|
|
|
mKeyboardNavigator = new nsMenuListener(this);
|
|
|
|
NS_IF_ADDREF(mKeyboardNavigator);
|
|
|
|
|
2000-08-30 02:40:19 +00:00
|
|
|
target->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
|
|
|
|
target->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
|
|
|
|
target->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
|
2000-02-09 09:34:35 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::RemoveKeyboardNavigator()
|
|
|
|
{
|
2000-02-18 10:22:20 +00:00
|
|
|
if (!mKeyboardNavigator)
|
|
|
|
return NS_OK;
|
|
|
|
|
2000-08-30 02:40:19 +00:00
|
|
|
mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
|
|
|
|
mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
|
|
|
|
mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
|
2000-02-09 09:34:35 +00:00
|
|
|
|
|
|
|
NS_IF_RELEASE(mKeyboardNavigator);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// helpers /////////////////////////////////////////////////////////////
|
|
|
|
|
1999-07-26 01:35:39 +00:00
|
|
|
PRBool
|
|
|
|
nsMenuPopupFrame::IsValidItem(nsIContent* aContent)
|
|
|
|
{
|
2003-11-19 01:20:56 +00:00
|
|
|
nsIAtom *tag = aContent->Tag();
|
1999-07-26 01:35:39 +00:00
|
|
|
|
2003-11-19 01:20:56 +00:00
|
|
|
return (tag == nsXULAtoms::menu ||
|
|
|
|
tag == nsXULAtoms::menuitem ||
|
|
|
|
tag == nsHTMLAtoms::option);
|
1999-07-26 01:35:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsMenuPopupFrame::IsDisabled(nsIContent* aContent)
|
|
|
|
{
|
2000-04-16 11:19:26 +00:00
|
|
|
nsString disabled;
|
2001-08-17 08:14:14 +00:00
|
|
|
aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, disabled);
|
2004-04-30 13:23:43 +00:00
|
|
|
if (disabled.EqualsLiteral("true"))
|
1999-07-26 01:35:39 +00:00
|
|
|
return PR_TRUE;
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
1999-09-10 08:47:12 +00:00
|
|
|
|
2001-09-10 07:34:54 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::AttributeChanged(nsIPresContext* aPresContext,
|
|
|
|
nsIContent* aChild,
|
|
|
|
PRInt32 aNameSpaceID,
|
|
|
|
nsIAtom* aAttribute,
|
2003-07-11 21:16:12 +00:00
|
|
|
PRInt32 aModType)
|
2001-09-10 07:34:54 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
nsresult rv = nsBoxFrame::AttributeChanged(aPresContext, aChild,
|
2003-07-11 21:16:12 +00:00
|
|
|
aNameSpaceID, aAttribute,
|
|
|
|
aModType);
|
2001-09-10 07:34:54 +00:00
|
|
|
|
|
|
|
if (aAttribute == nsXULAtoms::left || aAttribute == nsXULAtoms::top)
|
|
|
|
MoveToAttributePosition();
|
|
|
|
|
2002-11-19 05:06:36 +00:00
|
|
|
return rv;
|
2001-09-10 07:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsMenuPopupFrame::MoveToAttributePosition()
|
|
|
|
{
|
|
|
|
// Move the widget around when the user sets the |left| and |top| attributes.
|
|
|
|
// Note that this is not the best way to move the widget, as it results in lots
|
|
|
|
// of FE notifications and is likely to be slow as molasses. Use |moveTo| on
|
|
|
|
// nsIPopupBoxObject if possible.
|
|
|
|
nsAutoString left, top;
|
|
|
|
mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::left, left);
|
|
|
|
mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::top, top);
|
|
|
|
PRInt32 err1, err2, xPos, yPos;
|
|
|
|
xPos = left.ToInteger(&err1);
|
|
|
|
yPos = top.ToInteger(&err2);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2))
|
|
|
|
MoveTo(xPos, yPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-09-10 08:47:12 +00:00
|
|
|
NS_IMETHODIMP
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMenuPopupFrame::HandleEvent(nsIPresContext* aPresContext,
|
1999-09-10 08:47:12 +00:00
|
|
|
nsGUIEvent* aEvent,
|
1999-11-24 06:03:41 +00:00
|
|
|
nsEventStatus* aEventStatus)
|
1999-09-10 08:47:12 +00:00
|
|
|
{
|
1999-09-21 01:03:00 +00:00
|
|
|
return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
|
1999-09-10 08:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMenuPopupFrame::Destroy(nsIPresContext* aPresContext)
|
1999-09-10 08:47:12 +00:00
|
|
|
{
|
1999-09-21 01:03:00 +00:00
|
|
|
return nsBoxFrame::Destroy(aPresContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-10-26 04:44:41 +00:00
|
|
|
nsMenuPopupFrame::GetFrameForPoint(nsIPresContext* aPresContext,
|
|
|
|
const nsPoint& aPoint,
|
2000-03-31 07:02:06 +00:00
|
|
|
nsFramePaintLayer aWhichLayer,
|
1999-10-26 04:44:41 +00:00
|
|
|
nsIFrame** aFrame)
|
1999-09-21 01:03:00 +00:00
|
|
|
{
|
2000-03-22 02:43:08 +00:00
|
|
|
return nsBoxFrame::GetFrameForPoint(aPresContext, aPoint, aWhichLayer, aFrame);
|
1999-09-10 08:47:12 +00:00
|
|
|
}
|
2000-02-13 08:33:39 +00:00
|
|
|
|
2000-08-11 00:37:59 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Notify
|
|
|
|
//
|
|
|
|
// The item selection timer has fired, we might have to readjust the
|
|
|
|
// selected item. There are two cases here that we are trying to deal with:
|
|
|
|
// (1) diagonal movement from a parent menu to a submenu passing briefly over
|
|
|
|
// other items, and
|
|
|
|
// (2) moving out from a submenu to a parent or grandparent menu.
|
|
|
|
// In both cases, |mTimerMenu| is the menu item that might have an open submenu and
|
|
|
|
// |mCurrentMenu| is the item the mouse is currently over, which could be none of them.
|
|
|
|
//
|
|
|
|
// case (1):
|
|
|
|
// As the mouse moves from the parent item of a submenu (we'll call 'A') diagonally into the
|
|
|
|
// submenu, it probably passes through one or more sibilings (B). As the mouse passes
|
|
|
|
// through B, it becomes the current menu item and the timer is set and mTimerMenu is
|
|
|
|
// set to A. Before the timer fires, the mouse leaves the menu containing A and B and
|
|
|
|
// enters the submenus. Now when the timer fires, |mCurrentMenu| is null (!= |mTimerMenu|)
|
|
|
|
// so we have to see if anything in A's children is selected (recall that even disabled
|
|
|
|
// items are selected, the style just doesn't show it). If that is the case, we need to
|
|
|
|
// set the selected item back to A.
|
|
|
|
//
|
|
|
|
// case (2);
|
|
|
|
// Item A has an open submenu, and in it there is an item (B) which also has an open
|
|
|
|
// submenu (so there are 3 menus displayed right now). The mouse then leaves B's child
|
|
|
|
// submenu and selects an item that is a sibling of A, call it C. When the mouse enters C,
|
|
|
|
// the timer is set and |mTimerMenu| is A and |mCurrentMenu| is C. As the timer fires,
|
|
|
|
// the mouse is still within C. The correct behavior is to set the current item to C
|
|
|
|
// and close up the chain parented at A.
|
|
|
|
//
|
|
|
|
// This brings up the question of is the logic of case (1) enough? The answer is no,
|
|
|
|
// and is discussed in bugzilla bug 29400. Case (1) asks if A's submenu has a selected
|
|
|
|
// child, and if it does, set the selected item to A. Because B has a submenu open, it
|
|
|
|
// is selected and as a result, A is set to be the selected item even though the mouse
|
|
|
|
// rests in C -- very wrong.
|
|
|
|
//
|
|
|
|
// The solution is to use the same idea, but instead of only checking one level,
|
|
|
|
// drill all the way down to the deepest open submenu and check if it has something
|
|
|
|
// selected. Since the mouse is in a grandparent, it won't, and we know that we can
|
|
|
|
// safely close up A and all its children.
|
|
|
|
//
|
|
|
|
// The code below melds the two cases together.
|
|
|
|
//
|
2002-09-07 05:38:16 +00:00
|
|
|
NS_IMETHODIMP
|
2000-02-13 08:33:39 +00:00
|
|
|
nsMenuPopupFrame::Notify(nsITimer* aTimer)
|
|
|
|
{
|
2000-08-11 00:37:59 +00:00
|
|
|
// Our timer has fired.
|
2000-02-13 08:33:39 +00:00
|
|
|
if (aTimer == mCloseTimer.get()) {
|
|
|
|
PRBool menuOpen = PR_FALSE;
|
|
|
|
mTimerMenu->MenuIsOpen(menuOpen);
|
|
|
|
if (menuOpen) {
|
|
|
|
if (mCurrentMenu != mTimerMenu) {
|
2000-08-11 00:37:59 +00:00
|
|
|
// Walk through all of the sub-menus of this menu item until we get to the
|
|
|
|
// last sub-menu, then check if that sub-menu has an active menu item. If it
|
|
|
|
// does, then keep that menu open. If it doesn't, close menu and its sub-menus.
|
2000-02-13 08:33:39 +00:00
|
|
|
nsIFrame* child;
|
|
|
|
mTimerMenu->GetMenuChild(&child);
|
2000-08-11 00:37:59 +00:00
|
|
|
nsCOMPtr<nsIMenuFrame> currentMenuItem = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIMenuParent> menuParent = do_QueryInterface(child);
|
|
|
|
while (menuParent)
|
|
|
|
{
|
|
|
|
// get the selected menu item for this sub-menu
|
|
|
|
menuParent->GetCurrentMenuItem(getter_AddRefs(currentMenuItem));
|
|
|
|
menuParent = nsnull;
|
|
|
|
if (currentMenuItem)
|
|
|
|
{
|
|
|
|
// this sub-menu has a selected menu item - does that item open a sub-menu?
|
|
|
|
currentMenuItem->GetMenuChild(&child);
|
|
|
|
if (child) {
|
|
|
|
// the selected menu item opens a sub-menu - move down
|
|
|
|
// to that sub-menu and then go through the loop again
|
|
|
|
menuParent = do_QueryInterface(child);
|
|
|
|
}
|
|
|
|
} // if item is selected
|
|
|
|
} // while we're not at the last submenu
|
|
|
|
|
|
|
|
if (currentMenuItem)
|
|
|
|
{
|
|
|
|
// the sub-menu has a selected menu item, we're dealing with case (1)
|
|
|
|
SetCurrentMenuItem(mTimerMenu);
|
2000-02-13 08:33:39 +00:00
|
|
|
}
|
|
|
|
else {
|
2000-08-11 00:37:59 +00:00
|
|
|
// Nothing selected. Either the mouse never made it to the submenu
|
|
|
|
// in case (1) or we're in a sibling of a grandparent in case (2).
|
|
|
|
// Regardless, close up the open chain.
|
2000-02-13 08:33:39 +00:00
|
|
|
mTimerMenu->OpenMenu(PR_FALSE);
|
|
|
|
}
|
2000-08-11 00:37:59 +00:00
|
|
|
} // if not the menu with an open submenu
|
|
|
|
} // if menu open
|
|
|
|
|
2003-05-15 00:06:23 +00:00
|
|
|
if (mCloseTimer) {
|
|
|
|
mCloseTimer->Cancel();
|
|
|
|
}
|
2000-02-13 08:33:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mCloseTimer = nsnull;
|
|
|
|
mTimerMenu = nsnull;
|
2002-09-07 05:38:16 +00:00
|
|
|
return NS_OK;
|
2000-02-13 08:33:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsMenuPopupFrame::KillCloseTimer()
|
|
|
|
{
|
|
|
|
if (mCloseTimer && mTimerMenu) {
|
|
|
|
PRBool menuOpen = PR_FALSE;
|
|
|
|
mTimerMenu->MenuIsOpen(menuOpen);
|
|
|
|
if (menuOpen) {
|
|
|
|
mTimerMenu->OpenMenu(PR_FALSE);
|
|
|
|
}
|
|
|
|
mCloseTimer->Cancel();
|
|
|
|
mCloseTimer = nsnull;
|
|
|
|
mTimerMenu = nsnull;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-08-16 00:35:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2001-09-10 07:34:54 +00:00
|
|
|
nsMenuPopupFrame::KillPendingTimers ( )
|
2000-08-16 00:35:04 +00:00
|
|
|
{
|
|
|
|
return KillCloseTimer();
|
|
|
|
|
|
|
|
} // KillPendingTimers
|
2001-06-04 23:03:24 +00:00
|
|
|
|
2001-09-10 07:34:54 +00:00
|
|
|
void
|
|
|
|
nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop)
|
|
|
|
{
|
|
|
|
// Set the 'left' and 'top' attributes
|
|
|
|
nsAutoString left, top;
|
|
|
|
left.AppendInt(aLeft);
|
|
|
|
top.AppendInt(aTop);
|
|
|
|
|
|
|
|
mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::left, left, PR_FALSE);
|
|
|
|
mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::top, top, PR_FALSE);
|
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIView* view = GetView();
|
2001-09-10 07:34:54 +00:00
|
|
|
|
|
|
|
// Retrieve screen position of parent view
|
|
|
|
nsPoint screenPos;
|
2003-08-04 12:39:51 +00:00
|
|
|
GetScreenPosition(view->GetParent(), screenPos);
|
2001-09-10 07:34:54 +00:00
|
|
|
|
|
|
|
// Move the widget
|
2003-08-04 12:39:51 +00:00
|
|
|
view->GetWidget()->Move(aLeft - screenPos.x, aTop - screenPos.y);
|
2001-09-10 07:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsMenuPopupFrame::GetScreenPosition(nsIView* aView, nsPoint& aScreenPosition)
|
|
|
|
{
|
|
|
|
nsPoint screenPos(0,0);
|
|
|
|
|
|
|
|
nsIView* currView = aView;
|
|
|
|
nsIView* nextView = nsnull;
|
|
|
|
|
|
|
|
while (1) {
|
2003-08-04 12:39:51 +00:00
|
|
|
screenPos += currView->GetPosition();
|
2001-09-10 07:34:54 +00:00
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nextView = currView->GetParent();
|
2001-09-10 07:34:54 +00:00
|
|
|
if (!nextView)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
currView = nextView;
|
|
|
|
}
|
|
|
|
|
2003-08-04 12:39:51 +00:00
|
|
|
nsIWidget* rootWidget = currView->GetWidget();
|
2001-09-10 07:34:54 +00:00
|
|
|
nsRect bounds, screenBounds;
|
|
|
|
rootWidget->GetScreenBounds(bounds);
|
|
|
|
rootWidget->WidgetToScreen(bounds, screenBounds);
|
|
|
|
|
|
|
|
float t2p;
|
2004-02-11 04:57:07 +00:00
|
|
|
t2p = mPresContext->TwipsToPixels();
|
2001-09-10 07:34:54 +00:00
|
|
|
|
|
|
|
aScreenPosition.x = NSTwipsToIntPixels(screenPos.x, t2p) + screenBounds.x;
|
|
|
|
aScreenPosition.y = NSTwipsToIntPixels(screenPos.y, t2p) + screenBounds.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsMenuPopupFrame::GetAutoPosition(PRBool* aShouldAutoPosition)
|
|
|
|
{
|
|
|
|
*aShouldAutoPosition = mShouldAutoPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsMenuPopupFrame::SetAutoPosition(PRBool aShouldAutoPosition)
|
|
|
|
{
|
|
|
|
mShouldAutoPosition = aShouldAutoPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsMenuPopupFrame::EnableRollup(PRBool aShouldRollup)
|
|
|
|
{
|
|
|
|
if (!aShouldRollup) {
|
|
|
|
if (nsMenuFrame::sDismissalListener)
|
|
|
|
nsMenuFrame::sDismissalListener->Unregister();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
CreateDismissalListener();
|
|
|
|
}
|
|
|
|
|