/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

// ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
// author: atotic
// See UTearOffPalette.h for usage instructions and design notes
// ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

#include "UTearOffPalette.h"
#include "prtypes.h"
#include "CToolbarModeManager.h"
#include "CToolbarPatternBevelView.h"
#include "prefapi.h"
#include "uapp.h"

// PowerPlant
#include <LStream.h>
#include <URegions.h>
#include <LArray.h>
#include <LArrayIterator.h>

#define FLOAT_ID -19844	// Same define is in UFloatWindow.r
#define SIDE_FLOAT_ID -19845	// Same define is in UFloatWindow.r

#pragma mark CTearOffBar
#pragma mark -

// ---------------------------------------------------------------------------
//		₯ Constructor from stream
// ---------------------------------------------------------------------------
// Read in the data

CTearOffBar::CTearOffBar(LStream* inStream) : LView(inStream)
{
	*inStream >> fFloatingSubpaneID;
	*inStream >> fInPlaceSubpaneID;
	*inStream >> fAutoResize;
	*inStream >> fVisibility;
	*inStream >> fTitle;
	*inStream >> fSideTitle;
	*inStream >> fDisablePaletteWithWindow;

	fOriginalFrameSize = mFrameSize;
	fShown = false;
	fTransparentPane = 0;
	fAllowClickDrag = false;
}

// ---------------------------------------------------------------------------
//		₯ Destructor
// ---------------------------------------------------------------------------
// Unregister with the manager

CTearOffBar::~CTearOffBar()
{
	CTearOffManager::GetDefaultManager()->UnregisterBar(this);
}

// ---------------------------------------------------------------------------
//		₯ FinishCreateSelf
// ---------------------------------------------------------------------------
// Register with the manager

void CTearOffBar::FinishCreateSelf()
{
	CTearOffManager::GetDefaultManager()->RegisterBar(this);
}

// ---------------------------------------------------------------------------
//		₯ Click
// ---------------------------------------------------------------------------
// Slightly modified LView::Click
// We should handle clicks in the transparent pane
// We need this because the PPob view that holds the buttons that we put inside
// completely covers this view. Unless we make that view transparent, we'll never
// get any clicks

void CTearOffBar::Click( SMouseDownEvent	&inMouseDown )
{
									// Check if a SubPane of this View
									//   is hit
// Modified code
// Check if we have clicked on a 0 pane
	LPane	*clickedPane = FindDeepSubPaneContaining(inMouseDown.wherePort.h,
											inMouseDown.wherePort.v );
	if (clickedPane != nil && clickedPane->GetPaneID() == fTransparentPane)
	{
		LPane::Click(inMouseDown);
		return;
	}

// LView::Click code
	clickedPane = FindSubPaneHitBy(inMouseDown.wherePort.h,
											inMouseDown.wherePort.v);
											
	if	( clickedPane != nil )
	{		//  SubPane is hit, let it respond to the Click
		clickedPane->Click(inMouseDown);
		
	} else {						// No SubPane hit, or maybe pane 0. Inherited function
		LPane::Click(inMouseDown);	//   will process click on this View
	}
}

// ---------------------------------------------------------------------------
//		₯ ClickSelf
// ---------------------------------------------------------------------------
// Drag the outline of the bar out of the window
// Roughly copied out of UDesktop::DragDeskWindow

void CTearOffBar::ClickSelf(const SMouseDownEvent	&inMouseDown)
{
	if (::WaitMouseUp()) {				// Continue if mouse hasn't been
										//   released
										
		GrafPtr		savePort;			// Use ScreenPort for dragging
		GetPort(&savePort);
		GrafPtr		screenPort = UScreenPort::GetScreenPort();
		::SetPort(screenPort);
		StColorPenState::Normalize();

// Get surrounding rectangle in global port coordinates

		Rect frame;
		Point topLeft, botRight;
		topLeft.h = mFrameLocation.h;
		topLeft.v = mFrameLocation.v;
		botRight.h = topLeft.h + mFrameSize.width;
		botRight.v = topLeft.v + mFrameSize.height;
		PortToGlobalPoint(topLeft);
		PortToGlobalPoint(botRight);
		
		::SetRect( &frame, topLeft.h, topLeft.v, botRight.h, botRight.v );
		StRegion dragRgn(frame);
		
										// Let user drag a dotted outline
										//   of the Window
		Rect	dragRect = (**(GetGrayRgn())).rgnBBox;
		::InsetRect(&dragRect, 4, 4);
		
		Int32	dragDistance = ::DragGrayRgn(dragRgn, inMouseDown.macEvent.where,
											&dragRect, &dragRect,
											noConstraint, nil);
		::SetPort(savePort);
		
			// Check if the Window moved to a new, valid location.
			// DragGrayRgn returns the constant Drag_Aborted if the
			// user dragged outside the DragRect, which cancels
			// the drag operation. We also check for the case where
			// the drag finished at its original location (zero distance).
			
		Int16	horizDrag = LoWord(dragDistance);
		Int16	vertDrag = HiWord(dragDistance);
		
		if (dragDistance == 0x80008000) return;
			// 0x80008000 == Drag_Aborted from UFloatingDesktop
		Boolean realDrag = horizDrag != 0 || vertDrag != 0;
		if (fAllowClickDrag || realDrag)
		{
			 
				// A valid drag occurred. horizDrag and vertDrag are
				// distances from the current location. We need to
				// get the new location for the Window in global
				// coordinates. The bounding box of the Window's
				// content region gives the current location in
				// global coordinates. Add drag distances to this
				// to get the new location. 
			CTearOffManager::GetDefaultManager()->DragBar(this, 
													topLeft.h + horizDrag,
													topLeft.v + vertDrag,
													realDrag);
		}
	}
}

// ---------------------------------------------------------------------------
//		₯ AddListener
// ---------------------------------------------------------------------------
// Tell manager that we have a listener

void CTearOffBar::AddListener(LListener	*inListener)
{
	LBroadcaster::AddListener(inListener);
	CTearOffManager::GetDefaultManager()->RegisterListener(this, inListener);
}

// ---------------------------------------------------------------------------
//		₯ ListenToMessage 
// ---------------------------------------------------------------------------
// Tell TearOffManager that we got the message
void CTearOffBar::ListenToMessage(MessageT inMessage, void *ioParam)
{
	CTearOffManager::GetDefaultManager()->BarReceivedMessage(this, inMessage, ioParam);
}

// ---------------------------------------------------------------------------
//		₯ ListenToMessage 
// ---------------------------------------------------------------------------
// Tell TearOffManager that we got the message
void CTearOffBar::ActivateSelf()
{
	LView::ActivateSelf();
	CTearOffManager::GetDefaultManager()->BarActivated(this);
}

// ---------------------------------------------------------------------------
//		₯ ListenToMessage 
// ---------------------------------------------------------------------------
// Tell TearOffManager that we got the message
void CTearOffBar::DeactivateSelf()
{
	LView::DeactivateSelf();
	CTearOffManager::GetDefaultManager()->BarDeactivated(this);
}

// ---------------------------------------------------------------------------
//		₯ HidePalette 
// ---------------------------------------------------------------------------
// Delete all the subpanes.
// Manager will broadcast a message msg_FloatPalleteRemoved to your listener
void CTearOffBar::HidePalette()
{
	DeleteAllSubPanes();
	if (fAutoResize)
		ResizeFrameTo(0,0,TRUE);
	else
		Refresh();
	fShown = false;
}

// ---------------------------------------------------------------------------
//		₯ ShowPalette 
// ---------------------------------------------------------------------------
// Reads in the subview from PPob.
// Position the view properly

void CTearOffBar::ShowPalette()
{
	if (fShown)
		return;
	// setup commander (window), superview(this), and listener(this)
	
	SetDefaultView(this);
	LWindow * commander = LWindow::FetchWindowObject(GetMacPort());
	SignalIfNot_( commander );

	LView * view = UReanimator::CreateView(fInPlaceSubpaneID, this, commander);
	
	SignalIfNot_ ( view );	// We need that pane ID

	view->FinishCreate();
	fTransparentPane = view->GetPaneID();
	UReanimator::LinkListenerToControls(this, this, fInPlaceSubpaneID);
	UReanimator::LinkListenerToControls(CTearOffManager::GetDefaultManager(),
										this, fInPlaceSubpaneID);

	if (fAutoResize)
		ResizeFrameTo(fOriginalFrameSize.width, fOriginalFrameSize.height, TRUE);
#ifdef DEBUG
	if (fAutoResize)
	{
		SDimension16 s;
		view->GetFrameSize(s);
		// The container and the loaded view should be the same width/height???
		SignalIf_(s.width != fOriginalFrameSize.width);
		SignalIf_(s.height != fOriginalFrameSize.height);
	}	
#endif
	fShown = true;
}

#pragma mark -
#pragma mark CTearOffWindow
#pragma mark -
/************************************************************************
 * class CTearOffWindow
 *
 * just registers/unregisters itself with the 
 * You cannot subclass this class, because CTearOffManager creates all
 * palettes out of a single PPob.
 ************************************************************************/
#define SAVE_POSITION 1

#if SAVE_POSITION
#include "CSaveWindowStatus.h"
#endif // SAVE_POSITION

//======================================
class CTearOffWindow
		: public LWindow,
			public LListener
			, public CToolbarModeChangeListener
#if SAVE_POSITION
		, public CSaveWindowStatus
#endif // SAVE_POSITION
//======================================
{
private:
	typedef LWindow Inherited;
	friend class CTearOffManager;
public:
	enum { class_ID = 'DRFW' };
		
// Constructors

							CTearOffWindow(LStream* inStream);

	virtual				~CTearOffWindow();

// Events
	virtual void		ListenToMessage(MessageT inMessage, void *ioParam);

	virtual void HandleModeChange(Int8 inNewMode);
	virtual void DoSetZoom( Boolean );
	virtual void AdjustWindowSizeBy( SDimension16& size_change );

#if SAVE_POSITION
private:
	virtual ResIDT		GetStatusResID(void) const { return FLOAT_ID; }
	virtual UInt16		GetValidStatusVersion(void) const { return 0x0111; }
	virtual void		FinishCreateSelf();
	virtual void		AttemptClose();
	virtual void		DoClose();
#endif // SAVE_POSITION
private:
	UInt8				fVisibility;	// Inherited from the bar
};

//-----------------------------------
CTearOffWindow::CTearOffWindow(LStream* inStream)
//-----------------------------------
:	LWindow(inStream)
,	CSaveWindowStatus(this)
{
	PREF_SetBoolPref("taskbar.mac.is_open", true);
}

//-----------------------------------
CTearOffWindow::~CTearOffWindow()
//-----------------------------------
{
	// Notify the manager
	CTearOffManager::GetDefaultManager()->UnregisterWindow(this);

		// YIKES! Save this preference if the app _isn't_ quitting.  What an ugly hack!
	if ( CFrontApp::sApplication->GetState() == programState_ProcessingEvents )
		PREF_SetBoolPref("taskbar.mac.is_open", false);
}

#if SAVE_POSITION

//-----------------------------------
void CTearOffWindow::FinishCreateSelf()
//-----------------------------------
{
	Inherited::FinishCreateSelf();
	CSaveWindowStatus::FinishCreateWindow();
	
	SDimension16 zero_zero = {0,0};
	AdjustWindowSizeBy(zero_zero);
}

//-----------------------------------
void CTearOffWindow::AttemptClose()
//-----------------------------------
{
	AttemptCloseWindow();
	Inherited::AttemptClose();
}

//-----------------------------------
void CTearOffWindow::DoClose()
//-----------------------------------
{
	AttemptCloseWindow();
	Inherited::DoClose();
}

#endif // SAVE_POSITION


void CTearOffWindow::HandleModeChange( Int8 inNewMode )
	{
		SDimension16	size_change={ 0, 0 };
		if ( CToolbarButtonContainer* container_ptr = dynamic_cast<CToolbarButtonContainer*>(FindPaneByID('TBPv')) )
			container_ptr->HandleModeChange(inNewMode, size_change);
		AdjustWindowSizeBy(size_change);
	}


void CTearOffWindow::DoSetZoom( Boolean )
	{
		SDimension16	size_change={ 0, 0 };
		if ( CToolbarPatternBevelView* container_ptr = dynamic_cast<CToolbarPatternBevelView*>(FindPaneByID('TBPv')) )
			container_ptr->RotateArrangement(size_change);
		AdjustWindowSizeBy(size_change);

		SDimension16 frameSize;
		GetFrameSize(frameSize);
		PREF_SetBoolPref("taskbar.mac.is_vertical", XP_Bool(frameSize.height > frameSize.width));
	}

void CTearOffWindow::AdjustWindowSizeBy( SDimension16& size_change )
	{
			/*
				Always adjust the size (even if size_change is {0,0}) because the caller
				might be trying to ensure that the window is pinned to the screen.
			*/
		Rect frame_rect;
		CalcPortFrameRect(frame_rect);
		PortToGlobalPoint(topLeft(frame_rect));
		PortToGlobalPoint(botRight(frame_rect));

		frame_rect.right += size_change.width;
		frame_rect.bottom += size_change.height;

		VerifyWindowBounds(mWindowSelf, &frame_rect);

		DoSetBounds(frame_rect);
	}

// ---------------------------------------------------------------------------
//		₯ CTearOffWindow destructor
// ---------------------------------------------------------------------------
// Pass on to the manager
void CTearOffWindow::ListenToMessage(MessageT inMessage, void *ioParam)
{
	if ( inMessage == CToolbarModeManager::msg_ToolbarModeChange )
		HandleModeChange((Int8)ioParam);
	else
		CTearOffManager::GetDefaultManager()->WindowReceivedMessage(this, inMessage, ioParam);
}

/*****************************************************************************
 * class CTearOffManager
 *****************************************************************************/
#pragma mark -
#pragma mark CTearOffManager
#pragma mark -
CTearOffManager *  CTearOffManager::sDefaultFloatManager = NULL;

#pragma mark struct ButtonBarRecord
// holds information about a single button bar, stored in fPaletteBars list
struct ButtonBarRecord
{
	ResIDT				fID;	// ResID of the PPob loaded into the button bar.
	ResIDT				fWindowID;	// ResID of the PPob loaded into the floating window.
	CTearOffBar *	fBar;	// Pointer to the bar
	LListener * 		fListener;	// The listener for this bar
		
	ButtonBarRecord()
	{
		fID = -1;
		fWindowID = -1;
		fBar = NULL;
		fListener = NULL;
	}
	
	ButtonBarRecord(ResIDT id, ResIDT wID, CTearOffBar * bar, LListener * listener)
	{
		fID = id;
		fWindowID = wID;
		fBar = bar;
		fListener = listener;
	}
};

/****************************************************************************
 * struct FloatWindowRecord
 * used to keep a list of loaded windows
 ****************************************************************************/
#pragma mark struct FloatWindowRecord
struct FloatWindowRecord
{
	CTearOffWindow * fWindow;
	ResIDT				fID;		// Res ID of the PPob to be loaeded
	CTearOffBar * fActiveBar;

	FloatWindowRecord(CTearOffWindow * window, ResIDT resID, CTearOffBar * activeBar = NULL)
	{
		fWindow = window;
		fID = resID;
		fActiveBar = activeBar;
	}

};

// ---------------------------------------------------------------------------
//		₯ Compare the window records
// ---------------------------------------------------------------------------
// Wildcards can be used for fID and fWindow fields
// fListener field is not used.

#define WILDCARD -1

Int32 CTearOffManager::CPaletteWindowComparator::Compare(const void*		inItemOne,
									const void*		inItemTwo,
									Uint32			inSizeOne,
									Uint32			inSizeTwo) const
{
#pragma unused (inSizeOne, inSizeTwo)	// Cause we know what the items are

	FloatWindowRecord * item1 = (FloatWindowRecord *)inItemOne;
	FloatWindowRecord * item2 = (FloatWindowRecord *)inItemTwo;

	if ( item1->fID != item2->fID &&	// Different floaters
		 (item1->fID != WILDCARD) &&
		 (item2->fID != WILDCARD))	
		return (item1->fWindow - item2->fWindow);
	else							// Same floaters
		if ( item1->fWindow == item2->fWindow ||
			( item1->fWindow == (void*)WILDCARD ) ||
			( item2->fWindow == (void*)WILDCARD ) )
			return 0;
		else
			return item1->fWindow - item2->fWindow;
}

// ---------------------------------------------------------------------------
//		₯ Compare the bars
// ---------------------------------------------------------------------------
// Wildcards can be used for fID and fBar fields
// fListener field is not used.

Int32 CTearOffManager::CPaletteBarComparator::Compare(const void*		inItemOne,
									const void*		inItemTwo,
									Uint32			inSizeOne,
									Uint32			inSizeTwo) const
{
#pragma unused (inSizeOne, inSizeTwo)	// Cause we know what the items are

	ButtonBarRecord * item1 = (ButtonBarRecord *)inItemOne;
	ButtonBarRecord * item2 = (ButtonBarRecord *)inItemTwo;


	if ( item1->fID != item2->fID &&	// Different IDs
		 (item1->fID != WILDCARD) &&
		 (item2->fID != WILDCARD) )	
		return ( item1->fBar - item2->fBar );
	if (item1->fWindowID != item2->fWindowID &&	// Different floaters
		 (item1->fWindowID != WILDCARD) &&
		 (item2->fWindowID != WILDCARD) )
		return ( item1->fBar - item2->fBar );
	else							// Same floaters
		if ( item1->fBar == item2->fBar ||
			( item1->fBar == (void*)WILDCARD ) ||
			( item2->fBar == (void*)WILDCARD ) )
			return 0;
		else
			return item1->fBar - item2->fBar;
	return 0;	// Kill the warning
}

// ---------------------------------------------------------------------------
//		₯ CTearOffManager Constructor 
// ---------------------------------------------------------------------------
// sets the default manager

CTearOffManager::CTearOffManager(LCommander * defaultCommander)
{
	sDefaultFloatManager = this;
	
	// These lists cannot be sorted, because sometimes we do wildcard lookups
	// If you want to sort them, implement CompareKey better
	fPaletteBars = new LArray( sizeof (ButtonBarRecord), &fBarCompare, false );
	fPaletteWindows = new LArray( sizeof (FloatWindowRecord), &fWindowCompare, false );

	fDefaultCommander = defaultCommander;
}

// ---------------------------------------------------------------------------
//		₯ CTearOffManager destructor -- never called, here for completeness
// ---------------------------------------------------------------------------
// clean up
CTearOffManager::~CTearOffManager()
{
	delete fPaletteBars;
	delete fPaletteWindows;
}


void CTearOffManager::ListenToMessage(MessageT inMessage, void *ioParam)
{
	BroadcastMessage(inMessage, ioParam);
}


// ---------------------------------------------------------------------------
//		₯ RegisterBar 
// ---------------------------------------------------------------------------
// Insert the bar in the bar list. Hide or show it, depending on the window

void CTearOffManager::RegisterBar(CTearOffBar * inBar)
{
	// Insert it into the list
	ButtonBarRecord barRec( inBar->fInPlaceSubpaneID, inBar->fFloatingSubpaneID, inBar, NULL);
	fPaletteBars->InsertItemsAt(1, fPaletteBars->GetCount(), &barRec, sizeof( barRec ) );
	
	// Decide whether to hide or show the bar

	FloatWindowRecord fr( (CTearOffWindow *)WILDCARD, inBar->fFloatingSubpaneID );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fr );
	if ( where != LArray::index_Bad )
		inBar->HidePalette();
	else
		inBar->ShowPalette();
}

// ---------------------------------------------------------------------------
//		₯ UnregisterBar 
// ---------------------------------------------------------------------------
// Remove the bar from the bar list
// If there are any "left-over" floaters, remove them

void CTearOffManager::UnregisterBar(CTearOffBar * inBar)
{
	ButtonBarRecord bar( WILDCARD, WILDCARD, inBar, NULL);
	ArrayIndexT where = fPaletteBars->FetchIndexOfKey(&bar);
	if ( where != LArray::index_Bad )
		fPaletteBars->RemoveItemsAt( 1, where );
	else
		SignalCStr_("Unregistering a bar that was never registered?");

// If there are no other bars with the same ID,
// remove the floater window (floater might not exist)

	ButtonBarRecord brotherBar( inBar->fInPlaceSubpaneID, inBar->fFloatingSubpaneID,
								(CTearOffBar *)WILDCARD, NULL );
	
	where = fPaletteBars->FetchIndexOfKey(&brotherBar);
	if ( where == LArray::index_Bad )
	// Remove the floaters with this ID
	{
		FloatWindowRecord fw( (CTearOffWindow*) WILDCARD, inBar->fFloatingSubpaneID );
		ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );
		if ( where != LArray::index_Bad )
		{
			fPaletteWindows->FetchItemAt( where, &fw );
			if (fw.fWindow->fVisibility != 0)
				delete fw.fWindow;	// this will automatically remove it from the list
		}
	}
}
	
// ---------------------------------------------------------------------------
//		₯ RegisterListener 
// ---------------------------------------------------------------------------
// Set the listener
// Broadcast the palette message to the window, depending on where the palette is

void CTearOffManager::RegisterListener(CTearOffBar * inBar, 
											LListener * listener)
{
	ButtonBarRecord bar( WILDCARD, WILDCARD, inBar, listener );
	ArrayIndexT where = fPaletteBars->FetchIndexOfKey(&bar);
	if (where != LArray::index_Bad)
	{
	// assign the listener
		fPaletteBars->FetchItemAt( where, &bar );
		bar.fListener = listener;
		fPaletteBars->AssignItemsAt( 1, where , &bar );
	
	// broadcast the palette message to the listener
		FloatWindowRecord fr( (CTearOffWindow *)WILDCARD, inBar->fFloatingSubpaneID );
		ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fr );
		
		if ( where != LArray::index_Bad )
		{
		// We have a window to register
			fPaletteWindows->FetchItemAt( where, &fr );
			SetBarsView( inBar, fr.fWindow );
		}
		else
		// The bar holds the palette
		{
			SetBarsView( inBar, inBar );
		}
	}
	else
		SignalCStr_("Bar never registered?");
}
	
// ---------------------------------------------------------------------------
//		₯ BarReceivedMessage 
// ---------------------------------------------------------------------------
// Relay the message to its listeners

void CTearOffManager::BarReceivedMessage(CTearOffBar * inBar,
											MessageT inMessage,
											 void *ioParam)
{
	ButtonBarRecord bar( WILDCARD, WILDCARD, inBar, NULL );
	ArrayIndexT where = fPaletteBars->FetchIndexOfKey(&bar);
	
	if ( where != LArray::index_Bad )
	{
		fPaletteBars->FetchItemAt( where, &bar );
//		SignalIfNot_(bar.fListener);
		if (bar.fListener)
			bar.fListener->ListenToMessage( inMessage, ioParam );
	}
	else
		SignalCStr_("Bar never registered?");
}

// ---------------------------------------------------------------------------
//		₯ DragBar 
// ---------------------------------------------------------------------------
// Assert that the window exists
// move it to the new location

void CTearOffManager::DragBar( CTearOffBar * inBar,
								Int16 inH,
								Int16 inV,
								Boolean usePosition )
{
	try
	{
		CTearOffWindow * win = AssertBarWindowExists(inBar);

#if SAVE_POSITION
	if (usePosition) {
#endif
		Point topLeft;
		topLeft.h = inH;
		topLeft.v = inV;
		win->DoSetPosition(topLeft);
#if SAVE_POSITION
		}
#endif
		win->Show();
		
		SetBarsView( inBar, win );
	}
	catch(OSErr inErr)
	{
		SignalCStr_("Did you include UTearOffPalette.r in your project?");
		// No window, button bar stays where it was
	}
}

// ---------------------------------------------------------------------------
//		₯ BarActivated 
// ---------------------------------------------------------------------------
// Upon activation:
// Tell its float windowGets the floater window for the bar
// Tells listener what the current palette is
// 
void CTearOffManager::BarActivated( CTearOffBar * inBar)
{
// Find the floater window
	FloatWindowRecord fw( (CTearOffWindow *) WILDCARD, inBar->fFloatingSubpaneID );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );

// If we have a window, make this bar the active one
	if ( where != LArray::index_Bad )
	{
		fPaletteWindows->FetchItemAt( where, &fw );
		if ( fw.fActiveBar != inBar )
		{
			if ( fw.fActiveBar == NULL )	// If there was no view, we need to enable us
			{
				LPane * view;
				LArrayIterator iter( fw.fWindow->GetSubPanes());
				if ( iter.Next(&view) )	// We have disabled this on suspends
					view->Enable();
			}
			
			fw.fActiveBar = inBar;
			fPaletteWindows->AssignItemsAt( 1, where , &fw );
			fw.fWindow->Show();
			

			SetBarsView(inBar, fw.fWindow);
			
		}
	}
}

// ---------------------------------------------------------------------------
//		₯ BarDeactivated 
// ---------------------------------------------------------------------------
// Upon deactivation
// If we have a floating window, make it inactive and tell listener about it

void CTearOffManager::BarDeactivated( CTearOffBar * inBar)
{
	FloatWindowRecord fw( (CTearOffWindow *) WILDCARD, inBar->fFloatingSubpaneID );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );

	if ( where != LArray::index_Bad )
	{
		fPaletteWindows->FetchItemAt( where, &fw );
		if ( fw.fActiveBar == inBar )
		{
			if ( fw.fWindow->fVisibility == 2 )
				fw.fWindow->Hide();
			else
			{
				if (inBar->DisablePaletteWindow())
				{
					// Disable all the subpanes of our main subpane
					// Do not disable the main pane, because no one enables it
					LPane * view;
					LArrayIterator iter( fw.fWindow->GetSubPanes());
					if ( iter.Next(&view) )
						view->Disable();
				}
			}
			fw.fActiveBar = NULL;
			fPaletteWindows->AssignItemsAt( 1, where , &fw );
			RemoveBarsView(inBar, inBar->fTransparentPane);
		}
	}
}

// ---------------------------------------------------------------------------
//		₯ SetBarsView 
// ---------------------------------------------------------------------------
// Tell listener what its palette view is
// InView is either FBar, or FWindow. In either case, the palette is the first subview

void CTearOffManager::SetBarsView( CTearOffBar * inBar,
										LView * inView)
{
	SignalIfNot_( inView );

	// Get the bar record
	ButtonBarRecord br( WILDCARD, WILDCARD, inBar, (LListener*) WILDCARD );
	ArrayIndexT where = fPaletteBars->FetchIndexOfKey( &br );

	if ( where != LArray::index_Bad )
	{
		fPaletteBars->FetchItemAt( where, &br );
		if ( br.fListener )
		{
			// Palette is the first subview
			LView * topPaletteView;
			LArrayIterator iter( inView->GetSubPanes());
			if ( iter.Next(&topPaletteView) )
				br.fListener->ListenToMessage( msg_SetTearOffPalette, topPaletteView );
		}
	}
	else
		SignalCStr_("Where is the bar?");			
}

// ---------------------------------------------------------------------------
//		₯ RemoveBarsView 
// ---------------------------------------------------------------------------
// Similar to SetBarsView. Must get the top pane ID from somewhere 

void CTearOffManager::RemoveBarsView( CTearOffBar * inBar, PaneIDT inViewID)
{
	ButtonBarRecord br( WILDCARD, WILDCARD, inBar, (LListener*) WILDCARD );
	ArrayIndexT where = fPaletteBars->FetchIndexOfKey( &br );

	if ( where != LArray::index_Bad )
	{
		fPaletteBars->FetchItemAt( where, &br );
		if ( br.fListener )
			br.fListener->ListenToMessage( msg_RemoveTearOffPalette, (void*) inViewID );
	}
	else
		SignalCStr_("Where is the bar?");		
}

// ---------------------------------------------------------------------------
//		₯ AssertBarWindowExists 
// ---------------------------------------------------------------------------
// Create the window if it does not exist

CTearOffWindow * CTearOffManager::AssertBarWindowExists(CTearOffBar * inBar)
{
	return AssertBarWindowExists(
		inBar->fFloatingSubpaneID,
		inBar->IsFloaterOnSide(),
		inBar->fVisibility,
		inBar->fTitle
		);
}

// ---------------------------------------------------------------------------
//		₯ AssertBarWindowExists 
// ---------------------------------------------------------------------------
// Create the window if it does not exist

CTearOffWindow * CTearOffManager::AssertBarWindowExists(ResIDT inWinID,
														Boolean inSideways,
														UInt8 visibility,
														Str255 &title)
{
	FloatWindowRecord fw( (CTearOffWindow *) WILDCARD, inWinID );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );
	CTearOffWindow * newWindow = nil;
	if (where == LArray::index_Bad)
	{
	// Create a new window, load the palette into it
		ResIDT windowResID = inSideways ? SIDE_FLOAT_ID : FLOAT_ID;

		newWindow = (CTearOffWindow *) LWindow::CreateWindow(windowResID, fDefaultCommander);

		ThrowIfNil_(newWindow);
		newWindow->fVisibility = visibility;

		LView * view = UReanimator::CreateView(inWinID, newWindow, newWindow);
		ThrowIfNil_( view );
		view->FinishCreate();

		UReanimator::LinkListenerToControls(CTearOffManager::GetDefaultManager(),
											view, inWinID);
	// Resize the window
		Point corner = {0,0};
#if SAVE_POSITION
		newWindow->PortToGlobalPoint(corner);
#endif		
		SDimension16 viewSize;
		Rect winBounds;
		view->GetFrameSize(viewSize);
		::SetRect(&winBounds,
				corner.h, corner.v,
				corner.h + viewSize.width, corner.v + viewSize.height);
		newWindow->DoSetBounds( winBounds );
		newWindow->SetDescriptor(title);

		SBooleanRect boundToSuperview={ true, true, true, true };
		view->SetFrameBinding(boundToSuperview);

		Int32 toolbarStyle;
		if ( (PREF_GetIntPref("browser.chrome.toolbar_style", &toolbarStyle) == PREF_NOERROR)
			&& (toolbarStyle != CToolbarModeManager::defaultToolbarMode) )
			{
				((CTearOffWindow*)newWindow)->HandleModeChange(toolbarStyle);
			}

		XP_Bool is_vertical = true;
		if ( (PREF_GetBoolPref("taskbar.mac.is_vertical", &is_vertical) == PREF_NOERROR)
			&& !is_vertical )
			{
				((CTearOffWindow*)newWindow)->DoSetZoom(Boolean());
			}

	// Add the record to the list of managed floaters
		FloatWindowRecord fw(newWindow, inWinID, NULL);
		fPaletteWindows->InsertItemsAt( 1, fPaletteWindows->GetCount(), &fw, sizeof (fw) );

	// Hide the bars button palletes
		LArrayIterator iter(*fPaletteBars);
		ButtonBarRecord bar;
		while ( iter.Next(&bar) )
			if (bar.fWindowID == fw.fID)
			{
				bar.fBar->HidePalette();
				bar.fBar->fTransparentPane = view->GetPaneID();
				RemoveBarsView( bar.fBar, bar.fBar->fTransparentPane );
			}
	// Link up the new controls
		UReanimator::LinkListenerToControls( newWindow, newWindow, fw.fID );
	}
	else
	{
		fPaletteWindows->FetchItemAt( where, &fw );
		return fw.fWindow;
	}
	return newWindow;
} // CTearOffManager::AssertBarWindowExists

// ---------------------------------------------------------------------------
//		₯ UnregisterWindow 
// ---------------------------------------------------------------------------
// Remove the window from the list
// Show all the floaters

void CTearOffManager::UnregisterWindow(CTearOffWindow * window)
{
	// Remove the window from the list
	FloatWindowRecord fw( window, WILDCARD );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );

	if ( where != LArray::index_Bad )
	{
		fPaletteWindows->FetchItemAt( where, &fw );
		fPaletteWindows->RemoveItemsAt( 1, where );
	}
	else
		SignalCStr_("Removal of unregistered window?");
	
	// Tell all button bars to show themselves
	
	LArrayIterator iter(*fPaletteBars);
	ButtonBarRecord bar;
	while ( iter.Next(&bar) )
		if (bar.fWindowID == fw.fID)
			{
				bar.fBar->ShowPalette();
				SetBarsView(bar.fBar, bar.fBar);
			}
}

// ---------------------------------------------------------------------------
//		₯ CloseFloatingWindow 
// ---------------------------------------------------------------------------
// Closes the Floating Window

void CTearOffManager::CloseFloatingWindow(ResIDT inWinID)
{
	// find the window in the list
	FloatWindowRecord fw((CTearOffWindow *)WILDCARD, inWinID, (CTearOffBar *)WILDCARD );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );

	if ( where != LArray::index_Bad )
	{
		fPaletteWindows->FetchItemAt( where, &fw );
		fw.fWindow->ProcessCommand(cmd_Close);
	}
	else
	{
		SignalCStr_("close of unregistered window?");
	}
}

// ---------------------------------------------------------------------------
//		₯ OpenFloatingWindow 
// ---------------------------------------------------------------------------
// Opens the Floating Window

void CTearOffManager::OpenFloatingWindow(	ResIDT inWinID,
											Point inTopLeft,
											Boolean inSideways,
											UInt8 visibility,
											Str255 &title)
{
	// find the window in the list
	FloatWindowRecord fw((CTearOffWindow *)WILDCARD, inWinID, (CTearOffBar *)WILDCARD );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );

	if (LArray::index_Bad == where)
	{
		try
		{
			CTearOffWindow * win = AssertBarWindowExists(inWinID, inSideways, visibility, title);

#if !SAVE_POSITION
			win->DoSetPosition(inTopLeft);
#endif
			win->Show();
		}
		catch(OSErr inErr)
		{
			SignalCStr_("Did you include UTearOffPalette.r in your project?");
			// No window, button bar stays where it was
		}
	}
	else
	{
		SignalCStr_("open of open window?");
	}
}


// ---------------------------------------------------------------------------
//		₯ IsFloatingWindow 
// ---------------------------------------------------------------------------
// Closes the Floating Window

Boolean CTearOffManager::IsFloatingWindow(ResIDT inWinID)
{
	// find the window in the list
	FloatWindowRecord fw((CTearOffWindow *)WILDCARD, inWinID, (CTearOffBar *)WILDCARD );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );

	return where != LArray::index_Bad;
}

// ---------------------------------------------------------------------------
//		₯ WindowReceivedMessage 
// ---------------------------------------------------------------------------
// Rebroadcast the message to the listener of the currently active bar
void CTearOffManager::WindowReceivedMessage(CTearOffWindow * inWin,
											MessageT inMessage,
											 void *ioParam)
{
	// Get the window record
	FloatWindowRecord fw( inWin, WILDCARD );
	ArrayIndexT where = fPaletteWindows->FetchIndexOfKey( &fw );

	if ( where != LArray::index_Bad )
	{
		fPaletteWindows->FetchItemAt( where, &fw );
		
		// If there is an active bar attached to this window
		// rebroadcast the message to its listener

		if (fw.fActiveBar)
		{
			ButtonBarRecord bar( WILDCARD, WILDCARD, fw.fActiveBar , NULL );
			where = fPaletteBars->FetchIndexOfKey(&bar);
			if ( where != LArray::index_Bad )
			{
				fPaletteBars->FetchItemAt( where, &bar );
				if ( bar.fListener != NULL )
					bar.fListener->ListenToMessage( inMessage, ioParam );
			}
		}
		
	}
	else
		SignalCStr_("Message from a window we do not know about?");
}

#pragma mark -
// ---------------------------------------------------------------------------
//		₯ InitUTearOffPalette [static]
// ---------------------------------------------------------------------------
// Registers classes, creates a single float manager
void InitUTearOffPalette(LCommander * defCommander)
{
	RegisterClass_( CTearOffBar);
	RegisterClass_( CTearOffWindow);
	new CTearOffManager(defCommander);
}

