KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_template_selector.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) 2012 Brian Sidebotham <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.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
26#include <bitmaps.h>
28#include <widgets/ui_common.h>
29#include <algorithm>
30#include <wx_filename.h>
31#include <wx/dir.h>
32#include <wx/dirdlg.h>
33#include <wx/settings.h>
34#include <wx/bitmap.h>
35#include <wx/image.h>
36#include <wx/math.h>
38
39// Welcome / fallback HTML now provided by template_default_html.h
40
42 const wxString& aPath ) :
44{
45 m_parent = aParent;
46 m_templatesPath = aPath;
47}
48
49
51{
52 m_SizerChoice->Add( aTemplateWidget );
53 Layout();
54}
55
56
57// Sort the widgets alphabetically, leaving Default at the top
59{
60 std::vector<TEMPLATE_WIDGET*> sortedList;
61 TEMPLATE_WIDGET* default_temp = nullptr;
62 size_t count = m_SizerChoice->GetItemCount();
63
64 if( count <= 1 )
65 return;
66
67 for( size_t idx = 0; idx < count; idx++ )
68 {
69 wxSizerItem* item = m_SizerChoice->GetItem( idx );
70 if( item && item->IsWindow() )
71 {
72 TEMPLATE_WIDGET* temp = static_cast<TEMPLATE_WIDGET*>( item->GetWindow() );
73
74 const wxString title = *temp->GetTemplate()->GetTitle();
75
76 if( default_temp == nullptr && title.CmpNoCase( "default" ) == 0 )
77 default_temp = temp;
78 else
79 sortedList.push_back( temp );
80 }
81 }
82
83 std::sort(
84 sortedList.begin(), sortedList.end(),
85 []( TEMPLATE_WIDGET* aWidgetA, TEMPLATE_WIDGET* aWidgetB ) -> bool
86 {
87 const wxString* a = aWidgetA->GetTemplate()->GetTitle();
88 const wxString* b = aWidgetB->GetTemplate()->GetTitle();
89
90 return ( *a ).CmpNoCase( *b ) < 0;
91 });
92
93 m_SizerChoice->Clear( false );
94
95 if( default_temp != nullptr )
96 m_SizerChoice->Add( default_temp );
97
98 for (TEMPLATE_WIDGET* temp : sortedList)
99 {
100 m_SizerChoice->Add( temp );
101 }
102
103 Layout();
104}
105
106
108 TEMPLATE_WIDGET_BASE( aParent )
109{
110 m_parent = aParent;
111 m_dialog = aDialog;
112
113 // wxWidgets_3.xx way of doing the same...
114 // Bind(wxEVT_LEFT_DOWN, &TEMPLATE_WIDGET::OnMouse, this );
115
116 m_bitmapIcon->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( TEMPLATE_WIDGET::OnMouse ),
117 nullptr, this );
118 m_staticTitle->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( TEMPLATE_WIDGET::OnMouse ),
119 nullptr, this );
120
121 // We're not selected until we're clicked
122 Unselect();
123
124 // Start with template being NULL
125 m_currTemplate = nullptr;
126}
127
128
130{
131 m_dialog->SetWidget( this );
132 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNHIGHLIGHT ) );
133 m_selected = true;
134 Refresh();
135}
136
137
139{
140 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
141 m_selected = false;
142 Refresh();
143}
144
145
147{
148 m_currTemplate = aTemplate;
149 m_staticTitle->SetFont( KIUI::GetInfoFont( this ) );
150 m_staticTitle->SetLabel( *aTemplate->GetTitle() );
151 m_staticTitle->Wrap( 100 );
152 wxBitmap* icon = aTemplate->GetIcon();
153
154 if( icon && icon->IsOk() )
155 {
156 wxSize maxSize = m_bitmapIcon->GetSize();
157
158 if( icon->GetWidth() > maxSize.x || icon->GetHeight() > maxSize.y )
159 {
160 double scale = std::min( (double) maxSize.x / icon->GetWidth(),
161 (double) maxSize.y / icon->GetHeight() );
162 wxImage image = icon->ConvertToImage();
163 int w = wxRound( icon->GetWidth() * scale );
164 int h = wxRound( icon->GetHeight() * scale );
165 image.Rescale( w, h, wxIMAGE_QUALITY_HIGH );
166 m_bitmapIcon->SetBitmap( wxBitmap( image ) );
167 }
168 else
169 {
170 m_bitmapIcon->SetBitmap( *icon );
171 }
172 }
173 else
175}
176
177
178void TEMPLATE_WIDGET::OnMouse( wxMouseEvent& event )
179{
180 // Toggle selection here
181 Select();
182 event.Skip();
183}
184
185
186void DIALOG_TEMPLATE_SELECTOR::OnPageChange( wxNotebookEvent& event )
187{
188 int newPage = event.GetSelection();
189 int oldPage = event.GetOldSelection();
190
191 // Move webview panel from old page to new page
192 if( oldPage != wxNOT_FOUND && (unsigned)oldPage < m_panels.size() )
193 {
194 // Detach webview from old panel
195 m_panels[oldPage]->m_SizerBase->Detach( m_webviewPanel );
196 m_panels[oldPage]->Layout();
197 }
198
199 if( newPage != wxNOT_FOUND && (unsigned)newPage < m_panels.size() )
200 {
201 // Reparent the webview to the new panel
202 m_webviewPanel->Reparent( m_panels[newPage] );
203
204 // Attach webview to new panel
205 m_panels[newPage]->m_SizerBase->Add( m_webviewPanel, 1, wxEXPAND | wxALL, 5 );
206 m_panels[newPage]->Layout();
207
208 // Update template path
209 m_tcTemplatePath->SetValue( m_panels[newPage]->GetPath() );
210
211 // Reset to welcome page when switching tabs if no template selected
212 m_webviewPanel->SetPage( GetWelcomeHtml() );
213 }
214
215 event.Skip();
216}
217
218
219DIALOG_TEMPLATE_SELECTOR::DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxPoint& aPos,
220 const wxSize& aSize,
221 std::vector<std::pair<wxString, wxFileName>> aTitleDirList,
222 const wxFileName& aDefaultTemplate ) :
223 DIALOG_TEMPLATE_SELECTOR_BASE( aParent, wxID_ANY, _( "Project Template Selector" ), aPos,
224 aSize )
225{
228
229 m_selectedWidget = nullptr;
230 m_defaultTemplatePath = aDefaultTemplate;
231 m_defaultWidget = nullptr;
232
233 for( auto& [title, pathFname] : aTitleDirList )
234 {
235 pathFname.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
236 wxString path = pathFname.GetFullPath(); // caller ensures this ends with file separator.
237
239 m_panels.push_back( tpanel );
240
241 m_notebook->AddPage( tpanel, title );
242
243 if( m_notebook->GetPageCount() == 1 )
244 m_tcTemplatePath->SetValue( path );
245
246 buildPageContent( path, m_notebook->GetPageCount() - 1 );
247 }
248
249 // Move webview panel from dialog to first template selection panel
250 if( !m_panels.empty() )
251 {
252 // Find the sizer containing the webview and detach it
253 wxSizer* parentSizer = m_webviewPanel->GetContainingSizer();
254
255 if( parentSizer )
256 {
257 parentSizer->Detach( m_webviewPanel );
258 }
259
260 // Reparent the webview to the first panel
261 m_webviewPanel->Reparent( m_panels[0] );
262
263 // Add webview to first panel
264 m_panels[0]->m_SizerBase->Add( m_webviewPanel, 1, wxEXPAND | wxALL, 5 );
265 m_panels[0]->Layout();
266 }
267
268 if( m_defaultWidget )
269 m_defaultWidget->Select();
270
271 // Set welcome HTML after dialog is fully constructed
272 CallAfter( [this]()
273 {
274 #if defined (_WIN32)
275 wxSafeYield();
276 m_tcTemplatePath->SelectNone();
277 #endif
278
279 if( m_selectedWidget )
280 {
281 wxFileName htmlFile = m_selectedWidget->GetTemplate()->GetHtmlFile();
282
283 if( htmlFile.FileExists() && htmlFile.IsFileReadable() && htmlFile.GetSize() > 100 /* Basic HTML */ )
284 m_webviewPanel->LoadURL( wxFileName::FileNameToURL( htmlFile ) );
285 else
286 m_webviewPanel->SetPage( GetWelcomeHtml() );
287 }
288 else
289 {
290 m_webviewPanel->SetPage( GetWelcomeHtml() );
291 }
292 });
293
294 // When all widgets have the size fixed, call finishDialogSettings to update sizers
295 finishDialogSettings();
296}
297
298
300{
301 if( m_selectedWidget != nullptr )
302 m_selectedWidget->Unselect();
303
304 m_selectedWidget = aWidget;
305 wxFileName htmlFile = aWidget->GetTemplate()->GetHtmlFile();
306
307 if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
308 {
309 m_webviewPanel->LoadURL( wxFileName::FileNameToURL( htmlFile ) );
310 }
311 else
312 {
313 m_webviewPanel->SetPage( GetTemplateInfoHtml( *aWidget->GetTemplate()->GetTitle() ) );
314 }
315}
316
317
319{
320 TEMPLATE_WIDGET* w = new TEMPLATE_WIDGET( m_panels[aPage]->m_scrolledWindow, this );
321 w->SetTemplate( aTemplate );
322 m_panels[aPage]->AddTemplateWidget( w );
323 m_allWidgets.push_back( w );
324
325 wxFileName base = aTemplate->GetHtmlFile();
326 base.RemoveLastDir();
327
329 m_defaultWidget = w;
330
331 wxString dirName = base.GetDirs().IsEmpty() ? wxString() : base.GetDirs().back();
332
333 if( dirName.CmpNoCase( "default" ) == 0 )
334 {
335 // Prefer a directory literally named 'default'
336 m_defaultWidget = w;
337 }
338}
339
340
345
350
351
352void DIALOG_TEMPLATE_SELECTOR::buildPageContent( const wxString& aPath, int aPage )
353{
354 // Get a list of files under the template path to include as choices...
355 wxDir dir;
356
357 if( dir.Open( aPath ) )
358 {
359 if( dir.HasSubDirs( "meta" ) )
360 {
361 AddTemplate( aPage, new PROJECT_TEMPLATE( aPath ) );
362 }
363 else
364 {
365 wxString sub_name;
366 wxArrayString subdirs;
367
368 bool cont = dir.GetFirst( &sub_name, wxEmptyString, wxDIR_DIRS );
369
370 while( cont )
371 {
372 subdirs.Add( wxString( sub_name ) );
373 cont = dir.GetNext( &sub_name );
374 }
375
376 if( !subdirs.IsEmpty() )
377 subdirs.Sort();
378
379 for( const wxString& dir_name : subdirs )
380 {
381 wxDir sub_dir;
382 wxString sub_full = aPath + dir_name;
383
384 if( sub_dir.Open( sub_full ) )
385 AddTemplate( aPage, new PROJECT_TEMPLATE( sub_full ) );
386 }
387 }
388 }
389
390 m_panels[aPage]->SortAlphabetically();
391 Layout();
392}
393
394
396{
397 wxFileName fn;
398 fn.AssignDir( m_tcTemplatePath->GetValue() );
399 fn.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
400 wxString currPath = fn.GetFullPath();
401
402 wxDirDialog dirDialog( this, _( "Select Templates Directory" ), currPath,
403 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
404
405 if( dirDialog.ShowModal() != wxID_OK )
406 return;
407
408 wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() );
409
410 m_tcTemplatePath->SetValue( dirName.GetFullPath() );
411
412 // Rebuild the page from the new templates path:
414}
415
416
417void DIALOG_TEMPLATE_SELECTOR::onReload( wxCommandEvent& event )
418{
419 int page = m_notebook->GetSelection();
420
421 if( page < 0 )
422 return; // Should not happen
423
424 wxString currPath = m_tcTemplatePath->GetValue();
425
426 wxFileName fn;
427 fn.AssignDir( m_tcTemplatePath->GetValue() );
428 fn.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
429 currPath = fn.GetFullPath();
430 m_tcTemplatePath->SetValue( currPath );
431
433}
434
435
437{
438 // Rebuild the page from the new templates path:
439 int page = m_notebook->GetSelection();
440
441 if( page < 0 )
442 return; // Should not happen
443
444 wxString title = m_notebook->GetPageText( page );
445 wxString currPath = m_tcTemplatePath->GetValue();
446
447 // Block all events to the notebook and its children
448 wxEventBlocker blocker( m_notebook );
449
450 // Detach webview from current panel before deleting it
451 if( (unsigned)page < m_panels.size() )
452 {
453 m_panels[page]->m_SizerBase->Detach( m_webviewPanel );
454 m_webviewPanel->Reparent( this ); // Reparent to dialog
455 m_webviewPanel->Hide(); // Hide webview panel temporarily
456 }
457
458 m_notebook->DeletePage( page );
459
461 m_panels[page] = tpanel;
462 m_notebook->InsertPage( page, tpanel, title, true );
463
464 // Reparent and add webview back to the new panel
465 m_webviewPanel->Reparent( tpanel );
466 m_webviewPanel->Show();
467 tpanel->m_SizerBase->Add( m_webviewPanel, 1, wxEXPAND | wxALL, 5 );
468
469 buildPageContent( m_tcTemplatePath->GetValue(), page );
470
471 m_selectedWidget = nullptr;
472 // Reset to welcome page after rebuilding
473 m_webviewPanel->SetPage( GetWelcomeHtml() );
474
475 Layout();
476}
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:104
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
DIALOG_TEMPLATE_SELECTOR_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Project Template Selector"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(513, 523), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void AddTemplate(int aPage, PROJECT_TEMPLATE *aTemplate)
void OnPageChange(wxNotebookEvent &event) override
void onReload(wxCommandEvent &event) override
DIALOG_TEMPLATE_SELECTOR(wxWindow *aParent, const wxPoint &aPos, const wxSize &aSize, std::vector< std::pair< wxString, wxFileName > > aTitleDirList, const wxFileName &aDefaultTemplate)
void onDirectoryBrowseClicked(wxCommandEvent &event) override
std::vector< TEMPLATE_WIDGET * > m_allWidgets
void SetWidget(TEMPLATE_WIDGET *aWidget)
void buildPageContent(const wxString &aPath, int aPage)
std::vector< TEMPLATE_SELECTION_PANEL * > m_panels
A class which provides project template functionality.
wxBitmap * GetIcon()
Get the 64px^2 icon for the project template.
wxFileName GetHtmlFile()
Get the full Html filename for the project template.
wxString * GetTitle()
Get the title of the project (extracted from the html title tag)
TEMPLATE_SELECTION_PANEL_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL|wxBORDER_NONE, const wxString &name=wxEmptyString)
TEMPLATE_SELECTION_PANEL(wxNotebookPage *aParent, const wxString &aPath)
wxString m_templatesPath
the path to access to the folder containing the templates (which are also folders)
void AddTemplateWidget(TEMPLATE_WIDGET *aTemplateWidget)
TEMPLATE_WIDGET_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
PROJECT_TEMPLATE * GetTemplate()
void OnMouse(wxMouseEvent &event)
void SetTemplate(PROJECT_TEMPLATE *aTemplate)
Set the project template for this widget, which will determine the icon and title associated with thi...
DIALOG_TEMPLATE_SELECTOR * m_dialog
TEMPLATE_WIDGET(wxWindow *aParent, DIALOG_TEMPLATE_SELECTOR *aDialog)
PROJECT_TEMPLATE * m_currTemplate
#define _(s)
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
const int scale
wxString GetWelcomeHtml()
wxString GetTemplateInfoHtml(const wxString &aTemplateName)
Functions to provide common constants and other functions to assist in making a consistent UI.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:39