33#include <wx/settings.h>
38#include <wx/textdlg.h>
39#include <wx/textfile.h>
50 const wxString& aPath,
const wxString& aTitle,
51 const wxBitmap& aIcon ) :
52 wxPanel( aParent, wxID_ANY ),
56 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
58 wxBoxSizer* sizer =
new wxBoxSizer( wxHORIZONTAL );
60 wxStaticBitmap* icon =
new wxStaticBitmap(
this, wxID_ANY, aIcon );
61 icon->SetMinSize( FromDIP( wxSize( 16, 16 ) ) );
62 sizer->Add( icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxTOP | wxBOTTOM, 6 );
64 wxStaticText* label =
new wxStaticText(
this, wxID_ANY, aTitle );
66 sizer->Add( label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, 6 );
101 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ).ChangeLightness( 150 ) );
109 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
124 SetMinSize( FromDIP( wxSize( 200, -1 ) ) );
145#if wxCHECK_VERSION( 3, 3, 2 )
161 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
162 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
163 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
171 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
172 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
173 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
181 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
182 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
183 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
197 const std::vector<int> c_bitmapSizes = { 48, 64, 96, 128 };
199 wxBitmapBundle bundle;
200 wxVector<wxBitmap> bitmaps;
201 wxBitmap* icon = aTemplate->
GetIcon();
203 if( icon && icon->IsOk() )
208 wxSize defSize = bundle.GetDefaultSize();
210 for(
const int genSize : c_bitmapSizes )
212 double scale = std::min( (
double) genSize / defSize.GetWidth(),
213 (
double) genSize / defSize.GetHeight() );
215 wxSize scaledSize( wxRound( defSize.x *
scale ),
216 wxRound( defSize.y *
scale ) );
218 wxBitmap scaled = bundle.GetBitmap( scaledSize );
219 scaled.SetScaleFactor( 1.0 );
221 bitmaps.push_back( scaled );
224 m_bitmapIcon->SetBitmap( wxBitmapBundle::FromBitmaps( bitmaps ) );
233 wxString displayDesc = aDescription;
235 if( displayDesc.Length() > 120 )
236 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
247#if !wxCHECK_VERSION( 3, 3, 2 )
249 int wrapWidth = GetClientSize().GetWidth() - 48 - 20;
251 if( wrapWidth > 100 )
257 if( displayDesc.Length() > 120 )
258 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
292 menu.Append( wxID_EDIT,
m_isUserTemplate ?
_(
"Edit Template" ) :
_(
"Open Template (Read-Only)" ) );
293 menu.Append( wxID_OPEN,
_(
"Open Template Folder" ) );
294 menu.Append( wxID_COPY,
_(
"Duplicate Template" ) );
296 menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
297 [
this]( wxCommandEvent& evt )
299 if( evt.GetId() == wxID_EDIT )
301 if( evt.GetId() == wxID_OPEN )
303 else if( evt.GetId() == wxID_COPY )
317 templatePath.RemoveLastDir();
319 wxDir dir( templatePath.GetPath() );
321 if( !dir.IsOpened() )
328 bool found = dir.GetFirst( &filename,
"*.kicad_pro", wxDIR_FILES );
336 wxFileName projectFile( templatePath.GetPath(), filename );
338 m_dialog->SetProjectToEdit( projectFile.GetFullPath() );
350 templatePath.RemoveLastDir();
352 if( !wxLaunchDefaultApplication( templatePath.GetPath() ) )
353 DisplayError(
this, wxString::Format(
_(
"Failed to open '%s'." ), templatePath.GetPath() ) );
363 templatePath.RemoveLastDir();
364 wxString srcTemplatePath = templatePath.GetPath();
367 wxTextEntryDialog nameDlg(
m_dialog,
_(
"Enter name for the new template:" ),
_(
"Duplicate Template" ),
368 srcTemplateName +
_(
"_copy" ) );
370 if( nameDlg.ShowModal() != wxID_OK )
373 wxString newTemplateName = nameDlg.GetValue();
375 if( newTemplateName.IsEmpty() )
381 wxString userTemplatesPath =
m_dialog->GetUserTemplatesPath();
383 if( userTemplatesPath.IsEmpty() )
389 wxFileName destPath( userTemplatesPath, wxEmptyString );
390 destPath.AppendDir( newTemplateName );
391 wxString newTemplatePath = destPath.GetPath();
393 if( destPath.DirExists() )
399 if( !destPath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
405 wxDir sourceDir( srcTemplatePath );
407 if( !sourceDir.IsOpened() )
413 PROJECT_TREE_TRAVERSER traverser(
nullptr, srcTemplatePath, srcTemplateName, newTemplatePath, newTemplateName );
415 sourceDir.Traverse( traverser );
423 wxFileName metaHtmlFile( newTemplatePath,
"info.html" );
424 metaHtmlFile.AppendDir(
"meta" );
426 if( metaHtmlFile.FileExists() )
428 wxTextFile htmlFile( metaHtmlFile.GetFullPath() );
430 if( htmlFile.Open() )
432 bool modified =
false;
434 for(
size_t i = 0; i < htmlFile.GetLineCount(); i++ )
436 wxString line = htmlFile.GetLine( i );
438 if( line.Contains( wxT(
"<title>" ) ) && line.Contains( wxT(
"</title>" ) ) )
440 int titleStart = line.Find( wxT(
"<title>" ) );
441 int titleEnd = line.Find( wxT(
"</title>" ) );
443 if( titleStart != wxNOT_FOUND && titleEnd != wxNOT_FOUND && titleEnd > titleStart )
445 wxString before = line.Left( titleStart + 7 );
446 wxString after = line.Mid( titleEnd );
447 line = before + newTemplateName + after;
468 [dlg, newTemplatePath]()
470 DisplayInfoMessage( dlg, wxString::Format(
_(
"Template duplicated successfully to '%s'." ),
478 const wxSize& aSize,
const wxString& aUserTemplatesPath,
479 const wxString& aSystemTemplatesPath,
480 const wxString& aDefaultTemplatesPath,
481 const std::vector<wxString>& aRecentTemplates ) :
501 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
522 if( filterChoice >= 0 && filterChoice <
static_cast<int>(
m_filterChoice->GetCount() ) )
573 int width =
m_splitter->GetClientSize().GetWidth();
574 sashPos = width > 0 ? width / 3 : FromDIP( 300 );
630 wxFileName templateDir;
631 templateDir.AssignDir(
path );
633 if( !templateDir.DirExists() )
639 wxBitmap* icon = templ.
GetIcon();
643 if( icon && icon->IsOk() )
645 wxImage img = icon->ConvertToImage();
646 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
647 scaledIcon = wxBitmap( img );
652 wxImage img = scaledIcon.ConvertToImage();
653 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
654 scaledIcon = wxBitmap( img );
657 wxString displayTitle = title ? *title : templateDir.GetDirs().Last();
661 m_sizerMRU->Add( mruWidget, 0, wxEXPAND | wxBOTTOM, 2 );
686 [
this](
const wxString& aPath,
bool aIsUser )
688 if( aPath.IsEmpty() )
693 if( !dir.Open( aPath ) )
698 if( dir.HasSubDirs(
"meta" ) )
700 auto templ = std::make_unique<PROJECT_TEMPLATE>( aPath );
701 wxFileName htmlFile = templ->GetHtmlFile();
715 bool cont = dir.GetFirst( &subName, wxEmptyString, wxDIR_DIRS );
719 wxString subFull = aPath + wxFileName::GetPathSeparator() + subName;
725 metaTest.AssignDir( subFull );
728 if( metaTest.DirExists() )
730 auto templ = std::make_unique<PROJECT_TEMPLATE>( subFull );
731 wxFileName htmlFile = templ->GetHtmlFile();
732 wxString description = templ->GetError().IsEmpty() ?
ExtractDescription( htmlFile ) : templ->GetError();
744 cont = dir.GetNext( &subName );
761 wxString titleA = *a->GetTemplate()->GetTitle();
762 wxString titleB = *b->GetTemplate()->GetTitle();
763 int cmp = titleA.CmpNoCase( titleB );
768 if( titleA.CmpNoCase(
"default" ) == 0 )
770 else if( titleB.CmpNoCase(
"default" ) == 0 )
802 bool matchesFilter =
true;
804 if( filterChoice == 1 && !widget->IsUserTemplate() )
805 matchesFilter =
false;
806 else if( filterChoice == 2 && widget->IsUserTemplate() )
807 matchesFilter =
false;
809 bool matchesSearch =
true;
811 if( !searchText.IsEmpty() )
813 wxString title = widget->GetTemplate()->GetTitle()->Lower();
814 wxString description = widget->GetDescription().Lower();
816 matchesSearch = title.Contains( searchText ) || description.Contains( searchText );
819 widget->Show( matchesFilter && matchesSearch );
861 wxString selectedPath;
866 htmlFile.RemoveLastDir();
867 selectedPath = htmlFile.GetPath();
876 if( !selectedPath.IsEmpty() )
919 if( widget->GetTemplate() )
921 wxFileName htmlFile = widget->GetTemplate()->GetHtmlFile();
922 htmlFile.RemoveLastDir();
924 if( htmlFile.GetPath() == aPath )
926 if( aKeepMRUVisible )
935 widget->SelectWithoutStateChange();
940 widget->GetPosition(
nullptr, &y );
970 wxBoxSizer* sizer =
new wxBoxSizer( wxVERTICAL );
994 if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
997 wxString url = wxFileName::FileNameToURL( htmlFile );
1032 [
this](
const wxString& aPath,
const char* aLabel )
1034 if( aPath.IsEmpty() )
1038 dir.AssignDir( aPath );
1040 if( dir.DirExists() )
1076 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1081 if( widget->IsSelected() )
1082 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
1084 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1092 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1108 if( clientWidth <= 0 )
1114 wxSizeEvent sizeEvt( wxSize( clientWidth, -1 ) );
1115 widget->GetEventHandler()->ProcessEvent( sizeEvt );
1133 wxString script = wxString::Format( wxS( R
"(
1136 var style = document.createElement( 'style' );
1137 style.textContent = `
1140 document.head.appendChild( style );
1142 )" ), GetCommonStyles() );
1144#if !defined( __MINGW32__ )
1154 wxString selectedPath;
1159 htmlFile.RemoveLastDir();
1160 selectedPath = htmlFile.GetPath();
1169 if( !selectedPath.IsEmpty() )
1176 if( !aHtmlFile.FileExists() || !aHtmlFile.IsFileReadable() )
1177 return wxEmptyString;
1179 wxTextFile file( aHtmlFile.GetFullPath() );
1182 return wxEmptyString;
1186 for( wxString line = file.GetFirstLine(); !file.Eof(); line = file.GetNextLine() )
1188 content += line + wxT(
" " );
1194 wxRegEx reMetaDesc( wxT(
"<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']*)[\"']" ),
1197 if( reMetaDesc.Matches( content ) )
1198 return reMetaDesc.GetMatch( content, 1 );
1201 wxRegEx reParagraph( wxT(
"<p[^>]*>(.*?)</p>" ), wxRE_ICASE );
1203 if( reParagraph.Matches( content ) )
1205 wxString desc = reParagraph.GetMatch( content, 1 );
1208 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1209 reTags.ReplaceAll( &desc, wxT(
" " ) );
1212 desc.Replace( wxT(
" " ), wxT(
" " ) );
1213 desc.Replace( wxT(
"&" ), wxT(
"&" ) );
1214 desc.Replace( wxT(
"<" ), wxT(
"<" ) );
1215 desc.Replace( wxT(
">" ), wxT(
">" ) );
1216 desc.Replace( wxT(
""" ), wxT(
"\"" ) );
1219 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1220 reWhitespace.ReplaceAll( &desc, wxT(
" " ) );
1222 desc = desc.Trim().Trim(
false );
1224 if( !desc.IsEmpty() )
1229 wxRegEx reBody( wxT(
"<body[^>]*>(.*)</body>" ), wxRE_ICASE );
1231 if( reBody.Matches( content ) )
1233 wxString body = reBody.GetMatch( content, 1 );
1236 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1237 reTags.ReplaceAll( &body, wxT(
" " ) );
1240 body.Replace( wxT(
" " ), wxT(
" " ) );
1241 body.Replace( wxT(
"&" ), wxT(
"&" ) );
1242 body.Replace( wxT(
"<" ), wxT(
"<" ) );
1243 body.Replace( wxT(
">" ), wxT(
">" ) );
1244 body.Replace( wxT(
""" ), wxT(
"\"" ) );
1247 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1248 reWhitespace.ReplaceAll( &body, wxT(
" " ) );
1250 body = body.Trim().Trim(
false );
1253 if( body.Length() > 250 )
1254 return body.Left( 250 ) + wxS(
"..." );
1259 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)
wxSplitterWindow * m_splitter
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)
void OnSearchCtrl(wxCommandEvent &event) override
void OnFileSystemEvent(wxFileSystemWatcherEvent &event)
void OnSearchCtrlCancel(wxCommandEvent &event) override
void OnWebViewLoaded(wxWebViewEvent &event)
wxString m_defaultTemplatesPath
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)
DIALOG_TEMPLATE_SELECTOR(wxWindow *aParent, const wxPoint &aPos, const wxSize &aSize, const wxString &aUserTemplatesPath, const wxString &aSystemTemplatesPath, const wxString &aDefaultTemplatesPath, const std::vector< wxString > &aRecentTemplates)
void OnSearchTimer(wxTimerEvent &event)
wxFileSystemWatcher * m_watcher
void EnsurePreviewSplit()
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.