Audacity 3.2.0
Theme.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Theme.cpp
6
7 James Crook
8
9 Audacity is free software.
10 This file is licensed under the wxWidgets license, see License.txt
11
12********************************************************************//*****************************************************************//*****************************************************************//*****************************************************************//*****************************************************************/
55
56#include "Theme.h"
57
58
59
60#include <map>
61
62#include <wx/wxprec.h>
63#include <wx/brush.h>
64#include <wx/pen.h>
65#include <wx/file.h>
66#include <wx/ffile.h>
67#include <wx/txtstrm.h>
68#include <wx/wfstream.h>
69#include <wx/mstream.h>
70#include <wx/settings.h>
71#include <regex>
72
73#include "AllThemeResources.h"
74#include "BasicUI.h"
75#include "FileNames.h"
76#include "Prefs.h"
77#include "ImageManipulation.h"
78#include "Internat.h"
79#include "MemoryX.h"
80
81// theTheme is a global variable.
82THEME_API Theme theTheme;
83
85{
86}
87
89{
90}
91
92namespace {
93// Side of a square painted in one of the theme colours in image caches
94constexpr int iColSize = 10;
95
97{
98 auto strings = wxSplit(id.GET(), L'-');
99 wxString result;
100 for (auto &string : strings)
101 result += string.Capitalize();
102 return result;
103}
104
107{
108 return FileNames::MkDir(
109 wxFileName( FileNames::MkDir(themeDir), id.GET() ).GetFullPath() );
110}
111
114{
115 return FileNames::MkDir( wxFileName( ThemeSubdir(themeDir, id), wxT("Components") ).GetFullPath() );
116}
117
118constexpr auto ImageCacheFileName = L"ImageCache.png";
119
120constexpr auto ImageMapFileName = L"ImageCache.htm";
121
122constexpr auto ColorFileName = L"Colors.txt";
123
125{
126 return wxFileName( themeDir, wxT("ThemeImageDefsAsCee.h") ).GetFullPath();
127}
128
129constexpr auto ThemeCacheFileName = L"ThemeAsCeeCode.h";
130
131FilePath ThemeComponent(const wxString &dir, const wxString &Str)
132{
133 return wxFileName( dir, Str, wxT("png") ).GetFullPath();
134}
135}
136
138{
139 if ( !mpSet || mpSet->bInitialised )
140 return;
142
143#ifdef EXPERIMENTAL_EXTRA_THEME_RESOURCES
144 extern void RegisterExtraThemeResources();
145 RegisterExtraThemeResources();
146#endif
147}
148
150{
151 if (mThemeDir.empty())
153 wxFileName(FileNames::DataDir(), wxT("Theme")).GetFullPath());
154 return mThemeDir;
155}
156
158{
159 mThemeDir = path;
160}
161
163{
166 return true;
167}
168
170{
171 if ( !mpSet || mpSet->bInitialised )
172 return;
173 mpSet->bInitialised = true;
174
175// This initialises the variables e.g
176// RegisterImage( myFlags, bmpRecordButton, some image, wxT("RecordButton"));
177 int myFlags = resFlagPaired;
178 NameSet allNames;
179#define THEME_INITS
180#include "AllThemeResources.h"
181
182
183}
184
186{
187}
188
190{
191}
192
194 std::map< EnumValueSymbol, const ThemeBase::RegisteredTheme& >;
195
197{
198 static ThemeCacheLookup theMap;
199 return theMap;
200}
201
203 EnumValueSymbol symbol, PreferredSystemAppearance preferredSystemAppearance,
204 const std::vector<unsigned char> &data )
205 : symbol{ symbol }
206 , preferredSystemAppearance { preferredSystemAppearance }
207 , data { data }
208{
209 GetThemeCacheLookup().emplace(symbol, *this);
210}
211
213{
214 GetThemeCacheLookup().erase(symbol);
215}
216
218{
219 // Switch the active theme set
220 mpSet = &mSets[Theme.empty() ? GUITheme().Read() : Theme];
221 auto &resources = *mpSet;
223
224 const bool cbOkIfNotFound = true;
225
226 if( !ReadImageCache( Theme, cbOkIfNotFound ) )
227 {
228 // THEN get the default set.
229 ReadImageCache( GetFallbackThemeType(), !cbOkIfNotFound );
230
231 // JKC: Now we could go on and load the individual images
232 // on top of the default images using the commented out
233 // code that follows...
234 //
235 // However, I think it is better to get the user to
236 // build a NEW image cache, which they can do easily
237 // from the Theme preferences tab.
238#if 0
239 // and now add any available component images.
240 LoadComponents( cbOkIfNotFound );
241
242 // JKC: I'm usure about doing this next step automatically.
243 // Suppose the disk is write protected?
244 // Is having the image cache created automatically
245 // going to confuse users? Do we need version specific names?
246 // and now save the combined image as a cache for later use.
247 // We should load the images a little faster in future as a result.
249#endif
250 }
251}
252
256{
258
259 // Post-processing steps after loading of the cache
260
261 // Two always overwritten images
262 RotateImageInto( bmpRecordBeside, bmpRecordBelow, false );
263 RotateImageInto( bmpRecordBesideDisabled, bmpRecordBelowDisabled, false );
264
265 // Other modifications of images happening only when the setting
266 // GUIBlendThemes is true
267 if ( mpSet->bRecolourOnLoad ) {
269
270 wxColor Back = theTheme.Colour( clrTrackInfo );
271 wxColor CurrentText = theTheme.Colour( clrTrackPanelText );
272 wxColor DesiredText = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
273
274 int TextColourDifference = ColourDistance( CurrentText, DesiredText );
275
276 // Theming is very accepting of alternative text colours. They just need to
277 // have decent contrast to the background colour, if we're blending themes.
278 if ( TextColourDifference != 0 ) {
279 int ContrastLevel = ColourDistance( Back, DesiredText );
280 if ( ContrastLevel > 250 )
281 Colour( clrTrackPanelText ) = DesiredText;
282 }
283 mpSet->bRecolourOnLoad = false;
284 }
285
286
287 // Next line is not required as we haven't yet built the GUI
288 // when this function is (or should be) called.
289 // AColor::ApplyUpdatedImages();
290
291 // The next step doesn't post-process the theme data. It only modifies
292 // system settings. So it is not necessary to make it conditional on
293 // preferences, for avoidance of errors in the use of Theme preferences.
294 // (See commit 2020217)
296}
297
298void ThemeBase::RecolourBitmap( int iIndex, wxColour From, wxColour To )
299{
300 wxImage Image( Bitmap( iIndex ).ConvertToImage() );
301
302 std::unique_ptr<wxImage> pResult = ChangeImageColour(
303 &Image, From, To );
304 ReplaceImage( iIndex, pResult.get() );
305}
306
307int ThemeBase::ColourDistance( wxColour & From, wxColour & To ){
308 return
309 abs( From.Red() - To.Red() )
310 + abs( From.Green() - To.Green() )
311 + abs( From.Blue() - To.Blue() );
312}
313
314// This function coerces a theme to be more like the system colours.
315// Only used for built in themes. For custom themes a user
316// will choose a better theme for them and just not use a mismatching one.
318{
319 wxColour From = Colour( clrMedium );
320#if defined( __WXGTK__ )
321 wxColour To = wxSystemSettings::GetColour( wxSYS_COLOUR_BACKGROUND );
322#else
323 wxColour To = wxSystemSettings::GetColour( wxSYS_COLOUR_3DFACE );
324#endif
325 // only recolour if recolouring is slight.
326 int d = ColourDistance( From, To );
327
328 // Don't recolour if difference is too big.
329 if( d > 120 )
330 return;
331
332 // A minor tint difference from standard does not need
333 // to be recouloured either. Includes case of d==0 which is nothing
334 // needs to be done.
335 if( d < 40 )
336 return;
337
338 Colour( clrMedium ) = To;
339 RecolourBitmap( bmpUpButtonLarge, From, To );
340 RecolourBitmap( bmpDownButtonLarge, From, To );
341 RecolourBitmap( bmpHiliteButtonLarge, From, To );
342 RecolourBitmap( bmpUpButtonSmall, From, To );
343 RecolourBitmap( bmpDownButtonSmall, From, To );
344 RecolourBitmap( bmpHiliteButtonSmall, From, To );
345
346 Colour( clrTrackInfo ) = To;
347 RecolourBitmap( bmpUpButtonExpand, From, To );
348}
349
350wxImage ThemeBase::MaskedImage( char const ** pXpm, char const ** pMask )
351{
352 wxBitmap Bmp1( pXpm );
353 wxBitmap Bmp2( pMask );
354
355// wxLogDebug( wxT("Image 1: %i Image 2: %i"),
356// Bmp1.GetDepth(), Bmp2.GetDepth() );
357
358 // We want a 24-bit-depth bitmap if all is working, but on some
359 // platforms it might just return -1 (which means best available
360 // or not relevant).
361 // JKC: \todo check that we're not relying on 24 bit elsewhere.
362 wxASSERT( Bmp1.GetDepth()==-1 || Bmp1.GetDepth()==24);
363 wxASSERT( Bmp1.GetDepth()==-1 || Bmp2.GetDepth()==24);
364
365 int i,nBytes;
366 nBytes = Bmp1.GetWidth() * Bmp1.GetHeight();
367 wxImage Img1( Bmp1.ConvertToImage());
368 wxImage Img2( Bmp2.ConvertToImage());
369
370// unsigned char *src = Img1.GetData();
371 unsigned char *mk = Img2.GetData();
372 //wxImage::setAlpha requires memory allocated with malloc, not NEW
374 static_cast<unsigned char*>(malloc( nBytes )) };
375
376 // Extract alpha channel from second XPM.
377 for(i=0;i<nBytes;i++)
378 {
379 alpha[i] = mk[0];
380 mk+=3;
381 }
382
383 Img1.SetAlpha( alpha.release() );
384
385 //dmazzoni: the top line does not work on wxGTK
386 //wxBitmap Result( Img1, 32 );
387 //wxBitmap Result( Img1 );
388
389 return Img1;
390}
391
392// Legacy function to allow use of an XPM where no theme image was defined.
393// Bit depth and mask needs review.
394// Note that XPMs don't offer translucency, so unsuitable for a round shape overlay,
395// for example.
397 int &flags, int &iIndex, char const ** pXpm, const wxString & Name )
398{
399 wxBitmap Bmp( pXpm );
400 wxImage Img( Bmp.ConvertToImage() );
401 // The next line recommended by http://forum.audacityteam.org/viewtopic.php?f=50&t=96765
402 Img.SetMaskColour(0xDE, 0xDE, 0xDE);
403 Img.InitAlpha();
404
405 //dmazzoni: the top line does not work on wxGTK
406 //wxBitmap Bmp2( Img, 32 );
407 //wxBitmap Bmp2( Img );
408
409 RegisterImage( allNames, flags, iIndex, Img, Name );
410}
411
413 int &flags, int &iIndex, const wxImage &Image, const wxString & Name )
414{
415 auto &resources = *mpSet;
416 resources.mImages.push_back( Image );
417
418#ifdef __APPLE__
419 // On Mac, bitmaps with alpha don't work.
420 // So we convert to a mask and use that.
421 // It isn't quite as good, as alpha gives smoother edges.
422 //[Does not affect the large control buttons, as for those we do
423 // the blending ourselves anyway.]
424 wxImage TempImage( Image );
425 TempImage.ConvertAlphaToMask();
426 resources.mBitmaps.push_back( wxBitmap( TempImage ) );
427#else
428 resources.mBitmaps.push_back( wxBitmap( Image ) );
429#endif
430
431 flags &= ~resFlagSkip;
432 auto index = resources.mBitmaps.size() - 1;
433 if (iIndex == -1) {
434 // First time assignment of global variable identifying an image
435 iIndex = index;
436 mBitmapNames.push_back( Name );
437 mBitmapFlags.push_back( flags );
438 wxASSERT(allNames.insert(Name).second);
439 }
440 else {
441 // If revisiting for another theme set,
442 // images should be re-done in the same sequence
443 wxASSERT(iIndex == index);
444 wxASSERT(mBitmapNames[index] == Name);
445 wxASSERT(mBitmapFlags[index] == flags);
446 }
447}
448
450 int &iIndex, const wxColour &Clr, const wxString & Name )
451{
452 auto &resources = *mpSet;
453 resources.mColours.push_back( Clr );
454 auto index = resources.mColours.size() - 1;
455 if (iIndex == -1) {
456 // First time assignment of global variable identifying a colour
457 iIndex = index;
458 mColourNames.push_back( Name );
459 wxASSERT(allNames.insert(Name).second);
460 }
461 else {
462 // If revisiting for another theme set,
463 // colours should be re-done in the same sequence
464 wxASSERT(iIndex == index);
465 wxASSERT(mColourNames[index] == Name);
466 }
467}
468
470 : mxCacheWidth{ width }
471{
472 SetNewGroup(1);
473}
474
475void FlowPacker::SetNewGroup( int iGroupSize )
476{
478 mxPos =0;
480 iImageGroupSize = iGroupSize;
481 iImageGroupIndex = -1;
483}
484
486{
487 myPosBase = 750;
488 mxPos =0;
490 iImageGroupSize = 1;
491 iImageGroupIndex = -1;
493 myHeight = 11;
494}
495
496void FlowPacker::GetNextPosition( int xSize, int ySize )
497{
498 xSize += 2*mBorderWidth;
499 ySize += 2*mBorderWidth;
500 // if the height has increased, then we are on a NEW group.
501 if(( ySize > myHeight )||(((mFlags ^ mOldFlags )& ~resFlagSkip)!=0))
502 {
503 SetNewGroup( ((mFlags & resFlagPaired)!=0) ? 2 : 1 );
504 myHeight = ySize;
505// mFlags &= ~resFlagNewLine;
506// mOldFlags = mFlags;
507 }
508
511 {
514 }
515
516 if(mxPos > (mxCacheWidth - xSize ))
517 {
520 myHeight = ySize;
521 }
523
524 mComponentWidth = xSize;
525 mComponentHeight = ySize;
526}
527
529{
530 return wxRect( mxPos, myPos, mComponentWidth, mComponentHeight);
531}
532
534{
535 return Rect().Deflate( mBorderWidth, mBorderWidth );
536}
537
538void FlowPacker::RectMid( int &x, int &y )
539{
540 x = mxPos + mComponentWidth/2;
541 y = myPos + mComponentHeight/2;
542}
543
544
550class SourceOutputStream final : public wxOutputStream
551{
552public:
554 int OpenFile(const FilePath & Filename);
555 virtual ~SourceOutputStream();
556
557protected:
558 size_t OnSysWrite(const void *buffer, size_t bufsize) override;
559 wxFile File;
561};
562
565{
566 nBytes = 0;
567 bool bOk;
568 bOk = File.Open( Filename, wxFile::write );
569 if( bOk )
570 {
571 File.Write( wxString::Format(
572 wxT("/// @file %s\r\n"), wxFileName(Filename).GetFullName()));
573 File.Write( wxT("/// @brief This file was Auto-Generated.\r\n") );
574 File.Write( wxT("///\r\n") );
575 File.Write( wxT("/// It is included by Theme.cpp.\r\n") );
576 File.Write( wxT("/// Only check this into Git if you've read and understood the guidelines!\r\n\r\n ") );
577 }
578 return bOk;
579}
580
583size_t SourceOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
584{
585 wxString Temp;
586 for(int i=0;i<(int)bufsize;i++)
587 {
588 // Write one byte with a comma
589 Temp = wxString::Format( wxT("%i,"),(int)(((unsigned char*)buffer)[i]) );
590 File.Write( Temp );
591 nBytes++;
592 // New line if more than 20 bytes written since last time.
593 if( (nBytes %20)==0 )
594 {
595 File.Write( wxT("\r\n "));
596 }
597 }
598 return bufsize;
599}
600
603{
604 File.Write( wxT("\r\n") );
605 File.Close();
606}
607
608
609// Must be wide enough for bmpAudacityLogo. Use double width + 10.
610const int ImageCacheWidth = 440;
611
612const int ImageCacheHeight = 836;
613
615{
616 ValueRestorer cleanup{ mpSet };
617 for (auto &[key, data] : GetThemeCacheLookup())
618 if (!CreateOneImageCache(key.Internal(), true))
619 // Some file failed to save, message was given
620 return;
622/* i18n-hint: A theme is a consistent visual style across an application's
623graphical user interface, including choices of colors, and similarity of images
624such as those on button controls. Audacity can load and save alternative
625themes. */
626 XO("Themes written to:\n %s/*/%s.")
628}
629
630bool ThemeBase::CreateOneImageCache( teThemeType id, bool bBinarySave )
631{
632 SwitchTheme( id );
633 auto &resources = *mpSet;
634
636 ImageCache.SetRGB( wxRect( 0,0,ImageCacheWidth, ImageCacheHeight), 1,1,1);//Not-quite black.
637
638 // Ensure we have an alpha channel...
639 if( !ImageCache.HasAlpha() )
640 {
641 ImageCache.InitAlpha();
642 }
643
644 FlowPacker context{ ImageCacheWidth };
645
646//#define IMAGE_MAP
647#ifdef IMAGE_MAP
648 wxLogDebug( wxT("<img src=\"ImageCache.png\" usemap=\"#map1\">" ));
649 wxLogDebug( wxT("<map name=\"map1\">") );
650#endif
651
652 // Save the bitmaps
653 for (size_t i = 0; i < resources.mImages.size() ; ++i)
654 {
655 wxImage &SrcImage = resources.mImages[i];
656 context.mFlags = mBitmapFlags[i];
657 if( !(mBitmapFlags[i] & resFlagInternal) )
658 {
659 context.GetNextPosition( SrcImage.GetWidth(), SrcImage.GetHeight());
660 ImageCache.SetRGB( context.Rect(), 0xf2, 0xb0, 0x27 );
661 if( !(context.mFlags & resFlagSkip) )
662 PasteSubImage( &ImageCache, &SrcImage,
663 context.mxPos + context.mBorderWidth,
664 context.myPos + context.mBorderWidth);
665 else
666 ImageCache.SetRGB( context.RectInner(), 1,1,1);
667#ifdef IMAGE_MAP
668 // No href in html. Uses title not alt.
669 wxRect R( context.Rect() );
670 wxLogDebug( wxT("<area title=\"Bitmap:%s\" shape=rect coords=\"%i,%i,%i,%i\">"),
671 mBitmapNames[i],
672 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom() );
673#endif
674 }
675 }
676
677 // Now save the colours.
678 int x,y;
679
680 context.SetColourGroup();
681 for (size_t i = 0; i < resources.mColours.size(); ++i)
682 {
683 context.GetNextPosition( iColSize, iColSize );
684 wxColour c = resources.mColours[i];
685 ImageCache.SetRGB( context.Rect() , 0xf2, 0xb0, 0x27 );
686 ImageCache.SetRGB( context.RectInner() , c.Red(), c.Green(), c.Blue() );
687
688 // YUCK! No function in wxWidgets to set a rectangle of alpha...
689 for(x=0;x<iColSize;x++)
690 {
691 for(y=0;y<iColSize;y++)
692 {
693 ImageCache.SetAlpha( context.mxPos + x, context.myPos+y, 255);
694 }
695 }
696#ifdef IMAGE_MAP
697 // No href in html. Uses title not alt.
698 wxRect R( context.Rect() );
699 wxLogDebug( wxT("<area title=\"Colour:%s\" shape=rect coords=\"%i,%i,%i,%i\">"),
700 mColourNames[i],
701 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom() );
702#endif
703 }
704#if TEST_CARD
705 for ( unsigned i = 0; i < ImageCacheWidth; ++i)
706 for ( unsigned j = 0; j < ImageCacheHeight; ++j) {
707 int r = j &0xff;
708 int g = i &0xff;
709 int b = (j >> 8) | ((i>>4)&0xf0);
710 wxRect R( i,j, 1, 1);
711 ImageCache.SetRGB( R, r, g, b );
712 ImageCache.SetAlpha( i,j, 255);
713 }
714#endif
715
716#ifdef IMAGE_MAP
717 wxLogDebug( "</map>" );
718#endif
719
720 using namespace BasicUI;
721
722 // IF bBinarySave, THEN saving to a normal PNG file.
723 if( bBinarySave )
724 {
725 auto dir = ThemeSubdir(GetFilePath(), id);
726 auto FileName = wxFileName{ dir, ImageCacheFileName }.GetFullPath();
727
728 // Perhaps we should prompt the user if they are overwriting
729 // an existing theme cache?
730#if 0
731 if( wxFileExists( FileName ))
732 {
733 auto message =
734// XO(
735//"Theme cache file:\n %s\nalready exists.\nAre you sure you want to replace it?")
736// .Format( FileName );
737 TranslatableString{ FileName };
738 ShowMessageBox( message );
739 return false;
740 }
741#endif
742#if 0
743 // Deliberate policy to use the fast/cheap blocky pixel-multiplication
744 // algorithm, as this introduces no artifacts on repeated scale up/down.
745 ImageCache.Rescale(
746 ImageCache.GetWidth()*4,
747 ImageCache.GetHeight()*4,
748 wxIMAGE_QUALITY_NEAREST );
749#endif
750 if( !ImageCache.SaveFile( FileName, wxBITMAP_TYPE_PNG ))
751 {
753 XO("Audacity could not write file:\n %s.")
754 .Format( FileName ));
755 return false;
756 }
757 }
758 // ELSE saving to a C code textual version.
759 else
760 {
761 // Generated header file is not put in the sub-directory for
762 // the theme, but is instead distinguished by a prefix on the file name.
763 // So the header can simply be copied into the source code tree.
764 auto dir = GetFilePath();
765 SourceOutputStream OutStream;
767 auto FileName = wxFileName{ dir, name }.GetFullPath();
768 if( !OutStream.OpenFile( FileName ))
769 {
771 XO("Audacity could not open file:\n %s\nfor writing.")
772 .Format( FileName ));
773 return false;
774 }
775 if( !ImageCache.SaveFile(OutStream, wxBITMAP_TYPE_PNG ) )
776 {
778 XO("Audacity could not write images to file:\n %s.")
779 .Format( FileName ));
780 return false;
781 }
782 }
783 return true;
784}
785
787{
788 ValueRestorer cleanup{ mpSet };
789 for (auto &[key, data] : GetThemeCacheLookup())
790 WriteOneImageMap(key.Internal());
791}
792
796{
797 SwitchTheme( id );
798 auto &resources = *mpSet;
799
800 FlowPacker context{ ImageCacheWidth };
801
802 auto dir = ThemeSubdir(GetFilePath(), id);
803 auto FileName = wxFileName{ dir, ImageMapFileName }.GetFullPath();
804 wxFFile File( FileName, wxT("wb") );// I'll put in NEW lines explicitly.
805 if( !File.IsOpened() )
806 return;
807
808 File.Write( wxT("<html>\r\n"));
809 File.Write( wxT("<body bgcolor=\"303030\">\r\n"));
810 wxString Temp = wxString::Format( wxT("<img src=\"ImageCache.png\" width=\"%i\" usemap=\"#map1\">\r\n" ), ImageCacheWidth );
811 File.Write( Temp );
812 File.Write( wxT("<map name=\"map1\">\r\n") );
813
814 for (size_t i = 0; i < resources.mImages.size(); ++i)
815 {
816 wxImage &SrcImage = resources.mImages[i];
817 context.mFlags = mBitmapFlags[i];
818 if( !(mBitmapFlags[i] & resFlagInternal) )
819 {
820 context.GetNextPosition( SrcImage.GetWidth(), SrcImage.GetHeight());
821 // No href in html. Uses title not alt.
822 wxRect R( context.RectInner() );
823 File.Write( wxString::Format(
824 wxT("<area title=\"Bitmap:%s\" shape=rect coords=\"%i,%i,%i,%i\">\r\n"),
825 mBitmapNames[i],
826 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom()) );
827 }
828 }
829 // Now save the colours.
830 context.SetColourGroup();
831 for (size_t i = 0; i < resources.mColours.size(); ++i)
832 {
833 context.GetNextPosition( iColSize, iColSize );
834 // No href in html. Uses title not alt.
835 wxRect R( context.RectInner() );
836 File.Write( wxString::Format( wxT("<area title=\"Colour:%s\" shape=rect coords=\"%i,%i,%i,%i\">\r\n"),
837 mColourNames[i],
838 R.GetLeft(), R.GetTop(), R.GetRight(), R.GetBottom()) );
839 }
840 File.Write( wxT("</map>\r\n") );
841 File.Write( wxT("</body>\r\n"));
842 File.Write( wxT("</html>\r\n"));
843 // File will be closed automatically.
844}
845
848{
849 // The generated image defs macro calls depend only on sizes of images,
850 // not contents, and so there is only one file good across all themes.
851
852 auto &resources = *mpSet;
854
855 wxFFile File( ThemeImageDefsAsCee(GetFilePath()), wxT("wb") );
856 if( !File.IsOpened() )
857 return;
858 teResourceFlags PrevFlags = (teResourceFlags)-1;
859 for (size_t i = 0; i < resources.mImages.size(); ++i)
860 {
861 wxImage &SrcImage = resources.mImages[i];
862 // No href in html. Uses title not alt.
863 if( PrevFlags != mBitmapFlags[i] )
864 {
865 PrevFlags = (teResourceFlags)mBitmapFlags[i];
866 int t = (int)PrevFlags;
867 wxString Temp;
868 if( t==0 ) Temp = wxT(" resFlagNone ");
869 if( t & resFlagPaired ) Temp += wxT(" resFlagPaired ");
870 if( t & resFlagCursor ) Temp += wxT(" resFlagCursor ");
871 if( t & resFlagNewLine ) Temp += wxT(" resFlagNewLine ");
872 if( t & resFlagInternal ) Temp += wxT(" resFlagInternal ");
873 Temp.Replace( wxT(" "), wxT(" | ") );
874
875 File.Write( wxString::Format( wxT("\r\n SET_THEME_FLAGS( %s );\r\n"),
876 Temp ));
877 }
878 File.Write( wxString::Format(
879 wxT(" DEFINE_IMAGE( bmp%s, wxImage( %i, %i ), wxT(\"%s\"));\r\n"),
880 mBitmapNames[i],
881 SrcImage.GetWidth(),
882 SrcImage.GetHeight(),
883 mBitmapNames[i]
884 ));
885 }
886}
887
888
890// Fallback must be an internally supported type,
891// to guarantee it is found.
892 return "light";
893}
894
900bool ThemeBase::ReadImageCache( teThemeType type, bool bOkIfNotFound)
901{
902 auto &resources = *mpSet;
904 wxImage ImageCache;
905
906 // Ensure we have an alpha channel...
907// if( !ImageCache.HasAlpha() )
908// {
909// ImageCache.InitAlpha();
910// }
911
913
914 using namespace BasicUI;
915
916 if( type.empty() || type == "custom" )
917 {
919
920 // Take the image cache file for the theme chosen in preferences
921 auto dir = ThemeSubdir(GetFilePath(), GUITheme().Read());
922 const auto &FileName =
923 wxFileName{ dir, ImageCacheFileName }.GetFullPath();
924 if( !wxFileExists( FileName ))
925 {
926 if( bOkIfNotFound )
927 return false; // did not load the images, so return false.
929 XO("Audacity could not find file:\n %s.\nTheme not loaded.")
930 .Format( FileName ));
931 return false;
932 }
933 if( !ImageCache.LoadFile( FileName, wxBITMAP_TYPE_PNG ))
934 {
936 /* i18n-hint: Do not translate png. It is the name of a file format.*/
937 XO("Audacity could not load file:\n %s.\nBad png format perhaps?")
938 .Format( FileName ));
939 return false;
940 }
941 }
942 // ELSE we are reading from internal storage.
943 else
944 {
945 size_t ImageSize = 0;
946 const unsigned char * pImage = nullptr;
947 auto &lookup = GetThemeCacheLookup();
948 auto iter = lookup.find({type, {}});
949 if (const auto end = lookup.end(); iter == end) {
950 iter = lookup.find({"classic", {}});
951 wxASSERT(iter != end);
952 }
953
954 mPreferredSystemAppearance = iter->second.preferredSystemAppearance;
955
956 ImageSize = iter->second.data.size();
957 if (ImageSize == 0)
958 // This must be the image compiler
959 return true;
960
961 pImage = iter->second.data.data();
962 //wxLogDebug("Reading ImageCache %p size %i", pImage, ImageSize );
963 wxMemoryInputStream InternalStream( pImage, ImageSize );
964
965 if( !ImageCache.LoadFile( InternalStream, wxBITMAP_TYPE_PNG ))
966 {
967 // If we get this message, it means that the data in file
968 // was not a valid png image.
969 // Most likely someone edited it by mistake,
970 // Or some experiment is being tried with NEW formats for it.
972 XO(
973"Audacity could not read its default theme.\nPlease report the problem."));
974 return false;
975 }
976 //wxLogDebug("Read %i by %i", ImageCache.GetWidth(), ImageCache.GetHeight() );
977 }
978
979 // Resize a large image down.
980 if( ImageCache.GetWidth() > ImageCacheWidth ){
981 int h = ImageCache.GetHeight() * ((1.0*ImageCacheWidth)/ImageCache.GetWidth());
982 ImageCache.Rescale( ImageCacheWidth, h );
983 }
984 FlowPacker context{ ImageCacheWidth };
985 // Load the bitmaps
986 for (size_t i = 0; i < resources.mImages.size(); ++i)
987 {
988 wxImage &Image = resources.mImages[i];
989 context.mFlags = mBitmapFlags[i];
990 if( !(mBitmapFlags[i] & resFlagInternal) )
991 {
992 context.GetNextPosition( Image.GetWidth(),Image.GetHeight() );
993 wxRect R = context.RectInner();
994 //wxLogDebug( "[%i, %i, %i, %i, \"%s\"], ", R.x, R.y, R.width, R.height, mBitmapNames[i].c_str() );
995 Image = GetSubImageWithAlpha( ImageCache, context.RectInner() );
996 resources.mBitmaps[i] = wxBitmap(Image);
997 }
998 }
999 if( !ImageCache.HasAlpha() )
1000 ImageCache.InitAlpha();
1001
1002// return true; //To not load colours..
1003 // Now load the colours.
1004 int x,y;
1005 context.SetColourGroup();
1006 wxColour TempColour;
1007 for (size_t i = 0; i < resources.mColours.size(); ++i)
1008 {
1009 context.GetNextPosition( iColSize, iColSize );
1010 context.RectMid( x, y );
1011 wxRect R = context.RectInner();
1012 //wxLogDebug( "[%i, %i, %i, %i, \"%s\"], ", R.x, R.y, R.width, R.height, mColourNames[i].c_str() );
1013 // Only change the colour if the alpha is opaque.
1014 // This allows us to add NEW colours more easily.
1015 if( ImageCache.GetAlpha(x,y ) > 128 )
1016 {
1017 TempColour = wxColour(
1018 ImageCache.GetRed( x,y),
1019 ImageCache.GetGreen( x,y),
1020 ImageCache.GetBlue(x,y));
1025 if( TempColour != wxColour(1,1,1) )
1026 resources.mColours[i] = TempColour;
1027 }
1028 }
1029 return true;
1030}
1031
1032void ThemeBase::LoadThemeComponents( bool bOkIfNotFound )
1033{
1034 ValueRestorer cleanup{ mpSet };
1035 for (auto &[key, data] : GetThemeCacheLookup())
1036 LoadOneThemeComponents( key.Internal(), bOkIfNotFound );
1037}
1038
1040{
1041 SwitchTheme( id );
1042 auto &resources = *mpSet;
1043 // IF directory doesn't exist THEN return early.
1044 const auto dir = ThemeComponentsDir(GetFilePath(), id);
1045 if( !wxDirExists( dir ))
1046 return;
1047
1048 using namespace BasicUI;
1049
1050 int n=0;
1051 FilePath FileName;
1052 for (size_t i = 0; i < resources.mImages.size(); ++i)
1053 {
1054 if( !(mBitmapFlags[i] & resFlagInternal) )
1055 {
1056 FileName = ThemeComponent( dir, mBitmapNames[i] );
1057 if( wxFileExists( FileName ))
1058 {
1059 if( !resources.mImages[i].LoadFile( FileName, wxBITMAP_TYPE_PNG ))
1060 {
1062 XO(
1063 /* i18n-hint: Do not translate png. It is the name of a file format.*/
1064"Audacity could not load file:\n %s.\nBad png format perhaps?")
1065 .Format( FileName ));
1066 return;
1067 }
1072 if( ! resources.mImages[i].HasAlpha() )
1073 {
1074 // wxLogDebug( wxT("File %s lacked alpha"), mBitmapNames[i] );
1075 resources.mImages[i].InitAlpha();
1076 }
1077 resources.mBitmaps[i] = wxBitmap( resources.mImages[i] );
1078 n++;
1079 }
1080 }
1081 }
1082
1083 // Now read complete information about the colors from one text file
1084 {
1085 const auto fName = wxFileName{ dir, ColorFileName }.GetFullPath();
1086 wxTextFile file{ fName };
1087 file.Open();
1088 if (!file.IsOpened())
1089 ShowMessageBox( XO("Couldn't read from file: %s").Format( fName ) );
1090 else {
1091 ++n;
1092 // Scan the line for name and #xxxxxx;
1093 static const std::wregex expr{
1094 LR"(^ *([_[:alnum:]]+).*#([0-9a-fA-F]{6});)" };
1095 const auto begin = mColourNames.begin(),
1096 end = mColourNames.end();
1097 std::unordered_set<wxString> names;
1098 for (auto str = file.GetFirstLine();
1099 !file.Eof(); str = file.GetNextLine()) {
1100 if (std::wsmatch match;
1101 regex_search( str.ToStdWstring(), match, expr )) {
1102 const wxString name{ match[1] };
1103 if (!names.insert(name).second)
1104 ShowMessageBox( Verbatim("Ignoring duplicate color name: %s")
1105 .Format( name ) );
1106 else if (const auto iter = std::find(begin, end, name);
1107 iter == end)
1109 Verbatim("Unrecognized color name: %s").Format( name ) );
1110 else {
1111 auto rrggbb =
1112 static_cast<unsigned>(stoi(match[2], nullptr, 16));
1113 unsigned char rr = (rrggbb >> 16) & 0xffu;
1114 unsigned char gg = (rrggbb >> 8) & 0xffu;
1115 unsigned char bb = rrggbb & 0xffu;
1116 resources.mColours[iter - begin] = { rr, gg, bb };
1117 }
1118 }
1119 }
1120 }
1121 }
1122
1123 if( n==0 )
1124 {
1125 if( bOkIfNotFound )
1126 return;
1128 XO(
1129"None of the expected theme component files\n were found in:\n %s.")
1130 .Format( dir ));
1131 }
1132}
1133
1135{
1136 ValueRestorer cleanup{ mpSet };
1137 for (auto &[key, data] : GetThemeCacheLookup())
1138 if (!SaveOneThemeComponents( key.Internal() ))
1139 // Some file failed to save, message was given
1140 return;
1142 XO("Themes written to:\n %s/*/Components/.").Format(GetFilePath()));
1143}
1144
1146{
1147 using namespace BasicUI;
1148 SwitchTheme( id );
1149 auto &resources = *mpSet;
1150 // IF directory doesn't exist THEN create it
1151 const auto dir = ThemeComponentsDir(GetFilePath(), id);
1152 if( !wxDirExists( dir ))
1153 {
1159#ifdef __WXMSW__
1160 wxMkDir( dir.fn_str() );
1161#else
1162 wxMkDir( dir.fn_str(), 0700 );
1163#endif
1164 if( !wxDirExists( dir ))
1165 {
1167 XO("Could not create directory:\n %s")
1168 .Format( dir ) );
1169 return false;
1170 }
1171 }
1172
1173 int n=0;
1174 FilePath FileName;
1175 for (size_t i = 0; i < resources.mImages.size(); ++i)
1176 {
1177 if( !(mBitmapFlags[i] & resFlagInternal) )
1178 {
1179 FileName = ThemeComponent( dir, mBitmapNames[i] );
1180 if( wxFileExists( FileName ))
1181 {
1182 ++n;
1183 break;
1184 }
1185 }
1186 }
1187
1188 if( wxFileExists( ThemeComponent( dir, ColorFileName ) ) )
1189 ++n;
1190
1191 using namespace BasicUI;
1192
1193 if (n > 0)
1194 {
1195 auto result =
1197 XO(
1198"Some required files in:\n %s\nwere already present. Overwrite?")
1199 .Format( dir ),
1201 .ButtonStyle(Button::YesNo)
1202 .DefaultIsNo());
1203 if (result == MessageBoxResult::No)
1204 return false;
1205 }
1206
1207 for (size_t i = 0; i < resources.mImages.size(); ++i)
1208 {
1209 if( !(mBitmapFlags[i] & resFlagInternal) )
1210 {
1211 FileName = ThemeComponent( dir, mBitmapNames[i] );
1212 if( !resources.mImages[i].SaveFile( FileName, wxBITMAP_TYPE_PNG ))
1213 {
1215 XO("Audacity could not save file:\n %s")
1216 .Format( FileName ));
1217 return false;
1218 }
1219 }
1220 }
1221
1222 // Now write complete information about the colors in one text file
1223 {
1224 const auto fName = wxFileName{ dir, ColorFileName }.GetFullPath();
1225 wxFileOutputStream ffStream{ fName };
1226 if (!ffStream.IsOk()) {
1227 ShowMessageBox( XO("Couldn't write to file: %s").Format( fName ) );
1228 return false;
1229 }
1230 wxTextOutputStream ss{ffStream};
1231 // Open with, for instance, ".darkTheme {"
1232 ss << "." << id.GET() << "Theme {\n";
1233 for (size_t i = 0; i < resources.mColours.size(); ++i) {
1234 const auto &colour = resources.mColours[i];
1235 ss
1236 // Write names, aligning the colons in a column,
1237 // followed by #rrggbb;
1238 << wxString::Format("%30s: #", mColourNames[i])
1239 << wxString::Format("%02x", colour.Red())
1240 << wxString::Format("%02x", colour.Green())
1241 << wxString::Format("%02x", colour.Blue())
1242 << ";\n";
1243 }
1244 ss << "}";
1245 }
1246
1247 return true;
1248}
1249
1251{
1252 ValueRestorer cleanup{ mpSet };
1253 for (auto &[key, data] : GetThemeCacheLookup()) {
1254 // false indicates not using standard binary method.
1255 if (!CreateOneImageCache(key.Internal(), false))
1256 // Some file failed to save, message was given
1257 return;
1258 }
1260 /* i18n-hint "Cee" means the C computer programming language */
1261 XO("Themes as Cee code written to:\n %s/*%s.")
1263}
1264
1265wxImage ThemeBase::MakeImageWithAlpha( wxBitmap & Bmp )
1266{
1267 // BUG in wxWidgets. Conversion from BMP to image does not preserve alpha.
1268 wxImage image( Bmp.ConvertToImage() );
1269 return image;
1270}
1271
1273{
1274 auto iter = mSets.begin(), end = mSets.end();
1275 while (iter != end) {
1276 if (mpSet == &iter->second)
1277 ++iter;
1278 else
1279 iter = mSets.erase(iter);
1280 }
1281}
1282
1283wxColour & ThemeBase::Colour( int iIndex )
1284{
1285 wxASSERT( iIndex >= 0 );
1286 auto &resources = *mpSet;
1288 return resources.mColours[iIndex];
1289}
1290
1291void ThemeBase::SetBrushColour( wxBrush & Brush, int iIndex )
1292{
1293 wxASSERT( iIndex >= 0 );
1294 Brush.SetColour( Colour( iIndex ));
1295}
1296
1297void ThemeBase::SetPenColour( wxPen & Pen, int iIndex )
1298{
1299 wxASSERT( iIndex >= 0 );
1300 Pen.SetColour( Colour( iIndex ));
1301}
1302
1303wxBitmap & ThemeBase::Bitmap( int iIndex )
1304{
1305 wxASSERT( iIndex >= 0 );
1306 auto &resources = *mpSet;
1308 return resources.mBitmaps[iIndex];
1309}
1310
1311wxImage & ThemeBase::Image( int iIndex )
1312{
1313 wxASSERT( iIndex >= 0 );
1314 auto &resources = *mpSet;
1316 return resources.mImages[iIndex];
1317}
1318wxSize ThemeBase::ImageSize( int iIndex )
1319{
1320 wxASSERT( iIndex >= 0 );
1321 auto &resources = *mpSet;
1323 wxImage & Image = resources.mImages[iIndex];
1324 return wxSize( Image.GetWidth(), Image.GetHeight());
1325}
1326
1328void ThemeBase::ReplaceImage( int iIndex, wxImage * pImage )
1329{
1330 Image( iIndex ) = *pImage;
1331 Bitmap( iIndex ) = wxBitmap( *pImage );
1332}
1333
1334void ThemeBase::RotateImageInto( int iTo, int iFrom, bool bClockwise )
1335{
1336 wxImage img(theTheme.Bitmap( iFrom ).ConvertToImage() );
1337 wxImage img2 = img.Rotate90( bClockwise );
1338 ReplaceImage( iTo, &img2 );
1339}
1340
1342{
1343 auto symbols = []{
1344 std::vector<EnumValueSymbol> symbols;
1345
1346 // Gather registered themes
1347 for (const auto &[symbol, data] : GetThemeCacheLookup())
1348 symbols.emplace_back(symbol);
1349
1350 // Sort the names, with built-in themes to the front,
1351 // conserving the ordering that was used in 3.1.0; otherwise
1352 // sorting other registered themes alphabetically by identifier
1353 static const Identifier names[] = {
1354 "classic", "light", "dark", "high-contrast"
1355 };
1356 static auto index = [](const EnumValueSymbol &symbol){
1357 auto begin = std::begin(names), end = std::end(names);
1358 return std::find(begin, end, symbol.Internal()) - begin;
1359 };
1360 std::stable_sort( symbols.begin(), symbols.end(),
1361 [](auto &a, auto &b){ return index(a) < index(b); } );
1362
1363 // Last, custom
1364 symbols.emplace_back(
1365 /* i18n-hint: user defined */
1366 "custom", XO("Custom")
1367 );
1368
1369 return symbols;
1370 };
1371
1372 constexpr int defaultTheme = 1; // "light"
1373
1374 static ChoiceSetting setting {
1375 wxT("/GUI/Theme"), symbols(), defaultTheme
1376 };
1377
1378 return setting;
1379}
1380
1381BoolSetting GUIBlendThemes{ wxT("/GUI/BlendThemes"), true };
wxImage(22, 22)
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
static ThemeBase::RegisteredTheme theme
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
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
Definition: MemoryX.h:145
static const AudacityProject::AttachedObjects::RegisteredFactory key
wxString FilePath
Definition: Project.h:21
static TranslatableStrings names
Definition: TagsEditor.cpp:153
const int ImageCacheWidth
Definition: Theme.cpp:610
std::map< EnumValueSymbol, const ThemeBase::RegisteredTheme & > ThemeCacheLookup
Definition: Theme.cpp:194
static ThemeCacheLookup & GetThemeCacheLookup()
Definition: Theme.cpp:196
THEME_API Theme theTheme
Definition: Theme.cpp:82
const int ImageCacheHeight
Definition: Theme.cpp:612
THEME_API BoolSetting GUIBlendThemes
THEME_API ChoiceSetting & GUITheme()
teResourceFlags
Definition: Theme.h:59
@ resFlagNewLine
Definition: Theme.h:63
@ resFlagSkip
Definition: Theme.h:65
@ resFlagInternal
Definition: Theme.h:64
@ resFlagPaired
Definition: Theme.h:61
@ resFlagCursor
Definition: Theme.h:62
PreferredSystemAppearance
A system theme, that matches selected theme best (only works on macOS with builtin themes).
Definition: Theme.h:34
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:346
wxString Read() const
Definition: Prefs.cpp:388
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
A cursor for iterating the theme bitmap.
Definition: Theme.h:70
int myHeight
Definition: Theme.h:85
void SetColourGroup()
Definition: Theme.cpp:485
void SetNewGroup(int iGroupSize)
Definition: Theme.cpp:475
int myPosBase
Definition: Theme.h:92
void RectMid(int &x, int &y)
Definition: Theme.cpp:538
int iImageGroupIndex
Definition: Theme.h:90
wxRect RectInner()
Definition: Theme.cpp:533
int mxCacheWidth
Definition: Theme.h:93
int mComponentHeight
Definition: Theme.h:96
int mFlags
Definition: Theme.h:82
void GetNextPosition(int xSize, int ySize)
Definition: Theme.cpp:496
int mBorderWidth
Definition: Theme.h:86
int iImageGroupSize
Definition: Theme.h:89
FlowPacker(int width)
Definition: Theme.cpp:469
wxRect Rect()
Definition: Theme.cpp:528
int mComponentWidth
Definition: Theme.h:95
int mxPos
Definition: Theme.h:83
int mOldFlags
Definition: Theme.h:91
int myPos
Definition: Theme.h:84
Abstract base class used in importing a file.
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
bool empty() const
Definition: Identifier.h:61
CallbackReturn Publish(const ThemeChangeMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
Helper class based on wxOutputStream used to get a png file in text format.
Definition: Theme.cpp:551
size_t OnSysWrite(const void *buffer, size_t bufsize) override
Definition: Theme.cpp:583
int OpenFile(const FilePath &Filename)
Opens the file and also adds a standard comment at the start of it.
Definition: Theme.cpp:564
virtual ~SourceOutputStream()
Destructor. We close our text stream in here.
Definition: Theme.cpp:602
void SwitchTheme(teThemeType Theme)
Definition: Theme.cpp:217
bool SaveOneThemeComponents(teThemeType id)
void LoadTheme(teThemeType Theme)
Definition: Theme.cpp:255
void RegisterImage(NameSet &allNames, int &flags, int &iIndex, char const **pXpm, const wxString &Name)
Definition: Theme.cpp:396
wxColour & Colour(int iIndex)
ThemeSet * mpSet
Definition: Theme.h:207
void SetBrushColour(wxBrush &Brush, int iIndex)
void DeleteUnusedThemes()
void SaveThemeAsCode()
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)
Definition: Theme.cpp:350
int ColourDistance(wxColour &From, wxColour &To)
Definition: Theme.cpp:307
bool CreateOneImageCache(teThemeType id, bool bBinarySave)
Definition: Theme.cpp:630
void WriteOneImageMap(teThemeType id)
Definition: Theme.cpp:795
void RecolourTheme()
Definition: Theme.cpp:317
ThemeBase(void)
Definition: Theme.cpp:185
FilePath mThemeDir
Definition: Theme.h:198
teThemeType GetFallbackThemeType()
Definition: Theme.cpp:889
void LoadOneThemeComponents(teThemeType id, bool bOkIfNotFound=false)
Definition: Theme.cpp:1039
wxArrayString mBitmapNames
Definition: Theme.h:200
void SetPenColour(wxPen &Pen, int iIndex)
void WriteImageMap()
Definition: Theme.cpp:786
std::map< Identifier, ThemeSet > mSets
Definition: Theme.h:206
std::unordered_set< wxString > NameSet
Definition: Theme.h:152
FilePath GetFilePath()
Definition: Theme.cpp:149
wxArrayString mColourNames
Definition: Theme.h:202
PreferredSystemAppearance mPreferredSystemAppearance
Definition: Theme.h:204
bool ReadImageCache(teThemeType type={}, bool bOkIfNotFound=false)
Definition: Theme.cpp:900
std::vector< int > mBitmapFlags
Definition: Theme.h:201
void SetFilePath(const FilePath &path)
Definition: Theme.cpp:157
virtual void EnsureInitialised()=0
wxSize ImageSize(int iIndex)
void CreateImageCache()
Definition: Theme.cpp:614
static bool LoadPreferredTheme()
Definition: Theme.cpp:162
void WriteImageDefs()
Writes a series of Macro definitions that can be used in the include file.
Definition: Theme.cpp:847
void RegisterColour(NameSet &allNames, int &iIndex, const wxColour &Clr, const wxString &Name)
Definition: Theme.cpp:449
void SaveThemeComponents()
void RecolourBitmap(int iIndex, wxColour From, wxColour To)
Definition: Theme.cpp:298
virtual ~ThemeBase(void)
Definition: Theme.cpp:189
void LoadThemeComponents(bool bOkIfNotFound=false)
Definition: Theme.cpp:1032
Based on ThemeBase, Theme manages image and icon resources.
Definition: Theme.h:211
void EnsureInitialised() override
Definition: Theme.cpp:137
void RegisterImagesAndColours()
Definition: Theme.cpp:169
Theme(void)
Definition: Theme.cpp:84
~Theme(void)
Definition: Theme.cpp:88
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.
Definition: MemoryX.h:211
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:277
FILES_API wxString MkDir(const wxString &Str)
FILES_API FilePath DataDir()
Audacity user data directory.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
FilePath ThemeComponent(const wxString &dir, const wxString &Str)
Definition: Theme.cpp:131
constexpr auto ThemeCacheFileName
Definition: Theme.cpp:129
FilePath ThemeSubdir(const FilePath &themeDir, Identifier id)
Has the side-effect of ensuring existence of the directory.
Definition: Theme.cpp:106
constexpr auto ImageMapFileName
Definition: Theme.cpp:120
FilePath ThemeImageDefsAsCee(const FilePath &themeDir)
Definition: Theme.cpp:124
constexpr auto ImageCacheFileName
Definition: Theme.cpp:118
constexpr auto ColorFileName
Definition: Theme.cpp:122
wxString ThemeFilePrefix(teThemeType id)
Definition: Theme.cpp:96
FilePath ThemeComponentsDir(const FilePath &themeDir, Identifier id)
Has the side-effect of ensuring existence of the directory.
Definition: Theme.cpp:113
MessageBoxOptions && ButtonStyle(Button style) &&
Definition: BasicUI.h:107
const EnumValueSymbol symbol
Definition: Theme.h:143
RegisteredTheme(EnumValueSymbol symbol, PreferredSystemAppearance preferredSystemAppearance, const std::vector< unsigned char > &data)
Definition: Theme.cpp:202
std::vector< wxImage > mImages
Definition: Theme.h:103
bool bRecolourOnLoad
Definition: Theme.h:108
std::vector< wxColour > mColours
Definition: Theme.h:105
bool bInitialised
Definition: Theme.h:107