1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "symbol_preview_widget.h"
21 #include <sch_view.h>
22 #include <gal/gal_display_options.h>
23 #include <symbol_lib_table.h>
24 #include <sch_preview_panel.h>
25 #include <pgm_base.h>
26 #include <sch_painter.h>
27 #include <eda_draw_frame.h>
28 #include <eeschema_settings.h>
29 #include <settings/settings_manager.h>
30 #include <wx/log.h>
31 #include <wx/stattext.h>
32
33
SYMBOL_PREVIEW_WIDGET(wxWindow * aParent,KIWAY & aKiway,EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType)34 SYMBOL_PREVIEW_WIDGET::SYMBOL_PREVIEW_WIDGET( wxWindow* aParent, KIWAY& aKiway,
35 EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType ) :
36 wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ),
37 m_kiway( aKiway ),
38 m_preview( nullptr ), m_status( nullptr ), m_statusSizer( nullptr ), m_previewItem( nullptr )
39 {
40 auto common_settings = Pgm().GetCommonSettings();
41 auto app_settings = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
42
43 m_galDisplayOptions.ReadConfig( *common_settings, app_settings->m_Window, this );
44 m_galDisplayOptions.m_forceDisplayCursor = false;
45
46 EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = aCanvasType;
47
48 // Allows only a CAIRO or OPENGL canvas:
49 if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL
50 && canvasType != EDA_DRAW_PANEL_GAL::GAL_FALLBACK )
51 {
52 canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
53 }
54
55 m_preview = new SCH_PREVIEW_PANEL( aParent, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ),
56 m_galDisplayOptions, canvasType );
57 m_preview->SetStealsFocus( false );
58 m_preview->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER );
59 m_preview->GetGAL()->SetAxesEnabled( false );
60
61 // Do not display the grid: the look is not good for a small canvas area.
62 // But mainly, due to some strange bug I (JPC) was unable to fix, the grid creates
63 // strange artifacts on Windows when Eeschema is run from KiCad manager (but not in
64 // stand alone...).
65 m_preview->GetGAL()->SetGridVisibility( false );
66
67 // Early initialization of the canvas background color,
68 // before any OnPaint event is fired for the canvas using a wrong bg color
69 KIGFX::VIEW* view = m_preview->GetView();
70 auto settings = static_cast<KIGFX::SCH_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
71
72 if( auto* theme = Pgm().GetSettingsManager().GetColorSettings( app_settings->m_ColorTheme ) )
73 settings->LoadColors( theme );
74
75 const COLOR4D& backgroundColor = settings->GetBackgroundColor();
76 const COLOR4D& foregroundColor = settings->GetCursorColor();
77
78 m_preview->GetGAL()->SetClearColor( backgroundColor );
79
80 m_statusPanel = new wxPanel( this );
81 m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
82 m_status = new wxStaticText( m_statusPanel, wxID_ANY, wxEmptyString );
83 m_status->SetForegroundColour( settings->GetLayerColor( LAYER_REFERENCEPART ).ToColour() );
84 m_statusSizer = new wxBoxSizer( wxVERTICAL );
85 m_statusSizer->Add( 0, 0, 1 ); // add a spacer
86 m_statusSizer->Add( m_status, 0, wxALIGN_CENTER );
87 m_statusSizer->Add( 0, 0, 1 ); // add a spacer
88 m_statusPanel->SetSizer( m_statusSizer );
89
90 // Give the status panel the same color scheme as the canvas so it isn't jarring when
91 // switched to.
92 m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
93 m_statusPanel->SetForegroundColour( foregroundColor.ToColour() );
94
95 // Give the preview panel a small top border to align its top with the status panel,
96 // and give the status panel a small bottom border to align its bottom with the preview
97 // panel.
98 m_outerSizer = new wxBoxSizer( wxVERTICAL );
99 m_outerSizer->Add( m_preview, 1, wxTOP | wxEXPAND, 5 );
100 m_outerSizer->Add( m_statusPanel, 1, wxBOTTOM | wxEXPAND, 5 );
101
102 // Hide the status panel to start
103 m_statusPanel->Hide();
104
105 SetSizer( m_outerSizer );
106 Layout();
107
108 Connect( wxEVT_SIZE, wxSizeEventHandler( SYMBOL_PREVIEW_WIDGET::onSize ), nullptr, this );
109 }
110
111
~SYMBOL_PREVIEW_WIDGET()112 SYMBOL_PREVIEW_WIDGET::~SYMBOL_PREVIEW_WIDGET()
113 {
114 if( m_previewItem )
115 m_preview->GetView()->Remove( m_previewItem );
116
117 delete m_previewItem;
118 }
119
120
SetStatusText(wxString const & aText)121 void SYMBOL_PREVIEW_WIDGET::SetStatusText( wxString const& aText )
122 {
123 m_status->SetLabel( aText );
124 m_preview->Hide();
125 m_statusPanel->Show();
126 Layout();
127 }
128
129
onSize(wxSizeEvent & aEvent)130 void SYMBOL_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent )
131 {
132 if( m_previewItem )
133 {
134 fitOnDrawArea();
135 m_preview->ForceRefresh();
136 }
137
138 aEvent.Skip();
139 }
140
141
fitOnDrawArea()142 void SYMBOL_PREVIEW_WIDGET::fitOnDrawArea()
143 {
144 if( !m_previewItem )
145 return;
146
147 // set the view scale to fit the item on screen
148 KIGFX::VIEW* view = m_preview->GetView();
149
150 // Calculate the drawing area size, in internal units, for a scaling factor = 1.0
151 view->SetScale( 1.0 );
152 VECTOR2D clientSize = view->ToWorld( m_preview->GetClientSize(), false );
153 // Calculate the draw scale to fit the drawing area
154 double scale = std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ),
155 fabs( clientSize.y / m_itemBBox.GetHeight() ) );
156
157 // Above calculation will yield an exact fit; add a bit of whitespace around symbol
158 scale /= 1.2;
159
160 // Now fix the best scale
161 view->SetScale( scale );
162 view->SetCenter( m_itemBBox.Centre() );
163 }
164
165
DisplaySymbol(const LIB_ID & aSymbolID,int aUnit,int aConvert)166 void SYMBOL_PREVIEW_WIDGET::DisplaySymbol( const LIB_ID& aSymbolID, int aUnit, int aConvert )
167 {
168 KIGFX::VIEW* view = m_preview->GetView();
169 auto settings = static_cast<KIGFX::SCH_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
170 std::unique_ptr< LIB_SYMBOL > symbol;
171
172 try
173 {
174 LIB_SYMBOL* tmp = m_kiway.Prj().SchSymbolLibTable()->LoadSymbol( aSymbolID );
175
176 if( tmp )
177 symbol = tmp->Flatten();
178 }
179 catch( const IO_ERROR& ioe )
180 {
181 wxLogError( _( "Error loading symbol %s from library '%s'." ) + wxS( "\n%s" ),
182 aSymbolID.GetLibItemName().wx_str(),
183 aSymbolID.GetLibNickname().wx_str(),
184 ioe.What() );
185 }
186
187 if( m_previewItem )
188 {
189 view->Remove( m_previewItem );
190 delete m_previewItem;
191 m_previewItem = nullptr;
192 }
193
194 if( symbol )
195 {
196 // This will flatten derived parts so that the correct final symbol can be shown.
197 m_previewItem = symbol.release();
198
199 // If unit isn't specified for a multi-unit part, pick the first. (Otherwise we'll
200 // draw all of them.)
201 settings->m_ShowUnit = ( m_previewItem->IsMulti() && aUnit == 0 ) ? 1 : aUnit;
202
203 // For symbols having a De Morgan body style, use the first style
204 settings->m_ShowConvert =
205 ( m_previewItem->HasConversion() && aConvert == 0 ) ? 1 : aConvert;
206
207 view->Add( m_previewItem );
208
209 // Get the symbol size, in internal units
210 m_itemBBox = m_previewItem->GetUnitBoundingBox( settings->m_ShowUnit,
211 settings->m_ShowConvert );
212
213 if( !m_preview->IsShown() )
214 {
215 m_preview->Show();
216 m_statusPanel->Hide();
217 Layout(); // Ensure panel size is up to date.
218 }
219
220 // Calculate the draw scale to fit the drawing area
221 fitOnDrawArea();
222 }
223
224 m_preview->ForceRefresh();
225 }
226
227
DisplayPart(LIB_SYMBOL * aSymbol,int aUnit,int aConvert)228 void SYMBOL_PREVIEW_WIDGET::DisplayPart( LIB_SYMBOL* aSymbol, int aUnit, int aConvert )
229 {
230 KIGFX::VIEW* view = m_preview->GetView();
231
232 if( m_previewItem )
233 {
234 view->Remove( m_previewItem );
235 delete m_previewItem;
236 m_previewItem = nullptr;
237 }
238
239 if( aSymbol )
240 {
241 m_previewItem = new LIB_SYMBOL( *aSymbol );
242
243 // For symbols having a De Morgan body style, use the first style
244 auto settings =
245 static_cast<KIGFX::SCH_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
246
247 // If unit isn't specified for a multi-unit part, pick the first. (Otherwise we'll
248 // draw all of them.)
249 settings->m_ShowUnit = ( m_previewItem->IsMulti() && aUnit == 0 ) ? 1 : aUnit;
250
251 settings->m_ShowConvert =
252 ( m_previewItem->HasConversion() && aConvert == 0 ) ? 1 : aConvert;
253
254 view->Add( m_previewItem );
255
256 // Get the symbol size, in internal units
257 m_itemBBox = aSymbol->GetUnitBoundingBox( settings->m_ShowUnit, settings->m_ShowConvert );
258
259 // Calculate the draw scale to fit the drawing area
260 fitOnDrawArea();
261 }
262
263 m_preview->ForceRefresh();
264 m_preview->Show();
265 m_statusPanel->Hide();
266 Layout();
267 }
268