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 ) ) );
164#if wxCHECK_VERSION( 3, 3, 2 )
180 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
181 m_staticTitle->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
182 m_staticDescription->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
190 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
191 m_staticTitle->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
192 m_staticDescription->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
200 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
201 m_staticTitle->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
202 m_staticDescription->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
216 const std::vector<int> c_bitmapSizes = { 48, 64, 96, 128 };
218 wxBitmapBundle bundle;
219 wxVector<wxBitmap> bitmaps;
220 wxBitmap* icon = aTemplate->
GetIcon();
222 if( icon && icon->IsOk() )
227 wxSize defSize = bundle.GetDefaultSize();
229 for(
const int genSize : c_bitmapSizes )
231 double scale = std::min( (
double) genSize / defSize.GetWidth(),
232 (
double) genSize / defSize.GetHeight() );
234 wxSize scaledSize( wxRound( defSize.x *
scale ),
235 wxRound( defSize.y *
scale ) );
237 wxBitmap scaled = bundle.GetBitmap( scaledSize );
238 scaled.SetScaleFactor( 1.0 );
240 bitmaps.push_back( scaled );
243 m_bitmapIcon->SetBitmap( wxBitmapBundle::FromBitmaps( bitmaps ) );
252 wxString displayDesc = aDescription;
254 if( displayDesc.Length() > 120 )
255 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
266#if !wxCHECK_VERSION( 3, 3, 2 )
268 int wrapWidth = GetClientSize().GetWidth() - 48 - 20;
270 if( wrapWidth > 100 )
276 if( displayDesc.Length() > 120 )
277 displayDesc = displayDesc.Left( 120 ) + wxS(
"..." );
311 menu.Append( wxID_EDIT,
_(
"Edit Template" ) );
312 menu.Append( wxID_COPY,
_(
"Duplicate Template" ) );
314 menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
315 [
this]( wxCommandEvent& evt )
317 if( evt.GetId() == wxID_EDIT )
319 else if( evt.GetId() == wxID_COPY )
333 templatePath.RemoveLastDir();
335 wxDir dir( templatePath.GetPath() );
337 if( !dir.IsOpened() )
344 bool found = dir.GetFirst( &filename,
"*.kicad_pro", wxDIR_FILES );
352 wxFileName projectFile( templatePath.GetPath(), filename );
354 m_dialog->SetProjectToEdit( projectFile.GetFullPath() );
366 templatePath.RemoveLastDir();
367 wxString srcTemplatePath = templatePath.GetPath();
370 wxTextEntryDialog nameDlg(
m_dialog,
371 _(
"Enter name for the new template:" ),
372 _(
"Duplicate Template" ),
373 srcTemplateName +
_(
"_copy" ) );
375 if( nameDlg.ShowModal() != wxID_OK )
378 wxString newTemplateName = nameDlg.GetValue();
380 if( newTemplateName.IsEmpty() )
386 wxString userTemplatesPath =
m_dialog->GetUserTemplatesPath();
388 if( userTemplatesPath.IsEmpty() )
394 wxFileName destPath( userTemplatesPath, wxEmptyString );
395 destPath.AppendDir( newTemplateName );
396 wxString newTemplatePath = destPath.GetPath();
398 if( destPath.DirExists() )
401 wxString::Format(
_(
"Directory '%s' already exists." ),
406 if( !destPath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
409 wxString::Format(
_(
"Could not create directory '%s'." ),
414 wxDir sourceDir( srcTemplatePath );
416 if( !sourceDir.IsOpened() )
423 newTemplatePath, newTemplateName );
425 sourceDir.Traverse( traverser );
433 wxFileName metaHtmlFile( newTemplatePath,
"info.html" );
434 metaHtmlFile.AppendDir(
"meta" );
436 if( metaHtmlFile.FileExists() )
438 wxTextFile htmlFile( metaHtmlFile.GetFullPath() );
440 if( htmlFile.Open() )
442 bool modified =
false;
444 for(
size_t i = 0; i < htmlFile.GetLineCount(); i++ )
446 wxString line = htmlFile.GetLine( i );
448 if( line.Contains( wxT(
"<title>" ) ) && line.Contains( wxT(
"</title>" ) ) )
450 int titleStart = line.Find( wxT(
"<title>" ) );
451 int titleEnd = line.Find( wxT(
"</title>" ) );
453 if( titleStart != wxNOT_FOUND && titleEnd != wxNOT_FOUND && titleEnd > titleStart )
455 wxString before = line.Left( titleStart + 7 );
456 wxString after = line.Mid( titleEnd );
457 line = before + newTemplateName + after;
480 const wxString& aUserTemplatesPath,
481 const wxString& aSystemTemplatesPath,
482 const std::vector<wxString>& aRecentTemplates ) :
500 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
523 if( filterChoice >= 0 && filterChoice <
static_cast<int>(
m_filterChoice->GetCount() ) )
606 wxFileName templateDir;
607 templateDir.AssignDir(
path );
609 if( !templateDir.DirExists() )
615 wxBitmap* icon = templ.
GetIcon();
619 if( icon && icon->IsOk() )
621 wxImage img = icon->ConvertToImage();
622 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
623 scaledIcon = wxBitmap( img );
628 wxImage img = scaledIcon.ConvertToImage();
629 img.Rescale( 16, 16, wxIMAGE_QUALITY_HIGH );
630 scaledIcon = wxBitmap( img );
633 wxString displayTitle = title ? *title : templateDir.GetDirs().Last();
636 displayTitle, scaledIcon );
637 m_sizerMRU->Add( mruWidget, 0, wxEXPAND | wxBOTTOM, 2 );
661 auto scanDirectory = [
this](
const wxString& aPath,
bool aIsUser )
663 if( aPath.IsEmpty() )
668 if( !dir.Open( aPath ) )
673 if( dir.HasSubDirs(
"meta" ) )
675 auto templ = std::make_unique<PROJECT_TEMPLATE>( aPath );
676 wxFileName htmlFile = templ->GetHtmlFile();
690 bool cont = dir.GetFirst( &subName, wxEmptyString, wxDIR_DIRS );
694 wxString subFull = aPath + wxFileName::GetPathSeparator() + subName;
697 if( subDir.Open( subFull ) )
699 auto templ = std::make_unique<PROJECT_TEMPLATE>( subFull );
700 wxFileName htmlFile = templ->GetHtmlFile();
712 cont = dir.GetNext( &subName );
724 wxString titleA = *a->GetTemplate()->GetTitle();
725 wxString titleB = *b->GetTemplate()->GetTitle();
727 if( titleA.CmpNoCase(
"default" ) == 0 )
730 if( titleB.CmpNoCase(
"default" ) == 0 )
733 return titleA.CmpNoCase( titleB ) < 0;
763 bool matchesFilter =
true;
765 if( filterChoice == 1 && !widget->IsUserTemplate() )
766 matchesFilter =
false;
767 else if( filterChoice == 2 && widget->IsUserTemplate() )
768 matchesFilter =
false;
770 bool matchesSearch =
true;
772 if( !searchText.IsEmpty() )
774 wxString title = widget->GetTemplate()->GetTitle()->Lower();
775 wxString description = widget->GetDescription().Lower();
777 matchesSearch = title.Contains( searchText ) || description.Contains( searchText );
780 widget->Show( matchesFilter && matchesSearch );
822 wxString selectedPath;
827 htmlFile.RemoveLastDir();
828 selectedPath = htmlFile.GetPath();
837 if( !selectedPath.IsEmpty() )
880 if( widget->GetTemplate() )
882 wxFileName htmlFile = widget->GetTemplate()->GetHtmlFile();
883 htmlFile.RemoveLastDir();
885 if( htmlFile.GetPath() == aPath )
887 if( aKeepMRUVisible )
896 widget->SelectWithoutStateChange();
901 widget->GetPosition(
nullptr, &y );
929 wxDefaultSize, wxTAB_TRAVERSAL );
931 wxBoxSizer* sizer =
new wxBoxSizer( wxVERTICAL );
955 if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
958 wxString url = wxFileName::FileNameToURL( htmlFile );
997 if( userDir.DirExists() )
1006 wxFileName systemDir;
1009 if( systemDir.DirExists() )
1040 m_scrolledTemplates->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1045 if( widget->IsSelected() )
1047 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
1051 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1060 widget->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1076 if( clientWidth <= 0 )
1082 wxSizeEvent sizeEvt( wxSize( clientWidth, -1 ) );
1083 widget->GetEventHandler()->ProcessEvent( sizeEvt );
1102 wxString script = wxS(
1104 " document.body.classList.add('kicad-dark');"
1105 " var style = document.createElement('style');"
1106 " style.textContent = '"
1108 " --bg-primary: #FFFFFF;"
1109 " --bg-secondary: #F3F3F3;"
1110 " --bg-elevated: #FFFFFF;"
1111 " --text-primary: #545454;"
1112 " --text-secondary: #848484;"
1113 " --accent: #1A81C4;"
1114 " --accent-subtle: rgba(26, 129, 196, 0.08);"
1115 " --border: #E0E0E0;"
1116 " --shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);"
1118 " body.kicad-dark {"
1119 " --bg-primary: #1E1E1E;"
1120 " --bg-secondary: #2D2D2D;"
1121 " --bg-elevated: #333333;"
1122 " --text-primary: #DED3DD;"
1123 " --text-secondary: #848484;"
1124 " --accent: #42B8EB;"
1125 " --accent-subtle: rgba(66, 184, 235, 0.1);"
1126 " --border: #404040;"
1127 " --shadow: 0 1px 3px rgba(0,0,0,0.2), 0 1px 2px rgba(0,0,0,0.15);"
1129 " body.kicad-dark {"
1130 " background: var(--bg-primary) !important;"
1131 " color: var(--text-primary) !important;"
1134 " document.head.appendChild(style);"
1144 wxString selectedPath;
1149 htmlFile.RemoveLastDir();
1150 selectedPath = htmlFile.GetPath();
1159 if( !selectedPath.IsEmpty() )
1166 if( !aHtmlFile.FileExists() || !aHtmlFile.IsFileReadable() )
1167 return wxEmptyString;
1169 wxTextFile file( aHtmlFile.GetFullPath() );
1172 return wxEmptyString;
1176 for( wxString line = file.GetFirstLine(); !file.Eof(); line = file.GetNextLine() )
1178 content += line + wxT(
" " );
1184 wxRegEx reMetaDesc( wxT(
"<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']*)[\"']" ),
1187 if( reMetaDesc.Matches( content ) )
1188 return reMetaDesc.GetMatch( content, 1 );
1191 wxRegEx reParagraph( wxT(
"<p[^>]*>(.*?)</p>" ), wxRE_ICASE );
1193 if( reParagraph.Matches( content ) )
1195 wxString desc = reParagraph.GetMatch( content, 1 );
1198 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1199 reTags.ReplaceAll( &desc, wxT(
" " ) );
1202 desc.Replace( wxT(
" " ), wxT(
" " ) );
1203 desc.Replace( wxT(
"&" ), wxT(
"&" ) );
1204 desc.Replace( wxT(
"<" ), wxT(
"<" ) );
1205 desc.Replace( wxT(
">" ), wxT(
">" ) );
1206 desc.Replace( wxT(
""" ), wxT(
"\"" ) );
1209 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1210 reWhitespace.ReplaceAll( &desc, wxT(
" " ) );
1212 desc = desc.Trim().Trim(
false );
1214 if( !desc.IsEmpty() )
1219 wxRegEx reBody( wxT(
"<body[^>]*>(.*)</body>" ), wxRE_ICASE );
1221 if( reBody.Matches( content ) )
1223 wxString body = reBody.GetMatch( content, 1 );
1226 wxRegEx reTags( wxT(
"<[^>]*>" ) );
1227 reTags.ReplaceAll( &body, wxT(
" " ) );
1230 body.Replace( wxT(
" " ), wxT(
" " ) );
1231 body.Replace( wxT(
"&" ), wxT(
"&" ) );
1232 body.Replace( wxT(
"<" ), wxT(
"<" ) );
1233 body.Replace( wxT(
">" ), wxT(
">" ) );
1234 body.Replace( wxT(
""" ), wxT(
"\"" ) );
1237 wxRegEx reWhitespace( wxT(
"\\s+" ) );
1238 reWhitespace.ReplaceAll( &body, wxT(
" " ) );
1240 body = body.Trim().Trim(
false );
1243 if( body.Length() > 250 )
1244 return body.Left( 250 ) + wxS(
"..." );
1249 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.
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.