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