37#include <wx/settings.h>
42#include <wx/textdlg.h>
43#include <wx/textfile.h>
54 const wxString& aPath,
const wxString& aTitle,
55 const wxBitmap& aIcon ) :
56 wxPanel( aParent, wxID_ANY ),
60 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
62 wxBoxSizer* sizer =
new wxBoxSizer( wxHORIZONTAL );
64 wxStaticBitmap* icon =
new wxStaticBitmap(
this, wxID_ANY, aIcon );
65 icon->SetMinSize( FromDIP( wxSize( 16, 16 ) ) );
66 sizer->Add( icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxTOP | wxBOTTOM, 6 );
68 wxStaticText* label =
new wxStaticText(
this, wxID_ANY, aTitle );
70 sizer->Add( label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, 6 );
105 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ).ChangeLightness( 150 ) );
113 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
128 SetMinSize( FromDIP( wxSize( 200, -1 ) ) );
149#if wxCHECK_VERSION( 3, 3, 2 )
165 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
166 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
167 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
175 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
176 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
177 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
185 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
186 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
187 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
201 const std::vector<int> c_bitmapSizes = { 48, 64, 96, 128 };
203 wxBitmapBundle bundle;
204 wxVector<wxBitmap> bitmaps;
205 wxBitmap* icon = aTemplate->
GetIcon();
207 if( icon && icon->IsOk() )
212 wxSize defSize = bundle.GetDefaultSize();
214 for(
const int genSize : c_bitmapSizes )
216 double scale = std::min( (
double) genSize / defSize.GetWidth(),
217 (
double) genSize / defSize.GetHeight() );
219 wxSize scaledSize( wxRound( defSize.x *
scale ),
220 wxRound( defSize.y *
scale ) );
222 wxBitmap scaled = bundle.GetBitmap( scaledSize );
223 scaled.SetScaleFactor( 1.0 );
225 bitmaps.push_back( scaled );
228 m_bitmapIcon->SetBitmap( wxBitmapBundle::FromBitmaps( bitmaps ) );
237 wxString displayDesc = aDescription;
239 if( displayDesc.Length() > 120 )
240 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
251#if !wxCHECK_VERSION( 3, 3, 2 )
253 int wrapWidth = GetClientSize().GetWidth() - 48 - 20;
255 if( wrapWidth > 100 )
261 if( displayDesc.Length() > 120 )
262 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
296 menu.Append( wxID_EDIT,
m_isUserTemplate ?
_(
"Edit Template" ) :
_(
"Open Template (Read-Only)" ) );
297 menu.Append( wxID_OPEN,
_(
"Open Template Folder" ) );
298 menu.Append( wxID_COPY,
_(
"Duplicate Template" ) );
300 menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
301 [
this]( wxCommandEvent& evt )
303 if( evt.GetId() == wxID_EDIT )
305 if( evt.GetId() == wxID_OPEN )
307 else if( evt.GetId() == wxID_COPY )
321 templatePath.RemoveLastDir();
323 wxDir dir( templatePath.GetPath() );
325 if( !dir.IsOpened() )
332 bool found = dir.GetFirst( &filename,
"*.kicad_pro", wxDIR_FILES );
340 wxFileName projectFile( templatePath.GetPath(), filename );
342 m_dialog->SetProjectToEdit( projectFile.GetFullPath() );
354 templatePath.RemoveLastDir();
356 if( !wxLaunchDefaultApplication( templatePath.GetPath() ) )
357 DisplayError(
this, wxString::Format(
_(
"Failed to open '%s'." ), templatePath.GetPath() ) );
367 templatePath.RemoveLastDir();
368 wxString srcTemplatePath = templatePath.GetPath();
371 wxTextEntryDialog nameDlg(
m_dialog,
_(
"Enter name for the new template:" ),
_(
"Duplicate Template" ),
372 srcTemplateName +
_(
"_copy" ) );
374 if( nameDlg.ShowModal() != wxID_OK )
377 wxString newTemplateName = nameDlg.GetValue();
379 if( newTemplateName.IsEmpty() )
385 wxString userTemplatesPath =
m_dialog->GetUserTemplatesPath();
387 if( userTemplatesPath.IsEmpty() )
393 wxFileName destPath( userTemplatesPath, wxEmptyString );
394 destPath.AppendDir( newTemplateName );
395 wxString newTemplatePath = destPath.GetPath();
397 if( destPath.DirExists() )
403 if( !destPath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
409 wxDir sourceDir( srcTemplatePath );
411 if( !sourceDir.IsOpened() )
417 PROJECT_TREE_TRAVERSER traverser(
nullptr, srcTemplatePath, srcTemplateName, newTemplatePath, newTemplateName );
419 sourceDir.Traverse( traverser );
427 wxFileName metaHtmlFile( newTemplatePath,
"info.html" );
428 metaHtmlFile.AppendDir(
"meta" );
430 if( metaHtmlFile.FileExists() )
432 wxTextFile htmlFile( metaHtmlFile.GetFullPath() );
434 if( htmlFile.Open() )
436 bool modified =
false;
438 for(
size_t i = 0; i < htmlFile.GetLineCount(); i++ )
440 wxString line = htmlFile.GetLine( i );
442 if( line.Contains( wxT(
"<title>" ) ) && line.Contains( wxT(
"</title>" ) ) )
444 int titleStart = line.Find( wxT(
"<title>" ) );
445 int titleEnd = line.Find( wxT(
"</title>" ) );
447 if( titleStart != wxNOT_FOUND && titleEnd != wxNOT_FOUND && titleEnd > titleStart )
449 wxString before = line.Left( titleStart + 7 );
450 wxString after = line.Mid( titleEnd );
451 line = before + newTemplateName + after;
472 [dlg, newTemplatePath]()
474 DisplayInfoMessage( dlg, wxString::Format(
_(
"Template duplicated successfully to '%s'." ),
482 const wxSize& aSize,
const wxString& aUserTemplatesPath,
483 const wxString& aSystemTemplatesPath,
484 const std::vector<wxString>& aRecentTemplates ) :
502 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
525 if( filterChoice >= 0 && filterChoice <
static_cast<int>(
m_filterChoice->GetCount() ) )
608 wxFileName templateDir;
609 templateDir.AssignDir(
path );
611 if( !templateDir.DirExists() )
617 wxBitmap* icon = templ.
GetIcon();
621 if( icon && icon->IsOk() )
623 wxImage img = icon->ConvertToImage();
624 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
625 scaledIcon = wxBitmap( img );
630 wxImage img = scaledIcon.ConvertToImage();
631 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
632 scaledIcon = wxBitmap( img );
635 wxString displayTitle = title ? *title : templateDir.GetDirs().Last();
639 m_sizerMRU->Add( mruWidget, 0, wxEXPAND | wxBOTTOM, 2 );
664 [
this](
const wxString& aPath,
bool aIsUser )
666 if( aPath.IsEmpty() )
671 if( !dir.Open( aPath ) )
676 if( dir.HasSubDirs(
"meta" ) )
678 auto templ = std::make_unique<PROJECT_TEMPLATE>( aPath );
679 wxFileName htmlFile = templ->GetHtmlFile();
693 bool cont = dir.GetFirst( &subName, wxEmptyString, wxDIR_DIRS );
697 wxString subFull = aPath + wxFileName::GetPathSeparator() + subName;
700 if( subDir.Open( subFull ) )
702 auto templ = std::make_unique<PROJECT_TEMPLATE>( subFull );
703 wxFileName htmlFile = templ->GetHtmlFile();
715 cont = dir.GetNext( &subName );
727 wxString titleA = *a->GetTemplate()->GetTitle();
728 wxString titleB = *b->GetTemplate()->GetTitle();
729 int cmp = titleA.CmpNoCase( titleB );
734 if( titleA.CmpNoCase(
"default" ) == 0 )
736 else if( titleB.CmpNoCase(
"default" ) == 0 )
768 bool matchesFilter =
true;
770 if( filterChoice == 1 && !widget->IsUserTemplate() )
771 matchesFilter =
false;
772 else if( filterChoice == 2 && widget->IsUserTemplate() )
773 matchesFilter =
false;
775 bool matchesSearch =
true;
777 if( !searchText.IsEmpty() )
779 wxString title = widget->GetTemplate()->GetTitle()->Lower();
780 wxString description = widget->GetDescription().Lower();
782 matchesSearch = title.Contains( searchText ) || description.Contains( searchText );
785 widget->Show( matchesFilter && matchesSearch );
827 wxString selectedPath;
832 htmlFile.RemoveLastDir();
833 selectedPath = htmlFile.GetPath();
842 if( !selectedPath.IsEmpty() )
885 if( widget->GetTemplate() )
887 wxFileName htmlFile = widget->GetTemplate()->GetHtmlFile();
888 htmlFile.RemoveLastDir();
890 if( htmlFile.GetPath() == aPath )
892 if( aKeepMRUVisible )
901 widget->SelectWithoutStateChange();
906 widget->GetPosition(
nullptr, &y );
936 wxBoxSizer* sizer =
new wxBoxSizer( wxVERTICAL );
960 if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
963 wxString url = wxFileName::FileNameToURL( htmlFile );
1002 if( userDir.DirExists() )
1011 wxFileName systemDir;
1014 if( systemDir.DirExists() )
1046 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1051 if( widget->IsSelected() )
1052 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
1054 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1062 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1078 if( clientWidth <= 0 )
1084 wxSizeEvent sizeEvt( wxSize( clientWidth, -1 ) );
1085 widget->GetEventHandler()->ProcessEvent( sizeEvt );
1103 wxString script = wxString::Format( wxS( R
"(
1106 var style = document.createElement( 'style' );
1107 style.textContent = `
1110 document.head.appendChild( style );
1112 )" ), GetCommonStyles() );
1114#if !defined( __MINGW32__ )
1124 wxString selectedPath;
1129 htmlFile.RemoveLastDir();
1130 selectedPath = htmlFile.GetPath();
1139 if( !selectedPath.IsEmpty() )
1146 if( !aHtmlFile.FileExists() || !aHtmlFile.IsFileReadable() )
1147 return wxEmptyString;
1149 wxTextFile file( aHtmlFile.GetFullPath() );
1152 return wxEmptyString;
1156 for( wxString line = file.GetFirstLine(); !file.Eof(); line = file.GetNextLine() )
1158 content += line + wxT(
" " );
1164 wxRegEx reMetaDesc( wxT(
"<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']*)[\"']" ),
1167 if( reMetaDesc.Matches( content ) )
1168 return reMetaDesc.GetMatch( content, 1 );
1171 wxRegEx reParagraph( wxT(
"<p[^>]*>(.*?)</p>" ), wxRE_ICASE );
1173 if( reParagraph.Matches( content ) )
1175 wxString desc = reParagraph.GetMatch( content, 1 );
1178 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1179 reTags.ReplaceAll( &desc, wxT(
" " ) );
1182 desc.Replace( wxT(
" " ), wxT(
" " ) );
1183 desc.Replace( wxT(
"&" ), wxT(
"&" ) );
1184 desc.Replace( wxT(
"<" ), wxT(
"<" ) );
1185 desc.Replace( wxT(
">" ), wxT(
">" ) );
1186 desc.Replace( wxT(
""" ), wxT(
"\"" ) );
1189 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1190 reWhitespace.ReplaceAll( &desc, wxT(
" " ) );
1192 desc = desc.Trim().Trim(
false );
1194 if( !desc.IsEmpty() )
1199 wxRegEx reBody( wxT(
"<body[^>]*>(.*)</body>" ), wxRE_ICASE );
1201 if( reBody.Matches( content ) )
1203 wxString body = reBody.GetMatch( content, 1 );
1206 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1207 reTags.ReplaceAll( &body, wxT(
" " ) );
1210 body.Replace( wxT(
" " ), wxT(
" " ) );
1211 body.Replace( wxT(
"&" ), wxT(
"&" ) );
1212 body.Replace( wxT(
"<" ), wxT(
"<" ) );
1213 body.Replace( wxT(
">" ), wxT(
">" ) );
1214 body.Replace( wxT(
""" ), wxT(
"\"" ) );
1217 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1218 reWhitespace.ReplaceAll( &body, wxT(
" " ) );
1220 body = body.Trim().Trim(
false );
1223 if( body.Length() > 250 )
1224 return body.Left( 250 ) + wxS(
"..." );
1229 return wxEmptyString;
wxBitmapBundle KiBitmapBundleDef(BITMAPS aBitmap, int aDefHeight)
Constructs and returns a bitmap bundle for the given icon ID, with the default bitmap size being aDef...
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
wxSearchCtrl * m_searchCtrl
wxBoxSizer * m_sizerTemplateList
wxScrolledWindow * m_scrolledTemplates
wxPanel * m_panelTemplates
wxChoice * m_filterChoice
wxScrolledWindow * m_scrolledMRU
DIALOG_TEMPLATE_SELECTOR_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Project Template Selector"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(900, 600), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
wxPanel * m_webviewPlaceholder
bool m_loadingExternalHtml
wxString ExtractDescription(const wxFileName &aHtmlFile)
void SetState(DialogState aState)
wxString m_systemTemplatesPath
void SelectTemplateByPath(const wxString &aPath)
void OnBackClicked(wxCommandEvent &event) override
PROJECT_TEMPLATE * m_selectedTemplate
TEMPLATE_WIDGET * m_selectedWidget
void LoadTemplatePreview(PROJECT_TEMPLATE *aTemplate)
DIALOG_TEMPLATE_SELECTOR(wxWindow *aParent, const wxPoint &aPos, const wxSize &aSize, const wxString &aUserTemplatesPath, const wxString &aSystemTemplatesPath, const std::vector< wxString > &aRecentTemplates)
void OnSearchCtrl(wxCommandEvent &event) override
void OnFileSystemEvent(wxFileSystemWatcherEvent &event)
void OnSearchCtrlCancel(wxCommandEvent &event) override
void OnWebViewLoaded(wxWebViewEvent &event)
wxString m_userTemplatesPath
WEBVIEW_PANEL * m_webviewPanel
void OnFilterChanged(wxCommandEvent &event) override
PROJECT_TEMPLATE * GetSelectedTemplate()
void EnsureWebViewCreated()
void RefreshTemplateList()
~DIALOG_TEMPLATE_SELECTOR()
std::vector< TEMPLATE_WIDGET * > m_templateWidgets
void OnRefreshTimer(wxTimerEvent &event)
void OnScrolledTemplatesSize(wxSizeEvent &event)
void SetWidget(TEMPLATE_WIDGET *aWidget)
std::vector< TEMPLATE_MRU_WIDGET * > m_mruWidgets
void OnSysColourChanged(wxSysColourChangedEvent &event)
void OnSearchTimer(wxTimerEvent &event)
wxFileSystemWatcher * m_watcher
std::vector< std::unique_ptr< PROJECT_TEMPLATE > > m_templates
std::vector< wxString > m_recentTemplates
int m_TemplateFilterChoice
virtual SETTINGS_MANAGER & GetSettingsManager() const
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)
Traverser class to duplicate/copy project or template files with proper renaming.
wxString GetErrors() const
T * GetAppSettings(const char *aFilename)
Return a handle to the a given settings by type.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
This file is part of the common library.
static const wxChar * traceTemplateSelector
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...
PGM_BASE & Pgm()
The global program "get" accessor.
#define wxFileSystemWatcher
wxString GetWelcomeHtml(bool aDarkMode)
wxString GetTemplateInfoHtml(const wxString &aTemplateName, bool aDarkMode)
Functions to provide common constants and other functions to assist in making a consistent UI.