39#include <wx/display.h>
40#include <wx/evtloop.h>
44#include <wx/bmpbuttn.h>
45#include <wx/textctrl.h>
46#include <wx/stc/stc.h>
47#include <wx/combobox.h>
48#include <wx/odcombo.h>
50#include <wx/checkbox.h>
51#include <wx/spinctrl.h>
52#include <wx/splitter.h>
53#include <wx/radiobox.h>
54#include <wx/radiobut.h>
55#include <wx/variant.h>
59#include <nlohmann/json.hpp>
68 const wxPoint& pos, const wxSize& size,
long style,
69 const wxString&
name ) :
70 wxDialog( aParent,
id, title, pos, size, style,
name ),
73 m_useCalculatedSize( false ),
74 m_firstPaintEvent( true ),
75 m_initialFocusTarget(
nullptr ),
77 m_qmodal_loop(
nullptr ),
78 m_qmodal_showing( false ),
79 m_qmodal_parent_disabler(
nullptr ),
80 m_parentFrame(
nullptr ),
81 m_userPositioned( false ),
82 m_userResized( false )
91 while( !kiwayHolder && aParent->GetParent() )
93 aParent = aParent->GetParent();
108 TOOL_MANAGER* toolMgr = m_parentFrame->GetToolManager();
116 SetKiway(
this, &kiwayHolder->
Kiway() );
137 wxString msg = wxString::Format(
"Opening dialog %s", GetTitle() );
153 std::function<void( wxWindowList& )> disconnectFocusHandlers =
154 [&]( wxWindowList& children )
156 for( wxWindow* child : children )
158 if( wxTextCtrl* textCtrl =
dynamic_cast<wxTextCtrl*
>( child ) )
163 else if( wxStyledTextCtrl* scintilla =
dynamic_cast<wxStyledTextCtrl*
>( child ) )
170 disconnectFocusHandlers( child->GetChildren() );
175 disconnectFocusHandlers( GetChildren() );
202 GetSizer()->SetSizeHints(
this );
209 SetSize( ConvertDialogToPixels( sz ) );
216 return ConvertDialogToPixels( sz ).x;
223 return ConvertDialogToPixels( sz ).y;
235 wxDialog::SetPosition( aNewPosition );
248 ret = wxDialog::Show( show );
250 wxRect savedDialogRect;
258 auto geoIt = dlgIt->second.find(
"__geometry" );
260 if( geoIt != dlgIt->second.end() && geoIt->second.is_object() )
262 const nlohmann::json& g = geoIt->second;
263 savedDialogRect.SetPosition( wxPoint( g.value(
"x", 0 ), g.value(
"y", 0 ) ) );
264 savedDialogRect.SetSize( wxSize( g.value(
"w", 500 ), g.value(
"h", 300 ) ) );
268 if( savedDialogRect.GetSize().x != 0 && savedDialogRect.GetSize().y != 0 )
272 SetSize( savedDialogRect.GetPosition().x, savedDialogRect.GetPosition().y,
273 wxDialog::GetSize().x, wxDialog::GetSize().y, 0 );
277 SetSize( savedDialogRect.GetPosition().x, savedDialogRect.GetPosition().y,
278 std::max( wxDialog::GetSize().x, savedDialogRect.GetSize().x ),
279 std::max( wxDialog::GetSize().y, savedDialogRect.GetSize().y ), 0 );
282 if( m_parent !=
nullptr )
284 if( wxDisplay::GetFromPoint( m_parent->GetPosition() )
285 != wxDisplay::GetFromPoint( savedDialogRect.GetPosition() ) )
298 if( wxDisplay::GetFromWindow(
this ) == wxNOT_FOUND )
311 m_eventLoop->Exit( GetReturnCode() );
314 ret = wxDialog::Show( show );
319 m_parent->SetFocus();
336 dlgIt->second.erase(
"__geometry" );
357 return wxDialog::Enable( enable );
363 auto getSiblingIndex =
364 [](
const wxWindow* parent,
const wxWindow* child )
366 wxString childClass = child->GetClassInfo()->GetClassName();
369 for(
const wxWindow* sibling : parent->GetChildren() )
371 if( sibling->GetClassInfo()->GetClassName() != childClass )
374 if( sibling == child )
384 [&](
const wxWindow* window )
386 std::string key = wxString( window->GetClassInfo()->GetClassName() ).ToStdString();
388 if( window->GetParent() )
389 key +=
"_" + std::to_string( getSiblingIndex( window->GetParent(), window ) );
394 std::string key =
makeKey( aWin );
396 for(
const wxWindow* parent = aWin->GetParent(); parent && parent !=
this; parent = parent->GetParent() )
409 wxRect rect( GetPosition(), GetSize() );
411 geom[
"x" ] = rect.GetX();
412 geom[
"y" ] = rect.GetY();
413 geom[
"w" ] = rect.GetWidth();
414 geom[
"h" ] = rect.GetHeight();
415 dlgMap[
"__geometry" ] = geom;
417 std::function<void( wxWindow* )> saveFn =
422 if( !props->GetPropertyOr(
"persist",
false ) )
432 else if( wxComboBox* combo =
dynamic_cast<wxComboBox*
>( win ) )
433 dlgMap[ key ] = combo->GetValue();
434 else if( wxOwnerDrawnComboBox* od_combo =
dynamic_cast<wxOwnerDrawnComboBox*
>( win ) )
435 dlgMap[ key ] = od_combo->GetSelection();
436 else if( wxTextEntry* textEntry =
dynamic_cast<wxTextEntry*
>( win ) )
437 dlgMap[ key ] = textEntry->GetValue();
438 else if( wxChoice* choice =
dynamic_cast<wxChoice*
>( win ) )
439 dlgMap[ key ] = choice->GetSelection();
440 else if( wxCheckBox* check =
dynamic_cast<wxCheckBox*
>( win ) )
441 dlgMap[ key ] = check->GetValue();
442 else if( wxSpinCtrl* spin =
dynamic_cast<wxSpinCtrl*
>( win ) )
443 dlgMap[ key ] = spin->GetValue();
444 else if( wxRadioButton* radio =
dynamic_cast<wxRadioButton*
>( win ) )
445 dlgMap[ key ] = radio->GetValue();
446 else if( wxRadioBox* radioBox =
dynamic_cast<wxRadioBox*
>( win ) )
447 dlgMap[ key ] = radioBox->GetSelection();
448 else if( wxSplitterWindow* splitter =
dynamic_cast<wxSplitterWindow*
>( win ) )
449 dlgMap[ key ] = splitter->GetSashPosition();
450 else if( wxScrolledWindow* scrolled =
dynamic_cast<wxScrolledWindow*
>( win ) )
451 dlgMap[ key ] = scrolled->GetScrollPos( wxVERTICAL );
452 else if( wxNotebook* notebook =
dynamic_cast<wxNotebook*
>( win ) )
453 dlgMap[ key ] = notebook->GetPageText( notebook->GetSelection() );
456 for( wxWindow* child : win->GetChildren() )
462 if( !props->GetPropertyOr(
"persist",
false ) )
466 for( wxWindow* child : GetChildren() )
480 const auto& dlgMap = dlgIt->second;
482 std::function<void( wxWindow* )> loadFn =
487 if( !props->GetPropertyOr(
"persist",
false ) )
495 auto it = dlgMap.find( key );
497 if( it != dlgMap.end() )
499 const nlohmann::json& j = it->second;
503 if( j.is_number_integer() )
506 else if( wxComboBox* combo =
dynamic_cast<wxComboBox*
>( win ) )
509 combo->SetValue( wxString::FromUTF8( j.get<std::string>().c_str() ) );
511 else if( wxOwnerDrawnComboBox* od_combo =
dynamic_cast<wxOwnerDrawnComboBox*
>( win ) )
513 if( j.is_number_integer() )
515 int index = j.get<
int>();
517 if( index >= 0 && index < (
int) od_combo->GetCount() )
518 od_combo->SetSelection( index );
521 else if( wxTextEntry* textEntry =
dynamic_cast<wxTextEntry*
>( win ) )
524 textEntry->ChangeValue( wxString::FromUTF8( j.get<std::string>().c_str() ) );
526 else if( wxChoice* choice =
dynamic_cast<wxChoice*
>( win ) )
528 if( j.is_number_integer() )
530 int index = j.get<
int>();
532 if( index >= 0 && index < (
int) choice->GetCount() )
533 choice->SetSelection( index );
536 else if( wxCheckBox* check =
dynamic_cast<wxCheckBox*
>( win ) )
539 check->SetValue( j.get<
bool>() );
541 else if( wxSpinCtrl* spin =
dynamic_cast<wxSpinCtrl*
>( win ) )
543 if( j.is_number_integer() )
544 spin->SetValue( j.get<
int>() );
546 else if( wxRadioButton* radio =
dynamic_cast<wxRadioButton*
>( win ) )
549 radio->SetValue( j.get<
bool>() );
551 else if( wxRadioBox* radioBox =
dynamic_cast<wxRadioBox*
>( win ) )
553 if( j.is_number_integer() )
555 int index = j.get<
int>();
557 if( index >= 0 && index < (
int) radioBox->GetCount() )
558 radioBox->SetSelection( index );
561 else if( wxSplitterWindow* splitter =
dynamic_cast<wxSplitterWindow*
>( win ) )
563 if( j.is_number_integer() )
564 splitter->SetSashPosition( j.get<
int>() );
566 else if( wxScrolledWindow* scrolled =
dynamic_cast<wxScrolledWindow*
>( win ) )
568 if( j.is_number_integer() )
569 scrolled->SetScrollPos( wxVERTICAL, j.get<
int>() );
571 else if( wxNotebook* notebook =
dynamic_cast<wxNotebook*
>( win ) )
575 wxString pageTitle = wxString::FromUTF8( j.get<std::string>().c_str() );
577 for(
int page = 0; page < (int) notebook->GetPageCount(); ++page )
579 if( notebook->GetPageText( page ) == pageTitle )
580 notebook->SetSelection( page );
587 for( wxWindow* child : win->GetChildren() )
593 if( !props->GetPropertyOr(
"persist",
false ) )
597 for( wxWindow* child : GetChildren() )
606 aWindow->SetClientData( props );
621 for( wxWindow* child : children )
623 if( wxTextCtrl* textCtrl =
dynamic_cast<wxTextCtrl*
>( child ) )
632#if defined( __WXMAC__ ) || defined( __WXMSW__ )
633 if( !textCtrl->GetStringSelection().IsEmpty() )
637 else if( textCtrl->IsEditable() )
639 textCtrl->SelectAll();
645 else if( wxStyledTextCtrl* scintilla =
dynamic_cast<wxStyledTextCtrl*
>( child ) )
648 scintilla->Connect( wxEVT_SET_FOCUS,
652 if( !scintilla->GetSelectedText().IsEmpty() )
656 else if( scintilla->GetMarginWidth( 0 ) > 0 )
660 else if( scintilla->IsEditable() )
662 scintilla->SelectAll();
668 else if(
dynamic_cast<wxBitmapButton*
>( child ) !=
nullptr )
671 wxRect rect = child->GetRect();
673 child->ConvertDialogToPixels(
minSize );
675 rect.Inflate( std::max( 0,
minSize.x - rect.GetWidth() ),
676 std::max( 0,
minSize.y - rect.GetHeight() ) );
678 child->SetMinSize( rect.GetSize() );
679 child->SetSize( rect );
712 if( !GetTitle().StartsWith( wxS(
"*" ) ) )
713 SetTitle( wxS(
"*" ) + GetTitle() );
719 if( GetTitle().StartsWith( wxS(
"*" ) ) )
720 SetTitle( GetTitle().AfterFirst(
'*' ) );
732 return wxDialog::ShowModal();
765 wxWindow* win = wxWindow::GetCapture();
770 wxWindow* parent = GetParentForModalDialog( GetParent(), GetWindowStyle() );
787 wxGUIEventLoop event_loop;
798 return GetReturnCode();
820 if( ( retCode == wxID_OK ) && ( !Validate() || !TransferDataFromWindow() ) )
823 SetReturnCode( retCode );
827 wxFAIL_MSG( wxT(
"Either DIALOG_SHIM::EndQuasiModal was called twice, or ShowQuasiModal"
851 wxString msg = wxString::Format(
"Closing dialog %s", GetTitle() );
867 const int id = aEvent.GetId();
871 if(
id == GetAffirmativeId() )
875 else if(
id == wxID_APPLY )
886 else if(
id == wxID_CANCEL )
909 if( wxTextCtrl* textCtrl =
dynamic_cast<wxTextCtrl*
>( aEvent.GetEventObject() ) )
911 else if( wxStyledTextCtrl* scintilla =
dynamic_cast<wxStyledTextCtrl*
>( aEvent.GetEventObject() ) )
921 if( aEvt.GetKeyCode() ==
'U' && aEvt.GetModifiers() == wxMOD_CONTROL )
930 else if( ( aEvt.GetKeyCode() == WXK_RETURN || aEvt.GetKeyCode() == WXK_NUMPAD_ENTER ) && aEvt.ShiftDown() )
932 wxObject* eventSource = aEvt.GetEventObject();
934 if( wxTextCtrl* textCtrl =
dynamic_cast<wxTextCtrl*
>( eventSource ) )
937 if( !textCtrl->IsMultiLine() )
939 wxPostEvent(
this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
943#if defined( __WXMAC__ ) || defined( __WXMSW__ )
944 wxString eol =
"\r\n";
949 long pos = textCtrl->GetInsertionPoint();
950 textCtrl->WriteText( eol );
951 textCtrl->SetInsertionPoint( pos + eol.length() );
954 else if( wxStyledTextCtrl* scintilla =
dynamic_cast<wxStyledTextCtrl*
>( eventSource ) )
957 switch( scintilla->GetEOLMode() )
959 case wxSTC_EOL_CRLF: eol =
"\r\n";
break;
960 case wxSTC_EOL_CR: eol =
"\r";
break;
961 case wxSTC_EOL_LF: eol =
"\n";
break;
964 long pos = scintilla->GetCurrentPos();
965 scintilla->InsertText( pos, eol );
966 scintilla->GotoPos( pos + eol.length() );
972 else if( ( aEvt.GetKeyCode() == WXK_RETURN || aEvt.GetKeyCode() == WXK_NUMPAD_ENTER ) && aEvt.ControlDown() )
974 wxPostEvent(
this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
977 else if( aEvt.GetKeyCode() == WXK_TAB && !aEvt.ControlDown() )
979 wxWindow* currentWindow = wxWindow::FindFocus();
981 int delta = aEvt.ShiftDown() ? -1 : 1;
988 idx = ( ( idx +
delta ) % size + size ) % size;
991 for(
size_t i = 0; i <
m_tabOrder.size(); ++i )
995 currentIdx = (int) i;
1000 if( currentIdx >= 0 )
1002 advance( currentIdx );
1007 while(
dynamic_cast<wxTextEntry*
>(
m_tabOrder[ currentIdx ] ) ==
nullptr )
1008 advance( currentIdx );
1015 else if( aEvt.GetKeyCode() == WXK_ESCAPE )
1017 wxObject* eventSource = aEvt.GetEventObject();
1019 if( wxTextCtrl* textCtrl =
dynamic_cast<wxTextCtrl*
>( eventSource ) )
1025 textCtrl->SelectAll();
1029 else if( wxStyledTextCtrl* scintilla =
dynamic_cast<wxStyledTextCtrl*
>( eventSource ) )
1035 scintilla->SelectAll();
1047 wxStdDialogButtonSizer* sdbSizer =
dynamic_cast<wxStdDialogButtonSizer*
>( aSizer );
1050 [&]( wxButton* aButton )
1052 if( aLabels.count( aButton->GetId() ) > 0 )
1054 aButton->SetLabel( aLabels[ aButton->GetId() ] );
1060 switch( aButton->GetId() )
1062 case wxID_OK: aButton->SetLabel(
_(
"&OK" ) );
break;
1063 case wxID_CANCEL: aButton->SetLabel(
_(
"&Cancel" ) );
break;
1064 case wxID_YES: aButton->SetLabel(
_(
"&Yes" ) );
break;
1065 case wxID_NO: aButton->SetLabel(
_(
"&No" ) );
break;
1066 case wxID_APPLY: aButton->SetLabel(
_(
"&Apply" ) );
break;
1067 case wxID_SAVE: aButton->SetLabel(
_(
"&Save" ) );
break;
1068 case wxID_HELP: aButton->SetLabel(
_(
"&Help" ) );
break;
1069 case wxID_CONTEXT_HELP: aButton->SetLabel(
_(
"&Help" ) );
break;
1076 if( sdbSizer->GetAffirmativeButton() )
1077 setupButton( sdbSizer->GetAffirmativeButton() );
1079 if( sdbSizer->GetApplyButton() )
1080 setupButton( sdbSizer->GetApplyButton() );
1082 if( sdbSizer->GetNegativeButton() )
1083 setupButton( sdbSizer->GetNegativeButton() );
1085 if( sdbSizer->GetCancelButton() )
1086 setupButton( sdbSizer->GetCancelButton() );
1088 if( sdbSizer->GetHelpButton() )
1089 setupButton( sdbSizer->GetHelpButton() );
1093 if( sdbSizer->GetAffirmativeButton() )
1094 sdbSizer->GetAffirmativeButton()->SetDefault();
1097 for( wxSizerItem* item : aSizer->GetChildren() )
1099 if( item->GetSizer() )
std::map< std::string, std::map< std::string, nlohmann::json > > m_dialogControlValues
Persistent dialog control values.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
void SelectAllInTextCtrls(wxWindowList &children)
std::vector< wxWindow * > m_tabOrder
void OnPaint(wxPaintEvent &event)
virtual void TearDownQuasiModal()
Override this method to perform dialog tear down actions not suitable for object dtor.
int vertPixelsFromDU(int y) const
Convert an integer number of dialog units to pixels, vertically.
bool Show(bool show) override
wxGUIEventLoop * m_qmodal_loop
void onChildSetFocus(wxFocusEvent &aEvent)
void LoadControlState()
Load persisted control values from the current project's local settings.
void OptOut(wxWindow *aWindow)
Opt out of control state saving.
void SaveControlState()
Save control values and geometry to the current project's local settings.
void SetupStandardButtons(std::map< int, wxString > aLabels={})
WINDOW_DISABLER * m_qmodal_parent_disabler
void onInitDialog(wxInitDialogEvent &aEvent)
int horizPixelsFromDU(int x) const
Convert an integer number of dialog units to pixels, horizontally.
void resetSize()
Clear the existing dialog size and position.
std::map< wxWindow *, wxString > m_beforeEditValues
void setSizeInDU(int x, int y)
Set the dialog to the given dimensions in "dialog units".
bool IsQuasiModal() const
std::map< wxWindow *, UNIT_BINDER * > m_unitBinders
void EndQuasiModal(int retCode)
void RegisterUnitBinder(UNIT_BINDER *aUnitBinder, wxWindow *aWindow)
Register a UNIT_BINDER so that it can handle units in control-state save/restore.
void OnMove(wxMoveEvent &aEvent)
void CleanupAfterModalSubDialog()
std::string generateKey(const wxWindow *aWin) const
void PrepareForModalSubDialog()
void OnButton(wxCommandEvent &aEvent)
Properly handle the default button events when in the quasimodal mode when not calling EndQuasiModal ...
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
wxWindow * m_initialFocusTarget
void OnSize(wxSizeEvent &aEvent)
bool Enable(bool enable) override
void SetPosition(const wxPoint &aNewPosition)
Force the position of the dialog to a new position.
void OnCloseWindow(wxCloseEvent &aEvent)
Properly handle the wxCloseEvent when in the quasimodal mode when not calling EndQuasiModal which is ...
EDA_BASE_FRAME * m_parentFrame
virtual void OnCharHook(wxKeyEvent &aEvt)
EDA_UNITS GetUserUnits() const
The base frame for deriving all KiCad main window classes.
virtual void ToggleUserUnits()
A mix in class which holds the location of a wxWindow's KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
bool HasKiway() const
Safety check before asking for the Kiway reference.
HOLDER_TYPE GetType() const
void SetBlockingDialog(wxWindow *aWin)
virtual wxApp & App()
Return a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
virtual SETTINGS_MANAGER & GetSettingsManager() const
bool SetProperty(const std::string &aKey, T &&aValue)
Set a property with the given key and value.
static PROPERTY_HOLDER * SafeCast(void *aPtr) noexcept
Safely cast a void pointer to PROPERTY_HOLDER*.
COMMON_SETTINGS * GetCommonSettings() const
Retrieve the common settings shared by all applications.
EDA_UNITS GetUserUnits() const
Temporarily disable a window, and then re-enable on destruction.
void SuspendForTrueModal()
void ResumeAfterTrueModal()
static void recursiveDescent(wxSizer *aSizer, std::map< int, wxString > &aLabels)
const int minSize
Push and Shove router track width and via size dialog.
void ignore_unused(const T &)
void AddNavigationBreadcrumb(const wxString &aMsg, const wxString &aCategory)
Add a navigation breadcrumb.
static wxString makeKey(const wxString &aFirst, const wxString &aSecond)
Assemble a two part key as a simple concatenation of aFirst and aSecond parts, using a separator.
PGM_BASE & Pgm()
The global program "get" accessor.
KIWAY Kiway(KFCTL_STANDALONE)