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
635 wxImage ImageCache( ImageCacheWidth, ImageCacheHeight );
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#ifdef EXPERIMENTAL_DA
893 return "dark";
894#else
895 return "light";
896#endif
897}
898
904bool ThemeBase::ReadImageCache( teThemeType type, bool bOkIfNotFound)
905{
906 auto &resources = *mpSet;
908 wxImage ImageCache;
909
910 // Ensure we have an alpha channel...
911// if( !ImageCache.HasAlpha() )
912// {
913// ImageCache.InitAlpha();
914// }
915
917
918 using namespace BasicUI;
919
920 if( type.empty() || type == "custom" )
921 {
923
924 // Take the image cache file for the theme chosen in preferences
925 auto dir = ThemeSubdir(GetFilePath(), GUITheme().Read());
926 const auto &FileName =
927 wxFileName{ dir, ImageCacheFileName }.GetFullPath();
928 if( !wxFileExists( FileName ))
929 {
930 if( bOkIfNotFound )
931 return false; // did not load the images, so return false.
933 XO("Audacity could not find file:\n %s.\nTheme not loaded.")
934 .Format( FileName ));
935 return false;
936 }
937 if( !ImageCache.LoadFile( FileName, wxBITMAP_TYPE_PNG ))
938 {
940 /* i18n-hint: Do not translate png. It is the name of a file format.*/
941 XO("Audacity could not load file:\n %s.\nBad png format perhaps?")
942 .Format( FileName ));
943 return false;
944 }
945 }
946 // ELSE we are reading from internal storage.
947 else
948 {
949 size_t ImageSize = 0;
950 const unsigned char * pImage = nullptr;
951 auto &lookup = GetThemeCacheLookup();
952 auto iter = lookup.find({type, {}});
953 if (const auto end = lookup.end(); iter == end) {
954 iter = lookup.find({"classic", {}});
955 wxASSERT(iter != end);
956 }
957
958 mPreferredSystemAppearance = iter->second.preferredSystemAppearance;
959
960 ImageSize = iter->second.data.size();
961 if (ImageSize == 0)
962 // This must be the image compiler
963 return true;
964
965 pImage = iter->second.data.data();
966 //wxLogDebug("Reading ImageCache %p size %i", pImage, ImageSize );
967 wxMemoryInputStream InternalStream( pImage, ImageSize );
968
969 if( !ImageCache.LoadFile( InternalStream, wxBITMAP_TYPE_PNG ))
970 {
971 // If we get this message, it means that the data in file
972 // was not a valid png image.
973 // Most likely someone edited it by mistake,
974 // Or some experiment is being tried with NEW formats for it.
976 XO(
977"Audacity could not read its default theme.\nPlease report the problem."));
978 return false;
979 }
980 //wxLogDebug("Read %i by %i", ImageCache.GetWidth(), ImageCache.GetHeight() );
981 }
982
983 // Resize a large image down.
984 if( ImageCache.GetWidth() > ImageCacheWidth ){
985 int h = ImageCache.GetHeight() * ((1.0*ImageCacheWidth)/ImageCache.GetWidth());
986 ImageCache.Rescale( ImageCacheWidth, h );
987 }
988 FlowPacker context{ ImageCacheWidth };
989 // Load the bitmaps
990 for (size_t i = 0; i < resources.mImages.size(); ++i)
991 {
992 wxImage &Image = resources.mImages[i];
993 context.mFlags = mBitmapFlags[i];
994 if( !(mBitmapFlags[i] & resFlagInternal) )
995 {
996 context.GetNextPosition( Image.GetWidth(),Image.GetHeight() );
997 wxRect R = context.RectInner();
998 //wxLogDebug( "[%i, %i, %i, %i, \"%s\"], ", R.x, R.y, R.width, R.height, mBitmapNames[i].c_str() );
999 Image = GetSubImageWithAlpha( ImageCache, context.RectInner() );
1000 resources.mBitmaps[i] = wxBitmap(Image);
1001 }
1002 }
1003 if( !ImageCache.HasAlpha() )
1004 ImageCache.InitAlpha();
1005
1006// return true; //To not load colours..
1007 // Now load the colours.
1008 int x,y;
1009 context.SetColourGroup();
1010 wxColour TempColour;
1011 for (size_t i = 0; i < resources.mColours.size(); ++i)
1012 {
1013 context.GetNextPosition( iColSize, iColSize );
1014 context.RectMid( x, y );
1015 wxRect R = context.RectInner();
1016 //wxLogDebug( "[%i, %i, %i, %i, \"%s\"], ", R.x, R.y, R.width, R.height, mColourNames[i].c_str() );
1017 // Only change the colour if the alpha is opaque.
1018 // This allows us to add NEW colours more easily.
1019 if( ImageCache.GetAlpha(x,y ) > 128 )
1020 {
1021 TempColour = wxColour(
1022 ImageCache.GetRed( x,y),
1023 ImageCache.GetGreen( x,y),
1024 ImageCache.GetBlue(x,y));
1029 if( TempColour != wxColour(1,1,1) )
1030 resources.mColours[i] = TempColour;
1031 }
1032 }
1033 return true;
1034}
1035
1036void ThemeBase::LoadThemeComponents( bool bOkIfNotFound )
1037{
1038 ValueRestorer cleanup{ mpSet };
1039 for (auto &[key, data] : GetThemeCacheLookup())
1040 LoadOneThemeComponents( key.Internal(), bOkIfNotFound );
1041}
1042
1044{
1045 SwitchTheme( id );
1046 auto &resources = *mpSet;
1047 // IF directory doesn't exist THEN return early.
1048 const auto dir = ThemeComponentsDir(GetFilePath(), id);
1049 if( !wxDirExists( dir ))
1050 return;
1051
1052 using namespace BasicUI;
1053
1054 int n=0;
1055 FilePath FileName;
1056 for (size_t i = 0; i < resources.mImages.size(); ++i)
1057 {
1058 if( !(mBitmapFlags[i] & resFlagInternal) )
1059 {
1060 FileName = ThemeComponent( dir, mBitmapNames[i] );
1061 if( wxFileExists( FileName ))
1062 {
1063 if( !resources.mImages[i].LoadFile( FileName, wxBITMAP_TYPE_PNG ))
1064 {
1066 XO(
1067 /* i18n-hint: Do not translate png. It is the name of a file format.*/
1068"Audacity could not load file:\n %s.\nBad png format perhaps?")
1069 .Format( FileName ));
1070 return;
1071 }
1076 if( ! resources.mImages[i].HasAlpha() )
1077 {
1078 // wxLogDebug( wxT("File %s lacked alpha"), mBitmapNames[i] );
1079 resources.mImages[i].InitAlpha();
1080 }
1081 resources.mBitmaps[i] = wxBitmap( resources.mImages[i] );
1082 n++;
1083 }
1084 }
1085 }
1086
1087 // Now read complete information about the colors from one text file
1088 {
1089 const auto fName = wxFileName{ dir, ColorFileName }.GetFullPath();
1090 wxTextFile file{ fName };
1091 file.Open();
1092 if (!file.IsOpened())
1093 ShowMessageBox( XO("Couldn't read from file: %s").Format( fName ) );
1094 else {
1095 ++n;
1096 // Scan the line for name and #xxxxxx;
1097 static const std::wregex expr{
1098 LR"(^ *([_[:alnum:]]+).*#([0-9a-fA-F]{6});)" };
1099 const auto begin = mColourNames.begin(),
1100 end = mColourNames.end();
1101 std::unordered_set<wxString> names;
1102 for (auto str = file.GetFirstLine();
1103 !file.Eof(); str = file.GetNextLine()) {
1104 if (std::wsmatch match;
1105 regex_search( str.ToStdWstring(), match, expr )) {
1106 const wxString name{ match[1] };
1107 if (!names.insert(name).second)
1108 ShowMessageBox( Verbatim("Ignoring duplicate color name: %s")
1109 .Format( name ) );
1110 else if (const auto iter = std::find(begin, end, name);
1111 iter == end)
1113 Verbatim("Unrecognized color name: %s").Format( name ) );
1114 else {
1115 auto rrggbb =
1116 static_cast<unsigned>(stoi(match[2], nullptr, 16));
1117 unsigned char rr = (rrggbb >> 16) & 0xffu;
1118 unsigned char gg = (rrggbb >> 8) & 0xffu;
1119 unsigned char bb = rrggbb & 0xffu;
1120 resources.mColours[iter - begin] = { rr, gg, bb };
1121 }
1122 }
1123 }
1124 }
1125 }
1126
1127 if( n==0 )
1128 {
1129 if( bOkIfNotFound )
1130 return;
1132 XO(
1133"None of the expected theme component files\n were found in:\n %s.")
1134 .Format( dir ));
1135 }
1136}
1137
1139{
1140 ValueRestorer cleanup{ mpSet };
1141 for (auto &[key, data] : GetThemeCacheLookup())
1142 if (!SaveOneThemeComponents( key.Internal() ))
1143 // Some file failed to save, message was given
1144 return;
1146 XO("Themes written to:\n %s/*/Components/.").Format(GetFilePath()));
1147}
1148
1150{
1151 using namespace BasicUI;
1152 SwitchTheme( id );
1153 auto &resources = *mpSet;
1154 // IF directory doesn't exist THEN create it
1155 const auto dir = ThemeComponentsDir(GetFilePath(), id);
1156 if( !wxDirExists( dir ))
1157 {
1163#ifdef __WXMSW__
1164 wxMkDir( dir.fn_str() );
1165#else
1166 wxMkDir( dir.fn_str(), 0700 );
1167#endif
1168 if( !wxDirExists( dir ))
1169 {
1171 XO("Could not create directory:\n %s")
1172 .Format( dir ) );
1173 return false;
1174 }
1175 }
1176
1177 int n=0;
1178 FilePath FileName;
1179 for (size_t i = 0; i < resources.mImages.size(); ++i)
1180 {
1181 if( !(mBitmapFlags[i] & resFlagInternal) )
1182 {
1183 FileName = ThemeComponent( dir, mBitmapNames[i] );
1184 if( wxFileExists( FileName ))
1185 {
1186 ++n;
1187 break;
1188 }
1189 }
1190 }
1191
1192 if( wxFileExists( ThemeComponent( dir, ColorFileName ) ) )
1193 ++n;
1194
1195 using namespace BasicUI;
1196
1197 if (n > 0)
1198 {
1199 auto result =
1201 XO(
1202"Some required files in:\n %s\nwere already present. Overwrite?")
1203 .Format( dir ),
1205 .ButtonStyle(Button::YesNo)
1206 .DefaultIsNo());
1207 if (result == MessageBoxResult::No)
1208 return false;
1209 }
1210
1211 for (size_t i = 0; i < resources.mImages.size(); ++i)
1212 {
1213 if( !(mBitmapFlags[i] & resFlagInternal) )
1214 {
1215 FileName = ThemeComponent( dir, mBitmapNames[i] );
1216 if( !resources.mImages[i].SaveFile( FileName, wxBITMAP_TYPE_PNG ))
1217 {
1219 XO("Audacity could not save file:\n %s")
1220 .Format( FileName ));
1221 return false;
1222 }
1223 }
1224 }
1225
1226 // Now write complete information about the colors in one text file
1227 {
1228 const auto fName = wxFileName{ dir, ColorFileName }.GetFullPath();
1229 wxFileOutputStream ffStream{ fName };
1230 if (!ffStream.IsOk()) {
1231 ShowMessageBox( XO("Couldn't write to file: %s").Format( fName ) );
1232 return false;
1233 }
1234 wxTextOutputStream ss{ffStream};
1235 // Open with, for instance, ".darkTheme {"
1236 ss << "." << id.GET() << "Theme {\n";
1237 for (size_t i = 0; i < resources.mColours.size(); ++i) {
1238 const auto &colour = resources.mColours[i];
1239 ss
1240 // Write names, aligning the colons in a column,
1241 // followed by #rrggbb;
1242 << wxString::Format("%30s: #", mColourNames[i])
1243 << wxString::Format("%02x", colour.Red())
1244 << wxString::Format("%02x", colour.Green())
1245 << wxString::Format("%02x", colour.Blue())
1246 << ";\n";
1247 }
1248 ss << "}";
1249 }
1250
1251 return true;
1252}
1253
1255{
1256 ValueRestorer cleanup{ mpSet };
1257 for (auto &[key, data] : GetThemeCacheLookup()) {
1258 // false indicates not using standard binary method.
1259 if (!CreateOneImageCache(key.Internal(), false))
1260 // Some file failed to save, message was given
1261 return;
1262 }
1264 /* i18n-hint "Cee" means the C computer programming language */
1265 XO("Themes as Cee code written to:\n %s/*%s.")
1267}
1268
1269wxImage ThemeBase::MakeImageWithAlpha( wxBitmap & Bmp )
1270{
1271 // BUG in wxWidgets. Conversion from BMP to image does not preserve alpha.
1272 wxImage image( Bmp.ConvertToImage() );
1273 return image;
1274}
1275
1277{
1278 auto iter = mSets.begin(), end = mSets.end();
1279 while (iter != end) {
1280 if (mpSet == &iter->second)
1281 ++iter;
1282 else
1283 iter = mSets.erase(iter);
1284 }
1285}
1286
1287wxColour & ThemeBase::Colour( int iIndex )
1288{
1289 wxASSERT( iIndex >= 0 );
1290 auto &resources = *mpSet;
1292 return resources.mColours[iIndex];
1293}
1294
1295void ThemeBase::SetBrushColour( wxBrush & Brush, int iIndex )
1296{
1297 wxASSERT( iIndex >= 0 );
1298 Brush.SetColour( Colour( iIndex ));
1299}
1300
1301void ThemeBase::SetPenColour( wxPen & Pen, int iIndex )
1302{
1303 wxASSERT( iIndex >= 0 );
1304 Pen.SetColour( Colour( iIndex ));
1305}
1306
1307wxBitmap & ThemeBase::Bitmap( int iIndex )
1308{
1309 wxASSERT( iIndex >= 0 );
1310 auto &resources = *mpSet;
1312 return resources.mBitmaps[iIndex];
1313}
1314
1315wxImage & ThemeBase::Image( int iIndex )
1316{
1317 wxASSERT( iIndex >= 0 );
1318 auto &resources = *mpSet;
1320 return resources.mImages[iIndex];
1321}
1322wxSize ThemeBase::ImageSize( int iIndex )
1323{
1324 wxASSERT( iIndex >= 0 );
1325 auto &resources = *mpSet;
1327 wxImage & Image = resources.mImages[iIndex];
1328 return wxSize( Image.GetWidth(), Image.GetHeight());
1329}
1330
1332void ThemeBase::ReplaceImage( int iIndex, wxImage * pImage )
1333{
1334 Image( iIndex ) = *pImage;
1335 Bitmap( iIndex ) = wxBitmap( *pImage );
1336}
1337
1338void ThemeBase::RotateImageInto( int iTo, int iFrom, bool bClockwise )
1339{
1340 wxImage img(theTheme.Bitmap( iFrom ).ConvertToImage() );
1341 wxImage img2 = img.Rotate90( bClockwise );
1342 ReplaceImage( iTo, &img2 );
1343}
1344
1346{
1347 auto symbols = []{
1348 std::vector<EnumValueSymbol> symbols;
1349
1350 // Gather registered themes
1351 for (const auto &[symbol, data] : GetThemeCacheLookup())
1352 symbols.emplace_back(symbol);
1353
1354 // Sort the names, with built-in themes to the front,
1355 // conserving the ordering that was used in 3.1.0; otherwise
1356 // sorting other registered themes alphabetically by identifier
1357 static const Identifier names[] = {
1358 "classic", "light", "dark", "high-contrast"
1359 };
1360 static auto index = [](const EnumValueSymbol &symbol){
1361 auto begin = std::begin(names), end = std::end(names);
1362 return std::find(begin, end, symbol.Internal()) - begin;
1363 };
1364 std::stable_sort( symbols.begin(), symbols.end(),
1365 [](auto &a, auto &b){ return index(a) < index(b); } );
1366
1367 // Last, custom
1368 symbols.emplace_back(
1369 /* i18n-hint: user defined */
1370 "custom", XO("Custom")
1371 );
1372
1373 return symbols;
1374 };
1375
1376 constexpr int defaultTheme =
1377#ifdef EXPERIMENTAL_DA
1378 2 // "dark"
1379#else
1380 1 // "light"
1381#endif
1382 ;
1383
1384 static ChoiceSetting setting {
1385 wxT("/GUI/Theme"), symbols(), defaultTheme
1386 };
1387
1388 return setting;
1389}
1390
1391BoolSetting GUIBlendThemes{ wxT("/GUI/BlendThemes"), true };
Toolkit-neutral facade for basic user interface services.
static ThemeBase::RegisteredTheme theme
static const AudacityProject::AttachedObjects::RegisteredFactory key
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:82
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)
#define XO(s)
Definition: Internat.h:31
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:146
gPrefs Read(wxT("/GUI/VerticalZooming"), &bVZoom, false)
wxString FilePath
Definition: Project.h:20
static TranslatableStrings names
Definition: TagsEditor.cpp:151
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:286
wxString Read() const
Definition: Prefs.cpp:327
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:185
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:1043
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:904
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:1036
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:204
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:256
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:104
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