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