1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2004-2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
6 * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26 #include <base_screen.h>
27 #include <bitmaps.h>
28 #include <confirm.h>
29 #include <core/arraydim.h>
30 #include <dialog_shim.h>
31 #include <eda_draw_frame.h>
32 #include <filehistory.h>
33 #include <id.h>
34 #include <kiface_base.h>
35 #include <lockfile.h>
36 #include <macros.h>
37 #include <page_info.h>
38 #include <paths.h>
39 #include <pgm_base.h>
40 #include <render_settings.h>
41 #include <settings/app_settings.h>
42 #include <settings/color_settings.h>
43 #include <settings/common_settings.h>
44 #include <settings/settings_manager.h>
45 #include <title_block.h>
46 #include <tool/actions.h>
47 #include <tool/common_tools.h>
48 #include <tool/grid_menu.h>
49 #include <tool/selection_conditions.h>
50 #include <tool/tool_dispatcher.h>
51 #include <tool/tool_manager.h>
52 #include <tool/tool_menu.h>
53 #include <tool/zoom_menu.h>
54 #include <trace_helpers.h>
55 #include <view/view.h>
56 #include <drawing_sheet/ds_draw_item.h>
57 #include <widgets/msgpanel.h>
58 #include <wx/event.h>
59 #include <wx/snglinst.h>
60 #include <dialogs/dialog_grid_settings.h>
61 #include <widgets/ui_common.h>
62 #include <wx/dirdlg.h>
63 #include <wx/filedlg.h>
64 #include <wx/msgdlg.h>
65 #include <wx/socket.h>
66
67 #include <wx/snglinst.h>
68 #include <wx/fdrepdlg.h>
69
70 #define FR_HISTORY_LIST_CNT 10 ///< Maximum size of the find/replace history stacks.
71
72
BEGIN_EVENT_TABLE(EDA_DRAW_FRAME,KIWAY_PLAYER)73 BEGIN_EVENT_TABLE( EDA_DRAW_FRAME, KIWAY_PLAYER )
74 EVT_UPDATE_UI( ID_ON_GRID_SELECT, EDA_DRAW_FRAME::OnUpdateSelectGrid )
75 EVT_UPDATE_UI( ID_ON_ZOOM_SELECT, EDA_DRAW_FRAME::OnUpdateSelectZoom )
76
77 EVT_ACTIVATE( EDA_DRAW_FRAME::onActivate )
78 END_EVENT_TABLE()
79
80
81 EDA_DRAW_FRAME::EDA_DRAW_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrameType,
82 const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
83 long aStyle, const wxString & aFrameName ) :
84 KIWAY_PLAYER( aKiway, aParent, aFrameType, aTitle, aPos, aSize, aStyle, aFrameName )
85 {
86 m_socketServer = nullptr;
87 m_mainToolBar = nullptr;
88 m_drawToolBar = nullptr;
89 m_optionsToolBar = nullptr;
90 m_auxiliaryToolBar = nullptr;
91 m_gridSelectBox = nullptr;
92 m_zoomSelectBox = nullptr;
93 m_firstRunDialogSetting = 0;
94 m_undoRedoCountMax = DEFAULT_MAX_UNDO_ITEMS;
95
96 m_canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
97 m_canvas = nullptr;
98 m_toolDispatcher = nullptr;
99 m_messagePanel = nullptr;
100 m_currentScreen = nullptr;
101 m_showBorderAndTitleBlock = false; // true to display reference sheet.
102 m_gridColor = COLOR4D( DARKGRAY ); // Default grid color
103 m_showPageLimits = false;
104 m_drawBgColor = COLOR4D( BLACK ); // the background color of the draw canvas:
105 // BLACK for Pcbnew, BLACK or WHITE for Eeschema
106 m_colorSettings = nullptr;
107 m_msgFrameHeight = EDA_MSG_PANEL::GetRequiredHeight( this );
108 m_userUnits = EDA_UNITS::MILLIMETRES;
109 m_polarCoords = false;
110 m_findReplaceData = new wxFindReplaceData( wxFR_DOWN );
111
112 m_auimgr.SetFlags( wxAUI_MGR_DEFAULT );
113
114 CreateStatusBar( 8 );
115
116 // set the size of the status bar subwindows:
117
118 wxWindow* stsbar = GetStatusBar();
119 int spacer = KIUI::GetTextSize( wxT( "M" ), stsbar ).x * 2;
120
121 int dims[] = {
122
123 // remainder of status bar on far left is set to a default or whatever is left over.
124 -1,
125
126 // When using GetTextSize() remember the width of character '1' is not the same
127 // as the width of '0' unless the font is fixed width, and it usually won't be.
128
129 // zoom:
130 KIUI::GetTextSize( wxT( "Z 762000" ), stsbar ).x + spacer,
131
132 // cursor coords
133 KIUI::GetTextSize( wxT( "X 1234.1234 Y 1234.1234" ), stsbar ).x + spacer,
134
135 // delta distances
136 KIUI::GetTextSize( wxT( "dx 1234.1234 dy 1234.1234 dist 1234.1234" ), stsbar ).x + spacer,
137
138 // grid size
139 KIUI::GetTextSize( wxT( "grid X 1234.1234 Y 1234.1234" ), stsbar ).x + spacer,
140
141 // units display, Inches is bigger than mm
142 KIUI::GetTextSize( _( "Inches" ), stsbar ).x + spacer,
143
144 // Size for the "Current Tool" panel; longest string from SetTool()
145 KIUI::GetTextSize( wxT( "Add layer alignment target" ), stsbar ).x + spacer,
146
147 // constraint mode
148 KIUI::GetTextSize( _( "Constrain to H, V, 45" ), stsbar ).x + spacer
149 };
150
151 SetStatusWidths( arrayDim( dims ), dims );
152 stsbar->SetFont( KIUI::GetStatusFont( this ) );
153
154 // Create child subwindows.
155 GetClientSize( &m_frameSize.x, &m_frameSize.y );
156 m_framePos.x = m_framePos.y = 0;
157 m_frameSize.y -= m_msgFrameHeight;
158
159 m_messagePanel = new EDA_MSG_PANEL( this, -1, wxPoint( 0, m_frameSize.y ),
160 wxSize( m_frameSize.x, m_msgFrameHeight ) );
161
162 m_messagePanel->SetBackgroundColour( COLOR4D( LIGHTGRAY ).ToColour() );
163
164 #if wxCHECK_VERSION( 3, 1, 3 )
165 Bind( wxEVT_DPI_CHANGED,
166 [&]( wxDPIChangedEvent& )
167 {
168 wxMoveEvent dummy;
169 OnMove( dummy );
170 } );
171 #endif
172 }
173
174
~EDA_DRAW_FRAME()175 EDA_DRAW_FRAME::~EDA_DRAW_FRAME()
176 {
177 delete m_socketServer;
178
179 for( auto socket : m_sockets )
180 {
181 socket->Shutdown();
182 socket->Destroy();
183 }
184
185 saveCanvasTypeSetting( m_canvasType );
186
187 delete m_actions;
188 delete m_toolManager;
189 delete m_toolDispatcher;
190 delete m_canvas;
191
192 delete m_currentScreen;
193 m_currentScreen = nullptr;
194
195 delete m_findReplaceData;
196
197 m_auimgr.UnInit();
198
199 ReleaseFile();
200 }
201
202
ReleaseFile()203 void EDA_DRAW_FRAME::ReleaseFile()
204 {
205 m_file_checker = nullptr;
206 }
207
208
LockFile(const wxString & aFileName)209 bool EDA_DRAW_FRAME::LockFile( const wxString& aFileName )
210 {
211 m_file_checker = ::LockFile( aFileName );
212
213 return m_file_checker && !m_file_checker->IsAnotherRunning();
214 }
215
216
ScriptingConsoleEnableDisable()217 void EDA_DRAW_FRAME::ScriptingConsoleEnableDisable()
218 {
219 KIWAY_PLAYER* frame = Kiway().Player( FRAME_PYTHON, false );
220
221 wxRect rect = GetScreenRect();
222 wxPoint center = rect.GetPosition() + rect.GetSize() / 2;
223
224 if( !frame )
225 {
226 frame = Kiway().Player( FRAME_PYTHON, true, Kiway().GetTop() );
227
228 // If we received an error in the CTOR due to Python-ness, don't crash
229 if( !frame )
230 return;
231
232 if( !frame->IsVisible() )
233 frame->Show( true );
234
235 // On Windows, Raise() does not bring the window on screen, when iconized
236 if( frame->IsIconized() )
237 frame->Iconize( false );
238
239 frame->Raise();
240 frame->SetPosition( center - frame->GetSize() / 2 );
241
242 return;
243 }
244
245 frame->Show( !frame->IsVisible() );
246 frame->SetPosition( center - frame->GetSize() / 2 );
247 }
248
249
IsScriptingConsoleVisible()250 bool EDA_DRAW_FRAME::IsScriptingConsoleVisible()
251 {
252 KIWAY_PLAYER* frame = Kiway().Player( FRAME_PYTHON, false );
253 return frame && frame->IsVisible();
254 }
255
256
unitsChangeRefresh()257 void EDA_DRAW_FRAME::unitsChangeRefresh()
258 {
259 // Notify all tools the units have changed
260 if( m_toolManager )
261 m_toolManager->RunAction( ACTIONS::updateUnits, true );
262
263 UpdateStatusBar();
264 UpdateMsgPanel();
265 }
266
267
ToggleUserUnits()268 void EDA_DRAW_FRAME::ToggleUserUnits()
269 {
270 SetUserUnits( m_userUnits == EDA_UNITS::INCHES ? EDA_UNITS::MILLIMETRES : EDA_UNITS::INCHES );
271 unitsChangeRefresh();
272
273 wxCommandEvent e( UNITS_CHANGED );
274 ProcessEventLocally( e );
275 }
276
277
CommonSettingsChanged(bool aEnvVarsChanged,bool aTextVarsChanged)278 void EDA_DRAW_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
279 {
280 EDA_BASE_FRAME::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
281
282 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
283 KIGFX::VIEW_CONTROLS* viewControls = GetCanvas()->GetViewControls();
284
285 SetAutoSaveInterval( settings->m_System.autosave_interval );
286
287 viewControls->LoadSettings();
288
289 m_galDisplayOptions.ReadCommonConfig( *settings, this );
290
291 // Notify all tools the preferences have changed
292 if( m_toolManager )
293 m_toolManager->RunAction( ACTIONS::updatePreferences, true );
294 }
295
296
EraseMsgBox()297 void EDA_DRAW_FRAME::EraseMsgBox()
298 {
299 if( m_messagePanel )
300 m_messagePanel->EraseMsgBox();
301 }
302
303
UpdateGridSelectBox()304 void EDA_DRAW_FRAME::UpdateGridSelectBox()
305 {
306 UpdateStatusBar();
307 DisplayUnitsMsg();
308
309 if( m_gridSelectBox == nullptr )
310 return;
311
312 // Update grid values with the current units setting.
313 m_gridSelectBox->Clear();
314 wxArrayString gridsList;
315
316 GRID_MENU::BuildChoiceList( &gridsList, config(), this );
317
318 for( const wxString& grid : gridsList )
319 m_gridSelectBox->Append( grid );
320
321 m_gridSelectBox->Append( wxT( "---" ) );
322 m_gridSelectBox->Append( _( "Edit User Grid..." ) );
323
324 m_gridSelectBox->SetSelection( config()->m_Window.grid.last_size_idx );
325 }
326
327
OnUpdateSelectGrid(wxUpdateUIEvent & aEvent)328 void EDA_DRAW_FRAME::OnUpdateSelectGrid( wxUpdateUIEvent& aEvent )
329 {
330 // No need to update the grid select box if it doesn't exist or the grid setting change
331 // was made using the select box.
332 if( m_gridSelectBox == nullptr )
333 return;
334
335 int idx = config()->m_Window.grid.last_size_idx;
336 idx = std::max( 0, std::min( idx, (int) m_gridSelectBox->GetCount() - 1 ) );
337
338 if( idx != m_gridSelectBox->GetSelection() )
339 m_gridSelectBox->SetSelection( idx );
340 }
341
342
PrintPage(const RENDER_SETTINGS * aSettings)343 void EDA_DRAW_FRAME::PrintPage( const RENDER_SETTINGS* aSettings )
344 {
345 wxMessageBox( wxT("EDA_DRAW_FRAME::PrintPage() error") );
346 }
347
348
OnSelectGrid(wxCommandEvent & event)349 void EDA_DRAW_FRAME::OnSelectGrid( wxCommandEvent& event )
350 {
351 wxCHECK_RET( m_gridSelectBox, "m_gridSelectBox uninitialized" );
352
353 int idx = m_gridSelectBox->GetCurrentSelection();
354
355 if( idx == int( m_gridSelectBox->GetCount() ) - 2 )
356 {
357 // wxWidgets will check the separator, which we don't want.
358 // Re-check the current grid.
359 wxUpdateUIEvent dummy;
360 OnUpdateSelectGrid( dummy );
361 }
362 else if( idx == int( m_gridSelectBox->GetCount() ) - 1 )
363 {
364 // wxWidgets will check the Grid Settings... entry, which we don't want.
365 // Re-check the current grid.
366 wxUpdateUIEvent dummy;
367 OnUpdateSelectGrid( dummy );
368
369 // Give a time-slice to close the menu before opening the dialog.
370 // (Only matters on some versions of GTK.)
371 wxSafeYield();
372
373 m_toolManager->RunAction( ACTIONS::gridProperties, true );
374 }
375 else
376 {
377 m_toolManager->RunAction( ACTIONS::gridPreset, true, static_cast<intptr_t>( idx ) );
378 }
379
380 UpdateStatusBar();
381 m_canvas->Refresh();
382 }
383
384
OnGridSettings(wxCommandEvent & aEvent)385 void EDA_DRAW_FRAME::OnGridSettings( wxCommandEvent& aEvent )
386 {
387 DIALOG_GRID_SETTINGS dlg( this );
388
389 if( dlg.ShowModal() == wxID_OK )
390 {
391 UpdateStatusBar();
392 GetCanvas()->Refresh();
393 }
394 }
395
396
IsGridVisible() const397 bool EDA_DRAW_FRAME::IsGridVisible() const
398 {
399 return config()->m_Window.grid.show;
400 }
401
402
SetGridVisibility(bool aVisible)403 void EDA_DRAW_FRAME::SetGridVisibility( bool aVisible )
404 {
405 config()->m_Window.grid.show = aVisible;
406
407 // Update the display with the new grid
408 if( GetCanvas() )
409 {
410 // Check to ensure these exist, since this function could be called before
411 // the GAL and View have been created
412 if( GetCanvas()->GetGAL() )
413 GetCanvas()->GetGAL()->SetGridVisibility( aVisible );
414
415 if( GetCanvas()->GetView() )
416 GetCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
417
418 GetCanvas()->Refresh();
419 }
420 }
421
422
UpdateZoomSelectBox()423 void EDA_DRAW_FRAME::UpdateZoomSelectBox()
424 {
425 if( m_zoomSelectBox == nullptr )
426 return;
427
428 double zoom = m_canvas->GetGAL()->GetZoomFactor();
429
430 m_zoomSelectBox->Clear();
431 m_zoomSelectBox->Append( _( "Zoom Auto" ) );
432 m_zoomSelectBox->SetSelection( 0 );
433
434 for( unsigned i = 0; i < config()->m_Window.zoom_factors.size(); ++i )
435 {
436 double current = config()->m_Window.zoom_factors[i];
437
438 m_zoomSelectBox->Append( wxString::Format( _( "Zoom %.2f" ), current ) );
439
440 if( zoom == current )
441 m_zoomSelectBox->SetSelection( i + 1 );
442 }
443 }
444
445
OnUpdateSelectZoom(wxUpdateUIEvent & aEvent)446 void EDA_DRAW_FRAME::OnUpdateSelectZoom( wxUpdateUIEvent& aEvent )
447 {
448 if( m_zoomSelectBox == nullptr || m_zoomSelectBox->GetParent() == nullptr )
449 return;
450
451 int current = 0; // display Auto if no match found
452
453 // check for a match within 1%
454 double zoom = GetCanvas()->GetGAL()->GetZoomFactor();
455
456 for( unsigned i = 0; i < config()->m_Window.zoom_factors.size(); i++ )
457 {
458 if( std::fabs( zoom - config()->m_Window.zoom_factors[i] ) < ( zoom / 100.0 ) )
459 {
460 current = i + 1;
461 break;
462 }
463 }
464
465 if( current != m_zoomSelectBox->GetSelection() )
466 m_zoomSelectBox->SetSelection( current );
467 }
468
469
OnSelectZoom(wxCommandEvent & event)470 void EDA_DRAW_FRAME::OnSelectZoom( wxCommandEvent& event )
471 {
472 wxCHECK_RET( m_zoomSelectBox, "m_zoomSelectBox uninitialized" );
473
474 int id = m_zoomSelectBox->GetCurrentSelection();
475
476 if( id < 0 || !( id < (int)m_zoomSelectBox->GetCount() ) )
477 return;
478
479 m_toolManager->RunAction( ACTIONS::zoomPreset, true, static_cast<intptr_t>( id ) );
480 UpdateStatusBar();
481 m_canvas->Refresh();
482 }
483
484
OnMove(wxMoveEvent & aEvent)485 void EDA_DRAW_FRAME::OnMove( wxMoveEvent& aEvent )
486 {
487 // If the window is moved to a different display, the scaling factor may change
488 double oldFactor = m_galDisplayOptions.m_scaleFactor;
489 m_galDisplayOptions.UpdateScaleFactor();
490
491 if( oldFactor != m_galDisplayOptions.m_scaleFactor && m_canvas )
492 {
493 wxSize clientSize = GetClientSize();
494 GetCanvas()->GetGAL()->ResizeScreen( clientSize.x, clientSize.y );
495 GetCanvas()->GetView()->MarkDirty();
496 }
497
498 aEvent.Skip();
499 }
500
501
AddStandardSubMenus(TOOL_MENU & aToolMenu)502 void EDA_DRAW_FRAME::AddStandardSubMenus( TOOL_MENU& aToolMenu )
503 {
504 COMMON_TOOLS* commonTools = m_toolManager->GetTool<COMMON_TOOLS>();
505 CONDITIONAL_MENU& aMenu = aToolMenu.GetMenu();
506
507 aMenu.AddSeparator( 1000 );
508
509 auto zoomMenu = std::make_shared<ZOOM_MENU>( this );
510 zoomMenu->SetTool( commonTools );
511 aToolMenu.AddSubMenu( zoomMenu );
512
513 auto gridMenu = std::make_shared<GRID_MENU>( this );
514 gridMenu->SetTool( commonTools );
515 aToolMenu.AddSubMenu( gridMenu );
516
517 aMenu.AddMenu( zoomMenu.get(), SELECTION_CONDITIONS::ShowAlways, 1000 );
518 aMenu.AddMenu( gridMenu.get(), SELECTION_CONDITIONS::ShowAlways, 1000 );
519 }
520
521
DisplayToolMsg(const wxString & msg)522 void EDA_DRAW_FRAME::DisplayToolMsg( const wxString& msg )
523 {
524 SetStatusText( msg, 6 );
525 }
526
527
DisplayConstraintsMsg(const wxString & msg)528 void EDA_DRAW_FRAME::DisplayConstraintsMsg( const wxString& msg )
529 {
530 SetStatusText( msg, 7 );
531 }
532
533
DisplayGridMsg()534 void EDA_DRAW_FRAME::DisplayGridMsg()
535 {
536 wxString line;
537
538 line.Printf( "grid %s",
539 MessageTextFromValue( GetUserUnits(), GetCanvas()->GetGAL()->GetGridSize().x,
540 false ) );
541
542 SetStatusText( line, 4 );
543 }
544
545
DisplayUnitsMsg()546 void EDA_DRAW_FRAME::DisplayUnitsMsg()
547 {
548 wxString msg;
549
550 switch( m_userUnits )
551 {
552 case EDA_UNITS::INCHES: msg = _( "inches" ); break;
553 case EDA_UNITS::MILS: msg = _( "mils" ); break;
554 case EDA_UNITS::MILLIMETRES: msg = _( "mm" ); break;
555 default: msg = _( "Units" ); break;
556 }
557
558 SetStatusText( msg, 5 );
559 }
560
561
OnSize(wxSizeEvent & SizeEv)562 void EDA_DRAW_FRAME::OnSize( wxSizeEvent& SizeEv )
563 {
564 EDA_BASE_FRAME::OnSize( SizeEv );
565
566 m_frameSize = GetClientSize( );
567
568 SizeEv.Skip();
569 }
570
571
UpdateStatusBar()572 void EDA_DRAW_FRAME::UpdateStatusBar()
573 {
574 SetStatusText( GetZoomLevelIndicator(), 1 );
575
576 // Absolute and relative cursor positions are handled by overloading this function and
577 // handling the internal to user units conversion at the appropriate level.
578
579 // refresh units display
580 DisplayUnitsMsg();
581 }
582
583
GetZoomLevelIndicator() const584 const wxString EDA_DRAW_FRAME::GetZoomLevelIndicator() const
585 {
586 // returns a human readable value which can be displayed as zoom
587 // level indicator in dialogs.
588 double zoom = m_canvas->GetGAL()->GetZoomFactor();
589 return wxString::Format( wxT( "Z %.2f" ), zoom );
590 }
591
592
LoadSettings(APP_SETTINGS_BASE * aCfg)593 void EDA_DRAW_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
594 {
595 EDA_BASE_FRAME::LoadSettings( aCfg );
596
597 COMMON_SETTINGS* cmnCfg = Pgm().GetCommonSettings();
598 WINDOW_SETTINGS* window = GetWindowSettings( aCfg );
599
600 // Read units used in dialogs and toolbars
601 SetUserUnits( static_cast<EDA_UNITS>( aCfg->m_System.units ) );
602
603 m_undoRedoCountMax = aCfg->m_System.max_undo_items;
604 m_firstRunDialogSetting = aCfg->m_System.first_run_shown;
605
606 m_galDisplayOptions.ReadConfig( *cmnCfg, *window, this );
607
608 m_findReplaceData->SetFlags( aCfg->m_FindReplace.flags );
609 m_findReplaceData->SetFindString( aCfg->m_FindReplace.find_string );
610 m_findReplaceData->SetReplaceString( aCfg->m_FindReplace.replace_string );
611
612 for( auto& s : aCfg->m_FindReplace.find_history )
613 m_findStringHistoryList.Add( s );
614
615 for( auto& s : aCfg->m_FindReplace.replace_history )
616 m_replaceStringHistoryList.Add( s );
617 }
618
619
SaveSettings(APP_SETTINGS_BASE * aCfg)620 void EDA_DRAW_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
621 {
622 EDA_BASE_FRAME::SaveSettings( aCfg );
623
624 WINDOW_SETTINGS* window = GetWindowSettings( aCfg );
625
626 aCfg->m_System.units = static_cast<int>( m_userUnits );
627 aCfg->m_System.first_run_shown = m_firstRunDialogSetting;
628 aCfg->m_System.max_undo_items = GetMaxUndoItems();
629
630 m_galDisplayOptions.WriteConfig( *window );
631
632 aCfg->m_FindReplace.flags = m_findReplaceData->GetFlags();
633 aCfg->m_FindReplace.find_string = m_findReplaceData->GetFindString();
634 aCfg->m_FindReplace.replace_string = m_findReplaceData->GetReplaceString();
635
636 aCfg->m_FindReplace.find_history.clear();
637 aCfg->m_FindReplace.replace_history.clear();
638
639 for( size_t i = 0; i < m_findStringHistoryList.GetCount() && i < FR_HISTORY_LIST_CNT; i++ )
640 {
641 aCfg->m_FindReplace.find_history.push_back( m_findStringHistoryList[ i ].ToStdString() );
642 }
643
644 for( size_t i = 0; i < m_replaceStringHistoryList.GetCount() && i < FR_HISTORY_LIST_CNT; i++ )
645 {
646 aCfg->m_FindReplace.replace_history.push_back(
647 m_replaceStringHistoryList[ i ].ToStdString() );
648 }
649
650 // Save the units used in this frame
651 if( m_toolManager )
652 {
653 if( COMMON_TOOLS* cmnTool = m_toolManager->GetTool<COMMON_TOOLS>() )
654 {
655 aCfg->m_System.last_imperial_units = static_cast<int>( cmnTool->GetLastImperialUnits() );
656 aCfg->m_System.last_metric_units = static_cast<int>( cmnTool->GetLastMetricUnits() );
657 }
658 }
659 }
660
661
AppendMsgPanel(const wxString & aTextUpper,const wxString & aTextLower,int aPadding)662 void EDA_DRAW_FRAME::AppendMsgPanel( const wxString& aTextUpper, const wxString& aTextLower,
663 int aPadding )
664 {
665 if( m_messagePanel )
666 m_messagePanel->AppendMessage( aTextUpper, aTextLower, aPadding );
667 }
668
669
ClearMsgPanel()670 void EDA_DRAW_FRAME::ClearMsgPanel()
671 {
672 if( m_messagePanel )
673 m_messagePanel->EraseMsgBox();
674 }
675
676
SetMsgPanel(const std::vector<MSG_PANEL_ITEM> & aList)677 void EDA_DRAW_FRAME::SetMsgPanel( const std::vector<MSG_PANEL_ITEM>& aList )
678 {
679 if( m_messagePanel )
680 {
681 m_messagePanel->EraseMsgBox();
682
683 for( const MSG_PANEL_ITEM& item : aList )
684 m_messagePanel->AppendMessage( item );
685 }
686 }
687
688
SetMsgPanel(const wxString & aTextUpper,const wxString & aTextLower,int aPadding)689 void EDA_DRAW_FRAME::SetMsgPanel( const wxString& aTextUpper, const wxString& aTextLower,
690 int aPadding )
691 {
692 if( m_messagePanel )
693 {
694 m_messagePanel->EraseMsgBox();
695
696 m_messagePanel->AppendMessage( aTextUpper, aTextLower, aPadding );
697 }
698 }
699
700
SetMsgPanel(EDA_ITEM * aItem)701 void EDA_DRAW_FRAME::SetMsgPanel( EDA_ITEM* aItem )
702 {
703 wxCHECK_RET( aItem, wxT( "Invalid EDA_ITEM pointer. Bad programmer." ) );
704
705 std::vector<MSG_PANEL_ITEM> items;
706 aItem->GetMsgPanelInfo( this, items );
707 SetMsgPanel( items );
708 }
709
710
UpdateMsgPanel()711 void EDA_DRAW_FRAME::UpdateMsgPanel()
712 {
713 GetToolManager()->PostEvent( EVENTS::SelectedItemsModified );
714 }
715
716
ActivateGalCanvas()717 void EDA_DRAW_FRAME::ActivateGalCanvas()
718 {
719 GetCanvas()->SetEvtHandlerEnabled( true );
720 GetCanvas()->StartDrawing();
721 }
722
723
SwitchCanvas(EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType)724 void EDA_DRAW_FRAME::SwitchCanvas( EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType )
725 {
726 GetCanvas()->SwitchBackend( aCanvasType );
727 m_canvasType = GetCanvas()->GetBackend();
728
729 ActivateGalCanvas();
730 }
731
732
loadCanvasTypeSetting()733 EDA_DRAW_PANEL_GAL::GAL_TYPE EDA_DRAW_FRAME::loadCanvasTypeSetting()
734 {
735 #ifdef __WXMAC__
736 // Cairo renderer doesn't handle Retina displays so there's really only one game
737 // in town for Mac
738 return EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
739 #endif
740
741 EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
742 APP_SETTINGS_BASE* cfg = Kiface().KifaceSettings();
743
744 if( cfg )
745 canvasType = static_cast<EDA_DRAW_PANEL_GAL::GAL_TYPE>( cfg->m_Graphics.canvas_type );
746
747 if( canvasType < EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE
748 || canvasType >= EDA_DRAW_PANEL_GAL::GAL_TYPE_LAST )
749 {
750 wxASSERT( false );
751 canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
752 }
753
754 // Legacy canvas no longer supported. Switch to OpenGL, falls back to Cairo on failure
755 if( canvasType == EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE )
756 canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
757
758 return canvasType;
759 }
760
761
saveCanvasTypeSetting(EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType)762 bool EDA_DRAW_FRAME::saveCanvasTypeSetting( EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType )
763 {
764 // Not all classes derived from EDA_DRAW_FRAME can save the canvas type, because some
765 // have a fixed type, or do not have a option to set the canvas type (they inherit from
766 // a parent frame)
767 FRAME_T allowed_frames[] =
768 {
769 FRAME_SCH, FRAME_SCH_SYMBOL_EDITOR,
770 FRAME_PCB_EDITOR, FRAME_FOOTPRINT_EDITOR,
771 FRAME_GERBER,
772 FRAME_PL_EDITOR
773 };
774
775 bool allow_save = false;
776
777 for( unsigned ii = 0; ii < arrayDim( allowed_frames ); ii++ )
778 {
779 if( m_ident == allowed_frames[ii] )
780 {
781 allow_save = true;
782 break;
783 }
784 }
785
786 if( !allow_save )
787 return false;
788
789 if( aCanvasType < EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE
790 || aCanvasType >= EDA_DRAW_PANEL_GAL::GAL_TYPE_LAST )
791 {
792 wxASSERT( false );
793 return false;
794 }
795
796 APP_SETTINGS_BASE* cfg = Kiface().KifaceSettings();
797
798 if( cfg )
799 cfg->m_Graphics.canvas_type = static_cast<int>( aCanvasType );
800
801 return false;
802 }
803
804
GetNearestGridPosition(const wxPoint & aPosition) const805 wxPoint EDA_DRAW_FRAME::GetNearestGridPosition( const wxPoint& aPosition ) const
806 {
807 const wxPoint& gridOrigin = GetGridOrigin();
808 VECTOR2D gridSize = GetCanvas()->GetGAL()->GetGridSize();
809
810 double xOffset = fmod( gridOrigin.x, gridSize.x );
811 int x = KiROUND( (aPosition.x - xOffset) / gridSize.x );
812 double yOffset = fmod( gridOrigin.y, gridSize.y );
813 int y = KiROUND( (aPosition.y - yOffset) / gridSize.y );
814
815 return wxPoint( KiROUND( x * gridSize.x + xOffset ), KiROUND( y * gridSize.y + yOffset ) );
816 }
817
818
GetNearestHalfGridPosition(const wxPoint & aPosition) const819 wxPoint EDA_DRAW_FRAME::GetNearestHalfGridPosition( const wxPoint& aPosition ) const
820 {
821 const wxPoint& gridOrigin = GetGridOrigin();
822 VECTOR2D gridSize = GetCanvas()->GetGAL()->GetGridSize() / 2.0;
823
824 double xOffset = fmod( gridOrigin.x, gridSize.x );
825 int x = KiROUND( (aPosition.x - xOffset) / gridSize.x );
826 double yOffset = fmod( gridOrigin.y, gridSize.y );
827 int y = KiROUND( (aPosition.y - yOffset) / gridSize.y );
828
829 return wxPoint( KiROUND( x * gridSize.x + xOffset ), KiROUND( y * gridSize.y + yOffset ) );
830 }
831
832
GetDocumentExtents(bool aIncludeAllVisible) const833 const BOX2I EDA_DRAW_FRAME::GetDocumentExtents( bool aIncludeAllVisible ) const
834 {
835 return BOX2I();
836 }
837
838
HardRedraw()839 void EDA_DRAW_FRAME::HardRedraw()
840 {
841 // To be implemented by subclasses.
842 }
843
844
Zoom_Automatique(bool aWarpPointer)845 void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer )
846 {
847 m_toolManager->RunAction( ACTIONS::zoomFitScreen, true );
848 }
849
850
851 // Find the first child dialog.
findDialogs()852 std::vector<wxWindow*> EDA_DRAW_FRAME::findDialogs()
853 {
854 std::vector<wxWindow*> dialogs;
855
856 for( wxWindow* window : GetChildren() )
857 {
858 if( dynamic_cast<DIALOG_SHIM*>( window ) )
859 dialogs.push_back( window );
860 }
861
862 return dialogs;
863 }
864
865
FocusOnLocation(const wxPoint & aPos)866 void EDA_DRAW_FRAME::FocusOnLocation( const wxPoint& aPos )
867 {
868 bool centerView = false;
869 BOX2D r = GetCanvas()->GetView()->GetViewport();
870
871 // Center if we're off the current view, or within 10% of its edge
872 r.Inflate( - (int) r.GetWidth() / 10 );
873
874 if( !r.Contains( aPos ) )
875 centerView = true;
876
877 std::vector<BOX2D> dialogScreenRects;
878
879 for( wxWindow* dialog : findDialogs() )
880 {
881 dialogScreenRects.emplace_back( GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ),
882 dialog->GetSize() );
883 }
884
885 // Center if we're behind an obscuring dialog, or within 10% of its edge
886 for( BOX2D rect : dialogScreenRects )
887 {
888 rect.Inflate( rect.GetWidth() / 10 );
889
890 if( rect.Contains( GetCanvas()->GetView()->ToScreen( aPos ) ) )
891 centerView = true;
892 }
893
894 if( centerView )
895 {
896 try
897 {
898 GetCanvas()->GetView()->SetCenter( aPos, dialogScreenRects );
899 }
900 catch( const ClipperLib::clipperException& exc )
901 {
902 wxLogError( wxT( "Clipper library error '%s' occurred centering object." ),
903 exc.what() );
904 }
905 }
906
907 GetCanvas()->GetViewControls()->SetCrossHairCursorPosition( aPos );
908 }
909
910
911 static const wxString productName = wxT( "KiCad E.D.A. " );
912
913
PrintDrawingSheet(const RENDER_SETTINGS * aSettings,const PAGE_INFO & aPageInfo,const wxString & aFullSheetName,const wxString & aFileName,const TITLE_BLOCK & aTitleBlock,int aSheetCount,const wxString & aPageNumber,double aMils2Iu,const PROJECT * aProject,const wxString & aSheetLayer,bool aIsFirstPage)914 void PrintDrawingSheet( const RENDER_SETTINGS* aSettings, const PAGE_INFO& aPageInfo,
915 const wxString& aFullSheetName, const wxString& aFileName,
916 const TITLE_BLOCK& aTitleBlock, int aSheetCount,
917 const wxString& aPageNumber, double aMils2Iu, const PROJECT* aProject,
918 const wxString& aSheetLayer, bool aIsFirstPage )
919 {
920 DS_DRAW_ITEM_LIST drawList;
921
922 drawList.SetDefaultPenSize( aSettings->GetDefaultPenWidth() );
923 drawList.SetMilsToIUfactor( aMils2Iu );
924 drawList.SetPageNumber( aPageNumber );
925 drawList.SetSheetCount( aSheetCount );
926 drawList.SetFileName( aFileName );
927 drawList.SetSheetName( aFullSheetName );
928 drawList.SetSheetLayer( aSheetLayer );
929 drawList.SetProject( aProject );
930 drawList.SetIsFirstPage( aIsFirstPage );
931
932 drawList.BuildDrawItemsList( aPageInfo, aTitleBlock );
933
934 // Draw item list
935 drawList.Print( aSettings );
936 }
937
938
PrintDrawingSheet(const RENDER_SETTINGS * aSettings,BASE_SCREEN * aScreen,double aMils2Iu,const wxString & aFilename,const wxString & aSheetLayer)939 void EDA_DRAW_FRAME::PrintDrawingSheet( const RENDER_SETTINGS* aSettings, BASE_SCREEN* aScreen,
940 double aMils2Iu, const wxString &aFilename,
941 const wxString &aSheetLayer )
942 {
943 if( !m_showBorderAndTitleBlock )
944 return;
945
946 wxDC* DC = aSettings->GetPrintDC();
947 wxPoint origin = DC->GetDeviceOrigin();
948
949 if( origin.y > 0 )
950 {
951 DC->SetDeviceOrigin( 0, 0 );
952 DC->SetAxisOrientation( true, false );
953 }
954
955 ::PrintDrawingSheet( aSettings, GetPageSettings(), GetScreenDesc(), aFilename, GetTitleBlock(),
956 aScreen->GetPageCount(), aScreen->GetPageNumber(), aMils2Iu, &Prj(),
957 aSheetLayer, aScreen->GetVirtualPageNumber() == 1 );
958
959 if( origin.y > 0 )
960 {
961 DC->SetDeviceOrigin( origin.x, origin.y );
962 DC->SetAxisOrientation( true, true );
963 }
964 }
965
966
GetScreenDesc() const967 wxString EDA_DRAW_FRAME::GetScreenDesc() const
968 {
969 // Virtual function. Base class implementation returns an empty string.
970 return wxEmptyString;
971 }
972
973
LibraryFileBrowser(bool doOpen,wxFileName & aFilename,const wxString & wildcard,const wxString & ext,bool isDirectory,bool aIsGlobal,const wxString & aGlobalPath)974 bool EDA_DRAW_FRAME::LibraryFileBrowser( bool doOpen, wxFileName& aFilename,
975 const wxString& wildcard, const wxString& ext,
976 bool isDirectory, bool aIsGlobal,
977 const wxString& aGlobalPath )
978 {
979 wxString prompt = doOpen ? _( "Select Library" ) : _( "New Library" );
980 aFilename.SetExt( ext );
981
982 wxString dir = aGlobalPath;
983
984
985 if( isDirectory && doOpen )
986 {
987 if( !aIsGlobal )
988 {
989 dir = Prj().GetProjectPath();
990 }
991
992 wxDirDialog dlg( this, prompt, dir,
993 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
994
995 if( dlg.ShowModal() == wxID_CANCEL )
996 return false;
997
998 aFilename = dlg.GetPath();
999 aFilename.SetExt( ext );
1000 }
1001 else
1002 {
1003 // Ensure the file has a dummy name, otherwise GTK will display the regex from the filter
1004 if( aFilename.GetName().empty() )
1005 aFilename.SetName( "Library" );
1006
1007 if( !aIsGlobal )
1008 {
1009 dir = Prj().IsNullProject() ? aFilename.GetFullPath() : Prj().GetProjectPath();
1010 }
1011
1012 wxFileDialog dlg( this, prompt, dir, aFilename.GetFullName(),
1013 wildcard, doOpen ? wxFD_OPEN | wxFD_FILE_MUST_EXIST
1014 : wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT );
1015
1016 if( dlg.ShowModal() == wxID_CANCEL )
1017 return false;
1018
1019 aFilename = dlg.GetPath();
1020 aFilename.SetExt( ext );
1021 }
1022
1023 return true;
1024 }
1025
1026
RecreateToolbars()1027 void EDA_DRAW_FRAME::RecreateToolbars()
1028 {
1029 // Rebuild all toolbars, and update the checked state of check tools
1030 if( m_mainToolBar )
1031 ReCreateHToolbar();
1032
1033 if( m_drawToolBar ) // Drawing tools (typically on right edge of window)
1034 ReCreateVToolbar();
1035
1036 if( m_optionsToolBar ) // Options (typically on left edge of window)
1037 ReCreateOptToolbar();
1038
1039 if( m_auxiliaryToolBar ) // Additional tools under main toolbar
1040 ReCreateAuxiliaryToolbar();
1041 }
1042
1043
GetColorSettings() const1044 COLOR_SETTINGS* EDA_DRAW_FRAME::GetColorSettings() const
1045 {
1046 if( !m_colorSettings )
1047 {
1048 COLOR_SETTINGS* colorSettings = Pgm().GetSettingsManager().GetColorSettings();
1049
1050 const_cast<EDA_DRAW_FRAME*>( this )->m_colorSettings = colorSettings;
1051 }
1052
1053 return m_colorSettings;
1054 }
1055
1056
setupUnits(APP_SETTINGS_BASE * aCfg)1057 void EDA_DRAW_FRAME::setupUnits( APP_SETTINGS_BASE* aCfg )
1058 {
1059 COMMON_TOOLS* cmnTool = m_toolManager->GetTool<COMMON_TOOLS>();
1060
1061 if( cmnTool )
1062 {
1063 // Tell the tool what the units used last session
1064 cmnTool->SetLastUnits( static_cast<EDA_UNITS>( aCfg->m_System.last_imperial_units ) );
1065 cmnTool->SetLastUnits( static_cast<EDA_UNITS>( aCfg->m_System.last_metric_units ) );
1066 }
1067
1068 // Tell the tool what units the frame is currently using
1069 switch( static_cast<EDA_UNITS>( aCfg->m_System.units ) )
1070 {
1071 default:
1072 case EDA_UNITS::MILLIMETRES: m_toolManager->RunAction( ACTIONS::millimetersUnits ); break;
1073 case EDA_UNITS::INCHES: m_toolManager->RunAction( ACTIONS::inchesUnits ); break;
1074 case EDA_UNITS::MILS: m_toolManager->RunAction( ACTIONS::milsUnits ); break;
1075 }
1076 }
1077
1078
GetUnitPair(EDA_UNITS & aPrimaryUnit,EDA_UNITS & aSecondaryUnits)1079 void EDA_DRAW_FRAME::GetUnitPair( EDA_UNITS& aPrimaryUnit, EDA_UNITS& aSecondaryUnits )
1080 {
1081 COMMON_TOOLS* cmnTool = m_toolManager->GetTool<COMMON_TOOLS>();
1082
1083 aPrimaryUnit = GetUserUnits();
1084 aSecondaryUnits = EDA_UNITS::MILS;
1085
1086 if( EDA_UNIT_UTILS::IsImperialUnit( aPrimaryUnit ) )
1087 {
1088 if( cmnTool )
1089 aSecondaryUnits = cmnTool->GetLastMetricUnits();
1090 else
1091 aSecondaryUnits = EDA_UNITS::MILLIMETRES;
1092 }
1093 else
1094 {
1095 if( cmnTool )
1096 aSecondaryUnits = cmnTool->GetLastImperialUnits();
1097 else
1098 aSecondaryUnits = EDA_UNITS::MILS;
1099 }
1100 }
1101
1102
resolveCanvasType()1103 void EDA_DRAW_FRAME::resolveCanvasType()
1104 {
1105 m_canvasType = loadCanvasTypeSetting();
1106
1107 // Nudge user to switch to OpenGL if they are on legacy or Cairo
1108 if( m_firstRunDialogSetting < 1 )
1109 {
1110 if( m_canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL )
1111 {
1112 // Save Cairo as default in case OpenGL crashes
1113 saveCanvasTypeSetting( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO );
1114
1115 // Switch to OpenGL, which will save the new setting if successful
1116 SwitchCanvas( EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL );
1117
1118 // Switch back to Cairo if OpenGL is not supported
1119 if( GetCanvas()->GetBackend() == EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE )
1120 SwitchCanvas( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO );
1121
1122 HardRedraw();
1123 }
1124
1125 m_firstRunDialogSetting = 1;
1126 SaveSettings( config() );
1127 }
1128 }
1129
1130
handleActivateEvent(wxActivateEvent & aEvent)1131 void EDA_DRAW_FRAME::handleActivateEvent( wxActivateEvent& aEvent )
1132 {
1133 // Force a refresh of the message panel to ensure that the text is the right color
1134 // when the window activates
1135 if( !IsIconized() )
1136 m_messagePanel->Refresh();
1137 }
1138
1139
onActivate(wxActivateEvent & aEvent)1140 void EDA_DRAW_FRAME::onActivate( wxActivateEvent& aEvent )
1141 {
1142 handleActivateEvent( aEvent );
1143
1144 aEvent.Skip();
1145 }
1146