KiCad PCB EDA Suite
footprint_preview_panel.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) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors.
5  * Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com>
6  * Copyright (C) 2016 Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
7  *
8  * This program is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation, either version 3 of the License, or (at your
11  * option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <memory>
23 #include <mutex>
24 #include <utility>
25 
26 #include "pcbnew_settings.h"
27 #include <class_board.h>
28 #include <class_module.h>
29 #include <eda_draw_frame.h>
31 #include <fp_lib_table.h>
32 #include <id.h>
33 #include <io_mgr.h>
34 #include <kiway.h>
35 #include <math/box2.h>
36 #include <pcb_painter.h>
37 #include <pcb_draw_panel_gal.h>
38 #include <pcb_edit_frame.h>
39 #include <pgm_base.h>
41 #include <view/view.h>
42 #include <wx/stattext.h>
43 
48 {
50 
51  public:
52  FP_THREAD_IFACE() : m_panel( nullptr )
53  {
54  }
55 
58  {
59  std::lock_guard<std::mutex> lock( m_lock );
60  auto it = m_cachedFootprints.find( aFPID );
61 
62  if( it != m_cachedFootprints.end() )
63  return it->second;
64  else
65  return NULLOPT;
66  }
67 
72  CACHE_ENTRY AddToQueue( const LIB_ID& aEntry )
73  {
74  std::lock_guard<std::mutex> lock( m_lock );
75 
76  CACHE_ENTRY ent = { aEntry, nullptr, FPS_LOADING };
77  m_cachedFootprints[aEntry] = ent;
78  m_loaderQueue.push_back( ent );
79 
80  return ent;
81  }
82 
85  {
86  std::lock_guard<std::mutex> lock( m_lock );
87 
88  if( m_loaderQueue.empty() )
89  {
90  return NULLOPT;
91  }
92  else
93  {
94  auto ent = m_loaderQueue.front();
95  m_loaderQueue.pop_front();
96  return ent;
97  }
98  }
99 
101  void AddToCache( const CACHE_ENTRY& aEntry )
102  {
103  std::lock_guard<std::mutex> lock( m_lock );
104  m_cachedFootprints[aEntry.fpid] = aEntry;
105  }
106 
111  {
112  std::lock_guard<std::mutex> lock( m_lock );
113  m_current_fp = std::move( aFp );
114  }
115 
120  {
121  std::lock_guard<std::mutex> lock( m_lock );
122  return m_current_fp;
123  }
124 
129  {
130  std::lock_guard<std::mutex> lock( m_lock );
131  m_panel = aPanel;
132  }
133 
138  {
139  std::lock_guard<std::mutex> lock( m_lock );
140  return m_panel;
141  }
142 
147  bool QueueEvent( const wxEvent& aEvent )
148  {
149  std::lock_guard<std::mutex> lock( m_lock );
150 
151  if( m_panel )
152  {
153  m_panel->GetEventHandler()->QueueEvent( aEvent.Clone() );
154  return true;
155  }
156  else
157  {
158  return false;
159  }
160  }
161 
166  {
167  std::lock_guard<std::mutex> lock( m_lock );
168  return m_panel ? m_panel->Prj().PcbFootprintLibs() : nullptr;
169  }
170 
171  private:
172  std::deque<CACHE_ENTRY> m_loaderQueue;
173  std::map<LIB_ID, CACHE_ENTRY> m_cachedFootprints;
176  std::mutex m_lock;
177 };
178 
179 
184 class FP_LOADER_THREAD : public wxThread
185 {
187 
188  std::shared_ptr<FP_THREAD_IFACE> m_iface;
189 
190 public:
191  FP_LOADER_THREAD( const std::shared_ptr<FP_THREAD_IFACE>& aIface )
192  : wxThread( wxTHREAD_DETACHED ), m_iface( aIface )
193  {
194  }
195 
196 
198  {
199  }
200 
201 
202  void ProcessEntry( CACHE_ENTRY& aEntry )
203  {
204  FP_LIB_TABLE* fptbl = m_iface->GetTable();
205 
206  if( !fptbl )
207  return;
208 
209  try
210  {
211  aEntry.footprint.reset( fptbl->FootprintLoadWithOptionalNickname( aEntry.fpid ) );
212 
213  if( !aEntry.footprint )
214  aEntry.status = FPS_NOT_FOUND;
215  }
216  catch( const IO_ERROR& )
217  {
218  aEntry.status = FPS_NOT_FOUND;
219  }
220 
221  if( aEntry.status != FPS_NOT_FOUND )
222  aEntry.status = FPS_READY;
223 
224  m_iface->AddToCache( aEntry );
225 
226  if( aEntry.fpid == m_iface->GetCurrentFootprint() )
227  {
228  wxCommandEvent evt( wxEVT_COMMAND_TEXT_UPDATED, 1 );
229  m_iface->QueueEvent( evt );
230  }
231  }
232 
233 
234  virtual void* Entry() override
235  {
236  while( m_iface->GetPanel() )
237  {
238  auto ent = m_iface->PopFromQueue();
239 
240  if( ent )
241  ProcessEntry( *ent );
242  else
243  wxMilliSleep( 100 );
244  }
245 
246  return nullptr;
247  }
248 };
249 
250 
252  std::unique_ptr<KIGFX::GAL_DISPLAY_OPTIONS> aOpts, GAL_TYPE aGalType )
253  : PCB_DRAW_PANEL_GAL( aParent, -1, wxPoint( 0, 0 ), wxSize( 200, 200 ), *aOpts, aGalType ),
254  KIWAY_HOLDER( aKiway, KIWAY_HOLDER::PANEL ),
255  m_displayOptions( std::move( aOpts ) ),
256  m_currentFootprint( nullptr ),
257  m_footprintDisplayed( true )
258 {
259  m_iface = std::make_shared<FP_THREAD_IFACE>();
260  m_iface->SetPanel( this );
262  m_loader->Run();
263 
264  SetStealsFocus( false );
265  ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER );
266  EnableScrolling( false, false ); // otherwise Zoom Auto disables GAL canvas
267 
268  m_dummyBoard = std::make_unique<BOARD>();
269  UpdateColors();
271 
272  Raise();
273  Show( true );
274  StartDrawing();
275 
276  Connect( wxEVT_COMMAND_TEXT_UPDATED,
277  wxCommandEventHandler( FOOTPRINT_PREVIEW_PANEL::OnLoaderThreadUpdate ), NULL, this );
278 }
279 
280 
282 {
283  if( m_currentFootprint )
284  {
285  GetView()->Remove( m_currentFootprint.get() );
286  GetView()->Clear();
287  m_currentFootprint->SetParent( nullptr );
288  }
289 
290  m_iface->SetPanel( nullptr );
291 }
292 
293 
295 {
296  KIGFX::PAINTER* painter = GetView()->GetPainter();
297  auto settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
298 
299  return settings->GetBackgroundColor();
300 }
301 
302 
304 {
305  KIGFX::PAINTER* painter = GetView()->GetPainter();
306  auto settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
307 
308  return settings->GetCursorColor();
309 }
310 
311 
313 {
314  auto opt_ent = m_iface->GetFromCache( aFPID );
315 
316  if( opt_ent )
317  return *opt_ent;
318  else
319  return m_iface->AddToQueue( aFPID );
320 }
321 
322 
323 // This is separate to avoid having to export CACHE_ENTRY to the global namespace
325 {
326  (void) CacheAndReturn( aFPID );
327 }
328 
329 
330 void FOOTPRINT_PREVIEW_PANEL::renderFootprint( std::shared_ptr<MODULE> aFootprint )
331 {
332  if( m_currentFootprint )
333  {
334  GetView()->Remove( m_currentFootprint.get() );
335  GetView()->Clear();
336  m_currentFootprint->SetParent( nullptr );
337  }
338 
339  aFootprint->SetParent( m_dummyBoard.get() );
340 
341  // Ensure we are not using the high contrast mode to display the selected footprint
342  KIGFX::PAINTER* painter = GetView()->GetPainter();
343  auto settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
344  settings->SetContrastModeDisplay( HIGH_CONTRAST_MODE::NORMAL );
345 
346  GetView()->Add( aFootprint.get() );
347  GetView()->SetVisible( aFootprint.get(), true );
348  GetView()->Update( aFootprint.get(), KIGFX::ALL );
349 
350  // Save a reference to the footprint's shared pointer to say we are using it in the
351  // preview panel
352  m_currentFootprint = aFootprint;
353 
354  BOX2I bbox = aFootprint->ViewBBox();
355  bbox.Merge( aFootprint->Value().ViewBBox() );
356  bbox.Merge( aFootprint->Reference().ViewBBox() );
357 
358  if( bbox.GetSize().x > 0 && bbox.GetSize().y > 0 )
359  {
360  // Autozoom
361  GetView()->SetViewport( BOX2D( bbox.GetOrigin(), bbox.GetSize() ) );
362 
363  // Add a margin
364  GetView()->SetScale( GetView()->GetScale() * 0.7 );
365 
366  Refresh();
367  }
368 }
369 
370 
372 {
373  m_currentFPID = aFPID;
374  m_iface->SetCurrentFootprint( aFPID );
375  m_footprintDisplayed = false;
376 
378 
379  if( m_handler )
380  m_handler( fpe.status );
381 
382  if( fpe.status == FPS_READY )
383  {
384  if( !m_footprintDisplayed )
385  {
386  renderFootprint( fpe.footprint );
387  m_footprintDisplayed = true;
388  Refresh();
389  }
390  }
391 }
392 
393 
394 void FOOTPRINT_PREVIEW_PANEL::OnLoaderThreadUpdate( wxCommandEvent& aEvent )
395 {
397 }
398 
399 
401 {
402  m_handler = aHandler;
403 }
404 
405 
407 {
408  return static_cast<wxWindow*>( this );
409 }
410 
411 
413 {
414  PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
415 
416  if( cfg->m_Window.grid.sizes.empty() )
417  {
418  cfg->m_Window.grid.sizes = { "1000 mil",
419  "500 mil",
420  "250 mil",
421  "200 mil",
422  "100 mil",
423  "50 mil",
424  "25 mil",
425  "20 mil",
426  "10 mil",
427  "5 mil",
428  "2 mil",
429  "1 mil",
430  "5.0 mm",
431  "2.5 mm",
432  "1.0 mm",
433  "0.5 mm",
434  "0.25 mm",
435  "0.2 mm",
436  "0.1 mm",
437  "0.05 mm",
438  "0.025 mm",
439  "0.01 mm" };
440  }
441 
442  if( cfg->m_Window.zoom_factors.empty() )
443  {
444  cfg->m_Window.zoom_factors = { 0.035,
445  0.05,
446  0.08,
447  0.13,
448  0.22,
449  0.35,
450  0.6,
451  1.0,
452  1.5,
453  2.2,
454  3.5,
455  5.0,
456  8.0,
457  13.0,
458  20.0,
459  35.0,
460  50.0,
461  80.0,
462  130.0,
463  220.0,
464  300.0 };
465  }
466 
467  for( double& factor : cfg->m_Window.zoom_factors )
468  factor = std::min( factor, MAX_ZOOM_FACTOR );
469 
470  std::unique_ptr<KIGFX::GAL_DISPLAY_OPTIONS> gal_opts;
471 
472  gal_opts = std::make_unique<KIGFX::GAL_DISPLAY_OPTIONS>();
473  gal_opts->ReadConfig( *Pgm().GetCommonSettings(), cfg->m_Window, aParent );
474 
475  auto canvasType = static_cast<EDA_DRAW_PANEL_GAL::GAL_TYPE>( cfg->m_Graphics.canvas_type );
476  auto panel = new FOOTPRINT_PREVIEW_PANEL( aKiway, aParent, std::move( gal_opts ), canvasType );
477 
478  panel->UpdateColors();
479 
480  const GRID_SETTINGS& gridCfg = cfg->m_Window.grid;
481 
482  panel->GetGAL()->SetGridVisibility( gridCfg.show );
483 
484  //Bounds checking cannot include number of elements as an index!
485  int gridIdx = std::max( 0, std::min( gridCfg.last_size_idx, (int) gridCfg.sizes.size() - 1 ) );
486  int gridSize = (int) ValueFromString( EDA_UNITS::MILS, gridCfg.sizes[ gridIdx ] );
487  panel->GetGAL()->SetGridSize( VECTOR2D( gridSize, gridSize ) );
488 
489  return panel;
490 }
KIWAY_HOLDER is a mix in class which holds the location of a wxWindow's KIWAY.
Definition: kiway_holder.h:39
LIB_ID fpid
Footprint loader thread to prevent footprint loading from locking the UI.
std::shared_ptr< MODULE > m_currentFootprint
FOOTPRINT_STATUS status
void SetViewport(const BOX2D &aViewport)
Function SetViewport() Sets the visible area of the VIEW.
Definition: view.cpp:531
virtual void Update(VIEW_ITEM *aItem, int aUpdateFlags) override
For dynamic VIEWs, informs the associated VIEW that the graphical representation of this item has cha...
Definition: pcb_view.cpp:93
virtual const KIGFX::COLOR4D & GetBackgroundColor() override
Get the colors to use in a preview widget to match the preview panel.
bool QueueEvent(const wxEvent &aEvent)
Post an event to the panel, if the panel still exists.
#define MAX_ZOOM_FACTOR
BOX2< VECTOR2D > BOX2D
Definition: box2.h:523
std::shared_ptr< FP_THREAD_IFACE > m_iface
Template specialization to enable wxStrings for certain containers (e.g. unordered_map)
Definition: bitmap.cpp:59
virtual void * Entry() override
LIB_ID GetCurrentFootprint()
Threadsafe accessor to get the current footprint.
OPT< CACHE_ENTRY > PopFromQueue()
Pop an entry from the queue, or empty option if none is available.
void ProcessEntry(CACHE_ENTRY &aEntry)
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
Item needs to be redrawn.
Definition: view_item.h:63
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:102
virtual void DisplayFootprint(const LIB_ID &aFPID) override
Set the currently displayed footprint.
std::vector< double > zoom_factors
Definition: app_settings.h:86
virtual void Remove(VIEW_ITEM *aItem) override
Function Remove() Removes a VIEW_ITEM from the view.
Definition: pcb_view.cpp:76
PAINTER * GetPainter() const
Function GetPainter() Returns the painter object used by the view for drawing VIEW_ITEMS.
Definition: view.h:201
Panel that renders a single footprint via Cairo GAL, meant to be exported through Kiface.
FP_LIB_TABLE * GetTable()
Get an FP_LIB_TABLE, or null if the panel is dead.
CACHE_ENTRY AddToQueue(const LIB_ID &aEntry)
Push an entry to the loading queue and a placeholder to the cache; return the placeholder.
FOOTPRINT_PREVIEW_PANEL * GetPanel()
Get the associated panel.
PAINTER contains all the knowledge about how to draw graphical object onto any particular output devi...
Definition: painter.h:57
virtual wxWindow * GetWindow() override
Get the underlying wxWindow.
std::vector< wxString > sizes
Definition: app_settings.h:52
const auto NULLOPT
Definition: optional.h:9
#define NULL
VECTOR2< double > VECTOR2D
Definition: vector2d.h:593
std::unique_ptr< BOARD > m_dummyBoard
long long int ValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: base_units.cpp:468
Threadsafe interface class between loader thread and panel class.
void Clear()
Function Clear() Removes all items from the view.
Definition: view.cpp:1089
GRID_SETTINGS grid
Definition: app_settings.h:89
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
void SetStealsFocus(bool aStealsFocus)
Set whether focus is taken on certain events (mouseover, keys, etc).
virtual void SetStatusHandler(FOOTPRINT_STATUS_HANDLER aHandler) override
Set the callback to receive status updates.
virtual void CacheFootprint(const LIB_ID &aFPID) override
Preload a footprint into the cache.
static FOOTPRINT_PREVIEW_PANEL * New(KIWAY *aKiway, wxWindow *aParent)
KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the s...
Definition: kiway.h:273
FOOTPRINT_PREVIEW_PANEL(KIWAY *aKiway, wxWindow *aParent, std::unique_ptr< KIGFX::GAL_DISPLAY_OPTIONS > aOpts, GAL_TYPE aGalType)
Create a new panel.
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Function Merge modifies the position and size of the rectangle in order to contain aRect.
Definition: box2.h:386
void SyncLayersVisibility(const BOARD *aBoard)
Function SyncLayersVisibility Updates "visibility" property of each layer of a given BOARD.
void renderFootprint(std::shared_ptr< MODULE > aFootprint)
virtual KIGFX::PCB_VIEW * GetView() const override
Function GetView() Returns a pointer to the VIEW instance used in the panel.
void SetCurrentFootprint(LIB_ID aFp)
Threadsafe accessor to set the current footprint.
void StartDrawing()
Function StartDrawing() Begins drawing if it was stopped previously.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=NULL) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
void UpdateColors()
Updates the color settings in the painter and GAL.
std::map< LIB_ID, CACHE_ENTRY > m_cachedFootprints
VTBL_ENTRY FP_LIB_TABLE * PcbFootprintLibs(KIWAY &aKiway)
Return the table of footprint libraries.
Definition: project.cpp:283
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Function SetScale() Sets the scaling factor, zooming around a given anchor point.
Definition: view.cpp:559
std::shared_ptr< MODULE > footprint
virtual RENDER_SETTINGS * GetSettings()=0
Function GetAdapter Returns pointer to current settings that are going to be used when drawing items.
WINDOW_SETTINGS m_Window
Definition: app_settings.h:173
void SetPanel(FOOTPRINT_PREVIEW_PANEL *aPanel)
Set the associated panel, for QueueEvent() and GetTable().
see class PGM_BASE
void OnLoaderThreadUpdate(wxCommandEvent &aEvent)
FOOTPRINT_STATUS_HANDLER m_handler
virtual CACHE_ENTRY CacheAndReturn(const LIB_ID &aFPID)
std::shared_ptr< FP_THREAD_IFACE > m_iface
FP_LOADER_THREAD(const std::shared_ptr< FP_THREAD_IFACE > &aIface)
OPT< CACHE_ENTRY > GetFromCache(const LIB_ID &aFPID)
Retrieve a cache entry by LIB_ID.
boost::optional< T > OPT
Definition: optional.h:7
std::deque< CACHE_ENTRY > m_loaderQueue
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Sets the item visibility.
Definition: view.cpp:1459
MODULE * FootprintLoadWithOptionalNickname(const LIB_ID &aFootprintId)
Function FootprintLoadWithOptionalNickname loads a footprint having aFootprintId with possibly an emp...
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Function Add() Adds a VIEW_ITEM to the view.
Definition: pcb_view.cpp:59
const Vec & GetSize() const
Definition: box2.h:189
const Vec & GetOrigin() const
Definition: box2.h:193
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
FOOTPRINT_PREVIEW_PANEL * m_panel
virtual const KIGFX::COLOR4D & GetForegroundColor() override
void AddToCache(const CACHE_ENTRY &aEntry)
Add an entry to the cache.
std::function< void(FOOTPRINT_STATUS)> FOOTPRINT_STATUS_HANDLER
Common grid settings, available to every frame.
Definition: app_settings.h:49
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:100