////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// 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, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//////////////////////////////////////////////////////////////////////////////
//
// ScrollBar.H
// 
// History:
//		01/14/97 JMI	Started tracking history of this file.
//							Added UpdatePosCall and m_upcUser.
//							Also, range macros now consider m_lMinPos for other than
//							0.
//
//		01/15/97	JMI	Added overrides of base class's Save/LoadChildren() to
//							implement special cases for m_btnThumb, Up, & Down.
//
//		01/18/97	JMI	Converted Do() to take an RInputEvent* instead of a 
//							long*.
//
//		01/21/97	JMI	Added ReadMembers() and WriteMembers() overloads to read
//							and write members of this class.
//
//		01/26/97	JMI	Altered static HotCall to accept an RHot* instead of a
//							uint32_t 'as per' new RHot callbacks.
//
//		01/30/97	JMI	Took Do() out of here and put in scrollbar.cpp.
//							Fixed rounding error in POS2PIXEL().
//
//		02/05/97	JMI	Now has repeatability on up/left button, down/right
//							button, and tray presses.  To do this in a consistent
//							way from the Do(), the up and down buttons had to be
//							changed to being hooked at the HotCall() level (like
//							the thumb has always been done).
//							This removed the need for static versions of the Up/
//							DownBtnCall()s.
//							Also, moved instantiable Up/DownBtnCall definitions to
//							the CPP.
//
//		03/19/97	JMI	Converted to using the RHot::m_iecUser (was using
//							RHot::m_epcUser) so HotCall and CursorEvent now take
//							RInputEvent ptrs.
//
//		04/03/97	JMI	Added components for optional timed, smooth scrolling.
//							This included overriding OnLoseFocus().
//
//		06/27/97	JMI	Moved GetTray() definition from here to scrollbar.cpp.
//							SET_IF_NOT_NULL returned the expression even if the
//							passed ptr was NULL.  This would be okay except for
//							the usage in GetTray() included the very same ptr passed
//							and deferenced as part of the expression.
//
//		09/22/97	JMI	Also, added friend class CScrollBarPropPage for GUI 
//							editor.
//
//////////////////////////////////////////////////////////////////////////////
//
// See CPP for description.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef SCROLLBAR_H
#define SCROLLBAR_H

//////////////////////////////////////////////////////////////////////////////
// Headers.
//////////////////////////////////////////////////////////////////////////////

#include "System.h"
// If PATHS_IN_INCLUDES macro is defined, we can utilized relative
// paths to a header file.  In this case we generally go off of our
// RSPiX root directory.  System.h MUST be included before this macro
// is evaluated.  System.h is the header that, based on the current
// platform (or more so in this case on the compiler), defines 
// PATHS_IN_INCLUDES.  Blue.h includes system.h so you can include that
// instead.
#ifdef PATHS_IN_INCLUDES
	#include "ORANGE/GUI/btn.h"
#else
	#include "Btn.h"
#endif // PATHS_IN_INCLUDES

//////////////////////////////////////////////////////////////////////////////
// Macros.
//////////////////////////////////////////////////////////////////////////////

// Converts a position value, lPos, to a short pixel position w/i the scroll 
// bar of orientation o.
#define POS2PIXEL(lPos, o)	((short)((m_oOrientation == o) \
												? ((float)(lPos - m_lMinPos) * m_fPos2PixelRatio + 0.5) \
												: 0))

// Converts a position value to a pixel horizontal position w/i the scroll bar.
#define HPOS2PIXEL(lPos)	POS2PIXEL(lPos, Horizontal)

// Converts a position value to a pixel vertical position w/i the scroll bar.
#define VPOS2PIXEL(lPos)	POS2PIXEL(lPos, Vertical)

// Converts a pixel value (relative to Tray) to a long position value.
#define PIXEL2POS(sPix)		((long)((float)(sPix) / m_fPos2PixelRatio) + m_lMinPos)

// Converts one of two pixel values, dependent on orientation o, to a position
// value.
#define PIXELS2POS(sPixX, sPixY)	PIXEL2POS((m_oOrientation == Vertical) \
													? (sPixY) \
													: (sPixX) )

// Sets a value pointed to if ptr is not NULL.
#define SET_IF_NOT_NULL(pval, val)	(((pval) != NULL) \
															? *(pval) = (val) \
															: (0) )


//////////////////////////////////////////////////////////////////////////////
// Typedefs.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
class RScrollBar : public RGuiItem
	{
	public:	// Construction/Destruction.
		// Default constructor.
		RScrollBar(void);
		// Destructor.
		~RScrollBar(void);

//////////////////////////////////////////////////////////////////////////////

	public:
		
		////////////////////////////////////////////////////////////////////////
		// Enums.
		////////////////////////////////////////////////////////////////////////
		typedef enum
			{
			Vertical,		// For a vertical scrollbar.
			Horizontal		// For a horizontal scrollbar.  Very cryptic.
			} Orientation;

		typedef enum
			{
			Instant,			// Snaps to user specified positions.
			Smooth			// Slides to user specified positions.
			} Scrollage;

		////////////////////////////////////////////////////////////////////////
		// Typedefs.
		////////////////////////////////////////////////////////////////////////

		// User callback on change in position.
		typedef void (*UpdatePosCall)(	// Returns nothing.  Called when scroll
													// position is updated by any means.
			RScrollBar* psb);					// this RScrollBar.

	public:	// Methods.

		////////////////////////////////////////////////////////////////////////
		// Methods.
		////////////////////////////////////////////////////////////////////////

		//////////// Overridden ////////////////////////////////////////////////

		// Compose item.
		virtual					// If you override this, call this base if possible.
		void Compose(			// Returns nothing.
			RImage* pim = NULL);	// Dest image, uses m_im if NULL.

		// Cursor event notification.
		// Events in event area.
		virtual						// If you override this, call this base if possible.
		void CursorEvent(			// Returns nothing.
			RInputEvent* pie);	// In:  Most recent user input event.             
										// Out: pie->sUsed = TRUE, if used.

		// Destroys dynamic display data.
		virtual						// If you override this, call this base if possible.
			void Destroy(void)	// Returns nothing.
			{
			// Destroy children we know of.
			m_btnThumb.Destroy();
			m_btnUp.Destroy();
			m_btnDown.Destroy();

			// Destroy self.
			RGuiItem::Destroy();
			}

		// Activate or deactivate mouse reaction.
		virtual					// If you override this, call this base if possible.
		void SetActive(		// Returns nothing.
			int16_t sActive)		// TRUE to make active, FALSE otherwise.
			{ 
			m_btnThumb.SetActive(sActive);
			m_btnUp.SetActive(sActive);
			m_btnDown.SetActive(sActive);
			RGuiItem::SetActive(sActive); 
			}

		// Called by the static implementation of SetFocus() on the item losing
		// the focus.
		// It is okay to call SetFocus() from this function.
		virtual				// If you override this, call this base if possible.
		void OnLoseFocus(void)
			{
			// If scrolling smoothly . . .
			if (m_sInSmoothScroll != FALSE)
				{
				// Jump to destination, ending scrollage.
				SetPos(m_lScrollToPos);
				}

			// Call base class implementation.
			RGuiItem::OnLoseFocus();
			}

		////////////////////////////////////////////////////////////////////////

		// Set the current position of the thumb in the scroll bar.
		// This value will be clipped to m_lMin and m_lMax.
		void SetPos(		// Returns nothing.
			int32_t lPos)		// New position.
			{
			if (lPos > m_lMaxPos)
				{
				m_lCurPos	= m_lMaxPos;
				}
			else
				{
				if (lPos < m_lMinPos)
					{
					m_lCurPos	= m_lMinPos;
					}
				else
					{
					m_lCurPos	= lPos;
					}
				}

			// If we've reached the destination of a smooth scroll . . .
			if (m_lCurPos == m_lScrollToPos)
				{
				m_sInSmoothScroll	= FALSE;
				}

			int16_t	sX, sY, sW, sH;
			GetTray(&sX, &sY, &sW, &sH);

			m_btnThumb.Move(sX + HPOS2PIXEL(m_lCurPos), sY + VPOS2PIXEL(m_lCurPos));

			// If there's a callback . . .
			if (m_upcUser != NULL)
				{
				// Let user know.
				(*m_upcUser)(this);
				}
			}

		// Set the position the thumb will scroll to.
		// This value will be clipped to m_lMin and m_lMax.
		// This GUI must have the focus for correct operation.
		// If this GUI does not have the focus, SetPos() is called.
		void ScrollToPos(		// Returns nothing.
			int32_t lPos)			// In:  New position.
			{
			// If we don't have the focus . . .
			if (ms_pguiFocus != this)
				{
				SetPos(lPos);
				}
			else
				{
				if (lPos > m_lMaxPos)
					{
					m_lScrollToPos	= m_lMaxPos;
					}
				else
					{
					if (lPos < m_lMinPos)
						{
						m_lScrollToPos	= m_lMinPos;
						}
					else
						{
						m_lScrollToPos	= lPos;
						}
					}

				// Start.
				m_sInSmoothScroll	= TRUE;
				// Reset time.
				m_lLastSmoothTime	= rspGetMilliseconds();
				}
			}

		// Sets the range of the scroll bar and sizes & moves the thumb 
		// appropriately.
		void SetRange(
			int32_t lMin, 
			int32_t lMax);

		// Hot call for thumb positioner.
		void ThumbHotCall(		// Returns nothing.
			RInputEvent* pie);	// In:  Most recent user input event.             
										// Out: pie->sUsed = TRUE, if used.

		// Hot call for up arrow.
		void UpHotCall(			// Returns nothing.
			RInputEvent* pie);	// In:  Most recent user input event.             
										// Out: pie->sUsed = TRUE, if used.

		// Hot call for down arrow.
		void DownHotCall(			// Returns nothing.
			RInputEvent* pie);	// In:  Most recent user input event.             
										// Out: pie->sUsed = TRUE, if used.

		// Called when up/left button pressed.
		void UpBtnPressed(void);

		// Called when down/right button pressed.
		void DownBtnPressed(void);

		// Copies border info from this GuiItem to the specified.
		void CopyBorderInfoTo(
			RGuiItem* pgui)		// In: GuiItem to copy to.
			{
			pgui->m_u32BorderColor				= m_u32BorderColor;
			pgui->m_u32BorderHighlightColor	= m_u32BorderHighlightColor;
			pgui->m_u32BorderEdgeColor			= m_u32BorderEdgeColor;
			pgui->m_u32BorderShadowColor		= m_u32BorderShadowColor;
			pgui->m_u32TextColor					= m_u32TextColor;
			pgui->m_u32BackColor					= m_u32BackColor;

//			pgui->m_sBorderThickness			= m_sBorderThickness;
			}

		// Does tasks that require constant update for scroll bar.
		// If called iteratively, the thumb can be dragged on the bar.
		virtual	// If you override this, call this base if possible.
		void Do(						// Returns nothing.
			RInputEvent* pie);	// In:  Most recent user input event.
										// Out: pie->sUsed = TRUE, if used.

		// Draws an appropriate arrow for this button.
		void DrawUpArrow(	// Returns nothing.
			RImage* pim,	// Image to draw into.  Try to stay within 
								// prc please.
			RRect* prc);	// Where to in image.

		// Draws an appropriate arrow for this button.
		void DrawDownArrow(	// Returns nothing.
			RImage* pim,		// Image to draw into.  Try to stay within 
									// prc please.
			RRect* prc);		// Where to in image.

		////////////////////////////////////////////////////////////////////////
		// Querries.
		////////////////////////////////////////////////////////////////////////

		// Get the current position of the thumb in the scroll bar.
		int32_t GetPos(void)
			{ return m_lCurPos; }

		// Gets the range of the scroll bar.
		void GetRange(		// Returns nothing.
			int32_t* plMin,	// Out: Minimum position unless NULL.
			int32_t* plMax)	// Out: Maximum position unless NULL.
			{
			SET_IF_NOT_NULL(plMin, m_lMinPos);
			SET_IF_NOT_NULL(plMax, m_lMaxPos);
			}

		// Get position/size of tray relative to this item.
		void GetTray(	// Returns nothing.
			int16_t* psX,	// Out: x coordinate of tray unless NULL.
			int16_t* psY,	// Out: y coordinate of tray unless NULL.
			int16_t* psW,	// Out: Width of tray unless NULL.
			int16_t* psH);	// Out: Height of tray unless NULL.

		// Get the "hot" area (i.e., clickable area) relative to this item.
		virtual					// If you override this, call this base if possible.
		void GetHot(			// Returns nothing.
			int16_t* psX,			// Out: X position unless NULL.
			int16_t* psY,			// Out: Y position unless NULL.
			int16_t* psW,			// Out: Width unless NULL.
			int16_t* psH)			// Out: Height unless NULL.
			{
			// Use tray area.
			GetTray(psX, psY, psW, psH);
			}

//////////////////////////////////////////////////////////////////////////////

	public:	// Static

//////////////////////////////////////////////////////////////////////////////

	public:	// Querries.

//////////////////////////////////////////////////////////////////////////////

	protected:	// Internal functions.
		// These handle callbacks for the buttons and thumb.  They get an
		// instance for the scroll bar and call the equivalent instantiated
		// (non-static) functions.
		static void UpHotCall(
			RHot*	phot,			// Ptr to RHot that generated event.
			RInputEvent* pie)	// In:  Most recent user input event.             
									// Out: pie->sUsed = TRUE, if used.
			{ ((RScrollBar*)(phot->m_ulUser))->UpHotCall(pie); }
		static void DownHotCall(
			RHot*	phot,			// Ptr to RHot that generated event.
			RInputEvent* pie)	// In:  Most recent user input event.             
									// Out: pie->sUsed = TRUE, if used.
			{ ((RScrollBar*)(phot->m_ulUser))->DownHotCall(pie); }
		static void ThumbHotCall(
			RHot*	phot,			// Ptr to RHot that generated event.
			RInputEvent* pie)	// In:  Most recent user input event.             
									// Out: pie->sUsed = TRUE, if used.
			{ ((RScrollBar*)(phot->m_ulUser))->ThumbHotCall(pie); }
		static void DrawUpArrow(	// Returns nothing.
			RGuiItem* pgui,			// The RGuiItem being composed (this).
			RImage* pim,				// Image to draw into.  Try to stay within 
											// prc please.
			RRect* prc)					// Where to in image.
			{ ((RScrollBar*)pgui->m_ulUserInstance)->DrawUpArrow(pim, prc); }
		static void DrawDownArrow(	// Returns nothing.
			RGuiItem* pgui,			// The RGuiItem being composed (this).
			RImage* pim,				// Image to draw into.  Try to stay within 
											// prc please.
			RRect* prc)					// Where to in image.
			{ ((RScrollBar*)pgui->m_ulUserInstance)->DrawDownArrow(pim, prc); }

		// Save item's children to the specified file.
		virtual					// Overridden here.
		int16_t SaveChildren(	// Returns 0 on success.
			RFile*	pfile);	// File to save to.

		// Load item's children from the specified file.
		virtual					// Overridden here.
		int16_t LoadChildren(	// Returns 0 on success.
			RFile*	pfile);	// File to load from.

		// Read item's members from file.
		virtual				// Overridden here.
		int16_t ReadMembers(			// Returns 0 on success.
			RFile*	pfile,			// File to read from.
			U32		u32Version);	// File format version to use.

		// Write item's members to file.
		virtual				// Overridden here.
		int16_t WriteMembers(			// Returns 0 on success.
			RFile*	pfile);			// File to write to.

		// Set position, usually as requested by user, using the specified
		// type of user feedback.
		void UserSetPos(	// Returns nothing.
			int32_t	lPos)		// In:  New position.
			{
			switch (m_scrollage)
				{
				case Instant:
					SetPos(lPos);
					break;
				case Smooth:
					ScrollToPos(lPos);
					break;
				}
			}

//////////////////////////////////////////////////////////////////////////////

	public:	// Member variables.
		// Feel free to set the callbacks for these three Guis or the base class
		// to your own functions.  But if you change gui.m_bcUser or 
		// gui.m_ulUserInstace, you are responsible for calling SetPos() or the
		// callbacks (e.g., UpBtnPressed(void), DownBtnPressed(void), or 
		// TrayPressed(void)) as appropriate.
		RBtn			m_btnThumb;			// Mobile positioner/indicator.
		RBtn			m_btnUp;				// Up or left arrow button.
		RBtn			m_btnDown;			// Down or right arrow button.

		int32_t			m_lButtonIncDec;	// Amount to increment or decrement the
												// position when a button is pressed.
		int32_t			m_lTrayIncDec;		// Amount to increment or decrement the
												// position when the tray is pressed.

		int16_t			m_sArrowBorderDistance;	// Number of pixels between arrows'
														// edges and borders.

		UpdatePosCall	m_upcUser;		// User callback on postion change.

		Scrollage	m_scrollage;		// { RScrollBar::Instant,
												// RScrollBar::Smooth }

		int32_t			m_lPosPerSecond;	// Number of positions smoothly scrolled
												// through in a second.  The rate of
												// smooth scrollage.

		int32_t			m_lLastSmoothTime;	// Last time smooth scroll was processed.

		int32_t			m_lScrollToPos;	// Position to smoothly scroll to.
												// This GUI should have the focus to get
												// correct operation.

		int16_t			m_sInSmoothScroll;	// TRUE, if we are in a smooth scroll.
													// FALSE, otherwise.

		// These values may be querried and can be changed directly.
		// BUT, they may not affect the appearance of the scroll bar
		// until Compose() is called.
		Orientation	m_oOrientation;	// { RScrollBar::Vertical,
												// RScrollBar::Horizontal }

		int32_t			m_lMinThumbLength;	// Minimum length for thumb.

		int16_t			m_sClickOffsetX;	// Position in thumb that was clicked.
		int16_t			m_sClickOffsetY;	// Position in thumb that was clicked.

	protected:	// Internal typedefs.

	protected:	// Protected member variables.

		int32_t			m_lMinPos;				// Minimum value.
		int32_t			m_lMaxPos;				// Maximum value.

		int32_t			m_lCurPos;				// Current value.

		float			m_fPos2PixelRatio;	// Current ratio of scroll bar 
													// positions pixels to pixels.

	///////////////////////////////////////////////////////////////////////////
	// Friends.
	///////////////////////////////////////////////////////////////////////////
	friend class CScrollBarPropPage;

	};

#endif // SCROLLBAR_H
//////////////////////////////////////////////////////////////////////////////
//	EOF
//////////////////////////////////////////////////////////////////////////////
