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