OpenCPN Partial API docs
styles.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: Chart Symbols
5  * Author: Jesper Weissglas
6  *
7  ***************************************************************************
8  * Copyright (C) 2010 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  **************************************************************************/
25 #include "config.h"
26 
27 #include <wx/wxprec.h>
28 
29 #ifndef WX_PRECOMP
30 #include <wx/wx.h>
31 #endif
32 
33 #include <wx/filename.h>
34 #include <wx/dir.h>
35 #include <stdlib.h>
36 #include "OCPNPlatform.h"
37 
38 #include "styles.h"
39 #include "model/wx28compat.h"
40 #include "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 
48 extern OCPNPlatform* g_Platform;
49 
50 using namespace ocpnStyle;
51 
52 void 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 
61 wxBitmap 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 
170 wxBitmap 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 
209 bool 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 
219 wxBitmap 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 
238 wxBitmap 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 
283 wxBitmap 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 
503 wxBitmap 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 
564 wxBitmap 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 
583 wxBitmap 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 
604 wxBitmap 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 
611 wxBitmap Style::GetActiveBG() {
612  return graphics->GetSubBitmap(wxRect(activeBGlocation[currentOrientation].x,
613  activeBGlocation[currentOrientation].y,
614  toolSize[currentOrientation].x,
615  toolSize[currentOrientation].y));
616 }
617 
618 wxBitmap 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 
627 wxBitmap 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 
636 wxBitmap 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 
645 int Style::GetToolbarCornerRadius() { return cornerRadius[currentOrientation]; }
646 
647 void 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 
663 void 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 
684 void 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 
692 int Style::GetOrientation() { return currentOrientation; }
693 
694 void 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 
709 void 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 
721 Style::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 
748 Style::~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 
765 StyleManager::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 
781 StyleManager::StyleManager(const wxString& configDir) {
782  isOK = false;
783  currentStyle = NULL;
784  Init(configDir);
785  SetStyle(_T(""));
786 }
787 
788 StyleManager::~StyleManager(void) {
789  for (unsigned int i = 0; i < styles.Count(); i++) {
790  delete (Style*)(styles[i]);
791  }
792  styles.Clear();
793 }
794 
795 void 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 
1130 void 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 
1214 Style* StyleManager::GetCurrentStyle() { return currentStyle; }
Definition: Quilt.cpp:867