KiCad PCB EDA Suite
eda_base_frame.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 1992-2018 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 <wx/stdpaths.h>
27 #include <wx/string.h>
28 #include <wx/display.h>
29 #include <dialog_shim.h>
30 #include <id.h>
31 #include <kiface_i.h>
32 #include <pgm_base.h>
33 #include <trace_helpers.h>
34 #include <panel_hotkeys_editor.h>
36 #include <widgets/paged_dialog.h>
37 #include <bitmaps.h>
38 #include <tool/action_menu.h>
39 #include <tool/common_control.h>
40 #include <tool/tool_manager.h>
41 #include <tool/action_manager.h>
42 #include <menus_helpers.h>
43 #include <tool/actions.h>
44 #include <filehistory.h>
45 
46 
48 #define DEFAULT_AUTO_SAVE_INTERVAL 600
49 
52 
54 static const wxString entryAutoSaveInterval = "AutoSaveInterval";
55 
57 static const wxString entryPerspective = "Perspective";
58 
60 static const wxString entryMruPath = "MostRecentlyUsedPath";
61 
62 static const wxString entryPosY = "Pos_y";
63 static const wxString entryPosX = "Pos_x";
64 static const wxString entrySizeY = "Size_y";
65 static const wxString entrySizeX = "Size_x";
66 static const wxString entryMaximized = "Maximized";
67 
69 
70 BEGIN_EVENT_TABLE( EDA_BASE_FRAME, wxFrame )
71  EVT_MENU( wxID_ABOUT, EDA_BASE_FRAME::OnKicadAbout )
72  EVT_MENU( wxID_PREFERENCES, EDA_BASE_FRAME::OnPreferences )
73 
74  EVT_CHAR_HOOK( EDA_BASE_FRAME::OnCharHook )
75  EVT_MENU_OPEN( EDA_BASE_FRAME::OnMenuOpen )
76  EVT_MENU_CLOSE( EDA_BASE_FRAME::OnMenuOpen )
77  EVT_MENU_HIGHLIGHT_ALL( EDA_BASE_FRAME::OnMenuOpen )
78 END_EVENT_TABLE()
79 
80 EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType,
81  const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
82  long aStyle, const wxString& aFrameName, KIWAY* aKiway ) :
83  wxFrame( aParent, wxID_ANY, aTitle, aPos, aSize, aStyle, aFrameName ),
84  KIWAY_HOLDER( aKiway, KIWAY_HOLDER::FRAME ),
85  m_actions( nullptr ),
86  m_immediateActions( true ),
87  m_dragSelects( true ),
88  m_moveWarpsCursor( true ),
89  m_userUnits( EDA_UNITS::MILLIMETRES )
90 {
91  m_Ident = aFrameType;
92  m_hasAutoSave = false;
93  m_autoSaveState = false;
94  m_autoSaveInterval = -1;
95  m_autoSaveTimer = new wxTimer( this, ID_AUTO_SAVE_TIMER );
96  m_mruPath = wxStandardPaths::Get().GetDocumentsDir();
97  m_toolManager = nullptr;
98 
99  // Gives a reasonable minimal size to the frame:
100  const int minsize_x = 500;
101  const int minsize_y = 400;
102  SetSizeHints( minsize_x, minsize_y, -1, -1, -1, -1 );
103 
104  // Store dimensions of the user area of the main window.
105  GetClientSize( &m_FrameSize.x, &m_FrameSize.y );
106 
107  Connect( ID_AUTO_SAVE_TIMER, wxEVT_TIMER,
108  wxTimerEventHandler( EDA_BASE_FRAME::onAutoSaveTimer ) );
109 
110  // hook wxEVT_CLOSE_WINDOW so we can call SaveSettings(). This function seems
111  // to be called before any other hook for wxCloseEvent, which is necessary.
112  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_BASE_FRAME::windowClosing ) );
113 }
114 
115 
117 {
118  for( auto& iter : GetChildren() )
119  {
120  DIALOG_SHIM* dlg = dynamic_cast<DIALOG_SHIM*>( iter );
121  if( dlg && dlg->IsQuasiModal() )
122  return dlg;
123  }
124 
125  // FIXME: CvPcb is currently implemented on top of KIWAY_PLAYER rather than DIALOG_SHIM,
126  // so we have to look for it separately.
127  if( m_Ident == FRAME_SCH )
128  {
129  wxWindow* cvpcb = wxWindow::FindWindowByName( "CvpcbFrame" );
130  if( cvpcb )
131  return cvpcb;
132  }
133 
134  return nullptr;
135 }
136 
137 
138 void EDA_BASE_FRAME::windowClosing( wxCloseEvent& event )
139 {
140  // Don't allow closing when a quasi-modal is open.
141  wxWindow* quasiModal = findQuasiModalDialog();
142 
143  if( quasiModal )
144  {
145  // Raise and notify; don't give the user a warning regarding "quasi-modal dialogs"
146  // when they have no idea what those are.
147  quasiModal->Raise();
148  wxBell();
149 
150  event.Veto();
151  return;
152  }
153 
154  wxConfigBase* cfg = config();
155 
156  if( cfg )
157  SaveSettings( cfg ); // virtual, wxFrame specific
158 
159  event.Skip(); // we did not "handle" the event, only eavesdropped on it.
160 }
161 
162 
164 {
165  delete m_autoSaveTimer;
166 
168  {
170  }
171 }
172 
173 
175 {
176 #if defined( _WIN32 )
177  return true;
178 #else
179  return false;
180 #endif
181 }
182 
183 
185 {
186 #if defined( _WIN32 )
187  // Windows: Destroys any block reason that may have existed
188  ShutdownBlockReasonDestroy( GetHandle() );
189 #endif
190 }
191 
192 
193 void EDA_BASE_FRAME::SetShutdownBlockReason( const wxString& aReason )
194 {
195 #if defined( _WIN32 )
196  // Windows: sets up the pretty message on the shutdown page on why it's being "blocked"
197  // This is used in conjunction with handling WM_QUERYENDSESSION (wxCloseEvent)
198  // ShutdownBlockReasonCreate does not block by itself
199 
200  ShutdownBlockReasonDestroy( GetHandle() ); // Destroys any existing or nonexisting reason
201 
202  if( !ShutdownBlockReasonCreate( GetHandle(), aReason.wc_str() ) )
203  {
204  // Nothing bad happens if this fails, at worst it uses a generic application is preventing shutdown message
205  wxLogDebug( wxT( "ShutdownBlockReasonCreate failed to set reason: %s" ), aReason );
206  }
207 #endif
208 }
209 
210 
211 // TODO: Implement an RAII mechanism for the stack PushTool/PopTool pairs
212 void EDA_BASE_FRAME::PushTool( const std::string& actionName )
213 {
214  m_toolStack.push_back( actionName );
215 
216  // Human cognitive stacking is very shallow; deeper tool stacks just get annoying
217  if( m_toolStack.size() > 3 )
218  m_toolStack.erase( m_toolStack.begin() );
219 
220  TOOL_ACTION* action = m_toolManager->GetActionManager()->FindAction( actionName );
221 
222  if( action )
223  DisplayToolMsg( action->GetLabel() );
224  else
225  DisplayToolMsg( actionName );
226 }
227 
228 
229 void EDA_BASE_FRAME::PopTool( const std::string& actionName )
230 {
231  // Push/pop events can get out of order (such as when they're generated by the Simulator
232  // frame but not processed until the mouse is back in the Schematic frame), so make sure
233  // we're popping the right stack frame.
234 
235  for( int i = m_toolStack.size() - 1; i >= 0; --i )
236  {
237  if( m_toolStack[ i ] == actionName )
238  {
239  m_toolStack.erase( m_toolStack.begin() + i );
240 
241  // If there's something underneath us, and it's now the top of the stack, then
242  // re-activate it
243  if( ( --i ) >= 0 && i == (int)m_toolStack.size() - 1 )
244  {
245  std::string back = m_toolStack[ i ];
247 
248  if( action )
249  {
250  // Pop the action as running it will push it back onto the stack
251  m_toolStack.pop_back();
252 
253  TOOL_EVENT evt = action->MakeEvent();
254  evt.SetHasPosition( false );
255  GetToolManager()->PostEvent( evt );
256  }
257  }
258  else
260 
261  return;
262  }
263  }
264 }
265 
266 
268 {
269  if( m_toolStack.empty() )
270  return ACTIONS::selectionTool.GetName();
271  else
272  return m_toolStack.back();
273 }
274 
275 
276 bool EDA_BASE_FRAME::IsCurrentTool( const TOOL_ACTION& aAction ) const
277 {
278  if( m_toolStack.empty() )
279  return &aAction == &ACTIONS::selectionTool;
280  else
281  return m_toolStack.back() == aAction.GetName();
282 }
283 
284 
285 bool EDA_BASE_FRAME::ProcessEvent( wxEvent& aEvent )
286 {
287 #ifdef __WXMAC__
288  // Apple in its infinite wisdom will raise a disabled window before even passing
289  // us the event, so we have no way to stop it. Instead, we have to catch an
290  // improperly ordered disabled window and quasi-modal dialog here and reorder
291  // them.
292  if( !IsEnabled() && IsActive() )
293  {
294  wxWindow* dlg = findQuasiModalDialog();
295  if( dlg )
296  dlg->Raise();
297  }
298 #endif
299 
300  if( !wxFrame::ProcessEvent( aEvent ) )
301  return false;
302 
303  if( IsShown() && m_hasAutoSave && IsActive() &&
305  {
306  if( !m_autoSaveState )
307  {
308  wxLogTrace( traceAutoSave, wxT( "Starting auto save timer." ) );
309  m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT );
310  m_autoSaveState = true;
311  }
312  else if( m_autoSaveTimer->IsRunning() )
313  {
314  wxLogTrace( traceAutoSave, wxT( "Stopping auto save timer." ) );
315  m_autoSaveTimer->Stop();
316  m_autoSaveState = false;
317  }
318  }
319 
320  return true;
321 }
322 
323 
325 {
326  m_autoSaveInterval = aInterval;
327 
328  if( m_autoSaveTimer->IsRunning() )
329  {
330  if( m_autoSaveInterval > 0 )
331  {
332  m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT );
333  }
334  else
335  {
336  m_autoSaveTimer->Stop();
337  m_autoSaveState = false;
338  }
339  }
340 }
341 
342 
343 void EDA_BASE_FRAME::onAutoSaveTimer( wxTimerEvent& aEvent )
344 {
345  if( !doAutoSave() )
346  m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT );
347 }
348 
349 
351 {
352  wxCHECK_MSG( false, true, wxT( "Auto save timer function not overridden. Bad programmer!" ) );
353 }
354 
355 
356 void EDA_BASE_FRAME::OnCharHook( wxKeyEvent& event )
357 {
358  wxLogTrace( kicadTraceKeyEvent, "EDA_BASE_FRAME::OnCharHook %s", dump( event ) );
359  // Key events can be filtered here.
360  // Currently no filtering is made.
361  event.Skip();
362 }
363 
364 
365 void EDA_BASE_FRAME::OnMenuOpen( wxMenuEvent& event )
366 {
367  //
368  // wxWidgets has several issues that we have to work around:
369  //
370  // 1) wxWidgets 3.0.x Windows has a bug where wxEVT_MENU_OPEN and wxEVT_MENU_HIGHLIGHT
371  // events are not captured by the ACTON_MENU menus. So we forward them here.
372  // (FWIW, this one is fixed in wxWidgets 3.1.x.)
373  //
374  // 2) wxWidgets doesn't pass the menu pointer for wxEVT_MENU_HIGHLIGHT events. So we
375  // store the menu pointer from the wxEVT_MENU_OPEN call.
376  //
377  // 3) wxWidgets has no way to tell whether a command is from a menu selection or a
378  // hotkey. So we keep track of menu highlighting so we can differentiate.
379  //
380 
381  static ACTION_MENU* currentMenu;
382 
383  if( event.GetEventType() == wxEVT_MENU_OPEN )
384  {
385  currentMenu = dynamic_cast<ACTION_MENU*>( event.GetMenu() );
386 
387  if( currentMenu )
388  currentMenu->OnMenuEvent( event );
389  }
390  else if( event.GetEventType() == wxEVT_MENU_HIGHLIGHT )
391  {
392  if( currentMenu )
393  currentMenu->OnMenuEvent( event );
394  }
395  else if( event.GetEventType() == wxEVT_MENU_CLOSE )
396  {
397  if( currentMenu )
398  currentMenu->OnMenuEvent( event );
399 
400  currentMenu = nullptr;
401  }
402 
403  event.Skip();
404 }
405 
406 
408 {
409 }
410 
411 
412 void EDA_BASE_FRAME::AddStandardHelpMenu( wxMenuBar* aMenuBar )
413 {
414  COMMON_CONTROL* commonControl = m_toolManager->GetTool<COMMON_CONTROL>();
415  ACTION_MENU* helpMenu = new ACTION_MENU( false );
416 
417  helpMenu->SetTool( commonControl );
418 
419  helpMenu->Add( ACTIONS::help );
420  helpMenu->Add( ACTIONS::gettingStarted );
421  helpMenu->Add( ACTIONS::listHotKeys );
422  helpMenu->Add( ACTIONS::getInvolved );
423 
424  helpMenu->AppendSeparator();
425  helpMenu->Add( _( "&About KiCad" ), "", wxID_ABOUT, about_xpm );
426 
427  aMenuBar->Append( helpMenu, _( "&Help" ) );
428 }
429 
430 
432 {
433  if( GetMenuBar() )
434  {
435  ReCreateMenuBar();
436  GetMenuBar()->Refresh();
437  }
438 }
439 
440 
441 void EDA_BASE_FRAME::CommonSettingsChanged( bool aEnvVarsChanged )
442 {
443  if( GetToolManager() )
445 
446  if( GetMenuBar() )
447  {
448  // For icons in menus, icon scaling & hotkeys
449  ReCreateMenuBar();
450  GetMenuBar()->Refresh();
451  }
452 
453  wxConfigBase* settings = Pgm().CommonSettings();
454 
455  settings->Read( WARP_MOUSE_ON_MOVE_KEY, &m_moveWarpsCursor );
456  settings->Read( PREFER_SELECT_TO_DRAG_KEY, &m_dragSelects );
457  settings->Read( IMMEDIATE_ACTIONS_KEY, &m_immediateActions );
458 }
459 
460 
461 void EDA_BASE_FRAME::LoadSettings( wxConfigBase* aCfg )
462 {
463  int maximized = 0;
464 
465  wxString baseCfgName = ConfigBaseName();
466 
467  wxString text = baseCfgName + entryPosX;
468  aCfg->Read( text, &m_FramePos.x, m_FramePos.x );
469 
470  text = baseCfgName + entryPosY;
471  aCfg->Read( text, &m_FramePos.y, m_FramePos.y );
472 
473  text = baseCfgName + entrySizeX;
474  aCfg->Read( text, &m_FrameSize.x, m_FrameSize.x );
475 
476  text = baseCfgName + entrySizeY;
477  aCfg->Read( text, &m_FrameSize.y, m_FrameSize.y );
478 
479  text = baseCfgName + entryMaximized;
480  aCfg->Read( text, &maximized, 0 );
481 
482  if( m_hasAutoSave )
483  {
484  text = baseCfgName + entryAutoSaveInterval;
485  aCfg->Read( text, &m_autoSaveInterval, DEFAULT_AUTO_SAVE_INTERVAL );
486  }
487 
488  // Ensure the window is on a connected display, and is visible.
489  // (at least a corner of the frame must be visible on screen)
490  // Sometimes, if a window was moved on an auxiliary display, and when this
491  // display is no more available, it is not the case.
492  wxRect rect( m_FramePos, m_FrameSize );
493 
494  if( wxDisplay::GetFromPoint( rect.GetTopLeft() ) == wxNOT_FOUND &&
495  wxDisplay::GetFromPoint( rect.GetTopRight() ) == wxNOT_FOUND &&
496  wxDisplay::GetFromPoint( rect.GetBottomLeft() ) == wxNOT_FOUND &&
497  wxDisplay::GetFromPoint( rect.GetBottomRight() ) == wxNOT_FOUND )
498  {
499  m_FramePos = wxDefaultPosition;
500  }
501 
502  // Ensure Window title bar is visible
503 #if defined( __WXMAC__ )
504  // for macOSX, the window must be below system (macOSX) toolbar
505  // Ypos_min = GetMBarHeight(); seems no more exist in new API (subject to change)
506  int Ypos_min = 20;
507 #else
508  int Ypos_min = 0;
509 #endif
510  if( m_FramePos.y < Ypos_min )
511  m_FramePos.y = Ypos_min;
512 
513  if( maximized )
514  Maximize();
515 
516  aCfg->Read( baseCfgName + entryPerspective, &m_perspective );
517  aCfg->Read( baseCfgName + entryMruPath, &m_mruPath );
518 
519  wxConfigBase* settings = Pgm().CommonSettings();
520 
521  if( !settings->Read( WARP_MOUSE_ON_MOVE_KEY, &m_moveWarpsCursor ) )
522  {
523  // Legacy versions stored the property only for Eeschema, so see if we have it there
524  std::unique_ptr<wxConfigBase> pcbSettings = GetNewConfig( wxT( "eeschema" ) );
525  pcbSettings->Read( "MoveWarpsCursor", &m_moveWarpsCursor, true );
526  }
527 
528  if( !settings->Read( PREFER_SELECT_TO_DRAG_KEY, &m_dragSelects ) )
529  {
530  // Legacy versions stored the property only for PCBNew, so see if we have it there
531  std::unique_ptr<wxConfigBase> pcbSettings = GetNewConfig( wxT( "pcbnew" ) );
532  pcbSettings->Read( "DragSelects", &m_dragSelects, true );
533  }
534 
535  settings->Read( IMMEDIATE_ACTIONS_KEY, &m_immediateActions, false );
536 }
537 
538 
539 void EDA_BASE_FRAME::SaveSettings( wxConfigBase* aCfg )
540 {
541  wxString text;
542 
543  if( IsIconized() )
544  return;
545 
546  wxString baseCfgName = ConfigBaseName();
547 
548  m_FrameSize = GetSize();
549  m_FramePos = GetPosition();
550 
551  text = baseCfgName + wxT( "Pos_x" );
552  aCfg->Write( text, (long) m_FramePos.x );
553 
554  text = baseCfgName + wxT( "Pos_y" );
555  aCfg->Write( text, (long) m_FramePos.y );
556 
557  text = baseCfgName + wxT( "Size_x" );
558  aCfg->Write( text, (long) m_FrameSize.x );
559 
560  text = baseCfgName + wxT( "Size_y" );
561  aCfg->Write( text, (long) m_FrameSize.y );
562 
563  text = baseCfgName + wxT( "Maximized" );
564  aCfg->Write( text, IsMaximized() );
565 
566  if( m_hasAutoSave )
567  {
568  text = baseCfgName + entryAutoSaveInterval;
569  aCfg->Write( text, m_autoSaveInterval );
570  }
571 
572  // Once this is fully implemented, wxAuiManager will be used to maintain
573  // the persistance of the main frame and all it's managed windows and
574  // all of the legacy frame persistence position code can be removed.
575  wxString perspective = m_auimgr.SavePerspective();
576 
577  // printf( "perspective(%s): %s\n",
578  // TO_UTF8( m_FrameName + entryPerspective ), TO_UTF8( perspective ) );
579  aCfg->Write( baseCfgName + entryPerspective, perspective );
580  aCfg->Write( baseCfgName + entryMruPath, m_mruPath );
581 }
582 
583 
584 wxConfigBase* EDA_BASE_FRAME::config()
585 {
586  // KICAD_MANAGER_FRAME overrides this
587  wxConfigBase* ret = Kiface().KifaceSettings();
588  //wxASSERT( ret );
589  return ret;
590 }
591 
592 
594 {
595  return Kiface().KifaceSearch();
596 }
597 
598 
600 {
601  return Kiface().GetHelpFileName();
602 }
603 
604 
605 void EDA_BASE_FRAME::PrintMsg( const wxString& text )
606 {
607  SetStatusText( text );
608 }
609 
610 
611 void EDA_BASE_FRAME::UpdateFileHistory( const wxString& FullFileName, FILE_HISTORY* aFileHistory )
612 {
613  FILE_HISTORY* fileHistory = aFileHistory;
614 
615  if( !fileHistory )
616  fileHistory = &Kiface().GetFileHistory();
617 
618  fileHistory->AddFileToHistory( FullFileName );
619 }
620 
621 
622 wxString EDA_BASE_FRAME::GetFileFromHistory( int cmdId, const wxString& type,
623  FILE_HISTORY* aFileHistory )
624 {
625  FILE_HISTORY* fileHistory = aFileHistory;
626 
627  if( !fileHistory )
628  fileHistory = &Kiface().GetFileHistory();
629 
630  int baseId = fileHistory->GetBaseId();
631 
632  wxASSERT( cmdId >= baseId && cmdId < baseId + (int) fileHistory->GetCount() );
633 
634  unsigned i = cmdId - baseId;
635 
636  if( i < fileHistory->GetCount() )
637  {
638  wxString fn = fileHistory->GetHistoryFile( i );
639 
640  if( wxFileName::FileExists( fn ) )
641  return fn;
642  else
643  {
644  wxString msg = wxString::Format( _( "File \"%s\" was not found." ), fn );
645  wxMessageBox( msg );
646 
647  fileHistory->RemoveFileFromHistory( i );
648  }
649  }
650 
651  return wxEmptyString;
652 }
653 
654 
655 void EDA_BASE_FRAME::OnKicadAbout( wxCommandEvent& event )
656 {
657  void ShowAboutDialog(EDA_BASE_FRAME * aParent); // See AboutDialog_main.cpp
658  ShowAboutDialog( this );
659 }
660 
661 
662 void EDA_BASE_FRAME::OnPreferences( wxCommandEvent& event )
663 {
664  PAGED_DIALOG dlg( this, _( "Preferences" ) );
665  wxTreebook* book = dlg.GetTreebook();
666 
667  book->AddPage( new PANEL_COMMON_SETTINGS( &dlg, book ), _( "Common" ) );
668 
669  PANEL_HOTKEYS_EDITOR* hotkeysPanel = new PANEL_HOTKEYS_EDITOR( this, book, false );
670  book->AddPage( hotkeysPanel, _( "Hotkeys" ) );
671 
672  for( unsigned i = 0; i < KIWAY_PLAYER_COUNT; ++i )
673  {
674  KIWAY_PLAYER* frame = dlg.Kiway().Player( (FRAME_T) i, false );
675 
676  if( frame )
677  frame->InstallPreferences( &dlg, hotkeysPanel );
678  }
679 
680  // The Kicad manager frame is not a player so we have to add it by hand
681  wxWindow* manager = wxFindWindowByName( KICAD_MANAGER_FRAME_NAME );
682 
683  if( manager )
684  static_cast<EDA_BASE_FRAME*>( manager )->InstallPreferences( &dlg, hotkeysPanel );
685 
686  if( dlg.ShowModal() == wxID_OK )
687  dlg.Kiway().CommonSettingsChanged( false );
688 }
689 
690 
691 bool EDA_BASE_FRAME::IsWritable( const wxFileName& aFileName )
692 {
693  wxString msg;
694  wxFileName fn = aFileName;
695 
696  // Check for absence of a file path with a file name. Unfortunately KiCad
697  // uses paths relative to the current project path without the ./ part which
698  // confuses wxFileName. Making the file name path absolute may be less than
699  // elegant but it solves the problem.
700  if( fn.GetPath().IsEmpty() && fn.HasName() )
701  fn.MakeAbsolute();
702 
703  wxCHECK_MSG( fn.IsOk(), false,
704  wxT( "File name object is invalid. Bad programmer!" ) );
705  wxCHECK_MSG( !fn.GetPath().IsEmpty(), false,
706  wxT( "File name object path <" ) + fn.GetFullPath() +
707  wxT( "> is not set. Bad programmer!" ) );
708 
709  if( fn.IsDir() && !fn.IsDirWritable() )
710  {
711  msg.Printf( _( "You do not have write permissions to folder \"%s\"." ),
712  GetChars( fn.GetPath() ) );
713  }
714  else if( !fn.FileExists() && !fn.IsDirWritable() )
715  {
716  msg.Printf( _( "You do not have write permissions to save file \"%s\" to folder \"%s\"." ),
717  GetChars( fn.GetFullName() ), GetChars( fn.GetPath() ) );
718  }
719  else if( fn.FileExists() && !fn.IsFileWritable() )
720  {
721  msg.Printf( _( "You do not have write permissions to save file \"%s\"." ),
722  GetChars( fn.GetFullPath() ) );
723  }
724 
725  if( !msg.IsEmpty() )
726  {
727  wxMessageBox( msg );
728  return false;
729  }
730 
731  return true;
732 }
733 
734 
735 void EDA_BASE_FRAME::CheckForAutoSaveFile( const wxFileName& aFileName )
736 {
737  wxCHECK_RET( aFileName.IsOk(), wxT( "Invalid file name!" ) );
738 
739  wxFileName autoSaveFileName = aFileName;
740 
741  // Check for auto save file.
742  autoSaveFileName.SetName( GetAutoSaveFilePrefix() + aFileName.GetName() );
743 
744  wxLogTrace( traceAutoSave,
745  wxT( "Checking for auto save file " ) + autoSaveFileName.GetFullPath() );
746 
747  if( !autoSaveFileName.FileExists() )
748  return;
749 
750  wxString msg = wxString::Format( _(
751  "Well this is potentially embarrassing!\n"
752  "It appears that the last time you were editing the file\n"
753  "\"%s\"\n"
754  "it was not saved properly. Do you wish to restore the last saved edits you made?" ),
755  GetChars( aFileName.GetFullName() )
756  );
757 
758  int response = wxMessageBox( msg, Pgm().App().GetAppName(), wxYES_NO | wxICON_QUESTION, this );
759 
760  // Make a backup of the current file, delete the file, and rename the auto save file to
761  // the file name.
762  if( response == wxYES )
763  {
764  // Get the backup file name.
765  wxFileName backupFileName = aFileName;
766  backupFileName.SetExt( aFileName.GetExt() + GetBackupSuffix() );
767 
768  // If an old backup file exists, delete it. If an old copy of the file exists, rename
769  // it to the backup file name
770  if( aFileName.FileExists() )
771  {
772  // Rename the old file to the backup file name.
773  if( !wxRenameFile( aFileName.GetFullPath(), backupFileName.GetFullPath(), true ) )
774  {
775  msg.Printf( _( "Could not create backup file \"%s\"" ),
776  GetChars( backupFileName.GetFullPath() ) );
777  wxMessageBox( msg );
778  }
779  }
780 
781  if( !wxRenameFile( autoSaveFileName.GetFullPath(), aFileName.GetFullPath() ) )
782  {
783  wxMessageBox( _( "The auto save file could not be renamed to the board file name." ),
784  Pgm().App().GetAppName(), wxOK | wxICON_EXCLAMATION, this );
785  }
786  }
787  else
788  {
789  wxLogTrace( traceAutoSave,
790  wxT( "Removing auto save file " ) + autoSaveFileName.GetFullPath() );
791 
792  // Remove the auto save file when using the previous file as is.
793  wxRemoveFile( autoSaveFileName.GetFullPath() );
794  }
795 }
796 
797 
799 {
800  // This function should be overridden in child classes
801  return false;
802 }
#define IMMEDIATE_ACTIONS_KEY
Definition: pgm_base.h:58
EDA_UNITS
Definition: common.h:72
static TOOL_ACTION listHotKeys
Definition: actions.h:167
void PrintMsg(const wxString &text)
KIWAY_HOLDER is a mix in class which holds the location of a wxWindow's KIWAY.
Definition: kiway_holder.h:39
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
KIWAY_PLAYER is a wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of ...
Definition: kiway_player.h:59
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_holder.h:56
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:98
#define PREFER_SELECT_TO_DRAG_KEY
Definition: pgm_base.h:59
wxString m_mruPath
void SetShutdownBlockReason(const wxString &reason)
Sets the block reason why the window/application is preventing OS shutdown.
virtual bool doAutoSave()
This should be overridden by the derived class to handle the auto save feature.
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
std::unique_ptr< wxConfigBase > GetNewConfig(const wxString &aProgName)
Create a new wxConfig so we can put configuration files in a more proper place for each platform.
Definition: common.cpp:231
ACTION_MENU.
Definition: action_menu.h:43
VTBL_ENTRY void CommonSettingsChanged(bool aEnvVarsChanged)
Function CommonSettingsChanged Calls CommonSettingsChanged() on all KIWAY_PLAYERs.
Definition: kiway.cpp:471
FRAME_T
Enum FRAME_T is the set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition: frame_type.h:34
void windowClosing(wxCloseEvent &event)
(with its unexpected name so it does not collide with the real OnWindowClose() function provided in d...
virtual void SaveSettings(wxConfigBase *aCfg)
Saves common frame parameters to a configuration data file.
virtual bool isAutoSaveRequired() const
Return the auto save status of the application.
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:103
void onAutoSaveTimer(wxTimerEvent &aEvent)
Handle the auto save timer event.
wxString ConfigBaseName()
virtual bool IsContentModified()
Get if the contents of the frame have been modified since the last save.
virtual wxConfigBase * config()
Returns the wxConfigBase used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
void UpdateHotKeys(bool aFullUpdate)
Function UpdateHotKeys() Optionally reads the hotkey config files and then rebuilds the internal hotk...
SEARCH_STACK looks for files in a number of places.
Definition: search_stack.h:41
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:84
wxAuiManager m_auimgr
bool IsWritable(const wxFileName &aFileName)
Checks if aFileName can be written.
This class implements a file history object to store a list of files, that can then be added to a men...
Definition: filehistory.h:40
wxTimer * m_autoSaveTimer
static const wxString entryPosX
X position of frame, in pixels (suffix)
TOOL_ACTION * FindAction(const std::string &aActionName) const
Function FindAction() Finds an action with a given name (if there is one available).
wxString m_perspective
void UpdateFileHistory(const wxString &FullFileName, FILE_HISTORY *aFileHistory=NULL)
Update the list of recently opened files.
wxTreebook * GetTreebook()
Definition: paged_dialog.h:43
void AddFileToHistory(const wxString &aFile) override
Adds a file to the history.
Definition: filehistory.cpp:53
SEARCH_STACK & KifaceSearch()
Only for DSO specific 'non-library' files.
Definition: kiface_i.h:127
wxWindow * findQuasiModalDialog()
const wxString & GetHelpFileName() const
Function GetHelpFileName returns just the basename portion of the current help file.
Definition: kiface_i.h:121
void ShowAboutDialog(EDA_BASE_FRAME *aParent)
virtual void OnCharHook(wxKeyEvent &event)
Capture the key event before it is sent to the GUI.
const BITMAP_OPAQUE about_xpm[1]
Definition: about.cpp:84
void CheckForAutoSaveFile(const wxFileName &aFileName)
Check if an auto save file exists for aFileName and takes the appropriate action depending on the use...
VTBL_ENTRY KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=NULL)
Function Player returns the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:341
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
static wxString GetBackupSuffix()
virtual const SEARCH_STACK & sys_search()
Return a SEARCH_STACK pertaining to entire program.
virtual void DisplayToolMsg(const wxString &msg)
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
virtual void LoadSettings(wxConfigBase *aCfg)
Load common frame parameters from a configuration file.
TOOL_EVENT.
Definition: tool_event.h:171
void AddStandardHelpMenu(wxMenuBar *aMenuBar)
Adds the standard KiCad help menu to the menubar.
static const wxString entryPerspective
Configuration file entry for wxAuiManger perspective.
bool IsQuasiModal()
Definition: dialog_shim.h:124
KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the s...
Definition: kiway.h:274
wxLogTrace helper definitions.
#define DEFAULT_AUTO_SAVE_INTERVAL
The default auto save interval is 10 minutes.
void OnKicadAbout(wxCommandEvent &event)
COMMON_CONTROL.
void OnMenuOpen(wxMenuEvent &event)
Workaround some issues in wxWidgets where the menu events aren't captured by the menus themselves.
bool SupportsShutdownBlockReason()
Whether or not the window supports setting a shutdown block reason.
void OnMenuEvent(wxMenuEvent &aEvent)
void RemoveShutdownBlockReason()
Removes any shutdown block reason set.
virtual void CommonSettingsChanged(bool aEnvVarsChanged)
Notification event that some of the common (suite-wide) settings have changed.
static wxString GetAutoSaveFilePrefix()
void SetAutoSaveInterval(int aInterval)
wxString GetFileFromHistory(int cmdId, const wxString &type, FILE_HISTORY *aFileHistory=NULL)
Fetches the file name from the file history list.
wxString GetLabel() const
Definition: tool_action.cpp:69
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:101
see class PGM_BASE
std::vector< std::string > m_toolStack
static const wxString entrySizeX
Width of frame, in pixels (suffix)
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
ACTION_MANAGER * GetActionManager()
Definition: tool_manager.h:163
#define KICAD_MANAGER_FRAME_NAME
#define _(s)
Definition: 3d_actions.cpp:31
FILE_HISTORY & GetFileHistory()
Definition: kiface_i.h:123
The base frame for deriving all KiCad main window classes.
TOOL_ACTION.
Definition: tool_action.h:46
static const wxString entrySizeY
Height of frame, in pixels (suffix)
static const wxString entryPosY
Y position of frame, in pixels (suffix)
TOOL_EVENT MakeEvent() const
Function MakeEvent() Returns the event associated with the action (i.e.
Definition: tool_action.h:107
static TOOL_ACTION help
Definition: actions.h:166
const wxChar *const kicadTraceKeyEvent
Flag to enable wxKeyEvent debug tracing.
virtual wxString help_name()
static const wxString entryAutoSaveInterval
Configuration file entry name for auto save interval.
virtual void PopTool(const std::string &actionName)
void OnPreferences(wxCommandEvent &event)
static const wxString entryMaximized
Nonzero iff frame is maximized (suffix)
virtual void InstallPreferences(PAGED_DIALOG *, PANEL_HOTKEYS_EDITOR *)
Function InstallPreferences Allow a frame to load its preference panels (if any) into the preferences...
TOOL_MANAGER * m_toolManager
std::string CurrentToolName() const
static const wxString entryMruPath
Configuration file entry for most recently used path.
static TOOL_ACTION getInvolved
Definition: actions.h:168
void PostEvent(const TOOL_EVENT &aEvent)
Puts an event to the event queue to be processed at the end of event processing cycle.
Definition: tool_manager.h:237
bool IsCurrentTool(const TOOL_ACTION &aAction) const
virtual void ShowChangedLanguage()
Redraw the menus and what not in current language.
static TOOL_ACTION gettingStarted
Definition: actions.h:165
bool ProcessEvent(wxEvent &aEvent) override
Override the default process event handler to implement the auto save feature.
static TOOL_ACTION selectionTool
Definition: actions.h:143
void SetHasPosition(bool aHasPosition)
Definition: tool_event.h:261
const std::string & GetName() const
Function GetName() Returns name of the action.
Definition: tool_action.h:78
#define WARP_MOUSE_ON_MOVE_KEY
Definition: pgm_base.h:57
virtual void ReCreateMenuBar()
Recreates the menu bar.