Commit 816974b8 authored by Mark Roszko's avatar Mark Roszko Committed by Wayne Stambaugh

Hot key editor dialog improvemnts.

* Change hot key editing contol from wxGridTableBase to wxListCtrl.
* Separate hot key lists into separate tabs rather than one large list.
* Coding policy fixes.
parents 41670806 c85d64ae
......@@ -179,7 +179,6 @@ set( COMMON_SRCS
grid_tricks.cpp
gr_basic.cpp
hotkeys_basic.cpp
hotkey_grid_table.cpp
html_messagebox.cpp
kiface_i.cpp
kiway.cpp
......
This diff is collapsed.
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 8 2012)
// C++ code generated with wxFormBuilder (version Jun 6 2014)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
......@@ -14,37 +14,18 @@ HOTKEYS_EDITOR_DIALOG_BASE::HOTKEYS_EDITOR_DIALOG_BASE( wxWindow* parent, wxWind
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bMainSizer;
bMainSizer = new wxBoxSizer( wxHORIZONTAL );
bMainSizer = new wxBoxSizer( wxVERTICAL );
m_hotkeyGrid = new wxGrid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDOUBLE_BORDER|wxTAB_TRAVERSAL|wxWANTS_CHARS );
m_staticText1 = new wxStaticText( this, wxID_ANY, _("Select a row and press a new key combination to alter the binding."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText1->Wrap( 400 );
bMainSizer->Add( m_staticText1, 0, wxALL|wxEXPAND, 5 );
// Grid
m_hotkeyGrid->CreateGrid( 1, 2 );
m_hotkeyGrid->EnableEditing( false );
m_hotkeyGrid->EnableGridLines( true );
m_hotkeyGrid->EnableDragGridSize( false );
m_hotkeyGrid->SetMargins( 0, 0 );
m_hotkeySections = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Columns
m_hotkeyGrid->AutoSizeColumns();
m_hotkeyGrid->EnableDragColMove( false );
m_hotkeyGrid->EnableDragColSize( true );
m_hotkeyGrid->SetColLabelSize( 30 );
m_hotkeyGrid->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
// Rows
m_hotkeyGrid->EnableDragRowSize( true );
m_hotkeyGrid->SetRowLabelSize( 0 );
m_hotkeyGrid->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
// Label Appearance
// Cell Defaults
m_hotkeyGrid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP );
bMainSizer->Add( m_hotkeyGrid, 1, wxALL|wxEXPAND, 5 );
bMainSizer->Add( m_hotkeySections, 1, wxEXPAND | wxALL, 5 );
wxBoxSizer* b_buttonsSizer;
b_buttonsSizer = new wxBoxSizer( wxVERTICAL );
b_buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
m_OKButton = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0 );
b_buttonsSizer->Add( m_OKButton, 0, wxALL|wxEXPAND, 5 );
......@@ -56,16 +37,13 @@ HOTKEYS_EDITOR_DIALOG_BASE::HOTKEYS_EDITOR_DIALOG_BASE( wxWindow* parent, wxWind
b_buttonsSizer->Add( m_undoButton, 0, wxALL|wxEXPAND, 5 );
bMainSizer->Add( b_buttonsSizer, 0, wxALIGN_CENTER_VERTICAL, 5 );
bMainSizer->Add( b_buttonsSizer, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5 );
this->SetSizer( bMainSizer );
this->Layout();
// Connect Events
m_hotkeyGrid->Connect( wxEVT_CHAR, wxKeyEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnKeyPressed ), NULL, this );
m_hotkeyGrid->Connect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnRightClickOnCell ), NULL, this );
m_hotkeyGrid->Connect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnClickOnCell ), NULL, this );
m_OKButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnOKClicked ), NULL, this );
m_cancelButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::CancelClicked ), NULL, this );
m_undoButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::UndoClicked ), NULL, this );
......@@ -74,9 +52,6 @@ HOTKEYS_EDITOR_DIALOG_BASE::HOTKEYS_EDITOR_DIALOG_BASE( wxWindow* parent, wxWind
HOTKEYS_EDITOR_DIALOG_BASE::~HOTKEYS_EDITOR_DIALOG_BASE()
{
// Disconnect Events
m_hotkeyGrid->Disconnect( wxEVT_CHAR, wxKeyEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnKeyPressed ), NULL, this );
m_hotkeyGrid->Disconnect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnRightClickOnCell ), NULL, this );
m_hotkeyGrid->Disconnect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnClickOnCell ), NULL, this );
m_OKButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnOKClicked ), NULL, this );
m_cancelButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::CancelClicked ), NULL, this );
m_undoButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::UndoClicked ), NULL, this );
......
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 8 2012)
// C++ code generated with wxFormBuilder (version Jun 6 2014)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
......@@ -14,12 +14,13 @@
class DIALOG_SHIM;
#include "dialog_shim.h"
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/font.h>
#include <wx/grid.h>
#include <wx/stattext.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/notebook.h>
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/dialog.h>
......@@ -35,15 +36,13 @@ class HOTKEYS_EDITOR_DIALOG_BASE : public DIALOG_SHIM
private:
protected:
wxGrid* m_hotkeyGrid;
wxStaticText* m_staticText1;
wxNotebook* m_hotkeySections;
wxButton* m_OKButton;
wxButton* m_cancelButton;
wxButton* m_undoButton;
// Virtual event handlers, overide them in your derived class
virtual void OnKeyPressed( wxKeyEvent& event ) { event.Skip(); }
virtual void OnRightClickOnCell( wxGridEvent& event ) { event.Skip(); }
virtual void OnClickOnCell( wxGridEvent& event ) { event.Skip(); }
virtual void OnOKClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void CancelClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void UndoClicked( wxCommandEvent& event ) { event.Skip(); }
......@@ -51,7 +50,7 @@ class HOTKEYS_EDITOR_DIALOG_BASE : public DIALOG_SHIM
public:
HOTKEYS_EDITOR_DIALOG_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Hotkeys Editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 304,235 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
HOTKEYS_EDITOR_DIALOG_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Hotkeys Editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 450,500 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~HOTKEYS_EDITOR_DIALOG_BASE();
};
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <hotkey_grid_table.h>
/*
* Reads the hotkey table from its stored format into a format suitable
* for a wxGrid.
*/
HOTKEY_EDITOR_GRID_TABLE::HOTKEY_EDITOR_GRID_TABLE( struct EDA_HOTKEY_CONFIG* origin ) :
wxGridTableBase(), m_hotkeys()
{
EDA_HOTKEY_CONFIG* section;
for( section = origin; section->m_HK_InfoList; section++ )
{
// Add a dummy hotkey_spec which is a header before each hotkey list
hotkey_spec spec( *section->m_SectionTag, NULL );
m_hotkeys.push_back( spec );
EDA_HOTKEY** hotkey_descr_list;
// Add hotkeys descr
for( hotkey_descr_list = section->m_HK_InfoList; *hotkey_descr_list;
hotkey_descr_list++ )
{
EDA_HOTKEY* hotkey_descr = *hotkey_descr_list;
hotkey_spec spec( *section->m_SectionTag, new EDA_HOTKEY( hotkey_descr ) );
m_hotkeys.push_back( spec );
}
}
}
HOTKEY_EDITOR_GRID_TABLE::hotkey_spec_vector& HOTKEY_EDITOR_GRID_TABLE::getHotkeys()
{
return m_hotkeys;
}
int HOTKEY_EDITOR_GRID_TABLE::GetNumberRows()
{
return m_hotkeys.size();
}
int HOTKEY_EDITOR_GRID_TABLE::GetNumberCols()
{
return 2;
}
bool HOTKEY_EDITOR_GRID_TABLE::IsEmptyCell( int row, int col )
{
return col == 1 && m_hotkeys[row].second == NULL;
}
wxString HOTKEY_EDITOR_GRID_TABLE::GetValue( int row, int col )
{
EDA_HOTKEY* hotkey_descr = m_hotkeys[row].second;
if( col == 0 )
{
if( hotkey_descr == NULL )
{
// section header
return m_hotkeys[row].first;
}
else
{
return hotkey_descr->m_InfoMsg;
}
}
else
{
if( hotkey_descr == NULL )
{
// section header
return wxEmptyString;
}
else
{
return KeyNameFromKeyCode( hotkey_descr->m_KeyCode );
}
}
}
void HOTKEY_EDITOR_GRID_TABLE::SetValue( int row, int col, const wxString& value )
{
}
wxString HOTKEY_EDITOR_GRID_TABLE::GetTypeName( int row, int col )
{
return wxGRID_VALUE_STRING;
}
bool HOTKEY_EDITOR_GRID_TABLE::CanGetValueAs( int row, int col, const wxString& typeName )
{
return typeName == wxGRID_VALUE_STRING && col == 2;
}
bool HOTKEY_EDITOR_GRID_TABLE::CanSetValueAs( int row, int col, const wxString& typeName )
{
return false;
}
long HOTKEY_EDITOR_GRID_TABLE::GetValueAsLong( int row, int col )
{
return -1L;
}
double HOTKEY_EDITOR_GRID_TABLE::GetValueAsDouble( int row, int col )
{
return 0.0;
}
bool HOTKEY_EDITOR_GRID_TABLE::GetValueAsBool( int row, int col )
{
return false;
}
void HOTKEY_EDITOR_GRID_TABLE::SetValueAsLong( int row, int col, long value )
{
}
void HOTKEY_EDITOR_GRID_TABLE::SetValueAsDouble( int row, int col, double value )
{
}
void HOTKEY_EDITOR_GRID_TABLE::SetValueAsBool( int row, int col, bool value )
{
}
void* HOTKEY_EDITOR_GRID_TABLE::GetValueAsCustom( int row, int col )
{
return 0;
}
void HOTKEY_EDITOR_GRID_TABLE::SetValueAsCustom( int row, int col, void* value )
{
}
wxString HOTKEY_EDITOR_GRID_TABLE::GetColLabelValue( int col )
{
return col == 0 ? _( "Command" ) : _( "Hotkey" );
}
bool HOTKEY_EDITOR_GRID_TABLE::IsHeader( int row )
{
return m_hotkeys[row].second == NULL;
}
void HOTKEY_EDITOR_GRID_TABLE::SetKeyCode( int row, long key )
{
m_hotkeys[row].second->m_KeyCode = key;
}
void HOTKEY_EDITOR_GRID_TABLE::RestoreFrom( struct EDA_HOTKEY_CONFIG* origin )
{
int row = 0;
EDA_HOTKEY_CONFIG* section;
for( section = origin; section->m_HK_InfoList; section++ )
{
++row; // Skip header
EDA_HOTKEY** info_ptr;
for( info_ptr = section->m_HK_InfoList; *info_ptr; info_ptr++ )
{
EDA_HOTKEY* info = *info_ptr;
m_hotkeys[row++].second->m_KeyCode = info->m_KeyCode;
}
}
}
HOTKEY_EDITOR_GRID_TABLE::~HOTKEY_EDITOR_GRID_TABLE()
{
hotkey_spec_vector::iterator i;
for( i = m_hotkeys.begin(); i != m_hotkeys.end(); ++i )
delete i->second;
}
......@@ -52,6 +52,12 @@ wxString g_LibEditSectionTag( wxT( "[libedit]" ) );
wxString g_BoardEditorSectionTag( wxT( "[pcbnew]" ) );
wxString g_ModuleEditSectionTag( wxT( "[footprinteditor]" ) );
wxString g_CommonSectionTitle( wxT( "Common" ) );
wxString g_SchematicSectionTitle( wxT( "Schematic Editor" ) );
wxString g_LibEditSectionTitle( wxT( "Library Editor" ) );
wxString g_BoardEditorSectionTitle( wxT( "Board Editor" ) );
wxString g_ModuleEditSectionTitle( wxT( "Footprint Editor" ) );
/* Class to handle hotkey commnands. hotkeys have a default value
* This class allows the real key code changed by user from a key code list
......@@ -161,6 +167,10 @@ wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
int ii;
bool found = false;
// Assume keycode of 0 is "unassigned"
if( aKeycode == 0 )
return wxT( "<unassigned>");
if( (aKeycode & GR_KB_CTRL) != 0 )
modifier << MODIFIER_CTRL;
......@@ -224,6 +234,7 @@ static void AddModifierToKey( wxString& aFullKey, const wxString & aKey )
aFullKey << wxT( "\t" ) << MODIFIER_ALT << aKey;
}
/* AddHotkeyName
* Add the key name from the Command id value ( m_Idcommand member value)
* aText = a wxString. returns aText + key name
......@@ -307,6 +318,7 @@ wxString AddHotkeyName( const wxString& aText,
msg << wxT( " (" ) << keyname << wxT( ")" );
break;
}
break;
}
}
......@@ -414,9 +426,9 @@ void DisplayHotkeyList( EDA_DRAW_FRAME* aFrame, struct EDA_HOTKEY_CONFIG* aDescL
wxString msg = wxT( "<html><body bgcolor=\"#E2E2E2\">" );
msg += wxT( "<H3>");
msg += _("Hotkeys List");
msg += wxT("</H3> <table cellpadding=\"0\">");
msg += wxT( "<H3>" );
msg += _( "Hotkeys List" );
msg += wxT( "</H3> <table cellpadding=\"0\">" );
for( ; aDescList->m_HK_InfoList != NULL; aDescList++ )
{
......@@ -432,16 +444,16 @@ void DisplayHotkeyList( EDA_DRAW_FRAME* aFrame, struct EDA_HOTKEY_CONFIG* aDescL
// Some chars should be modified, using html encoding, to be
// displayed by DisplayHtmlInfoMessage()
keyname.Replace( wxT("<"), wxT("&lt;") );
keyname.Replace( wxT(">"), wxT("&gt;") );
msg += wxT( "<tr><td>" ) + hk_decr->m_InfoMsg + wxT("</td>");
msg += wxT("<td><b>&nbsp;&nbsp;") + keyname + wxT( "</b></td></tr>" );
keyname.Replace( wxT( "<" ), wxT( "&lt;" ) );
keyname.Replace( wxT( ">" ), wxT( "&gt;" ) );
msg += wxT( "<tr><td>" ) + hk_decr->m_InfoMsg + wxT( "</td>" );
msg += wxT( "<td><b>&nbsp;&nbsp;" ) + keyname + wxT( "</b></td></tr>" );
}
}
}
msg += wxT("</table></html></body>");
DisplayHtmlInfoMessage( aFrame, _("Hotkeys List"), msg, wxSize(340, 750));
msg += wxT( "</table></html></body>" );
DisplayHtmlInfoMessage( aFrame, _( "Hotkeys List" ), msg, wxSize( 340, 750 ) );
}
......@@ -466,17 +478,6 @@ EDA_HOTKEY* GetDescriptorFromHotkey( int aKey, EDA_HOTKEY** aList )
}
/**
* Function WriteHotkeyConfig
* Store the current hotkey list
* It is stored using the standard wxConfig mechanism or a file.
*
* @param aDescList = pointer to the current hotkey list.
* @param aFullFileName = a wxString pointer to a full file name.
* if NULL, use the standard wxConfig mechanism (default)
* the output format is: shortcut "key" "function"
* lines starting with # are comments
*/
int EDA_BASE_FRAME::WriteHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList,
wxString* aFullFileName )
{
......@@ -490,10 +491,10 @@ int EDA_BASE_FRAME::WriteHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList,
for( ; aDescList->m_HK_InfoList != NULL; aDescList++ )
{
if( aDescList->m_Comment )
if( aDescList->m_Title )
{
msg += wxT( "# " );
msg += wxString( aDescList->m_Comment );
msg += *aDescList->m_Title;
msg += wxT( "\n" );
}
......@@ -542,13 +543,6 @@ int EDA_BASE_FRAME::WriteHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList,
}
/**
* Function ReadHotkeyConfigFile
* Read an old configuration file (&ltfile&gt.key) and fill the current hotkey list
* with hotkeys
* @param aFilename = file name to read.
* @param aDescList = current hotkey list descr. to initialise.
*/
int EDA_BASE_FRAME::ReadHotkeyConfigFile( const wxString& aFilename,
struct EDA_HOTKEY_CONFIG* aDescList )
{
......@@ -574,6 +568,7 @@ int EDA_BASE_FRAME::ReadHotkeyConfigFile( const wxString& aFilename,
return 1;
}
void ReadHotkeyConfig( const wxString& Appname, struct EDA_HOTKEY_CONFIG* aDescList )
{
wxConfigBase* config = GetNewConfig( Appname );
......@@ -591,6 +586,7 @@ void ReadHotkeyConfig( const wxString& Appname, struct EDA_HOTKEY_CONFIG* aDescL
ParseHotkeyConfig( data, aDescList );
}
/* Function ReadHotkeyConfig
* Read configuration data and fill the current hotkey list with hotkeys
* aDescList is the current hotkey list descr. to initialize.
......
......@@ -131,7 +131,7 @@ static EDA_HOTKEY HkRedo( wxT( "Redo" ), HK_REDO, GR_KB_SHIFT + GR_KB_CTRL + 'Z'
// mouse click command:
static EDA_HOTKEY HkMouseLeftClick( wxT( "Mouse Left Click" ), HK_LEFT_CLICK, WXK_RETURN, 0 );
static EDA_HOTKEY HkMouseLeftDClick( wxT( "Mouse Left DClick" ), HK_LEFT_DCLICK, WXK_END, 0 );
static EDA_HOTKEY HkMouseLeftDClick( wxT( "Mouse Left Double Click" ), HK_LEFT_DCLICK, WXK_END, 0 );
// Schematic editor
static EDA_HOTKEY HkBeginWire( wxT( "Begin Wire" ), HK_BEGIN_WIRE, 'W', ID_WIRE_BUTT );
......@@ -149,7 +149,7 @@ static EDA_HOTKEY HkAddComponent( wxT( "Add Component" ), HK_ADD_NEW_COMPONENT,
ID_SCH_PLACE_COMPONENT );
static EDA_HOTKEY HkAddPower( wxT( "Add Power" ), HK_ADD_NEW_POWER, 'P',
ID_PLACE_POWER_BUTT );
static EDA_HOTKEY HkAddNoConn( wxT( "Add NoConnected Flag" ), HK_ADD_NOCONN_FLAG, 'Q',
static EDA_HOTKEY HkAddNoConn( wxT( "Add No Connect Flag" ), HK_ADD_NOCONN_FLAG, 'Q',
ID_NOCONN_BUTT );
static EDA_HOTKEY HkAddHierSheet( wxT( "Add Sheet" ), HK_ADD_HIER_SHEET, 'S',
ID_SHEET_SYMBOL_BUTT );
......@@ -207,7 +207,7 @@ static EDA_HOTKEY HkInsertPin( wxT( "Repeat Pin" ), HK_REPEAT_LAST, WXK_INSERT )
static EDA_HOTKEY HkMoveLibItem( wxT( "Move Library Item" ), HK_LIBEDIT_MOVE_GRAPHIC_ITEM, 'M' );
// Load/save files
static EDA_HOTKEY HkSaveLib( wxT( "Save Lib" ), HK_SAVE_LIB, 'S' + GR_KB_CTRL );
static EDA_HOTKEY HkSaveLib( wxT( "Save Library" ), HK_SAVE_LIB, 'S' + GR_KB_CTRL );
static EDA_HOTKEY HkSaveSchematic( wxT( "Save Schematic" ), HK_SAVE_SCH, 'S' + GR_KB_CTRL );
static EDA_HOTKEY HkLoadSchematic( wxT( "Load Schematic" ), HK_LOAD_SCH, 'L' + GR_KB_CTRL );
......@@ -305,9 +305,9 @@ static EDA_HOTKEY* viewlib_Hotkey_List[] =
// an hotkey config file)
struct EDA_HOTKEY_CONFIG g_Eeschema_Hokeys_Descr[] =
{
{ &g_CommonSectionTag, common_Hotkey_List, L"Common keys" },
{ &g_SchematicSectionTag, schematic_Hotkey_List, L"Schematic editor keys" },
{ &g_LibEditSectionTag, libEdit_Hotkey_List, L"library editor keys" },
{ &g_CommonSectionTag, common_Hotkey_List, &g_CommonSectionTitle },
{ &g_SchematicSectionTag, schematic_Hotkey_List, &g_SchematicSectionTitle },
{ &g_LibEditSectionTag, libEdit_Hotkey_List, &g_LibEditSectionTitle },
{ NULL, NULL, NULL }
};
......@@ -315,8 +315,8 @@ struct EDA_HOTKEY_CONFIG g_Eeschema_Hokeys_Descr[] =
// (used to list current hotkeys)
struct EDA_HOTKEY_CONFIG g_Schematic_Hokeys_Descr[] =
{
{ &g_CommonSectionTag, common_Hotkey_List, NULL },
{ &g_SchematicSectionTag, schematic_Hotkey_List, NULL },
{ &g_CommonSectionTag, common_Hotkey_List, &g_CommonSectionTitle },
{ &g_SchematicSectionTag, schematic_Hotkey_List, &g_SchematicSectionTitle },
{ NULL, NULL, NULL }
};
......@@ -324,8 +324,8 @@ struct EDA_HOTKEY_CONFIG g_Schematic_Hokeys_Descr[] =
// (used to list current hotkeys)
struct EDA_HOTKEY_CONFIG g_Libedit_Hokeys_Descr[] =
{
{ &g_CommonSectionTag, common_Hotkey_List, NULL },
{ &g_LibEditSectionTag, libEdit_Hotkey_List, NULL },
{ &g_CommonSectionTag, common_Hotkey_List, &g_CommonSectionTitle },
{ &g_LibEditSectionTag, libEdit_Hotkey_List, &g_LibEditSectionTitle },
{ NULL, NULL, NULL }
};
......@@ -333,7 +333,7 @@ struct EDA_HOTKEY_CONFIG g_Libedit_Hokeys_Descr[] =
// (used to list current hotkeys)
struct EDA_HOTKEY_CONFIG g_Viewlib_Hokeys_Descr[] =
{
{ &g_CommonSectionTag, common_basic_Hotkey_List, NULL },
{ &g_CommonSectionTag, common_basic_Hotkey_List, &g_CommonSectionTitle },
{ NULL, NULL, NULL }
};
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file dialog_hotkeys_editor.h
*/
#ifndef __dialog_hotkeys_editor__
#define __dialog_hotkeys_editor__
......@@ -17,34 +43,222 @@
#include <wx/grid.h>
#include <hotkeys_basic.h>
#include <hotkey_grid_table.h>
#include <draw_frame.h>
#include <../common/dialogs/dialog_hotkeys_editor_base.h>
class HOTKEYS_EDITOR_DIALOG;
/**
* Class HOTKEY_LIST_CTRL
* is a class to contain the contents of a hotkey editor tab page.
*/
class HOTKEY_LIST_CTRL : public wxListCtrl
{
public:
HOTKEY_LIST_CTRL( wxWindow* aParent, struct EDA_HOTKEY_CONFIG* aSection );
~HOTKEY_LIST_CTRL() {};
/**
* Function DeselectRow
* Deselect the given row
*
* @param aRow is the row to deselect
*/
void DeselectRow( int aRow );
/**
* Function GetHotkeys
* Access to return the vector used for the list control data. This will contain the
* "live" state of the user's configuration.
*
* @return Pointer to vector of hotkey settings
*/
std::vector< EDA_HOTKEY* >& GetHotkeys() { return m_hotkeys; }
/**
* Function RestoreFrom
* Restores list control hotkey keycodes to the keycodes present in the
* given hotkey configuration array.
*
* @param aSection is a pointer to the hotkey configuration array
*/
void RestoreFrom( struct EDA_HOTKEY_CONFIG* aSection );
private:
int m_curEditingRow;
wxString* m_sectionTag;
std::vector< EDA_HOTKEY* > m_hotkeys;
/**
* Function recalculateColumns
* Adjusts the width of grid columns in proportion of the max text length of both
*/
void recalculateColumns();
protected:
/**
* Function OnGetItemText
* Returns the requested row, column data to the list control.
*
* @param aRow is the row of the data which matches our hotkeys vector as a index
* @param aColumn is the column of the data which is either Command(0) or KeyCode(1)
*
* @return String containing the text for the specified row, column combination
*/
wxString OnGetItemText( long aRow, long aColumn ) const;
/**
* Function OnChar
* Decoded key press handler which is used to set key codes in the list control
*
* @param aEvent is the key press event, the keycode is retrieved from it
*/
void OnChar( wxKeyEvent& aEvent );
/**
* Function OnListItemSelected
* Item selection handler which is used to record what index is selected to alter
* update with the key press
*
* @param aEvent is the button press event, unused
*/
void OnListItemSelected( wxListEvent& aEvent );
/**
* Function OnSize
* Sizing update handler to recompute the column widths dynamically and maximize them.
* Due to wxWidget's broken autosizing support (it's completely inconsistent across
* platforms), we just do it based on a scale of
*
* @param aEvent is the button press event, unused
*/
void OnSize( wxSizeEvent& aEvent );
};
/**
* Class HOTKEY_SECTION_PAGE
* is a class to contain the contents of a hotkey editor tab page.
*/
class HOTKEY_SECTION_PAGE : public wxPanel
{
public:
private:
EDA_HOTKEY_CONFIG* m_hotkeySection;
HOTKEY_LIST_CTRL *m_hotkeyList;
HOTKEYS_EDITOR_DIALOG* m_dialog;
public:
/** Constructor to create a setup page for one netlist format.
* Used in Netlist format Dialog box creation
* @param parent = wxNotebook * parent
* @param title = title (name) of the notebook page
* @param id_NetType = netlist type id
*/
HOTKEY_SECTION_PAGE( HOTKEYS_EDITOR_DIALOG* aDialog, wxNotebook* aParent,
const wxString& aTitle,
EDA_HOTKEY_CONFIG* aSection );
~HOTKEY_SECTION_PAGE() {};
/**
* Function Restore
* Resets the hotkeys back to their original unedited state
*/
void Restore();
/**
* Function GetHotkeys
* Accessor to retrieve hotkeys list from list control
*
* @return Pointer to vector used for list control data
*/
std::vector< EDA_HOTKEY* >& GetHotkeys() { return m_hotkeyList->GetHotkeys(); }
/**
* Function GetHotkeySection
* Accessor to retrieve hotkey configuration array assigned to a tab control page
*
* @return Pointer to hotkey configuration array
*/
EDA_HOTKEY_CONFIG* GetHotkeySection() { return m_hotkeySection; }
/**
* Function GetDialog
* Returns pointer to parent dialog window
*
* @return Pointer to parent dialog window
*/
HOTKEYS_EDITOR_DIALOG* GetDialog() { return m_dialog; }
};
/**
* Class HOTKEYS_EDITOR_DIALOG
* is the child class of HOTKEYS_EDITOR_DIALOG_BASE. This is the class
* used to create a hotkey editor.
*/
class HOTKEYS_EDITOR_DIALOG : public HOTKEYS_EDITOR_DIALOG_BASE
{
protected:
EDA_DRAW_FRAME* m_parent;
struct EDA_HOTKEY_CONFIG* m_hotkeys;
HOTKEY_EDITOR_GRID_TABLE* m_table;
int m_curEditingRow;
std::vector<HOTKEY_SECTION_PAGE*> m_hotkeySectionPages;
public:
HOTKEYS_EDITOR_DIALOG( EDA_DRAW_FRAME* parent, EDA_HOTKEY_CONFIG* hotkeys );
HOTKEYS_EDITOR_DIALOG( EDA_DRAW_FRAME* aParent, EDA_HOTKEY_CONFIG* aHotkeys );
~HOTKEYS_EDITOR_DIALOG() {};
/**
* Function CanSetKey
* Check if we can set a hotkey, this will prompt the user if there
* is a conflict between keys. The key code should have already been
* checked that it's not for the same entry as its currently in or else
* it'll prompt the change on itself.
* The function will do conflict detection depending on aSectionTag.
* g_CommonSectionTag means the key code must be checked with all sections.
* While other tags means the key code only must be checked with the aSectionTag
* section and g_CommonSectionTag section.
*
* @param aKey is the key code that wants to be set
* @param aSectionTag is the section tag that the key code came from
*
* @return True if the user accepted the overwrite or no conflict existed
*/
bool CanSetKey( long aKey, const wxString* aSectionTag );
private:
void OnOKClicked( wxCommandEvent& event );
void CancelClicked( wxCommandEvent& event );
void UndoClicked( wxCommandEvent& event );
void OnClickOnCell( wxGridEvent& event );
void OnRightClickOnCell( wxGridEvent& event );
void OnKeyPressed( wxKeyEvent& event );
void SetHotkeyCellState( int aRow, bool aHightlight );
/**
* Function OnOKClicked
* Close the dialog and make save all changes to hotkeys
*
* @param aEvent is the button press event, unused
*/
void OnOKClicked( wxCommandEvent& aEvent );
/**
* Function CancelClicked
* Close the dialog and make no changes to hotkeys
*
* @param aEvent is the button press event, unused
*/
void CancelClicked( wxCommandEvent& aEvent );
/**
* Function UndoClicked
* Reinit the hotkeys to the initial state (removes all pending changes)
*
* @param aEvent is the button press event, unused
*/
void UndoClicked( wxCommandEvent& aEvent );
};
void InstallHotkeyFrame( EDA_DRAW_FRAME* parent, EDA_HOTKEY_CONFIG* hotkeys );
/**
* Function InstallHotkeyFrame
* Create a hotkey editor dialog window with the provided hotkey configuration array
*
* @param aParent is the parent window
* @param aHotkeys is the hotkey configuration array
*/
void InstallHotkeyFrame( EDA_DRAW_FRAME* aParent, EDA_HOTKEY_CONFIG* aHotkeys );
#endif
#ifndef __hotkeys_grid_table__
#define __hotkeys_grid_table__
#include <wx/intl.h>
#include <wx/string.h>
#include <wx/grid.h>
#include <vector>
#include <utility>
#include <fctsys.h>
#include <pgm_base.h>
#include <common.h>
#include <hotkeys_basic.h>
class HOTKEY_EDITOR_GRID_TABLE : public wxGridTableBase
{
public:
typedef std::pair< wxString, EDA_HOTKEY* > hotkey_spec;
typedef std::vector< hotkey_spec > hotkey_spec_vector;
HOTKEY_EDITOR_GRID_TABLE( struct EDA_HOTKEY_CONFIG* origin );
virtual ~HOTKEY_EDITOR_GRID_TABLE();
hotkey_spec_vector& getHotkeys();
private:
virtual int GetNumberRows();
virtual int GetNumberCols();
virtual bool IsEmptyCell( int row, int col );
virtual wxString GetValue( int row, int col );
virtual void SetValue( int row, int col, const wxString& value );
virtual wxString GetTypeName( int row, int col );
virtual bool CanGetValueAs( int row, int col, const wxString& typeName );
virtual bool CanSetValueAs( int row, int col, const wxString& typeName );
virtual long GetValueAsLong( int row, int col );
virtual double GetValueAsDouble( int row, int col );
virtual bool GetValueAsBool( int row, int col );
virtual void SetValueAsLong( int row, int col, long value );
virtual void SetValueAsDouble( int row, int col, double value );
virtual void SetValueAsBool( int row, int col, bool value );
virtual void* GetValueAsCustom( int row, int col );
virtual void SetValueAsCustom( int row, int col, void* value );
virtual wxString GetColLabelValue( int col );
public:
virtual bool IsHeader( int row );
virtual void SetKeyCode( int row, long key );
virtual void RestoreFrom( struct EDA_HOTKEY_CONFIG* origin );
protected:
std::vector< hotkey_spec > m_hotkeys;
};
#endif
......@@ -44,6 +44,12 @@ extern wxString g_LibEditSectionTag;
extern wxString g_BoardEditorSectionTag;
extern wxString g_ModuleEditSectionTag;
extern wxString g_CommonSectionTitle;
extern wxString g_SchematicSectionTitle;
extern wxString g_LibEditSectionTitle;
extern wxString g_BoardEditorSectionTitle;
extern wxString g_ModuleEditSectionTitle;
/**
* class EDA_HOTKEY
......@@ -81,7 +87,7 @@ struct EDA_HOTKEY_CONFIG
public:
wxString* m_SectionTag; // The configuration file section name.
EDA_HOTKEY** m_HK_InfoList; // List of EDA_HOTKEY pointers
const wchar_t* m_Comment; // Will be printed in the config file only.
wxString* m_Title; // Title displayed in hotkey editor and used as comment in file
};
......
......@@ -59,7 +59,7 @@
// mouse click command:
static EDA_HOTKEY HkMouseLeftClick( wxT( "Mouse Left Click" ), HK_LEFT_CLICK, WXK_RETURN, 0 );
static EDA_HOTKEY HkMouseLeftDClick( wxT( "Mouse Left DClick" ), HK_LEFT_DCLICK, WXK_END, 0 );
static EDA_HOTKEY HkMouseLeftDClick( wxT( "Mouse Left Double Click" ), HK_LEFT_DCLICK, WXK_END, 0 );
static EDA_HOTKEY HkResetLocalCoord( wxT( "Reset Local Coordinates" ),
HK_RESET_LOCAL_COORD, ' ' );
......@@ -107,11 +107,12 @@ EDA_HOTKEY* s_PlEditor_Hotkey_List[] =
// list of sections and corresponding hotkey list for Pl_Editor
// (used to create an hotkey config file)
wxString s_PlEditorSectionTag( wxT( "[pl_editor]" ) );
wxString s_PlEditorSectionTitle( wxT( "Part Layout Editor" ) );
struct EDA_HOTKEY_CONFIG s_PlEditor_Hokeys_Descr[] =
{
{ &g_CommonSectionTag, s_Common_Hotkey_List, L"Common keys" },
{ &s_PlEditorSectionTag, s_PlEditor_Hotkey_List, L"pl_editor keys" },
{ &g_CommonSectionTag, s_Common_Hotkey_List, &g_CommonSectionTitle },
{ &s_PlEditorSectionTag, s_PlEditor_Hotkey_List, &s_PlEditorSectionTitle },
{ NULL, NULL, NULL }
};
......
......@@ -36,7 +36,7 @@
// mouse click command:
static EDA_HOTKEY HkMouseLeftClick( wxT( "Mouse Left Click" ),
HK_LEFT_CLICK, WXK_RETURN, 0 );
static EDA_HOTKEY HkMouseLeftDClick( wxT( "Mouse Left DClick" ),
static EDA_HOTKEY HkMouseLeftDClick( wxT( "Mouse Left Double Click" ),
HK_LEFT_DCLICK, WXK_END, 0 );
static EDA_HOTKEY HkSwitch2CopperLayer( wxT( "Switch to Copper (B.Cu) layer" ),
......@@ -64,21 +64,21 @@ static EDA_HOTKEY HkSwitch2PreviousCopperLayer( wxT( "Switch to Previous Layer"
HK_SWITCH_LAYER_TO_PREVIOUS, '-' );
static EDA_HOTKEY HkSaveModule( wxT( "Save Module" ), HK_SAVE_MODULE, 'S' + GR_KB_CTRL );
static EDA_HOTKEY HkSavefile( wxT( "Save board" ), HK_SAVE_BOARD, 'S' + GR_KB_CTRL );
static EDA_HOTKEY HkSavefileAs( wxT( "Save board as" ), HK_SAVE_BOARD_AS, 'S' + GR_KB_CTRL + GR_KB_SHIFT );
static EDA_HOTKEY HkLoadfile( wxT( "Load board" ), HK_LOAD_BOARD, 'L' + GR_KB_CTRL );
static EDA_HOTKEY HkSavefile( wxT( "Save Board" ), HK_SAVE_BOARD, 'S' + GR_KB_CTRL );
static EDA_HOTKEY HkSavefileAs( wxT( "Save Board As" ), HK_SAVE_BOARD_AS, 'S' + GR_KB_CTRL + GR_KB_SHIFT );
static EDA_HOTKEY HkLoadfile( wxT( "Load Board" ), HK_LOAD_BOARD, 'L' + GR_KB_CTRL );
static EDA_HOTKEY HkFindItem( wxT( "Find Item" ), HK_FIND_ITEM, 'F' + GR_KB_CTRL );
static EDA_HOTKEY HkBackspace( wxT( "Delete track segment" ), HK_BACK_SPACE, WXK_BACK );
static EDA_HOTKEY HkAddNewTrack( wxT( "Add new track" ), HK_ADD_NEW_TRACK, 'X' );
static EDA_HOTKEY HkBackspace( wxT( "Delete Track Segment" ), HK_BACK_SPACE, WXK_BACK );
static EDA_HOTKEY HkAddNewTrack( wxT( "Add New Track" ), HK_ADD_NEW_TRACK, 'X' );
static EDA_HOTKEY HkAddThroughVia( wxT( "Add Through Via" ), HK_ADD_THROUGH_VIA, 'V' );
static EDA_HOTKEY HkSelLayerAndAddThroughVia( wxT( "Sel Layer and Add Through Via" ),
static EDA_HOTKEY HkSelLayerAndAddThroughVia( wxT( "Select Layer and Add Through Via" ),
HK_SEL_LAYER_AND_ADD_THROUGH_VIA, '<' );
static EDA_HOTKEY HkAddMicroVia( wxT( "Add MicroVia" ), HK_ADD_MICROVIA, 'V' + GR_KB_CTRL );
static EDA_HOTKEY HkAddBlindBuriedVia( wxT( "Add Blind/Buried Via" ), HK_ADD_BLIND_BURIED_VIA, 'V' + GR_KB_ALT );
static EDA_HOTKEY HkSelLayerAndAddBlindBuriedVia( wxT( "Sel Layer and Add Blind/Buried Via" ),
static EDA_HOTKEY HkSelLayerAndAddBlindBuriedVia( wxT( "Select Layer and Add Blind/Buried Via" ),
HK_SEL_LAYER_AND_ADD_BLIND_BURIED_VIA, '<' + GR_KB_ALT );
static EDA_HOTKEY HkSwitchTrackPosture( wxT( "Switch Track Posture" ), HK_SWITCH_TRACK_POSTURE, '/' );
static EDA_HOTKEY HkDragTrackKeepSlope( wxT( "Drag track keep slope" ), HK_DRAG_TRACK_KEEP_SLOPE, 'D' );
static EDA_HOTKEY HkDragTrackKeepSlope( wxT( "Drag Track Keep Slope" ), HK_DRAG_TRACK_KEEP_SLOPE, 'D' );
static EDA_HOTKEY HkPlaceItem( wxT( "Place Item" ), HK_PLACE_ITEM, 'P' );
static EDA_HOTKEY HkEditBoardItem( wxT( "Edit Item" ), HK_EDIT_ITEM, 'E' );
static EDA_HOTKEY HkFlipItem( wxT( "Flip Item" ), HK_FLIP_ITEM, 'F' );
......@@ -90,16 +90,16 @@ static EDA_HOTKEY HkGetAndMoveFootprint( wxT( "Get and Move Footprint" ), HK_GET
static EDA_HOTKEY HkLock_Unlock_Footprint( wxT( "Lock/Unlock Footprint" ), HK_LOCK_UNLOCK_FOOTPRINT, 'L' );
static EDA_HOTKEY HkDelete( wxT( "Delete Track or Footprint" ), HK_DELETE, WXK_DELETE );
static EDA_HOTKEY HkResetLocalCoord( wxT( "Reset Local Coordinates" ), HK_RESET_LOCAL_COORD, ' ' );
static EDA_HOTKEY HkSwitchHighContrastMode( wxT("Switch Highcontrast mode"), HK_SWITCH_HIGHCONTRAST_MODE,'H');
static EDA_HOTKEY HkSwitchHighContrastMode( wxT( "Toggle High Contrast Mode" ), HK_SWITCH_HIGHCONTRAST_MODE,'H');
static EDA_HOTKEY HkSetGridOrigin( wxT("Set Grid Origin"), HK_SET_GRID_ORIGIN, 'S' );
static EDA_HOTKEY HkResetGridOrigin( wxT("Reset Grid Origin"), HK_RESET_GRID_ORIGIN, 'Z' );
static EDA_HOTKEY HkSetGridOrigin( wxT( "Set Grid Origin" ), HK_SET_GRID_ORIGIN, 'S' );
static EDA_HOTKEY HkResetGridOrigin( wxT( "Reset Grid Origin" ), HK_RESET_GRID_ORIGIN, 'Z' );
static EDA_HOTKEY HkCanvasDefault( wxT( "Switch to default canvas" ),
static EDA_HOTKEY HkCanvasDefault( wxT( "Switch to Default Canvas" ),
HK_CANVAS_DEFAULT, WXK_F9 );
static EDA_HOTKEY HkCanvasOpenGL( wxT( "Switch to OpenGL canvas" ),
static EDA_HOTKEY HkCanvasOpenGL( wxT( "Switch to OpenGL Canvas" ),
HK_CANVAS_OPENGL, WXK_F11 );
static EDA_HOTKEY HkCanvasCairo( wxT( "Switch to Cairo canvas" ),
static EDA_HOTKEY HkCanvasCairo( wxT( "Switch to Cairo Canvas" ),
HK_CANVAS_CAIRO, WXK_F12 );
/* Fit on Screen */
......@@ -285,32 +285,32 @@ EDA_HOTKEY* module_viewer_Hotkey_List[] = {
// list of sections and corresponding hotkey list for Pcbnew
// (used to create an hotkey config file, and edit hotkeys )
struct EDA_HOTKEY_CONFIG g_Pcbnew_Editor_Hokeys_Descr[] = {
{ &g_CommonSectionTag, common_Hotkey_List, L"Common keys" },
{ &g_BoardEditorSectionTag, board_edit_Hotkey_List, L"Board editor keys" },
{ &g_ModuleEditSectionTag, module_edit_Hotkey_List, L"Footprint editor keys" },
{ &g_CommonSectionTag, common_Hotkey_List, &g_CommonSectionTitle },
{ &g_BoardEditorSectionTag, board_edit_Hotkey_List, &g_BoardEditorSectionTitle },
{ &g_ModuleEditSectionTag, module_edit_Hotkey_List, &g_ModuleEditSectionTitle },
{ NULL, NULL, NULL }
};
// list of sections and corresponding hotkey list for the board editor
// (used to list current hotkeys in the board editor)
struct EDA_HOTKEY_CONFIG g_Board_Editor_Hokeys_Descr[] = {
{ &g_CommonSectionTag, common_Hotkey_List, NULL },
{ &g_BoardEditorSectionTag, board_edit_Hotkey_List, NULL },
{ &g_CommonSectionTag, common_Hotkey_List, &g_CommonSectionTitle },
{ &g_BoardEditorSectionTag, board_edit_Hotkey_List, &g_BoardEditorSectionTitle },
{ NULL, NULL, NULL }
};
// list of sections and corresponding hotkey list for the footprint editor
// (used to list current hotkeys in the module editor)
struct EDA_HOTKEY_CONFIG g_Module_Editor_Hokeys_Descr[] = {
{ &g_CommonSectionTag, common_Hotkey_List, NULL },
{ &g_ModuleEditSectionTag, module_edit_Hotkey_List, NULL },
{ &g_CommonSectionTag, common_Hotkey_List, &g_CommonSectionTitle },
{ &g_ModuleEditSectionTag, module_edit_Hotkey_List, &g_ModuleEditSectionTitle },
{ NULL, NULL, NULL }
};
// list of sections and corresponding hotkey list for the footprint viewer
// (used to list current hotkeys in the module viewer)
struct EDA_HOTKEY_CONFIG g_Module_Viewer_Hokeys_Descr[] = {
{ &g_CommonSectionTag, common_basic_Hotkey_List, NULL },
{ &g_CommonSectionTag, common_basic_Hotkey_List, &g_CommonSectionTitle },
{ NULL, NULL, NULL }
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment