gecko-dev/lib/mac/UserInterface/CScrollerWithArrows.cp
2004-04-25 16:36:22 +00:00

542 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is 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 MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//
// CScrollerWithArrows
// Mike Pinkerton, Netscape Communications
//
// See header file for class descriptions.
//
// NOTES:
// - does not yet autoscroll when doing drag and drop
// - icons for horizontal orientation are incorrect, but we don't have any better ones
// in the app so I just put the vertical ones in as place holders.
// - has only been tested in vertical orientation
// - has only been tested with all indents set to 0
//
#include "CScrollerWithArrows.h"
#include <LStdControl.h>
#include <PP_Types.h>
#include <UGAColorRamp.h>
#pragma mark -- class CScrollArrowControl
//
// CScrollerWithArrows
//
// Default contsructor
//
CScrollerWithArrows::CScrollerWithArrows()
{
mScrollingView = nil;
mScrollingViewID = -1;
mUpLeftArrow = nil;
mDownRightArrow = nil;
mIsVertical = false;
}
//
// CScrollerWithArrows
//
// Stream Constructor
//
CScrollerWithArrows::CScrollerWithArrows(
LStream *inStream)
: LView(inStream)
{
SScrollerArrowInfo scrollerInfo;
inStream->ReadData(&scrollerInfo, sizeof(SScrollerArrowInfo));
// ScrollingView has not yet been created, since SuperViews are
// created before their subviews when building Panes from a Stream.
// Therefore, we store the ID of the ScrollingView so that the
// FinishCreateSelf function can set up the proper connections.
mScrollingViewID = scrollerInfo.scrollingViewID;
mScrollingView = nil;
mUpLeftArrow = nil;
mDownRightArrow = nil;
// Determine orientation of scrolling. I guess I could just make this
// a flag in constructor....
if ( mFrameSize.height > mFrameSize.width )
mIsVertical = true;
try {
MakeScrollArrows(scrollerInfo.leftIndent, scrollerInfo.rightIndent,
scrollerInfo.topIndent, scrollerInfo.bottomIndent);
}
catch (...) { // Failed to fully build scroll arrows
delete mUpLeftArrow;
delete mDownRightArrow;
throw;
}
} // CScrollerWithArrows stream constructor
//
// ~CScrollerWithArrows
//
CScrollerWithArrows::~CScrollerWithArrows()
{
// view subsystem will dispose of scroll arrows because they are children
}
//
// MakeScrollArrows
//
// Create the arrow control objects in the view, indented accordingly to fit the
// needs of the subview. In contrast to how LScroller works, the indents apply to
// both scrollers.
//
void
CScrollerWithArrows::MakeScrollArrows(
Int16 inLeftIndent,
Int16 inRightIndent,
Int16 inTopIndent,
Int16 inBottomIndent)
{
SPaneInfo arrowInfo; // Common information for scroll arrows
arrowInfo.visible = false; // arrows aren't visible until
arrowInfo.enabled = true; // scroller is activated
arrowInfo.userCon = 0;
arrowInfo.superView = this;
arrowInfo.paneID = 0;
if ( IsVertical() ) {
arrowInfo.bindings.left = true;
arrowInfo.bindings.right = true;
arrowInfo.width = mFrameSize.width - inLeftIndent - inRightIndent;
arrowInfo.height = 16;
arrowInfo.left = inLeftIndent;
// create the top vertical arrow
arrowInfo.top = inTopIndent;
arrowInfo.bindings.bottom = false;
mUpLeftArrow = MakeOneScrollArrow ( arrowInfo, kUpLeft );
mUpLeftArrow->AddListener(this);
// create the bottom vertical arrow
arrowInfo.top = mFrameSize.height - inBottomIndent - arrowInfo.height;
arrowInfo.bindings.top = false;
arrowInfo.bindings.bottom = true;
mDownRightArrow = MakeOneScrollArrow ( arrowInfo, kDownRight );
mDownRightArrow->AddListener(this);
} // if view orientation is vertical
else {
arrowInfo.bindings.top = true;
arrowInfo.bindings.bottom = true;
arrowInfo.width = 16;
arrowInfo.height = mFrameSize.height - inTopIndent - inBottomIndent;
arrowInfo.top = inTopIndent;
// create the left horizontal arrow
arrowInfo.left = inLeftIndent;
arrowInfo.bindings.right = false;
mUpLeftArrow = MakeOneScrollArrow ( arrowInfo, kUpLeft );
mUpLeftArrow->AddListener(this);
// create the right horizontal arrow
arrowInfo.left = mFrameSize.width - inRightIndent - arrowInfo.width;
arrowInfo.bindings.left = false;
arrowInfo.bindings.right = true;
mDownRightArrow = MakeOneScrollArrow ( arrowInfo, kDownRight );
mDownRightArrow->AddListener(this);
} // else view orientation is horizontal
SetDefaultAttachable(this); // Reset so Attachments don't get
// attached to the ScrollBars
} // MakeScrollArrows
//
// MakeOneScrollArrow
//
// Creates a scroll arrow in the given direction.
//
LControl*
CScrollerWithArrows :: MakeOneScrollArrow ( const SPaneInfo &inPaneInfo,
ScrollDir inScrollWhichWay )
{
ResIDT iconID = 0;
SControlInfo controlInfo;
controlInfo.value = 0;
controlInfo.minValue = 0;
controlInfo.maxValue = 0;
if ( inScrollWhichWay == kUpLeft ) {
controlInfo.valueMessage = kControlUpButtonPart;
iconID = IsVertical() ? CScrollArrowControl::kIconUp : CScrollArrowControl::kIconLeft;
}
else {
controlInfo.valueMessage = kControlDownButtonPart;
iconID = IsVertical() ? CScrollArrowControl::kIconDown : CScrollArrowControl::kIconRight;
}
return new CScrollArrowControl ( inPaneInfo, controlInfo, iconID );
} // MakeOneScrollArrow
//
// FinishCreateSelf
//
// Finish creation of the scroller by installing its scrolling subview.
//
void
CScrollerWithArrows::FinishCreateSelf()
{
LView* scrollingView = dynamic_cast<LView*>(FindPaneByID(mScrollingViewID));
if (scrollingView != nil) {
GrafPtr myPort = GetMacPort();
if (myPort == nil)
myPort = UQDGlobals::GetCurrentPort();
StVisRgn suppressDrawing(myPort);
InstallView(scrollingView);
}
} // FinishCreateSelf
//
// ActivateSelf
//
// Show arrows that were hidden when app was deactivated
//
void
CScrollerWithArrows::ActivateSelf()
{
if ( mUpLeftArrow )
mUpLeftArrow->Show();
if ( mDownRightArrow )
mDownRightArrow->Show();
} // ActivateSelf
//
// DeactivateSelf
//
// According to Mac Human Interface Guidelines, ScrollBars in inactive
// windows are hidden. We follow this by hiding the scroll arrows.
//
void
CScrollerWithArrows::DeactivateSelf()
{
if ( mUpLeftArrow && mUpLeftArrow->IsVisible() ) {
mUpLeftArrow->Hide();
mUpLeftArrow->DontRefresh(true);
}
if ( mDownRightArrow && mDownRightArrow->IsVisible() ) {
mDownRightArrow->Hide();
mDownRightArrow->DontRefresh(true);
}
if ( FocusExposed() ) { // Redraw now!
Rect frame;
CalcLocalFrameRect(frame);
if (ExecuteAttachments(msg_DrawOrPrint, &frame)) {
DrawSelf();
}
}
} // DeactivateSelf
//
// InstallView
//
// Install a Scrolling View within this Scroller
//
void
CScrollerWithArrows::InstallView ( LView *inScrollingView )
{
mScrollingView = inScrollingView;
AdjustScrollArrows();
} // InstallView
//
// ExpandSubPane
//
// Expand a SubPane, which should be the Scroller's ScrollingView, to
// fill the interior of a Scroller
//
void
CScrollerWithArrows::ExpandSubPane ( LPane *inSub, Boolean inExpandHoriz,
Boolean inExpandVert )
{
SDimension16 subSize;
inSub->GetFrameSize(subSize);
SPoint32 subLocation;
inSub->GetFrameLocation(subLocation);
if (inExpandHoriz) {
subSize.width = mFrameSize.width - 2;
if ( !IsVertical() )
subSize.width -= 2 * 15; // account for 2 scroll arrows
subLocation.h = 1;
} else
subLocation.h -= mFrameLocation.h;
if (inExpandVert) {
subSize.height = mFrameSize.height - 2;
if ( IsVertical() )
subSize.height -= 2 * 15; // account for 2 scroll arrows
subLocation.v = 1;
} else
subLocation.v -= mFrameLocation.v;
inSub->PlaceInSuperFrameAt(subLocation.h, subLocation.v, false);
inSub->ResizeFrameTo(subSize.width, subSize.height, false);
} // ExpandSubPane
//
// AdjustScrollArrows
//
// Adjust the arrows (value, min, and max) according to the current
// state of the Scroller and ScrollingView
//
void
CScrollerWithArrows::AdjustScrollArrows()
{
// nothing to scroll, bail out after disabling scrollarrows.
if ( !mScrollingView ) {
if ( mUpLeftArrow )
mUpLeftArrow->SetValue(0);
if ( mDownRightArrow )
mDownRightArrow->SetMinValue(0);
return;
}
SPoint32 scrollUnit;
SDimension16 scrollFrameSize;
SDimension32 scrollImageSize;
SPoint32 scrollPosition;
mScrollingView->GetScrollUnit(scrollUnit);
mScrollingView->GetFrameSize(scrollFrameSize);
mScrollingView->GetImageSize(scrollImageSize);
mScrollingView->GetScrollPosition(scrollPosition);
bool enable = false;
if ( IsVertical() ) {
Int32 vertDiff = scrollImageSize.height - scrollFrameSize.height;
if (scrollPosition.v > vertDiff)
vertDiff = scrollPosition.v;
// if there is a reason to scroll, then enable the scroll arrows
if (vertDiff > 0)
enable = true;
}
else {
Int32 horizDiff = scrollImageSize.width - scrollFrameSize.width;
if (scrollPosition.h > horizDiff)
horizDiff = scrollPosition.h;
// if there is a reason to scroll, then enable the scroll arrows
if (horizDiff > 0)
enable = true;
}
if ( enable ) {
mUpLeftArrow->Enable();
mDownRightArrow->Enable();
}
else {
mUpLeftArrow->Disable();
mDownRightArrow->Disable();
}
} // AdjustScrollArrows
//
// ResizeFrameBy
//
// Adjust the framesize by the given ammounts and adjust the scroll arrows to reflect
// the new sizes.
//
void
CScrollerWithArrows::ResizeFrameBy( Int16 inWidthDelta, Int16 inHeightDelta, Boolean inRefresh )
{
// Let LView do all the work. All Scroller has to do is
// adjust the ScrollBars to account for the new size
// of the Scroller and resize its Image so it matches
// its Frame size.
LView::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh);
{
// Prevent scroll bars from drawing while adjusting them
StVisRgn suppressDrawing(GetMacPort());
AdjustScrollArrows();
}
ResizeImageBy(inWidthDelta, inHeightDelta, false);
} // ResizeFrameBy
//
// SubImageChanged
//
// arrow settings depend on the ScrollingView Image, so adjust them
// to match the current state.
//
void
CScrollerWithArrows::SubImageChanged ( LView *inSubView )
{
if (inSubView == mScrollingView)
AdjustScrollArrows();
} // SubImageChanged
//
// ListenToMessage
//
// Unlike LScroller which just listens for the thumb changing message, this scroller
// uses messages for all aspects of scrolling (of course, there is no such thing as
// a thumb in this case).
//
void
CScrollerWithArrows::ListenToMessage( MessageT inMessage, void *ioParam)
{
Int32 scrollUnits = 0;
switch ( inMessage ) {
case kControlUpButtonPart: // Scroll up/left one unit
scrollUnits = -1;
break;
case kControlDownButtonPart: // Scroll down/right one unit
scrollUnits = 1;
break;
} // case of which button clicked
if ( scrollUnits ) {
SPoint32 scrollUnit;
Int32 scrollBy;
mScrollingView->GetScrollUnit(scrollUnit);
scrollBy = scrollUnits * (IsVertical() ? scrollUnit.v : scrollUnit.h);
mScrollingView->ScrollPinnedImageBy(0, scrollBy, true);
}
} // ListenToMessage
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
#pragma mark -- class CScrollArrowControl
CScrollArrowControl :: CScrollArrowControl ( const SPaneInfo &inPaneInfo,
const SControlInfo &inControlInfo,
ResIDT inIconResID )
: LGAIconButton ( inPaneInfo, inControlInfo, controlMode_Button, inIconResID,
sizeSelector_SmallIconSize, iconPosition_Center )
{
// nothing else needed
}
CScrollArrowControl :: CScrollArrowControl ( LStream* inStream )
:LGAIconButton(inStream)
{
// nothing else needed
}
//
// HotSpotAction
//
// Do the normal hot-spot stuff, but also send the message that the click is still going on,
// which effectively does auto-scroll. It is a bit too fast for my taste, but it works.
//
void
CScrollArrowControl :: HotSpotAction ( Int16 inHotSpot, Boolean inCurrInside, Boolean inPrevInside )
{
LGAIconButton::HotSpotAction ( inHotSpot, inCurrInside, inPrevInside );
if ( inCurrInside )
BroadcastValueMessage();
} // HotSpotAction
//
// DrawSelf
//
// While the cursor tracking, etc of LGAIconButton is great, we don't want a border around
// the scroll triangle (it just looks bad). Only paint the background and draw the icon
//
void
CScrollArrowControl :: DrawSelf ( )
{
StColorPenState::Normalize ();
// Get the frame for the control and paint it
Rect localFrame;
CalcLocalFrameRect ( localFrame );
localFrame.right--;
::RGBForeColor ( &UGAColorRamp::GetColor(2));
::PaintRect ( &localFrame );
DrawIcon();
} // DrawSelf