1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 
18 #if defined(__GNUG__) && !defined(__APPLE__)
19 #pragma implementation "SkinManager.h"
20 #endif
21 
22 #include "stdwx.h"
23 #include "diagnostics.h"
24 #include "parse.h"
25 #include "util.h"
26 #include "error_numbers.h"
27 #include "miofile.h"
28 #include "filesys.h"
29 #include "BOINCGUIApp.h"
30 #include "BOINCBaseFrame.h"
31 #include "SkinManager.h"
32 #include "MainDocument.h"
33 #include "version.h"
34 
35 
36 ////@begin XPM images
37 #include "res/skins/default/graphic/background_image.xpm"
38 #include "res/skins/default/graphic/dialog_background_image.xpm"
39 #include "res/skins/default/graphic/project_image.xpm"
40 #include "res/skins/default/graphic/workunit_animation_image.xpm"
41 #include "res/skins/default/graphic/workunit_running_image.xpm"
42 #include "res/skins/default/graphic/workunit_suspended_image.xpm"
43 #include "res/skins/default/graphic/workunit_waiting_image.xpm"
44 #include "res/boinc.xpm"
45 #include "res/boinc32.xpm"
46 #include "res/boincdisconnect.xpm"
47 #include "res/boincdisconnect32.xpm"
48 #include "res/boincsnooze.xpm"
49 #include "res/boincsnooze32.xpm"
50 #include "res/boinc_logo.xpm"
51 ////@end XPM images
52 
53 
54 // Flag to enable the various error messages
55 static bool show_error_msgs = false;
56 
57 
IMPLEMENT_DYNAMIC_CLASS(CSkinItem,wxObject)58 IMPLEMENT_DYNAMIC_CLASS(CSkinItem, wxObject)
59 
60 
61 CSkinItem::CSkinItem() {
62 }
63 
64 
~CSkinItem()65 CSkinItem::~CSkinItem() {
66 }
67 
68 
ParseColor(wxString strColor)69 wxColour CSkinItem::ParseColor(wxString strColor) {
70     long red, green, blue;
71     wxStringTokenizer tkz(strColor, wxT(":"), wxTOKEN_RET_EMPTY);
72     wxString(tkz.GetNextToken()).ToLong(&red);
73 	wxString(tkz.GetNextToken()).ToLong(&green);
74 	wxString(tkz.GetNextToken()).ToLong(&blue);
75     return wxColour((unsigned char)red, (unsigned char)green, (unsigned char)blue);
76 }
77 
78 
IMPLEMENT_DYNAMIC_CLASS(CSkinImage,CSkinItem)79 IMPLEMENT_DYNAMIC_CLASS(CSkinImage, CSkinItem)
80 
81 
82 CSkinImage::CSkinImage() {
83     Clear();
84 }
85 
86 
~CSkinImage()87 CSkinImage::~CSkinImage() {
88     Clear();
89 }
90 
91 
Clear()92 void CSkinImage::Clear() {
93     m_strDesiredBitmap.Clear();
94     m_strDesiredBackgroundColor.Clear();
95     m_bmpBitmap = wxNullBitmap;
96     m_colBackgroundColor = wxNullColour;
97     m_iAnchorHorizontal = -1;
98     m_iAnchorVertical = -1;
99 
100 }
101 
102 
Parse(MIOFILE & in)103 int CSkinImage::Parse(MIOFILE& in) {
104     char buf[256];
105     std::string strBuffer;
106 
107     while (in.fgets(buf, 256)) {
108         if (match_tag(buf, "</image>")) break;
109         else if (parse_str(buf, "<imagesrc>", strBuffer)) {
110             if (strBuffer.length()) {
111                 m_strDesiredBitmap = wxString(
112                     wxGetApp().GetSkinManager()->ConstructSkinPath() +
113                     wxString(strBuffer.c_str(), wxConvUTF8)
114                 );
115             }
116             continue;
117         } else if (parse_str(buf, "<background_color>", strBuffer)) {
118             if (strBuffer.length()) {
119                 m_strDesiredBackgroundColor = wxString(strBuffer.c_str(), wxConvUTF8);
120             }
121             continue;
122         } else if (match_tag(buf, "<anchor_horizontal_left>")) {
123             m_iAnchorHorizontal = BKGD_ANCHOR_HORIZ_LEFT;
124         } else if (match_tag(buf, "<anchor_horizontal_center>")) {
125             m_iAnchorHorizontal = BKGD_ANCHOR_HORIZ_CENTER;
126         } else if (match_tag(buf, "<anchor_horizontal_right>")) {
127             m_iAnchorHorizontal = BKGD_ANCHOR_HORIZ_RIGHT;
128         } else if (match_tag(buf, "<anchor_vertical_top>")) {
129             m_iAnchorVertical = BKGD_ANCHOR_VERT_TOP;;
130         } else if (match_tag(buf, "<anchor_vertical_center>")) {
131             m_iAnchorVertical = BKGD_ANCHOR_VERT_CENTER;;
132         } else if (match_tag(buf, "<anchor_vertical_bottom>")) {
133             m_iAnchorVertical = BKGD_ANCHOR_VERT_BOTTOM;;
134         }
135     }
136 
137     return BOINC_SUCCESS;
138 }
139 
140 
GetBitmap()141 wxBitmap* CSkinImage::GetBitmap() {
142     Validate();
143     return &m_bmpBitmap;
144 }
145 
146 
GetBackgroundColor()147 wxColour* CSkinImage::GetBackgroundColor() {
148     Validate();
149     return &m_colBackgroundColor;
150 }
151 
152 
SetDefaults(wxString strComponentName,const char ** ppDefaultBitmap)153 bool CSkinImage::SetDefaults(wxString strComponentName, const char** ppDefaultBitmap) {
154     m_strComponentName = strComponentName;
155     m_ppDefaultBitmap = ppDefaultBitmap;
156     return true;
157 }
158 
159 
SetDefaults(wxString strComponentName,const char ** ppDefaultBitmap,wxString strBackgroundColor,int horizontalAnchor,int verticalAnchor)160 bool CSkinImage::SetDefaults(wxString strComponentName,
161                                 const char** ppDefaultBitmap,
162                                 wxString strBackgroundColor,
163                                 int horizontalAnchor,
164                                 int verticalAnchor
165                             ) {
166     m_strComponentName = strComponentName;
167     m_ppDefaultBitmap = ppDefaultBitmap;
168     m_strDefaultBackgroundColor = strBackgroundColor;
169     if (m_iAnchorHorizontal < 0) {
170         m_iAnchorHorizontal = horizontalAnchor;
171     }
172     if (m_iAnchorVertical < 0) {
173         m_iAnchorVertical = verticalAnchor;
174     }
175     return true;
176 }
177 
178 
Validate()179 bool CSkinImage::Validate() {
180     if (!m_bmpBitmap.Ok()) {
181         if (!m_strDesiredBitmap.IsEmpty()) {
182             wxImage img = wxImage(m_strDesiredBitmap, wxBITMAP_TYPE_ANY);
183             if (img.IsOk()) {
184 #ifdef __WXMSW__
185 // TODO: Choose from multiple size images if provided, else resize the closest one
186                 if ((GetXDPIScaling() > 1.05) || (GetYDPIScaling() > 1.05)) {
187                     img.Rescale((int) (img.GetWidth()*GetXDPIScaling()),
188                                 (int) (img.GetHeight()*GetYDPIScaling()),
189                                 wxIMAGE_QUALITY_BILINEAR
190                             );
191                 }
192 #endif
193                 m_bmpBitmap = wxBitmap(img);
194             }
195         }
196         if (!m_bmpBitmap.Ok()) {
197             if (show_error_msgs) {
198                 fprintf(stderr, "Skin Manager: Failed to load '%s' image. Using default.\n", (const char *)m_strComponentName.mb_str());
199             }
200             m_bmpBitmap = GetScaledBitmapFromXPMData(m_ppDefaultBitmap);
201             wxASSERT(m_bmpBitmap.Ok());
202         }
203     }
204     if (!m_colBackgroundColor.Ok()) {
205         if (!m_strDesiredBackgroundColor.IsEmpty()) {
206             m_colBackgroundColor = ParseColor(m_strDesiredBackgroundColor);
207         }
208         if (!m_colBackgroundColor.Ok()) {
209             if (show_error_msgs) {
210                 fprintf(stderr, "Skin Manager: Failed to load '%s' background color. Using default.\n", (const char *)m_strComponentName.mb_str());
211             }
212             m_colBackgroundColor = ParseColor(m_strDefaultBackgroundColor);
213             wxASSERT(m_colBackgroundColor.Ok());
214         }
215     }
216     return true;
217 }
218 
219 
IMPLEMENT_DYNAMIC_CLASS(CSkinIcon,CSkinItem)220 IMPLEMENT_DYNAMIC_CLASS(CSkinIcon, CSkinItem)
221 
222 
223 CSkinIcon::CSkinIcon() {
224     Clear();
225 }
226 
227 
~CSkinIcon()228 CSkinIcon::~CSkinIcon() {
229     Clear();
230 }
231 
232 
Clear()233 void CSkinIcon::Clear() {
234     m_strDesiredIcon.Clear();
235     m_strDesiredIcon32.Clear();
236     m_strDesiredTransparencyMask.Clear();
237     m_strDesiredTransparencyMask32.Clear();
238     m_icoIcon = m_icoDefaultIcon;
239 }
240 
241 
Parse(MIOFILE & in)242 int CSkinIcon::Parse(MIOFILE& in) {
243     char buf[256];
244     std::string strBuffer;
245 
246     while (in.fgets(buf, 256)) {
247         if (match_tag(buf, "</image>")) break;
248         else if (parse_str(buf, "<imagesrc>", strBuffer)) {
249             if (strBuffer.length()) {
250                 m_strDesiredIcon = wxString(
251                     wxGetApp().GetSkinManager()->ConstructSkinPath() +
252                     wxString(strBuffer.c_str(), wxConvUTF8)
253                 );
254             }
255             continue;
256         } else if (parse_str(buf, "<transparency_mask>", strBuffer)) {
257             if (strBuffer.length()) {
258                 m_strDesiredTransparencyMask = wxString(strBuffer.c_str(), wxConvUTF8);
259             }
260             continue;
261         }
262     }
263 
264     return BOINC_SUCCESS;
265 }
266 
267 
Parse32(MIOFILE & in)268 int CSkinIcon::Parse32(MIOFILE& in) {
269     char buf[256];
270     std::string strBuffer;
271 
272     while (in.fgets(buf, 256)) {
273         if (match_tag(buf, "</image>")) break;
274         else if (parse_str(buf, "<imagesrc>", strBuffer)) {
275             if (strBuffer.length()) {
276                 m_strDesiredIcon32 = wxString(
277                     wxGetApp().GetSkinManager()->ConstructSkinPath() +
278                     wxString(strBuffer.c_str(), wxConvUTF8)
279                 );
280             }
281             continue;
282         } else if (parse_str(buf, "<transparency_mask>", strBuffer)) {
283             if (strBuffer.length()) {
284                 m_strDesiredTransparencyMask32 = wxString(strBuffer.c_str(), wxConvUTF8);
285             }
286             continue;
287         }
288     }
289 
290     return BOINC_SUCCESS;
291 }
292 
293 
GetIcon()294 wxIconBundle* CSkinIcon::GetIcon() {
295     Validate();
296     return &m_icoIcon;
297 }
298 
299 
SetDefaults(wxString strComponentName,wxString strIcon)300 bool CSkinIcon::SetDefaults(wxString strComponentName, wxString strIcon) {
301     m_strComponentName = strComponentName;
302     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 16, 16));
303     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 20, 20));
304     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 24, 24));
305     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 32, 32));
306     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 40, 40));
307     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 48, 48));
308     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 64, 64));
309     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 80, 80));
310     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 96, 96));
311     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 128, 128));
312     m_icoDefaultIcon.AddIcon(wxIcon(strIcon, wxICON_DEFAULT_TYPE, 256, 256));
313     return true;
314 }
315 
316 
SetDefaults(wxString strComponentName,const char ** m_ppIcon,const char ** m_ppIcon32)317 bool CSkinIcon::SetDefaults(wxString strComponentName, const char** m_ppIcon, const char** m_ppIcon32) {
318     m_strComponentName = strComponentName;
319     m_icoDefaultIcon.AddIcon(wxIcon(m_ppIcon));
320     m_icoDefaultIcon.AddIcon(wxIcon(m_ppIcon32));
321     return true;
322 }
323 
324 
Validate()325 bool CSkinIcon::Validate() {
326     if (!m_strDesiredIcon.IsEmpty()) {
327         // Configure bitmap object with optional transparency mask
328         wxImage img = wxImage(m_strDesiredIcon, wxBITMAP_TYPE_ANY);
329         wxBitmap bmp = wxBitmap(img);
330         // If PNG file has alpha channel use it as mask & ignore <transparency_mask> tag
331         if (!(m_strDesiredTransparencyMask.IsEmpty() || img.HasAlpha())) {
332             bmp.SetMask(new wxMask(bmp, ParseColor(m_strDesiredTransparencyMask)));
333         }
334         // Now set the icon object using the newly created bitmap with optional transparency mask
335         wxIcon ico;
336         ico.CopyFromBitmap(bmp);
337         m_icoIcon.AddIcon(ico);
338     }
339     if (!m_strDesiredIcon32.IsEmpty()) {
340         // Configure bitmap object with optional transparency mask
341         wxImage img32 = wxImage(m_strDesiredIcon32, wxBITMAP_TYPE_ANY);
342         wxBitmap bmp32 = wxBitmap(img32);
343         // If PNG file has alpha channel use it as mask & ignore <transparency_mask> tag
344         if (!(m_strDesiredTransparencyMask32.IsEmpty() || img32.HasAlpha())) {
345             bmp32.SetMask(new wxMask(bmp32, ParseColor(m_strDesiredTransparencyMask32)));
346         }
347         // Now set the icon object using the newly created bitmap with optional transparency mask
348         wxIcon ico32;
349         ico32.CopyFromBitmap(bmp32);
350         m_icoIcon.AddIcon(ico32);
351     }
352     if (!m_icoIcon.Ok()) {
353         if (show_error_msgs) {
354             fprintf(stderr, "Skin Manager: Failed to load '%s' icon. Using default.\n", (const char *)m_strComponentName.mb_str());
355         }
356         m_icoIcon = m_icoDefaultIcon;
357         wxASSERT(m_icoIcon.Ok());
358     }
359     return true;
360 }
361 
362 
IMPLEMENT_DYNAMIC_CLASS(CSkinSimple,CSkinItem)363 IMPLEMENT_DYNAMIC_CLASS(CSkinSimple, CSkinItem)
364 
365 
366 CSkinSimple::CSkinSimple() {
367     Clear();
368 }
369 
370 
~CSkinSimple()371 CSkinSimple::~CSkinSimple() {
372     Clear();
373 }
374 
375 
Clear()376 void CSkinSimple::Clear() {
377 	m_BackgroundImage.Clear();
378     m_DialogBackgroundImage.Clear();
379     m_ProjectImage.Clear();
380 	m_StaticLineColor = wxNullColour;
381 	m_NoticeAlertColor = *wxRED;
382     m_WorkunitAnimationImage.Clear();
383     m_WorkunitRunningImage.Clear();
384     m_WorkunitSuspendedImage.Clear();
385     m_WorkunitWaitingImage.Clear();
386     m_iPanelOpacity = DEFAULT_OPACITY;
387 }
388 
389 
Parse(MIOFILE & in)390 int CSkinSimple::Parse(MIOFILE& in) {
391     char buf[256];
392     std::string strBuffer;
393 
394     while (in.fgets(buf, 256)) {
395         if (match_tag(buf, "</simple>")) break;
396         else if (match_tag(buf, "<background_image>")) {
397             m_BackgroundImage.Parse(in);
398             continue;
399         } else if (match_tag(buf, "<dialog_background_image>")) {
400             m_DialogBackgroundImage.Parse(in);
401             continue;
402         } else if (match_tag(buf, "<project_image>")) {
403             m_ProjectImage.Parse(in);
404             continue;
405         } else if (parse_str(buf, "<static_line_color>", strBuffer)) {
406             m_StaticLineColor = ParseColor(wxString(strBuffer.c_str(), wxConvUTF8));
407             continue;
408         } else if (parse_str(buf, "<notice_alert_color>", strBuffer)) {
409             m_NoticeAlertColor = ParseColor(wxString(strBuffer.c_str(), wxConvUTF8));
410             continue;
411         } else if (match_tag(buf, "<workunit_animation_image>")) {
412             m_WorkunitAnimationImage.Parse(in);
413             continue;
414         } else if (match_tag(buf, "<workunit_running_image>")) {
415             m_WorkunitRunningImage.Parse(in);
416             continue;
417         } else if (match_tag(buf, "<workunit_suspended_image>")) {
418             m_WorkunitSuspendedImage.Parse(in);
419             continue;
420         } else if (match_tag(buf, "<workunit_waiting_image>")) {
421             m_WorkunitWaitingImage.Parse(in);
422             continue;
423         } else if (parse_int(buf, "<panel_opacity>", m_iPanelOpacity)) {
424             continue;
425         }
426     }
427 
428     InitializeDelayedValidation();
429 
430     return 0;
431 }
432 
433 
InitializeDelayedValidation()434 bool CSkinSimple::InitializeDelayedValidation() {
435     m_BackgroundImage.SetDefaults(
436         wxT("background"), (const char**)background_image_xpm,
437         wxT("211:211:211"), BKGD_ANCHOR_HORIZ_LEFT, BKGD_ANCHOR_VERT_TOP
438     );
439     m_DialogBackgroundImage.SetDefaults(
440         wxT("dialog background"), (const char**)dialog_background_image_xpm,
441         wxT("255:255:255"), BKGD_ANCHOR_HORIZ_CENTER, BKGD_ANCHOR_VERT_CENTER
442     );
443     m_ProjectImage.SetDefaults(
444         wxT("project"), (const char**)project_image_xpm
445     );
446      m_WorkunitAnimationImage.SetDefaults(
447         wxT("workunit animation"), (const char**)workunit_animation_image_xpm
448     );
449      m_WorkunitRunningImage.SetDefaults(
450         wxT("workunit running"), (const char**)workunit_running_image_xpm
451     );
452      m_WorkunitSuspendedImage.SetDefaults(
453         wxT("workunit suspended"), (const char**)workunit_suspended_image_xpm
454     );
455      m_WorkunitWaitingImage.SetDefaults(
456         wxT("workunit waiting"), (const char**)workunit_waiting_image_xpm
457     );
458     return true;
459 }
460 
461 
IMPLEMENT_DYNAMIC_CLASS(CSkinAdvanced,CSkinItem)462 IMPLEMENT_DYNAMIC_CLASS(CSkinAdvanced, CSkinItem)
463 
464 
465 CSkinAdvanced::CSkinAdvanced() {
466     Clear();
467 }
468 
469 
~CSkinAdvanced()470 CSkinAdvanced::~CSkinAdvanced() {
471     Clear();
472 }
473 
474 
Clear()475 void CSkinAdvanced::Clear() {
476     m_bIsBranded = false;
477     m_strApplicationName = wxEmptyString;
478     m_strApplicationShortName = wxEmptyString;
479     m_iconApplicationIcon.Clear();
480     m_iconApplicationDisconnectedIcon.Clear();
481     m_iconApplicationSnoozeIcon.Clear();
482     m_bitmapApplicationLogo = wxNullBitmap;
483     m_strOrganizationName = wxEmptyString;
484     m_strOrganizationWebsite = wxEmptyString;
485     m_strOrganizationHelpUrl = wxEmptyString;
486     m_bDefaultTabSpecified = false;
487     m_iDefaultTab = 0;
488     m_strExitMessage = wxEmptyString;
489 }
490 
491 
Parse(MIOFILE & in)492 int CSkinAdvanced::Parse(MIOFILE& in) {
493     char buf[256];
494     std::string strBuffer;
495 
496     while (in.fgets(buf, 256)) {
497         if (match_tag(buf, "</advanced>")) break;
498         else if (parse_bool(buf, "is_branded", m_bIsBranded)) continue;
499         else if (parse_str(buf, "<application_name>", strBuffer)) {
500             m_strApplicationName = wxString(strBuffer.c_str(), wxConvUTF8);
501             continue;
502         } else if (parse_str(buf, "<application_short_name>", strBuffer)) {
503             m_strApplicationShortName = wxString(strBuffer.c_str(), wxConvUTF8);
504             continue;
505         } else if (match_tag(buf, "<application_icon>")) {
506             m_iconApplicationIcon.Parse(in);
507             continue;
508         } else if (match_tag(buf, "<application_icon32>")) {
509             m_iconApplicationIcon.Parse32(in);
510             continue;
511         } else if (match_tag(buf, "<application_disconnected_icon>")) {
512             m_iconApplicationDisconnectedIcon.Parse(in);
513             continue;
514         } else if (match_tag(buf, "<application_snooze_icon>")) {
515             m_iconApplicationSnoozeIcon.Parse(in);
516             continue;
517         } else if (parse_str(buf, "<application_logo>", strBuffer)) {
518             if(strBuffer.length()) {
519                 wxString str = wxString(
520                     wxGetApp().GetSkinManager()->ConstructSkinPath() +
521                     wxString(strBuffer.c_str(), wxConvUTF8)
522                 );
523                 if (boinc_file_exists(str.c_str())) {
524                     wxImage img = wxImage(str.c_str(), wxBITMAP_TYPE_ANY);
525                     if (img.IsOk()) {
526 #ifdef __WXMSW__
527 // TODO: Choose from multiple size images if provided, else resize the closest one
528                         if ((GetXDPIScaling() > 1.05) || (GetYDPIScaling() > 1.05)) {
529                             img.Rescale((int) (img.GetWidth()*GetXDPIScaling()),
530                                         (int) (img.GetHeight()*GetYDPIScaling()),
531                                         wxIMAGE_QUALITY_BILINEAR
532                                     );
533                         }
534 #endif
535                         m_bitmapApplicationLogo = wxBitmap(img);
536                     }
537                 }
538             }
539             continue;
540         } else if (parse_str(buf, "<organization_name>", strBuffer)) {
541             m_strOrganizationName = wxString(strBuffer.c_str(), wxConvUTF8);
542             continue;
543         } else if (parse_str(buf, "<organization_website>", strBuffer)) {
544             m_strOrganizationWebsite = wxString(strBuffer.c_str(), wxConvUTF8);
545             continue;
546         } else if (parse_str(buf, "<organization_help_url>", strBuffer)) {
547             m_strOrganizationHelpUrl = wxString(strBuffer.c_str(), wxConvUTF8);
548             continue;
549         } else if (parse_int(buf, "<open_tab>", m_iDefaultTab)) {
550             m_bDefaultTabSpecified = true;
551             continue;
552         } else if (parse_str(buf, "<exit_message>", strBuffer)) {
553             m_strExitMessage = wxString(strBuffer.c_str(), wxConvUTF8);
554             continue;
555         }
556     }
557 
558     InitializeDelayedValidation();
559 
560     return 0;
561 }
562 
563 
GetApplicationName()564 wxString CSkinAdvanced::GetApplicationName() {
565     wxString strApplicationName = m_strApplicationName;
566 #ifdef BOINC_PRERELEASE
567     strApplicationName += wxT(" (Pre-release)");
568 #endif
569     return strApplicationName;
570 }
571 
572 
GetApplicationShortName()573 wxString CSkinAdvanced::GetApplicationShortName() {
574     return m_strApplicationShortName;
575 }
576 
577 
GetApplicationIcon()578 wxIconBundle* CSkinAdvanced::GetApplicationIcon() {
579     return m_iconApplicationIcon.GetIcon();
580 }
581 
582 
GetApplicationDisconnectedIcon()583 wxIconBundle* CSkinAdvanced::GetApplicationDisconnectedIcon() {
584     return m_iconApplicationDisconnectedIcon.GetIcon();
585 }
586 
587 
GetApplicationSnoozeIcon()588 wxIconBundle* CSkinAdvanced::GetApplicationSnoozeIcon() {
589     return m_iconApplicationSnoozeIcon.GetIcon();
590 }
591 
592 
GetApplicationLogo()593 wxBitmap* CSkinAdvanced::GetApplicationLogo() {
594     return &m_bitmapApplicationLogo;
595 }
596 
597 
GetOrganizationName()598 wxString CSkinAdvanced::GetOrganizationName() {
599     return m_strOrganizationName;
600 }
601 
602 
GetOrganizationWebsite()603 wxString CSkinAdvanced::GetOrganizationWebsite() {
604     return m_strOrganizationWebsite;
605 }
606 
607 
GetOrganizationHelpUrl()608 wxString CSkinAdvanced::GetOrganizationHelpUrl() {
609     return m_strOrganizationHelpUrl;
610 }
611 
612 
GetDefaultTab()613 int CSkinAdvanced::GetDefaultTab() {
614     return m_iDefaultTab;
615 }
616 
617 
GetExitMessage()618 wxString CSkinAdvanced::GetExitMessage() {
619     return m_strExitMessage;
620 }
621 
622 
IsBranded()623 bool CSkinAdvanced::IsBranded() {
624     return m_bIsBranded;
625 }
626 
627 
InitializeDelayedValidation()628 bool CSkinAdvanced::InitializeDelayedValidation() {
629     if (m_strApplicationName.IsEmpty()) {
630         if (show_error_msgs) {
631             fprintf(stderr, "Skin Manager: Application name was not defined. Using default.\n");
632         }
633         m_strApplicationName = wxT("BOINC Manager");
634         wxASSERT(!m_strApplicationName.IsEmpty());
635     }
636     if (m_strApplicationShortName.IsEmpty()) {
637         if (show_error_msgs) {
638             fprintf(stderr, "Skin Manager: Application short name was not defined. Using default.\n");
639         }
640         m_strApplicationShortName = wxT("BOINC");
641         wxASSERT(!m_strApplicationShortName.IsEmpty());
642     }
643 #ifdef _WIN32
644     m_iconApplicationIcon.SetDefaults(wxT("application"), wxT("boinc"));
645     m_iconApplicationDisconnectedIcon.SetDefaults(wxT("application disconnected"), wxT("boincdisconnect"));
646     m_iconApplicationSnoozeIcon.SetDefaults(wxT("application snooze"), wxT("boincsnooze"));
647 #else
648     m_iconApplicationIcon.SetDefaults(wxT("application"), boinc_xpm, boinc32_xpm);
649     m_iconApplicationDisconnectedIcon.SetDefaults(wxT("application disconnected"), boincdisconnect_xpm, boincdisconnect32_xpm);
650     m_iconApplicationSnoozeIcon.SetDefaults(wxT("application snooze"), boincsnooze_xpm, boincsnooze32_xpm);
651 #endif
652     if (!m_bitmapApplicationLogo.Ok()) {
653         if (show_error_msgs) {
654             fprintf(stderr, "Skin Manager: Failed to load application logo. Using default.\n");
655         }
656         m_bitmapApplicationLogo = wxBitmap((const char**)boinc_logo_xpm);
657         wxASSERT(m_bitmapApplicationLogo.Ok());
658     }
659     if (m_strOrganizationName.IsEmpty()) {
660         if (show_error_msgs) {
661             fprintf(stderr, "Skin Manager: Organization name was not defined. Using default.\n");
662         }
663         m_strOrganizationName = wxT("Space Sciences Laboratory, U.C. Berkeley");
664         wxASSERT(!m_strOrganizationName.IsEmpty());
665     }
666     if (m_strOrganizationWebsite.IsEmpty()) {
667         if (show_error_msgs) {
668             fprintf(stderr, "Skin Manager: Organization web site was not defined. Using default.\n");
669         }
670         m_strOrganizationWebsite = wxT("https://boinc.berkeley.edu");
671         wxASSERT(!m_strOrganizationWebsite.IsEmpty());
672     }
673     if (m_strOrganizationHelpUrl.IsEmpty()) {
674         if (show_error_msgs) {
675             fprintf(stderr, "Skin Manager: Organization help url was not defined. Using default.\n");
676         }
677         m_strOrganizationHelpUrl = wxT("https://boinc.berkeley.edu/manager_links.php");
678         wxASSERT(!m_strOrganizationHelpUrl.IsEmpty());
679     }
680     if (!m_bDefaultTabSpecified) {
681         if (show_error_msgs) {
682             fprintf(stderr, "Skin Manager: Default tab was not defined. Using default.\n");
683         }
684         m_bDefaultTabSpecified = true;
685         m_iDefaultTab = 0;
686     }
687     if (m_strExitMessage.IsEmpty()) {
688         if (show_error_msgs) {
689             fprintf(stderr, "Skin Manager: Exit message was not defined. Using default.\n");
690         }
691         m_strExitMessage = wxEmptyString;
692     }
693     return true;
694 }
695 
696 
IMPLEMENT_DYNAMIC_CLASS(CSkinWizardATP,CSkinItem)697 IMPLEMENT_DYNAMIC_CLASS(CSkinWizardATP, CSkinItem)
698 
699 
700 CSkinWizardATP::CSkinWizardATP() {
701     Clear();
702 }
703 
704 
~CSkinWizardATP()705 CSkinWizardATP::~CSkinWizardATP() {
706     Clear();
707 }
708 
709 
Clear()710 void CSkinWizardATP::Clear() {
711     m_strTitle = wxEmptyString;
712 }
713 
714 
Parse(MIOFILE & in)715 int CSkinWizardATP::Parse(MIOFILE& in) {
716     char buf[256];
717     std::string strBuffer;
718 
719     while (in.fgets(buf, 256)) {
720         if (match_tag(buf, "</attach_to_project>")) break;
721         else if (parse_str(buf, "<title>", strBuffer)) {
722             m_strTitle = wxString(strBuffer.c_str(), wxConvUTF8);
723             continue;
724         }
725     }
726 
727     InitializeDelayedValidation();
728 
729     return 0;
730 }
731 
732 
InitializeDelayedValidation()733 bool CSkinWizardATP::InitializeDelayedValidation() {
734     if (m_strTitle.IsEmpty()) {
735         if (show_error_msgs) {
736             fprintf(stderr, "Skin Manager: Add project wizard title was not defined. Using default.\n");
737         }
738         m_strTitle = wxT("BOINC Manager");
739         wxASSERT(!m_strTitle.IsEmpty());
740     }
741     return true;
742 }
743 
744 
IMPLEMENT_DYNAMIC_CLASS(CSkinWizardATAM,CSkinItem)745 IMPLEMENT_DYNAMIC_CLASS(CSkinWizardATAM, CSkinItem)
746 
747 
748 CSkinWizardATAM::CSkinWizardATAM() {
749     Clear();
750 }
751 
752 
~CSkinWizardATAM()753 CSkinWizardATAM::~CSkinWizardATAM() {
754     Clear();
755 }
756 
757 
Clear()758 void CSkinWizardATAM::Clear() {
759     m_strAccountInfoMessage = wxEmptyString;
760 }
761 
762 
Parse(MIOFILE & in)763 int CSkinWizardATAM::Parse(MIOFILE& in) {
764     char buf[256];
765     std::string strBuffer;
766 
767     while (in.fgets(buf, 256)) {
768         if (match_tag(buf, "</attach_to_account_manager>")) break;
769         else if (parse_str(buf, "<account_info_message>", strBuffer)) {
770             m_strAccountInfoMessage = wxString(strBuffer.c_str(), wxConvUTF8);
771             continue;
772         }
773     }
774 
775     InitializeDelayedValidation();
776 
777     return 0;
778 }
779 
780 
InitializeDelayedValidation()781 bool CSkinWizardATAM::InitializeDelayedValidation() {
782     return true;
783 }
784 
785 
IMPLEMENT_DYNAMIC_CLASS(CSkinWizards,CSkinItem)786 IMPLEMENT_DYNAMIC_CLASS(CSkinWizards, CSkinItem)
787 
788 
789 CSkinWizards::CSkinWizards() {
790     Clear();
791 }
792 
793 
~CSkinWizards()794 CSkinWizards::~CSkinWizards() {
795     Clear();
796 }
797 
798 
Clear()799 void CSkinWizards::Clear() {
800     m_AttachToProjectWizard.Clear();
801     m_AttachToAccountManagerWizard.Clear();
802 }
803 
804 
Parse(MIOFILE & in)805 int CSkinWizards::Parse(MIOFILE& in) {
806     char buf[256];
807 
808     while (in.fgets(buf, 256)) {
809         if (match_tag(buf, "</wizards>")) break;
810         else if (match_tag(buf, "<attach_to_project>")) {
811             m_AttachToProjectWizard.Parse(in);
812             continue;
813         } else if (match_tag(buf, "<attach_to_account_manager>")) {
814             m_AttachToAccountManagerWizard.Parse(in);
815             continue;
816         }
817     }
818 
819     InitializeDelayedValidation();
820 
821     return 0;
822 }
823 
824 
InitializeDelayedValidation()825 bool CSkinWizards::InitializeDelayedValidation() {
826     return m_AttachToProjectWizard.InitializeDelayedValidation() &&
827            m_AttachToAccountManagerWizard.InitializeDelayedValidation();
828 }
829 
830 
IMPLEMENT_DYNAMIC_CLASS(CSkinManager,CSkinItem)831 IMPLEMENT_DYNAMIC_CLASS(CSkinManager, CSkinItem)
832 
833 
834 CSkinManager::CSkinManager() {}
835 
836 
CSkinManager(bool debugSkins)837 CSkinManager::CSkinManager(bool debugSkins) {
838     show_error_msgs = debugSkins;
839     Clear();
840 }
841 
842 
~CSkinManager()843 CSkinManager::~CSkinManager() {
844     Clear();
845 }
846 
ReloadSkin(wxString strSkin)847 bool CSkinManager::ReloadSkin(wxString strSkin) {
848     int      retval = ERR_XML_PARSE;
849     FILE*    p;
850     MIOFILE  mf;
851 
852     // This fixes a (rare) crash bug
853     if (strSkin.IsEmpty()) {
854         strSkin = GetDefaultSkinName();
855     }
856 
857     // Clear out all the old stuff
858     Clear();
859 
860     // Set the default skin back to Default
861     m_strSelectedSkin = strSkin;
862 
863     // TODO: Eliminate the <en> tags: localization is no longer in skin files.
864     p = fopen((const char*)ConstructSkinFileName().mb_str(wxConvUTF8), "r");
865     if (p) {
866         mf.init_file(p);
867         retval = Parse(mf, wxT("en"));
868         fclose(p);
869     }
870 
871     if (retval && show_error_msgs) {
872         fprintf(stderr, "Skin Manager: Failed to load skin '%s'.\n", (const char *)ConstructSkinFileName().mb_str(wxConvUTF8));
873     }
874 
875     InitializeDelayedValidation();
876 
877     // Tell whichever UI elements that are loaded to reload the
878     //   skinable resources they use.
879     wxGetApp().FireReloadSkin();
880 
881     return true;
882 }
883 
GetCurrentSkins()884 wxArrayString& CSkinManager::GetCurrentSkins() {
885     unsigned int i;
886     wxString     strSkinLocation = wxString(GetSkinsLocation() + wxFileName::GetPathSeparator());
887     wxString     strSkinFileName = wxString(wxFileName::GetPathSeparator() + GetSkinFileName());
888     wxString     strBuffer;
889 
890     // Initialize array
891     m_astrSkins.Clear();
892 
893     // Go get all the valid skin directories.
894     wxDir::GetAllFiles(strSkinLocation, &m_astrSkins, wxString(wxT("*") + GetSkinFileName()));
895 
896     // Trim out the path information for all the entries
897     for (i = 0; i < m_astrSkins.GetCount(); i++) {
898         strBuffer = m_astrSkins[i];
899 
900         strBuffer = strBuffer.Remove(0, strSkinLocation.Length());
901         strBuffer = strBuffer.Remove(strBuffer.Find(strSkinFileName.c_str()), strSkinFileName.Length());
902 
903         // Special case: 'Default' to mean the embedded default skin.
904         //   remove any duplicate entries
905         if (GetDefaultSkinName() != strBuffer) {
906             m_astrSkins[i] = strBuffer;
907         } else {
908             m_astrSkins.RemoveAt(i);
909             i--;
910         }
911     }
912 
913     // Insert the 'Default' entry into the skins list.
914     m_astrSkins.Insert(GetDefaultSkinName(), 0);
915 
916     // return the current list of skins
917     return m_astrSkins;
918 }
919 
920 
GetDefaultSkinName()921 wxString CSkinManager::GetDefaultSkinName() {
922     return wxString(wxT("Default"));
923 }
924 
925 
ConstructSkinFileName()926 wxString CSkinManager::ConstructSkinFileName() {
927     return wxString(
928         GetSkinsLocation() +
929         wxString(wxFileName::GetPathSeparator()) +
930         m_strSelectedSkin +
931         wxString(wxFileName::GetPathSeparator()) +
932         GetSkinFileName()
933     );
934 }
935 
936 
ConstructSkinPath()937 wxString CSkinManager::ConstructSkinPath() {
938     return wxString(
939         GetSkinsLocation() +
940         wxString(wxFileName::GetPathSeparator()) +
941         m_strSelectedSkin +
942         wxString(wxFileName::GetPathSeparator())
943     );
944 }
945 
946 
GetSkinFileName()947 wxString CSkinManager::GetSkinFileName() {
948     // Construct path to skins directory
949     return wxString(wxT("skin.xml"));
950 }
951 
952 
GetSkinsLocation()953 wxString CSkinManager::GetSkinsLocation() {
954     // Construct path to skins directory
955     wxString strSkinLocation = wxEmptyString;
956 
957 #ifdef __WXMSW__
958     strSkinLocation  = wxGetApp().GetRootDirectory();
959     strSkinLocation += wxFileName::GetPathSeparator();
960     strSkinLocation += wxT("skins");
961 #else
962     strSkinLocation = wxString(wxGetCwd() + wxString(wxFileName::GetPathSeparator()) + wxT("skins"));
963 #endif
964 
965     return strSkinLocation;
966 }
967 
968 
Clear()969 void CSkinManager::Clear() {
970     m_SimpleSkin.Clear();
971     m_AdvancedSkin.Clear();
972     m_WizardsSkin.Clear();
973 
974     m_astrSkins.Clear();
975     m_strSelectedSkin.Clear();
976 }
977 
978 
Parse(MIOFILE & in,wxString strDesiredLocale)979 int CSkinManager::Parse(MIOFILE& in, wxString strDesiredLocale) {
980     char     buf[256];
981     wxString strLocaleStartTag;
982     wxString strLocaleEndTag;
983     bool     bLocaleFound = false;
984 
985     // Construct the start and end tags for the locale we want.
986     strLocaleStartTag.Printf(wxT("<%s>"), strDesiredLocale.c_str());
987     strLocaleEndTag.Printf(wxT("</%s>"), strDesiredLocale.c_str());
988 
989     // TODO: Eliminate the <en> tags: localization is no longer in skin files.
990     // Look for the begining of the desired locale.
991     while (in.fgets(buf, 256)) {
992         if (match_tag(buf, (const char*)strLocaleStartTag.mb_str(wxConvUTF8))) {
993             bLocaleFound = true;
994             break;
995         }
996     }
997 
998     if (!bLocaleFound) return ERR_XML_PARSE;
999 
1000     while (in.fgets(buf, 256)) {
1001         if (match_tag(buf, (const char*)strLocaleStartTag.mb_str(wxConvUTF8))) break;
1002         else if (match_tag(buf, "<simple>")) {
1003             m_SimpleSkin.Parse(in);
1004             continue;
1005         } else if (match_tag(buf, "<advanced>")) {
1006             m_AdvancedSkin.Parse(in);
1007             continue;
1008         } else if (match_tag(buf, "<wizards>")) {
1009             m_WizardsSkin.Parse(in);
1010             continue;
1011         }
1012     }
1013 
1014     InitializeDelayedValidation();
1015 
1016     return 0;
1017 }
1018 
1019 
InitializeDelayedValidation()1020 bool CSkinManager::InitializeDelayedValidation() {
1021     return m_SimpleSkin.InitializeDelayedValidation() &&
1022            m_AdvancedSkin.InitializeDelayedValidation() &&
1023            m_WizardsSkin.InitializeDelayedValidation();
1024 }
1025 
1026