/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2002-2003 The Inti Development Team.
 *  Copyright (C) 2000 Red Hat, 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 2 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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

//! @file inti/gtk/spinbutton.h
//! @brief A GtkSpinButton C++ wrapper interface.
//!
//! Provides SpinButton, a widget that allows the user to set the value of some attribute.

#ifndef INTI_GTK_SPIN_BUTTON_H
#define INTI_GTK_SPIN_BUTTON_H

#ifndef INTI_GTK_ENTRY_H
#include <inti/gtk/entry.h>
#endif

#ifndef __GTK_SPIN_BUTTON_H__
#include <gtk/gtkspinbutton.h>
#endif

namespace Inti {

namespace Gtk {

class SpinButtonClass;

//! @enum SpinType
//! SpinType defines the types of spin movement that can occur.
//! The ammount of spin is defined by the spin buttons adjustment.

enum SpinType
{
	SPIN_STEP_FORWARD = GTK_SPIN_STEP_FORWARD, //!< Spin forward by one step value.
	SPIN_STEP_BACKWARD = GTK_SPIN_STEP_BACKWARD, //!< Spin backward by one step value.
	SPIN_PAGE_FORWARD = GTK_SPIN_PAGE_FORWARD, //!< Spin forward by one page value.
	SPIN_PAGE_BACKWARD = GTK_SPIN_PAGE_BACKWARD, //!< Spin backward by one page value.
	SPIN_HOME = GTK_SPIN_HOME, //!< Spin to the minimum value.
	SPIN_END = GTK_SPIN_END, //!< Spin to maximum value.
	SPIN_USER_DEFINED = GTK_SPIN_USER_DEFINED //!< Spin by an ammount specified by the user.
};

//! @enum SpinButtonUpdatePolicy
//! SpinButtonUpdatePolicy determines how the spin button should be updated.

enum SpinButtonUpdatePolicy
{
	UPDATE_ALWAYS = GTK_UPDATE_ALWAYS, 
	//!< When refreshing your SpinButton, the value is always displayed.

	UPDATE_IF_VALID = GTK_UPDATE_IF_VALID 
	//!< When refreshing your SpinButton, the value is only displayed if it is valid
	//!< within the bounds of the spin button's Adjustment.
};

//! @class SpinButton spinbutton.h inti/gtk/spinbutton.h
//! @brief A GtkSpinButton C++ wrapper class.
//!
//! A SpinButton is an ideal way to allow the user to set the value of some attribute.
//! Rather than having to directly type a number into a Entry, SpinButton allows the
//! user to click on one of two arrows to increment or decrement the displayed value.
//! A value can still be typed in, with the bonus that it can be checked to ensure it
//! is in a given range. The main properties of a SpinButton are through an Adjustment.
//! See the Adjustment section for more details about an adjustment's properties.
//!
//! <B>Example 1:</B> Using a SpinButton to get an integer.
//! @code
//! #include <inti/main.h>
//! #include <inti/gtk/window.h>
//! #include <inti/gtk/adjustment.h>
//! #include <inti/gtk/spinbutton.h>
//!
//! using namespace Inti;
//!
//! class Window : public Gtk::Window
//! {
//! 	Gtk::SpinButton *spinner;
//! public:
//! 	Window();
//! 	int grab_value() const;
//! };
//!
//! Window::Window()
//! {
//! 	set_border_width(5);
//! 	Gtk::Adjustment *spinner_adj = new Gtk::Adjustment(50.0, 0.0, 100.0, 1.0, 5.0, 5.0);
//!
//! 	// Creates the spinner, with no decimal places.
//! 	spinner = new Gtk::SpinButton(spinner_adj, 1.0, 0);
//! 	add(*spinner);
//! 	show_all();
//! }
//!
//! int
//! Window::grab_value() const
//! {
//! 	return spinner->get_value_as_int();
//! }
//!
//! int main (int argc, char *argv[])
//! {
//! 	using namespace Main;
//!
//! 	init(&argc, &argv);
//!
//! 	Window window;
//! 	window.sig_destroy().connect(slot(&Inti::Main::quit));
//!
//! 	run();
//! 	return 0;
//! }
//! @endcode
//!
//! <B>Example 2:</B> Using a SpinButton to get a floating point value.
//! @code
//! #include <inti/main.h>
//! #include <inti/gtk/window.h>
//! #include <inti/gtk/adjustment.h>
//! #include <inti/gtk/spinbutton.h>
//!
//! using namespace Inti;
//!
//! class Window : public Gtk::Window
//! {
//! 	Gtk::SpinButton *spinner;
//! public:
//! 	Window();
//! 	double grab_value() const;
//! };
//!
//! Window::Window()
//! {
//! 	set_border_width(5);
//! 	Gtk::Adjustment *spinner_adj = new Gtk::Adjustment(2.500, 0.0, 5.0, 0.001, 0.1, 0.1);
//!
//! 	// Creates the spinner, with no decimal places.
//! 	spinner = new Gtk::SpinButton(spinner_adj, 0.001, 3);
//! 	add(*spinner);
//! 	show_all();
//! }
//!
//! double
//! Window::grab_value() const
//! {
//! 	return spinner->get_value();
//! }
//!
//! int main (int argc, char *argv[])
//! {
//! 	using namespace Main;
//!
//! 	init(&argc, &argv);
//!
//! 	Window window;
//! 	window.sig_destroy().connect(slot(&Inti::Main::quit));
//!
//! 	run();
//! 	return 0;
//! }
//! @endcode

class SpinButton : public Entry
{
	friend class G::Object;
	friend class SpinButtonClass;

	SpinButton(const SpinButton&);
	SpinButton& operator=(const SpinButton&);

protected:
//! @name Constructors
//! @{

	explicit SpinButton(GtkSpinButton *spin_button, bool reference = false);
	//!< Construct a new SpinButton from an existing GtkSpinButton.
	//!< @param spin_button A pointer to a GtkSpinButton.
	//!< @param reference Set false if the initial reference count is floating, set true if it's not.
	//!<
	//!< <BR>The <EM>spin_button</EM> can be a newly created GtkSpinButton or an existing
	//!< GtkSpinButton (see G::Object::Object).

//! @}
//! @name Signal Handlers
//! @{

	virtual int on_input(double *new_value);
	//!< Called when a new value is displayed in the spinbutton.
	//!< @param new_value The new value of the spin button.
	//!< @return -1 if an error occurs, otherwise 0.

	virtual int on_output();
	//!< Called to display the new value in the spin button.
	//!< @return Always zero.

	virtual void on_value_changed();
	//!< Called when the value has changed.

//! @}
//  Properties

	typedef G::Property<Adjustment*, G::Object*> AdjustmentPropertyType;
	typedef G::PropertyProxy<G::Object, AdjustmentPropertyType> AdjustmentPropertyProxy;
	static const AdjustmentPropertyType adjustment_property;

	typedef G::Property<double> ClimbRatePropertyType;
	typedef G::PropertyProxy<G::Object, ClimbRatePropertyType> ClimbRatePropertyProxy;
	static const ClimbRatePropertyType climb_rate_property;

	typedef G::Property<unsigned int> DigitsPropertyType;
	typedef G::PropertyProxy<G::Object, DigitsPropertyType> DigitsPropertyProxy;
	static const DigitsPropertyType digits_property;

	typedef G::Property<bool> SnapToTicksPropertyType;
	typedef G::PropertyProxy<G::Object, SnapToTicksPropertyType> SnapToTicksPropertyProxy;
	static const SnapToTicksPropertyType snap_to_ticks_property;

	typedef G::Property<bool> NumericPropertyType;
	typedef G::PropertyProxy<G::Object, NumericPropertyType> NumericPropertyProxy;
	static const NumericPropertyType numeric_property;

	typedef G::Property<bool> WrapPropertyType;
	typedef G::PropertyProxy<G::Object, WrapPropertyType> WrapPropertyProxy;
	static const WrapPropertyType wrap_property;

	typedef G::Property<SpinButtonUpdatePolicy, int> UpdatePolicyPropertyType;
	typedef G::PropertyProxy<G::Object, UpdatePolicyPropertyType> UpdatePolicyPropertyProxy;
	static const UpdatePolicyPropertyType update_policy_property;

	typedef G::Property<double> ValuePropertyType;
	typedef G::PropertyProxy<G::Object, ValuePropertyType> ValuePropertyProxy;
	static const ValuePropertyType value_property;

//  Signals

	typedef G::Signal1<int, double*> InputSignalType;
	typedef G::SignalProxy<TypeInstance, InputSignalType> InputSignalProxy;
	static const InputSignalType input_signal;

	typedef G::Signal0<int> OutputSignalType;
	typedef G::SignalProxy<TypeInstance, OutputSignalType> OutputSignalProxy;
	static const OutputSignalType output_signal;

	typedef G::Signal0<void> ValueChangedSignalType;
	typedef G::SignalProxy<TypeInstance, ValueChangedSignalType> ValueChangedSignalProxy;
	static const ValueChangedSignalType value_changed_signal;

public:
//! @name Constructors
//! @{

	SpinButton();
	//!< Construct a new spin button.

	SpinButton(Adjustment *adjustment, double climb_rate, unsigned int digits);
	//!< Construct a new spin button with the specified Adjustment, climb_rate and digits.
	//!< @param adjustment The Adjustment object that this spin button should use.
	//!< @param climb_rate Specifies how much the spin button changes when an arrow is clicked on.
	//!< @param digits The number of decimal places to display.

	SpinButton(double min, double max, double step = 1.0);
	//!< Construct a new spin button without specifying an Adjustment.
	//!< @param min The minimum allowable value.
	//!< @param max The maximum allowable value.
	//!< @param step The increment added or subtracted by spinning the widget.
	//!<
	//!< <BR>This is a convenience constructor that allows creation of a numeric SpinButton
	//!< without manually creating an adjustment. The value is initially set to the minimum
	//!< value and a page increment of 10 * step is the default. The precision of the 
	//!< spinbutton is equivalent to the precision of step.

	virtual ~SpinButton();
	//!< Destructor.
	
//! @}
//! @name Accessors
//! @{

	GtkSpinButton* gtk_spin_button() const { return (GtkSpinButton*)instance; }
	//!< Get a pointer to the GtkSpinButton structure.

	GtkSpinButtonClass* gtk_spin_button_class() const;
	//!< Get a pointer to the GtkSpinButtonClass structure.

	operator GtkSpinButton* () const;
	//!< Conversion operator; safely converts a SpinButton to a GtkSpinButton pointer.

	Adjustment* get_adjustment() const;
	//!< Get the adjustment associated with the SpinButton.

	unsigned int get_digits() const;
	//!< Fetches the precision of the spin button (see set_digits()).
	//!< @return The current precision.

	void get_increments(double *step, double *page) const;
	//!< Gets the current step and page the increments used by the spin button (see set_increments()).
	//!< @param step The location to store step increment, or null.
	//!< @param page The location to store page increment, or null.

	void get_range(double *min, double *max) const;
	//!< Gets the range allowed for the spin button (see set_range()).
	//!< @param min The location to store minimum allowed value, or null.
	//!< @param max The location to store maximum allowed value, or null.

	double get_value() const;
	//!< Get the value in the spin button.
	
	int get_value_as_int() const;
	//!< Get the value of the spin button represented as an integer.
	//!< @return The value of the spin button.

	SpinButtonUpdatePolicy get_update_policy() const;
	//!< Gets the update behavior of the spin button (see set_update_policy()).
	//!< @return The current update policy.

	bool get_numeric() const;
	//!< Returns whether non-numeric text can be typed into the spin button (see set_numeric()).
	//!< @return <EM>true</EM> if only numeric text can be entered.

	bool get_wrap() const;
	//!< Returns whether the spin button's value wraps around to the opposite limit
	//!< when the upper or lower limit of the range is exceeded (see set_wrap()).
	//!< @return <EM>true</EM> if the spin button wraps around.

	bool get_snap_to_ticks() const;
	//!< Returns whether the values are corrected to the nearest step (see set_snap_to_ticks()).
	//!< @return <EM>true</EM> if values are snapped to the nearest step.

//! @}
//! @name Methods
//! @{

	void configure(Adjustment *adjustment, double climb_rate, unsigned int digits);
	//!< Changes the properties of an existing spin button.
	//!< @param adjustment The new Adjustment object this spin button should use.
	//!< @param climb_rate How much the spin button changes when an arrow is clicked on.
	//!< @param digits The number of decimal places to display in the spin button.
	//!<
	//!< <BR>The adjustment, climb rate, and number of decimal places are all changed accordingly,
	//!< after this function call.

	void set_adjustment(Adjustment *adjustment);
	//!< Replaces the Adjustment associated with the spin button.
	//!< @param adjustment An Adjustment to replace the existing adjustment.

	void set_digits(unsigned int digits);
	//!< Set the precision to be displayed by the spin button.
	//!< @param digits The number of digits to be displayed for the spin button's value.
	//!<
	//!< <BR>Up to 20 digit precision is allowed.

	void set_increments(double step, double page);
	//!< Sets the step and page increments for the spin button.
	//!< @param step The increment applied for a button 1 press.
	//!< @param page The increment applied for a button 2 press.
	//!<
	//!< <BR>This affects how quickly the value changes when the spin button's arrows are activated.

	void set_range(double min, double max);
	//!< Sets the minimum and maximum allowable values for the spin button.
	//!< @param min The minimum allowable value.
	//!< @param max The maximum allowable value.

	void set_value(double value);
	//!< Set the value of the spin button.
	//!< @param value The new value.

	void set_update_policy(SpinButtonUpdatePolicy policy);
	//!< Sets the update behavior of the spin button. 
	//!< @param policy A SpinButtonUpdatePolicy value.
	//!<
	//!< <BR>This determines whether the spin button is always updated or only when a valid value is set.

	void set_numeric(bool numeric);
	//!< Sets the flag that determines if non-numeric text can be typed into the spin button.
	//!< @param numeric <EM>true</EM> if only numeric entry is allowed.

	void spin(SpinType direction, double increment);
	//!< Increment or decrement a spin button's value in a specified direction by a specified amount.
	//!< @param direction A SpinType indicating the direction to spin.
	//!< @param increment The step increment to apply in the specified direction.
	
	void set_wrap(bool wrap);
	//!< Sets the flag that determines if a spin button value wraps around to the opposite limit
	//!< when the upper or lower limit of the range is exceeded.
	//!< @param wrap <EM>true</EM> if wrapping behavior is performed.

	void set_snap_to_ticks(bool snap_to_ticks);
	//!< Sets the policy as to whether values are corrected to the nearest step increment
	//!< when a spin button is activated after providing an invalid value.
	//!< @param snap_to_ticks A flag indicating if invalid values should be corrected.

	void update();
	//!< Manually force an update of the spin button.

//! @}
//! @name Property Proxies
//! @{

	const AdjustmentPropertyProxy prop_adjustment()
	{
		return AdjustmentPropertyProxy(this, &adjustment_property);
	}
	//!< The adjustment that holds the value of the spin button (Adjustment* : Read / Write).

	const ClimbRatePropertyProxy prop_climb_rate()
	{
		return ClimbRatePropertyProxy(this, &climb_rate_property);
	}
	//!< The acceleration rate when you hold down a button (double : Read / Write).

	const DigitsPropertyProxy prop_digits()
	{
		return DigitsPropertyProxy(this, &digits_property);
	}
	//!< The number of decimal places to display (unsigned int : Read / Write).

	const SnapToTicksPropertyProxy prop_snap_to_ticks()
	{
		return SnapToTicksPropertyProxy(this, &snap_to_ticks_property);
	}
	//!< Whether erroneous values are automatically changed to a spin button's nearest
	//!< step increment (bool : Read / Write).

	const NumericPropertyProxy prop_numeric()
	{
		return NumericPropertyProxy(this, &numeric_property);
	}
	//!< Whether non-numeric characters should be ignored (bool : Read / Write).

	const WrapPropertyProxy prop_wrap()
	{
		return WrapPropertyProxy(this, &wrap_property);
	}
	//!< Whether a spin button should wrap upon reaching its limits (bool : Read / Write).

	const UpdatePolicyPropertyProxy prop_update_policy()
	{
		return UpdatePolicyPropertyProxy(this, &update_policy_property);
	}
	//!< Whether the spin button should update always, or only when the value is legal
	//!< (SpinButtonUpdatePolicy : Read / Write).

	const ValuePropertyProxy prop_value()
	{
		return ValuePropertyProxy(this, &value_property);
	}
	//!< Reads the current value, or sets a new value (double : Read / Write).

//! @}
//! @name Signal Proxies
//! @{

	const InputSignalProxy sig_input()
	{
		return InputSignalProxy(this, &input_signal);
	}
	//!< Connect to the input_signal; emitted when a new value is displayed in the spin button.

	const OutputSignalProxy sig_output()
	{
		return OutputSignalProxy(this, &output_signal);
	}
	//!< Connect to the output_signal; emitted to display the new value in the spin button.

	const ValueChangedSignalProxy sig_value_changed()
	{
		return ValueChangedSignalProxy(this, &value_changed_signal);
	}
	//!< Connect to the value_changed_signal; emitted when the value has changed.

//! @}
};

} // namespace Gtk

} // namespace Inti

#endif // INTI_GTK_SPIN_BUTTON_H

