67#include <wx/txtstrm.h>
68#include <wx/wfstream.h>
69#include <wx/mstream.h>
70#include <wx/settings.h>
98 auto strings = wxSplit(
id.GET(), L
'-');
100 for (
auto &
string : strings)
101 result +=
string.Capitalize();
126 return wxFileName( themeDir,
wxT(
"ThemeImageDefsAsCee.h") ).GetFullPath();
133 return wxFileName( dir, Str,
wxT(
"png") ).GetFullPath();
143#ifdef EXPERIMENTAL_EXTRA_THEME_RESOURCES
144 extern void RegisterExtraThemeResources();
145 RegisterExtraThemeResources();
194 std::map< EnumValueSymbol, const ThemeBase::RegisteredTheme& >;
204 const std::vector<unsigned char> &data )
206 , preferredSystemAppearance { preferredSystemAppearance }
221 auto &resources = *
mpSet;
224 const bool cbOkIfNotFound =
true;
240 LoadComponents( cbOkIfNotFound );
263 RotateImageInto( bmpRecordBesideDisabled, bmpRecordBelowDisabled,
false );
287 abs( From.Red() - To.Red() )
288 + abs( From.Green() - To.Green() )
289 + abs( From.Blue() - To.Blue() );
294 wxBitmap Bmp1( pXpm );
295 wxBitmap Bmp2( pMask );
304 wxASSERT( Bmp1.GetDepth()==-1 || Bmp1.GetDepth()==24);
305 wxASSERT( Bmp1.GetDepth()==-1 || Bmp2.GetDepth()==24);
308 nBytes = Bmp1.GetWidth() * Bmp1.GetHeight();
309 wxImage Img1( Bmp1.ConvertToImage());
310 wxImage Img2( Bmp2.ConvertToImage());
313 unsigned char *mk = Img2.GetData();
316 static_cast<unsigned char*
>(malloc( nBytes )) };
319 for(i=0;i<nBytes;i++)
325 Img1.SetAlpha( alpha.release() );
339 int &flags,
int &iIndex,
char const ** pXpm,
const wxString & Name )
341 wxBitmap Bmp( pXpm );
342 wxImage Img( Bmp.ConvertToImage() );
344 Img.SetMaskColour(0xDE, 0xDE, 0xDE);
355 int &flags,
int &iIndex,
const wxImage &
Image,
const wxString & Name )
357 auto &resources = *
mpSet;
367 TempImage.ConvertAlphaToMask();
368 resources.mBitmaps.push_back( wxBitmap( TempImage ) );
370 resources.mBitmaps.push_back( wxBitmap(
Image ) );
373 flags &= ~resFlagSkip;
374 auto index = resources.mBitmaps.size() - 1;
380 wxASSERT(allNames.insert(Name).second);
385 wxASSERT(iIndex == index);
392 int &iIndex,
const wxColour &Clr,
const wxString & Name )
394 auto &resources = *
mpSet;
395 resources.
mColours.push_back( Clr );
396 auto index = resources.mColours.size() - 1;
401 wxASSERT(allNames.insert(Name).second);
406 wxASSERT(iIndex == index);
412 : mxCacheWidth{ width }
500 size_t OnSysWrite(
const void *buffer,
size_t bufsize)
override;
510 bOk =
File.Open( Filename, wxFile::write );
513 File.Write( wxString::Format(
514 wxT(
"/// @file %s\r\n"), wxFileName(Filename).GetFullName()));
515 File.Write(
wxT(
"/// @brief This file was Auto-Generated.\r\n") );
517 File.Write(
wxT(
"/// It is included by Theme.cpp.\r\n") );
518 File.Write(
wxT(
"/// Only check this into Git if you've read and understood the guidelines!\r\n\r\n ") );
528 for(
int i=0;i<(int)bufsize;i++)
531 Temp = wxString::Format(
wxT(
"%i,"),(
int)(((
unsigned char*)buffer)[i]) );
568 XO(
"Themes written to:\n %s/*/%s.")
575 auto &resources = *
mpSet;
581 if( !ImageCache.HasAlpha() )
583 ImageCache.InitAlpha();
590 wxLogDebug(
wxT(
"<img src=\"ImageCache.png\" usemap=\"#map1\">" ));
591 wxLogDebug(
wxT(
"<map name=\"map1\">") );
595 for (
size_t i = 0; i < resources.mImages.size() ; ++i)
597 wxImage &SrcImage = resources.mImages[i];
601 context.GetNextPosition( SrcImage.GetWidth(), SrcImage.GetHeight());
602 ImageCache.SetRGB( context.Rect(), 0xf2, 0xb0, 0x27 );
605 context.mxPos + context.mBorderWidth,
606 context.myPos + context.mBorderWidth);
608 ImageCache.SetRGB( context.RectInner(), 1,1,1);
611 wxRect R( context.Rect() );
612 wxLogDebug(
wxT(
"<area title=\"Bitmap:%s\" shape=rect coords=\"%i,%i,%i,%i\">"),
614 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom() );
622 context.SetColourGroup();
623 for (
size_t i = 0; i < resources.mColours.size(); ++i)
626 wxColour c = resources.mColours[i];
627 ImageCache.SetRGB( context.Rect() , 0xf2, 0xb0, 0x27 );
628 ImageCache.SetRGB( context.RectInner() , c.Red(), c.Green(), c.Blue() );
635 ImageCache.SetAlpha( context.mxPos + x, context.myPos+y, 255);
640 wxRect R( context.Rect() );
641 wxLogDebug(
wxT(
"<area title=\"Colour:%s\" shape=rect coords=\"%i,%i,%i,%i\">"),
643 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom() );
651 int b = (j >> 8) | ((i>>4)&0xf0);
652 wxRect R( i,j, 1, 1);
653 ImageCache.SetRGB( R, r, g, b );
654 ImageCache.SetAlpha( i,j, 255);
659 wxLogDebug(
"</map>" );
673 if( wxFileExists( FileName ))
688 ImageCache.GetWidth()*4,
689 ImageCache.GetHeight()*4,
690 wxIMAGE_QUALITY_NEAREST );
692 if( !ImageCache.SaveFile( FileName, wxBITMAP_TYPE_PNG ))
695 XO(
"Audacity could not write file:\n %s.")
709 auto FileName = wxFileName{ dir,
name }.GetFullPath();
710 if( !OutStream.
OpenFile( FileName ))
713 XO(
"Audacity could not open file:\n %s\nfor writing.")
717 if( !ImageCache.SaveFile(OutStream, wxBITMAP_TYPE_PNG ) )
720 XO(
"Audacity could not write images to file:\n %s.")
740 auto &resources = *
mpSet;
746 wxFFile File( FileName,
wxT(
"wb") );
747 if( !File.IsOpened() )
750 File.Write(
wxT(
"<html>\r\n"));
751 File.Write(
wxT(
"<body bgcolor=\"303030\">\r\n"));
752 wxString Temp = wxString::Format(
wxT(
"<img src=\"ImageCache.png\" width=\"%i\" usemap=\"#map1\">\r\n" ),
ImageCacheWidth );
754 File.Write(
wxT(
"<map name=\"map1\">\r\n") );
756 for (
size_t i = 0; i < resources.mImages.size(); ++i)
758 wxImage &SrcImage = resources.mImages[i];
762 context.GetNextPosition( SrcImage.GetWidth(), SrcImage.GetHeight());
764 wxRect R( context.RectInner() );
765 File.Write( wxString::Format(
766 wxT(
"<area title=\"Bitmap:%s\" shape=rect coords=\"%i,%i,%i,%i\">\r\n"),
768 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom()) );
772 context.SetColourGroup();
773 for (
size_t i = 0; i < resources.mColours.size(); ++i)
777 wxRect R( context.RectInner() );
778 File.Write( wxString::Format(
wxT(
"<area title=\"Colour:%s\" shape=rect coords=\"%i,%i,%i,%i\">\r\n"),
780 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom()) );
782 File.Write(
wxT(
"</map>\r\n") );
783 File.Write(
wxT(
"</body>\r\n"));
784 File.Write(
wxT(
"</html>\r\n"));
794 auto &resources = *
mpSet;
798 if( !File.IsOpened() )
801 for (
size_t i = 0; i < resources.mImages.size(); ++i)
803 wxImage &SrcImage = resources.mImages[i];
808 int t = (int)PrevFlags;
810 if( t==0 ) Temp =
wxT(
" resFlagNone ");
815 Temp.Replace(
wxT(
" "),
wxT(
" | ") );
817 File.Write( wxString::Format(
wxT(
"\r\n SET_THEME_FLAGS( %s );\r\n"),
820 File.Write( wxString::Format(
821 wxT(
" DEFINE_IMAGE( bmp%s, wxImage( %i, %i ), wxT(\"%s\"));\r\n"),
824 SrcImage.GetHeight(),
844 auto &resources = *
mpSet;
856 if( type.
empty() || type ==
"custom" )
862 const auto &FileName =
864 if( !wxFileExists( FileName ))
869 XO(
"Audacity could not find file:\n %s.\nTheme not loaded.")
873 if( !ImageCache.LoadFile( FileName, wxBITMAP_TYPE_PNG ))
877 XO(
"Audacity could not load file:\n %s.\nBad png format perhaps?")
886 const unsigned char * pImage =
nullptr;
888 auto iter = lookup.find({type, {}});
889 if (
const auto end = lookup.end(); iter ==
end) {
890 iter = lookup.find({
"classic", {}});
891 wxASSERT(iter !=
end);
901 pImage = iter->second.data.data();
903 wxMemoryInputStream InternalStream( pImage,
ImageSize );
905 if( !ImageCache.LoadFile( InternalStream, wxBITMAP_TYPE_PNG ))
913"Audacity could not read its default theme.\nPlease report the problem."));
921 int h = ImageCache.GetHeight() * ((1.0*
ImageCacheWidth)/ImageCache.GetWidth());
926 for (
size_t i = 0; i < resources.mImages.size(); ++i)
932 context.GetNextPosition(
Image.GetWidth(),
Image.GetHeight() );
933 wxRect R = context.RectInner();
936 resources.mBitmaps[i] = wxBitmap(
Image);
939 if( !ImageCache.HasAlpha() )
940 ImageCache.InitAlpha();
945 context.SetColourGroup();
947 for (
size_t i = 0; i < resources.mColours.size(); ++i)
950 context.RectMid( x, y );
951 wxRect R = context.RectInner();
955 if( ImageCache.GetAlpha(x,y ) > 128 )
957 TempColour = wxColour(
958 ImageCache.GetRed( x,y),
959 ImageCache.GetGreen( x,y),
960 ImageCache.GetBlue(x,y));
965 if( TempColour != wxColour(1,1,1) )
966 resources.mColours[i] = TempColour;
982 auto &resources = *
mpSet;
985 if( !wxDirExists( dir ))
992 for (
size_t i = 0; i < resources.mImages.size(); ++i)
997 if( wxFileExists( FileName ))
999 if( !resources.mImages[i].LoadFile( FileName, wxBITMAP_TYPE_PNG ))
1004"Audacity could not load file:\n %s.\nBad png format perhaps?")
1012 if( ! resources.mImages[i].HasAlpha() )
1015 resources.mImages[i].InitAlpha();
1017 resources.mBitmaps[i] = wxBitmap( resources.mImages[i] );
1025 const auto fName = wxFileName{ dir,
ColorFileName }.GetFullPath();
1026 wxTextFile file{ fName };
1028 if (!file.IsOpened())
1033 static const std::wregex expr{
1034 LR
"(^ *([_[:alnum:]]+).*#([0-9a-fA-F]{6});)" };
1037 std::unordered_set<wxString>
names;
1038 for (
auto str = file.GetFirstLine();
1039 !file.Eof();
str = file.GetNextLine()) {
1040 if (std::wsmatch match;
1041 regex_search(
str.ToStdWstring(), match, expr )) {
1042 const wxString
name{ match[1] };
1046 else if (
const auto iter = std::find(
begin,
end,
name);
1052 static_cast<unsigned>(stoi(match[2],
nullptr, 16));
1053 unsigned char rr = (rrggbb >> 16) & 0xffu;
1054 unsigned char gg = (rrggbb >> 8) & 0xffu;
1055 unsigned char bb = rrggbb & 0xffu;
1056 resources.mColours[iter -
begin] = { rr, gg, bb };
1069"None of the expected theme component files\n were found in:\n %s.")
1089 auto &resources = *
mpSet;
1092 if( !wxDirExists( dir ))
1100 wxMkDir( dir.fn_str() );
1102 wxMkDir( dir.fn_str(), 0700 );
1104 if( !wxDirExists( dir ))
1107 XO(
"Could not create directory:\n %s")
1115 for (
size_t i = 0; i < resources.mImages.size(); ++i)
1120 if( wxFileExists( FileName ))
1138"Some required files in:\n %s\nwere already present. Overwrite?")
1143 if (result == MessageBoxResult::No)
1147 for (
size_t i = 0; i < resources.mImages.size(); ++i)
1152 if( !resources.mImages[i].SaveFile( FileName, wxBITMAP_TYPE_PNG ))
1155 XO(
"Audacity could not save file:\n %s")
1164 const auto fName = wxFileName{ dir,
ColorFileName }.GetFullPath();
1165 wxFileOutputStream ffStream{ fName };
1166 if (!ffStream.IsOk()) {
1170 wxTextOutputStream ss{ffStream};
1172 ss <<
"." <<
id.GET() <<
"Theme {\n";
1173 for (
size_t i = 0; i < resources.mColours.size(); ++i) {
1174 const auto &colour = resources.mColours[i];
1179 << wxString::Format(
"%02x", colour.Red())
1180 << wxString::Format(
"%02x", colour.Green())
1181 << wxString::Format(
"%02x", colour.Blue())
1201 XO(
"Themes as Cee code written to:\n %s/*%s.")
1208 wxImage image( Bmp.ConvertToImage() );
1215 while (iter !=
end) {
1216 if (
mpSet == &iter->second)
1219 iter =
mSets.erase(iter);
1225 wxASSERT( iIndex >= 0 );
1226 auto &resources = *
mpSet;
1228 return resources.mColours[iIndex];
1233 wxASSERT( iIndex >= 0 );
1234 Brush.SetColour(
Colour( iIndex ));
1239 wxASSERT( iIndex >= 0 );
1240 Pen.SetColour(
Colour( iIndex ));
1245 wxASSERT( iIndex >= 0 );
1246 auto &resources = *
mpSet;
1248 return resources.mBitmaps[iIndex];
1253 wxASSERT( iIndex >= 0 );
1254 auto &resources = *
mpSet;
1256 return resources.mImages[iIndex];
1260 wxASSERT( iIndex >= 0 );
1261 auto &resources = *
mpSet;
1264 return wxSize(
Image.GetWidth(),
Image.GetHeight());
1270 Image( iIndex ) = *pImage;
1271 Bitmap( iIndex ) = wxBitmap( *pImage );
1277 wxImage img2 = img.Rotate90( bClockwise );
1284 std::vector<EnumValueSymbol> symbols;
1288 symbols.emplace_back(symbol);
1294 "classic",
"light",
"dark",
"high-contrast",
"modern"
1300 std::stable_sort( symbols.begin(), symbols.end(),
1301 [](
auto &a,
auto &b){ return index(a) < index(b); } );
1304 symbols.emplace_back(
1306 "custom",
XO(
"Custom")
1312 constexpr int defaultTheme = 1;
1315 wxT(
"/GUI/Theme"), symbols(), defaultTheme
Toolkit-neutral facade for basic user interface services.
static ThemeBase::RegisteredTheme theme
wxImage GetSubImageWithAlpha(const wxImage &Src, const wxRect &rect)
void PasteSubImage(wxImage *background, wxImage *foreground, int xoff, int yoff)
std::unique_ptr< wxImage > ChangeImageColour(wxImage *srcImage, wxColour &dstColour)
std::unique_ptr< Character[], freer > MallocString
const int ImageCacheWidth
std::map< EnumValueSymbol, const ThemeBase::RegisteredTheme & > ThemeCacheLookup
static ThemeCacheLookup & GetThemeCacheLookup()
const int ImageCacheHeight
THEME_API ChoiceSetting & GUITheme()
PreferredSystemAppearance
A system theme, that matches selected theme best (only works on macOS with builtin themes).
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
A cursor for iterating the theme bitmap.
void SetNewGroup(int iGroupSize)
void RectMid(int &x, int &y)
void GetNextPosition(int xSize, int ySize)
An explicitly nonlocalized string, not meant for the user to see.
CallbackReturn Publish(const ThemeChangeMessage &message)
Send a message to connected callbacks.
Helper class based on wxOutputStream used to get a png file in text format.
size_t OnSysWrite(const void *buffer, size_t bufsize) override
int OpenFile(const FilePath &Filename)
Opens the file and also adds a standard comment at the start of it.
virtual ~SourceOutputStream()
Destructor. We close our text stream in here.
void SwitchTheme(teThemeType Theme)
bool SaveOneThemeComponents(teThemeType id)
void LoadTheme(teThemeType Theme)
void RegisterImage(NameSet &allNames, int &flags, int &iIndex, char const **pXpm, const wxString &Name)
wxColour & Colour(int iIndex)
void SetBrushColour(wxBrush &Brush, int iIndex)
void DeleteUnusedThemes()
wxImage MakeImageWithAlpha(wxBitmap &Bmp)
wxImage & Image(int iIndex)
wxBitmap & Bitmap(int iIndex)
void ReplaceImage(int iIndex, wxImage *pImage)
void RotateImageInto(int iTo, int iFrom, bool bClockwise)
wxImage MaskedImage(char const **pXpm, char const **pMask)
int ColourDistance(wxColour &From, wxColour &To)
bool CreateOneImageCache(teThemeType id, bool bBinarySave)
void WriteOneImageMap(teThemeType id)
teThemeType GetFallbackThemeType()
void LoadOneThemeComponents(teThemeType id, bool bOkIfNotFound=false)
wxArrayString mBitmapNames
void SetPenColour(wxPen &Pen, int iIndex)
std::map< Identifier, ThemeSet > mSets
std::unordered_set< wxString > NameSet
wxArrayString mColourNames
PreferredSystemAppearance mPreferredSystemAppearance
bool ReadImageCache(teThemeType type={}, bool bOkIfNotFound=false)
std::vector< int > mBitmapFlags
void SetFilePath(const FilePath &path)
virtual void EnsureInitialised()=0
wxSize ImageSize(int iIndex)
static bool LoadPreferredTheme()
void WriteImageDefs()
Writes a series of Macro definitions that can be used in the include file.
void RegisterColour(NameSet &allNames, int &iIndex, const wxColour &Clr, const wxString &Name)
void SaveThemeComponents()
void RecolourBitmap(int iIndex, wxColour From, wxColour To)
void LoadThemeComponents(bool bOkIfNotFound=false)
Based on ThemeBase, Theme manages image and icon resources.
void EnsureInitialised() override
void RegisterImagesAndColours()
Holds a msgid for the translation catalog; may also bind format arguments.
TranslatableString & Format(Args &&...args) &
Capture variadic format arguments (by copy) when there is no plural.
Set a variable temporarily in a scope.
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
FILES_API wxString MkDir(const wxString &Str)
FILES_API FilePath DataDir()
Audacity user data directory.
FilePath ThemeComponent(const wxString &dir, const wxString &Str)
constexpr auto ThemeCacheFileName
FilePath ThemeSubdir(const FilePath &themeDir, Identifier id)
Has the side-effect of ensuring existence of the directory.
constexpr auto ImageMapFileName
FilePath ThemeImageDefsAsCee(const FilePath &themeDir)
constexpr auto ImageCacheFileName
constexpr auto ColorFileName
wxString ThemeFilePrefix(teThemeType id)
FilePath ThemeComponentsDir(const FilePath &themeDir, Identifier id)
Has the side-effect of ensuring existence of the directory.
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
MessageBoxOptions && ButtonStyle(Button style) &&
const EnumValueSymbol symbol
RegisteredTheme(EnumValueSymbol symbol, PreferredSystemAppearance preferredSystemAppearance, const std::vector< unsigned char > &data)
std::vector< wxImage > mImages
std::vector< wxColour > mColours