1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2015-2021 KiCad Developers, see change_log.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 #include <base_units.h>
26 #include <kiway.h>
27 #include <pgm_base.h>
28 #include <eeschema_settings.h>
29 #include <symbol_editor/symbol_editor_settings.h>
30 #include <sch_draw_panel.h>
31 #include <sch_view.h>
32 #include <sch_painter.h>
33 #include <settings/settings_manager.h>
34 #include <confirm.h>
35 #include <preview_items/selection_area.h>
36 #include <symbol_library.h>
37 #include <sch_base_frame.h>
38 #include <symbol_lib_table.h>
39 #include <tool/action_toolbar.h>
40 #include <tool/tool_manager.h>
41 #include <tool/tool_dispatcher.h>
42 #include <tools/ee_actions.h>
43 #include <tools/ee_selection_tool.h>
44 
45 
SchGetLibSymbol(const LIB_ID & aLibId,SYMBOL_LIB_TABLE * aLibTable,SYMBOL_LIB * aCacheLib,wxWindow * aParent,bool aShowErrorMsg)46 LIB_SYMBOL* SchGetLibSymbol( const LIB_ID& aLibId, SYMBOL_LIB_TABLE* aLibTable,
47                              SYMBOL_LIB* aCacheLib, wxWindow* aParent, bool aShowErrorMsg )
48 {
49     wxCHECK_MSG( aLibTable, nullptr, "Invalid symbol library table." );
50 
51     LIB_SYMBOL* symbol = nullptr;
52 
53     try
54     {
55         symbol = aLibTable->LoadSymbol( aLibId );
56 
57         if( !symbol && aCacheLib )
58         {
59             wxCHECK_MSG( aCacheLib->IsCache(), nullptr, "Invalid cache library." );
60 
61             wxString cacheName = aLibId.GetLibNickname().wx_str();
62             cacheName += "_" + aLibId.GetLibItemName();
63             symbol = aCacheLib->FindSymbol( cacheName );
64         }
65     }
66     catch( const IO_ERROR& ioe )
67     {
68         if( aShowErrorMsg )
69         {
70             wxString msg = wxString::Format( _( "Error loading symbol %s from library '%s'." ),
71                                              aLibId.GetLibItemName().wx_str(),
72                                              aLibId.GetLibNickname().wx_str() );
73             DisplayErrorMessage( aParent, msg, ioe.What() );
74         }
75     }
76 
77     return symbol;
78 }
79 
80 
SCH_BASE_FRAME(KIWAY * aKiway,wxWindow * aParent,FRAME_T aWindowType,const wxString & aTitle,const wxPoint & aPosition,const wxSize & aSize,long aStyle,const wxString & aFrameName)81 SCH_BASE_FRAME::SCH_BASE_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aWindowType,
82                                 const wxString& aTitle, const wxPoint& aPosition,
83                                 const wxSize& aSize, long aStyle, const wxString& aFrameName ) :
84     EDA_DRAW_FRAME( aKiway, aParent, aWindowType, aTitle, aPosition, aSize, aStyle, aFrameName ),
85     m_base_frame_defaults( nullptr, "base_Frame_defaults" )
86 {
87     createCanvas();
88 
89     Bind( wxEVT_IDLE,
90           [this]( wxIdleEvent& aEvent )
91           {
92               // Handle cursor adjustments.  While we can get motion and key events through
93               // wxWidgets, we can't get modifier-key-up events.
94               if( m_toolManager )
95               {
96                   EE_SELECTION_TOOL* selTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
97 
98                   if( selTool )
99                       selTool->OnIdle( aEvent );
100               }
101           } );
102 }
103 
104 
~SCH_BASE_FRAME()105 SCH_BASE_FRAME::~SCH_BASE_FRAME()
106 {
107 }
108 
109 
GetScreen() const110 SCH_SCREEN* SCH_BASE_FRAME::GetScreen() const
111 {
112     return (SCH_SCREEN*) EDA_DRAW_FRAME::GetScreen();
113 }
114 
115 
eeconfig() const116 EESCHEMA_SETTINGS* SCH_BASE_FRAME::eeconfig() const
117 {
118     return dynamic_cast<EESCHEMA_SETTINGS*>( config() );
119 }
120 
121 
libeditconfig() const122 SYMBOL_EDITOR_SETTINGS* SCH_BASE_FRAME::libeditconfig() const
123 {
124     return dynamic_cast<SYMBOL_EDITOR_SETTINGS*>( config() );
125 }
126 
127 
SetPageSettings(const PAGE_INFO & aPageSettings)128 void SCH_BASE_FRAME::SetPageSettings( const PAGE_INFO& aPageSettings )
129 {
130     GetScreen()->SetPageSettings( aPageSettings );
131 }
132 
133 
GetPageSettings() const134 const PAGE_INFO& SCH_BASE_FRAME::GetPageSettings () const
135 {
136     return GetScreen()->GetPageSettings();
137 }
138 
139 
GetPageSizeIU() const140 const wxSize SCH_BASE_FRAME::GetPageSizeIU() const
141 {
142     // GetSizeIU is compile time dependent:
143     return GetScreen()->GetPageSettings().GetSizeIU();
144 }
145 
146 
GetTitleBlock() const147 const TITLE_BLOCK& SCH_BASE_FRAME::GetTitleBlock() const
148 {
149     wxASSERT( GetScreen() );
150     return GetScreen()->GetTitleBlock();
151 }
152 
153 
SetTitleBlock(const TITLE_BLOCK & aTitleBlock)154 void SCH_BASE_FRAME::SetTitleBlock( const TITLE_BLOCK& aTitleBlock )
155 {
156     wxASSERT( GetScreen() );
157     GetScreen()->SetTitleBlock( aTitleBlock );
158 }
159 
160 
UpdateStatusBar()161 void SCH_BASE_FRAME::UpdateStatusBar()
162 {
163     wxString        line;
164     BASE_SCREEN*    screen = GetScreen();
165 
166     if( !screen )
167         return;
168 
169     EDA_DRAW_FRAME::UpdateStatusBar();
170 
171     // Display absolute and relative coordinates
172     VECTOR2D cursorPos = GetCanvas()->GetViewControls()->GetCursorPosition();
173     VECTOR2D d         = cursorPos - screen->m_LocalOrigin;
174 
175     line.Printf( "X %s  Y %s",
176                  MessageTextFromValue( GetUserUnits(), cursorPos.x, false ),
177                  MessageTextFromValue( GetUserUnits(), cursorPos.y, false ) );
178     SetStatusText( line, 2 );
179 
180     line.Printf( "dx %s  dy %s  dist %s",
181                  MessageTextFromValue( GetUserUnits(), d.x, false ),
182                  MessageTextFromValue( GetUserUnits(), d.y, false ),
183                  MessageTextFromValue( GetUserUnits(), hypot( d.x, d.y ), false ) );
184     SetStatusText( line, 3 );
185 
186     // refresh grid display
187     DisplayGridMsg();
188 
189     // refresh units display
190     DisplayUnitsMsg();
191 }
192 
193 
GetLibSymbol(const LIB_ID & aLibId,bool aUseCacheLib,bool aShowErrorMsg)194 LIB_SYMBOL* SCH_BASE_FRAME::GetLibSymbol( const LIB_ID& aLibId, bool aUseCacheLib,
195                                           bool aShowErrorMsg )
196 {
197     SYMBOL_LIB* cache = ( aUseCacheLib ) ? Prj().SchLibs()->GetCacheLibrary() : nullptr;
198 
199     return SchGetLibSymbol( aLibId, Prj().SchSymbolLibTable(), cache, this, aShowErrorMsg );
200 }
201 
202 
saveSymbolLibTables(bool aGlobal,bool aProject)203 bool SCH_BASE_FRAME::saveSymbolLibTables( bool aGlobal, bool aProject )
204 {
205     wxString msg;
206     bool success = true;
207 
208     if( aGlobal )
209     {
210         try
211         {
212             SYMBOL_LIB_TABLE::GetGlobalLibTable().Save( SYMBOL_LIB_TABLE::GetGlobalTableFileName() );
213         }
214         catch( const IO_ERROR& ioe )
215         {
216             success = false;
217             msg.Printf( _( "Error saving global symbol library table:\n%s" ), ioe.What() );
218             wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
219         }
220     }
221 
222     if( aProject && !Prj().GetProjectName().IsEmpty() )
223     {
224         wxFileName fn( Prj().GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
225 
226         try
227         {
228             Prj().SchSymbolLibTable()->Save( fn.GetFullPath() );
229         }
230         catch( const IO_ERROR& ioe )
231         {
232             success = false;
233             msg.Printf( _( "Error saving project-specific symbol library table:\n%s" ),
234                         ioe.What() );
235             wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
236         }
237     }
238 
239     return success;
240 }
241 
242 
RedrawScreen(const wxPoint & aCenterPoint,bool aWarpPointer)243 void SCH_BASE_FRAME::RedrawScreen( const wxPoint& aCenterPoint, bool aWarpPointer )
244 {
245     GetCanvas()->GetView()->SetCenter( aCenterPoint );
246 
247     if( aWarpPointer )
248         GetCanvas()->GetViewControls()->CenterOnCursor();
249 
250     GetCanvas()->Refresh();
251 }
252 
253 
CenterScreen(const wxPoint & aCenterPoint,bool aWarpPointer)254 void SCH_BASE_FRAME::CenterScreen( const wxPoint& aCenterPoint, bool aWarpPointer )
255 {
256     GetCanvas()->GetView()->SetCenter( aCenterPoint );
257 
258     if( aWarpPointer )
259         GetCanvas()->GetViewControls()->WarpCursor( aCenterPoint, true );
260 
261     GetCanvas()->Refresh();
262 }
263 
264 
HardRedraw()265 void SCH_BASE_FRAME::HardRedraw()
266 {
267     if( GetCanvas() && GetCanvas()->GetView() )
268     {
269         GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL );
270         GetCanvas()->ForceRefresh();
271     }
272 }
273 
274 
GetCanvas() const275 SCH_DRAW_PANEL* SCH_BASE_FRAME::GetCanvas() const
276 {
277     return static_cast<SCH_DRAW_PANEL*>( EDA_DRAW_FRAME::GetCanvas() );
278 }
279 
280 
GetRenderSettings()281 KIGFX::SCH_RENDER_SETTINGS* SCH_BASE_FRAME::GetRenderSettings()
282 {
283     if( GetCanvas() && GetCanvas()->GetView() )
284     {
285         if( KIGFX::PAINTER* painter = GetCanvas()->GetView()->GetPainter() )
286             return static_cast<KIGFX::SCH_RENDER_SETTINGS*>( painter->GetSettings() );
287     }
288 
289     return nullptr;
290 }
291 
292 
createCanvas()293 void SCH_BASE_FRAME::createCanvas()
294 {
295     m_canvasType = loadCanvasTypeSetting();
296 
297     SetCanvas( new SCH_DRAW_PANEL( this, wxID_ANY, wxPoint( 0, 0 ), m_frameSize,
298                                    GetGalDisplayOptions(), m_canvasType ) );
299     ActivateGalCanvas();
300 }
301 
302 
UpdateItem(EDA_ITEM * aItem,bool isAddOrDelete,bool aUpdateRtree)303 void SCH_BASE_FRAME::UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete, bool aUpdateRtree )
304 {
305     EDA_ITEM* parent = aItem->GetParent();
306 
307     if( aItem->Type() == SCH_SHEET_PIN_T )
308     {
309         // Sheet pins aren't in the view.  Refresh their parent.
310         if( parent )
311             GetCanvas()->GetView()->Update( parent );
312     }
313     else
314     {
315         if( !isAddOrDelete )
316             GetCanvas()->GetView()->Update( aItem );
317 
318         // Some children are drawn from their parents.  Mark them for re-paint.
319         static KICAD_T parentTypes[] = { SCH_SYMBOL_T, SCH_SHEET_T, SCH_GLOBAL_LABEL_T, EOT };
320 
321         if( parent && parent->IsType( parentTypes ) )
322             GetCanvas()->GetView()->Update( parent, KIGFX::REPAINT );
323     }
324 
325     /**
326      * Be careful when calling this.  Update will invalidate RTree iterators, so you cannot call this
327      * while doing things like `for( SCH_ITEM* item : screen->Items() )`
328      */
329     if( aUpdateRtree)
330         GetScreen()->Update( static_cast<SCH_ITEM*>( aItem ) );
331 
332     // Calling Refresh() here introduces a bi-stable state: when doing operations on a
333     // large number of items if at some point the refresh timer times out and does a
334     // refresh it will take long enough that the next item will also time out, and the
335     // next, and the next, etc.
336     // GetCanvas()->Refresh();
337 }
338 
339 
RefreshSelection()340 void SCH_BASE_FRAME::RefreshSelection()
341 {
342     if( m_toolManager )
343     {
344         EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
345         SELECTION&         selection = selectionTool->GetSelection();
346         KIGFX::SCH_VIEW*   view = GetCanvas()->GetView();
347 
348         for( EDA_ITEM* item : selection )
349         {
350             EDA_ITEM* parent = item->GetParent();
351 
352             if( item->Type() == SCH_SHEET_PIN_T )
353             {
354                 // Sheet pins aren't in the view.  Refresh their parent.
355                 if( parent )
356                     GetCanvas()->GetView()->Update( parent );
357             }
358             else
359             {
360                 view->Update( item, KIGFX::REPAINT );
361 
362                 // Symbol children are drawn from their parents.  Mark them for re-paint.
363                 if( parent && parent->Type() == SCH_SYMBOL_T )
364                     GetCanvas()->GetView()->Update( parent, KIGFX::REPAINT );
365             }
366         }
367     }
368 }
369 
370 
AddToScreen(EDA_ITEM * aItem,SCH_SCREEN * aScreen)371 void SCH_BASE_FRAME::AddToScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen )
372 {
373     auto screen = aScreen;
374 
375     if( aScreen == nullptr )
376         screen = GetScreen();
377 
378     screen->Append( (SCH_ITEM*) aItem );
379 
380     if( screen == GetScreen() )
381     {
382         GetCanvas()->GetView()->Add( aItem );
383         UpdateItem( aItem, true );           // handle any additional parent semantics
384     }
385 }
386 
387 
RemoveFromScreen(EDA_ITEM * aItem,SCH_SCREEN * aScreen)388 void SCH_BASE_FRAME::RemoveFromScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen )
389 {
390     auto screen = aScreen;
391 
392     if( aScreen == nullptr )
393         screen = GetScreen();
394 
395     if( screen == GetScreen() )
396         GetCanvas()->GetView()->Remove( aItem );
397 
398     screen->Remove( (SCH_ITEM*) aItem );
399 
400     if( screen == GetScreen() )
401         UpdateItem( aItem, true );           // handle any additional parent semantics
402 }
403 
404 
SyncView()405 void SCH_BASE_FRAME::SyncView()
406 {
407     GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL );
408 }
409 
410 
GetLayerColor(SCH_LAYER_ID aLayer)411 COLOR4D SCH_BASE_FRAME::GetLayerColor( SCH_LAYER_ID aLayer )
412 {
413     return GetColorSettings()->GetColor( aLayer );
414 }
415 
416 
CommonSettingsChanged(bool aEnvVarsChanged,bool aTextVarsChanged)417 void SCH_BASE_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
418 {
419     EDA_DRAW_FRAME::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
420 
421     EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
422     m_colorSettings = Pgm().GetSettingsManager().GetColorSettings( cfg->m_ColorTheme );
423 
424     GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL );
425     GetCanvas()->Refresh();
426 }
427 
428 
GetColorSettings() const429 COLOR_SETTINGS* SCH_BASE_FRAME::GetColorSettings() const
430 {
431     if( !m_colorSettings )
432     {
433         SETTINGS_MANAGER&  settingsManager = Pgm().GetSettingsManager();
434         EESCHEMA_SETTINGS* cfg = settingsManager.GetAppSettings<EESCHEMA_SETTINGS>();
435         COLOR_SETTINGS*    colorSettings = settingsManager.GetColorSettings( cfg->m_ColorTheme );
436 
437         const_cast<SCH_BASE_FRAME*>( this )->m_colorSettings = colorSettings;
438     }
439 
440     return m_colorSettings;
441 }
442 
443 
GetDrawBgColor() const444 COLOR4D SCH_BASE_FRAME::GetDrawBgColor() const
445 {
446     return GetColorSettings()->GetColor( LAYER_SCHEMATIC_BACKGROUND );
447 }
448 
449