OpenCPN Partial API docs
Loading...
Searching...
No Matches
styles.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Chart Symbols
5 * Author: Jesper Weissglas
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25#include "config.h"
26
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif
32
33#include <wx/filename.h>
34#include <wx/dir.h>
35#include <stdlib.h>
36#include "OCPNPlatform.h"
37
38#include "styles.h"
39#include "model/wx28compat.h"
40#include "model/svg_utils.h"
41#include "color_handler.h"
42#include "tinyxml.h"
43#ifdef __OCPN__ANDROID__
44#include "androidUTIL.h"
45#include "qdebug.h"
46#endif
47
48extern OCPNPlatform* g_Platform;
49
50ocpnStyle::StyleManager* g_StyleManager;
51
52using namespace ocpnStyle;
53
54void bmdump(wxBitmap bm, wxString name) {
55 wxImage img = bm.ConvertToImage();
56 img.SaveFile(name << ".png", wxBITMAP_TYPE_PNG);
57}
58
59// This function can be used to create custom bitmap blending for all platforms
60// where 32 bit bitmap ops are broken. Can hopefully be removed for
61// wxWidgets 3.0...
62
63wxBitmap MergeBitmaps(wxBitmap back, wxBitmap front, wxSize offset) {
64 // If the front bitmap has no alpha channel, then merging will accomplish
65 // nothing So, simply return the bitmap intact However, if the bitmaps are
66 // different sizes, do the render anyway.
67 wxImage im_front = front.ConvertToImage();
68 if (!im_front.HasAlpha() && (front.GetWidth() == back.GetWidth()))
69 return front;
70
71#ifdef __WXMSW__
72 // WxWidgets still has some trouble overlaying bitmaps with transparency.
73 // This is true on wx2.8 as well as wx3.0
74 // In the specific case where the back bitmap has alpha, but the front does
75 // not, we obviously mean for the front to be drawn over the back, with 100%
76 // opacity. To do this, we need to convert the back bitmap to simple no-alpha
77 // model.
78 if (!im_front.HasAlpha()) {
79 wxImage im_back = back.ConvertToImage();
80 back = wxBitmap(im_back);
81 }
82#endif
83
84 wxBitmap merged(back.GetWidth(), back.GetHeight(), back.GetDepth());
85
86 // Manual alpha blending for broken wxWidgets alpha bitmap support, pervasive
87 // in wx2.8. And also in wx3, at least on Windows...
88#if 1
89
90#if !wxCHECK_VERSION(2, 9, 4)
91 merged.UseAlpha();
92 back.UseAlpha();
93 front.UseAlpha();
94#endif
95
96 wxImage im_back = back.ConvertToImage();
97 wxImage im_result = back.ConvertToImage(); // Only way to make result have
98 // alpha channel in wxW 2.8.
99
100 unsigned char* presult = im_result.GetData();
101 unsigned char* pback = im_back.GetData();
102 unsigned char* pfront = im_front.GetData();
103
104 unsigned char* afront = NULL;
105 if (im_front.HasAlpha()) afront = im_front.GetAlpha();
106
107 unsigned char* aback = NULL;
108 if (im_back.HasAlpha()) aback = im_back.GetAlpha();
109
110 unsigned char* aresult = NULL;
111 if (im_result.HasAlpha()) aresult = im_result.GetAlpha();
112
113 // Do alpha blending, associative version of "over" operator.
114 if (presult && pback && pfront) {
115 for (int i = 0; i < back.GetHeight(); i++) {
116 for (int j = 0; j < back.GetWidth(); j++) {
117 int fX = j - offset.x;
118 int fY = i - offset.y;
119
120 bool inFront = true;
121 if (fX < 0 || fY < 0) inFront = false;
122 if (fX >= front.GetWidth()) inFront = false;
123 if (fY >= front.GetHeight()) inFront = false;
124
125 if (inFront) {
126 double alphaF = 1.0;
127 if (afront) alphaF = (double)(*afront++) / 255.0;
128 double alphaB = 1.0;
129 if (aback) alphaB = (double)(*aback++) / 255.0;
130 double alphaRes = alphaF + alphaB * (1.0 - alphaF);
131 if (aresult) {
132 unsigned char a = alphaRes * 255;
133 *aresult++ = a;
134 }
135 unsigned char r =
136 (*pfront++ * alphaF + *pback++ * alphaB * (1.0 - alphaF)) /
137 alphaRes;
138 *presult++ = r;
139 unsigned char g =
140 (*pfront++ * alphaF + *pback++ * alphaB * (1.0 - alphaF)) /
141 alphaRes;
142 *presult++ = g;
143 unsigned char b =
144 (*pfront++ * alphaF + *pback++ * alphaB * (1.0 - alphaF)) /
145 alphaRes;
146 *presult++ = b;
147 } else {
148 if (aresult && aback) *aresult++ = *aback++;
149 *presult++ = *pback++;
150 *presult++ = *pback++;
151 *presult++ = *pback++;
152 }
153 }
154 }
155 }
156 merged = wxBitmap(im_result);
157#else
158 wxMemoryDC mdc(merged);
159 mdc.Clear();
160 mdc.DrawBitmap(back, 0, 0, true);
161 mdc.DrawBitmap(front, offset.x, offset.y, true);
162 mdc.SelectObject(wxNullBitmap);
163#endif
164
165 return merged;
166}
167
168// The purpouse of ConvertTo24Bit is to take an icon with 32 bit depth and alpha
169// channel and put it in a 24 bit deep bitmap with no alpha, that can be safely
170// drawn in the crappy wxWindows implementations.
171
172wxBitmap ConvertTo24Bit(wxColor bgColor, wxBitmap front) {
173 if (front.GetDepth() == 24) return front;
174
175#if !wxCHECK_VERSION(2, 9, 4)
176 front.UseAlpha();
177#endif
178
179 wxImage im_front = front.ConvertToImage();
180 unsigned char* pfront = im_front.GetData();
181 if (!pfront) return wxNullBitmap;
182
183 unsigned char* presult =
184 (unsigned char*)malloc(front.GetWidth() * front.GetHeight() * 3);
185 if (!presult) return wxNullBitmap;
186
187 unsigned char* po_result = presult;
188
189 unsigned char* afront = NULL;
190 if (im_front.HasAlpha()) afront = im_front.GetAlpha();
191
192 for (int i = 0; i < front.GetWidth(); i++) {
193 for (int j = 0; j < front.GetHeight(); j++) {
194 double alphaF = 1.0;
195 if (afront) alphaF = (double)(*afront++) / 256.0;
196 unsigned char r = *pfront++ * alphaF + bgColor.Red() * (1.0 - alphaF);
197 *presult++ = r;
198 unsigned char g = *pfront++ * alphaF + bgColor.Green() * (1.0 - alphaF);
199 *presult++ = g;
200 unsigned char b = *pfront++ * alphaF + bgColor.Blue() * (1.0 - alphaF);
201 *presult++ = b;
202 }
203 }
204
205 wxImage im_result(front.GetWidth(), front.GetHeight(), po_result);
206
207 wxBitmap result = wxBitmap(im_result);
208 return result;
209}
210
211bool Style::NativeToolIconExists(const wxString& name) {
212 if (toolIndex.find(name) == toolIndex.end())
213 return false;
214 else
215 return true;
216}
217
218// Tools and Icons perform on-demand loading and dimming of bitmaps.
219// Changing color scheme invalidates all loaded bitmaps.
220
221wxBitmap Style::GetIconScaled(const wxString& name, double scaleFactor,
222 bool bforceReload) {
223 if (iconIndex.find(name) == iconIndex.end()) {
224 wxString msg("The requested icon was not found in the style: ");
225 msg += name;
226 wxLogMessage(msg);
227 return wxBitmap(GetToolSize().x, GetToolSize().y); // Prevents crashing.
228 }
229
230 int index = iconIndex[name]; // FIXME: this operation is not const but should
231 // be, use 'find'
232
233 Icon* icon = (Icon*)icons[index];
234 if (icon->size.x == 0) icon->size = toolSize[currentOrientation];
235
236 return GetIcon(name, icon->size.x * scaleFactor, icon->size.y * scaleFactor,
237 bforceReload);
238}
239
240wxBitmap Style::GetIcon(const wxString& name, int width, int height,
241 bool bforceReload) {
242 if (iconIndex.find(name) == iconIndex.end()) {
243 wxString msg("The requested icon was not found in the style: ");
244 msg += name;
245 wxLogMessage(msg);
246 return wxBitmap(GetToolSize().x, GetToolSize().y); // Prevents crashing.
247 }
248
249 int index = iconIndex[name]; // FIXME: this operation is not const but should
250 // be, use 'find'
251
252 Icon* icon = (Icon*)icons[index];
253
254 if (icon->loaded && !bforceReload) return icon->icon;
255 if (icon->size.x == 0) icon->size = toolSize[currentOrientation];
256
257 wxSize retSize = icon->size;
258 if ((width > 0) && (height > 0)) retSize = wxSize(width, height);
259
260 wxBitmap bm;
261#ifdef ocpnUSE_SVG
262 wxString fullFilePath = myConfigFileDir + this->sysname +
263 wxFileName::GetPathSeparator() + name + ".svg";
264 if (wxFileExists(fullFilePath))
265 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
266 else {
268#endif // ocpnUSE_SVG
269 wxRect location(icon->iconLoc, icon->size);
270 bm = graphics->GetSubBitmap(location);
271 if (retSize != icon->size) {
272 wxImage scaled_image = bm.ConvertToImage();
273 bm = wxBitmap(
274 scaled_image.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
275 }
276
277#ifdef ocpnUSE_SVG
278 }
279#endif // ocpnUSE_SVG
280 icon->icon = SetBitmapBrightness(bm, colorscheme);
281 icon->loaded = true;
282 return icon->icon;
283}
284
285wxBitmap Style::GetToolIcon(const wxString& toolname, int iconType,
286 bool rollover, int width, int height) {
287 if (toolIndex.find(toolname) == toolIndex.end()) {
288 // This will produce a flood of log messages for some PlugIns, notably
289 // WMM_PI, and GRADAR_PI
290 // wxString msg( _T("The requested tool was not found in the style:
291 // ") ); msg += toolname; wxLogMessage( msg );
292 return wxBitmap(GetToolSize().x, GetToolSize().y, 1);
293 }
294
295 int index = toolIndex[toolname];
296
297 Tool* tool = (Tool*)tools[index];
298
299 wxSize size = tool->customSize;
300 if (size.x == 0) size = toolSize[currentOrientation];
301
302 wxSize retSize = size;
303 if ((width > 0) && (height > 0)) retSize = wxSize(width, height);
304
305 switch (iconType) {
306 case TOOLICON_NORMAL: {
307 if (tool->iconLoaded && !rollover) {
308 return tool->icon;
309 }
310 if (tool->rolloverLoaded && rollover) return tool->rollover;
311
312 wxRect location(tool->iconLoc, size);
313
314 // If rollover icon does not exist, use the defult icon
315 if (rollover) {
316 if ((tool->rolloverLoc.x != 0) || (tool->rolloverLoc.y != 0))
317 location = wxRect(tool->rolloverLoc, size);
318 }
319
320 if (currentOrientation) {
321 location.x -= verticalIconOffset.x;
322 location.y -= verticalIconOffset.y;
323 }
324
325 wxBitmap bm;
326#ifdef ocpnUSE_SVG
327 wxString fullFilePath;
328 if (rollover) {
329 fullFilePath = myConfigFileDir + this->sysname +
330 wxFileName::GetPathSeparator() + toolname +
331 "_rollover.svg";
332 if (!wxFileExists(fullFilePath))
333 fullFilePath = myConfigFileDir + this->sysname +
334 wxFileName::GetPathSeparator() + toolname + ".svg";
335 } else
336 fullFilePath = myConfigFileDir + this->sysname +
337 wxFileName::GetPathSeparator() + toolname + ".svg";
338 if (wxFileExists(fullFilePath))
339 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
340 else {
342#endif // ocpnUSE_SVG
343 bm = graphics->GetSubBitmap(location);
344
345 if (hasBackground) {
346 bm = MergeBitmaps(GetNormalBG(), bm, wxSize(0, 0));
347 } else {
348 wxBitmap bg(GetToolSize().x, GetToolSize().y);
349 wxMemoryDC mdc(bg);
350 mdc.SetBackground(
351 wxBrush(GetGlobalColor("GREY2"), wxBRUSHSTYLE_SOLID));
352 mdc.Clear();
353 mdc.SelectObject(wxNullBitmap);
354 bm = MergeBitmaps(bg, bm, wxSize(0, 0));
355 }
356
357 if (retSize != size) {
358 wxImage scaled_image = bm.ConvertToImage();
359 bm = wxBitmap(
360 scaled_image.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
361 }
362
363#ifdef ocpnUSE_SVG
364 }
365#endif // ocpnUSE_SVG
366
367 if (rollover) {
368 tool->rollover = SetBitmapBrightness(bm, colorscheme);
369 tool->rolloverLoaded = true;
370 return tool->rollover;
371 } else {
372 if (toolname == "mob_btn") {
373 double dimLevel = 1.0;
374 if (colorscheme == GLOBAL_COLOR_SCHEME_DUSK)
375 dimLevel = 0.5;
376 else if (colorscheme == GLOBAL_COLOR_SCHEME_NIGHT)
377 dimLevel = 0.5;
378 tool->icon = SetBitmapBrightnessAbs(bm, dimLevel);
379 } else {
380 tool->icon = SetBitmapBrightness(bm, colorscheme);
381 }
382
383 tool->iconLoaded = true;
384 return tool->icon;
385 }
386 }
387 case TOOLICON_TOGGLED: {
388 if (tool->toggledLoaded && !rollover) return tool->toggled;
389 if (tool->rolloverToggledLoaded && rollover) return tool->rolloverToggled;
390
391 wxRect location(tool->iconLoc, size);
392 if (rollover) location = wxRect(tool->rolloverLoc, size);
393 wxSize offset(0, 0);
394 if (GetToolSize() != GetToggledToolSize()) {
395 offset = GetToggledToolSize() - GetToolSize();
396 offset /= 2;
397 }
398 if (currentOrientation) {
399 location.x -= verticalIconOffset.x;
400 location.y -= verticalIconOffset.y;
401 }
402 wxBitmap bm;
403#ifdef ocpnUSE_SVG
404 wxString fullFilePath;
405 if (rollover)
406 fullFilePath = myConfigFileDir + this->sysname +
407 wxFileName::GetPathSeparator() + toolname +
408 "_rollover_toggled.svg";
409 else
410 fullFilePath = myConfigFileDir + this->sysname +
411 wxFileName::GetPathSeparator() + toolname +
412 "_toggled.svg";
413 if (wxFileExists(fullFilePath))
414 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
415 else {
416 // Could not find a toggled SVG, so try to make one
417 if (rollover)
418 fullFilePath = myConfigFileDir + this->sysname +
419 wxFileName::GetPathSeparator() + toolname +
420 "_rollover.svg";
421 else
422 fullFilePath = myConfigFileDir + this->sysname +
423 wxFileName::GetPathSeparator() + toolname + ".svg";
424
425 if (wxFileExists(fullFilePath)) {
426 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
427
428 wxBitmap bmBack = GetToggledBG();
429 if ((bmBack.GetWidth() != retSize.x) ||
430 (bmBack.GetHeight() != retSize.y)) {
431 wxImage scaled_back = bmBack.ConvertToImage();
432 bmBack = wxBitmap(
433 scaled_back.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
434 }
435 bm = MergeBitmaps(bmBack, bm, wxSize(0, 0));
436 }
437 }
438
439#endif // ocpnUSE_SVG
440 if (!bm.Ok()) {
441 bm = graphics->GetSubBitmap(location);
442 bm = MergeBitmaps(GetToggledBG(), bm, offset);
443
444 if (retSize != size) {
445 wxImage scaled_image = bm.ConvertToImage();
446 bm = wxBitmap(
447 scaled_image.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
448 }
449 }
450
451 if (rollover) {
452 tool->rolloverToggled = SetBitmapBrightness(bm, colorscheme);
453 tool->rolloverToggledLoaded = true;
454 return tool->rolloverToggled;
455 } else {
456 tool->toggled = SetBitmapBrightness(bm, colorscheme);
457 tool->toggledLoaded = true;
458 return tool->toggled;
459 }
460 }
461 case TOOLICON_DISABLED: {
462 if (tool->disabledLoaded) return tool->disabled;
463 wxRect location(tool->disabledLoc, size);
464
465 wxBitmap bm;
466#ifdef ocpnUSE_SVG
467 wxString fullFilePath = myConfigFileDir + this->sysname +
468 wxFileName::GetPathSeparator() + toolname +
469 "_disabled.svg";
470 if (wxFileExists(fullFilePath))
471 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
472 else {
474#endif // ocpnUSE_SVG
475 bm = graphics->GetSubBitmap(location);
476
477 if (hasBackground) {
478 bm = MergeBitmaps(GetNormalBG(), bm, wxSize(0, 0));
479 }
480
481 if (retSize != size) {
482 wxImage scaled_image = bm.ConvertToImage();
483 bm = wxBitmap(
484 scaled_image.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
485 }
486#ifdef ocpnUSE_SVG
487 }
488#endif // ocpnUSE_SVG
489 if (currentOrientation) {
490 location.x -= verticalIconOffset.x;
491 location.y -= verticalIconOffset.y;
492 }
493 tool->disabled = SetBitmapBrightness(bm, colorscheme);
494 tool->disabledLoaded = true;
495 return tool->disabled;
496 }
497 }
498 wxString msg(
499 "A requested icon type for this tool was not found in the style: ");
500 msg += toolname;
501 wxLogMessage(msg);
502 return wxBitmap(GetToolSize().x, GetToolSize().y); // Prevents crashing.
503}
504
505wxBitmap Style::BuildPluginIcon(wxBitmap& bm, int iconType, double factor) {
506 if (!bm.IsOk()) return wxNullBitmap;
507
508 wxBitmap iconbm;
509
510 switch (iconType) {
511 case TOOLICON_NORMAL:
512 case TOOLICON_TOGGLED: {
513 if (hasBackground) {
514 wxBitmap bg;
515 if (iconType == TOOLICON_NORMAL)
516 bg = GetNormalBG();
517 else
518 bg = GetToggledBG();
519
520 if ((bg.GetWidth() >= bm.GetWidth()) &&
521 (bg.GetHeight() >= bm.GetHeight())) {
522 int w = bg.GetWidth() * factor;
523 int h = bg.GetHeight() * factor;
524 wxImage scaled_image = bg.ConvertToImage();
525 bg = wxBitmap(scaled_image.Scale(w, h, wxIMAGE_QUALITY_HIGH));
526
527 wxSize offset = wxSize(bg.GetWidth() - bm.GetWidth(),
528 bg.GetHeight() - bm.GetHeight());
529 offset /= 2;
530 iconbm = MergeBitmaps(bg, bm, offset);
531 } else {
532 // A bit of contorted logic for non-square backgrounds...
533 double factor = ((double)bm.GetHeight()) / bg.GetHeight();
534 int nw = bg.GetWidth() * factor;
535 int nh = bm.GetHeight();
536 if (bg.GetWidth() == bg.GetHeight()) nw = nh;
537 wxImage scaled_image = bg.ConvertToImage();
538 bg = wxBitmap(scaled_image.Scale(nw, nh, wxIMAGE_QUALITY_HIGH));
539
540 wxSize offset = wxSize(bg.GetWidth() - bm.GetWidth(),
541 bg.GetHeight() - bm.GetHeight());
542 offset /= 2;
543 iconbm = MergeBitmaps(bg, bm, offset);
544 }
545
546 } else {
547 wxBitmap bg(GetToolSize().x, GetToolSize().y);
548 wxMemoryDC mdc(bg);
549 wxSize offset = GetToolSize() - wxSize(bm.GetWidth(), bm.GetHeight());
550 offset /= 2;
551 mdc.SetBackground(wxBrush(GetGlobalColor("GREY2"), wxBRUSHSTYLE_SOLID));
552 mdc.Clear();
553 mdc.SelectObject(wxNullBitmap);
554 iconbm = MergeBitmaps(bg, bm, offset);
555 }
556 break;
557 }
558 default:
559 return wxNullBitmap;
560 break;
561 }
562 return SetBitmapBrightness(iconbm, colorscheme);
563}
564
565wxBitmap Style::SetBitmapBrightness(wxBitmap& bitmap, ColorScheme cs) {
566 double dimLevel;
567 switch (cs) {
568 case GLOBAL_COLOR_SCHEME_DUSK: {
569 dimLevel = 0.8;
570 break;
571 }
572 case GLOBAL_COLOR_SCHEME_NIGHT: {
573 dimLevel = 0.5;
574 break;
575 }
576 default: {
577 return bitmap;
578 }
579 }
580
581 return SetBitmapBrightnessAbs(bitmap, dimLevel);
582}
583
584wxBitmap Style::SetBitmapBrightnessAbs(wxBitmap& bitmap, double level) {
585 wxImage image = bitmap.ConvertToImage();
586
587 int gimg_width = image.GetWidth();
588 int gimg_height = image.GetHeight();
589
590 for (int iy = 0; iy < gimg_height; iy++) {
591 for (int ix = 0; ix < gimg_width; ix++) {
592 if (!image.IsTransparent(ix, iy, 30)) {
593 wxImage::RGBValue rgb(image.GetRed(ix, iy), image.GetGreen(ix, iy),
594 image.GetBlue(ix, iy));
595 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
596 hsv.value = hsv.value * level;
597 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
598 image.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
599 }
600 }
601 }
602 return wxBitmap(image);
603}
604
605wxBitmap Style::GetNormalBG() {
606 wxSize size = toolSize[currentOrientation];
607 return graphics->GetSubBitmap(wxRect(normalBGlocation[currentOrientation].x,
608 normalBGlocation[currentOrientation].y,
609 size.x, size.y));
610}
611
612wxBitmap Style::GetActiveBG() {
613 return graphics->GetSubBitmap(wxRect(activeBGlocation[currentOrientation].x,
614 activeBGlocation[currentOrientation].y,
615 toolSize[currentOrientation].x,
616 toolSize[currentOrientation].y));
617}
618
619wxBitmap Style::GetToggledBG() {
620 wxSize size = toolSize[currentOrientation];
621 if (toggledBGSize[currentOrientation].x) {
622 size = toggledBGSize[currentOrientation];
623 }
624 return graphics->GetSubBitmap(
625 wxRect(toggledBGlocation[currentOrientation], size));
626}
627
628wxBitmap Style::GetToolbarStart() {
629 wxSize size = toolbarStartSize[currentOrientation];
630 if (toolbarStartSize[currentOrientation].x == 0) {
631 size = toolbarStartSize[currentOrientation];
632 }
633 return graphics->GetSubBitmap(
634 wxRect(toolbarStartLoc[currentOrientation], size));
635}
636
637wxBitmap Style::GetToolbarEnd() {
638 wxSize size = toolbarEndSize[currentOrientation];
639 if (toolbarEndSize[currentOrientation].x == 0) {
640 size = toolbarEndSize[currentOrientation];
641 }
642 return graphics->GetSubBitmap(
643 wxRect(toolbarEndLoc[currentOrientation], size));
644}
645
646int Style::GetToolbarCornerRadius() { return cornerRadius[currentOrientation]; }
647
648void Style::DrawToolbarLineStart(wxBitmap& bmp, double scale) {
649 if (!HasToolbarStart()) return;
650 wxMemoryDC dc(bmp);
651 wxBitmap sbmp = GetToolbarStart();
652 if (fabs(scale - 1.0) > 0.01) {
653 int h = sbmp.GetHeight() * scale;
654 int w = sbmp.GetWidth() * scale;
655 if ((h > 0) && (w > 0)) {
656 wxImage scaled_image = sbmp.ConvertToImage();
657 sbmp = wxBitmap(scaled_image.Scale(w, h, wxIMAGE_QUALITY_HIGH));
658 }
659 }
660 dc.DrawBitmap(sbmp, 0, 0, true);
661 dc.SelectObject(wxNullBitmap);
662}
663
664void Style::DrawToolbarLineEnd(wxBitmap& bmp, double scale) {
665 if (!HasToolbarStart()) return;
666 wxMemoryDC dc(bmp);
667 wxBitmap sbmp = GetToolbarEnd();
668 if (fabs(scale - 1.0) > 0.01) {
669 int h = sbmp.GetHeight() * scale;
670 int w = sbmp.GetWidth() * scale;
671 if ((h > 0) && (w > 0)) {
672 wxImage scaled_image = sbmp.ConvertToImage();
673 sbmp = wxBitmap(scaled_image.Scale(w, h, wxIMAGE_QUALITY_HIGH));
674 }
675 }
676
677 if (currentOrientation) {
678 dc.DrawBitmap(sbmp, 0, bmp.GetHeight() - sbmp.GetHeight(), true);
679 } else {
680 dc.DrawBitmap(sbmp, bmp.GetWidth() - sbmp.GetWidth(), 0, true);
681 }
682 dc.SelectObject(wxNullBitmap);
683}
684
685void Style::SetOrientation(long orient) {
686 int newOrient = 0;
687 if (orient == wxTB_VERTICAL) newOrient = 1;
688 if (newOrient == currentOrientation) return;
689 currentOrientation = newOrient;
690 Unload();
691}
692
693int Style::GetOrientation() { return currentOrientation; }
694
695void Style::SetColorScheme(ColorScheme cs) {
696 colorscheme = cs;
697 Unload();
698
699 if ((consoleTextBackgroundSize.x) && (consoleTextBackgroundSize.y)) {
700 wxBitmap bm = graphics->GetSubBitmap(
701 wxRect(consoleTextBackgroundLoc, consoleTextBackgroundSize));
702
703 // The background bitmap in the icons file may be too small but it's better
704 // to resize it when we use it
705
706 consoleTextBackground = SetBitmapBrightness(bm, cs);
707 }
708}
709
710void Style::Unload() {
711 for (unsigned int i = 0; i < tools.Count(); i++) {
712 Tool* tool = (Tool*)tools[i];
713 tool->Unload();
714 }
715
716 for (unsigned int i = 0; i < icons.Count(); i++) {
717 Icon* icon = (Icon*)icons[i];
718 icon->Unload();
719 }
720}
721
722Style::Style(void) {
723 graphics = NULL;
724 currentOrientation = 0;
725 colorscheme = GLOBAL_COLOR_SCHEME_DAY;
726 marginsInvisible = false;
727 hasBackground = false;
728 chartStatusIconWidth = 0;
729 chartStatusWindowTransparent = false;
730 embossHeight = 40;
731 embossFont = wxEmptyString;
732
733 // Set compass window style defauilts
734 compassMarginTop = 4;
735 compassMarginRight = 0;
736 compassMarginBottom = 4;
737 compassMarginLeft = 4;
738 compasscornerRadius = 3;
739 compassXoffset = 0;
740 compassYoffset = 0;
741
742 for (int i = 0; i < 2; i++) {
743 toolbarStartLoc[i] = wxPoint(0, 0);
744 toolbarEndLoc[i] = wxPoint(0, 0);
745 cornerRadius[i] = 0;
746 }
747}
748
749Style::~Style(void) {
750 for (unsigned int i = 0; i < tools.Count(); i++) {
751 delete (Tool*)(tools[i]);
752 }
753 tools.Clear();
754
755 for (unsigned int i = 0; i < icons.Count(); i++) {
756 delete (Icon*)(icons[i]);
757 }
758 icons.Clear();
759
760 if (graphics) delete graphics;
761
762 toolIndex.clear();
763 iconIndex.clear();
764}
765
766StyleManager::StyleManager(void) {
767 isOK = false;
768 currentStyle = NULL;
769 Init(g_Platform->GetSharedDataDir() + "uidata" +
770 wxFileName::GetPathSeparator());
771 Init(g_Platform->GetHomeDir());
772 Init(g_Platform->GetHomeDir() + ".opencpn" + wxFileName::GetPathSeparator());
773 SetStyle("");
774#ifdef ocpnUSE_SVG
775 wxLogMessage("Using SVG Icons");
776#else
777 wxLogMessage("Using PNG Icons");
778#endif
779}
780
781StyleManager::StyleManager(const wxString& configDir) {
782 isOK = false;
783 currentStyle = NULL;
784 Init(configDir);
785 SetStyle("");
786}
787
788StyleManager::~StyleManager(void) {
789 for (unsigned int i = 0; i < styles.Count(); i++) {
790 delete (Style*)(styles[i]);
791 }
792 styles.Clear();
793}
794
795void StyleManager::Init(const wxString& fromPath) {
796 TiXmlDocument doc;
797
798 if (!wxDir::Exists(fromPath)) {
799 wxString msg = "No styles found at: ";
800 msg << fromPath;
801 wxLogMessage(msg);
802 return;
803 }
804
805 wxDir dir(fromPath);
806 if (!dir.IsOpened()) return;
807
808 wxString filename;
809
810 // We allow any number of styles to load from files called
811 // style<something>.xml
812
813 bool more = dir.GetFirst(&filename, "style*.xml", wxDIR_FILES);
814
815 if (!more) {
816 wxString msg = "No styles found at: ";
817 msg << fromPath;
818 wxLogMessage(msg);
819 return;
820 }
821
822 bool firstFile = true;
823 while (more) {
824 wxString name, extension;
825
826 if (!firstFile) more = dir.GetNext(&filename);
827 if (!more) break;
828 firstFile = false;
829
830 wxString fullFilePath = fromPath + filename;
831
832 if (!doc.LoadFile((const char*)fullFilePath.mb_str())) {
833 wxString msg("Attempt to load styles from this file failed: ");
834 msg += fullFilePath;
835 wxLogMessage(msg);
836 continue;
837 }
838
839 wxString msg("Styles loading from ");
840 msg += fullFilePath;
841 wxLogMessage(msg);
842
843 TiXmlHandle hRoot(doc.RootElement());
844
845 wxString root = wxString(doc.RootElement()->Value(), wxConvUTF8);
846 if (root != _T("styles" )) {
847 wxLogMessage(" StyleManager: Expected XML Root <styles> not found.");
848 continue;
849 }
850
851 TiXmlElement* styleElem = hRoot.FirstChild().Element();
852
853 for (; styleElem; styleElem = styleElem->NextSiblingElement()) {
854 if (wxString(styleElem->Value(), wxConvUTF8) == "style") {
855 Style* style = new Style();
856 styles.Add(style);
857
858 style->name = wxString(styleElem->Attribute("name"), wxConvUTF8);
859 style->sysname = wxString(styleElem->Attribute("sysname"), wxConvUTF8);
860 style->myConfigFileDir = fromPath;
861
862 TiXmlElement* subNode = styleElem->FirstChild()->ToElement();
863
864 for (; subNode; subNode = subNode->NextSiblingElement()) {
865 wxString nodeType(subNode->Value(), wxConvUTF8);
866
867 if (nodeType == "description") {
868 style->description = wxString(subNode->GetText(), wxConvUTF8);
869 continue;
870 }
871 if (nodeType == "chart-status-icon") {
872 int w = 0;
873 subNode->QueryIntAttribute("width", &w);
874 style->chartStatusIconWidth = w;
875 continue;
876 }
877 if (nodeType == "chart-status-window") {
878 style->chartStatusWindowTransparent =
879 wxString(subNode->Attribute("transparent"), wxConvUTF8)
880 .Lower()
881 .IsSameAs("true");
882 continue;
883 }
884 if (nodeType == "embossed-indicators") {
885 style->embossFont =
886 wxString(subNode->Attribute("font"), wxConvUTF8);
887 subNode->QueryIntAttribute("size", &(style->embossHeight));
888 continue;
889 }
890 if (nodeType == "graphics-file") {
891 style->graphicsFile =
892 wxString(subNode->Attribute("name"), wxConvUTF8);
893 isOK = true; // If we got this far we are at least partially OK...
894 continue;
895 }
896 if (nodeType == "active-route") {
897 TiXmlHandle handle(subNode);
898 TiXmlElement* tag = handle.Child("font-color", 0).ToElement();
899 if (tag) {
900 int r, g, b;
901 tag->QueryIntAttribute("r", &r);
902 tag->QueryIntAttribute("g", &g);
903 tag->QueryIntAttribute("b", &b);
904 style->consoleFontColor = wxColour(r, g, b);
905 }
906 tag = handle.Child("text-background-location", 0).ToElement();
907 if (tag) {
908 int x, y, w, h;
909 tag->QueryIntAttribute("x", &x);
910 tag->QueryIntAttribute("y", &y);
911 tag->QueryIntAttribute("width", &w);
912 tag->QueryIntAttribute("height", &h);
913 style->consoleTextBackgroundLoc = wxPoint(x, y);
914 style->consoleTextBackgroundSize = wxSize(w, h);
915 }
916 continue;
917 }
918 if (nodeType == "icons") {
919 TiXmlElement* iconNode = subNode->FirstChild()->ToElement();
920
921 for (; iconNode; iconNode = iconNode->NextSiblingElement()) {
922 wxString nodeType(iconNode->Value(), wxConvUTF8);
923 if (nodeType == "icon") {
924 Icon* icon = new Icon();
925 style->icons.Add(icon);
926 icon->name = wxString(iconNode->Attribute("name"), wxConvUTF8);
927 style->iconIndex[icon->name] = style->icons.Count() - 1;
928 TiXmlHandle handle(iconNode);
929 TiXmlElement* tag =
930 handle.Child("icon-location", 0).ToElement();
931 if (tag) {
932 int x, y;
933 tag->QueryIntAttribute("x", &x);
934 tag->QueryIntAttribute("y", &y);
935 icon->iconLoc = wxPoint(x, y);
936 }
937 tag = handle.Child("size", 0).ToElement();
938 if (tag) {
939 int x, y;
940 tag->QueryIntAttribute("x", &x);
941 tag->QueryIntAttribute("y", &y);
942 icon->size = wxSize(x, y);
943 }
944 }
945 }
946 }
947 if (nodeType == "tools") {
948 TiXmlElement* toolNode = subNode->FirstChild()->ToElement();
949
950 for (; toolNode; toolNode = toolNode->NextSiblingElement()) {
951 wxString nodeType(toolNode->Value(), wxConvUTF8);
952
953 if (nodeType == "horizontal" || nodeType == "vertical") {
954 int orientation = 0;
955 if (nodeType == "vertical") orientation = 1;
956
957 TiXmlElement* attrNode = toolNode->FirstChild()->ToElement();
958 for (; attrNode; attrNode = attrNode->NextSiblingElement()) {
959 wxString nodeType(attrNode->Value(), wxConvUTF8);
960 if (nodeType == "separation") {
961 attrNode->QueryIntAttribute(
962 "distance", &style->toolSeparation[orientation]);
963 continue;
964 }
965 if (nodeType == "margin") {
966 attrNode->QueryIntAttribute(
967 "top", &style->toolMarginTop[orientation]);
968 attrNode->QueryIntAttribute(
969 "right", &style->toolMarginRight[orientation]);
970 attrNode->QueryIntAttribute(
971 "bottom", &style->toolMarginBottom[orientation]);
972 attrNode->QueryIntAttribute(
973 "left", &style->toolMarginLeft[orientation]);
974 wxString invis =
975 wxString(attrNode->Attribute("invisible"), wxConvUTF8);
976 style->marginsInvisible = (invis.Lower() == "true");
977 continue;
978 ;
979 }
980 if (nodeType == "toggled-location") {
981 int x, y;
982 attrNode->QueryIntAttribute("x", &x);
983 attrNode->QueryIntAttribute("y", &y);
984 style->toggledBGlocation[orientation] = wxPoint(x, y);
985 x = 0;
986 y = 0;
987 attrNode->QueryIntAttribute("width", &x);
988 attrNode->QueryIntAttribute("height", &y);
989 style->toggledBGSize[orientation] = wxSize(x, y);
990 continue;
991 }
992 if (nodeType == "toolbar-start") {
993 int x, y;
994 attrNode->QueryIntAttribute("x", &x);
995 attrNode->QueryIntAttribute("y", &y);
996 style->toolbarStartLoc[orientation] = wxPoint(x, y);
997 x = 0;
998 y = 0;
999 attrNode->QueryIntAttribute("width", &x);
1000 attrNode->QueryIntAttribute("height", &y);
1001 style->toolbarStartSize[orientation] = wxSize(x, y);
1002 continue;
1003 }
1004 if (nodeType == "toolbar-end") {
1005 int x, y;
1006 attrNode->QueryIntAttribute("x", &x);
1007 attrNode->QueryIntAttribute("y", &y);
1008 style->toolbarEndLoc[orientation] = wxPoint(x, y);
1009 x = 0;
1010 y = 0;
1011 attrNode->QueryIntAttribute("width", &x);
1012 attrNode->QueryIntAttribute("height", &y);
1013 style->toolbarEndSize[orientation] = wxSize(x, y);
1014 continue;
1015 }
1016 if (nodeType == "toolbar-corners") {
1017 int r;
1018 attrNode->QueryIntAttribute("radius", &r);
1019 style->cornerRadius[orientation] = r;
1020 continue;
1021 }
1022 if (nodeType == "background-location") {
1023 int x, y;
1024 attrNode->QueryIntAttribute("x", &x);
1025 attrNode->QueryIntAttribute("y", &y);
1026 style->normalBGlocation[orientation] = wxPoint(x, y);
1027 style->HasBackground(true);
1028 continue;
1029 }
1030 if (nodeType == "active-location") {
1031 int x, y;
1032 attrNode->QueryIntAttribute("x", &x);
1033 attrNode->QueryIntAttribute("y", &y);
1034 style->activeBGlocation[orientation] = wxPoint(x, y);
1035 continue;
1036 }
1037 if (nodeType == "size") {
1038 int x, y;
1039 attrNode->QueryIntAttribute("x", &x);
1040 attrNode->QueryIntAttribute("y", &y);
1041 style->toolSize[orientation] = wxSize(x, y);
1042 continue;
1043 }
1044 if (nodeType == "icon-offset") {
1045 int x, y;
1046 attrNode->QueryIntAttribute("x", &x);
1047 attrNode->QueryIntAttribute("y", &y);
1048 style->verticalIconOffset = wxSize(x, y);
1049 continue;
1050 }
1051 }
1052 continue;
1053 }
1054 if (nodeType == "compass") {
1055 TiXmlElement* attrNode = toolNode->FirstChild()->ToElement();
1056 for (; attrNode; attrNode = attrNode->NextSiblingElement()) {
1057 wxString nodeType(attrNode->Value(), wxConvUTF8);
1058 if (nodeType == "margin") {
1059 attrNode->QueryIntAttribute("top",
1060 &style->compassMarginTop);
1061 attrNode->QueryIntAttribute("right",
1062 &style->compassMarginRight);
1063 attrNode->QueryIntAttribute("bottom",
1064 &style->compassMarginBottom);
1065 attrNode->QueryIntAttribute("left",
1066 &style->compassMarginLeft);
1067 continue;
1068 }
1069 if (nodeType == "compass-corners") {
1070 int r;
1071 attrNode->QueryIntAttribute("radius", &r);
1072 style->compasscornerRadius = r;
1073 continue;
1074 }
1075 if (nodeType == "offset") {
1076 attrNode->QueryIntAttribute("x", &style->compassXoffset);
1077 attrNode->QueryIntAttribute("y", &style->compassYoffset);
1078 continue;
1079 }
1080 }
1081 }
1082
1083 if (nodeType == "tool") {
1084 Tool* tool = new Tool();
1085 style->tools.Add(tool);
1086 tool->name = wxString(toolNode->Attribute("name"), wxConvUTF8);
1087 style->toolIndex[tool->name] = style->tools.Count() - 1;
1088 TiXmlHandle toolHandle(toolNode);
1089 TiXmlElement* toolTag =
1090 toolHandle.Child("icon-location", 0).ToElement();
1091 if (toolTag) {
1092 int x, y;
1093 toolTag->QueryIntAttribute("x", &x);
1094 toolTag->QueryIntAttribute("y", &y);
1095 tool->iconLoc = wxPoint(x, y);
1096 }
1097 toolTag = toolHandle.Child("rollover-location", 0).ToElement();
1098 if (toolTag) {
1099 int x, y;
1100 toolTag->QueryIntAttribute("x", &x);
1101 toolTag->QueryIntAttribute("y", &y);
1102 tool->rolloverLoc = wxPoint(x, y);
1103 }
1104 toolTag = toolHandle.Child("disabled-location", 0).ToElement();
1105 if (toolTag) {
1106 int x, y;
1107 toolTag->QueryIntAttribute("x", &x);
1108 toolTag->QueryIntAttribute("y", &y);
1109 tool->disabledLoc = wxPoint(x, y);
1110 }
1111 toolTag = toolHandle.Child("size", 0).ToElement();
1112 if (toolTag) {
1113 int x, y;
1114 toolTag->QueryIntAttribute("x", &x);
1115 toolTag->QueryIntAttribute("y", &y);
1116 tool->customSize = wxSize(x, y);
1117 }
1118 continue;
1119 }
1120 }
1121 continue;
1122 }
1123 }
1124 }
1125 }
1126 }
1127}
1128
1129void StyleManager::SetStyle(wxString name) {
1130 Style* style = NULL;
1131 bool ok = true;
1132 if (currentStyle)
1133 currentStyle->Unload();
1134 else
1135 ok = false;
1136
1137 bool selectFirst = false;
1138
1139 // Verify the named style exists
1140 // If not, just use the "first" style
1141 bool bstyleFound = false;
1142
1143 for (unsigned int i = 0; i < styles.Count(); i++) {
1144 style = (Style*)(styles.Item(i));
1145 if (style->name == name) {
1146 bstyleFound = true;
1147 break;
1148 }
1149 }
1150
1151 if ((name.Length() == 0) || !bstyleFound) selectFirst = true;
1152
1153 for (unsigned int i = 0; i < styles.Count(); i++) {
1154 style = (Style*)(styles[i]);
1155 if (style->name == name || selectFirst) {
1156 if (style->graphics) {
1157 currentStyle = style;
1158 ok = true;
1159 break;
1160 }
1161
1162 wxString fullFilePath = style->myConfigFileDir +
1163 wxFileName::GetPathSeparator() +
1164 style->graphicsFile;
1165
1166 if (!wxFileName::FileExists(fullFilePath)) {
1167 wxString msg("Styles Graphics File not found: ");
1168 msg += fullFilePath;
1169 wxLogMessage(msg);
1170 ok = false;
1171 if (selectFirst) continue;
1172 break;
1173 }
1174
1175 wxImage img; // Only image does PNG LoadFile properly on GTK.
1176
1177 if (!img.LoadFile(fullFilePath, wxBITMAP_TYPE_PNG)) {
1178 wxString msg("Styles Graphics File failed to load: ");
1179 msg += fullFilePath;
1180 wxLogMessage(msg);
1181 ok = false;
1182 break;
1183 }
1184 style->graphics = new wxBitmap(img);
1185 currentStyle = style;
1186 ok = true;
1187 break;
1188 }
1189 }
1190
1191 if (!ok || !currentStyle->graphics) {
1192 wxString msg("The requested style was not found: ");
1193 msg += name;
1194 wxLogMessage(msg);
1195 return;
1196 }
1197
1198 if (currentStyle) {
1199 if ((currentStyle->consoleTextBackgroundSize.x) &&
1200 (currentStyle->consoleTextBackgroundSize.y)) {
1201 currentStyle->consoleTextBackground =
1202 currentStyle->graphics->GetSubBitmap(
1203 wxRect(currentStyle->consoleTextBackgroundLoc,
1204 currentStyle->consoleTextBackgroundSize));
1205 }
1206 }
1207
1208 if (currentStyle) nextInvocationStyle = currentStyle->name;
1209
1210 return;
1211}
1212
1213Style* StyleManager::GetCurrentStyle() { return currentStyle; }
Provides platform-specific support utilities for OpenCPN.
Global color handling by name.
SVG utilities.