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