gecko-dev/embedding/browser/powerplant/source/CThrobber.cpp
2001-08-18 15:10:38 +00:00

405 lines
10 KiB
C++

/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications, Inc. Portions created by Netscape are
* Copyright (C) 1999, Mozilla. All Rights Reserved.
*
* Contributor(s):
* Conrad Carlen <conrad@ingress.com>
*/
#include "CThrobber.h"
#include "CBrowserWindow.h"
#include <LString.h>
#include <LStream.h>
#include <UDrawingState.h>
#include "nsIWidget.h"
#include "nsWidgetsCID.h"
#include "nsIComponentManager.h"
#include "nsIDeviceContext.h"
#include "nsITimer.h"
#include "nsFont.h"
#include "nsIFontMetrics.h"
#include "prprf.h"
// CIDs
static NS_DEFINE_IID(kChildCID, NS_CHILD_CID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
// Static variables
map<nsIWidget*, CThrobber*> CThrobber::mgThrobberMap;
// Constants
const PRUint32 kThrobFrequency = 66; // animation frequency in milliseconds
//*****************************************************************************
//*** CThrobber: constructors/destructor
//*****************************************************************************
CThrobber::CThrobber() :
mNumImages(2), mCompletedImages(0), mRunning(false)
{
NS_INIT_REFCNT(); // caller must add ref as normal
AddThrobber(this);
}
CThrobber::CThrobber(LStream* inStream) :
LControl(inStream),
mNumImages(2), mCompletedImages(0), mRunning(false)
{
mRefCnt = 1; // PowerPlant is making us, and it sure isn't going to do an AddRef.
LStr255 tempStr;
mDefImageURL[0] = '\0';
*inStream >> (StringPtr) tempStr;
memcpy(mDefImageURL, (char *)&tempStr[1], (PRInt32)tempStr.Length());
mDefImageURL[(PRInt32)tempStr.Length()] = '\0';
mAnimImageURL[0] = '\0';
*inStream >> (StringPtr) tempStr;
memcpy(mAnimImageURL, (char *)&tempStr[1], (PRInt32)tempStr.Length());
mAnimImageURL[(PRInt32)tempStr.Length()] = '\0';
}
CThrobber::~CThrobber()
{
if (mWidget)
mWidget->Destroy();
DestroyImages();
RemoveThrobber(this);
}
NS_IMPL_ISUPPORTS0(CThrobber)
#if 0
void CThrobber::Notify(nsIImageRequest *aImageRequest,
nsIImage *aImage,
nsImageNotification aNotificationType,
PRInt32 aParam1, PRInt32 aParam2,
void *aParam3)
{
if (aNotificationType == nsImageNotification_kImageComplete)
{
mCompletedImages++;
// Remove ourselves as an observer of the image request object, because
// the image request objects each hold a reference to us. This avoids a
// circular reference problem. If we don't, our ref count will never reach
// 0 and we won't get destroyed and neither will the image request objects
aImageRequest->RemoveObserver((nsIImageRequestObserver*)this);
}
}
void CThrobber::NotifyError(nsIImageRequest *aImageRequest,
nsImageError aErrorType)
{
}
#endif
void CThrobber::FinishCreateSelf()
{
CBrowserWindow *ourWindow = dynamic_cast<CBrowserWindow*>(LWindow::FetchWindowObject(Compat_GetMacWindow()));
ThrowIfNil_(ourWindow);
// Get the widget from the browser window
nsCOMPtr<nsIWidget> parentWidget;
ourWindow->GetWidget(getter_AddRefs(parentWidget));
ThrowIfNil_(parentWidget);
FocusDraw();
Rect portFrame;
CalcPortFrameRect(portFrame);
nsRect r(portFrame.left, portFrame.top, portFrame.right - portFrame.left, portFrame.bottom - portFrame.top);
// Create widget
nsresult rv;
mWidget = do_CreateInstance(kChildCID, &rv);
if (!mWidget)
Throw_(NS_ERROR_GET_CODE(rv));
mWidget->Create(parentWidget, r, HandleThrobberEvent, NULL);
rv = LoadImages();
if (NS_SUCCEEDED(rv))
AddThrobber(this);
}
void CThrobber::ShowSelf()
{
mWidget->Show(PR_TRUE);
}
void CThrobber::HideSelf()
{
mWidget->Show(PR_FALSE);
}
void CThrobber::DrawSelf()
{
#if 0
// Draw directly with the rendering context instead of passing an
// update event through nsMacMessageSink. By the time this routine is
// called, PowerPlant has taken care of the location, z order, and clipping
// of each view. Since focusing puts the the origin at our top left corner,
// all we have to do is get the bounds of the widget and put that at (0,0)
StColorPortState origState(UQDGlobals::GetCurrentPort());
nsCOMPtr<nsIRenderingContext> cx = getter_AddRefs(mWidget->GetRenderingContext());
nsRect bounds;
nsIImageRequest *imgreq;
nsIImage *img;
mWidget->GetClientBounds(bounds);
bounds.x = bounds.y = 0;
//cx->SetClipRect(bounds, nsClipCombine_kReplace, clipState);
PRUint32 index = mRunning ? kAnimImageIndex : kDefaultImageIndex;
imgreq = index < mImages->size() ? (*mImages)[index] : nsnull;
img = imgreq ? imgreq->GetImage() : nsnull;
if (img)
{
cx->DrawImage(img, 0, 0);
NS_RELEASE(img);
}
#endif
}
void CThrobber::AdjustCursorSelf(Point /* inPortPt */,
const EventRecord& /* inMacEvent */)
{
// Overridden to do nothing - Cursor handling is done by HandleThrobberEvent
}
void CThrobber::ResizeFrameBy(SInt16 inWidthDelta,
SInt16 inHeightDelta,
Boolean inRefresh)
{
LControl::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh);
AdjustFrame(inRefresh);
}
void CThrobber::MoveBy(SInt32 inHorizDelta,
SInt32 inVertDelta,
Boolean inRefresh)
{
LControl::MoveBy(inHorizDelta, inVertDelta, inRefresh);
AdjustFrame(inRefresh);
}
void CThrobber::Start()
{
mRunning = true;
}
void CThrobber::Stop()
{
mRunning = false;
FocusDraw();
mWidget->Invalidate(PR_TRUE);
}
void CThrobber::AdjustFrame(Boolean inRefresh)
{
FocusDraw();
Rect portFrame;
CalcPortFrameRect(portFrame);
nsRect r(portFrame.left, portFrame.top, portFrame.right - portFrame.left, portFrame.bottom - portFrame.top);
mWidget->Resize(r.x, r.y, r.width, r.height, inRefresh);
}
NS_METHOD CThrobber::LoadImages()
{
nsresult rv = NS_ERROR_FAILURE;
#if 0
mImages = new vector<nsIImageRequest*>(mNumImages, nsnull);
if (nsnull == mImages) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = NS_NewImageGroup(&mImageGroup);
if (NS_OK != rv) {
return rv;
}
nsIDeviceContext *deviceCtx = mWidget->GetDeviceContext();
mImageGroup->Init(deviceCtx, nsnull);
NS_RELEASE(deviceCtx);
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_OK != rv) {
return rv;
}
mTimer->Init(ThrobTimerCallback, this, kThrobFrequency, NS_PRIORITY_NORMAL, NS_TYPE_REPEATING_SLACK);
nscolor bgcolor = NS_RGB(0, 0, 0);
// Get the default image
if (strlen(mDefImageURL)) {
(*mImages)[kDefaultImageIndex] = mImageGroup->GetImage(mDefImageURL,
(nsIImageRequestObserver *)this,
nsnull /*&bgcolor*/,
mFrameSize.width,
mFrameSize.height, 0);
}
// Get the animated image
if (strlen(mAnimImageURL)) {
(*mImages)[kAnimImageIndex] = mImageGroup->GetImage(mAnimImageURL,
(nsIImageRequestObserver *)this,
nsnull /*&bgcolor*/,
mFrameSize.width,
mFrameSize.height, 0);
}
mWidget->Invalidate(PR_TRUE);
#endif
return rv;
}
void CThrobber::DestroyImages()
{
if (mTimer)
{
mTimer->Cancel();
}
#if 0
if (mImageGroup)
{
mImageGroup->Interrupt();
for (vector<nsIImageRequest*>::iterator iter = mImages->begin(); iter < mImages->end(); ++iter)
{
NS_IF_RELEASE(*iter);
}
NS_RELEASE(mImageGroup);
}
if (mImages)
{
delete mImages;
mImages = nsnull;
}
#endif
}
void CThrobber::Tick()
{
if (mRunning) {
FocusDraw();
mWidget->Invalidate(PR_TRUE);
} else if (mCompletedImages == (PRUint32)mNumImages) {
FocusDraw();
mWidget->Invalidate(PR_TRUE);
mCompletedImages = 0;
}
#ifndef REPEATING_TIMERS
nsresult rv;
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_OK == rv) {
mTimer->Init(ThrobTimerCallback, this, kThrobFrequency);
}
#endif
}
CThrobber* CThrobber::FindThrobberForWidget(nsIWidget* aWidget)
{
map<nsIWidget*, CThrobber*>::iterator iter = mgThrobberMap.find(aWidget);
if (iter == mgThrobberMap.end())
return nsnull;
else
return iter->second;
}
void CThrobber::AddThrobber(CThrobber* aThrobber)
{
pair<nsIWidget*, CThrobber*> entry(aThrobber->mWidget, aThrobber);
mgThrobberMap[aThrobber->mWidget] = aThrobber;
}
void CThrobber::RemoveThrobber(CThrobber* aThrobber)
{
map<nsIWidget*, CThrobber*>::iterator iter = mgThrobberMap.find(aThrobber->mWidget);
if (iter != mgThrobberMap.end())
mgThrobberMap.erase(iter);
}
nsEventStatus PR_CALLBACK CThrobber::HandleThrobberEvent(nsGUIEvent *aEvent)
{
CThrobber* throbber = FindThrobberForWidget(aEvent->widget);
if (nsnull == throbber) {
return nsEventStatus_eIgnore;
}
switch (aEvent->message)
{
case NS_PAINT:
break;
case NS_MOUSE_LEFT_BUTTON_UP:
// Broadcast a message
break;
case NS_MOUSE_ENTER:
aEvent->widget->SetCursor(eCursor_hyperlink);
break;
case NS_MOUSE_EXIT:
aEvent->widget->SetCursor(eCursor_standard);
break;
}
return nsEventStatus_eIgnore;
}
void CThrobber::ThrobTimerCallback(nsITimer *aTimer, void *aClosure)
{
CThrobber* throbber = (CThrobber*)aClosure;
throbber->Tick();
}