/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/10
// RCS-ID:      $Id: keybinder.h,v 1.5 2001/04/21 14:19:44 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifndef __KEYBINDER_G__
#define __KEYBINDER_G__

#include "wx/dynarray.h"
#include "wx/hash.h"
#include "wx/event.h"
#include "wx/panel.h"
#include "wx/dialog.h"
#include "wx/listbox.h"

#include "wx/menu.h"

/*=== Keyboard-shortcut customization utility (TAB size=4) ===*/

// (NOTE:: these classes/interfaces can be used independently of PMF)

class wxCommandInfo;
class wxCommandGroup;
class wxKeyBinder;

WX_DEFINE_ARRAY( wxCommandInfo*,  wxCommandInfoListT );
WX_DEFINE_ARRAY( wxCommandGroup*, wxCommandGroupListT );

enum wxKEY_MODIFIERS
{
	wxALT_DONW     = 0x01,
	wxCONTROL_DOWN = 0x02,
	wxSHIFT_DOWN   = 0x04
};

#define wxMAX_KEYS_PER_CMD 5 // FIXME:: should not be fixed...

// TBD:: it has no relation with wxDocManager's wxCommand class,
//       should this be more explicite in it's name?

class wxCommandInfo : public wxObject
{
public:
	wxCommandInfo();
	~wxCommandInfo();

	// ctor
	wxCommandInfo( int id, const wxString& title, 
				   const wxString& helpString = wxEmptyString, const wxString& keyStroke = wxEmptyString,
				   bool isCheckable = FALSE, bool isChecked = FALSE );

	bool AddKey( int keyCode, int modifiers ); // returns TRUE, if array's not overfilled
	bool RemoveKey( size_t keyNo );
	bool KeyPresent( int keyCode, int modifiers );

	void Prepare(); // called by wxKeyBinder to call after initialization
					// of static wxCommandInfo objects is finished (becasue
					// _gVKHash cannot be accessed during static inits)

	size_t GetKeyCount();

	int GetId();
	const wxString& GetTitle();
	wxString GetStrippedTitle();
	const wxString& GetHelpString();
	bool IsCheckable();
	bool IsChecked();
	const wxBitmap& GetBitmap();

	void SetId( int id );
	void SetTitle( const wxString& title );
	void SetHelpString( const wxString& helpString );
	void SetCheckable( bool checkable );
	void SetChecked( bool checked );
	void SetBitmap( const wxBitmap& bmp );

	int* GetKeyCodes();
	int* GetKeyModifiers();


	void SetUserData( wxObject* pData );
	wxObject* GetUserData();
	virtual void ResetUserData();

	bool IsSeparator() { return mTitle == "SEPARATOR"; }

	// returns NULL for this class, but is overriden in wxCommandGroup

	virtual wxCommandInfo* Clone();
	virtual void AssignInfoTo( wxCommandInfo* pOther );

	virtual void SetCmdParent( wxCommandInfo* pParent );
	virtual wxCommandInfo* GetParent();

	virtual wxCommandInfoListT* GetSubcommands();
	virtual bool HasSubcommands();
	virtual void AddSubcommand( wxCommandInfo* pInfo );
	virtual wxCommandInfo* FindCommandById( int id );

	// utilities (static)

	static wxString KeyCodeToString( int keyCode );
	static wxString KeyModifierToString( int keyModifier );

	static int StringToKeyCode( const wxString& keyName );
	static int StringToKeyModifier( const wxString& keyModifier );

	static int GetKeyModifier( wxKeyEvent& event );
	static wxString GetKeyStrokeString( wxKeyEvent& event );
	static wxString GetKeyStrokeString( wxCommandInfo& cmd, int keyIndex );

	static bool ParseKeyStrokeString( const wxString& stroke, int* key, int* modifier );
	static bool IsPritableChar( int c );

	static wxString StripControlChars( const wxString& cmdTitle );

protected:

	int mId;
	wxString mTitle;
	wxString mHelpString;
	bool     mIsCheckable;
	bool	 mIsChecked;
	wxBitmap mBitmap;

	wxString mTmpStroke; // NOTE:: this is to workaround problem 
						 //        when static _gVKHash is not initialized
						 //        but wxCommandInfo is being created from
						 //        static. initializations in other files

	wxObject* mpUserData;    // is not released, just opque pointer

	// up to three bindings are allowed for one command (fixed-array to save heap mem.)

	int mKeyCodes [wxMAX_KEYS_PER_CMD];  // -1 indicates that no binding is present
	int mModifiers[wxMAX_KEYS_PER_CMD];

	wxCommandInfo* mpParent;
};

class wxCommandGroup : public wxCommandInfo
{
public:
	wxCommandGroup();
	wxCommandGroup( const wxString& title );

	// takes an C-array of commands, the command containing empty title ("")
	// marks the end of the list. Commands of the list are cloned into this group

	wxCommandGroup( const wxString& title, wxCommandInfo* pCommands );

	~wxCommandGroup();

	// 3 overriden methods of wxCommandInfo
	virtual wxCommandInfo* Clone();
	virtual wxCommandInfoListT* GetSubcommands();
	virtual void AddSubcommand( wxCommandInfo* pInfo );

public:
	wxCommandInfoListT mCommands;
};

// to use wxKeyBinder, add command groups to it, then use "PushEventHandler()" 
// for hooking it to whatever window or control. After that, key-events will 
// automaticlly be translated into wxMenu-events for that window (if binding is present)

class wxKeyBinder : public wxEvtHandler
{
public:
	wxKeyBinder();
	~wxKeyBinder();

	void AddGroup( wxCommandGroup* pGroup );

	// pGroups is C-array, where command object with empty title ("")
	// indicates the end of the list

	void AddGroups( wxCommandGroup* pGroups );
	void RemoveGroup( wxCommandGroup* pGroup );

	wxCommandInfo* FindCommandById( int id );

	wxCommandInfoListT& GetGroups();

	wxKeyBinder* Clone();
	void AssignFrom( wxKeyBinder& other );
	void ResetUserData();

	void UpdateKeyAssignments(); // recreates key-hash after,
								 // changes to commands were made
								 // externally (e.g. customization dialog)

	virtual void CreateMenuBar( wxFrame* pForWindow );

	/*** event-handlers ***/
	void OnChar( wxKeyEvent& event );
	void OnClose( wxCloseEvent& event );

	DECLARE_EVENT_TABLE()

protected:
	void UpdateHashForCommands( wxCommandInfoListT& lst );
	void UpdateHashForCommand( wxCommandInfo& cmd );
	void CreateMenusRecursively( wxCommandInfoListT& lst, wxMenuBar* pBar, 
								 wxMenu* pMenu, bool topLevel );

protected:
	wxCommandInfoListT mGroups;
	wxHashTable        mKeysHash;
};

// key-customization panel, can be used within any parent window/dialog

class wxKeyConfigPanel : public	wxPanel
{
public:
	wxKeyConfigPanel( wxWindow* pParent, wxKeyBinder* pBinder, 
					  bool showApplyBtn = FALSE );

	~wxKeyConfigPanel();

	void ApplyChanges();

	void OnGroupSelected( wxCommandEvent& event );
	void OnCommandSelected( wxCommandEvent& event );
	void OnBindingSelected( wxCommandEvent& event );

	void OnAssignKey( wxCommandEvent& event );
	void OnRemoveKey( wxCommandEvent& event );

	// notifications received from MyKeyFieldSpy:

	void OnKeyPressed( wxKeyEvent& event );
	void OnKeyFieldFocused();
	void OnIdle( wxIdleEvent& event ); // does updates of mpKeyField

	DECLARE_EVENT_TABLE()

protected:
	void UpdateButtons();
	void FillInGroups();
	void FillInCommands();
	void FillInBindings();
	int  GetSelectedCmdIndex();

protected:
	wxListBox*  mpGroups;
	wxListBox*  mpCommands;
	wxListBox*  mpBindings;
	wxTextCtrl* mpKeyField;

	wxButton* mpAssignBtn;
	wxButton* mpRemoveBtn;
	wxTextCtrl* mpDescLabel;

	wxKeyBinder*  mpBinder;	  // target, to which short-cuts will be applied
	wxKeyBinder   mTmpBinder; // used while itneractively editing shortcuts

	wxCommandInfo* mpCurGroup;

	bool        mStrokeUpdatePending;
	wxString    mCurKeyStroke;
};

// presents key-customization panel in a dialog

class wxKeyConfigDialog : public wxDialog
{
public:
	wxKeyConfigDialog( wxWindow* pParent, wxKeyBinder* pBinder );
	~wxKeyConfigDialog();

	void OnApply( wxCommandEvent& event );
	void OnCancel( wxCommandEvent& event );

	void OnSize( wxSizeEvent& event );

	DECLARE_EVENT_TABLE()

protected:
	wxKeyConfigPanel* mpConfigPanel;
};


#endif // __KEYBINDER_G__
