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