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;
703 metaTest.AssignDir( subFull );
706 if( metaTest.DirExists() )
708 auto templ = std::make_unique<PROJECT_TEMPLATE>( subFull );
709 wxFileName htmlFile = templ->GetHtmlFile();
710 wxString description = templ->GetError().IsEmpty() ?
ExtractDescription( htmlFile ) : templ->GetError();
722 cont = dir.GetNext( &subName );
734 wxString titleA = *a->GetTemplate()->GetTitle();
735 wxString titleB = *b->GetTemplate()->GetTitle();
736 int cmp = titleA.CmpNoCase( titleB );
741 if( titleA.CmpNoCase(
"default" ) == 0 )
743 else if( titleB.CmpNoCase(
"default" ) == 0 )
775 bool matchesFilter =
true;
777 if( filterChoice == 1 && !widget->IsUserTemplate() )
778 matchesFilter =
false;
779 else if( filterChoice == 2 && widget->IsUserTemplate() )
780 matchesFilter =
false;
782 bool matchesSearch =
true;
784 if( !searchText.IsEmpty() )
786 wxString title = widget->GetTemplate()->GetTitle()->Lower();
787 wxString description = widget->GetDescription().Lower();
789 matchesSearch = title.Contains( searchText ) || description.Contains( searchText );
792 widget->Show( matchesFilter && matchesSearch );
834 wxString selectedPath;
839 htmlFile.RemoveLastDir();
840 selectedPath = htmlFile.GetPath();
849 if( !selectedPath.IsEmpty() )
892 if( widget->GetTemplate() )
894 wxFileName htmlFile = widget->GetTemplate()->GetHtmlFile();
895 htmlFile.RemoveLastDir();
897 if( htmlFile.GetPath() == aPath )
899 if( aKeepMRUVisible )
908 widget->SelectWithoutStateChange();
913 widget->GetPosition(
nullptr, &y );
943 wxBoxSizer* sizer =
new wxBoxSizer( wxVERTICAL );
967 if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
970 wxString url = wxFileName::FileNameToURL( htmlFile );
1009 if( userDir.DirExists() )
1018 wxFileName systemDir;
1021 if( systemDir.DirExists() )
1053 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1058 if( widget->IsSelected() )
1059 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
1061 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1069 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1085 if( clientWidth <= 0 )
1091 wxSizeEvent sizeEvt( wxSize( clientWidth, -1 ) );
1092 widget->GetEventHandler()->ProcessEvent( sizeEvt );
1110 wxString script = wxString::Format( wxS( R
"(
1113 var style = document.createElement( 'style' );
1114 style.textContent = `
1117 document.head.appendChild( style );
1119 )" ), GetCommonStyles() );
1121#if !defined( __MINGW32__ )
1131 wxString selectedPath;
1136 htmlFile.RemoveLastDir();
1137 selectedPath = htmlFile.GetPath();
1146 if( !selectedPath.IsEmpty() )
1153 if( !aHtmlFile.FileExists() || !aHtmlFile.IsFileReadable() )
1154 return wxEmptyString;
1156 wxTextFile file( aHtmlFile.GetFullPath() );
1159 return wxEmptyString;
1163 for( wxString line = file.GetFirstLine(); !file.Eof(); line = file.GetNextLine() )
1165 content += line + wxT(
" " );
1171 wxRegEx reMetaDesc( wxT(
"<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']*)[\"']" ),
1174 if( reMetaDesc.Matches( content ) )
1175 return reMetaDesc.GetMatch( content, 1 );
1178 wxRegEx reParagraph( wxT(
"<p[^>]*>(.*?)</p>" ), wxRE_ICASE );
1180 if( reParagraph.Matches( content ) )
1182 wxString desc = reParagraph.GetMatch( content, 1 );
1185 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1186 reTags.ReplaceAll( &desc, wxT(
" " ) );
1189 desc.Replace( wxT(
" " ), wxT(
" " ) );
1190 desc.Replace( wxT(
"&" ), wxT(
"&" ) );
1191 desc.Replace( wxT(
"<" ), wxT(
"<" ) );
1192 desc.Replace( wxT(
">" ), wxT(
">" ) );
1193 desc.Replace( wxT(
""" ), wxT(
"\"" ) );
1196 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1197 reWhitespace.ReplaceAll( &desc, wxT(
" " ) );
1199 desc = desc.Trim().Trim(
false );
1201 if( !desc.IsEmpty() )
1206 wxRegEx reBody( wxT(
"<body[^>]*>(.*)</body>" ), wxRE_ICASE );
1208 if( reBody.Matches( content ) )
1210 wxString body = reBody.GetMatch( content, 1 );
1213 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1214 reTags.ReplaceAll( &body, wxT(
" " ) );
1217 body.Replace( wxT(
" " ), wxT(
" " ) );
1218 body.Replace( wxT(
"&" ), wxT(
"&" ) );
1219 body.Replace( wxT(
"<" ), wxT(
"<" ) );
1220 body.Replace( wxT(
">" ), wxT(
">" ) );
1221 body.Replace( wxT(
""" ), wxT(
"\"" ) );
1224 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1225 reWhitespace.ReplaceAll( &body, wxT(
" " ) );
1227 body = body.Trim().Trim(
false );
1230 if( body.Length() > 250 )
1231 return body.Left( 250 ) + wxS(
"..." );
1236 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)
PGM_BASE & Pgm()
The global program "get" accessor.
#define METADIR
A directory which contains information about the project template and does not get copied.
#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.