1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2013 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 <bitmaps.h>
27 #include <wx/hyperlink.h>
28 #include <base_screen.h>
29 #include <symbol_library.h>
30 #include <confirm.h>
31 #include <core/kicad_algo.h>
32 #include <eeschema_id.h>
33 #include <eeschema_settings.h>
34 #include <env_paths.h>
35 #include <kiface_base.h>
36 #include <kiplatform/app.h>
37 #include <kiway_express.h>
38 #include <symbol_edit_frame.h>
39 #include <symbol_library_manager.h>
40 #include <lib_text.h>
41 #include <symbol_editor_settings.h>
42 #include <paths.h>
43 #include <pgm_base.h>
44 #include <sch_painter.h>
45 #include <sch_view.h>
46 #include <settings/settings_manager.h>
47 #include <symbol_lib_table.h>
48 #include <tool/action_manager.h>
49 #include <tool/action_toolbar.h>
50 #include <tool/common_control.h>
51 #include <tool/common_tools.h>
52 #include <tool/editor_conditions.h>
53 #include <tool/picker_tool.h>
54 #include <tool/selection.h>
55 #include <tool/tool_dispatcher.h>
56 #include <tool/tool_manager.h>
57 #include <tool/zoom_tool.h>
58 #include <tools/ee_actions.h>
59 #include <tools/ee_inspection_tool.h>
60 #include <tools/ee_point_editor.h>
61 #include <tools/ee_selection_tool.h>
62 #include <tools/symbol_editor_control.h>
63 #include <tools/symbol_editor_drawing_tools.h>
64 #include <tools/symbol_editor_edit_tool.h>
65 #include <tools/symbol_editor_move_tool.h>
66 #include <tools/symbol_editor_pin_tool.h>
67 #include <widgets/app_progress_dialog.h>
68 #include <widgets/infobar.h>
69 #include <widgets/lib_tree.h>
70 #include <widgets/wx_progress_reporters.h>
71 #include <widgets/symbol_tree_pane.h>
72 #include <wildcards_and_files_ext.h>
73 #include <panel_sym_lib_table.h>
74 #include <wx/choicdlg.h>
75 #include <string_utils.h>
76 
77 
78 bool SYMBOL_EDIT_FRAME::m_showDeMorgan = false;
79 
80 
BEGIN_EVENT_TABLE(SYMBOL_EDIT_FRAME,EDA_DRAW_FRAME)81 BEGIN_EVENT_TABLE( SYMBOL_EDIT_FRAME, EDA_DRAW_FRAME )
82     EVT_SIZE( SYMBOL_EDIT_FRAME::OnSize )
83 
84     EVT_COMBOBOX( ID_LIBEDIT_SELECT_UNIT_NUMBER, SYMBOL_EDIT_FRAME::OnSelectUnit )
85 
86     // menubar commands
87     EVT_MENU( wxID_EXIT, SYMBOL_EDIT_FRAME::OnExitKiCad )
88     EVT_MENU( wxID_CLOSE, SYMBOL_EDIT_FRAME::CloseWindow )
89     EVT_MENU( ID_GRID_SETTINGS, SCH_BASE_FRAME::OnGridSettings )
90 
91     // Update user interface elements.
92     EVT_UPDATE_UI( ID_LIBEDIT_SELECT_UNIT_NUMBER, SYMBOL_EDIT_FRAME::OnUpdateUnitNumber )
93 
94 END_EVENT_TABLE()
95 
96 
97 SYMBOL_EDIT_FRAME::SYMBOL_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
98         SCH_BASE_FRAME( aKiway, aParent, FRAME_SCH_SYMBOL_EDITOR, _( "Library Editor" ),
99                         wxDefaultPosition, wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE,
100                         LIB_EDIT_FRAME_NAME ),
101         m_unitSelectBox( nullptr ),
102         m_isSymbolFromSchematic( false )
103 {
104     SetShowDeMorgan( false );
105     m_SyncPinEdit = false;
106 
107     m_symbol = nullptr;
108     m_treePane = nullptr;
109     m_libMgr = nullptr;
110     m_unit = 1;
111     m_convert = 1;
112     m_aboutTitle = _( "KiCad Symbol Editor" );
113 
114     wxIcon icon;
115     wxIconBundle icon_bundle;
116 
117     icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_libedit ) );
118     icon_bundle.AddIcon( icon );
119     icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_libedit_32 ) );
120     icon_bundle.AddIcon( icon );
121     icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_libedit_16 ) );
122     icon_bundle.AddIcon( icon );
123 
124     SetIcons( icon_bundle );
125 
126     m_settings = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
127     LoadSettings( m_settings );
128 
129     m_libMgr = new SYMBOL_LIBRARY_MANAGER( *this );
130 
131     // Preload libraries before using SyncLibraries the first time, as the preload is threaded
132     WX_PROGRESS_REPORTER reporter( this, _( "Loading Symbol Libraries" ),
133                                    m_libMgr->GetLibraryCount(), true );
134     m_libMgr->Preload( reporter );
135 
136     SyncLibraries( false );
137     m_treePane = new SYMBOL_TREE_PANE( this, m_libMgr );
138 
139     resolveCanvasType();
140     SwitchCanvas( m_canvasType );
141 
142     // Ensure axis are always drawn
143     KIGFX::GAL_DISPLAY_OPTIONS& gal_opts = GetGalDisplayOptions();
144     gal_opts.m_axesEnabled = true;
145 
146     m_dummyScreen = new SCH_SCREEN();
147     SetScreen( m_dummyScreen );
148     GetScreen()->m_Center = true;
149 
150     GetCanvas()->GetViewControls()->SetCrossHairCursorPosition( VECTOR2D( 0, 0 ), false );
151 
152     GetRenderSettings()->LoadColors( GetColorSettings() );
153     GetRenderSettings()->m_IsSymbolEditor = true;
154     GetCanvas()->GetGAL()->SetAxesColor( m_colorSettings->GetColor( LAYER_SCHEMATIC_GRID_AXES ) );
155 
156     setupTools();
157     setupUIConditions();
158 
159     ReCreateMenuBar();
160     ReCreateHToolbar();
161     ReCreateVToolbar();
162     ReCreateOptToolbar();
163 
164     updateTitle();
165     UpdateSymbolMsgPanelInfo();
166     RebuildSymbolUnitsList();
167 
168     m_auimgr.SetManagedWindow( this );
169 
170     CreateInfoBar();
171     m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( "MainToolbar" )
172                       .Top().Layer( 6 ) );
173     m_auimgr.AddPane( m_messagePanel, EDA_PANE().Messages().Name( "MsgPanel" )
174                       .Bottom().Layer( 6 ) );
175 
176     m_auimgr.AddPane( m_optionsToolBar, EDA_PANE().VToolbar().Name( "OptToolbar" )
177                       .Left().Layer( 3 ) );
178     m_auimgr.AddPane( m_treePane, EDA_PANE().Palette().Name( "SymbolTree" )
179                       .Left().Layer( 2 )
180                       .Caption( _( "Libraries" ) )
181                       .MinSize( 250, -1 ).BestSize( 250, -1 ) );
182     m_auimgr.AddPane( m_drawToolBar, EDA_PANE().VToolbar().Name( "ToolsToolbar" )
183                       .Right().Layer( 2 ) );
184 
185     m_auimgr.AddPane( GetCanvas(), wxAuiPaneInfo().Name( "DrawFrame" )
186                       .CentrePane() );
187 
188     FinishAUIInitialization();
189 
190     if( m_settings->m_LibWidth > 0 )
191     {
192         wxAuiPaneInfo& treePane = m_auimgr.GetPane( "SymbolTree" );
193 
194         // wxAUI hack: force width by setting MinSize() and then Fixed()
195         // thanks to ZenJu http://trac.wxwidgets.org/ticket/13180
196         treePane.MinSize( m_settings->m_LibWidth, -1 );
197         treePane.Fixed();
198         m_auimgr.Update();
199 
200         // now make it resizable again
201         treePane.Resizable();
202         m_auimgr.Update();
203 
204         // Note: DO NOT call m_auimgr.Update() anywhere after this; it will nuke the size
205         // back to minimum.
206         treePane.MinSize( 250, -1 );
207     }
208 
209     Raise();
210     Show( true );
211 
212     SyncView();
213     GetCanvas()->GetView()->UseDrawPriority( true );
214     GetCanvas()->GetGAL()->SetAxesEnabled( true );
215 
216     setupUnits( m_settings );
217 
218     // Set the working/draw area size to display a symbol to a reasonable value:
219     // A 600mm x 600mm with a origin at the area center looks like a large working area
220     double max_size_x = Millimeter2iu( 600 );
221     double max_size_y = Millimeter2iu( 600 );
222     BOX2D bbox;
223     bbox.SetOrigin( -max_size_x /2, -max_size_y/2 );
224     bbox.SetSize( max_size_x, max_size_y );
225     GetCanvas()->GetView()->SetBoundary( bbox );
226 
227     m_toolManager->RunAction( ACTIONS::zoomFitScreen, true );
228 
229     KIPLATFORM::APP::SetShutdownBlockReason( this, _( "Library changes are unsaved" ) );
230 
231     // Catch unhandled accelerator command characters that were no handled by the library tree
232     // panel.
233     Bind( wxEVT_CHAR, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
234     Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
235 
236     // Ensure the window is on top
237     Raise();
238 }
239 
240 
~SYMBOL_EDIT_FRAME()241 SYMBOL_EDIT_FRAME::~SYMBOL_EDIT_FRAME()
242 {
243     // Shutdown all running tools
244     if( m_toolManager )
245         m_toolManager->ShutdownAllTools();
246 
247     if( IsSymbolFromSchematic() )
248     {
249         delete m_symbol;
250         m_symbol = nullptr;
251 
252         SCH_SCREEN* screen = GetScreen();
253         delete screen;
254         m_isSymbolFromSchematic = false;
255     }
256     // current screen is destroyed in EDA_DRAW_FRAME
257     SetScreen( m_dummyScreen );
258 
259     auto libedit = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
260     Pgm().GetSettingsManager().Save( libedit );
261 
262     delete m_libMgr;
263 }
264 
265 
LoadSettings(APP_SETTINGS_BASE * aCfg)266 void SYMBOL_EDIT_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
267 {
268     wxCHECK_RET( m_settings, "Call to SYMBOL_EDIT_FRAME::LoadSettings with null m_boardAdapter" );
269 
270     SCH_BASE_FRAME::LoadSettings( GetSettings() );
271 
272     GetRenderSettings()->m_ShowPinsElectricalType = m_settings->m_ShowPinElectricalType;
273 
274     // Hidden elements must be editable
275     GetRenderSettings()->m_ShowHiddenText = true;
276     GetRenderSettings()->m_ShowHiddenPins = true;
277     GetRenderSettings()->m_ShowUmbilicals = false;
278 }
279 
280 
SaveSettings(APP_SETTINGS_BASE * aCfg)281 void SYMBOL_EDIT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
282 {
283     wxCHECK_RET( m_settings, "Call to SYMBOL_EDIT_FRAME::LoadSettings with null m_boardAdapter" );
284 
285     GetGalDisplayOptions().m_axesEnabled = true;
286 
287     SCH_BASE_FRAME::SaveSettings( GetSettings() );
288 
289     m_settings->m_ShowPinElectricalType  = GetRenderSettings()->m_ShowPinsElectricalType;
290     m_settings->m_LibWidth               = m_treePane->GetSize().x;
291 }
292 
293 
config() const294 APP_SETTINGS_BASE* SYMBOL_EDIT_FRAME::config() const
295 {
296     return static_cast<APP_SETTINGS_BASE*>( GetSettings() );
297 }
298 
299 
GetColorSettings() const300 COLOR_SETTINGS* SYMBOL_EDIT_FRAME::GetColorSettings() const
301 {
302     SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
303 
304     if( GetSettings()->m_UseEeschemaColorSettings )
305         return mgr.GetColorSettings( mgr.GetAppSettings<EESCHEMA_SETTINGS>()->m_ColorTheme );
306     else
307         return mgr.GetColorSettings( GetSettings()->m_ColorTheme );
308 }
309 
310 
setupTools()311 void SYMBOL_EDIT_FRAME::setupTools()
312 {
313     // Create the manager and dispatcher & route draw panel events to the dispatcher
314     m_toolManager = new TOOL_MANAGER;
315     m_toolManager->SetEnvironment( GetScreen(), GetCanvas()->GetView(),
316                                    GetCanvas()->GetViewControls(), GetSettings(), this );
317     m_actions = new EE_ACTIONS();
318     m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
319 
320     // Register tools
321     m_toolManager->RegisterTool( new COMMON_CONTROL );
322     m_toolManager->RegisterTool( new COMMON_TOOLS );
323     m_toolManager->RegisterTool( new ZOOM_TOOL );
324     m_toolManager->RegisterTool( new EE_SELECTION_TOOL );
325     m_toolManager->RegisterTool( new PICKER_TOOL );
326     m_toolManager->RegisterTool( new EE_INSPECTION_TOOL );
327     m_toolManager->RegisterTool( new SYMBOL_EDITOR_PIN_TOOL );
328     m_toolManager->RegisterTool( new SYMBOL_EDITOR_DRAWING_TOOLS );
329     m_toolManager->RegisterTool( new EE_POINT_EDITOR );
330     m_toolManager->RegisterTool( new SYMBOL_EDITOR_MOVE_TOOL );
331     m_toolManager->RegisterTool( new SYMBOL_EDITOR_EDIT_TOOL );
332     m_toolManager->RegisterTool( new SYMBOL_EDITOR_CONTROL );
333     m_toolManager->InitTools();
334 
335     // Run the selection tool, it is supposed to be always active
336     m_toolManager->InvokeTool( "eeschema.InteractiveSelection" );
337 
338     GetCanvas()->SetEventDispatcher( m_toolDispatcher );
339 }
340 
341 
setupUIConditions()342 void SYMBOL_EDIT_FRAME::setupUIConditions()
343 {
344     SCH_BASE_FRAME::setupUIConditions();
345 
346     ACTION_MANAGER*   mgr = m_toolManager->GetActionManager();
347     EDITOR_CONDITIONS cond( this );
348 
349     wxASSERT( mgr );
350 
351 #define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
352 #define CHECK( x )  ACTION_CONDITIONS().Check( x )
353 
354     auto haveSymbolCond =
355             [this]( const SELECTION& )
356             {
357                 return m_symbol;
358             };
359 
360     auto isEditableCond =
361             [this]( const SELECTION& )
362             {
363                 // Only root symbols from the new s-expression libraries or the schematic
364                 // are editable.
365                 return IsSymbolEditable() && !IsSymbolAlias();
366             };
367 
368     auto libModifiedCondition =
369             [this]( const SELECTION& sel )
370             {
371                 return m_libMgr->HasModifications();
372             };
373 
374     auto libSelectedCondition =
375             [this]( const SELECTION& sel )
376             {
377                 return !GetTargetLibId().GetLibNickname().empty();
378             };
379 
380     auto canEditProperties =
381             [this]( const SELECTION& sel )
382             {
383                 return m_symbol && ( !IsSymbolFromLegacyLibrary() || IsSymbolFromSchematic() );
384             };
385 
386     auto saveSymbolAsCondition =
387             [this]( const SELECTION& aSel )
388             {
389                 return getTargetSymbol() != nullptr;
390             };
391 
392     mgr->SetConditions( ACTIONS::saveAll,           ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
393     mgr->SetConditions( ACTIONS::save,              ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
394     mgr->SetConditions( EE_ACTIONS::saveLibraryAs,  ENABLE( libSelectedCondition ) );
395     mgr->SetConditions( EE_ACTIONS::saveSymbolAs,   ENABLE( saveSymbolAsCondition ) );
396     mgr->SetConditions( EE_ACTIONS::newSymbol,      ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
397     mgr->SetConditions( EE_ACTIONS::importSymbol,   ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
398 
399     mgr->SetConditions( ACTIONS::undo,              ENABLE( haveSymbolCond && cond.UndoAvailable() ) );
400     mgr->SetConditions( ACTIONS::redo,              ENABLE( haveSymbolCond && cond.RedoAvailable() ) );
401     mgr->SetConditions( ACTIONS::revert,            ENABLE( haveSymbolCond && libModifiedCondition ) );
402 
403     mgr->SetConditions( ACTIONS::toggleGrid,        CHECK( cond.GridVisible() ) );
404     mgr->SetConditions( ACTIONS::toggleCursorStyle, CHECK( cond.FullscreenCursor() ) );
405     mgr->SetConditions( ACTIONS::millimetersUnits,  CHECK( cond.Units( EDA_UNITS::MILLIMETRES ) ) );
406     mgr->SetConditions( ACTIONS::inchesUnits,       CHECK( cond.Units( EDA_UNITS::INCHES ) ) );
407     mgr->SetConditions( ACTIONS::milsUnits,         CHECK( cond.Units( EDA_UNITS::MILS ) ) );
408 
409     mgr->SetConditions( ACTIONS::cut,               ENABLE( isEditableCond ) );
410     mgr->SetConditions( ACTIONS::copy,              ENABLE( haveSymbolCond ) );
411     mgr->SetConditions( ACTIONS::paste,             ENABLE( isEditableCond && SELECTION_CONDITIONS::Idle ) );
412     mgr->SetConditions( ACTIONS::doDelete,          ENABLE( isEditableCond ) );
413     mgr->SetConditions( ACTIONS::duplicate,         ENABLE( isEditableCond ) );
414     mgr->SetConditions( ACTIONS::selectAll,         ENABLE( haveSymbolCond ) );
415 
416     mgr->SetConditions( EE_ACTIONS::rotateCW,       ENABLE( isEditableCond ) );
417     mgr->SetConditions( EE_ACTIONS::rotateCCW,      ENABLE( isEditableCond ) );
418     mgr->SetConditions( EE_ACTIONS::mirrorH,        ENABLE( isEditableCond ) );
419     mgr->SetConditions( EE_ACTIONS::mirrorV,        ENABLE( isEditableCond ) );
420 
421     mgr->SetConditions( ACTIONS::zoomTool,          CHECK( cond.CurrentTool( ACTIONS::zoomTool ) ) );
422     mgr->SetConditions( ACTIONS::selectionTool,     CHECK( cond.CurrentTool( ACTIONS::selectionTool ) ) );
423 
424     auto pinTypeCond =
425             [this]( const SELECTION& )
426             {
427                 return GetRenderSettings()->m_ShowPinsElectricalType;
428             };
429 
430     auto showCompTreeCond =
431             [this]( const SELECTION& )
432             {
433                 return IsSymbolTreeShown();
434             };
435 
436     mgr->SetConditions( EE_ACTIONS::showElectricalTypes, CHECK( pinTypeCond ) );
437     mgr->SetConditions( EE_ACTIONS::showSymbolTree, CHECK( showCompTreeCond ) );
438 
439     auto demorganCond =
440             [this]( const SELECTION& )
441             {
442                 return GetShowDeMorgan();
443             };
444 
445     auto demorganStandardCond =
446             [this]( const SELECTION& )
447             {
448                 return m_convert == LIB_ITEM::LIB_CONVERT::BASE;
449             };
450 
451     auto demorganAlternateCond =
452             [this]( const SELECTION& )
453             {
454                 return m_convert == LIB_ITEM::LIB_CONVERT::DEMORGAN;
455             };
456 
457     auto multiUnitModeCond =
458             [this]( const SELECTION& )
459             {
460                 return m_symbol && m_symbol->IsMulti() && !m_symbol->UnitsLocked();
461             };
462 
463     auto syncedPinsModeCond =
464             [this]( const SELECTION& )
465             {
466                 return m_SyncPinEdit;
467             };
468 
469     auto haveDatasheetCond =
470             [this]( const SELECTION& )
471             {
472                 return m_symbol && !m_symbol->GetDatasheetField().GetText().IsEmpty();
473             };
474 
475     mgr->SetConditions( EE_ACTIONS::showDatasheet,    ENABLE( haveDatasheetCond ) );
476     mgr->SetConditions( EE_ACTIONS::symbolProperties, ENABLE( canEditProperties && haveSymbolCond ) );
477     mgr->SetConditions( EE_ACTIONS::runERC,           ENABLE( haveSymbolCond ) );
478     mgr->SetConditions( EE_ACTIONS::pinTable,         ENABLE( isEditableCond && haveSymbolCond ) );
479 
480     mgr->SetConditions( EE_ACTIONS::showDeMorganStandard,
481                         ACTION_CONDITIONS().Enable( demorganCond ).Check( demorganStandardCond ) );
482     mgr->SetConditions( EE_ACTIONS::showDeMorganAlternate,
483                         ACTION_CONDITIONS().Enable( demorganCond ).Check( demorganAlternateCond ) );
484     mgr->SetConditions( EE_ACTIONS::toggleSyncedPinsMode,
485                         ACTION_CONDITIONS().Enable( multiUnitModeCond ).Check( syncedPinsModeCond ) );
486 
487 // Only enable a tool if the symbol is edtable
488 #define EDIT_TOOL( tool ) ACTION_CONDITIONS().Enable( isEditableCond ).Check( cond.CurrentTool( tool ) )
489 
490     mgr->SetConditions( ACTIONS::deleteTool,             EDIT_TOOL( ACTIONS::deleteTool ) );
491     mgr->SetConditions( EE_ACTIONS::placeSymbolPin,      EDIT_TOOL( EE_ACTIONS::placeSymbolPin ) );
492     mgr->SetConditions( EE_ACTIONS::placeSymbolText,     EDIT_TOOL( EE_ACTIONS::placeSymbolText ) );
493     mgr->SetConditions( EE_ACTIONS::drawSymbolRectangle, EDIT_TOOL( EE_ACTIONS::drawSymbolRectangle ) );
494     mgr->SetConditions( EE_ACTIONS::drawSymbolCircle,    EDIT_TOOL( EE_ACTIONS::drawSymbolCircle ) );
495     mgr->SetConditions( EE_ACTIONS::drawSymbolArc,       EDIT_TOOL( EE_ACTIONS::drawSymbolArc ) );
496     mgr->SetConditions( EE_ACTIONS::drawSymbolLines,     EDIT_TOOL( EE_ACTIONS::drawSymbolLines ) );
497     mgr->SetConditions( EE_ACTIONS::placeSymbolAnchor,   EDIT_TOOL( EE_ACTIONS::placeSymbolAnchor ) );
498 
499 #undef CHECK
500 #undef ENABLE
501 #undef EDIT_TOOL
502 }
503 
504 
canCloseWindow(wxCloseEvent & aEvent)505 bool SYMBOL_EDIT_FRAME::canCloseWindow( wxCloseEvent& aEvent )
506 {
507     // Shutdown blocks must be determined and vetoed as early as possible
508     if( KIPLATFORM::APP::SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION
509             && IsContentModified() )
510     {
511         return false;
512     }
513 
514     if( m_isSymbolFromSchematic && IsContentModified() )
515     {
516         SCH_EDIT_FRAME* schframe = (SCH_EDIT_FRAME*) Kiway().Player( FRAME_SCH, false );
517 
518         switch( UnsavedChangesDialog( this,
519                                       _( "Save changes to schematic before closing?" ),
520                                       nullptr ) )
521         {
522         case wxID_YES:
523             if( schframe && GetCurSymbol() )  // Should be always the case
524                 schframe->SaveSymbolToSchematic( *GetCurSymbol(), m_schematicSymbolUUID );
525 
526             return true;
527 
528         case wxID_NO: return true;
529 
530         default:
531         case wxID_CANCEL: return false;
532         }
533     }
534 
535     if( !saveAllLibraries( true ) )
536     {
537         return false;
538     }
539 
540     return true;
541 }
542 
543 
doCloseWindow()544 void SYMBOL_EDIT_FRAME::doCloseWindow()
545 {
546     Destroy();
547 }
548 
549 
RebuildSymbolUnitsList()550 void SYMBOL_EDIT_FRAME::RebuildSymbolUnitsList()
551 {
552     if( !m_unitSelectBox )
553         return;
554 
555     if( m_unitSelectBox->GetCount() != 0 )
556         m_unitSelectBox->Clear();
557 
558     if( !m_symbol || m_symbol->GetUnitCount() <= 1 )
559     {
560         m_unit = 1;
561         m_unitSelectBox->Append( wxEmptyString );
562     }
563     else
564     {
565         for( int i = 0; i < m_symbol->GetUnitCount(); i++ )
566         {
567             wxString sub  = LIB_SYMBOL::SubReference( i+1, false );
568             wxString unit = wxString::Format( _( "Unit %s" ), sub );
569             m_unitSelectBox->Append( unit );
570         }
571     }
572 
573     // Ensure the selected unit is compatible with the number of units of the current symbol:
574     if( m_symbol && m_symbol->GetUnitCount() < m_unit )
575         m_unit = 1;
576 
577     m_unitSelectBox->SetSelection(( m_unit > 0 ) ? m_unit - 1 : 0 );
578 }
579 
580 
OnToggleSymbolTree(wxCommandEvent & event)581 void SYMBOL_EDIT_FRAME::OnToggleSymbolTree( wxCommandEvent& event )
582 {
583     auto& treePane = m_auimgr.GetPane( m_treePane );
584     treePane.Show( !IsSymbolTreeShown() );
585     m_auimgr.Update();
586 }
587 
588 
IsSymbolTreeShown() const589 bool SYMBOL_EDIT_FRAME::IsSymbolTreeShown() const
590 {
591     return const_cast<wxAuiManager&>( m_auimgr ).GetPane( m_treePane ).IsShown();
592 }
593 
594 
FreezeLibraryTree()595 void SYMBOL_EDIT_FRAME::FreezeLibraryTree()
596 {
597     m_treePane->Freeze();
598     m_libMgr->GetAdapter()->Freeze();
599 }
600 
601 
ThawLibraryTree()602 void SYMBOL_EDIT_FRAME::ThawLibraryTree()
603 {
604     m_libMgr->GetAdapter()->Thaw();
605     m_treePane->Thaw();
606 }
607 
608 
OnExitKiCad(wxCommandEvent & event)609 void SYMBOL_EDIT_FRAME::OnExitKiCad( wxCommandEvent& event )
610 {
611     Kiway().OnKiCadExit();
612 }
613 
614 
OnUpdateUnitNumber(wxUpdateUIEvent & event)615 void SYMBOL_EDIT_FRAME::OnUpdateUnitNumber( wxUpdateUIEvent& event )
616 {
617     event.Enable( m_symbol && m_symbol->GetUnitCount() > 1 );
618 }
619 
620 
OnSelectUnit(wxCommandEvent & event)621 void SYMBOL_EDIT_FRAME::OnSelectUnit( wxCommandEvent& event )
622 {
623     int i = event.GetSelection();
624 
625     if( ( i == wxNOT_FOUND ) || ( ( i + 1 ) == m_unit ) )
626         return;
627 
628     m_toolManager->RunAction( ACTIONS::cancelInteractive, true );
629     m_toolManager->RunAction( EE_ACTIONS::clearSelection, true );
630 
631     m_unit = i + 1;
632 
633     m_toolManager->ResetTools( TOOL_BASE::MODEL_RELOAD );
634     RebuildView();
635     UpdateSymbolMsgPanelInfo();
636 }
637 
638 
IsSymbolFromLegacyLibrary() const639 bool SYMBOL_EDIT_FRAME::IsSymbolFromLegacyLibrary() const
640 {
641     if( m_symbol )
642     {
643         SYMBOL_LIB_TABLE_ROW* row = m_libMgr->GetLibrary( m_symbol->GetLibNickname() );
644 
645         if( row && row->GetType() == SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) )
646             return true;
647     }
648 
649     return false;
650 }
651 
652 
GetCurLib() const653 wxString SYMBOL_EDIT_FRAME::GetCurLib() const
654 {
655     wxString libNickname = Prj().GetRString( PROJECT::SCH_LIBEDIT_CUR_LIB );
656 
657     if( !libNickname.empty() )
658     {
659         if( !Prj().SchSymbolLibTable()->HasLibrary( libNickname ) )
660         {
661             Prj().SetRString( PROJECT::SCH_LIBEDIT_CUR_LIB, wxEmptyString );
662             libNickname = wxEmptyString;
663         }
664     }
665 
666     return libNickname;
667 }
668 
669 
SetCurLib(const wxString & aLibNickname)670 wxString SYMBOL_EDIT_FRAME::SetCurLib( const wxString& aLibNickname )
671 {
672     wxString old = GetCurLib();
673 
674     if( aLibNickname.empty() || !Prj().SchSymbolLibTable()->HasLibrary( aLibNickname ) )
675         Prj().SetRString( PROJECT::SCH_LIBEDIT_CUR_LIB, wxEmptyString );
676     else
677         Prj().SetRString( PROJECT::SCH_LIBEDIT_CUR_LIB, aLibNickname );
678 
679     return old;
680 }
681 
682 
SetCurSymbol(LIB_SYMBOL * aSymbol,bool aUpdateZoom)683 void SYMBOL_EDIT_FRAME::SetCurSymbol( LIB_SYMBOL* aSymbol, bool aUpdateZoom )
684 {
685     m_toolManager->RunAction( EE_ACTIONS::clearSelection, true );
686     GetCanvas()->GetView()->Clear();
687     delete m_symbol;
688 
689     m_symbol = aSymbol;
690 
691     // select the current symbol in the tree widget
692     if( !IsSymbolFromSchematic() && m_symbol )
693         m_treePane->GetLibTree()->SelectLibId( m_symbol->GetLibId() );
694     else
695         m_treePane->GetLibTree()->Unselect();
696 
697     wxString symbolName = m_symbol ? m_symbol->GetName() : wxString();
698 
699     // retain in case this wxFrame is re-opened later on the same PROJECT
700     Prj().SetRString( PROJECT::SCH_LIBEDIT_CUR_SYMBOL, symbolName );
701 
702     // Ensure synchronized pin edit can be enabled only symbols with interchangeable units
703     m_SyncPinEdit = aSymbol && aSymbol->IsRoot() && aSymbol->IsMulti() && !aSymbol->UnitsLocked();
704 
705     m_toolManager->ResetTools( TOOL_BASE::MODEL_RELOAD );
706 
707     GetRenderSettings()->m_ShowUnit = m_unit;
708     GetRenderSettings()->m_ShowConvert = m_convert;
709     GetRenderSettings()->m_ShowDisabled = IsSymbolFromLegacyLibrary() && !IsSymbolFromSchematic();
710     GetRenderSettings()->m_ShowGraphicsDisabled = IsSymbolAlias() && !IsSymbolFromSchematic();
711     GetCanvas()->DisplaySymbol( m_symbol );
712     GetCanvas()->GetView()->HideDrawingSheet();
713     GetCanvas()->GetView()->ClearHiddenFlags();
714 
715     if( aUpdateZoom )
716         m_toolManager->RunAction( ACTIONS::zoomFitScreen, true );
717 
718     GetCanvas()->Refresh();
719 
720     WX_INFOBAR* infobar = GetInfoBar();
721 
722     if( IsSymbolFromSchematic() )
723     {
724         wxString msg;
725         msg.Printf( _( "Editing symbol %s from schematic.  Saving will update the schematic "
726                        "only." ), m_reference );
727 
728         infobar->RemoveAllButtons();
729         infobar->ShowMessage( msg, wxICON_INFORMATION );
730     }
731     else if( IsSymbolFromLegacyLibrary() )
732     {
733         wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY,
734                                                        _( "Manage symbol libraries" ),
735                                                        wxEmptyString );
736 
737         button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>(
738                 [&]( wxHyperlinkEvent& aEvent )
739                 {
740                     InvokeSchEditSymbolLibTable( &Kiway(), this );
741                 } ) );
742 
743         infobar->RemoveAllButtons();
744         infobar->AddButton( button );
745         infobar->ShowMessage( _( "Symbols in legacy libraries are not editable.  Use Manage "
746                                  "Symbol Libraries to migrate to current format." ),
747                               wxICON_INFORMATION );
748     }
749     else if( IsSymbolAlias() )
750     {
751         wxString parentSymbolName = m_symbol->GetParent().lock()->GetName();
752         wxString msg;
753         wxString link;
754 
755         msg.Printf( _( "Symbol %s is derived from %s.  Symbol graphics will not be editable." ),
756                     UnescapeString( symbolName ),
757                     UnescapeString( parentSymbolName ) );
758 
759         link.Printf( _( "Open %s" ), UnescapeString( parentSymbolName ) );
760 
761         wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, link, wxEmptyString );
762         button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>(
763                 [&]( wxHyperlinkEvent& aEvent )
764                 {
765                     LoadSymbolFromCurrentLib( m_symbol->GetParent().lock()->GetName(),
766                                               GetUnit(), GetConvert() );
767                 } ) );
768 
769         infobar->RemoveAllButtons();
770         infobar->AddButton( button );
771         infobar->ShowMessage( msg, wxICON_INFORMATION );
772     }
773     else
774     {
775         infobar->Dismiss();
776     }
777 }
778 
779 
GetLibManager()780 SYMBOL_LIBRARY_MANAGER& SYMBOL_EDIT_FRAME::GetLibManager()
781 {
782     wxASSERT( m_libMgr );
783     return *m_libMgr;
784 }
785 
786 
OnModify()787 void SYMBOL_EDIT_FRAME::OnModify()
788 {
789     GetScreen()->SetContentModified();
790     storeCurrentSymbol();
791 
792     m_treePane->GetLibTree()->RefreshLibTree();
793 
794     if( !GetTitle().StartsWith( "*" ) )
795         updateTitle();
796 }
797 
798 
SynchronizePins()799 bool SYMBOL_EDIT_FRAME::SynchronizePins()
800 {
801     return m_SyncPinEdit && m_symbol && m_symbol->IsMulti() && !m_symbol->UnitsLocked();
802 }
803 
804 
AddLibraryFile(bool aCreateNew)805 bool SYMBOL_EDIT_FRAME::AddLibraryFile( bool aCreateNew )
806 {
807     // Select the target library table (global/project)
808     SYMBOL_LIB_TABLE* libTable = selectSymLibTable();
809 
810     if( !libTable )
811         return false;
812 
813     wxFileName fn = m_libMgr->GetUniqueLibraryName();
814 
815     if( !LibraryFileBrowser( !aCreateNew, fn, KiCadSymbolLibFileWildcard(),
816                              KiCadSymbolLibFileExtension, false,
817                              ( libTable == &SYMBOL_LIB_TABLE::GetGlobalLibTable() ),
818                              PATHS::GetDefaultUserSymbolsPath() ) )
819     {
820         return false;
821     }
822 
823     wxString libName = fn.GetName();
824 
825     if( libName.IsEmpty() )
826         return false;
827 
828     if( m_libMgr->LibraryExists( libName ) )
829     {
830         DisplayError( this, wxString::Format( _( "Library '%s' already exists." ), libName ) );
831         return false;
832     }
833 
834     if( aCreateNew )
835     {
836         if( !m_libMgr->CreateLibrary( fn.GetFullPath(), libTable ) )
837         {
838             DisplayError( this, wxString::Format( _( "Could not create the library file '%s'.\n"
839                                                      "Make sure you have write permissions and try again." ),
840                                                   fn.GetFullPath() ) );
841             return false;
842         }
843     }
844     else
845     {
846         if( !m_libMgr->AddLibrary( fn.GetFullPath(), libTable ) )
847         {
848             DisplayError( this, _( "Could not open the library file." ) );
849             return false;
850         }
851     }
852 
853     bool globalTable = ( libTable == &SYMBOL_LIB_TABLE::GetGlobalLibTable() );
854     saveSymbolLibTables( globalTable, !globalTable );
855 
856     std::string packet = fn.GetFullPath().ToStdString();
857     this->Kiway().ExpressMail( FRAME_SCH_SYMBOL_EDITOR, MAIL_LIB_EDIT, packet );
858 
859     return true;
860 }
861 
862 
GetTreeLIBID(int * aUnit) const863 LIB_ID SYMBOL_EDIT_FRAME::GetTreeLIBID( int* aUnit ) const
864 {
865     return m_treePane->GetLibTree()->GetSelectedLibId( aUnit );
866 }
867 
868 
getTargetSymbol() const869 LIB_SYMBOL* SYMBOL_EDIT_FRAME::getTargetSymbol() const
870 {
871     LIB_ID libId = GetTreeLIBID();
872 
873     if( libId.IsValid() )
874     {
875         LIB_SYMBOL* alias = m_libMgr->GetAlias( libId.GetLibItemName(), libId.GetLibNickname() );
876         return alias;
877     }
878 
879     return m_symbol;
880 }
881 
882 
GetTargetLibId() const883 LIB_ID SYMBOL_EDIT_FRAME::GetTargetLibId() const
884 {
885     LIB_ID id;
886 
887     if( IsSymbolTreeShown() )
888         id = GetTreeLIBID();
889 
890     if( id.GetLibNickname().empty() && m_symbol )
891         id = m_symbol->GetLibId();
892 
893     return id;
894 }
895 
896 
GetCurrentTreeNode() const897 LIB_TREE_NODE* SYMBOL_EDIT_FRAME::GetCurrentTreeNode() const
898 {
899     return m_treePane->GetLibTree()->GetCurrentTreeNode();
900 }
901 
902 
getTargetLib() const903 wxString SYMBOL_EDIT_FRAME::getTargetLib() const
904 {
905     return GetTargetLibId().GetLibNickname();
906 }
907 
908 
SyncLibraries(bool aShowProgress,const wxString & aForceRefresh)909 void SYMBOL_EDIT_FRAME::SyncLibraries( bool aShowProgress, const wxString& aForceRefresh )
910 {
911     LIB_ID selected;
912 
913     if( m_treePane )
914         selected = m_treePane->GetLibTree()->GetSelectedLibId();
915 
916     if( aShowProgress )
917     {
918         APP_PROGRESS_DIALOG progressDlg( _( "Loading Symbol Libraries" ), wxEmptyString,
919                                          m_libMgr->GetAdapter()->GetLibrariesCount(), this );
920 
921         m_libMgr->Sync( aForceRefresh,
922                 [&]( int progress, int max, const wxString& libName )
923                 {
924                     progressDlg.Update( progress, wxString::Format( _( "Loading library '%s'..." ),
925                                                                     libName ) );
926                 } );
927     }
928     else
929     {
930         m_libMgr->Sync( aForceRefresh,
931                 [&]( int progress, int max, const wxString& libName )
932                 {
933                 } );
934     }
935 
936     if( m_treePane )
937     {
938         wxDataViewItem found;
939 
940         if( selected.IsValid() )
941         {
942             // Check if the previously selected item is still valid,
943             // if not - it has to be unselected to prevent crash
944             found = m_libMgr->GetAdapter()->FindItem( selected );
945 
946             if( !found )
947                 m_treePane->GetLibTree()->Unselect();
948         }
949 
950         m_treePane->GetLibTree()->Regenerate( true );
951 
952         // Try to select the parent library, in case the symbol is not found
953         if( !found && selected.IsValid() )
954         {
955             selected.SetLibItemName( "" );
956             found = m_libMgr->GetAdapter()->FindItem( selected );
957 
958             if( found )
959                 m_treePane->GetLibTree()->SelectLibId( selected );
960         }
961 
962         // If no selection, see if there's a current symbol to centre
963         if( !selected.IsValid() && m_symbol )
964         {
965             LIB_ID current( GetCurLib(), m_symbol->GetName() );
966             m_treePane->GetLibTree()->CenterLibId( current );
967         }
968     }
969 }
970 
971 
RegenerateLibraryTree()972 void SYMBOL_EDIT_FRAME::RegenerateLibraryTree()
973 {
974     LIB_ID target = GetTargetLibId();
975 
976     m_treePane->GetLibTree()->Regenerate( true );
977 
978     if( target.IsValid() )
979         m_treePane->GetLibTree()->CenterLibId( target );
980 }
981 
982 
RefreshLibraryTree()983 void SYMBOL_EDIT_FRAME::RefreshLibraryTree()
984 {
985     m_treePane->GetLibTree()->RefreshLibTree();
986 }
987 
988 
selectSymLibTable(bool aOptional)989 SYMBOL_LIB_TABLE* SYMBOL_EDIT_FRAME::selectSymLibTable( bool aOptional )
990 {
991     // If no project is loaded, always work with the global table
992     if( Prj().IsNullProject() )
993     {
994         SYMBOL_LIB_TABLE* ret = &SYMBOL_LIB_TABLE::GetGlobalLibTable();
995 
996         if( aOptional )
997         {
998             wxMessageDialog dlg( this, _( "Add the library to the global library table?" ),
999                                  _( "Add To Global Library Table" ), wxYES_NO );
1000 
1001             if( dlg.ShowModal() != wxID_OK )
1002                 ret = nullptr;
1003         }
1004 
1005         return ret;
1006     }
1007 
1008     wxArrayString libTableNames;
1009     libTableNames.Add( _( "Global" ) );
1010     libTableNames.Add( _( "Project" ) );
1011 
1012     wxSingleChoiceDialog dlg( this, _( "Choose the Library Table to add the library to:" ),
1013                               _( "Add To Library Table" ), libTableNames );
1014 
1015     if( aOptional )
1016     {
1017         dlg.FindWindow( wxID_CANCEL )->SetLabel( _( "Skip" ) );
1018         dlg.FindWindow( wxID_OK )->SetLabel( _( "Add" ) );
1019     }
1020 
1021     if( dlg.ShowModal() != wxID_OK )
1022         return nullptr;
1023 
1024     switch( dlg.GetSelection() )
1025     {
1026     case 0:  return &SYMBOL_LIB_TABLE::GetGlobalLibTable();
1027     case 1:  return Prj().SchSymbolLibTable();
1028     default: return nullptr;
1029     }
1030 }
1031 
1032 
backupFile(const wxFileName & aOriginalFile,const wxString & aBackupExt)1033 bool SYMBOL_EDIT_FRAME::backupFile( const wxFileName& aOriginalFile, const wxString& aBackupExt )
1034 {
1035     if( aOriginalFile.FileExists() )
1036     {
1037         wxFileName backupFileName( aOriginalFile );
1038         backupFileName.SetExt( aBackupExt );
1039 
1040         if( backupFileName.FileExists() )
1041             wxRemoveFile( backupFileName.GetFullPath() );
1042 
1043         if( !wxCopyFile( aOriginalFile.GetFullPath(), backupFileName.GetFullPath() ) )
1044         {
1045             DisplayError( this, wxString::Format( _( "Failed to save backup to '%s'." ),
1046                                                   backupFileName.GetFullPath() ) );
1047             return false;
1048         }
1049     }
1050 
1051     return true;
1052 }
1053 
1054 
storeCurrentSymbol()1055 void SYMBOL_EDIT_FRAME::storeCurrentSymbol()
1056 {
1057     if( m_symbol && !GetCurLib().IsEmpty() && GetScreen()->IsContentModified() )
1058         m_libMgr->UpdateSymbol( m_symbol, GetCurLib() ); // UpdateSymbol() makes a copy
1059 }
1060 
1061 
isCurrentSymbol(const LIB_ID & aLibId) const1062 bool SYMBOL_EDIT_FRAME::isCurrentSymbol( const LIB_ID& aLibId ) const
1063 {
1064     // This will return the root symbol of any alias
1065     LIB_SYMBOL* symbol = m_libMgr->GetBufferedSymbol( aLibId.GetLibItemName(),
1066                                                   aLibId.GetLibNickname() );
1067 
1068     // Now we can compare the libId of the current symbol and the root symbol
1069     return ( symbol && m_symbol && symbol->GetLibId() == m_symbol->GetLibId() );
1070 }
1071 
1072 
emptyScreen()1073 void SYMBOL_EDIT_FRAME::emptyScreen()
1074 {
1075     m_treePane->GetLibTree()->Unselect();
1076     SetCurLib( wxEmptyString );
1077     SetCurSymbol( nullptr, false );
1078     SetScreen( m_dummyScreen );
1079     ClearUndoRedoList();
1080     m_toolManager->RunAction( ACTIONS::zoomFitScreen, true );
1081     Refresh();
1082 }
1083 
1084 
CommonSettingsChanged(bool aEnvVarsChanged,bool aTextVarsChanged)1085 void SYMBOL_EDIT_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
1086 {
1087     SCH_BASE_FRAME::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
1088 
1089     GetCanvas()->GetGAL()->SetAxesColor( m_colorSettings->GetColor( LAYER_SCHEMATIC_GRID_AXES ) );
1090     GetCanvas()->GetGAL()->DrawGrid();
1091 
1092     RecreateToolbars();
1093 
1094     if( aEnvVarsChanged )
1095         SyncLibraries( true );
1096 
1097     Layout();
1098     SendSizeEvent();
1099 }
1100 
1101 
ShowChangedLanguage()1102 void SYMBOL_EDIT_FRAME::ShowChangedLanguage()
1103 {
1104     // call my base class
1105     SCH_BASE_FRAME::ShowChangedLanguage();
1106 
1107     // tooltips in toolbars
1108     RecreateToolbars();
1109 
1110     // status bar
1111     UpdateMsgPanel();
1112 }
1113 
1114 
SetScreen(BASE_SCREEN * aScreen)1115 void SYMBOL_EDIT_FRAME::SetScreen( BASE_SCREEN* aScreen )
1116 {
1117     SCH_BASE_FRAME::SetScreen( aScreen );
1118 }
1119 
1120 
RebuildView()1121 void SYMBOL_EDIT_FRAME::RebuildView()
1122 {
1123     GetRenderSettings()->m_ShowUnit = m_unit;
1124     GetRenderSettings()->m_ShowConvert = m_convert;
1125     GetRenderSettings()->m_ShowDisabled = IsSymbolFromLegacyLibrary() && !IsSymbolFromSchematic();
1126     GetRenderSettings()->m_ShowGraphicsDisabled = IsSymbolAlias() && !IsSymbolFromSchematic();
1127     GetCanvas()->DisplaySymbol( m_symbol );
1128     GetCanvas()->GetView()->HideDrawingSheet();
1129     GetCanvas()->GetView()->ClearHiddenFlags();
1130 
1131     GetCanvas()->Refresh();
1132 }
1133 
1134 
HardRedraw()1135 void SYMBOL_EDIT_FRAME::HardRedraw()
1136 {
1137     SyncLibraries( true );
1138 
1139     if( m_symbol )
1140     {
1141         EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
1142         EE_SELECTION&      selection = selectionTool->GetSelection();
1143 
1144         for( LIB_ITEM& item : m_symbol->GetDrawItems() )
1145         {
1146             if( !alg::contains( selection, &item ) )
1147                 item.ClearSelected();
1148             else
1149                 item.SetSelected();
1150         }
1151     }
1152 
1153     RebuildView();
1154 }
1155 
1156 
GetDocumentExtents(bool aIncludeAllVisible) const1157 const BOX2I SYMBOL_EDIT_FRAME::GetDocumentExtents( bool aIncludeAllVisible ) const
1158 {
1159     if( !m_symbol )
1160     {
1161         return BOX2I( VECTOR2I( Mils2iu( -100 ), Mils2iu( -100 ) ),
1162                       VECTOR2I( Mils2iu( 200 ), Mils2iu( 200 ) ) );
1163     }
1164     else
1165     {
1166         EDA_RECT boundingBox = m_symbol->Flatten()->GetUnitBoundingBox( m_unit, m_convert );
1167         return BOX2I( boundingBox.GetOrigin(), VECTOR2I( boundingBox.GetWidth(),
1168                                                          boundingBox.GetHeight() ) );
1169     }
1170 }
1171 
1172 
KiwayMailIn(KIWAY_EXPRESS & mail)1173 void SYMBOL_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
1174 {
1175     const std::string& payload = mail.GetPayload();
1176 
1177     switch( mail.Command() )
1178     {
1179     case MAIL_LIB_EDIT:
1180         if( !payload.empty() )
1181         {
1182             wxString libFileName( payload );
1183             wxString libNickname;
1184             wxString msg;
1185 
1186             SYMBOL_LIB_TABLE*    libTable = Prj().SchSymbolLibTable();
1187             const LIB_TABLE_ROW* libTableRow = libTable->FindRowByURI( libFileName );
1188 
1189             if( !libTableRow )
1190             {
1191                 msg.Printf( _( "The current configuration does not include the library '%s'.\n"
1192                                "Use Manage Symbol Libraries to edit the configuration." ),
1193                             libFileName );
1194                 DisplayErrorMessage( this, _( "Library not found in symbol library table." ), msg );
1195                 break;
1196             }
1197 
1198             libNickname = libTableRow->GetNickName();
1199 
1200             if( !libTable->HasLibrary( libNickname, true ) )
1201             {
1202                 msg.Printf( _( "The library '%s' is not enabled in the current configuration.\n"
1203                                "Use Manage Symbol Libraries to edit the configuration." ),
1204                             UnescapeString( libNickname ) );
1205                 DisplayErrorMessage( this, _( "Symbol library not enabled." ), msg );
1206                 break;
1207             }
1208 
1209             SetCurLib( libNickname );
1210 
1211             if( m_treePane )
1212             {
1213                 LIB_ID id( libNickname, wxEmptyString );
1214                 m_treePane->GetLibTree()->SelectLibId( id );
1215                 m_treePane->GetLibTree()->ExpandLibId( id );
1216                 m_treePane->GetLibTree()->CenterLibId( id );
1217             }
1218         }
1219 
1220         break;
1221 
1222     default:
1223         ;
1224     }
1225 }
1226 
1227 
SwitchCanvas(EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType)1228 void SYMBOL_EDIT_FRAME::SwitchCanvas( EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType )
1229 {
1230     // switches currently used canvas ( Cairo / OpenGL):
1231     SCH_BASE_FRAME::SwitchCanvas( aCanvasType );
1232 
1233     // Set options specific to symbol editor (axies are always enabled):
1234     GetCanvas()->GetGAL()->SetAxesEnabled( true );
1235     GetCanvas()->GetGAL()->SetAxesColor( m_colorSettings->GetColor( LAYER_SCHEMATIC_GRID_AXES ) );
1236 }
1237 
1238 
HasLibModifications() const1239 bool SYMBOL_EDIT_FRAME::HasLibModifications() const
1240 {
1241     wxCHECK( m_libMgr, false );
1242 
1243     return m_libMgr->HasModifications();
1244 }
1245 
1246 
IsContentModified() const1247 bool SYMBOL_EDIT_FRAME::IsContentModified() const
1248 {
1249     wxCHECK( m_libMgr, false );
1250 
1251     // Test if the currently edited symbol is modified
1252     if( GetScreen() && GetScreen()->IsContentModified() && GetCurSymbol() )
1253         return true;
1254 
1255     // Test if any library has been modified
1256     for( const wxString& libName : m_libMgr->GetLibraryNames() )
1257     {
1258         if( m_libMgr->IsLibraryModified( libName ) && !m_libMgr->IsLibraryReadOnly( libName ) )
1259             return true;
1260     }
1261 
1262     return false;
1263 }
1264 
1265 
ClearUndoORRedoList(UNDO_REDO_LIST whichList,int aItemCount)1266 void SYMBOL_EDIT_FRAME::ClearUndoORRedoList( UNDO_REDO_LIST whichList, int aItemCount )
1267 {
1268     if( aItemCount == 0 )
1269         return;
1270 
1271     UNDO_REDO_CONTAINER& list = whichList == UNDO_LIST ? m_undoList : m_redoList;
1272 
1273     for( PICKED_ITEMS_LIST* command : list.m_CommandsList )
1274     {
1275         command->ClearListAndDeleteItems();
1276         delete command;
1277     }
1278 
1279     list.m_CommandsList.clear();
1280 }
1281 
1282 
GetCurrentSelection()1283 SELECTION& SYMBOL_EDIT_FRAME::GetCurrentSelection()
1284 {
1285     return m_toolManager->GetTool<EE_SELECTION_TOOL>()->GetSelection();
1286 }
1287 
1288 
LoadSymbolFromSchematic(SCH_SYMBOL * aSymbol)1289 void SYMBOL_EDIT_FRAME::LoadSymbolFromSchematic( SCH_SYMBOL* aSymbol )
1290 {
1291     std::unique_ptr<LIB_SYMBOL> symbol = aSymbol->GetLibSymbolRef()->Flatten();
1292     wxCHECK( symbol, /* void */ );
1293 
1294     std::vector<LIB_FIELD> fullSetOfFields;
1295 
1296     for( int i = 0; i < (int) aSymbol->GetFields().size(); ++i )
1297     {
1298         const SCH_FIELD& field = aSymbol->GetFields()[i];
1299         wxPoint          pos = field.GetPosition() - aSymbol->GetPosition();
1300         LIB_FIELD        libField( symbol.get(), field.GetId() );
1301 
1302         if( i >= MANDATORY_FIELDS && !field.GetName( false ).IsEmpty() )
1303             libField.SetName( field.GetName( false ) );
1304 
1305         libField.SetText( field.GetText() );
1306         libField.SetEffects( field );
1307         libField.SetPosition( wxPoint( pos.x, -pos.y ) );
1308 
1309         fullSetOfFields.emplace_back( std::move( libField ) );
1310     }
1311 
1312     symbol->SetFields( fullSetOfFields );
1313 
1314     if( m_symbol )
1315         SetCurSymbol( nullptr, false );
1316 
1317     m_isSymbolFromSchematic = true;
1318     m_schematicSymbolUUID = aSymbol->m_Uuid;
1319     m_reference = symbol->GetFieldById( REFERENCE_FIELD )->GetText();
1320     m_unit = std::max( 1, aSymbol->GetUnit() );
1321     m_convert = std::max( 1, aSymbol->GetConvert() );
1322 
1323     // The buffered screen for the symbol
1324     SCH_SCREEN* tmpScreen = new SCH_SCREEN();
1325 
1326     SetScreen( tmpScreen );
1327     SetCurSymbol( symbol.release(), true );
1328 
1329     ReCreateMenuBar();
1330     ReCreateHToolbar();
1331 
1332     if( IsSymbolTreeShown() )
1333     {
1334         wxCommandEvent evt;
1335         OnToggleSymbolTree( evt );
1336     }
1337 
1338     updateTitle();
1339     RebuildSymbolUnitsList();
1340     SetShowDeMorgan( GetCurSymbol()->HasConversion() );
1341     UpdateSymbolMsgPanelInfo();
1342     Refresh();
1343 }
1344 
1345 
addLibTableEntry(const wxString & aLibFile,TABLE_SCOPE aScope)1346 bool SYMBOL_EDIT_FRAME::addLibTableEntry( const wxString& aLibFile, TABLE_SCOPE aScope )
1347 {
1348     wxFileName fn = aLibFile;
1349     wxFileName libTableFileName( Prj().GetProjectPath(),
1350                                  SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
1351     wxString libNickname = fn.GetName();
1352     SYMBOL_LIB_TABLE* libTable = Prj().SchSymbolLibTable();
1353     const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
1354 
1355     if( libTable->HasLibrary( libNickname ) )
1356     {
1357         wxString tmp;
1358         int suffix = 1;
1359 
1360         while( libTable->HasLibrary( libNickname ) )
1361         {
1362             tmp.Printf( "%s%d", fn.GetName(), suffix );
1363             libNickname = tmp;
1364             suffix += 1;
1365         }
1366     }
1367 
1368     SYMBOL_LIB_TABLE_ROW* row = new SYMBOL_LIB_TABLE_ROW();
1369     row->SetNickName( libNickname );
1370 
1371     wxString normalizedPath = NormalizePath( aLibFile, &envVars, Prj().GetProjectPath() );
1372 
1373     if( aScope == GLOBAL_LIB_TABLE )
1374     {
1375         libTable = &SYMBOL_LIB_TABLE::GetGlobalLibTable();
1376         libTableFileName = SYMBOL_LIB_TABLE::GetGlobalTableFileName();
1377 
1378         // We cannot normalize against the current project path when saving to global table.
1379         normalizedPath = NormalizePath( aLibFile, &envVars, wxEmptyString );
1380     }
1381 
1382     row->SetFullURI( normalizedPath );
1383 
1384     wxCHECK( libTable->InsertRow( row ), false );
1385 
1386     try
1387     {
1388         libTable->Save( libTableFileName.GetFullPath() );
1389     }
1390     catch( const IO_ERROR& ioe )
1391     {
1392         wxString msg = aScope == GLOBAL_LIB_TABLE ? _( "Error saving global library table." )
1393                                                   : _( "Error saving project library table." );
1394 
1395         wxMessageDialog dlg( this, msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
1396         dlg.SetExtendedMessage( ioe.What() );
1397         dlg.ShowModal();
1398 
1399         return false;
1400     }
1401 
1402     return true;
1403 }
1404 
1405 
replaceLibTableEntry(const wxString & aLibNickname,const wxString & aLibFile)1406 bool SYMBOL_EDIT_FRAME::replaceLibTableEntry( const wxString& aLibNickname,
1407                                               const wxString& aLibFile )
1408 {
1409     // Check the global library table first because checking the project library table
1410     // checks the global library table as well due to library chaining.
1411     bool isGlobalTable = true;
1412     wxFileName libTableFileName = SYMBOL_LIB_TABLE::GetGlobalTableFileName();;
1413     const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
1414     SYMBOL_LIB_TABLE* libTable = &SYMBOL_LIB_TABLE::GetGlobalLibTable();
1415     SYMBOL_LIB_TABLE_ROW* row = libTable->FindRow( aLibNickname );
1416 
1417     if( !row )
1418     {
1419         libTableFileName.SetPath( Prj().GetProjectPath() );
1420         libTableFileName.SetName( SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
1421         libTable = Prj().SchSymbolLibTable();
1422         isGlobalTable = false;
1423         row = libTable->FindRow( aLibNickname );
1424     }
1425 
1426     wxCHECK( row, false );
1427 
1428     wxString projectPath;
1429 
1430     if( !isGlobalTable )
1431         projectPath = Prj().GetProjectPath();
1432 
1433     wxString normalizedPath = NormalizePath( aLibFile, &envVars, projectPath );
1434 
1435     row->SetFullURI( normalizedPath );
1436     row->SetType( "KiCad" );
1437 
1438     try
1439     {
1440         libTable->Save( libTableFileName.GetFullPath() );
1441     }
1442     catch( const IO_ERROR& ioe )
1443     {
1444         wxString msg = isGlobalTable ? _( "Error saving global library table." )
1445                                      : _( "Error saving project library table." );
1446 
1447         wxMessageDialog dlg( this, msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
1448         dlg.SetExtendedMessage( ioe.What() );
1449         dlg.ShowModal();
1450 
1451         return false;
1452     }
1453 
1454     return true;
1455 }
1456 
1457 
IsSymbolAlias() const1458 bool SYMBOL_EDIT_FRAME::IsSymbolAlias() const
1459 {
1460     return m_symbol && !m_symbol->IsRoot();
1461 }
1462 
1463 
IsSymbolEditable() const1464 bool SYMBOL_EDIT_FRAME::IsSymbolEditable() const
1465 {
1466     return m_symbol && ( !IsSymbolFromLegacyLibrary() || IsSymbolFromSchematic() );
1467 }
1468