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,
293 m_category == CATEGORY::USER ?
_(
"Edit Template" ) :
_(
"Open Template (Read-Only)" ) );
294 menu.Append( wxID_OPEN,
_(
"Open Template Folder" ) );
295 menu.Append( wxID_COPY,
_(
"Duplicate Template" ) );
297 menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
298 [
this]( wxCommandEvent& evt )
300 if( evt.GetId() == wxID_EDIT )
302 else if( evt.GetId() == wxID_OPEN )
304 else if( evt.GetId() == wxID_COPY )
318 templatePath.RemoveLastDir();
320 wxDir dir( templatePath.GetPath() );
322 if( !dir.IsOpened() )
329 bool found = dir.GetFirst( &filename,
"*.kicad_pro", wxDIR_FILES );
337 wxFileName projectFile( templatePath.GetPath(), filename );
339 m_dialog->SetProjectToEdit( projectFile.GetFullPath() );
351 templatePath.RemoveLastDir();
353 if( !wxLaunchDefaultApplication( templatePath.GetPath() ) )
354 DisplayError(
this, wxString::Format(
_(
"Failed to open '%s'." ), templatePath.GetPath() ) );
364 templatePath.RemoveLastDir();
365 wxString srcTemplatePath = templatePath.GetPath();
368 wxTextEntryDialog nameDlg(
m_dialog,
_(
"Enter name for the new template:" ),
_(
"Duplicate Template" ),
369 srcTemplateName +
_(
"_copy" ) );
371 if( nameDlg.ShowModal() != wxID_OK )
374 wxString newTemplateName = nameDlg.GetValue();
376 if( newTemplateName.IsEmpty() )
382 wxString userTemplatesPath =
m_dialog->GetUserTemplatesPath();
384 if( userTemplatesPath.IsEmpty() )
390 wxFileName destPath( userTemplatesPath, wxEmptyString );
391 destPath.AppendDir( newTemplateName );
392 wxString newTemplatePath = destPath.GetPath();
394 if( destPath.DirExists() )
400 if( !destPath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
406 wxDir sourceDir( srcTemplatePath );
408 if( !sourceDir.IsOpened() )
414 PROJECT_TREE_TRAVERSER traverser(
nullptr, srcTemplatePath, srcTemplateName, newTemplatePath, newTemplateName );
416 sourceDir.Traverse( traverser );
424 wxFileName metaHtmlFile( newTemplatePath,
"info.html" );
425 metaHtmlFile.AppendDir(
"meta" );
427 if( metaHtmlFile.FileExists() )
429 wxTextFile htmlFile( metaHtmlFile.GetFullPath() );
431 if( htmlFile.Open() )
433 bool modified =
false;
435 for(
size_t i = 0; i < htmlFile.GetLineCount(); i++ )
437 wxString line = htmlFile.GetLine( i );
439 if( line.Contains( wxT(
"<title>" ) ) && line.Contains( wxT(
"</title>" ) ) )
441 int titleStart = line.Find( wxT(
"<title>" ) );
442 int titleEnd = line.Find( wxT(
"</title>" ) );
444 if( titleStart != wxNOT_FOUND && titleEnd != wxNOT_FOUND && titleEnd > titleStart )
446 wxString before = line.Left( titleStart + 7 );
447 wxString after = line.Mid( titleEnd );
448 line = before + newTemplateName + after;
469 [dlg, newTemplatePath]()
471 DisplayInfoMessage( dlg, wxString::Format(
_(
"Template duplicated successfully to '%s'." ),
479 const wxSize& aSize,
const wxString& aUserTemplatesPath,
480 const wxString& aSystemTemplatesPath,
481 const wxString& aDefaultTemplatesPath,
482 const std::vector<wxString>& aRecentTemplates,
483 const wxString& aBrowsedTemplatesPath ) :
508 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
534 if( filterChoice >= 0 && filterChoice <
static_cast<int>(
m_filterChoice->GetCount() ) )
591 int width =
m_splitter->GetClientSize().GetWidth();
592 sashPos = width > 0 ? width / 3 : FromDIP( 300 );
648 wxFileName templateDir;
649 templateDir.AssignDir(
path );
651 if( !templateDir.DirExists() )
657 wxBitmap* icon = templ.
GetIcon();
661 if( icon && icon->IsOk() )
663 wxImage img = icon->ConvertToImage();
664 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
665 scaledIcon = wxBitmap( img );
670 wxImage img = scaledIcon.ConvertToImage();
671 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
672 scaledIcon = wxBitmap( img );
675 wxString displayTitle = title ? *title : templateDir.GetDirs().Last();
679 m_sizerMRU->Add( mruWidget, 0, wxEXPAND | wxBOTTOM, 2 );
706 if( aPath.IsEmpty() )
711 if( !dir.Open( aPath ) )
715 static_cast<int>( aCategory ) );
717 if( dir.HasSubDirs(
"meta" ) )
719 auto templ = std::make_unique<PROJECT_TEMPLATE>( aPath );
720 wxFileName htmlFile = templ->GetHtmlFile();
734 bool cont = dir.GetFirst( &subName, wxEmptyString, wxDIR_DIRS );
738 wxString subFull = aPath + wxFileName::GetPathSeparator() + subName;
744 metaTest.AssignDir( subFull );
747 if( metaTest.DirExists() )
749 auto templ = std::make_unique<PROJECT_TEMPLATE>( subFull );
750 wxFileName htmlFile = templ->GetHtmlFile();
751 wxString description = templ->GetError().IsEmpty() ?
ExtractDescription( htmlFile ) : templ->GetError();
763 cont = dir.GetNext( &subName );
783 wxString titleA = *a->GetTemplate()->GetTitle();
784 wxString titleB = *b->GetTemplate()->GetTitle();
785 int cmp = titleA.CmpNoCase( titleB );
790 if( titleA.CmpNoCase(
"default" ) == 0 )
792 else if( titleB.CmpNoCase(
"default" ) == 0 )
824 bool matchesFilter =
true;
828 if( filterChoice == 1 && cat != TEMPLATE_WIDGET::CATEGORY::USER )
829 matchesFilter =
false;
830 else if( filterChoice == 2 && cat != TEMPLATE_WIDGET::CATEGORY::SYSTEM )
831 matchesFilter =
false;
832 else if( filterChoice == 3 && cat != TEMPLATE_WIDGET::CATEGORY::BROWSED )
833 matchesFilter =
false;
835 bool matchesSearch =
true;
837 if( !searchText.IsEmpty() )
839 wxString title = widget->GetTemplate()->GetTitle()->Lower();
840 wxString description = widget->GetDescription().Lower();
842 matchesSearch = title.Contains( searchText ) || description.Contains( searchText );
845 widget->Show( matchesFilter && matchesSearch );
887 wxString selectedPath;
892 htmlFile.RemoveLastDir();
893 selectedPath = htmlFile.GetPath();
902 if( !selectedPath.IsEmpty() )
945 if( widget->GetTemplate() )
947 wxFileName htmlFile = widget->GetTemplate()->GetHtmlFile();
948 htmlFile.RemoveLastDir();
950 if( htmlFile.GetPath() == aPath )
952 if( aKeepMRUVisible )
961 widget->SelectWithoutStateChange();
966 widget->GetPosition(
nullptr, &y );
996 wxBoxSizer* sizer =
new wxBoxSizer( wxVERTICAL );
1020 if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
1023 wxString url = wxFileName::FileNameToURL( htmlFile );
1058 [
this](
const wxString& aPath,
const char* aLabel )
1060 if( aPath.IsEmpty() )
1064 dir.AssignDir( aPath );
1066 if( dir.DirExists() )
1088 [](
const wxString& a,
const wxString& b )
1090 if( a.IsEmpty() || b.IsEmpty() )
1101 return lhs.SameAs( rhs );
1132 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1137 if( widget->IsSelected() )
1138 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
1140 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1148 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1164 if( clientWidth <= 0 )
1170 wxSizeEvent sizeEvt( wxSize( clientWidth, -1 ) );
1171 widget->GetEventHandler()->ProcessEvent( sizeEvt );
1189 wxString script = wxString::Format( wxS( R
"(
1192 var style = document.createElement( 'style' );
1193 style.textContent = `
1196 document.head.appendChild( style );
1198 )" ), GetCommonStyles() );
1200#if !defined( __MINGW32__ )
1210 wxString selectedPath;
1215 htmlFile.RemoveLastDir();
1216 selectedPath = htmlFile.GetPath();
1225 if( !selectedPath.IsEmpty() )
1232 if( !aHtmlFile.FileExists() || !aHtmlFile.IsFileReadable() )
1233 return wxEmptyString;
1235 wxTextFile file( aHtmlFile.GetFullPath() );
1238 return wxEmptyString;
1242 for( wxString line = file.GetFirstLine(); !file.Eof(); line = file.GetNextLine() )
1244 content += line + wxT(
" " );
1250 wxRegEx reMetaDesc( wxT(
"<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']*)[\"']" ),
1253 if( reMetaDesc.Matches( content ) )
1254 return reMetaDesc.GetMatch( content, 1 );
1257 wxRegEx reParagraph( wxT(
"<p[^>]*>(.*?)</p>" ), wxRE_ICASE );
1259 if( reParagraph.Matches( content ) )
1261 wxString desc = reParagraph.GetMatch( content, 1 );
1264 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1265 reTags.ReplaceAll( &desc, wxT(
" " ) );
1268 desc.Replace( wxT(
" " ), wxT(
" " ) );
1269 desc.Replace( wxT(
"&" ), wxT(
"&" ) );
1270 desc.Replace( wxT(
"<" ), wxT(
"<" ) );
1271 desc.Replace( wxT(
">" ), wxT(
">" ) );
1272 desc.Replace( wxT(
""" ), wxT(
"\"" ) );
1275 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1276 reWhitespace.ReplaceAll( &desc, wxT(
" " ) );
1278 desc = desc.Trim().Trim(
false );
1280 if( !desc.IsEmpty() )
1285 wxRegEx reBody( wxT(
"<body[^>]*>(.*)</body>" ), wxRE_ICASE );
1287 if( reBody.Matches( content ) )
1289 wxString body = reBody.GetMatch( content, 1 );
1292 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1293 reTags.ReplaceAll( &body, wxT(
" " ) );
1296 body.Replace( wxT(
" " ), wxT(
" " ) );
1297 body.Replace( wxT(
"&" ), wxT(
"&" ) );
1298 body.Replace( wxT(
"<" ), wxT(
"<" ) );
1299 body.Replace( wxT(
">" ), wxT(
">" ) );
1300 body.Replace( wxT(
""" ), wxT(
"\"" ) );
1303 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1304 reWhitespace.ReplaceAll( &body, wxT(
" " ) );
1306 body = body.Trim().Trim(
false );
1309 if( body.Length() > 250 )
1310 return body.Left( 250 ) + wxS(
"..." );
1315 return wxEmptyString;
1329 if( startPath.IsEmpty() )
1332 wxDirDialog dirDialog(
this,
_(
"Select Templates Directory" ), startPath,
1333 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
1335 if( dirDialog.ShowModal() != wxID_OK )
1338 wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() );
1349 if( filterChoice != 0 && filterChoice != 3 )
1388 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().