From 7d640e36adfebd819ee7e8dc98adc9ec1a5730a8 Mon Sep 17 00:00:00 2001 From: "hewitt%netscape.com" Date: Tue, 4 Dec 2001 23:45:22 +0000 Subject: [PATCH] 93839 - tooltiptext should work without specifying tooltip, r=pinkerton, sr=hyatt --- layout/xul/base/src/nsXULTooltipListener.cpp | 681 +++++++++++++++++++ layout/xul/base/src/nsXULTooltipListener.h | 147 ++++ 2 files changed, 828 insertions(+) create mode 100644 layout/xul/base/src/nsXULTooltipListener.cpp create mode 100644 layout/xul/base/src/nsXULTooltipListener.h diff --git a/layout/xul/base/src/nsXULTooltipListener.cpp b/layout/xul/base/src/nsXULTooltipListener.cpp new file mode 100644 index 000000000000..b1267a4e6abb --- /dev/null +++ b/layout/xul/base/src/nsXULTooltipListener.cpp @@ -0,0 +1,681 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsXULTooltipListener.h" + +#include "nsIDOMMouseEvent.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOMXULDocument.h" +#include "nsIDOMXULElement.h" +#include "nsIDocument.h" +#include "nsXULAtoms.h" +#include "nsIPresShell.h" +#include "nsIFrame.h" +#include "nsIPopupBoxObject.h" +#include "nsIPref.h" +#include "nsIServiceManager.h" +#include "nsIOutlinerView.h" +#include "nsGUIEvent.h" +#include "nsIPrivateDOMEvent.h" +#include "nsIPresContext.h" + +////////////////////////////////////////////////////////////////////////// +//// nsISupports + +nsXULTooltipListener::nsXULTooltipListener() + : mSourceNode(nsnull), mTargetNode(nsnull), + mCurrentTooltip(nsnull), + mMouseClientX(0), mMouseClientY(0), + mIsTargetOutliner(PR_FALSE), mNeedTitletip(PR_FALSE), + mOutlinerBox(nsnull), mLastOutlinerRow(-1) +{ + NS_INIT_REFCNT(); +} + +nsXULTooltipListener::~nsXULTooltipListener() +{ + HideTooltip(); + + // unregister the prefs callback + nsCOMPtr prefs(do_GetService(NS_PREF_CONTRACTID)); + if (prefs) { + // register the callback so we get notified of updates + prefs->UnregisterCallback("browser.chrome.toolbar_tips", (PrefChangedFunc)sTooltipPrefChanged, (void*)this); + } +} + +NS_IMPL_ADDREF(nsXULTooltipListener) +NS_IMPL_RELEASE(nsXULTooltipListener) + +NS_INTERFACE_MAP_BEGIN(nsXULTooltipListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMMouseMotionListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMXULListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMMouseListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMouseMotionListener) +NS_INTERFACE_MAP_END + +////////////////////////////////////////////////////////////////////////// +//// nsIDOMMouseListener + +NS_IMETHODIMP +nsXULTooltipListener::MouseDown(nsIDOMEvent* aMouseEvent) +{ + HideTooltip(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTooltipListener::MouseOut(nsIDOMEvent* aMouseEvent) +{ + // if the timer is running and no tooltip is shown, we + // have to cancel the timer here so that it doesn't + // show the tooltip if we move the mouse out of the window + if (mTooltipTimer && !mCurrentTooltip) { + mTooltipTimer->Cancel(); + mTooltipTimer = nsnull; + return NS_OK; + } + + if (mNeedTitletip) + return NS_OK; + + // check to see if the mouse left the targetNode, and if so, + // hide the tooltip + if (mCurrentTooltip) { + // which node did the mouse leave? + nsCOMPtr eventTarget; + aMouseEvent->GetTarget(getter_AddRefs(eventTarget)); + nsCOMPtr targetNode(do_QueryInterface(eventTarget)); + + // which node is our tooltip on? + nsCOMPtr doc; + mCurrentTooltip->GetDocument(*getter_AddRefs(doc)); + nsCOMPtr xulDoc(do_QueryInterface(doc)); + if (!doc) // remotely possible someone could have + return NS_OK; // removed tooltip from dom while it was open + nsCOMPtr tooltipNode; + xulDoc->GetTooltipNode (getter_AddRefs(tooltipNode)); + + // if they're the same, the mouse left the node the tooltip appeared on, + // close the tooltip. + if (tooltipNode == targetNode) { + HideTooltip(); + + // reset special outliner tracking + if (mIsTargetOutliner) { + mOutlinerBox = nsnull; + mLastOutlinerRow = -1; + mLastOutlinerCol.Truncate(); + } + } + } + + return NS_OK; +} + +////////////////////////////////////////////////////////////////////////// +//// nsIDOMMouseMotionListener + +NS_IMETHODIMP +nsXULTooltipListener::MouseMove(nsIDOMEvent* aMouseEvent) +{ + if (!sShowTooltips) + return NS_OK; + + // stash the coordinates of the event so that we can still get back to it from within the + // timer callback. On win32, we'll get a MouseMove event even when a popup goes away -- + // even when the mouse doesn't change position! To get around this, we make sure the + // mouse has really moved before proceeding. + nsCOMPtr mouseEvent(do_QueryInterface(aMouseEvent)); + PRInt32 newMouseX, newMouseY; + mouseEvent->GetClientX(&newMouseX); + mouseEvent->GetClientY(&newMouseY); + if (mMouseClientX == newMouseX && mMouseClientY == newMouseY) + return NS_OK; + mMouseClientX = newMouseX; + mMouseClientY = newMouseY; + + if (mIsTargetOutliner) + CheckOutlinerBodyMove(mouseEvent); + + // as the mouse moves, we want to make sure we reset the timer to show it, + // so that the delay is from when the mouse stops moving, not when it enters + // the node. + KillTooltipTimer(); + + // If the mouse moves while the tooltip is up, don't do anything. We make it + // go away only if it times out or leaves the target node. If nothing is + // showing, though, we have to do the work. + if (!mCurrentTooltip) { + mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1"); + if (mTooltipTimer) { + nsCOMPtr eventTarget; + aMouseEvent->GetTarget(getter_AddRefs(eventTarget)); + if (eventTarget) { + nsCOMPtr targetContent(do_QueryInterface(eventTarget)); + mTargetNode = targetContent; + } + if (mTargetNode) { + nsresult rv = mTooltipTimer->Init(sTooltipCallback, this, kTooltipShowTime, NS_PRIORITY_HIGH); + if (NS_FAILED(rv)) + mTargetNode = nsnull; + } + } + } + + return NS_OK; +} + +////////////////////////////////////////////////////////////////////////// +//// nsIDOMKeyListener + +NS_IMETHODIMP +nsXULTooltipListener::KeyDown(nsIDOMEvent* aKeyEvent) +{ + HideTooltip(); + return NS_OK; +} + +////////////////////////////////////////////////////////////////////////// +//// nsIDOMEventListener + + +NS_IMETHODIMP +nsXULTooltipListener::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString type; + aEvent->GetType(type); + if (type.Equals(NS_LITERAL_STRING("DOMMouseScroll"))) + HideTooltip(); + return NS_OK; +} + +////////////////////////////////////////////////////////////////////////// +//// nsXULTooltipListener + +NS_IMETHODIMP +nsXULTooltipListener::PopupHiding(nsIDOMEvent* aEvent) +{ + DestroyTooltip(); + return NS_OK; +} + +////////////////////////////////////////////////////////////////////////// +//// nsXULTooltipListener + +PRBool nsXULTooltipListener::sShowTooltips = PR_FALSE; + +nsresult +nsXULTooltipListener::Init(nsIContent* aSourceNode, nsIRootBox* aRootBox) +{ + mRootBox = aRootBox; + mSourceNode = aSourceNode; + AddTooltipSupport(aSourceNode); + + // if the target is an outlinerbody, we may have some special + // case handling to do + nsCOMPtr tag; + mSourceNode->GetTag(*getter_AddRefs(tag)); + mIsTargetOutliner = tag == nsXULAtoms::outlinerbody; + + static PRBool prefChangeRegistered = PR_FALSE; + + // Only the first time, register the callback and get the initial value of the pref + if (!prefChangeRegistered) { + nsCOMPtr prefs(do_GetService(NS_PREF_CONTRACTID)); + if (prefs) { + // get the initial value of the pref + nsresult rv = prefs->GetBoolPref("browser.chrome.toolbar_tips", &sShowTooltips); + if (NS_SUCCEEDED(rv)) { + // register the callback so we get notified of updates + rv = prefs->RegisterCallback("browser.chrome.toolbar_tips", (PrefChangedFunc)sTooltipPrefChanged, nsnull); + if (NS_SUCCEEDED(rv)) + prefChangeRegistered = PR_TRUE; + } + } + } + + return NS_OK; +} + +nsresult +nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode) +{ + if (!aNode) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr evtTarget(do_QueryInterface(aNode)); + evtTarget->AddEventListener(NS_LITERAL_STRING("mouseout"), (nsIDOMMouseListener*)this, PR_FALSE); + evtTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), (nsIDOMMouseListener*)this, PR_FALSE); + + return NS_OK; +} + +nsresult +nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode) +{ + if (!aNode) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr evtTarget(do_QueryInterface(aNode)); + evtTarget->RemoveEventListener(NS_LITERAL_STRING("mouseout"), (nsIDOMMouseListener*)this, PR_FALSE); + evtTarget->RemoveEventListener(NS_LITERAL_STRING("mousemove"), (nsIDOMMouseListener*)this, PR_FALSE); + + return NS_OK; +} + +void +nsXULTooltipListener::CheckOutlinerBodyMove(nsIDOMMouseEvent* aMouseEvent) +{ + if (!mOutlinerBox) { + // This will be called from MouseMove before the tooltip timer is + // killed so that mTargetNode isn't yet set to null + if (mTargetNode) { + nsCOMPtr doc; + mSourceNode->GetDocument(*getter_AddRefs(doc)); + nsCOMPtr shell; + doc->GetShellAt(0, getter_AddRefs(shell)); + if (shell) { + nsIFrame* bodyFrame = nsnull; + shell->GetPrimaryFrameFor(mTargetNode, &bodyFrame); + if (bodyFrame) { + nsCOMPtr bx(do_QueryInterface(bodyFrame)); + mOutlinerBox = bx; + } + } + } + } + + if (mOutlinerBox) { + PRInt32 x, y; + aMouseEvent->GetClientX(&x); + aMouseEvent->GetClientY(&y); + PRInt32 row; + nsXPIDLString colId, obj; + + mOutlinerBox->GetCellAt(x, y, &row, getter_Copies(colId), getter_Copies(obj)); + + // determine if we are going to need a titletip + // XXX check the disabletitletips attribute on the outliner content + mNeedTitletip = PR_FALSE; + if (obj.Equals(NS_LITERAL_STRING("text"))) { + nsCOMPtr view; + mOutlinerBox->GetView(getter_AddRefs(view)); + PRInt32 rowCount; + view->GetRowCount(&rowCount); + if (row < rowCount) { + PRBool isCropped; + mOutlinerBox->IsCellCropped(row, colId, &isCropped); + mNeedTitletip = isCropped; + } + } + + + if (row != mLastOutlinerRow || !mLastOutlinerCol.Equals(colId)) { + HideTooltip(); + } + + mLastOutlinerRow = row; + mLastOutlinerCol.Assign(colId); + } +} + +nsresult +nsXULTooltipListener::ShowTooltip() +{ + // get the tooltip content designated for the target node + GetTooltipFor(mSourceNode, &mCurrentTooltip); + if (!mCurrentTooltip) + return NS_ERROR_FAILURE; // the target node doesn't need a tooltip + + // set the node in the document that triggered the tooltip and show it + nsCOMPtr doc; + mCurrentTooltip->GetDocument(*getter_AddRefs(doc)); + nsCOMPtr xulDoc(do_QueryInterface(doc)); + if (xulDoc) { + // Make sure the target node is still attached to some document. + // It might have been deleted. + nsCOMPtr targetDoc; + mSourceNode->GetDocument(*getter_AddRefs(targetDoc)); + if (targetDoc) { + if (!mIsTargetOutliner) { + mLastOutlinerRow = -1; + mLastOutlinerCol.Truncate(); + } + + nsCOMPtr targetNode(do_QueryInterface(mTargetNode)); + xulDoc->SetTooltipNode(targetNode); + xulDoc->SetPopupNode(targetNode); + LaunchTooltip(mSourceNode, mMouseClientX, mMouseClientY); + + // at this point, |mCurrentTooltip| holds the content node of + // the tooltip. If there is an attribute on the popup telling us + // not to create the auto-hide timer, don't. + nsCOMPtr tooltipEl(do_QueryInterface(mCurrentTooltip)); + nsAutoString noAutoHide; + tooltipEl->GetAttribute(NS_LITERAL_STRING("noautohide"), noAutoHide); + if (noAutoHide != NS_LITERAL_STRING("true")) + CreateAutoHideTimer(); + + // listen for popuphidden on the tooltip node, so that we can + // be sure DestroyPopup is called even if someone else closes the tooltip + nsCOMPtr evtTarget(do_QueryInterface(mCurrentTooltip)); + evtTarget->AddEventListener(NS_LITERAL_STRING("popuphiding"), + (nsIDOMMouseListener*)this, PR_FALSE); + + // listen for mousedown,keydown, and DOMMouseScroll events at document level + nsCOMPtr doc; + mSourceNode->GetDocument(*getter_AddRefs(doc)); + if (doc) { + evtTarget = do_QueryInterface(doc); + evtTarget->AddEventListener(NS_LITERAL_STRING("DOMMouseScroll"), + (nsIDOMMouseListener*)this, PR_TRUE); + evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), + (nsIDOMMouseListener*)this, PR_TRUE); + evtTarget->AddEventListener(NS_LITERAL_STRING("keydown"), + (nsIDOMMouseListener*)this, PR_TRUE); + } + } + } + + return NS_OK; +} + +static void +GetOutlinerCellCoords(nsIOutlinerBoxObject* aOutlinerBox, nsIContent* aSourceNode, + PRInt32 aRow, nsAutoString aCol, PRInt32* aX, PRInt32* aY) +{ + PRInt32 junk; + const PRUnichar empty[] = {'\0'}; + aOutlinerBox->GetCoordsForCellItem(aRow, aCol.get(), empty, aX, aY, &junk, &junk); + nsCOMPtr xulEl(do_QueryInterface(aSourceNode)); + nsCOMPtr bx; + xulEl->GetBoxObject(getter_AddRefs(bx)); + PRInt32 myX, myY; + bx->GetX(&myX); + bx->GetY(&myY); + *aX += myX; + *aY += myY; +} + +static void +SetTitletipLabel(nsIOutlinerBoxObject* aOutlinerBox, nsIContent* aTooltip, + PRInt32 aRow, nsAutoString aCol) +{ + nsCOMPtr view; + aOutlinerBox->GetView(getter_AddRefs(view)); + + PRUnichar* cellText = nsnull; + view->GetCellText(aRow, aCol.get(), &cellText); + + nsAutoString label; + label.Assign(cellText); + + aTooltip->SetAttr(nsnull, nsXULAtoms::label, label, PR_FALSE); +} + +nsresult +nsXULTooltipListener::LaunchTooltip(nsIContent* aTarget, PRInt32 aX, PRInt32 aY) +{ + nsresult rv = NS_OK; + + if (!mCurrentTooltip) + return NS_OK; + + nsCOMPtr popupBox; + nsCOMPtr xulTooltipEl(do_QueryInterface(mCurrentTooltip)); + if (!xulTooltipEl) { + NS_ERROR("tooltip isn't a XUL element!"); + return NS_ERROR_FAILURE; + } + + xulTooltipEl->GetBoxObject(getter_AddRefs(popupBox)); + nsCOMPtr popupBoxObject(do_QueryInterface(popupBox)); + if (popupBoxObject) { + PRInt32 x = aX; + PRInt32 y = aY; + if (mNeedTitletip) { + GetOutlinerCellCoords(mOutlinerBox, mSourceNode, + mLastOutlinerRow, mLastOutlinerCol, &x, &y); + + SetTitletipLabel(mOutlinerBox, mCurrentTooltip, mLastOutlinerRow, mLastOutlinerCol); + mCurrentTooltip->SetAttr(nsnull, nsXULAtoms::titletip, NS_LITERAL_STRING("true"), PR_FALSE); + } else + mCurrentTooltip->UnsetAttr(nsnull, nsXULAtoms::titletip, PR_FALSE); + + nsCOMPtr targetEl(do_QueryInterface(aTarget)); + popupBoxObject->ShowPopup(targetEl, xulTooltipEl, x, y, + NS_LITERAL_STRING("tooltip").get(), + NS_LITERAL_STRING("none").get(), + NS_LITERAL_STRING("topleft").get()); + } + + return NS_OK; +} + +nsresult +nsXULTooltipListener::HideTooltip() +{ + if (mCurrentTooltip) { + // hide the popup through its box object + nsCOMPtr tooltipEl(do_QueryInterface(mCurrentTooltip)); + nsCOMPtr boxObject; + if (tooltipEl) + tooltipEl->GetBoxObject(getter_AddRefs(boxObject)); + nsCOMPtr popupObject(do_QueryInterface(boxObject)); + if (popupObject) + popupObject->HidePopup(); + } + + DestroyTooltip(); + + return NS_OK; +} + +static void +GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) +{ + *aResult = nsnull; + PRInt32 childCount; + aContent->ChildCount(childCount); + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr child; + aContent->ChildAt(i, *getter_AddRefs(child)); + nsCOMPtr tag; + child->GetTag(*getter_AddRefs(tag)); + if (aTag == tag.get()) { + *aResult = child; + NS_ADDREF(*aResult); + return; + } + } + + return; +} + +nsresult +nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip) +{ + if (!aTarget) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr targetEl(do_QueryInterface(aTarget)); + if (!targetEl) + return NS_ERROR_FAILURE; // could be a text node or something + + PRBool needTooltip; + targetEl->HasAttribute(NS_LITERAL_STRING("tooltiptext"), &needTooltip); + if (needTooltip) { + // specifying tooltiptext means we will always use the default tooltip + mRootBox->GetDefaultTooltip(aTooltip); + return NS_OK; + } else { + nsAutoString tooltipId; + targetEl->GetAttribute(NS_LITERAL_STRING("tooltip"), tooltipId); + + // if tooltip == _child, look for first child + if (tooltipId.Equals(NS_LITERAL_STRING("_child"))) { + GetImmediateChild(aTarget, nsXULAtoms::tooltip, aTooltip); // this addrefs + } else { + if (!tooltipId.IsEmpty()) { + // tooltip must be an id, use getElementById to find it + nsresult rv; + nsCOMPtr document; + if (NS_FAILED(rv = aTarget->GetDocument(*getter_AddRefs(document)))) { + NS_ERROR("Unable to retrieve the tooltip node document."); + return rv; + } + + nsCOMPtr xulDocument = do_QueryInterface(document); + if (!xulDocument) { + NS_ERROR("tooltip attached to an element that isn't in XUL!"); + return NS_ERROR_FAILURE; + } + + nsCOMPtr tooltipEl; + xulDocument->GetElementById(tooltipId, getter_AddRefs(tooltipEl)); + + if (tooltipEl) { + mNeedTitletip = PR_FALSE; + + nsCOMPtr tooltipContent(do_QueryInterface(tooltipEl)); + *aTooltip = tooltipContent; + + return NS_OK; + } + } + } + } + + // titletips should just use the default tooltip + if (mIsTargetOutliner && mNeedTitletip) { + mRootBox->GetDefaultTooltip(aTooltip); + return NS_OK; + } + + return NS_OK; +} + +nsresult +nsXULTooltipListener::DestroyTooltip() +{ + if (mCurrentTooltip) { + // clear out the tooltip node on the document + nsCOMPtr doc; + mCurrentTooltip->GetDocument(*getter_AddRefs(doc)); + if (doc) { + nsCOMPtr xulDoc(do_QueryInterface(doc)); + if (xulDoc) + xulDoc->SetTooltipNode(nsnull); + + // remove the mousedown and keydown listener from document + nsCOMPtr evtTarget(do_QueryInterface(doc)); + evtTarget->RemoveEventListener(NS_LITERAL_STRING("DOMMouseScroll"), (nsIDOMMouseListener*)this, PR_TRUE); + evtTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)this, PR_TRUE); + evtTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMMouseListener*)this, PR_TRUE); + } + + // remove the popuphidden listener from tooltip + nsCOMPtr evtTarget(do_QueryInterface(mCurrentTooltip)); + evtTarget->RemoveEventListener(NS_LITERAL_STRING("popuphiding"), (nsIDOMMouseListener*)this, PR_FALSE); + + // kill any ongoing timers + KillTooltipTimer(); + if (mAutoHideTimer) { + mAutoHideTimer->Cancel(); + mAutoHideTimer = nsnull; + } + + mCurrentTooltip = nsnull; + } + + return NS_OK; +} + +void +nsXULTooltipListener::KillTooltipTimer() +{ + if (mTooltipTimer) { + mTooltipTimer->Cancel(); + mTooltipTimer = nsnull; + mTargetNode = nsnull; + } +} + +void +nsXULTooltipListener::CreateAutoHideTimer() +{ + if (mAutoHideTimer) { + mAutoHideTimer->Cancel(); + mAutoHideTimer = nsnull; + } + + mAutoHideTimer = do_CreateInstance("@mozilla.org/timer;1"); + if ( mAutoHideTimer ) + mAutoHideTimer->Init(sAutoHideCallback, this, kTooltipAutoHideTime, NS_PRIORITY_HIGH); +} + +void +nsXULTooltipListener::sTooltipCallback(nsITimer *aTimer, void *aListener) +{ + nsXULTooltipListener* self = NS_STATIC_CAST(nsXULTooltipListener*, aListener); + if (self) + self->ShowTooltip(); +} + +void +nsXULTooltipListener::sAutoHideCallback(nsITimer *aTimer, void* aListener) +{ + nsXULTooltipListener* self = NS_STATIC_CAST(nsXULTooltipListener*, aListener); + if (self) + self->HideTooltip(); +} + +int +nsXULTooltipListener::sTooltipPrefChanged(const char* aPref, void* aData) +{ + nsXULTooltipListener* self = NS_STATIC_CAST(nsXULTooltipListener*, aData); + if (self) { + nsCOMPtr prefs(do_GetService(NS_PREF_CONTRACTID)); + if (prefs) + prefs->GetBoolPref("browser.chrome.toolbar_tips", &sShowTooltips); + } + return NS_OK; +} diff --git a/layout/xul/base/src/nsXULTooltipListener.h b/layout/xul/base/src/nsXULTooltipListener.h new file mode 100644 index 000000000000..8e0cf33e165f --- /dev/null +++ b/layout/xul/base/src/nsXULTooltipListener.h @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXULTooltipListener_h__ +#define nsXULTooltipListener_h__ + +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseMotionListener.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsIDOMXULListener.h" +#include "nsIContent.h" +#include "nsIDOMElement.h" +#include "nsITimer.h" +#include "nsIRootBox.h" +#include "nsIOutlinerBoxObject.h" +#include "nsString.h" + +class nsXULTooltipListener : public nsIDOMMouseListener, + public nsIDOMMouseMotionListener, + public nsIDOMKeyListener, + public nsIDOMXULListener +{ +public: + + nsXULTooltipListener(); + virtual ~nsXULTooltipListener(); + + // nsISupports + NS_DECL_ISUPPORTS + + // nsIDOMMouseListener + NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent); + NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; }; + NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }; + NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }; + NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; }; + NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent); + + // nsIDOMMouseMotionListener + NS_IMETHOD DragMove(nsIDOMEvent* aMouseEvent) { return NS_OK; }; + NS_IMETHOD MouseMove(nsIDOMEvent* aMouseEvent); + + // nsIDOMKeyListener + NS_IMETHOD KeyDown(nsIDOMEvent* aKeyEvent); + NS_IMETHOD KeyUp(nsIDOMEvent* aKeyEvent) { return NS_OK; }; + NS_IMETHOD KeyPress(nsIDOMEvent* aKeyEvent) { return NS_OK; }; + + // nsIDOMXULListener + NS_IMETHOD PopupShowing(nsIDOMEvent* aEvent) { return NS_OK; }; + NS_IMETHOD PopupShown(nsIDOMEvent* aEvent) { return NS_OK; }; + NS_IMETHOD PopupHiding(nsIDOMEvent* aEvent); + NS_IMETHOD PopupHidden(nsIDOMEvent* aEvent) { return NS_OK; }; + NS_IMETHOD Close(nsIDOMEvent* aEvent) { return NS_OK; }; + NS_IMETHOD Command(nsIDOMEvent* aEvent) { return NS_OK; }; + NS_IMETHOD Broadcast(nsIDOMEvent* aEvent) { return NS_OK; }; + NS_IMETHOD CommandUpdate(nsIDOMEvent* aEvent) { return NS_OK; }; + + // nsIDOMEventListener + NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent); + + nsresult Init(nsIContent* aSourceNode, nsIRootBox* aRootBox); + nsresult SetDefaultTooltip(nsIContent* aDefaultTooltip); + nsresult GetDefaultTooltip(nsIContent** aDefaultTooltip); + nsresult AddTooltipSupport(nsIContent* aNode); + nsresult RemoveTooltipSupport(nsIContent* aNode); + +protected: + + // pref callback for when the "show tooltips" pref changes + static int sTooltipPrefChanged (const char* aPref, void* aData); + static PRBool sShowTooltips; + + void KillTooltipTimer(); + void CreateAutoHideTimer(); + + void CheckOutlinerBodyMove(nsIDOMMouseEvent* aMouseEvent); + + nsresult ShowTooltip(); + nsresult LaunchTooltip(nsIContent* aTarget, PRInt32 aX, PRInt32 aY); + nsresult HideTooltip(); + nsresult DestroyTooltip(); + nsresult GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip); + + nsIRootBox* mRootBox; + nsIContent* mSourceNode; + nsIContent* mTargetNode; + nsIContent* mCurrentTooltip; + + // a timer for showing the tooltip + nsCOMPtr mTooltipTimer; + static void sTooltipCallback (nsITimer* aTimer, void* aListener); + PRInt32 mMouseClientX, mMouseClientY; + + // a timer for auto-hiding the tooltip after a certain delay + nsCOMPtr mAutoHideTimer; + static void sAutoHideCallback (nsITimer* aTimer, void* aListener); + + // various delays for tooltips + enum { + kTooltipAutoHideTime = 5000, // 5000ms = 5 seconds + kTooltipShowTime = 500 // 500ms = 0.5 seconds + }; + + // special members for handling outliners + PRBool mIsTargetOutliner; + PRBool mNeedTitletip; + PRInt32 mLastOutlinerRow; + nsIOutlinerBoxObject* mOutlinerBox; + nsAutoString mLastOutlinerCol; +}; + +#endif // nsXULTooltipListener \ No newline at end of file