|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// pfGUIListBoxMod Definition //
|
|
|
|
// //
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "hsTypes.h"
|
|
|
|
#include "pfGUIListBoxMod.h"
|
|
|
|
#include "pfGUIListElement.h"
|
|
|
|
#include "pfGameGUIMgr.h"
|
|
|
|
#include "pfGUIUpDownPairMod.h"
|
|
|
|
#include "pfGUIControlHandlers.h"
|
|
|
|
#include "pfGUIDialogMod.h"
|
|
|
|
|
|
|
|
#include "../pnMessage/plRefMsg.h"
|
|
|
|
#include "../pfMessage/pfGameGUIMsg.h"
|
|
|
|
#include "../plMessage/plAnimCmdMsg.h"
|
|
|
|
#include "../plAvatar/plAGModifier.h"
|
|
|
|
#include "../plGImage/plDynamicTextMap.h"
|
|
|
|
#include "../plInputCore/plInputInterface.h"
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define kIndentAmount 16
|
|
|
|
|
|
|
|
|
|
|
|
//// plSmallRect Stuff ///////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::plSmallRect::Set( Int16 l, Int16 t, Int16 r, Int16 b )
|
|
|
|
{
|
|
|
|
fLeft = l;
|
|
|
|
fTop = t;
|
|
|
|
fRight = r;
|
|
|
|
fBottom = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool pfGUIListBoxMod::plSmallRect::Contains( Int16 x, Int16 y )
|
|
|
|
{
|
|
|
|
if( x < fLeft || x > fRight || y < fTop || y > fBottom )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Wee Little Control Proc for scrolling ///////////////////////////////////
|
|
|
|
|
|
|
|
class pfScrollProc : public pfGUICtrlProcObject
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
|
|
|
|
pfGUIListBoxMod *fParent;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
pfScrollProc( pfGUIListBoxMod *parent ) : fParent( parent ) {}
|
|
|
|
|
|
|
|
virtual void DoSomething( pfGUIControlMod *ctrl )
|
|
|
|
{
|
|
|
|
// Do a check here to make sure we actually changed scroll
|
|
|
|
// positions--if not, we don't want to update, since that'll be
|
|
|
|
// slow as hell
|
|
|
|
int newScrollPos = (int)fParent->fScrollControl->GetMax() - (int)fParent->fScrollControl->GetCurrValue();
|
|
|
|
if( newScrollPos != fParent->fScrollPos )
|
|
|
|
{
|
|
|
|
fParent->IUpdate();
|
|
|
|
fParent->HandleExtendedEvent( pfGUIListBoxMod::kScrollPosChanged );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//// Constructor/Destructor //////////////////////////////////////////////////
|
|
|
|
|
|
|
|
pfGUIListBoxMod::pfGUIListBoxMod()
|
|
|
|
{
|
|
|
|
SetFlag( kWantsInterest );
|
|
|
|
fCurrClick = -1;
|
|
|
|
fCurrHover = -1;
|
|
|
|
fModsAtDragTime = 0;
|
|
|
|
fScrollControl = nil;
|
|
|
|
fCheckScroll = false;
|
|
|
|
fSingleSelElement = -1;
|
|
|
|
fScrollRangeUpdateDeferred = false;
|
|
|
|
fScrollPos = 0;
|
|
|
|
fLocked = false;
|
|
|
|
fReadyToRoll = false;
|
|
|
|
fClicking = false;
|
|
|
|
|
|
|
|
fScrollProc = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
pfGUIListBoxMod::~pfGUIListBoxMod()
|
|
|
|
{
|
|
|
|
ClearAllElements();
|
|
|
|
|
|
|
|
if( fScrollProc && fScrollProc->DecRef() )
|
|
|
|
delete fScrollProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IEval ///////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
hsBool pfGUIListBoxMod::IEval( double secs, hsScalar del, UInt32 dirty )
|
|
|
|
{
|
|
|
|
return pfGUIControlMod::IEval( secs, del, dirty );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// MsgReceive //////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
hsBool pfGUIListBoxMod::MsgReceive( plMessage *msg )
|
|
|
|
{
|
|
|
|
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg );
|
|
|
|
if( refMsg != nil )
|
|
|
|
{
|
|
|
|
if( refMsg->fType == kRefScrollCtrl )
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
|
|
|
|
{
|
|
|
|
fScrollControl = pfGUIValueCtrl::ConvertNoRef( refMsg->GetRef() );
|
|
|
|
fScrollControl->SetHandler( fScrollProc );
|
|
|
|
fScrollControl->SetStep( 1.f );
|
|
|
|
ICalcScrollRange();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fScrollControl = nil;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( refMsg->fType == kRefDynTextMap )
|
|
|
|
{
|
|
|
|
// Capture this so we can reset our flag, but do NOT just return from it!
|
|
|
|
if( !( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) )
|
|
|
|
fReadyToRoll = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pfGUIControlMod::MsgReceive( msg );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IPostSetUpDynTextMap ////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::IPostSetUpDynTextMap( void )
|
|
|
|
{
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
fReadyToRoll = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IUpdate /////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::IUpdate( void )
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
UInt16 x, y, width, height, maxHeight;
|
|
|
|
|
|
|
|
|
|
|
|
if( !fReadyToRoll )
|
|
|
|
return;
|
|
|
|
if( fScrollRangeUpdateDeferred )
|
|
|
|
{
|
|
|
|
fScrollRangeUpdateDeferred = false;
|
|
|
|
ICalcScrollRange();
|
|
|
|
}
|
|
|
|
|
|
|
|
fDynTextMap->ClearToColor( GetColorScheme()->fBackColor );
|
|
|
|
if( fElements.GetCount() == 0 )
|
|
|
|
{
|
|
|
|
fDynTextMap->FlushToHost();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fLocked )
|
|
|
|
{
|
|
|
|
hsStatusMessage( "WARNING: Forcing unlock on GUI list box due to call to IUpdate()\n" );
|
|
|
|
UnlockList();
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fScrollControl != nil )
|
|
|
|
{
|
|
|
|
// We reverse the value, since we want the "up" button to scroll "up", which actually
|
|
|
|
// *decreases* the scrollPos
|
|
|
|
fScrollPos = (int)fScrollControl->GetMax() - (int)fScrollControl->GetCurrValue();
|
|
|
|
|
|
|
|
if( fCheckScroll )
|
|
|
|
{
|
|
|
|
// If this is set, we just did something to change the selection to a single item.
|
|
|
|
// Since it makes sense to try to ensure this item is on the screen, we thus do so here.
|
|
|
|
// We don't want to do this every time, because we could be simply clicking on the
|
|
|
|
// scroll bars, in which case we DON'T want to do this check, since it would be
|
|
|
|
// fighting against the user, which is a big UI design no-no.
|
|
|
|
// To do this check, we search on either side of our scroll range (we can only
|
|
|
|
// search past after we draw, unfortunately). If there's a selected item out
|
|
|
|
// of our range, then we best move the scrollPos to move it into view
|
|
|
|
for( j = 0; j < fScrollPos; j++ )
|
|
|
|
{
|
|
|
|
int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount();
|
|
|
|
|
|
|
|
hsBool anySelected = false;
|
|
|
|
for( i = fWrapStartIdxs[ j ]; i < end; i++ )
|
|
|
|
anySelected |= fElements[ i ]->IsSelected();
|
|
|
|
|
|
|
|
if( anySelected )
|
|
|
|
{
|
|
|
|
// Shit. Move the scrollPos up to this item at the very least
|
|
|
|
fScrollPos = j;
|
|
|
|
fScrollControl->SetCurrValue( (hsScalar)( (int)fScrollControl->GetMax() - fScrollPos ) );
|
|
|
|
fCheckScroll = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fScrollPos = 0;
|
|
|
|
|
|
|
|
fElementBounds.SetCountAndZero( fElements.GetCount() );
|
|
|
|
|
|
|
|
if( !HasFlag( kScrollLeftToRight ) )
|
|
|
|
{
|
|
|
|
for( j = fScrollPos, y = 4; j < fWrapStartIdxs.GetCount() && y < fDynTextMap->GetVisibleHeight() - 8; j++ )
|
|
|
|
{
|
|
|
|
int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount();
|
|
|
|
|
|
|
|
for( maxHeight = 0, i = fWrapStartIdxs[ j ], x = 0; i < end; i++ )
|
|
|
|
{
|
|
|
|
if( HasFlag( kGrowLeavesAndProcessOxygen ) && fElements[ i ]->IsCollapsed() )
|
|
|
|
continue; // Skip collapsed items
|
|
|
|
|
|
|
|
fElements[ i ]->GetSize( fDynTextMap, &width, &height );
|
|
|
|
|
|
|
|
if( !HasFlag( kAllowMultipleElementsPerRow ) )
|
|
|
|
width = fDynTextMap->GetVisibleWidth();
|
|
|
|
|
|
|
|
if( y + height >= fDynTextMap->GetVisibleHeight() - 8 )
|
|
|
|
height = fDynTextMap->GetVisibleHeight() - 8 - y;
|
|
|
|
|
|
|
|
if( HasFlag( kGrowLeavesAndProcessOxygen ) )
|
|
|
|
{
|
|
|
|
x += fElements[ i ]->GetIndentLevel() * kIndentAmount;
|
|
|
|
if( x + width > fDynTextMap->GetVisibleWidth() )
|
|
|
|
width = fDynTextMap->GetVisibleWidth() - x;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !fElements[ i ]->Draw( fDynTextMap, x, y, width, height ) )
|
|
|
|
{
|
|
|
|
// Couldn't draw, so force it to be at the end of the scroll range
|
|
|
|
y = fDynTextMap->GetVisibleHeight() - 8;
|
|
|
|
j--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxHeight = ( height > maxHeight ) ? height : maxHeight;
|
|
|
|
fElementBounds[ i ].Set( x, y, x + width - 1, y + height - 1 );
|
|
|
|
x += width;
|
|
|
|
}
|
|
|
|
|
|
|
|
y += maxHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( j = fScrollPos, x = 4; j < fWrapStartIdxs.GetCount() && x < fDynTextMap->GetVisibleWidth() - 8; j++ )
|
|
|
|
{
|
|
|
|
int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount();
|
|
|
|
|
|
|
|
for( maxHeight = 0, i = fWrapStartIdxs[ j ], y = 0; i < end; i++ )
|
|
|
|
{
|
|
|
|
fElements[ i ]->GetSize( fDynTextMap, &width, &height );
|
|
|
|
|
|
|
|
if( !HasFlag( kAllowMultipleElementsPerRow ) )
|
|
|
|
height = fDynTextMap->GetVisibleHeight();
|
|
|
|
|
|
|
|
if( x + width >= fDynTextMap->GetVisibleWidth() - 8 )
|
|
|
|
width = fDynTextMap->GetVisibleWidth() - 8 - x;
|
|
|
|
|
|
|
|
if( !fElements[ i ]->Draw( fDynTextMap, x, y, width, height ) )
|
|
|
|
{
|
|
|
|
// Couldn't draw, so force it to be at the end of the scroll range
|
|
|
|
x = fDynTextMap->GetVisibleWidth() - 8;
|
|
|
|
j--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxHeight = ( width > maxHeight ) ? width : maxHeight;
|
|
|
|
fElementBounds[ i ].Set( x, y, x + width - 1, y + height - 1 );
|
|
|
|
y += height;
|
|
|
|
}
|
|
|
|
|
|
|
|
x += maxHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Second part of our scroll check--if we got here, then there was nothing selected
|
|
|
|
// before the viewing area. So check the rest
|
|
|
|
if( fCheckScroll && fScrollControl != nil )
|
|
|
|
{
|
|
|
|
fCheckScroll = false;
|
|
|
|
for( ; j < fWrapStartIdxs.GetCount(); j++ )
|
|
|
|
{
|
|
|
|
int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount();
|
|
|
|
|
|
|
|
hsBool anySelected = false;
|
|
|
|
for( i = fWrapStartIdxs[ j ]; i < end; i++ )
|
|
|
|
anySelected |= fElements[ i ]->IsSelected();
|
|
|
|
|
|
|
|
if( anySelected )
|
|
|
|
{
|
|
|
|
fScrollPos = j;
|
|
|
|
fScrollControl->SetCurrValue( (hsScalar)( (int)fScrollControl->GetMax() - fScrollPos ) );
|
|
|
|
IUpdate(); // Gotta update again, since we just changed the scrollPos after the fact
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fDynTextMap->FlushToHost();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ICalcWrapStarts /////////////////////////////////////////////////////////
|
|
|
|
// Calculates the starting indexes for each row of items. Call whenever you
|
|
|
|
// update the element list.
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::ICalcWrapStarts( void )
|
|
|
|
{
|
|
|
|
UInt16 i, x, y, maxHeight, width, height;
|
|
|
|
|
|
|
|
|
|
|
|
fWrapStartIdxs.Reset();
|
|
|
|
|
|
|
|
if( HasFlag( kAllowMultipleElementsPerRow ) )
|
|
|
|
{
|
|
|
|
if( !HasFlag( kScrollLeftToRight ) )
|
|
|
|
{
|
|
|
|
for( i = 0, x = (UInt16)-1, y = 4, maxHeight = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
fElements[ i ]->GetSize( fDynTextMap, &width, &height );
|
|
|
|
|
|
|
|
if( x + width >= fDynTextMap->GetVisibleWidth() )
|
|
|
|
{
|
|
|
|
x = 0;
|
|
|
|
y += maxHeight;
|
|
|
|
maxHeight = 0;
|
|
|
|
fWrapStartIdxs.Append( i );
|
|
|
|
}
|
|
|
|
x += width;
|
|
|
|
maxHeight = ( height > maxHeight ) ? height : maxHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( i = 0, y = (UInt16)-1, x = 4, maxHeight = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
fElements[ i ]->GetSize( fDynTextMap, &width, &height );
|
|
|
|
if( y + height >= fDynTextMap->GetVisibleHeight() )
|
|
|
|
{
|
|
|
|
y = 0;
|
|
|
|
x += maxHeight;
|
|
|
|
maxHeight = 0;
|
|
|
|
fWrapStartIdxs.Append( i );
|
|
|
|
}
|
|
|
|
y += height;
|
|
|
|
maxHeight = ( width > maxHeight ) ? width : maxHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( HasFlag( kGrowLeavesAndProcessOxygen ) && fElements[ i ]->IsCollapsed() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fWrapStartIdxs.Append( i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ICalcScrollRange ////////////////////////////////////////////////////////
|
|
|
|
// Call whenever you update the element list
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::ICalcScrollRange( void )
|
|
|
|
{
|
|
|
|
UInt16 ctrlHt, ctrlWd, height, width, maxHeight;
|
|
|
|
int i, j, end;
|
|
|
|
|
|
|
|
|
|
|
|
if( !fReadyToRoll )
|
|
|
|
{
|
|
|
|
fScrollRangeUpdateDeferred = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Basically, the scroll range is 0 to (numElements-1), but since if we scroll
|
|
|
|
// that far, we won't see anything left except the last element, we have to calculate
|
|
|
|
// how many elements away from the end we can get before we see blank space. Which means
|
|
|
|
// counting up from the end until we have passed the height of our control.
|
|
|
|
|
|
|
|
end = fElements.GetCount();
|
|
|
|
if( !HasFlag( kScrollLeftToRight ) )
|
|
|
|
{
|
|
|
|
if( HasFlag( kGrowLeavesAndProcessOxygen ) )
|
|
|
|
{
|
|
|
|
// OK, that really isn't the end, the end will the count of non-collapsed elements. Haha.
|
|
|
|
for( i = 0, end = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( !fElements[ i ]->IsCollapsed() )
|
|
|
|
end++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = fWrapStartIdxs.GetCount() - 1, height = 0; i >= 0; i-- )
|
|
|
|
{
|
|
|
|
// Get the max height of this row
|
|
|
|
maxHeight = 0;
|
|
|
|
for( j = fWrapStartIdxs[ i ]; j < end; j++ )
|
|
|
|
{
|
|
|
|
fElements[ j ]->GetSize( fDynTextMap, &width, &ctrlHt );
|
|
|
|
maxHeight = ( ctrlHt > maxHeight ) ? ctrlHt : maxHeight;
|
|
|
|
}
|
|
|
|
end = fWrapStartIdxs[ i ];
|
|
|
|
height += maxHeight;
|
|
|
|
|
|
|
|
if( height > fDynTextMap->GetVisibleHeight() - 8 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( i = fWrapStartIdxs.GetCount() - 1, width = 0; i >= 0; i-- )
|
|
|
|
{
|
|
|
|
// Get the max width of this column
|
|
|
|
maxHeight = 0;
|
|
|
|
for( j = fWrapStartIdxs[ i ]; j < end; j++ )
|
|
|
|
{
|
|
|
|
fElements[ j ]->GetSize( fDynTextMap, &ctrlWd, &ctrlHt );
|
|
|
|
maxHeight = ( ctrlWd > maxHeight ) ? ctrlWd : maxHeight;
|
|
|
|
}
|
|
|
|
end = fWrapStartIdxs[ i ];
|
|
|
|
width += maxHeight;
|
|
|
|
|
|
|
|
if( width > fDynTextMap->GetVisibleWidth() - 8 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: i went one past, so our scroll range is really 0 to i+1
|
|
|
|
if( fScrollControl )
|
|
|
|
{
|
|
|
|
// Because we reverse the position on draw, we have to do some special trick here
|
|
|
|
// to make sure the reversed position stays the same
|
|
|
|
fScrollPos = (int)fScrollControl->GetMax() - (int)fScrollControl->GetCurrValue();
|
|
|
|
if( fScrollPos >= fWrapStartIdxs.GetCount() )
|
|
|
|
fScrollPos = fWrapStartIdxs.GetCount() - 1;
|
|
|
|
|
|
|
|
if( i < 0 )
|
|
|
|
// Smaller than the viewing area, so we don't scroll at all
|
|
|
|
fScrollControl->SetRange( 0.f, 0.f );
|
|
|
|
else
|
|
|
|
fScrollControl->SetRange( 0.f, (hsScalar)( i + 1 ) );
|
|
|
|
|
|
|
|
fScrollControl->SetCurrValue( (hsScalar)( (int)fScrollControl->GetMax() - fScrollPos ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::PurgeDynaTextMapImage()
|
|
|
|
{
|
|
|
|
if ( fDynTextMap != nil )
|
|
|
|
fDynTextMap->PurgeImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Read/Write //////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::Read( hsStream *s, hsResMgr *mgr )
|
|
|
|
{
|
|
|
|
pfGUIControlMod::Read(s, mgr);
|
|
|
|
|
|
|
|
fScrollControl = nil;
|
|
|
|
if( s->ReadBool() )
|
|
|
|
{
|
|
|
|
fScrollProc = TRACKED_NEW pfScrollProc( this );
|
|
|
|
fScrollProc->IncRef();
|
|
|
|
mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefScrollCtrl ), plRefFlags::kActiveRef );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( HasFlag( kDisableSelection ) )
|
|
|
|
ClearFlag( kWantsInterest );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::Write( hsStream *s, hsResMgr *mgr )
|
|
|
|
{
|
|
|
|
pfGUIControlMod::Write( s, mgr );
|
|
|
|
|
|
|
|
if( fScrollControl != nil )
|
|
|
|
{
|
|
|
|
s->WriteBool( true );
|
|
|
|
mgr->WriteKey( s, fScrollControl->GetKey() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
s->WriteBool( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// FilterMousePosition /////////////////////////////////////////////////////
|
|
|
|
// Tests the mouse point and decides whether we still want to accept input
|
|
|
|
// based on the position. This allows us to act etheral (i.e. pass mouse
|
|
|
|
// messages through) when the mouse is over an empty portion of the list.
|
|
|
|
|
|
|
|
hsBool pfGUIListBoxMod::FilterMousePosition( hsPoint3 &mousePt )
|
|
|
|
{
|
|
|
|
if( !HasFlag( kAllowMousePassThrough ) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Int32 hover = fCurrClick = IGetItemFromPoint( mousePt );
|
|
|
|
if( hover == -1 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// HandleMouseDown /////////////////////////////////////////////////////////
|
|
|
|
// What we do: normal click deselects all and selects the item clicked on
|
|
|
|
// (if any). Shift-click and ctrl-click avoids the deselect and toggles
|
|
|
|
// the item clicked on.
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers )
|
|
|
|
{
|
|
|
|
Int32 i;
|
|
|
|
|
|
|
|
int lastSelection = -1;
|
|
|
|
if (HasFlag(kForbidNoSelection))
|
|
|
|
{
|
|
|
|
// grab the last item we had selected just in case they clicked outside the list of selectable objects
|
|
|
|
for (i=0; i<fElements.GetCount(); i++)
|
|
|
|
if (fElements[i]->IsSelected())
|
|
|
|
{
|
|
|
|
lastSelection = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( HasFlag(kSingleSelect) || ( !( modifiers & ( pfGameGUIMgr::kShiftDown | pfGameGUIMgr::kCtrlDown ) ) && !HasFlag( kHandsOffMultiSelect ) ) )
|
|
|
|
{
|
|
|
|
// Deselect everyone!
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
fElements[ i ]->SetSelected( false );
|
|
|
|
fSingleSelElement = -1;
|
|
|
|
}
|
|
|
|
else if( modifiers & pfGameGUIMgr::kShiftDown )
|
|
|
|
{
|
|
|
|
IFindSelectionRange( &fMinSel, &fMaxSel );
|
|
|
|
}
|
|
|
|
|
|
|
|
fCurrHover = fCurrClick = IGetItemFromPoint( mousePt );
|
|
|
|
if( fCurrClick != -1 )
|
|
|
|
{
|
|
|
|
if( ( ( modifiers & pfGameGUIMgr::kShiftDown ) && !HasFlag( kSingleSelect ) ) )
|
|
|
|
{
|
|
|
|
// Select our new range, deselect everything outside
|
|
|
|
if( fCurrClick <= fMaxSel )
|
|
|
|
{
|
|
|
|
ISelectRange( 0, (Int8)(fCurrClick - 1), false );
|
|
|
|
ISelectRange( (Int8)fCurrClick, (Int8)fMaxSel, true );
|
|
|
|
ISelectRange( (Int8)(fMaxSel + 1), -1, false );
|
|
|
|
}
|
|
|
|
else if( fCurrClick >= fMinSel )
|
|
|
|
{
|
|
|
|
ISelectRange( 0, (Int8)(fMinSel - 1), false );
|
|
|
|
ISelectRange( (Int8)fMinSel, (Int8)fCurrClick, true );
|
|
|
|
ISelectRange( (Int8)(fCurrClick + 1), -1, false );
|
|
|
|
}
|
|
|
|
fElements[ fCurrClick ]->SetSelected( true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fElements[ fCurrClick ]->SetSelected( true );
|
|
|
|
fSingleSelElement = fCurrClick;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (HasFlag(kForbidNoSelection) && (lastSelection != -1))
|
|
|
|
{
|
|
|
|
fElements[ lastSelection ]->SetSelected(true);
|
|
|
|
fSingleSelElement = lastSelection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We want drag/up events to be processed with the modifiers we had on
|
|
|
|
// mouse down, not the current ones. So store 'em for reference
|
|
|
|
fModsAtDragTime = modifiers;
|
|
|
|
fClicking = true;
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// HandleMouseUp ///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers )
|
|
|
|
{
|
|
|
|
fClicking = false;
|
|
|
|
if( !( fModsAtDragTime & ( pfGameGUIMgr::kShiftDown | pfGameGUIMgr::kCtrlDown ) ) && !HasFlag( kHandsOffMultiSelect ) )
|
|
|
|
{
|
|
|
|
// No modifiers--simply select whatever item we're on
|
|
|
|
Int32 item = IGetItemFromPoint( mousePt );
|
|
|
|
if( item != fCurrClick )
|
|
|
|
{
|
|
|
|
if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() )
|
|
|
|
fElements[ fCurrClick ]->SetSelected( false );
|
|
|
|
fCurrHover = fCurrClick = item;
|
|
|
|
if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() )
|
|
|
|
fElements[ fCurrClick ]->SetSelected( true );
|
|
|
|
else if (HasFlag(kForbidNoSelection) && fCurrClick == -1)
|
|
|
|
{
|
|
|
|
fElements[fSingleSelElement]->SetSelected(true);
|
|
|
|
fCurrClick = fSingleSelElement;
|
|
|
|
}
|
|
|
|
fCheckScroll = true;
|
|
|
|
fSingleSelElement = fCurrClick;
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We didn't change selection, so go ahead and pass the click on to
|
|
|
|
// the item, in case it wants to do something
|
|
|
|
if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() )
|
|
|
|
{
|
|
|
|
hsPoint3 localPt = mousePt;
|
|
|
|
IScreenToLocalPt( localPt );
|
|
|
|
|
|
|
|
UInt16 lX = (UInt16)(( localPt.fX * ( fDynTextMap->GetVisibleWidth() - 1 ) ) - fElementBounds[ fCurrClick ].fLeft);
|
|
|
|
UInt16 lY = (UInt16)(( localPt.fY * ( fDynTextMap->GetVisibleHeight() - 1 ) )- fElementBounds[ fCurrClick ].fTop);
|
|
|
|
|
|
|
|
if( fElements[ fCurrClick ]->MouseClicked( lX, lY ) )
|
|
|
|
{
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DoSomething(); // Change in selection triggers our DoSomething() call
|
|
|
|
}
|
|
|
|
|
|
|
|
//// HandleMouseHover ////////////////////////////////////////////////////////
|
|
|
|
// Just updates our currHover item so we can update the mouse appropriately
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers )
|
|
|
|
{
|
|
|
|
fCurrHover = IGetItemFromPoint( mousePt );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// HandleMouseDrag /////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
if( ( fModsAtDragTime & pfGameGUIMgr::kShiftDown && !HasFlag( kSingleSelect ) ) )
|
|
|
|
{
|
|
|
|
// Select our new range, deselect everything outside
|
|
|
|
if( fCurrClick <= fMaxSel )
|
|
|
|
{
|
|
|
|
ISelectRange( 0, (Int8)(fCurrClick - 1), false );
|
|
|
|
ISelectRange( (Int8)fCurrClick, (Int8)fMaxSel, true );
|
|
|
|
ISelectRange( (Int8)(fMaxSel + 1), -1, false );
|
|
|
|
}
|
|
|
|
else if( fCurrClick >= fMinSel )
|
|
|
|
{
|
|
|
|
ISelectRange( 0, (Int8)(fMinSel - 1), false );
|
|
|
|
ISelectRange( (Int8)fMinSel, (Int8)fCurrClick, true );
|
|
|
|
ISelectRange( (Int8)(fCurrClick + 1), -1, false );
|
|
|
|
}
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
else if( !( fModsAtDragTime & ( pfGameGUIMgr::kShiftDown | pfGameGUIMgr::kCtrlDown ) ) )
|
|
|
|
{
|
|
|
|
// No modifiers--simply select whatever item we're on
|
|
|
|
if( HasFlag( kDragAndDropCapable ) )
|
|
|
|
{
|
|
|
|
// If we're drag and drop capable, then a mouse drag function results in
|
|
|
|
// the dialog entering Drag Mode(tm). Basically, we tell our parent dialog
|
|
|
|
// which items to drag and it'll run with the rest
|
|
|
|
fDialog->ClearDragList();
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fElements[ i ]->IsSelected() )
|
|
|
|
fDialog->AddToDragList( fElements[ i ] );
|
|
|
|
}
|
|
|
|
fDialog->EnterDragMode( this );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Int32 item = IGetItemFromPoint( mousePt );
|
|
|
|
if( item != fCurrClick )
|
|
|
|
{
|
|
|
|
if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() )
|
|
|
|
fElements[ fCurrClick ]->SetSelected( false );
|
|
|
|
fCurrHover = fCurrClick = item;
|
|
|
|
if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() )
|
|
|
|
fElements[ fCurrClick ]->SetSelected( true );
|
|
|
|
fCheckScroll = true;
|
|
|
|
fSingleSelElement = fCurrClick;
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// HandleMouseDblClick /////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::HandleMouseDblClick( hsPoint3 &mousePt, UInt8 modifiers )
|
|
|
|
{
|
|
|
|
if( !HasFlag( kGrowLeavesAndProcessOxygen ) )
|
|
|
|
return; // We only care about double clicks if we make oxygen
|
|
|
|
|
|
|
|
Int32 item = IGetItemFromPoint( mousePt );
|
|
|
|
if( item != -1 )
|
|
|
|
{
|
|
|
|
if( fElements[ item ]->GetType() == pfGUIListElement::kTreeRoot )
|
|
|
|
{
|
|
|
|
pfGUIListTreeRoot *root = (pfGUIListTreeRoot *)fElements[ item ];
|
|
|
|
|
|
|
|
root->ShowChildren( !root->IsShowingChildren() );
|
|
|
|
if( !fLocked )
|
|
|
|
{
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IGetItemFromPoint ///////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Int32 pfGUIListBoxMod::IGetItemFromPoint( hsPoint3 &mousePt )
|
|
|
|
{
|
|
|
|
if( !fBounds.IsInside( &mousePt ) )
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
hsPoint3 localPt = mousePt; // despite getting a ref to the point (why?) we do NOT want to modify it
|
|
|
|
IScreenToLocalPt( localPt );
|
|
|
|
UInt32 i;
|
|
|
|
Int16 clickItem, clickY = (Int16)( localPt.fY * ( fDynTextMap->GetVisibleHeight() - 1 ) );
|
|
|
|
Int16 clickX = (Int16)( localPt.fX * ( fDynTextMap->GetVisibleWidth() - 1 ) );
|
|
|
|
|
|
|
|
// We have a nice array that has the starting (top) Y's of each visible element. So we just
|
|
|
|
// check against that.
|
|
|
|
UInt32 startAt = 0;
|
|
|
|
// make sure that we have a valid fScrollPos
|
|
|
|
if ( fScrollPos != -1 )
|
|
|
|
{
|
|
|
|
// make sure that there is an Idx at fScrollPos
|
|
|
|
if ( fWrapStartIdxs.GetCount() > fScrollPos )
|
|
|
|
{
|
|
|
|
startAt = fWrapStartIdxs[ fScrollPos ];
|
|
|
|
clickItem = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for( i = startAt; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( i<fElementBounds.GetCount() && fElementBounds[ i ].Contains( clickX, clickY ) )
|
|
|
|
{
|
|
|
|
clickItem = (Int16)i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( clickItem > fElements.GetCount() - 1 || clickItem < 0 )
|
|
|
|
clickItem = -1;
|
|
|
|
|
|
|
|
return clickItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IFindSelectionRange /////////////////////////////////////////////////////
|
|
|
|
// Find the min and max item that's selected. Returns -1 for both if nobody
|
|
|
|
// is selected
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::IFindSelectionRange( Int32 *min, Int32 *max )
|
|
|
|
{
|
|
|
|
Int32 i;
|
|
|
|
|
|
|
|
|
|
|
|
*min = *max = -1;
|
|
|
|
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fElements[ i ]->IsSelected() )
|
|
|
|
{
|
|
|
|
*min = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = fElements.GetCount() - 1; i >= 0; i-- )
|
|
|
|
{
|
|
|
|
if( fElements[ i ]->IsSelected() )
|
|
|
|
{
|
|
|
|
*max = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ISelectRange ////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::ISelectRange( Int8 min, Int8 max, hsBool select )
|
|
|
|
{
|
|
|
|
Int16 i;
|
|
|
|
|
|
|
|
|
|
|
|
if( max == -1 )
|
|
|
|
max = fElements.GetCount() - 1;
|
|
|
|
|
|
|
|
for( i = min; i <= max; i++ )
|
|
|
|
fElements[ i ]->SetSelected( select );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// SetSelection ////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::SetSelection( Int32 item )
|
|
|
|
{
|
|
|
|
if( HasFlag( kSingleSelect ) )
|
|
|
|
{
|
|
|
|
// Easy, only one item selected
|
|
|
|
if( fSingleSelElement != -1 )
|
|
|
|
fElements[ fSingleSelElement ]->SetSelected( false );
|
|
|
|
|
|
|
|
fSingleSelElement = item;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ISelectRange( 0, -1, false );
|
|
|
|
|
|
|
|
if( item != -1 && item < fElements.GetCount() )
|
|
|
|
fElements[ item ]->SetSelected( true );
|
|
|
|
|
|
|
|
fCheckScroll = true;
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::RemoveSelection( Int32 item )
|
|
|
|
{
|
|
|
|
// make sure the item is valid
|
|
|
|
if ( item != -1 && item < fElements.GetCount() )
|
|
|
|
{
|
|
|
|
// if single select and its what is selected
|
|
|
|
if ( HasFlag( kSingleSelect) && fSingleSelElement == item )
|
|
|
|
{
|
|
|
|
// then remove the selection and make nothing selected
|
|
|
|
fElements[ fSingleSelElement ]->SetSelected( false );
|
|
|
|
fSingleSelElement = item;
|
|
|
|
}
|
|
|
|
// else if multiple selection
|
|
|
|
else
|
|
|
|
// just remove the selection
|
|
|
|
fElements[ item ]->SetSelected( false );
|
|
|
|
}
|
|
|
|
fCheckScroll = true;
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::AddSelection( Int32 item )
|
|
|
|
{
|
|
|
|
// make sure the item is valid (can't add a non-selection!)
|
|
|
|
if ( item != -1 && item < fElements.GetCount() )
|
|
|
|
{
|
|
|
|
// if single select then just like SetSelection
|
|
|
|
if ( HasFlag( kSingleSelect) )
|
|
|
|
{
|
|
|
|
SetSelection(item);
|
|
|
|
}
|
|
|
|
// else if multiple selection
|
|
|
|
else
|
|
|
|
// just set its selection
|
|
|
|
fElements[ item ]->SetSelected( true );
|
|
|
|
}
|
|
|
|
fCheckScroll = true;
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// HandleKeyPress //////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
hsBool pfGUIListBoxMod::HandleKeyPress( char key, UInt8 modifiers )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool pfGUIListBoxMod::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers )
|
|
|
|
{
|
|
|
|
if( key == KEY_CAPSLOCK )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( HasFlag( kDisableKeyActions ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( event == pfGameGUIMgr::kKeyDown || event == pfGameGUIMgr::kKeyRepeat )
|
|
|
|
{
|
|
|
|
// Use arrow keys to do our dirty work
|
|
|
|
if( key == KEY_UP )
|
|
|
|
{
|
|
|
|
if( fCurrClick == -1 && fElements.GetCount() > 0 )
|
|
|
|
fCurrClick = 0;
|
|
|
|
else while( fCurrClick > 0 )
|
|
|
|
{
|
|
|
|
fCurrClick--;
|
|
|
|
if( !HasFlag( kGrowLeavesAndProcessOxygen ) || !fElements[ fCurrClick ]->IsCollapsed() )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( key == KEY_DOWN )
|
|
|
|
{
|
|
|
|
if( fCurrClick == -1 && fElements.GetCount() > 0 )
|
|
|
|
fCurrClick = fElements.GetCount() - 1;
|
|
|
|
else while( fCurrClick < fElements.GetCount() - 1 )
|
|
|
|
{
|
|
|
|
fCurrClick++;
|
|
|
|
if( !HasFlag( kGrowLeavesAndProcessOxygen ) || !fElements[ fCurrClick ]->IsCollapsed() )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( key == KEY_HOME )
|
|
|
|
{
|
|
|
|
if( fElements.GetCount() > 0 )
|
|
|
|
fCurrClick = 0;
|
|
|
|
}
|
|
|
|
else if( key == KEY_END )
|
|
|
|
{
|
|
|
|
if( fElements.GetCount() > 0 )
|
|
|
|
fCurrClick = fElements.GetCount() - 1;
|
|
|
|
}
|
|
|
|
else if( key == KEY_ENTER && HasFlag( kGrowLeavesAndProcessOxygen ) )
|
|
|
|
{
|
|
|
|
if( fCurrClick != -1 && fElements[ fCurrClick ]->GetType() == pfGUIListElement::kTreeRoot )
|
|
|
|
{
|
|
|
|
pfGUIListTreeRoot *root = (pfGUIListTreeRoot *)fElements[ fCurrClick ];
|
|
|
|
root->ShowChildren( !root->IsShowingChildren() );
|
|
|
|
|
|
|
|
if( !fLocked )
|
|
|
|
{
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ISelectRange( 0, -1, false );
|
|
|
|
if( fCurrClick != -1 )
|
|
|
|
fElements[ fCurrClick ]->SetSelected( true );
|
|
|
|
fSingleSelElement = fCurrClick;
|
|
|
|
|
|
|
|
DoSomething(); // Change in selection triggers our DoSomething() call
|
|
|
|
|
|
|
|
fCheckScroll = true;
|
|
|
|
IUpdate();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ScrollToEnd /////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::ScrollToEnd( void )
|
|
|
|
{
|
|
|
|
if( fScrollControl != nil )
|
|
|
|
{
|
|
|
|
fScrollPos = (int)fScrollControl->GetMin();
|
|
|
|
fScrollControl->SetCurrValue( fScrollControl->GetMax() );
|
|
|
|
}
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ScrollToBegin ///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::ScrollToBegin( void )
|
|
|
|
{
|
|
|
|
if( fScrollControl != nil )
|
|
|
|
{
|
|
|
|
fScrollPos = (int)fScrollControl->GetMax();
|
|
|
|
fScrollControl->SetCurrValue( fScrollControl->GetMin() );
|
|
|
|
}
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::SetColorScheme( pfGUIColorScheme *newScheme )
|
|
|
|
{
|
|
|
|
UInt16 i;
|
|
|
|
|
|
|
|
pfGUIControlMod::SetColorScheme( newScheme );
|
|
|
|
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
fElements[ i ]->SetColorScheme( newScheme );
|
|
|
|
fElements[ i ]->SetSkin( fSkin );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::SetScrollPos( Int32 pos )
|
|
|
|
{
|
|
|
|
if ( pos >= (int)fScrollControl->GetMin() && pos <= (int)fScrollControl->GetMax() )
|
|
|
|
{
|
|
|
|
fScrollControl->SetCurrValue( (hsScalar)pos );
|
|
|
|
fScrollPos = (int)fScrollControl->GetMax() - pos;
|
|
|
|
}
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
Int32 pfGUIListBoxMod::GetScrollPos( void )
|
|
|
|
{
|
|
|
|
return (int)fScrollControl->GetCurrValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
Int32 pfGUIListBoxMod::GetScrollRange( void )
|
|
|
|
{
|
|
|
|
return (int)fScrollControl->GetMax() - (int)fScrollControl->GetMin();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// Element Manipulation ////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
UInt16 pfGUIListBoxMod::AddElement( pfGUIListElement *el )
|
|
|
|
{
|
|
|
|
UInt16 idx = fElements.GetCount();
|
|
|
|
fElements.Append( el );
|
|
|
|
el->SetColorScheme( GetColorScheme() );
|
|
|
|
el->SetSkin( fSkin );
|
|
|
|
if( !fLocked )
|
|
|
|
{
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
IUpdate();
|
|
|
|
HandleExtendedEvent( pfGUIListBoxMod::kItemAdded );
|
|
|
|
}
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::RemoveElement( UInt16 index )
|
|
|
|
{
|
|
|
|
// Make sure no other elements care about this one
|
|
|
|
UInt16 i, j;
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fElements[ i ]->GetType() == pfGUIListElement::kTreeRoot )
|
|
|
|
{
|
|
|
|
pfGUIListTreeRoot *root = (pfGUIListTreeRoot *)fElements[ i ];
|
|
|
|
for( j = 0; j < root->GetNumChildren(); )
|
|
|
|
{
|
|
|
|
if( root->GetChild( j ) == fElements[ index ] )
|
|
|
|
root->RemoveChild( j );
|
|
|
|
else
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete fElements[ index ];
|
|
|
|
fElements.Remove( index );
|
|
|
|
|
|
|
|
if( index == fSingleSelElement )
|
|
|
|
fSingleSelElement = -1;
|
|
|
|
else if( index < fSingleSelElement )
|
|
|
|
fSingleSelElement--;
|
|
|
|
fCurrHover = fCurrClick = -1;
|
|
|
|
|
|
|
|
if( !fLocked )
|
|
|
|
{
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
IUpdate();
|
|
|
|
HandleExtendedEvent( pfGUIListBoxMod::kItemRemoved );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Int16 pfGUIListBoxMod::FindElement( pfGUIListElement *toCompareTo )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fElements[ i ]->CompareTo( toCompareTo ) == 0 )
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (Int16)-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::ClearAllElements( void )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < fElements.GetCount(); i++ )
|
|
|
|
delete fElements[ i ];
|
|
|
|
fElements.Reset();
|
|
|
|
fSingleSelElement = -1;
|
|
|
|
|
|
|
|
if( !fLocked )
|
|
|
|
{
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
HandleExtendedEvent( pfGUIListBoxMod::kListCleared );
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt16 pfGUIListBoxMod::AddString( const char *string )
|
|
|
|
{
|
|
|
|
return AddElement( TRACKED_NEW pfGUIListText( string ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt16 pfGUIListBoxMod::AddString( const wchar_t *string )
|
|
|
|
{
|
|
|
|
return AddElement( TRACKED_NEW pfGUIListText( string ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
Int16 pfGUIListBoxMod::FindString( const char *toCompareTo )
|
|
|
|
{
|
|
|
|
pfGUIListText text( toCompareTo );
|
|
|
|
return FindElement( &text );
|
|
|
|
}
|
|
|
|
|
|
|
|
Int16 pfGUIListBoxMod::FindString( const wchar_t *toCompareTo )
|
|
|
|
{
|
|
|
|
pfGUIListText text( toCompareTo );
|
|
|
|
return FindElement( &text );
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt16 pfGUIListBoxMod::GetNumElements( void )
|
|
|
|
{
|
|
|
|
return fElements.GetCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
pfGUIListElement *pfGUIListBoxMod::GetElement( UInt16 idx )
|
|
|
|
{
|
|
|
|
return fElements[ idx ];
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::LockList( void )
|
|
|
|
{
|
|
|
|
fLocked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pfGUIListBoxMod::UnlockList( void )
|
|
|
|
{
|
|
|
|
fLocked = false;
|
|
|
|
ICalcWrapStarts();
|
|
|
|
ICalcScrollRange();
|
|
|
|
IUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IGetDesiredCursor ///////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
UInt32 pfGUIListBoxMod::IGetDesiredCursor( void ) const
|
|
|
|
{
|
|
|
|
if( fCurrHover == -1 )
|
|
|
|
return plInputInterface::kNullCursor;
|
|
|
|
|
|
|
|
if( fClicking )
|
|
|
|
return plInputInterface::kCursorClicked;
|
|
|
|
|
|
|
|
return plInputInterface::kCursorPoised;
|
|
|
|
}
|
|
|
|
|