/** 
 * @file llfloater.h
 * @brief LLFloater base class
 *
 * $LicenseInfo:firstyear=2002&license=viewergpl$
 * 
 * Copyright (c) 2002-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

// Floating "windows" within the GL display, like the inventory floater,
// mini-map floater, etc.


#ifndef LL_FLOATER_H
#define LL_FLOATER_H

#include "llpanel.h"
#include "lluuid.h"
#include "lltabcontainer.h"
#include "llnotifications.h"
#include <set>

class LLDragHandle;
class LLResizeHandle;
class LLResizeBar;
class LLButton;
class LLMultiFloater;
class LLFloater;

const S32 LLFLOATER_VPAD = 6;
const S32 LLFLOATER_HPAD = 6;
const S32 LLFLOATER_CLOSE_BOX_SIZE = 16;
const S32 LLFLOATER_HEADER_SIZE = 18;

const BOOL RESIZE_YES = TRUE;
const BOOL RESIZE_NO = FALSE;

const S32 DEFAULT_MIN_WIDTH = 100;
const S32 DEFAULT_MIN_HEIGHT = 100;

const BOOL DRAG_ON_TOP = FALSE;
const BOOL DRAG_ON_LEFT = TRUE;

const BOOL MINIMIZE_YES = TRUE;
const BOOL MINIMIZE_NO = FALSE;

const BOOL CLOSE_YES = TRUE;
const BOOL CLOSE_NO = FALSE;

const BOOL ADJUST_VERTICAL_YES = TRUE;
const BOOL ADJUST_VERTICAL_NO = FALSE;

// associates a given notification instance with a particular floater
class LLFloaterNotificationContext : 
	public LLNotificationContext
{
public:
	LLFloaterNotificationContext(LLHandle<LLFloater> floater_handle) :
		mFloaterHandle(floater_handle)
	{}

	LLFloater* getFloater() { return mFloaterHandle.get(); }
private:
	LLHandle<LLFloater> mFloaterHandle;
};


class LLFloater : public LLPanel
{
	friend class LLFloaterView;
	friend class LLMultiFloater;
public:
	enum EFloaterButtons
	{
		BUTTON_CLOSE,
		BUTTON_RESTORE,
		BUTTON_MINIMIZE,
		BUTTON_TEAR_OFF,
		BUTTON_EDIT,
		BUTTON_COUNT
	};
	
	LLFloater();
 	LLFloater(const std::string& name); //simple constructor for data-driven initialization
	LLFloater(	const std::string& name, const LLRect& rect, const std::string& title,
		BOOL resizable = FALSE,
		S32 min_width = DEFAULT_MIN_WIDTH,
		S32 min_height = DEFAULT_MIN_HEIGHT,
		BOOL drag_on_left = FALSE,
		BOOL minimizable = TRUE,
		BOOL close_btn = TRUE,
		BOOL bordered = BORDER_NO);

	LLFloater(	const std::string& name, const std::string& rect_control, const std::string& title,
		BOOL resizable = FALSE,
		S32 min_width = DEFAULT_MIN_WIDTH, 
		S32 min_height = DEFAULT_MIN_HEIGHT,
		BOOL drag_on_left = FALSE,
		BOOL minimizable = TRUE,
		BOOL close_btn = TRUE,
		BOOL bordered = BORDER_NO);

	virtual ~LLFloater();

	virtual LLXMLNodePtr getXML(bool save_children = true) const;
	static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
	void initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory, BOOL open = TRUE);

	/*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false);
	/*virtual*/ BOOL canSnapTo(const LLView* other_view);
	/*virtual*/ void setSnappedTo(const LLView* snap_view);
	/*virtual*/ void setFocus( BOOL b );
	/*virtual*/ void setIsChrome(BOOL is_chrome);

	// Can be called multiple times to reset floater parameters.
	// Deletes all children of the floater.
	virtual void		initFloater(const std::string& title, BOOL resizable, 
						S32 min_width, S32 min_height, BOOL drag_on_left,
						BOOL minimizable, BOOL close_btn);

	virtual void	open();	/* Flawfinder: ignore */

	// If allowed, close the floater cleanly, releasing focus.
	// app_quitting is passed to onClose() below.
	virtual void	close(bool app_quitting = false);

	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
	
	// Release keyboard and mouse focus
	void			releaseFocus();

	// moves to center of gFloaterView
	void			center();
	// applies rectangle stored in mRectControl, if any
	void			applyRectControl();


	LLMultiFloater* getHost() { return (LLMultiFloater*)mHostHandle.get(); }

	void			applyTitle();
	const std::string&	getCurrentTitle() const;
	void			setTitle( const std::string& title);
	std::string		getTitle();
	void			setShortTitle( const std::string& short_title );
	std::string		getShortTitle();
	void			setTitleVisible(bool visible);
	virtual void	setMinimized(BOOL b);
	void			moveResizeHandlesToFront();
	void			addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE);
	void			addDependentFloater(LLHandle<LLFloater> dependent_handle, BOOL reposition = TRUE);
	LLFloater*		getDependee() { return (LLFloater*)mDependeeHandle.get(); }
	void		removeDependentFloater(LLFloater* dependent);
	BOOL			isMinimized()					{ return mMinimized; }
	BOOL			isFrontmost();
	BOOL			isDependent()					{ return !mDependeeHandle.isDead(); }
	void			setCanMinimize(BOOL can_minimize);
	void			setCanClose(BOOL can_close);
	void			setCanTearOff(BOOL can_tear_off);
	virtual void	setCanResize(BOOL can_resize);
	void			setCanDrag(BOOL can_drag);
	void			setHost(LLMultiFloater* host);
	BOOL			isResizable() const				{ return mResizable; }
	void			setResizeLimits( S32 min_width, S32 min_height );
	void			getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; }

	bool			isMinimizeable() const{ return mButtonsEnabled[BUTTON_MINIMIZE]; }
	// Does this window have a close button, NOT can we close it right now.
	bool			isCloseable() const{ return (mButtonsEnabled[BUTTON_CLOSE]); }
	bool			isDragOnLeft() const{ return mDragOnLeft; }
	S32				getMinWidth() const{ return mMinWidth; }
	S32				getMinHeight() const{ return mMinHeight; }

	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
	virtual BOOL	handleRightMouseDown(S32 x, S32 y, MASK mask);
	virtual BOOL	handleDoubleClick(S32 x, S32 y, MASK mask);
	virtual BOOL	handleMiddleMouseDown(S32 x, S32 y, MASK mask);
	virtual void	draw();

	virtual void	onOpen() {}

	// Call destroy() to free memory, or setVisible(FALSE) to keep it
	// If app_quitting, you might not want to save your visibility.
	// Defaults to destroy().
	virtual void	onClose(bool app_quitting) { destroy(); }

	// This cannot be "const" until all derived floater canClose()
	// methods are const as well.  JC
	virtual BOOL	canClose() { return TRUE; }

	virtual void	setVisible(BOOL visible);
	void			setFrontmost(BOOL take_focus = TRUE);

	// Defaults to false.
	virtual BOOL	canSaveAs() const { return FALSE; }

	virtual void	saveAs() {}
	
	// <edit>
	virtual LLUUID  getItemID() { return LLUUID::null; }
	// </edit>

	void			setSnapTarget(LLHandle<LLFloater> handle) { mSnappedTo = handle; }
	void			clearSnapTarget() { mSnappedTo.markDead(); }
	LLHandle<LLFloater>	getSnapTarget() const { return mSnappedTo; }

	LLHandle<LLFloater> getHandle() const { return mHandle; }

	// Return a closeable floater, if any, given the current focus.
	static LLFloater* getClosableFloaterFromFocus(); 

	// Close the floater returned by getClosableFloaterFromFocus() and 
	// handle refocusing.
	static void		closeFocusedFloater();

	LLNotification::Params contextualNotification(const std::string& name) 
	{ 
	    return LLNotification::Params(name).context(mNotificationContext); 
	}

	static void		onClickClose(void *userdata);
	static void		onClickMinimize(void *userdata);
	static void		onClickTearOff(void *userdata);
	static void		onClickEdit(void *userdata);

	static void		setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; }
	static void		setEditModeEnabled(BOOL enable);
	static BOOL		getEditModeEnabled() { return sEditModeEnabled; }
	static LLMultiFloater*		getFloaterHost() {return sHostp; }

	void			enableResizeCtrls(bool enable, bool width = true, bool height = true);
protected:

	virtual void	bringToFront(S32 x, S32 y);
	virtual void	setVisibleAndFrontmost(BOOL take_focus=TRUE);    
	
	void		setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized
	const LLRect&	getExpandedRect() const { return mExpandedRect; }

	void			setAutoFocus(BOOL focus) { mAutoFocus = focus; } // whether to automatically take focus when opened
	LLDragHandle*	getDragHandle() const { return mDragHandle; }

	void			destroy() { die(); } // Don't call this directly.  You probably want to call close(). JC

private:
	
	void			setForeground(BOOL b);	// called only by floaterview
	void			cleanupHandles(); // remove handles to dead floaters
	void			createMinimizeButton();
	void			updateButtons();
	void			buildButtons();
	BOOL			offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index);

	LLRect			mExpandedRect;
	LLDragHandle*	mDragHandle;
	LLResizeBar*	mResizeBar[4];
	LLResizeHandle*	mResizeHandle[4];
	LLButton		*mMinimizeButton;
	BOOL			mCanTearOff;
	BOOL			mMinimized;
	BOOL			mForeground;
	LLHandle<LLFloater>	mDependeeHandle;
	std::string		mTitle;
	std::string		mShortTitle;

	BOOL			mFirstLook;			// TRUE if the _next_ time this floater is visible will be the first time in the session that it is visible.

	BOOL			mResizable;
	S32				mMinWidth;
	S32				mMinHeight;

	BOOL			mEditing;
	
	typedef std::set<LLHandle<LLFloater> > handle_set_t;
	typedef std::set<LLHandle<LLFloater> >::iterator handle_set_iter_t;
	handle_set_t	mDependents;
	bool			mDragOnLeft;

	BOOL			mButtonsEnabled[BUTTON_COUNT];
protected:
	LLButton*		mButtons[BUTTON_COUNT];
private:
	F32				mButtonScale;
	BOOL			mAutoFocus;
	LLHandle<LLFloater> mSnappedTo;
	
	LLHandle<LLFloater> mHostHandle;
	LLHandle<LLFloater> mLastHostHandle;

	static LLMultiFloater* sHostp;
	static BOOL		sEditModeEnabled;
	static std::string	sButtonActiveImageNames[BUTTON_COUNT];
	static std::string	sButtonInactiveImageNames[BUTTON_COUNT];
	static std::string	sButtonPressedImageNames[BUTTON_COUNT];
	static std::string	sButtonNames[BUTTON_COUNT];
	static std::string	sButtonToolTips[BUTTON_COUNT];
	typedef void (*click_callback)(void *);
	static click_callback sButtonCallbacks[BUTTON_COUNT];

	typedef std::map<LLHandle<LLFloater>, LLFloater*> handle_map_t;
	typedef std::map<LLHandle<LLFloater>, LLFloater*>::iterator handle_map_iter_t;
	static handle_map_t	sFloaterMap;

	std::vector<LLHandle<LLView> > mMinimizedHiddenChildren;

	BOOL			mHasBeenDraggedWhileMinimized;
	S32				mPreviousMinimizedBottom;
	S32				mPreviousMinimizedLeft;
	
	LLFloaterNotificationContext* mNotificationContext;
	LLRootHandle<LLFloater>		mHandle;	
};

/////////////////////////////////////////////////////////////
// LLFloaterView
// Parent of all floating panels

class LLFloaterView : public LLUICtrl
{
public:
	LLFloaterView( const std::string& name, const LLRect& rect );

	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
	void reshapeFloater(S32 width, S32 height, BOOL called_from_parent, BOOL adjust_vertical);

	/*virtual*/ void draw();
	/*virtual*/ LLRect getSnapRect() const;
	void refresh();

	void			getNewFloaterPosition( S32* left, S32* top );
	void			resetStartingFloaterPosition();
	LLRect			findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor );

	// Given a child of gFloaterView, make sure this view can fit entirely onscreen.
	void			adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside);

	void			getMinimizePosition( S32 *left, S32 *bottom);
	void			restoreAll();		// un-minimize all floaters
	typedef std::set<LLView*> skip_list_t;
	void pushVisibleAll(BOOL visible, const skip_list_t& skip_list = skip_list_t());
	void popVisibleAll(const skip_list_t& skip_list = skip_list_t());

	void			setCycleMode(BOOL mode) { mFocusCycleMode = mode; }
	BOOL			getCycleMode() const { return mFocusCycleMode; }
	void			bringToFront( LLFloater* child, BOOL give_focus = TRUE );
	void			highlightFocusedFloater();
	void			unhighlightFocusedFloater();
	void			focusFrontFloater();
	void			destroyAllChildren();
	// attempt to close all floaters
	void			closeAllChildren(bool app_quitting);
	BOOL			allChildrenClosed();
	// <edit>
	void			minimizeAllChildren();
	// </edit>

	LLFloater* getFrontmost();
	LLFloater* getBackmost();
	LLFloater* getParentFloater(LLView* viewp);
	LLFloater* getFocusedFloater();
	void		syncFloaterTabOrder();

	// Returns z order of child provided. 0 is closest, larger numbers
	// are deeper in the screen. If there is no such child, the return
	// value is not defined.
	S32 getZOrder(LLFloater* child);

	void setSnapOffsetBottom(S32 offset) { mSnapOffsetBottom = offset; }

private:
	S32				mColumn;
	S32				mNextLeft;
	S32				mNextTop;
	BOOL			mFocusCycleMode;
	S32				mSnapOffsetBottom;
};

// visibility policy specialized for floaters
template<>
class VisibilityPolicy<LLFloater>
{
public:
	// visibility methods
	static bool visible(LLFloater* instance, const LLSD& key)
	{
		if (instance) 
		{
			return !instance->isMinimized() && instance->isInVisibleChain();
		}
		return FALSE;
	}

	static void show(LLFloater* instance, const LLSD& key);

	static void hide(LLFloater* instance, const LLSD& key)
	{
		if (instance) instance->close();
	}
};


// singleton implementation for floaters (provides visibility policy)
// https://wiki.lindenlab.com/mediawiki/index.php?title=LLFloaterSingleton&oldid=164990

template <class T> class LLFloaterSingleton : public LLUISingleton<T, VisibilityPolicy<LLFloater> >
{
};

extern LLFloaterView* gFloaterView;

#endif  // LL_FLOATER_H



