34#include <wx/settings.h>
39#include <wx/textdlg.h>
40#include <wx/textfile.h>
51 const wxString& aPath,
const wxString& aTitle,
52 const wxBitmap& aIcon ) :
53 wxPanel( aParent, wxID_ANY ),
57 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
59 wxBoxSizer* sizer =
new wxBoxSizer( wxHORIZONTAL );
61 wxStaticBitmap* icon =
new wxStaticBitmap(
this, wxID_ANY, aIcon );
62 icon->SetMinSize( FromDIP( wxSize( 16, 16 ) ) );
63 sizer->Add( icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxTOP | wxBOTTOM, 6 );
65 wxStaticText* label =
new wxStaticText(
this, wxID_ANY, aTitle );
67 sizer->Add( label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, 6 );
102 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ).ChangeLightness( 150 ) );
110 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
125 SetMinSize( FromDIP( wxSize( 200, -1 ) ) );
146#if wxCHECK_VERSION( 3, 3, 2 )
162 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
163 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
164 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
172 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
173 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
174 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
182 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
183 m_titleLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
184 m_descLabel->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
198 const std::vector<int> c_bitmapSizes = { 48, 64, 96, 128 };
200 wxBitmapBundle bundle;
201 wxVector<wxBitmap> bitmaps;
202 wxBitmap* icon = aTemplate->
GetIcon();
204 if( icon && icon->IsOk() )
209 wxSize defSize = bundle.GetDefaultSize();
211 for(
const int genSize : c_bitmapSizes )
213 double scale = std::min( (
double) genSize / defSize.GetWidth(),
214 (
double) genSize / defSize.GetHeight() );
216 wxSize scaledSize( wxRound( defSize.x *
scale ),
217 wxRound( defSize.y *
scale ) );
219 wxBitmap scaled = bundle.GetBitmap( scaledSize );
220 scaled.SetScaleFactor( 1.0 );
222 bitmaps.push_back( scaled );
225 m_bitmapIcon->SetBitmap( wxBitmapBundle::FromBitmaps( bitmaps ) );
234 wxString displayDesc = aDescription;
236 if( displayDesc.Length() > 120 )
237 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
248#if !wxCHECK_VERSION( 3, 3, 2 )
250 int wrapWidth = GetClientSize().GetWidth() - 48 - 20;
252 if( wrapWidth > 100 )
258 if( displayDesc.Length() > 120 )
259 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
293 menu.Append( wxID_EDIT,
294 m_category == CATEGORY::USER ?
_(
"Edit Template" ) :
_(
"Open Template (Read-Only)" ) );
295 menu.Append( wxID_OPEN,
_(
"Open Template Folder" ) );
296 menu.Append( wxID_COPY,
_(
"Duplicate Template" ) );
298 menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
299 [
this]( wxCommandEvent& evt )
301 if( evt.GetId() == wxID_EDIT )
303 else if( evt.GetId() == wxID_OPEN )
305 else if( evt.GetId() == wxID_COPY )
319 templatePath.RemoveLastDir();
321 wxDir dir( templatePath.GetPath() );
323 if( !dir.IsOpened() )
330 bool found = dir.GetFirst( &filename,
"*.kicad_pro", wxDIR_FILES );
338 wxFileName projectFile( templatePath.GetPath(), filename );
340 m_dialog->SetProjectToEdit( projectFile.GetFullPath() );
352 templatePath.RemoveLastDir();
354 if( !wxLaunchDefaultApplication( templatePath.GetPath() ) )
355 DisplayError(
this, wxString::Format(
_(
"Failed to open '%s'." ), templatePath.GetPath() ) );
365 templatePath.RemoveLastDir();
366 wxString srcTemplatePath = templatePath.GetPath();
369 wxTextEntryDialog nameDlg(
m_dialog,
_(
"Enter name for the new template:" ),
_(
"Duplicate Template" ),
370 srcTemplateName +
_(
"_copy" ) );
372 if( nameDlg.ShowModal() != wxID_OK )
375 wxString newTemplateName = nameDlg.GetValue();
377 if( newTemplateName.IsEmpty() )
383 wxString userTemplatesPath =
m_dialog->GetUserTemplatesPath();
385 if( userTemplatesPath.IsEmpty() )
391 wxFileName destPath( userTemplatesPath, wxEmptyString );
392 destPath.AppendDir( newTemplateName );
393 wxString newTemplatePath = destPath.GetPath();
395 if( destPath.DirExists() )
401 if( !destPath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
407 wxDir sourceDir( srcTemplatePath );
409 if( !sourceDir.IsOpened() )
415 PROJECT_TREE_TRAVERSER traverser(
nullptr, srcTemplatePath, srcTemplateName, newTemplatePath, newTemplateName );
417 sourceDir.Traverse( traverser );
425 wxFileName metaHtmlFile( newTemplatePath,
"info.html" );
426 metaHtmlFile.AppendDir(
"meta" );
428 if( metaHtmlFile.FileExists() )
430 wxTextFile htmlFile( metaHtmlFile.GetFullPath() );
432 if( htmlFile.Open() )
434 bool modified =
false;
436 for(
size_t i = 0; i < htmlFile.GetLineCount(); i++ )
438 wxString line = htmlFile.GetLine( i );
440 if( line.Contains( wxT(
"<title>" ) ) && line.Contains( wxT(
"</title>" ) ) )
442 int titleStart = line.Find( wxT(
"<title>" ) );
443 int titleEnd = line.Find( wxT(
"</title>" ) );
445 if( titleStart != wxNOT_FOUND && titleEnd != wxNOT_FOUND && titleEnd > titleStart )
447 wxString before = line.Left( titleStart + 7 );
448 wxString after = line.Mid( titleEnd );
449 line = before + newTemplateName + after;
470 [dlg, newTemplatePath]()
472 DisplayInfoMessage( dlg, wxString::Format(
_(
"Template duplicated successfully to '%s'." ),
480 const wxSize& aSize,
const wxString& aUserTemplatesPath,
481 const wxString& aSystemTemplatesPath,
482 const wxString& aDefaultTemplatesPath,
483 const std::vector<wxString>& aRecentTemplates,
484 const wxString& aBrowsedTemplatesPath ) :
509 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
535 if( filterChoice >= 0 && filterChoice <
static_cast<int>(
m_filterChoice->GetCount() ) )
592 int width =
m_splitter->GetClientSize().GetWidth();
593 sashPos = width > 0 ? width / 3 : FromDIP( 300 );
649 wxFileName templateDir;
650 templateDir.AssignDir(
path );
652 if( !templateDir.DirExists() )
658 wxBitmap* icon = templ.
GetIcon();
662 if( icon && icon->IsOk() )
664 wxImage img = icon->ConvertToImage();
665 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
666 scaledIcon = wxBitmap( img );
671 wxImage img = scaledIcon.ConvertToImage();
672 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
673 scaledIcon = wxBitmap( img );
676 wxString displayTitle = title ? *title : templateDir.GetDirs().Last();
680 m_sizerMRU->Add( mruWidget, 0, wxEXPAND | wxBOTTOM, 2 );
707 if( aPath.IsEmpty() )
712 if( !dir.Open( aPath ) )
716 static_cast<int>( aCategory ) );
718 if( dir.HasSubDirs(
"meta" ) )
720 auto templ = std::make_unique<PROJECT_TEMPLATE>( aPath );
721 wxFileName htmlFile = templ->GetHtmlFile();
735 bool cont = dir.GetFirst( &subName, wxEmptyString, wxDIR_DIRS );
739 wxString subFull = aPath + wxFileName::GetPathSeparator() + subName;
745 metaTest.AssignDir( subFull );
748 if( metaTest.DirExists() )
750 auto templ = std::make_unique<PROJECT_TEMPLATE>( subFull );
751 wxFileName htmlFile = templ->GetHtmlFile();
752 wxString description = templ->GetError().IsEmpty() ?
ExtractDescription( htmlFile ) : templ->GetError();
764 cont = dir.GetNext( &subName );
784 wxString titleA = *a->GetTemplate()->GetTitle();
785 wxString titleB = *b->GetTemplate()->GetTitle();
786 int cmp = titleA.CmpNoCase( titleB );
791 if( titleA.CmpNoCase(
"default" ) == 0 )
793 else if( titleB.CmpNoCase(
"default" ) == 0 )
825 bool matchesFilter =
true;
829 if( filterChoice == 1 && cat != TEMPLATE_WIDGET::CATEGORY::USER )
830 matchesFilter =
false;
831 else if( filterChoice == 2 && cat != TEMPLATE_WIDGET::CATEGORY::SYSTEM )
832 matchesFilter =
false;
833 else if( filterChoice == 3 && cat != TEMPLATE_WIDGET::CATEGORY::BROWSED )
834 matchesFilter =
false;
836 bool matchesSearch =
true;
838 if( !searchText.IsEmpty() )
840 wxString title = widget->GetTemplate()->GetTitle()->Lower();
841 wxString description = widget->GetDescription().Lower();
843 matchesSearch = title.Contains( searchText ) || description.Contains( searchText );
846 widget->Show( matchesFilter && matchesSearch );
888 wxString selectedPath;
893 htmlFile.RemoveLastDir();
894 selectedPath = htmlFile.GetPath();
903 if( !selectedPath.IsEmpty() )
946 if( widget->GetTemplate() )
948 wxFileName htmlFile = widget->GetTemplate()->GetHtmlFile();
949 htmlFile.RemoveLastDir();
951 if( htmlFile.GetPath() == aPath )
953 if( aKeepMRUVisible )
962 widget->SelectWithoutStateChange();
967 widget->GetPosition(
nullptr, &y );
997 wxBoxSizer* sizer =
new wxBoxSizer( wxVERTICAL );
1021 if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
1024 wxString url = wxFileName::FileNameToURL( htmlFile );
1059 [
this](
const wxString& aPath,
const char* aLabel )
1061 if( aPath.IsEmpty() )
1065 dir.AssignDir( aPath );
1075 if( dir.DirExists() )
1097 [](
const wxString& a,
const wxString& b )
1099 if( a.IsEmpty() || b.IsEmpty() )
1110 return lhs.SameAs( rhs );
1141 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1146 if( widget->IsSelected() )
1147 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
1149 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1157 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1173 if( clientWidth <= 0 )
1179 wxSizeEvent sizeEvt( wxSize( clientWidth, -1 ) );
1180 widget->GetEventHandler()->ProcessEvent( sizeEvt );
1198 wxString script = wxString::Format( wxS( R
"(
1201 var style = document.createElement( 'style' );
1202 style.textContent = `
1205 document.head.appendChild( style );
1207 )" ), GetCommonStyles() );
1209#if !defined( __MINGW32__ )
1219 wxString selectedPath;
1224 htmlFile.RemoveLastDir();
1225 selectedPath = htmlFile.GetPath();
1234 if( !selectedPath.IsEmpty() )
1241 if( !aHtmlFile.FileExists() || !aHtmlFile.IsFileReadable() )
1242 return wxEmptyString;
1244 wxTextFile file( aHtmlFile.GetFullPath() );
1247 return wxEmptyString;
1251 for( wxString line = file.GetFirstLine(); !file.Eof(); line = file.GetNextLine() )
1253 content += line + wxT(
" " );
1259 wxRegEx reMetaDesc( wxT(
"<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']*)[\"']" ),
1262 if( reMetaDesc.Matches( content ) )
1263 return reMetaDesc.GetMatch( content, 1 );
1266 wxRegEx reParagraph( wxT(
"<p[^>]*>(.*?)</p>" ), wxRE_ICASE );
1268 if( reParagraph.Matches( content ) )
1270 wxString desc = reParagraph.GetMatch( content, 1 );
1273 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1274 reTags.ReplaceAll( &desc, wxT(
" " ) );
1277 desc.Replace( wxT(
" " ), wxT(
" " ) );
1278 desc.Replace( wxT(
"&" ), wxT(
"&" ) );
1279 desc.Replace( wxT(
"<" ), wxT(
"<" ) );
1280 desc.Replace( wxT(
">" ), wxT(
">" ) );
1281 desc.Replace( wxT(
""" ), wxT(
"\"" ) );
1284 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1285 reWhitespace.ReplaceAll( &desc, wxT(
" " ) );
1287 desc = desc.Trim().Trim(
false );
1289 if( !desc.IsEmpty() )
1294 wxRegEx reBody( wxT(
"<body[^>]*>(.*)</body>" ), wxRE_ICASE );
1296 if( reBody.Matches( content ) )
1298 wxString body = reBody.GetMatch( content, 1 );
1301 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1302 reTags.ReplaceAll( &body, wxT(
" " ) );
1305 body.Replace( wxT(
" " ), wxT(
" " ) );
1306 body.Replace( wxT(
"&" ), wxT(
"&" ) );
1307 body.Replace( wxT(
"<" ), wxT(
"<" ) );
1308 body.Replace( wxT(
">" ), wxT(
">" ) );
1309 body.Replace( wxT(
""" ), wxT(
"\"" ) );
1312 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1313 reWhitespace.ReplaceAll( &body, wxT(
" " ) );
1315 body = body.Trim().Trim(
false );
1318 if( body.Length() > 250 )
1319 return body.Left( 250 ) + wxS(
"..." );
1324 return wxEmptyString;
1338 if( startPath.IsEmpty() )
1341 wxDirDialog dirDialog(
this,
_(
"Select Templates Directory" ), startPath,
1342 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
1344 if( dirDialog.ShowModal() != wxID_OK )
1347 wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() );
1358 if( filterChoice != 0 && filterChoice != 3 )
1397 parentSizer->Layout();
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
wxStaticText * m_browsedPathLabel
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)
wxButton * m_clearBrowseButton
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
wxString m_browsedTemplatesPath
TEMPLATE_WIDGET * m_selectedWidget
void LoadTemplatePreview(PROJECT_TEMPLATE *aTemplate)
void OnSearchCtrl(wxCommandEvent &event) override
void OnFileSystemEvent(wxFileSystemWatcherEvent &event)
void onClearBrowsedClicked(wxCommandEvent &aEvent) override
bool browsedPathIsDuplicate() const
Returns true if the browsed path is empty or duplicates one of the standard template directories (aft...
void OnSearchCtrlCancel(wxCommandEvent &event) override
void OnWebViewLoaded(wxWebViewEvent &event)
wxString m_defaultTemplatesPath
wxString m_userTemplatesPath
WEBVIEW_PANEL * m_webviewPanel
void OnFilterChanged(wxCommandEvent &event) override
void onBrowseClicked(wxCommandEvent &aEvent) 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 updateBrowsedPathLabel()
void OnSearchTimer(wxTimerEvent &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, const wxString &aBrowsedTemplatesPath=wxEmptyString)
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.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().