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 colorscheme == GLOBAL_COLOR_SCHEME_NIGHT_HICON)
380 dimLevel = 0.5;
381 else if (colorscheme == GLOBAL_COLOR_SCHEME_NIGHT)
382 dimLevel = 0.5;
383 tool->icon = SetBitmapBrightnessAbs(bm, dimLevel);
384 } else {
385 tool->icon = SetBitmapBrightness(bm, colorscheme);
386 }
387
388 tool->iconLoaded = true;
389 return tool->icon;
390 }
391 }
392 case TOOLICON_TOGGLED: {
393 if (tool->toggledLoaded && !rollover) return tool->toggled;
394 if (tool->rolloverToggledLoaded && rollover) return tool->rolloverToggled;
395
396 wxRect location(tool->iconLoc, size);
397 if (rollover) location = wxRect(tool->rolloverLoc, size);
398 wxSize offset(0, 0);
399 if (GetToolSize() != GetToggledToolSize()) {
400 offset = GetToggledToolSize() - GetToolSize();
401 offset /= 2;
402 }
403 if (currentOrientation) {
404 location.x -= verticalIconOffset.x;
405 location.y -= verticalIconOffset.y;
406 }
407 wxBitmap bm;
408#ifdef ocpnUSE_SVG
409 wxString fullFilePath;
410 if (rollover)
411 fullFilePath = myConfigFileDir + this->sysname +
412 wxFileName::GetPathSeparator() + toolname +
413 "_rollover_toggled.svg";
414 else
415 fullFilePath = myConfigFileDir + this->sysname +
416 wxFileName::GetPathSeparator() + toolname +
417 "_toggled.svg";
418 if (wxFileExists(fullFilePath))
419 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
420 else {
421 // Could not find a toggled SVG, so try to make one
422 if (rollover)
423 fullFilePath = myConfigFileDir + this->sysname +
424 wxFileName::GetPathSeparator() + toolname +
425 "_rollover.svg";
426 else
427 fullFilePath = myConfigFileDir + this->sysname +
428 wxFileName::GetPathSeparator() + toolname + ".svg";
429
430 if (wxFileExists(fullFilePath)) {
431 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
432
433 wxBitmap bmBack = GetToggledBG();
434 if ((bmBack.GetWidth() != retSize.x) ||
435 (bmBack.GetHeight() != retSize.y)) {
436 wxImage scaled_back = bmBack.ConvertToImage();
437 bmBack = wxBitmap(
438 scaled_back.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
439 }
440 bm = MergeBitmaps(bmBack, bm, wxSize(0, 0));
441 }
442 }
443
444#endif // ocpnUSE_SVG
445 if (!bm.Ok()) {
446 bm = graphics->GetSubBitmap(location);
447 bm = MergeBitmaps(GetToggledBG(), bm, offset);
448
449 if (retSize != size) {
450 wxImage scaled_image = bm.ConvertToImage();
451 bm = wxBitmap(
452 scaled_image.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
453 }
454 }
455
456 if (rollover) {
457 tool->rolloverToggled = SetBitmapBrightness(bm, colorscheme);
458 tool->rolloverToggledLoaded = true;
459 return tool->rolloverToggled;
460 } else {
461 tool->toggled = SetBitmapBrightness(bm, colorscheme);
462 tool->toggledLoaded = true;
463 return tool->toggled;
464 }
465 }
466 case TOOLICON_DISABLED: {
467 if (tool->disabledLoaded) return tool->disabled;
468 wxRect location(tool->disabledLoc, size);
469
470 wxBitmap bm;
471#ifdef ocpnUSE_SVG
472 wxString fullFilePath = myConfigFileDir + this->sysname +
473 wxFileName::GetPathSeparator() + toolname +
474 "_disabled.svg";
475 if (wxFileExists(fullFilePath))
476 bm = LoadSVG(fullFilePath, retSize.x, retSize.y);
477 else {
479#endif // ocpnUSE_SVG
480 bm = graphics->GetSubBitmap(location);
481
482 if (hasBackground) {
483 bm = MergeBitmaps(GetNormalBG(), bm, wxSize(0, 0));
484 }
485
486 if (retSize != size) {
487 wxImage scaled_image = bm.ConvertToImage();
488 bm = wxBitmap(
489 scaled_image.Scale(retSize.x, retSize.y, wxIMAGE_QUALITY_HIGH));
490 }
491#ifdef ocpnUSE_SVG
492 }
493#endif // ocpnUSE_SVG
494 if (currentOrientation) {
495 location.x -= verticalIconOffset.x;
496 location.y -= verticalIconOffset.y;
497 }
498 tool->disabled = SetBitmapBrightness(bm, colorscheme);
499 tool->disabledLoaded = true;
500 return tool->disabled;
501 }
502 }
503 wxString msg(
504 "A requested icon type for this tool was not found in the style: ");
505 msg += toolname;
506 wxLogMessage(msg);
507 return wxBitmap(GetToolSize().x, GetToolSize().y); // Prevents crashing.
508}
509
510wxBitmap Style::BuildPluginIcon(wxBitmap& bm, int iconType, double factor) {
511 if (!bm.IsOk()) return wxNullBitmap;
512
513 wxBitmap iconbm;
514
515 switch (iconType) {
516 case TOOLICON_NORMAL:
517 case TOOLICON_TOGGLED: {
518 if (hasBackground) {
519 wxBitmap bg;
520 if (iconType == TOOLICON_NORMAL)
521 bg = GetNormalBG();
522 else
523 bg = GetToggledBG();
524
525 if ((bg.GetWidth() >= bm.GetWidth()) &&
526 (bg.GetHeight() >= bm.GetHeight())) {
527 int w = bg.GetWidth() * factor;
528 int h = bg.GetHeight() * factor;
529 wxImage scaled_image = bg.ConvertToImage();
530 bg = wxBitmap(scaled_image.Scale(w, h, wxIMAGE_QUALITY_HIGH));
531
532 wxSize offset = wxSize(bg.GetWidth() - bm.GetWidth(),
533 bg.GetHeight() - bm.GetHeight());
534 offset /= 2;
535 iconbm = MergeBitmaps(bg, bm, offset);
536 } else {
537 // A bit of contorted logic for non-square backgrounds...
538 double factor = ((double)bm.GetHeight()) / bg.GetHeight();
539 int nw = bg.GetWidth() * factor;
540 int nh = bm.GetHeight();
541 if (bg.GetWidth() == bg.GetHeight()) nw = nh;
542 wxImage scaled_image = bg.ConvertToImage();
543 bg = wxBitmap(scaled_image.Scale(nw, nh, wxIMAGE_QUALITY_HIGH));
544
545 wxSize offset = wxSize(bg.GetWidth() - bm.GetWidth(),
546 bg.GetHeight() - bm.GetHeight());
547 offset /= 2;
548 iconbm = MergeBitmaps(bg, bm, offset);
549 }
550
551 } else {
552 wxBitmap bg(GetToolSize().x, GetToolSize().y);
553 wxMemoryDC mdc(bg);
554 wxSize offset = GetToolSize() - wxSize(bm.GetWidth(), bm.GetHeight());
555 offset /= 2;
556 mdc.SetBackground(wxBrush(GetGlobalColor("GREY2"), wxBRUSHSTYLE_SOLID));
557 mdc.Clear();
558 mdc.SelectObject(wxNullBitmap);
559 iconbm = MergeBitmaps(bg, bm, offset);
560 }
561 break;
562 }
563 default:
564 return wxNullBitmap;
565 break;
566 }
567 return SetBitmapBrightness(iconbm, colorscheme);
568}
569
570wxBitmap Style::SetBitmapBrightness(wxBitmap& bitmap, ColorScheme cs) {
571 double dimLevel;
572 switch (cs) {
573 case GLOBAL_COLOR_SCHEME_DUSK:
574 case GLOBAL_COLOR_SCHEME_NIGHT_HICON: {
575 dimLevel = 0.8;
576 break;
577 }
578 case GLOBAL_COLOR_SCHEME_NIGHT: {
579 dimLevel = 0.5;
580 break;
581 }
582 default: {
583 return bitmap;
584 }
585 }
586
587 return SetBitmapBrightnessAbs(bitmap, dimLevel);
588}
589
590wxBitmap Style::SetBitmapBrightnessAbs(wxBitmap& bitmap, double level) {
591 wxImage image = bitmap.ConvertToImage();
592
593 int gimg_width = image.GetWidth();
594 int gimg_height = image.GetHeight();
595
596 for (int iy = 0; iy < gimg_height; iy++) {
597 for (int ix = 0; ix < gimg_width; ix++) {
598 if (!image.IsTransparent(ix, iy, 30)) {
599 wxImage::RGBValue rgb(image.GetRed(ix, iy), image.GetGreen(ix, iy),
600 image.GetBlue(ix, iy));
601 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
602 hsv.value = hsv.value * level;
603 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
604 image.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
605 }
606 }
607 }
608 return wxBitmap(image);
609}
610
611wxBitmap Style::GetNormalBG() {
612 wxSize size = toolSize[currentOrientation];
613 return graphics->GetSubBitmap(wxRect(normalBGlocation[currentOrientation].x,
614 normalBGlocation[currentOrientation].y,
615 size.x, size.y));
616}
617
618wxBitmap Style::GetActiveBG() {
619 return graphics->GetSubBitmap(wxRect(activeBGlocation[currentOrientation].x,
620 activeBGlocation[currentOrientation].y,
621 toolSize[currentOrientation].x,
622 toolSize[currentOrientation].y));
623}
624
625wxBitmap Style::GetToggledBG() {
626 wxSize size = toolSize[currentOrientation];
627 if (toggledBGSize[currentOrientation].x) {
628 size = toggledBGSize[currentOrientation];
629 }
630 return graphics->GetSubBitmap(
631 wxRect(toggledBGlocation[currentOrientation], size));
632}
633
634wxBitmap Style::GetToolbarStart() {
635 wxSize size = toolbarStartSize[currentOrientation];
636 if (toolbarStartSize[currentOrientation].x == 0) {
637 size = toolbarStartSize[currentOrientation];
638 }
639 return graphics->GetSubBitmap(
640 wxRect(toolbarStartLoc[currentOrientation], size));
641}
642
643wxBitmap Style::GetToolbarEnd() {
644 wxSize size = toolbarEndSize[currentOrientation];
645 if (toolbarEndSize[currentOrientation].x == 0) {
646 size = toolbarEndSize[currentOrientation];
647 }
648 return graphics->GetSubBitmap(
649 wxRect(toolbarEndLoc[currentOrientation], size));
650}
651
652int Style::GetToolbarCornerRadius() { return cornerRadius[currentOrientation]; }
653
654void Style::DrawToolbarLineStart(wxBitmap& bmp, double scale) {
655 if (!HasToolbarStart()) return;
656 wxMemoryDC dc(bmp);
657 wxBitmap sbmp = GetToolbarStart();
658 if (fabs(scale - 1.0) > 0.01) {
659 int h = sbmp.GetHeight() * scale;
660 int w = sbmp.GetWidth() * scale;
661 if ((h > 0) && (w > 0)) {
662 wxImage scaled_image = sbmp.ConvertToImage();
663 sbmp = wxBitmap(scaled_image.Scale(w, h, wxIMAGE_QUALITY_HIGH));
664 }
665 }
666 dc.DrawBitmap(sbmp, 0, 0, true);
667 dc.SelectObject(wxNullBitmap);
668}
669
670void Style::DrawToolbarLineEnd(wxBitmap& bmp, double scale) {
671 if (!HasToolbarStart()) return;
672 wxMemoryDC dc(bmp);
673 wxBitmap sbmp = GetToolbarEnd();
674 if (fabs(scale - 1.0) > 0.01) {
675 int h = sbmp.GetHeight() * scale;
676 int w = sbmp.GetWidth() * scale;
677 if ((h > 0) && (w > 0)) {
678 wxImage scaled_image = sbmp.ConvertToImage();
679 sbmp = wxBitmap(scaled_image.Scale(w, h, wxIMAGE_QUALITY_HIGH));
680 }
681 }
682
683 if (currentOrientation) {
684 dc.DrawBitmap(sbmp, 0, bmp.GetHeight() - sbmp.GetHeight(), true);
685 } else {
686 dc.DrawBitmap(sbmp, bmp.GetWidth() - sbmp.GetWidth(), 0, true);
687 }
688 dc.SelectObject(wxNullBitmap);
689}
690
691void Style::SetOrientation(long orient) {
692 int newOrient = 0;
693 if (orient == wxTB_VERTICAL) newOrient = 1;
694 if (newOrient == currentOrientation) return;
695 currentOrientation = newOrient;
696 Unload();
697}
698
699int Style::GetOrientation() { return currentOrientation; }
700
701void Style::SetColorScheme(ColorScheme cs) {
702 colorscheme = cs;
703 Unload();
704
705 if ((consoleTextBackgroundSize.x) && (consoleTextBackgroundSize.y)) {
706 wxBitmap bm = graphics->GetSubBitmap(
707 wxRect(consoleTextBackgroundLoc, consoleTextBackgroundSize));
708
709 // The background bitmap in the icons file may be too small but it's better
710 // to resize it when we use it
711
712 consoleTextBackground = SetBitmapBrightness(bm, cs);
713 }
714}
715
716void Style::Unload() {
717 for (unsigned int i = 0; i < tools.Count(); i++) {
718 Tool* tool = (Tool*)tools[i];
719 tool->Unload();
720 }
721
722 for (unsigned int i = 0; i < icons.Count(); i++) {
723 Icon* icon = (Icon*)icons[i];
724 icon->Unload();
725 }
726}
727
728Style::Style() {
729 graphics = NULL;
730 currentOrientation = 0;
731 colorscheme = GLOBAL_COLOR_SCHEME_DAY;
732 marginsInvisible = false;
733 hasBackground = false;
734 chartStatusIconWidth = 0;
735 chartStatusWindowTransparent = false;
736 embossHeight = 40;
737 embossFont = "";
738
739 // Set compass window style defauilts
740 compassMarginTop = 4;
741 compassMarginRight = 0;
742 compassMarginBottom = 4;
743 compassMarginLeft = 4;
744 compasscornerRadius = 3;
745 compassXoffset = 0;
746 compassYoffset = 0;
747
748 for (int i = 0; i < 2; i++) {
749 toolbarStartLoc[i] = wxPoint(0, 0);
750 toolbarEndLoc[i] = wxPoint(0, 0);
751 cornerRadius[i] = 0;
752 }
753}
754
755Style::~Style() {
756 for (unsigned int i = 0; i < tools.Count(); i++) {
757 delete (Tool*)(tools[i]);
758 }
759 tools.Clear();
760
761 for (unsigned int i = 0; i < icons.Count(); i++) {
762 delete (Icon*)(icons[i]);
763 }
764 icons.Clear();
765
766 if (graphics) delete graphics;
767
768 toolIndex.clear();
769 iconIndex.clear();
770}
771
772StyleManager::StyleManager() {
773 isOK = false;
774 currentStyle = NULL;
775 Init(g_Platform->GetSharedDataDir() + "uidata" +
776 wxFileName::GetPathSeparator());
777 Init(g_Platform->GetHomeDir());
778 Init(g_Platform->GetHomeDir() + ".opencpn" + wxFileName::GetPathSeparator());
779 SetStyle("");
780#ifdef ocpnUSE_SVG
781 wxLogMessage("Using SVG Icons");
782#else
783 wxLogMessage("Using PNG Icons");
784#endif
785}
786
787StyleManager::StyleManager(const wxString& configDir) {
788 isOK = false;
789 currentStyle = NULL;
790 Init(configDir);
791 SetStyle("");
792}
793
794StyleManager::~StyleManager() {
795 for (unsigned int i = 0; i < styles.Count(); i++) {
796 delete (Style*)(styles[i]);
797 }
798 styles.Clear();
799}
800
801void StyleManager::Init(const wxString& fromPath) {
802 TiXmlDocument doc;
803
804 if (!wxDir::Exists(fromPath)) {
805 wxString msg = "No styles found at: ";
806 msg << fromPath;
807 wxLogMessage(msg);
808 return;
809 }
810
811 wxDir dir(fromPath);
812 if (!dir.IsOpened()) return;
813
814 wxString filename;
815
816 // We allow any number of styles to load from files called
817 // style<something>.xml
818
819 bool more = dir.GetFirst(&filename, "style*.xml", wxDIR_FILES);
820
821 if (!more) {
822 wxString msg = "No styles found at: ";
823 msg << fromPath;
824 wxLogMessage(msg);
825 return;
826 }
827
828 bool firstFile = true;
829 while (more) {
830 wxString name, extension;
831
832 if (!firstFile) more = dir.GetNext(&filename);
833 if (!more) break;
834 firstFile = false;
835
836 wxString fullFilePath = fromPath + filename;
837
838 if (!doc.LoadFile((const char*)fullFilePath.mb_str())) {
839 wxString msg("Attempt to load styles from this file failed: ");
840 msg += fullFilePath;
841 wxLogMessage(msg);
842 continue;
843 }
844
845 wxString msg("Styles loading from ");
846 msg += fullFilePath;
847 wxLogMessage(msg);
848
849 TiXmlHandle hRoot(doc.RootElement());
850
851 wxString root = wxString(doc.RootElement()->Value(), wxConvUTF8);
852 if (root != "styles") {
853 wxLogMessage(" StyleManager: Expected XML Root <styles> not found.");
854 continue;
855 }
856
857 TiXmlElement* styleElem = hRoot.FirstChild().Element();
858
859 for (; styleElem; styleElem = styleElem->NextSiblingElement()) {
860 if (wxString(styleElem->Value(), wxConvUTF8) == "style") {
861 Style* style = new Style();
862 styles.Add(style);
863
864 style->name = wxString(styleElem->Attribute("name"), wxConvUTF8);
865 style->sysname = wxString(styleElem->Attribute("sysname"), wxConvUTF8);
866 style->myConfigFileDir = fromPath;
867
868 TiXmlElement* subNode = styleElem->FirstChild()->ToElement();
869
870 for (; subNode; subNode = subNode->NextSiblingElement()) {
871 wxString nodeType(subNode->Value(), wxConvUTF8);
872
873 if (nodeType == "description") {
874 style->description = wxString(subNode->GetText(), wxConvUTF8);
875 continue;
876 }
877 if (nodeType == "chart-status-icon") {
878 int w = 0;
879 subNode->QueryIntAttribute("width", &w);
880 style->chartStatusIconWidth = w;
881 continue;
882 }
883 if (nodeType == "chart-status-window") {
884 style->chartStatusWindowTransparent =
885 wxString(subNode->Attribute("transparent"), wxConvUTF8)
886 .Lower()
887 .IsSameAs("true");
888 continue;
889 }
890 if (nodeType == "embossed-indicators") {
891 style->embossFont =
892 wxString(subNode->Attribute("font"), wxConvUTF8);
893 subNode->QueryIntAttribute("size", &(style->embossHeight));
894 continue;
895 }
896 if (nodeType == "graphics-file") {
897 style->graphicsFile =
898 wxString(subNode->Attribute("name"), wxConvUTF8);
899 isOK = true; // If we got this far we are at least partially OK...
900 continue;
901 }
902 if (nodeType == "active-route") {
903 TiXmlHandle handle(subNode);
904 TiXmlElement* tag = handle.Child("font-color", 0).ToElement();
905 if (tag) {
906 int r, g, b;
907 tag->QueryIntAttribute("r", &r);
908 tag->QueryIntAttribute("g", &g);
909 tag->QueryIntAttribute("b", &b);
910 style->consoleFontColor = wxColour(r, g, b);
911 }
912 tag = handle.Child("text-background-location", 0).ToElement();
913 if (tag) {
914 int x, y, w, h;
915 tag->QueryIntAttribute("x", &x);
916 tag->QueryIntAttribute("y", &y);
917 tag->QueryIntAttribute("width", &w);
918 tag->QueryIntAttribute("height", &h);
919 style->consoleTextBackgroundLoc = wxPoint(x, y);
920 style->consoleTextBackgroundSize = wxSize(w, h);
921 }
922 continue;
923 }
924 if (nodeType == "icons") {
925 TiXmlElement* iconNode = subNode->FirstChild()->ToElement();
926
927 for (; iconNode; iconNode = iconNode->NextSiblingElement()) {
928 wxString nodeType(iconNode->Value(), wxConvUTF8);
929 if (nodeType == "icon") {
930 Icon* icon = new Icon();
931 style->icons.Add(icon);
932 icon->name = wxString(iconNode->Attribute("name"), wxConvUTF8);
933 style->iconIndex[icon->name] = style->icons.Count() - 1;
934 TiXmlHandle handle(iconNode);
935 TiXmlElement* tag =
936 handle.Child("icon-location", 0).ToElement();
937 if (tag) {
938 int x, y;
939 tag->QueryIntAttribute("x", &x);
940 tag->QueryIntAttribute("y", &y);
941 icon->iconLoc = wxPoint(x, y);
942 }
943 tag = handle.Child("size", 0).ToElement();
944 if (tag) {
945 int x, y;
946 tag->QueryIntAttribute("x", &x);
947 tag->QueryIntAttribute("y", &y);
948 icon->size = wxSize(x, y);
949 }
950 }
951 }
952 }
953 if (nodeType == "tools") {
954 TiXmlElement* toolNode = subNode->FirstChild()->ToElement();
955
956 for (; toolNode; toolNode = toolNode->NextSiblingElement()) {
957 wxString nodeType(toolNode->Value(), wxConvUTF8);
958
959 if (nodeType == "horizontal" || nodeType == "vertical") {
960 int orientation = 0;
961 if (nodeType == "vertical") orientation = 1;
962
963 TiXmlElement* attrNode = toolNode->FirstChild()->ToElement();
964 for (; attrNode; attrNode = attrNode->NextSiblingElement()) {
965 wxString nodeType(attrNode->Value(), wxConvUTF8);
966 if (nodeType == "separation") {
967 attrNode->QueryIntAttribute(
968 "distance", &style->toolSeparation[orientation]);
969 continue;
970 }
971 if (nodeType == "margin") {
972 attrNode->QueryIntAttribute(
973 "top", &style->toolMarginTop[orientation]);
974 attrNode->QueryIntAttribute(
975 "right", &style->toolMarginRight[orientation]);
976 attrNode->QueryIntAttribute(
977 "bottom", &style->toolMarginBottom[orientation]);
978 attrNode->QueryIntAttribute(
979 "left", &style->toolMarginLeft[orientation]);
980 wxString invis =
981 wxString(attrNode->Attribute("invisible"), wxConvUTF8);
982 style->marginsInvisible = (invis.Lower() == "true");
983 continue;
984 ;
985 }
986 if (nodeType == "toggled-location") {
987 int x, y;
988 attrNode->QueryIntAttribute("x", &x);
989 attrNode->QueryIntAttribute("y", &y);
990 style->toggledBGlocation[orientation] = wxPoint(x, y);
991 x = 0;
992 y = 0;
993 attrNode->QueryIntAttribute("width", &x);
994 attrNode->QueryIntAttribute("height", &y);
995 style->toggledBGSize[orientation] = wxSize(x, y);
996 continue;
997 }
998 if (nodeType == "toolbar-start") {
999 int x, y;
1000 attrNode->QueryIntAttribute("x", &x);
1001 attrNode->QueryIntAttribute("y", &y);
1002 style->toolbarStartLoc[orientation] = wxPoint(x, y);
1003 x = 0;
1004 y = 0;
1005 attrNode->QueryIntAttribute("width", &x);
1006 attrNode->QueryIntAttribute("height", &y);
1007 style->toolbarStartSize[orientation] = wxSize(x, y);
1008 continue;
1009 }
1010 if (nodeType == "toolbar-end") {
1011 int x, y;
1012 attrNode->QueryIntAttribute("x", &x);
1013 attrNode->QueryIntAttribute("y", &y);
1014 style->toolbarEndLoc[orientation] = wxPoint(x, y);
1015 x = 0;
1016 y = 0;
1017 attrNode->QueryIntAttribute("width", &x);
1018 attrNode->QueryIntAttribute("height", &y);
1019 style->toolbarEndSize[orientation] = wxSize(x, y);
1020 continue;
1021 }
1022 if (nodeType == "toolbar-corners") {
1023 int r;
1024 attrNode->QueryIntAttribute("radius", &r);
1025 style->cornerRadius[orientation] = r;
1026 continue;
1027 }
1028 if (nodeType == "background-location") {
1029 int x, y;
1030 attrNode->QueryIntAttribute("x", &x);
1031 attrNode->QueryIntAttribute("y", &y);
1032 style->normalBGlocation[orientation] = wxPoint(x, y);
1033 style->HasBackground(true);
1034 continue;
1035 }
1036 if (nodeType == "active-location") {
1037 int x, y;
1038 attrNode->QueryIntAttribute("x", &x);
1039 attrNode->QueryIntAttribute("y", &y);
1040 style->activeBGlocation[orientation] = wxPoint(x, y);
1041 continue;
1042 }
1043 if (nodeType == "size") {
1044 int x, y;
1045 attrNode->QueryIntAttribute("x", &x);
1046 attrNode->QueryIntAttribute("y", &y);
1047 style->toolSize[orientation] = wxSize(x, y);
1048 continue;
1049 }
1050 if (nodeType == "icon-offset") {
1051 int x, y;
1052 attrNode->QueryIntAttribute("x", &x);
1053 attrNode->QueryIntAttribute("y", &y);
1054 style->verticalIconOffset = wxSize(x, y);
1055 continue;
1056 }
1057 }
1058 continue;
1059 }
1060 if (nodeType == "compass") {
1061 TiXmlElement* attrNode = toolNode->FirstChild()->ToElement();
1062 for (; attrNode; attrNode = attrNode->NextSiblingElement()) {
1063 wxString nodeType(attrNode->Value(), wxConvUTF8);
1064 if (nodeType == "margin") {
1065 attrNode->QueryIntAttribute("top",
1066 &style->compassMarginTop);
1067 attrNode->QueryIntAttribute("right",
1068 &style->compassMarginRight);
1069 attrNode->QueryIntAttribute("bottom",
1070 &style->compassMarginBottom);
1071 attrNode->QueryIntAttribute("left",
1072 &style->compassMarginLeft);
1073 continue;
1074 }
1075 if (nodeType == "compass-corners") {
1076 int r;
1077 attrNode->QueryIntAttribute("radius", &r);
1078 style->compasscornerRadius = r;
1079 continue;
1080 }
1081 if (nodeType == "offset") {
1082 attrNode->QueryIntAttribute("x", &style->compassXoffset);
1083 attrNode->QueryIntAttribute("y", &style->compassYoffset);
1084 continue;
1085 }
1086 }
1087 }
1088
1089 if (nodeType == "tool") {
1090 Tool* tool = new Tool();
1091 style->tools.Add(tool);
1092 tool->name = wxString(toolNode->Attribute("name"), wxConvUTF8);
1093 style->toolIndex[tool->name] = style->tools.Count() - 1;
1094 TiXmlHandle toolHandle(toolNode);
1095 TiXmlElement* toolTag =
1096 toolHandle.Child("icon-location", 0).ToElement();
1097 if (toolTag) {
1098 int x, y;
1099 toolTag->QueryIntAttribute("x", &x);
1100 toolTag->QueryIntAttribute("y", &y);
1101 tool->iconLoc = wxPoint(x, y);
1102 }
1103 toolTag = toolHandle.Child("rollover-location", 0).ToElement();
1104 if (toolTag) {
1105 int x, y;
1106 toolTag->QueryIntAttribute("x", &x);
1107 toolTag->QueryIntAttribute("y", &y);
1108 tool->rolloverLoc = wxPoint(x, y);
1109 }
1110 toolTag = toolHandle.Child("disabled-location", 0).ToElement();
1111 if (toolTag) {
1112 int x, y;
1113 toolTag->QueryIntAttribute("x", &x);
1114 toolTag->QueryIntAttribute("y", &y);
1115 tool->disabledLoc = wxPoint(x, y);
1116 }
1117 toolTag = toolHandle.Child("size", 0).ToElement();
1118 if (toolTag) {
1119 int x, y;
1120 toolTag->QueryIntAttribute("x", &x);
1121 toolTag->QueryIntAttribute("y", &y);
1122 tool->customSize = wxSize(x, y);
1123 }
1124 continue;
1125 }
1126 }
1127 continue;
1128 }
1129 }
1130 }
1131 }
1132 }
1133}
1134
1135void StyleManager::SetStyle(wxString name) {
1136 Style* style = NULL;
1137 bool ok = true;
1138 if (currentStyle)
1139 currentStyle->Unload();
1140 else
1141 ok = false;
1142
1143 bool selectFirst = false;
1144
1145 // Verify the named style exists
1146 // If not, just use the "first" style
1147 bool bstyleFound = false;
1148
1149 for (unsigned int i = 0; i < styles.Count(); i++) {
1150 style = (Style*)(styles.Item(i));
1151 if (style->name == name) {
1152 bstyleFound = true;
1153 break;
1154 }
1155 }
1156
1157 if ((name.Length() == 0) || !bstyleFound) selectFirst = true;
1158
1159 for (unsigned int i = 0; i < styles.Count(); i++) {
1160 style = (Style*)(styles[i]);
1161 if (style->name == name || selectFirst) {
1162 if (style->graphics) {
1163 currentStyle = style;
1164 ok = true;
1165 break;
1166 }
1167
1168 wxString fullFilePath = style->myConfigFileDir +
1169 wxFileName::GetPathSeparator() +
1170 style->graphicsFile;
1171
1172 if (!wxFileName::FileExists(fullFilePath)) {
1173 wxString msg("Styles Graphics File not found: ");
1174 msg += fullFilePath;
1175 wxLogMessage(msg);
1176 ok = false;
1177 if (selectFirst) continue;
1178 break;
1179 }
1180
1181 wxImage img; // Only image does PNG LoadFile properly on GTK.
1182
1183 if (!img.LoadFile(fullFilePath, wxBITMAP_TYPE_PNG)) {
1184 wxString msg("Styles Graphics File failed to load: ");
1185 msg += fullFilePath;
1186 wxLogMessage(msg);
1187 ok = false;
1188 break;
1189 }
1190 style->graphics = new wxBitmap(img);
1191 currentStyle = style;
1192 ok = true;
1193 break;
1194 }
1195 }
1196
1197 if (!ok || !currentStyle->graphics) {
1198 wxString msg("The requested style was not found: ");
1199 msg += name;
1200 wxLogMessage(msg);
1201 return;
1202 }
1203
1204 if (currentStyle) {
1205 if ((currentStyle->consoleTextBackgroundSize.x) &&
1206 (currentStyle->consoleTextBackgroundSize.y)) {
1207 currentStyle->consoleTextBackground =
1208 currentStyle->graphics->GetSubBitmap(
1209 wxRect(currentStyle->consoleTextBackgroundLoc,
1210 currentStyle->consoleTextBackgroundSize));
1211 }
1212 }
1213
1214 if (currentStyle) nextInvocationStyle = currentStyle->name;
1215
1216 return;
1217}
1218
1219Style* 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.