	/***************/
	/* hotkeys.cpp */
	/***************/

#include "fctsys.h"

#include "gr_basic.h"

#include "common.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"

#include "id.h"

#include "protos.h"

enum hotkey_id_commnand {
	HK_NOT_FOUND = 0,
	HK_RESET_LOCAL_COORD,
	HK_HELP,
	HK_ZOOM_IN,
	HK_ZOOM_OUT,
	HK_ZOOM_REDRAW,
	HK_ZOOM_CENTER,
	HK_NEXT_SEARCH,
	HK_DELETE,
	HK_REPEAT_LAST,
	HK_MOVEBLOCK_TO_DRAGBLOCK,
	HK_ROTATE_COMPONENT,
	HK_MIRROR_X_COMPONENT,
	HK_MIRROR_Y_COMPONENT,
	HK_ORIENT_NORMAL_COMPONENT,
	HK_MOVE_COMPONENT,
	HK_ADD_NEW_COMPONENT,
	HK_BEGIN_WIRE
};

/* Class to handle hotkey commnands. hotkeys have a default value
This class allows (for the future..) the real key code changed by user(from a key code list file, TODO)
*/
class Ki_HotkeyInfo
{
public:
	int m_KeyCode;					// Key code (ascii value for ascii keys or wxWidgets code for function key
	wxString m_InfoMsg;				// info message.
	hotkey_id_commnand m_Idcommand;	// internal id for the corresponding command (see hotkey_id_commnand list)

public:
	Ki_HotkeyInfo(const wxChar * infomsg, hotkey_id_commnand idcommand, int keycode);
};

Ki_HotkeyInfo::Ki_HotkeyInfo(const wxChar * infomsg, hotkey_id_commnand idcommand, int keycode)
{
	m_KeyCode = keycode;		// Key code (ascii value for ascii keys or wxWidgets code for function key
	m_InfoMsg = infomsg;		// info message.
	m_Idcommand = idcommand;	// internal id for the corresponding command (see hotkey_id_commnand list)
}

/* local variables */
/* Hotkey list: */
static Ki_HotkeyInfo HkBeginWire(wxT("begin Wire"), HK_BEGIN_WIRE, 'W');
static Ki_HotkeyInfo HkAddComponent(wxT("Add Component"), HK_ADD_NEW_COMPONENT, 'A');
static Ki_HotkeyInfo HkMirrorYComponent(wxT("Mirror Y Component"), HK_MIRROR_Y_COMPONENT, 'Y');
static Ki_HotkeyInfo HkMirrorXComponent(wxT("Mirror X Component"), HK_MIRROR_X_COMPONENT, 'X');
static Ki_HotkeyInfo HkOrientNormalComponent(wxT("Orient Normal Component"), HK_ORIENT_NORMAL_COMPONENT, 'N');
static Ki_HotkeyInfo HkRotateComponent(wxT("Rotate Component"), HK_ROTATE_COMPONENT, 'R');
static Ki_HotkeyInfo HkMoveComponent(wxT("Move Component"), HK_MOVE_COMPONENT, 'M');
static Ki_HotkeyInfo HkMove2Drag(wxT("Switch move block to drag block"), HK_MOVEBLOCK_TO_DRAGBLOCK, '\t');
static Ki_HotkeyInfo HkInsert(wxT("Repeat Last Item"), HK_REPEAT_LAST, WXK_INSERT);
static Ki_HotkeyInfo HkDelete(wxT("Delete Item"), HK_DELETE, WXK_DELETE);
static Ki_HotkeyInfo HkResetLocalCoord(wxT("Reset local coord."), HK_RESET_LOCAL_COORD, ' ');
static Ki_HotkeyInfo HkNextSearch(wxT("Next Search"), HK_NEXT_SEARCH, WXK_F5);
static Ki_HotkeyInfo HkZoomCenter(wxT("Zoom Center"), HK_ZOOM_CENTER, WXK_F4);
static Ki_HotkeyInfo HkZoomRedraw(wxT("Zoom Redraw"), HK_ZOOM_REDRAW, WXK_F3);
static Ki_HotkeyInfo HkZoomOut(wxT("Zoom Out"), HK_ZOOM_OUT, WXK_F2);
static Ki_HotkeyInfo HkZoomIn(wxT("Zoom In"), HK_ZOOM_IN, WXK_F1);
static Ki_HotkeyInfo HkHelp(wxT("Help: this message"), HK_HELP, '?');

// List of hotkey descriptors for schematic
static Ki_HotkeyInfo *s_Schematic_Hotkey_List[] = {
	&HkHelp,
	&HkZoomIn, &HkZoomOut, &HkZoomRedraw, &HkZoomCenter,
	&HkNextSearch, &HkResetLocalCoord,
	&HkDelete, &HkInsert, &HkMove2Drag,
	&HkMoveComponent, &HkAddComponent,
	&HkRotateComponent, &HkMirrorXComponent, &HkMirrorYComponent, & HkOrientNormalComponent,
	&HkBeginWire,
	NULL
};


// Library editor:
static Ki_HotkeyInfo HkInsertPin(wxT("Repeat Pin"), HK_REPEAT_LAST, WXK_INSERT);

// List of hotkey descriptors for libray editor
static Ki_HotkeyInfo *s_LibEdit_Hotkey_List[] =
{
	&HkHelp,
	&HkZoomIn, &HkZoomOut, &HkZoomRedraw, &HkZoomCenter,
	&HkResetLocalCoord,
	&HkInsertPin,
	NULL
};


/****************************************************/
static wxString ReturnKeyNameFromKeyCode(int keycode)
/****************************************************/
/*
	* return the key name from the key code
	* Only some wxWidgets key values are handled for function key
	* @param key = key code (ascii value, or wxWidgets value for function keys)
	* @return the key name wxString
*/
{
wxString keyname, modifier, fullkeyname;
	
	if ( keycode & GR_KB_CTRL) modifier << wxT("Ctrl ");
	if ( keycode & GR_KB_ALT) modifier << wxT("Alt ");
	if ( keycode & GR_KB_SHIFT) modifier << wxT("Shift ");
	keycode &= ~(GR_KB_CTRL|GR_KB_ALT|GR_KB_SHIFT);

	switch ( keycode )
	{
		default:
			keyname.Printf(wxT("%c"), keycode);
			break;
		
		case WXK_F1:
		case WXK_F2:
		case WXK_F3:
		case WXK_F4:
		case WXK_F5:
		case WXK_F6:
		case WXK_F7:
		case WXK_F8:
		case WXK_F9:
		case WXK_F10:
		case WXK_F11:
		case WXK_F12:
			keyname.Printf(wxT("F%d"), keycode - WXK_F1 + 1);
			break;
			
		case ' ':
			keyname = wxT("space");
			break;

		case '\t':
			keyname = wxT("Tab");
			break;

		case WXK_DELETE:
			keyname = wxT("Delete");
			break;

		case WXK_INSERT:
			keyname = wxT("Insert");
			break;
	}
	
	fullkeyname = modifier + keyname;
	return keyname;
}

/****************************************************************************/
static void DisplayHotkeyList(WinEDA_DrawFrame * frame, Ki_HotkeyInfo ** List)
/*****************************************************************************/
/*
	* Displays the current hotkey list
	* @param frame = current open frame
	* @param List = pointer to a Ki_HotkeyInfo list of commands
	* @return none
*/
{
wxString keyname;
	
	wxString msg = _("Current hotkey list:\n\n");
	for ( ; * List != NULL; List++ )
	{
		Ki_HotkeyInfo * hk_decr = * List;
		if ( hk_decr->m_InfoMsg.IsEmpty() ) break;
		msg += _("key ");
		keyname = ReturnKeyNameFromKeyCode(hk_decr->m_KeyCode);
		msg += keyname + wxT(":    ") + hk_decr->m_InfoMsg + wxT("\n");
	}
	DisplayInfo(frame, msg);
}

/******************************************************************/
static int GetCommandCodeFromHotkey(int key, Ki_HotkeyInfo ** List)
/******************************************************************/
/*
	* Return an id identifier fron a key code for OnHotKey() function
	* @param key = key code (ascii value, or wxWidgets value for function keys
	* @param List = pointer to a Ki_HotkeyInfo list of commands
	* @return the corresponding function identifier from the Ki_HotkeyInfo  List
*/
{
	for ( ; * List != NULL; List++ )
	{
		Ki_HotkeyInfo * hk_decr = * List;
		if ( hk_decr->m_KeyCode == key ) return hk_decr->m_Idcommand;
	}
	
	return HK_NOT_FOUND;
}

/***********************************************************/
void WinEDA_SchematicFrame::OnHotKey(wxDC * DC, int hotkey,
					EDA_BaseStruct * DrawStruct)
/***********************************************************/
/* Hot keys. Some commands are relatives to the item under the mouse cursor
	Commands are case insensitive
	Zoom commands are not managed here
*/
{
bool PopupOn = m_CurrentScreen->m_CurrentItem  &&
			m_CurrentScreen->m_CurrentItem->m_Flags;
bool RefreshToolBar = FALSE;	// We must refresh tool bar when the undo/redo tool state is modified
	
	if ( hotkey == 0 ) return;

wxPoint MousePos = m_CurrentScreen->m_MousePosition; 

	/* Convert lower to upper case (the usual toupper function has problem with non ascii codes like function keys */
	if( (hotkey >= 'a') && (hotkey <= 'z') ) hotkey += 'A' - 'a';
	
	// Search commnd from key :
	switch ( GetCommandCodeFromHotkey(hotkey, s_Schematic_Hotkey_List) )
	{
		default:
		case HK_NOT_FOUND:
			return;
			break;
		
		case HK_HELP:	// Display Current hotkey list
			DisplayHotkeyList(this, s_Schematic_Hotkey_List);
			break;

		case HK_ZOOM_IN:
		case HK_ZOOM_OUT:
		case HK_ZOOM_REDRAW:
		case HK_ZOOM_CENTER:
		case HK_RESET_LOCAL_COORD:
			break;

		case HK_MOVEBLOCK_TO_DRAGBLOCK:    // Switch to drag mode, when block moving
			HandleBlockEndByPopUp(BLOCK_DRAG, DC);
			break;

		case HK_DELETE:
			if ( PopupOn ) break;
			RefreshToolBar = LocateAndDeleteItem(this, DC);
			m_CurrentScreen->SetModify();
			m_CurrentScreen->m_CurrentItem = NULL;
			TestDanglingEnds(m_CurrentScreen->EEDrawList, DC);
			break;

		case HK_REPEAT_LAST:
			if ( g_ItemToRepeat && (g_ItemToRepeat->m_Flags == 0)  )
			{
				RepeatDrawItem(DC);
			}
			else wxBell();
			break;

		case HK_NEXT_SEARCH :
			if ( g_LastSearchIsMarker ) WinEDA_SchematicFrame::FindMarker(1);
			else FindSchematicItem(wxEmptyString, 2);
			break;

        case HK_ADD_NEW_COMPONENT:	// Add component
			if ( DrawStruct && DrawStruct->m_Flags ) break;
            // switch to m_ID_current_state = ID_COMPONENT_BUTT;
            if ( m_ID_current_state != ID_COMPONENT_BUTT ) SetToolID( ID_COMPONENT_BUTT, wxCURSOR_PENCIL, _("Add Component"));
            OnLeftClick(DC, MousePos);
			break;

		case HK_BEGIN_WIRE:	// Add wire
 			if ( DrawStruct )	// An item is selected. If edited and not a wire, a new command is not possible
			{
				if ( DrawStruct->m_Flags )	// Item selected and edition in progress
				{
					if (DrawStruct->m_StructType == DRAW_SEGMENT_STRUCT_TYPE )
					{
						EDA_DrawLineStruct * segment = (EDA_DrawLineStruct *)DrawStruct;
						if ( segment->m_Layer != LAYER_WIRE ) break;
					}
					else break;
				}
			}
           // switch to m_ID_current_state = ID_WIRE_BUTT;
            if ( m_ID_current_state != ID_WIRE_BUTT ) SetToolID( ID_WIRE_BUTT, wxCURSOR_PENCIL, _("Add Wire"));
            OnLeftClick(DC, MousePos);
            break;

		case HK_ROTATE_COMPONENT:	// Component Rotation
			if ( DrawStruct == NULL )
			{
				DrawStruct = PickStruct( GetScreen()->m_Curseur,
					GetScreen()->EEDrawList, LIBITEM|TEXTITEM|LABELITEM );
				if ( DrawStruct == NULL ) break;
				if ( DrawStruct->m_StructType == DRAW_LIB_ITEM_STRUCT_TYPE )
						DrawStruct = LocateSmallestComponent( GetScreen() );
				if ( DrawStruct == NULL ) break;
			}
			switch (DrawStruct->m_StructType)
			{
				case DRAW_LIB_ITEM_STRUCT_TYPE:
					if ( DrawStruct->m_Flags == 0 )
					{
						SaveCopyInUndoList(DrawStruct, IS_CHANGED);
						RefreshToolBar = TRUE;
					}
						
					CmpRotationMiroir(
						(EDA_SchComponentStruct *) DrawStruct, DC, CMP_ROTATE_COUNTERCLOCKWISE );
					break;

				case DRAW_TEXT_STRUCT_TYPE:
				case DRAW_LABEL_STRUCT_TYPE:
				case DRAW_GLOBAL_LABEL_STRUCT_TYPE:
					if ( DrawStruct->m_Flags == 0 )
					{
						SaveCopyInUndoList(DrawStruct, IS_CHANGED);
						RefreshToolBar = TRUE;
					}
						ChangeTextOrient( (DrawTextStruct*)DrawStruct, DC);
						break;
			}
			break;

		case HK_MIRROR_Y_COMPONENT:	// Mirror Y (Component)
			if ( DrawStruct == NULL )
				DrawStruct = LocateSmallestComponent( GetScreen() );
			if ( DrawStruct )
			{
				if ( DrawStruct->m_Flags == 0 )
				{
					SaveCopyInUndoList(DrawStruct, IS_CHANGED);
					RefreshToolBar = TRUE;
				}
				CmpRotationMiroir(
					(EDA_SchComponentStruct *) DrawStruct, DC, CMP_MIROIR_Y );
			}
			break;

		case HK_MIRROR_X_COMPONENT:	// Mirror X (Component)
			if ( DrawStruct == NULL )
				DrawStruct = LocateSmallestComponent( GetScreen() );
			if ( DrawStruct )
			{
				if ( DrawStruct->m_Flags == 0 )
				{
					SaveCopyInUndoList(DrawStruct, IS_CHANGED);
					RefreshToolBar = TRUE;
				}
				CmpRotationMiroir(
					(EDA_SchComponentStruct *) DrawStruct, DC, CMP_MIROIR_X );
			}
			break;

 		case HK_ORIENT_NORMAL_COMPONENT:	// Orient 0, no mirror (Component)
			if ( DrawStruct == NULL )
				DrawStruct = LocateSmallestComponent( GetScreen() );
			if ( DrawStruct )
			{
				if ( DrawStruct->m_Flags == 0 )
				{
					SaveCopyInUndoList(DrawStruct, IS_CHANGED);
					RefreshToolBar = TRUE;
				}
				CmpRotationMiroir(
					(EDA_SchComponentStruct *) DrawStruct, DC, CMP_NORMAL );
				TestDanglingEnds(m_CurrentScreen->EEDrawList, DC);
			}
			break;

		case HK_MOVE_COMPONENT:	// Start move Component
			if ( PopupOn ) break;
			if ( DrawStruct == NULL )
				DrawStruct = LocateSmallestComponent( GetScreen() );
			if ( DrawStruct && (DrawStruct->m_Flags ==0) )
			{
				m_CurrentScreen->m_CurrentItem = DrawStruct;
				Process_Move_Item(m_CurrentScreen->m_CurrentItem, DC);
			}
			break;
	}
	
	if ( RefreshToolBar ) SetToolbars();
}


/***********************************************************/
void WinEDA_LibeditFrame::OnHotKey(wxDC * DC, int hotkey,
					EDA_BaseStruct * DrawStruct)
/***********************************************************/
/* Hot keys for the component editot. Some commands are relatives to the item under the mouse cursor
	Commands are case insensitive
	Zoom commands are not managed here
*/
{
bool PopupOn = m_CurrentScreen->m_CurrentItem  &&
			m_CurrentScreen->m_CurrentItem->m_Flags;

bool RefreshToolBar = FALSE;	// We must refresh tool bar when the undo/redo tool state is modified
	
	if ( hotkey == 0 ) return;

wxPoint MousePos = m_CurrentScreen->m_MousePosition; 

	/* Convert lower to upper case (the usual toupper function has problem with non ascii codes like function keys */
	if( (hotkey >= 'a') && (hotkey <= 'z') ) hotkey += 'A' - 'a';
	switch ( GetCommandCodeFromHotkey(hotkey, s_LibEdit_Hotkey_List)  )
	{
		default:
		case HK_NOT_FOUND:
			return;
			break;
		
		case HK_HELP:	// Display Current hotkey list
			DisplayHotkeyList(this, s_LibEdit_Hotkey_List);
			break;

		case HK_ZOOM_IN:
		case HK_ZOOM_OUT:
		case HK_ZOOM_REDRAW:
		case HK_ZOOM_CENTER:
		case HK_RESET_LOCAL_COORD:
			break;

		case HK_REPEAT_LAST:
			if ( LibItemToRepeat && (LibItemToRepeat->m_Flags == 0) &&
				 (LibItemToRepeat->m_StructType == COMPONENT_PIN_DRAW_TYPE) )
			{
				RepeatPinItem(DC, (LibDrawPin*) LibItemToRepeat);
			}
			else wxBell();
			break;
	}

	if ( RefreshToolBar ) SetToolbars();
}

