/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2002 The Inti Development Team.
 *
 *  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/main.h
//! @brief Initialization and main processing loop interface
//!
//! Inti wraps the GTK+ initialization functions and events into the Main namespace
//! rather than a static class. Before using Inti, you need to initialize it.
//! Initialization connects to the window system display, and parses some standard
//! command line arguments. The init() function initializes Inti. init() exits the
//! application if errors occur; to avoid this, use init_check(). init_check() allows
//! you to recover from a failed Inti initialization - you might start up your 
//! application in text mode instead.
//! 
//! Like all GUI toolkits, Inti uses an event-driven programming model. When the user
//! is doing nothing, Inti sits in the main loop and waits for input. If the user 
//! performs some action - say, a mouse click - then the main loop "wakes up" and
//! delivers an event to Inti. Inti forwards the event to one or more widgets.
//!
//! When widgets receive an event, they frequently emit one or more signals. Signals
//! notify your program that "something interesting happened" by invoking slots you've
//! connected to the signal with the signal's connect() method. Slots are callable
//! objects that can be connected to signals. Slots can be used to connect static functions,
//! class methods and function objects to a signal. When your slot is called it invokes
//! your function or method.
//!
//! When your methods are invoked, you would typically take some action - for example,
//! when an Open button is clicked you might display a Gtk::FileSelectionDialog. After
//! a callback finishes, Inti will return to the main loop and await more user input.

#ifndef INTI_MAIN_H
#define INTI_MAIN_H

#ifndef __GTK_MAIN_H__
#include <gtk/gtkmain.h>
#endif

#ifndef INTI_SLOT_H
#include <inti/slot.h>
#endif

#ifndef INTI_CONNECTION_H
#include <inti/connection.h>
#endif

#ifndef INTI_POINTER_H
#include <inti/pointer.h>
#endif

#ifndef _CPP_VECTOR
#include <vector>
#endif

namespace Inti {
	
namespace Gdk {
class Event;
class EventKey;
}

namespace Gtk {
class Widget;
}

namespace Main {

class SlotNode;

//! @name Initializers
//! @{

	void init(int *argc, char ***argv);
	//!< Initialize the Inti library.
	//!< @param argc Address of the argc parameter of your main function.
	//!<             Changed if any arguments were handled.
	//!< @param argv Address of the argv parameter of main(). Any parameters
	//!<             understood by init() are stripped before returning.
	//!<
	//!< <BR>Call this method before using any other Inti methods in your GUI applications.
	//!< It will initialize everything needed to operate the toolkit and parses some
	//!< standard command line options. argc and argv are adjusted accordingly so your
	//!< own code will never see those standard arguments.

	bool init_check(int *argc, char ***argv);
	//!< Initialize the Inti library.
	//!< @param argc A reference to the argc of the main() function.
	//!< @param argv A reference to the argv of the main() function.
	//!< @return <EM>true</EM> if the GUI has been successfully initialized, <EM>false</EM> otherwise.
	//!<
	//!< <BR> This method does the same work as init() with only a single change: It does not
	//!< terminate the program if the GUI can't be initialized. Instead it returns <EM>false</EM>
	//!< on failure. This way the application can fall back to some other means of communication
	//!< with the user - for example a ommand line interface.

	void init_add(const Slot0<int> *callback);
	//!< Register a slot to be called when the mainloop is started.
	//!< @param callback A slot to invoke when main() is called next.
	//!<
	//!< <BR>The callback slot must be explicitly unreferenced before the program ends.

//! @}
//! @name Accessors
//! @{

	PangoLanguage* default_language();
	//!< Get the default language currently in effect.
	//!< @return The ISO language code for the default language currently in effect.
	//!<
	//!< <BR>Note that this can change over the life of an application. The default language
	//!< is derived from the current locale. It determines, for example, whether Inti uses
	//!< the right-to-left or left-to-right text direction.

	bool events_pending();
	//!< Checks if any events are pending.
	//!< @return <EM>true</EM> if any events are pending, <EM>false</EM> otherwise.
	//!<
	//!< <BR>This can be used to update the GUI and invoke timeouts etc. while doing
	//!< some time intensive computation.
	//!
	//!< <B>Example:</B> Updating the GUI during a long computation.
	//!< @code
	//!< // computation going on
	//!< ...
	//!< while (events_pending())
	//!< 	iterate();
	//!< ...
	//!< // computation continued
	//!< @endcode

	Pointer<Gtk::Widget> event_widget(Gdk::Event& event);
	//!< Get the widget that originally received the <EM>event</EM>.
	//!< @param event A reference to a Gdk::Event.
	//!< @return The widget that originally received event, or null.
	//!<
	//!< <BR>If <EM>event</EM> is null or the event was not associated with any widget,
	//!< returns null, otherwise returns the widget that received the event originally.
	//!< The widget is returned as a smart pointer because there is the possiblity that
	//!< the widget was wrapped for the first time and needs to be unreferenced.

//! @}
//! @name Main Event Loop
//! @{

	void run();
	//!< Runs the main loop until quit() is called.
	//!< You can nest calls to run(). In that case quit() will make the innermost
	//!< invocation of the main loop return.

	int level();
	//!< Asks for the current nesting level of the main loop.
	//!< @return The nesting level of the current invocation of the main loop.
	//!<
	//!< <BR>This can be useful when connecting a slot to the <EM>quit_signal</EM>.

	void quit();
	//!< Makes the innermost invocation of the main loop return when it regains control.

	bool iterate(bool blocking = true);
	//!< Runs a single iteration of the mainloop.
	//!< @param blocking Set <EM>true</EM> if you want to block if no events are pending.
	//!< @return <EM>true</EM> if quit() has been called for the innermost mainloop.
	//!<
	//!< <BR>If <EM>blocking</EM> is true and no events are waiting to be processed Inti
	//!< will block until the next event is noticed. If <EM>blocking</EM> is false and
	//!< no events are available return.

	void grab_add(Gtk::Widget& widget);
	//!< Makes <EM>widget</EM> the current grabbed widget.
	//!< @param widget The widget that grabs keyboard and pointer events.
	//!<
	//!< <BR> This means that interaction with other widgets in the same application
	//!< is blocked and mouse as well as keyboard events are delivered to this widget.

	Pointer<Gtk::Widget> grab_get_current();
	//!< Queries the current grab.
	//!< @return The widget which currently has the grab or null if no grab is active.

	void grab_remove(Gtk::Widget& widget);
	//!< Removes the grab from the given widget.
	//!< @param widget The widget which gives up the grab.
	//!< You have to pair calls to grab_add() and grab_remove().

//! @}
//! @name Signals
//! @{

	//! @class QuitSignal main.h inti/main.h
	//! @brief Quit signal class.
	class QuitSignal
	{
	public:
		typedef Slot0<bool> SlotType;
		//!< Function signature for handlers connected to this signal.

		Connection connect(const SlotType *slot, unsigned int main_level = 0);
		//!< Connect a slot to be called when an instance of the mainloop is left.
		//!< @param slot The slot to call.
		//!< @param main_level Level at which termination the slot shall be called.
		//!< @return A connection object that can be used to break or alter the connection.
		//!<
		//!< <BR>You can pass 0 as the <EM>main_level</EM> to have your slot invoked at the
		//!< termination of the current mainloop.
	};

	//! A namespace instance of the QuitSignal for connecting slots to be invoked at the
	//! termination of the current mainloop.
	extern QuitSignal quit_signal;

	//! @class TimeoutSignal main.h inti/main.h
	//! @brief Timeout signal class.
	class TimeoutSignal
	{
	public:
		typedef Slot0<bool> SlotType;
		//!< Function signature for handlers connected to this signal.

		Connection connect(const SlotType *slot, unsigned int interval);
		//!< Connect a slot to be called periodically.
		//!< @param slot The slot to call periodically.
		//!< @param interval The time between calls to the function, in milliseconds.
		//!< @return A connection object that can be used to break or alter the connection.
		//!<
		//!< <BR>The slot will be called repeatedly after <EM>interval</EM> milliseconds
		//!< until the handler returns <EM>false</EM>, at which point the timeout is destroyed
		//!< and will not be called again. If you return false you don't need to disconnect
		//!< the timer. You can call Connection::is_connected() to check if the connection
		//!< is connected.  
	};

	//! A namespace instance of the TimeoutSignal for connecting slots to be invoked periodically.
	extern TimeoutSignal timeout_signal;

	//! @class IdleSignal main.h inti/main.h
	//! @brief Idle signal class.
	class IdleSignal
	{
	public:
		typedef Slot0<bool> SlotType;
		//!< Function signature for handlers connected to this signal.

		Connection connect(const SlotType *slot, int priority = G_PRIORITY_DEFAULT_IDLE);
		//!< Connect a slot to be called when the event loop is idle.
		//!< @param slot The slot to call.
		//!< @param priority The priority which should not be above G_PRIORITY_HIGH_IDLE.
		//!< @return A connection object that can be used to break or alter the connection.
		//!<
		//!< <BR>You can give a priority different from GTK_PRIORITY_DEFAULT to the idle function.
		//!< Note that you will interfere with GTK+ if you use a priority above GTK_PRIORITY_RESIZE.
		//!< The user function returns <EM>false</EM> to remove itself or <EM>true</EM> to have it
		//!< called again.
	};

	//! A namespace instance of the IdleSignal for connecting slots to be invoked when the event
	//! loop is idle.
	extern IdleSignal idle_signal;

	//! @class KeySnooperSignal main.h inti/main.h
	//! @brief KeySnooper signal class.
	class KeySnooperSignal
	{
		std::vector<SlotNode*> connection_list;

	public:
		typedef Slot2<bool, Gtk::Widget&, const Gdk::EventKey&> SlotType;
		//!< Function signature for handlers connected to this signal.

		~KeySnooperSignal();
		//!< Destructor.

		Connection connect(const SlotType *slot);
		//!< Connect a slot to be called on all key events before delivering them normally.
		//!< @param slot The slot to call.
		//!< @return A connection object that can be used to break or alter the connection.
		//!<
		//!< <BR>Connect a user function that will receive keypress events. It is the snooper's
		//!< responsiblity to pass the keypress on to the widget, but care must taken that its
		//!< not passed twice. The user function should return <EM>false</EM> to prevent further
		//!< snooping.
	};
	
	//! A namespace instance of the KeySnooperSignal for connecting slots that will receive
	//! keypress events.
	extern KeySnooperSignal key_snooper_signal;

	//! @class InputSignal main.h inti/main.h
	//! @brief Input signal class.
	class InputSignal
	{
	public:
		typedef Slot2<void, int, GdkInputCondition> SlotType;
		//!< Function signature for handlers connected to this signal.

		Connection connect(const SlotType *slot, int source, GdkInputCondition condition);
		//!< Connect a slot to be called when a condition becomes true on a file descriptor.
		//!< @param slot The slot to call.
		//!< @param source A file descriptor.
		//!< @param condition The condition.
		//!< @return A connection object that can be used to break or alter the connection.
		//!<
		//!< <BR>Connect a user function that will monitor a file descriptor source for the
		//!< specified GdkInputCondition activity: GDK_INPUT_READ, GDK_INPUT_WRITE or 
		//!< GDK_INPUT_EXCEPTION.
	};

	//! A namespace instance of the InputSignal for connecting slots to be called when a 
	//! condition becomes true on a file descriptor.
	extern InputSignal input_signal;
	
//! @}

} // namespace Main

} // namespace Inti

//! INTI_MAIN is a convenience macro that writes a simple main function.

#define INTI_MAIN(MainWidget)\
	int main (int argc, char *argv[])\
	{\
		Inti::Main::init(&argc, &argv);\
		MainWidget main_widget;\
		main_widget.sig_destroy().connect(slot(&Inti::Main::quit));\
		main_widget.show();\
		Inti::Main::run();\
		return 0;\
	}

#endif // INTI_MAIN_H

