1: /*
     2:  ============================================================================
     3:  xoblite™ -> an advanced "extended shell" for Microsoft® Windows® 11
     4:  Copyright © 2002-2026 Karl Henrik Henriksson [qwilk/@xoblite]
     5:  Copyright © 2001-2004 The Blackbox for Windows Development Team
     6:  http://xoblite.net/ + https://github.com/xoblite/
     7:  ============================================================================
     8: 
     9:   Blackbox for Windows is free software, released under the
    10:   GNU General Public License (GPL version 2 or later), with an extension
    11:   that allows linking of proprietary modules under a controlled interface.
    12:   What this means is that plugins etc. are allowed to be released
    13:   under any license the author wishes. Please note, however, that the
    14:   original Blackbox gradient math code used in Blackbox for Windows
    15:   is available under the BSD license.
    16: 
    17:   http://www.fsf.org/licenses/gpl.html
    18:   http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
    19:   http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
    20: 
    21:   This program is distributed in the hope that it will be useful,
    22:   but WITHOUT ANY WARRANTY; without even the implied warranty of
    23:   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    24:   GNU General Public License for more details.
    25: 
    26:   For additional license information, please read the included license.html.
    27: 
    28:  ============================================================================
    29: */
    30: 
    31: #include "Themes.h" 
    32: #include <io.h> 
    33: 
    34: extern Settings* pSettings;
    35: extern Desktop* pDesktop;
    36: 
    37: typedef struct fontListItem
    38: {
    39:     char fontFile[MAX_PATH];
    40: } fontListItem;
    41: 
    42: typedef vector<fontListItem*> fontListVector;
    43: fontListVector fontList;
    44: 
    45: //===========================================================================
    46: 
    47: bool SetTheme(LPSTR themePath, bool startingUp)
    48: {
    49:     if (startingUp) ZeroMemory(&fontList, sizeof(fontList));
    50: 
    51:     if (themePath != 0)
    52:     {
    53:         char path[MAX_LINE_LENGTH], theme[MAX_LINE_LENGTH];
    54:         strcpy_s(theme, sizeofArray(theme), themePath);
    55:         if (strchr(theme, '\"')) StrRemoveEncap(theme);
    56:         if (strchr(theme, '$')) ReplaceShellFolders(theme);
    57:         if (strchr(theme, '%')) ReplaceEnvVars(theme);
    58:         if (theme[strnlen_s(theme, sizeofArray(theme))-1] == '\\') theme[strnlen_s(theme, sizeofArray(theme))-1] = 0;
    59: 
    60:         //====================
    61: 
    62:         // Is the requested theme the same as the current theme? If so, just reload it... (i.e. restart operation)
    63:         if (!startingUp && !_stricmp(pSettings->SF_currentThemePath, theme))
    64:         {
    65:             char temp[MAX_LINE_LENGTH];
    66:             if (!_stricmp(pSettings->selectedTheme, "[Default]")) strcpy_s(temp, sizeofArray(temp), "Reloading the default theme...");
    67:             else sprintf_s(temp, sizeofArray(temp), "Reloading the \"%s\" theme...", pSettings->selectedTheme);
    68:             SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)temp);
    69: 
    70:             restartBlackboxStop();
    71:             UnregisterThemeFonts();
    72:             RegisterThemeFonts();
    73:             restartBlackboxStart();
    74: 
    75:             PlaySoundFX(SFX_THEME_CHANGE);
    76: 
    77:             return true;
    78:         }
    79: 
    80:         //====================
    81: 
    82:         // Otherwise, first make sure the requested theme is valid...
    83:         // (i.e. being either the [Default] theme, or another theme having at least a configuration xoblite.rc/blackbox.rc file)
    84:         bool validTheme = !_stricmp(pSettings->SF_blackboxPath, theme);
    85:         if (!validTheme)
    86:         {
    87:             strcpy_s(path, sizeofArray(path), theme);
    88:             strcat_s(path, sizeofArray(path), "\\xoblite.rc");
    89:             validTheme = FileExists(path);
    90:             if (!validTheme)
    91:             {
    92:                 strcpy_s(path, sizeofArray(path), theme);
    93:                 strcat_s(path, sizeofArray(path), "\\blackbox.rc");
    94:                 validTheme = FileExists(path);
    95:             }
    96:         }
    97: 
    98:         if (validTheme)
    99:         {
   100:             // Save settings, unload plugins etc...
   101:             if (!startingUp) restartBlackboxStop();
   102: 
   103:             // Unload font resources for the old theme...
   104:             if (!startingUp) UnregisterThemeFonts();
   105: 
   106:             // Set the new theme path...
   107:             strcpy_s(pSettings->SF_currentThemePath, sizeofArray(pSettings->SF_currentThemePath), theme);
   108:             pSettings->usingDefaultTheme = !_stricmp(pSettings->SF_blackboxPath, pSettings->SF_currentThemePath);
   109: 
   110:             //====================
   111: 
   112:             // Are we using the [Default] theme?
   113:             if (pSettings->usingDefaultTheme)
   114:             {
   115:                 // Reset the main configuration file paths to initiate
   116:                 // a new ConfigFileExists check for each of them...
   117:                 strcpy_s(pSettings->bbrcFile, sizeofArray(pSettings->bbrcFile), "");
   118:                 bbrcPath();
   119: 
   120:                 strcpy_s(pSettings->xobrcFile, sizeofArray(pSettings->xobrcFile), "");
   121:                 extensionsrcPath();
   122:                 strcpy_s(pSettings->xobrcDefaultFile, sizeofArray(pSettings->xobrcDefaultFile), pSettings->xobrcFile);
   123: 
   124: //              strcpy_s(pSettings->pluginsFile, sizeofArray(pSettings->pluginsFile), "");
   125: //              plugrcPath();
   126: 
   127:                 if (!startingUp)
   128:                 {
   129:                     pSettings->writeProtection = false;
   130:                     strcpy_s(pSettings->selectedTheme, sizeofArray(pSettings->selectedTheme), "[Default]");
   131:                     WriteString(pSettings->xobrcDefaultFile, "xoblite.selected.theme:", pSettings->selectedTheme);
   132: 
   133:                     // Show theme change message in the console...
   134:                     SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)"Switching to the default theme...");
   135:                 }
   136:             }
   137: 
   138:             //====================
   139: 
   140:             else // Nope, we are using any other theme than the [Default] theme...
   141:             {
   142:                 // Set the new theme's configuration file paths...
   143:                 strcpy_s(path, sizeofArray(path), pSettings->SF_currentThemePath);
   144:                 strcat_s(path, sizeofArray(path), "\\xoblite.rc");
   145:                 if (FileExists(path))
   146:                 {
   147:                     bbrcPath(path);
   148:                     extensionsrcPath(path);
   149:                 }
   150:                 else
   151:                 {
   152:                     strcpy_s(path, sizeofArray(path), pSettings->SF_currentThemePath);
   153:                     strcat_s(path, sizeofArray(path), "\\blackbox.rc");
   154:                     if (FileExists(path)) bbrcPath(path);
   155:                     strcpy_s(path, sizeofArray(path), pSettings->SF_currentThemePath);
   156:                     strcat_s(path, sizeofArray(path), "\\extensions.rc");
   157:                     if (FileExists(path)) extensionsrcPath(path);
   158:                 }
   159: /*
   160: //              strcpy_s(path, sizeofArray(path), pSettings->SF_currentThemePath);
   161: //              strcat_s(path, sizeofArray(path), "\\plugins.rc");
   162: //              strcpy_s(path, sizeofArray(path), pSettings->pluginsFile);
   163: //              if (FileExists(path)) plugrcPath(path);
   164: //              else
   165:                 strcpy_s(pSettings->pluginsFile, sizeofArray(pSettings->pluginsFile), ReadString(pSettings->xobrcDefaultFile, "xoblite.pluginsFile:", "$Blackbox$\\plugins.rc"));
   166:                 if (strchr(pSettings->pluginsFile, '\"')) StrRemoveEncap(pSettings->pluginsFile);
   167:                 if (strchr(pSettings->pluginsFile, '$')) ReplaceShellFolders(pSettings->pluginsFile);
   168:                 if (strchr(pSettings->pluginsFile, '%')) ReplaceEnvVars(pSettings->pluginsFile);
   169: 
   170:                 if (!FileExists(pSettings->pluginsFile))
   171:                 {
   172:                     // Reset plugin.rc file path to initiate a new ConfigFileExists check...
   173:                     // (this allows for xoblite.rc (or bb+ext.rc) only themes using a global plugins.rc)
   174: //                  strcpy_s(pSettings->pluginsFile, sizeofArray(pSettings->pluginsFile), "");
   175:                     GetBlackboxPath(path, sizeof(path));
   176:                     strcat_s(path, sizeofArray(path), "\\plugins.rc");
   177:                     plugrcPath();
   178:                 }
   179: */
   180:                 if (!startingUp)
   181:                 {
   182:                     pSettings->writeProtection = false;
   183:                     strcpy_s(pSettings->selectedTheme, sizeofArray(pSettings->selectedTheme), &pSettings->SF_currentThemePath[strnlen_s(pSettings->themesFolder, sizeofArray(pSettings->themesFolder))+1]);
   184:                     WriteString(pSettings->xobrcDefaultFile, "xoblite.selected.theme:", pSettings->selectedTheme);
   185: 
   186:                     // Show theme change message in the console...
   187:                     char temp[MAX_LINE_LENGTH];
   188:                     sprintf_s(temp, sizeofArray(temp), "Switching to the \"%s\" theme...", pSettings->selectedTheme);
   189:                     SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)temp);
   190:                 }
   191:             }
   192: 
   193:             //====================
   194: 
   195:             if (!startingUp)
   196:             {
   197:                 // Load font resources for the new theme...
   198:                 RegisterThemeFonts();
   199: 
   200:                 // Finally, we load the new theme settings...
   201:                 restartBlackboxStart();
   202:                 // ...and point out the theme change audibly as well... ;)
   203:                 PlaySoundFX(SFX_THEME_CHANGE);
   204: 
   205:                 pSettings->Statistics.changedTheme++; // Update statistics...
   206:             }
   207:         }
   208: 
   209:         //====================
   210: 
   211:         else if (!startingUp)
   212:         {
   213:             // The specified theme path wasn't valid!
   214:             char msg[MAX_LINE_LENGTH];
   215:             sprintf_s(msg, sizeofArray(msg), "%s   \n\ndoes not seem to be a valid theme path.   \nPlease check your related configuration file.   ", themePath);
   216:             MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST);
   217:             return false;
   218:         }
   219:     }
   220: 
   221:     return false;
   222: }
   223: 
   224: //===========================================================================
   225: // Function: RegisterThemeFonts / UnregisterThemeFonts
   226: // Purpose: Dynamically adds / removes theme specific font resources
   227: //===========================================================================
   228: 
   229: void RegisterThemeFonts()
   230: {
   231:     if (fontList.size() > 0) UnregisterThemeFonts(); // Safeguard
   232:     if (pSettings->disableThemeFonts) return;
   233: 
   234:     char fontsFolder[MAX_PATH];
   235:     strcpy_s(fontsFolder, sizeofArray(fontsFolder), pSettings->SF_currentThemePath);
   236:     strcat_s(fontsFolder, sizeofArray(fontsFolder), "\\fonts");
   237:     if (!FolderExists(fontsFolder))
   238:     {
   239:         strcpy_s(fontsFolder, sizeofArray(fontsFolder), pSettings->SF_blackboxPath);
   240:         strcat_s(fontsFolder, sizeofArray(fontsFolder), "\\fonts");
   241:     }
   242: 
   243:     if (FolderExists(fontsFolder))
   244:     {
   245:         char searchPattern[MAX_PATH];
   246:         strcpy_s(searchPattern, sizeofArray(searchPattern), fontsFolder);
   247:         strcat_s(searchPattern, sizeofArray(searchPattern), "\\*");
   248: 
   249:         _finddata_t found;
   250:         long handle = _findfirst(searchPattern, &found);
   251:         if (handle != -1)
   252:         {
   253:             char fontFile[MAX_PATH];
   254: 
   255:             do
   256:             {
   257:                 // Skip hidden files and any subfolders... (nb. we only register fonts in the main /fonts folder for a theme!)
   258:                 if (strcmp(found.name, ".") == 0 || strcmp(found.name, "..") == 0 || (found.attrib & _A_HIDDEN)) continue;
   259:                 if (found.attrib & _A_SUBDIR) continue;
   260: 
   261:                 // Have we found a valid font file?
   262:                 if (IsInString(found.name, ".ttf") || IsInString(found.name, ".otf") || IsInString(found.name, ".fon"))
   263:                 {
   264:                     // Add the font resource, marked as private to our process, to the system...
   265:                     sprintf_s(fontFile, sizeofArray(fontFile), "%s\\%s", fontsFolder, found.name);
   266:                     if (AddFontResourceEx(fontFile, FR_PRIVATE, 0) > 0)
   267:                     {
   268:                         fontListItem* addedFont = new fontListItem;
   269:                         strcpy_s(addedFont->fontFile, sizeofArray(addedFont->fontFile), fontFile);
   270:                         fontList.push_back(addedFont);
   271:                     }
   272:                 }
   273:             }
   274:             while (_findnext(handle, &found) == 0);
   275:         }
   276: 
   277:         _findclose(handle);
   278: 
   279:         if (fontList.size() > 0)
   280:         {
   281:             PostMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
   282: 
   283:             char msg[255];
   284:             if (fontList.size() > 1) sprintf_s(msg, sizeofArray(msg), "Adding theme fonts -> %d fonts registered.", fontList.size());
   285:             else sprintf_s(msg, sizeofArray(msg), "Adding theme fonts -> %d font registered.", fontList.size());
   286:             SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
   287:         }
   288:     }
   289: }
   290: 
   291: //====================
   292: 
   293: void UnregisterThemeFonts()
   294: {
   295:     if (fontList.empty()) return;
   296: 
   297:     int fontsUnregistered = fontList.size();
   298: 
   299:     for (int i=0; i<(int)fontList.size(); i++)
   300:     {
   301:         RemoveFontResourceEx(fontList[i]->fontFile, FR_PRIVATE, 0);
   302:         delete fontList[i];
   303:     }
   304:     fontList.clear();
   305: //  ZeroMemory(&fontList, sizeof(fontList));
   306: 
   307:     PostMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
   308: /*
   309:     if (pSettings->debugLogging)
   310:     {
   311:         char msg[255];
   312:         if (fontsUnregistered > 1) sprintf_s(msg, sizeofArray(msg), "Removing theme fonts -> %d fonts unregistered.", fontsUnregistered);
   313:         else sprintf_s(msg, sizeofArray(msg), "Removing theme fonts -> %d font unregistered.", fontsUnregistered);
   314:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
   315:     }
   316: */
   317: }
   318: 
   319: //===========================================================================
   320: