PLplot  5.11.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
wxwidgets_dev.cpp
Go to the documentation of this file.
1 // Copyright (C) 2015 Phil Rosenberg
2 // Copyright (C) 2005 Werner Smekal, Sjaak Verdoold
3 // Copyright (C) 2005 Germain Carrera Corraleche
4 // Copyright (C) 1999 Frank Huebner
5 //
6 // This file is part of PLplot.
7 //
8 // PLplot is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU Library General Public License as published
10 // by the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // PLplot is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public License
19 // along with PLplot; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 
23 #define DEBUG
24 #define NEED_PLDEBUG
25 
26 //set this to help when debugging wxPLViewer issues
27 //it uses a memory map name without random characters
28 //and does not execute the viewer, allowing the user to do
29 //it in a debugger
30 //#define WXPLVIEWER_DEBUG
31 
32 //headers needed for Rand
33 #ifdef WIN32
34 //this include must occur before any other include of stdlib.h
35 //due to the #define _CRT_RAND_S
36 #define _CRT_RAND_S
37 #include <stdlib.h>
38 #else
39 #include <fstream>
40 #endif
41 
42 //plplot headers
43 #include "plDevs.h"
44 #include "wxwidgets.h" // includes wx/wx.h
45 
46 // wxwidgets headers
47 #include <wx/dir.h>
48 
49 // std and driver headers
50 #include <cmath>
51 #include <limits>
52 
53 
54 //--------------------------------------------------------------------------
55 // Scaler class
56 // This class changes the logical scale of a dc on construction and resets
57 // it to its original value on destruction. It is ideal for making temporary
58 // changes to the scale and guarenteeing that the scale gets set back.
59 //--------------------------------------------------------------------------
60 class Scaler
61 {
62 public:
63  Scaler( wxDC * dc, double xScale, double yScale )
64  {
65  m_dc = dc;
66  if ( m_dc )
67  {
68  dc->GetLogicalScale( &m_xScaleOld, &m_yScaleOld );
69  dc->SetLogicalScale( xScale, yScale );
70  }
71  }
73  {
74  if ( m_dc )
75  m_dc->SetLogicalScale( m_xScaleOld, m_yScaleOld );
76  }
77 private:
78  wxDC *m_dc;
79  double m_xScaleOld;
80  double m_yScaleOld;
81 };
82 
83 //--------------------------------------------------------------------------
84 // OriginChanger class
85 // This class changes the logical origin of a dc on construction and resets
86 // it to its original value on destruction. It is ideal for making temporary
87 // changes to the origin and guarenteeing that the scale gets set back.
88 //--------------------------------------------------------------------------
90 {
91 public:
92  OriginChanger( wxDC * dc, wxCoord xOrigin, wxCoord yOrigin )
93  {
94  m_dc = dc;
95  if ( m_dc )
96  {
97  dc->GetLogicalOrigin( &m_xOriginOld, &m_yOriginOld );
98  dc->SetLogicalOrigin( xOrigin, yOrigin );
99  }
100  }
102  {
103  if ( m_dc )
104  m_dc->SetLogicalOrigin( m_xOriginOld, m_yOriginOld );
105  }
106 private:
107  wxDC *m_dc;
108  wxCoord m_xOriginOld;
109  wxCoord m_yOriginOld;
110 };
111 
112 //--------------------------------------------------------------------------
113 // DrawingObjectsChanger class
114 // This class changes the pen and brush of a dc on construction and resets
115 // them to their original values on destruction. It is ideal for making temporary
116 // changes to the pen and brush and guarenteeing that they get set back.
117 //--------------------------------------------------------------------------
119 {
120 public:
121  DrawingObjectsChanger( wxDC *dc, const wxPen &pen, const wxBrush &brush )
122  {
123  m_dc = dc;
124  if ( m_dc )
125  {
126  m_pen = dc->GetPen();
127  m_brush = dc->GetBrush();
128  dc->SetPen( pen );
129  dc->SetBrush( brush );
130  }
131  }
133  {
134  if ( m_dc )
135  {
136  m_dc->SetPen( m_pen );
137  m_dc->SetBrush( m_brush );
138  }
139  }
140 private:
141  wxDC *m_dc;
142  wxPen m_pen;
143  wxBrush m_brush;
144 };
145 
146 //--------------------------------------------------------------------------
147 // TextObjectsChanger class
148 // This class changes the font and text colours of a dc on construction and resets
149 // them to their original values on destruction. It is ideal for making temporary
150 // changes to the text and guarenteeing that they get set back.
151 //--------------------------------------------------------------------------
153 {
154 public:
155  TextObjectsChanger( wxDC *dc, const wxFont &font, const wxColour &textForeground, const wxColour &textBackground )
156  {
157  m_dc = dc;
158  if ( m_dc )
159  {
160  m_font = dc->GetFont();
161  m_textForeground = dc->GetTextForeground();
162  m_textBackground = dc->GetTextBackground();
163  dc->SetTextForeground( textForeground );
164  dc->SetTextBackground( textBackground );
165  dc->SetFont( font );
166  }
167  }
168  TextObjectsChanger( wxDC *dc, const wxFont &font )
169  {
170  m_dc = dc;
171  if ( m_dc )
172  {
173  m_font = dc->GetFont();
174  m_textForeground = dc->GetTextForeground();
175  m_textBackground = dc->GetTextBackground();
176  dc->SetFont( font );
177  }
178  }
180  {
181  if ( m_dc )
182  {
183  m_dc->SetTextForeground( m_textForeground );
184  m_dc->SetTextBackground( m_textBackground );
185  m_dc->SetFont( m_font );
186  }
187  }
188 private:
189  wxDC *m_dc;
190  wxFont m_font;
193 };
194 
195 //--------------------------------------------------------------------------
196 // Clipper class
197 // This class changes the clipping region of a dc on construction and restores
198 // it to its previous region on destruction. It is ideal for making temporary
199 // changes to the clip region and guarenteeing that the scale gets set back.
200 //
201 // It turns out that clipping is mostly broken for wxGCDC - see
202 // http://trac.wxwidgets.org/ticket/17013. So there are a lot of things in
203 // this class to work aound those bugs. In particular you should check
204 // isEveryThingClipped before drawing as I'm not sure if non-overlapping
205 //clip regions behave properly.
206 //--------------------------------------------------------------------------
207 class Clipper
208 {
209 public:
210  Clipper( wxDC * dc, const wxRect &rect )
211  {
212  m_dc = dc;
213  m_clipEverything = true;
214  if ( m_dc )
215  {
216  dc->GetClippingBox( m_boxOld );
217  wxRect newRect = rect;
218  m_clipEverything = !( newRect.Intersects( m_boxOld )
219  || ( m_boxOld.width == 0 && m_boxOld.height == 0 ) );
220  if ( m_clipEverything )
221  dc->SetClippingRegion( wxRect( -1, -1, 1, 1 ) ); //not sure if this works
222  else
223  dc->SetClippingRegion( rect );
224  }
225  }
227  {
228  if ( m_dc )
229  {
230  m_dc->DestroyClippingRegion();
231  m_dc->SetClippingRegion( wxRect( 0, 0, 0, 0 ) );
232  m_dc->DestroyClippingRegion();
233  if ( m_boxOld.width != 0 && m_boxOld.height != 0 )
234  m_dc->SetClippingRegion( m_boxOld );
235  }
236  }
238  {
239  return m_clipEverything;
240  }
241 private:
242  wxDC *m_dc;
243  wxRect m_boxOld;
245 };
246 
247 //--------------------------------------------------------------------------
248 // class Rand
249 // This is a simple random number generator class, created soley so that
250 // random numbers can be generated in this file without "contaminating" the
251 // global series of random numbers with a new seed.
252 // It uses an algorithm that apparently used to be used in gcc rand()
253 // provided under GNU LGPL v2.1.
254 //--------------------------------------------------------------------------
255 class Rand
256 {
257 public:
259  {
260 #ifdef WIN32
261  rand_s( &m_seed );
262 #else
263  std::fstream fin( "/dev/random", std::ios::in );
264  fin.read( (char *) ( &m_seed ), sizeof ( m_seed ) );
265  fin.close();
266 #endif
267  }
268  Rand( unsigned int seed )
269  {
270  m_seed = seed;
271  }
272  unsigned int operator()()
273  {
274  unsigned int next = m_seed;
275  int result;
276 
277  next *= 1103515245;
278  next += 12345;
279  result = (unsigned int) ( next / max ) % 2048;
280 
281  next *= 1103515245;
282  next += 12345;
283  result <<= 10;
284  result ^= (unsigned int) ( next / max ) % 1024;
285 
286  next *= 1103515245;
287  next += 12345;
288  result <<= 10;
289  result ^= (unsigned int) ( next / max ) % 1024;
290 
291  m_seed = next;
292 
293  return result;
294  }
295  static const unsigned int max = 65536;
296 private:
297  unsigned int m_seed;
298 };
299 
300 void plFontToWxFontParameters( PLUNICODE fci, PLFLT scaledFontSize, wxFontFamily &family, int &style, int &weight, int &pt )
301 {
302  unsigned char plFontFamily, plFontStyle, plFontWeight;
303 
304  plP_fci2hex( fci, &plFontFamily, PL_FCI_FAMILY );
305  plP_fci2hex( fci, &plFontStyle, PL_FCI_STYLE );
306  plP_fci2hex( fci, &plFontWeight, PL_FCI_WEIGHT );
307 
308  family = fontFamilyLookup[plFontFamily];
309  style = fontStyleLookup[plFontStyle];
310  weight = fontWeightLookup[plFontWeight];
311  pt = ROUND( scaledFontSize );
312 }
313 
314 //--------------------------------------------------------------------------
315 // FontGrabber::FontGrabber( )
316 //
317 // Default constructor
318 //--------------------------------------------------------------------------
320 {
321  //set m_prevScaledFontSize to a nan to ensure testing the prev values
322  //always gives false
323  m_prevFci = 0;
324  m_prevScaledFontSize = std::numeric_limits<PLFLT>::quiet_NaN();
325  m_prevUnderlined = false;
326  m_lastWasCached = false;
327 }
328 
329 //--------------------------------------------------------------------------
330 // wxFont FontGrabber::GetFont( PLUNICODE fci )
331 //
332 // Get the requested font either fresh of from the cache.
333 //--------------------------------------------------------------------------
334 wxFont FontGrabber::GetFont( PLUNICODE fci, PLFLT scaledFontSize, bool underlined )
335 {
336  if ( m_prevFci == fci && m_prevScaledFontSize == scaledFontSize
337  && m_prevUnderlined == underlined )
338  {
339  m_lastWasCached = true;
340  return m_prevFont;
341  }
342 
343  m_prevFci = fci;
344  m_prevScaledFontSize = scaledFontSize;
345  m_prevUnderlined = underlined;
346  m_lastWasCached = false;
347 
348  wxFontFamily family;
349  int style;
350  int weight;
351  int pt;
352  plFontToWxFontParameters( fci, scaledFontSize, family, style, weight, pt );
353 
354  return m_prevFont = wxFont( pt, family, style, weight, underlined, wxEmptyString, wxFONTENCODING_DEFAULT );
355 }
356 
357 //--------------------------------------------------------------------------
358 // wxPLDevice::wxPLDevice( void )
359 //
360 // Constructor of the standard wxWidgets device based on the wxPLDevBase
361 // class. Only some initialisations are done.
362 //--------------------------------------------------------------------------
363 wxPLDevice::wxPLDevice( PLStream *pls, char * mfo, PLINT text, PLINT hrshsym )
364  : m_plplotEdgeLength( PLFLT( SHRT_MAX ) )
365 {
366  m_fixedAspect = false;
367 
368  m_lineSpacing = 1.0;
369 
370  m_dc = NULL;
371 
375 
376  if ( mfo )
377  strcpy( m_mfo, mfo );
378  else
379  //assume we will be outputting to the default
380  //memory map until we are given a dc to draw to
381 #ifdef WXPLVIEWER_DEBUG
382  strcpy( m_mfo, "plplotMemoryMap" );
383 #else
384  strcpy( m_mfo, "plplotMemoryMap??????????" );
385 #endif
386 
387  // be verbose and write out debug messages
388 #ifdef _DEBUG
389  pls->verbose = 1;
390  pls->debug = 1;
391 #endif
392 
393  pls->color = 1; // Is a color device
394  pls->dev_flush = 1; // Handles flushes
395  pls->dev_fill0 = 1; // Can handle solid fills
396  pls->dev_fill1 = 0; // Can't handle pattern fills
397  pls->dev_dash = 0;
398  pls->dev_clear = 1; // driver supports clear
399  pls->plbuf_write = 1; // use the plot buffer!
400  pls->termin = ( strlen( m_mfo ) ) > 0 ? 0 : 1; // interactive device unless we are writing to memory - pretty sure this is an unused option though
401  pls->graphx = GRAPHICS_MODE; // This indicates this is a graphics driver. PLPlot with therefore cal pltext() before outputting text, however we currently have not implemented catching that text.
402 
403  if ( text )
404  {
405  pls->dev_text = 1; // want to draw text
406  pls->dev_unicode = 1; // want unicode
407  if ( hrshsym )
408  pls->dev_hrshsym = 1;
409  }
410 
411 
412  // Set up physical limits of plotting device in plplot internal units
413  // we just tell plplot we are the maximum plint in both dimensions
414  //which gives us the best resolution
415  plP_setphy( (PLINT) 0, (PLINT) SHRT_MAX,
416  (PLINT) 0, (PLINT) SHRT_MAX );
417 
418  // set dpi and page size defaults
419  // the user might have already set this with plspage
420  // so check first
421  if ( !plsc->pageset )
422  c_plspage( 90, 90, 900, 675, 0, 0 );
423 
424  //check dpi is non-zero otherwise we get infinities in some calcualtions
425  //and ridiculous numbers in others
426  if ( pls->xdpi == 0.0 || pls->ydpi == 0 )
427  {
428  if ( pls->xdpi == 0.0 && pls->ydpi == 0 )
429  c_plspage( 90, 90, 0, 0, 0, 0 );
430  else
431  {
432  PLFLT dpi = MAX( pls->xdpi, pls->ydpi );
433  pls->xdpi = dpi;
434  pls->ydpi = dpi;
435  }
436  }
437 
439 
440  SetSize( pls, plsc->xlength, plsc->ylength );
441 
442  if ( pls->dev_data )
443  SetDC( pls, (wxDC *) pls->dev_data );
444  else
445  SetupMemoryMap();
446 
447  //this must be the absolute last thing that is done
448  //so that if an exception is thrown pls->dev remains as NULL
449  pls->dev = (void *) this;
450 }
451 
452 
453 //--------------------------------------------------------------------------
454 // wxPLDevice::~wxPLDevice( void )
455 //
456 // The deconstructor frees memory allocated by the device.
457 //--------------------------------------------------------------------------
459 {
460  if ( m_outputMemoryMap.isValid() )
461  {
463  header->completeFlag = 1;
464  }
465 }
466 
467 //--------------------------------------------------------------------------
468 // void wxPLDevice::PreDestructorTidy( PLStream *pls )
469 //
470 // This function performs any tidying up that requires a PLStream. should
471 // be called before the destructor obviously
472 //--------------------------------------------------------------------------
474 {
475  if ( !m_dc && pls->nopause )
477 }
478 
479 //--------------------------------------------------------------------------
480 // void wxPLDevice::DrawLine( short x1a, short y1a, short x2a, short y2a )
481 //
482 // Draw a line from (x1a, y1a) to (x2a, y2a).
483 //--------------------------------------------------------------------------
484 void wxPLDevice::DrawLine( short x1a, short y1a, short x2a, short y2a )
485 {
486  if ( !m_dc )
487  return;
488 
489  Clipper clipper( m_dc, GetClipRegion().GetBox() );
490  Scaler scaler( m_dc, 1.0 / m_scale, 1.0 / m_scale );
491  DrawingObjectsChanger drawingObjectsChanger( m_dc, m_pen, m_brush );
492  m_dc->DrawLine( (wxCoord) ( m_xAspect * x1a ), (wxCoord) ( m_yAspect * ( m_plplotEdgeLength - y1a ) ),
493  (wxCoord) ( m_xAspect * x2a ), (wxCoord) ( m_yAspect * ( m_plplotEdgeLength - y2a ) ) );
494 }
495 
496 
497 //--------------------------------------------------------------------------
498 // void wxPLDevice::DrawPolyline( short *xa, short *ya, PLINT npts )
499 //
500 // Draw a poly line - coordinates are in the xa and ya arrays.
501 //--------------------------------------------------------------------------
502 void wxPLDevice::DrawPolyline( short *xa, short *ya, PLINT npts )
503 {
504  if ( !m_dc )
505  return;
506  Clipper clipper( m_dc, GetClipRegion().GetBox() );
507  Scaler scaler( m_dc, 1.0 / m_scale, 1.0 / m_scale );
508  DrawingObjectsChanger drawingObjectsChanger( m_dc, m_pen, m_brush );
509  for ( PLINT i = 1; i < npts; i++ )
510  m_dc->DrawLine( m_xAspect * xa[i - 1], m_yAspect * ( m_plplotEdgeLength - ya[i - 1] ),
511  m_xAspect * xa[i], m_yAspect * ( m_plplotEdgeLength - ya[i] ) );
512 }
513 
514 
515 //--------------------------------------------------------------------------
516 // void wxPLDevice::ClearBackground( PLStream* pls, PLINT bgr, PLINT bgg, PLINT bgb,
517 // PLINT x1, PLINT y1, PLINT x2, PLINT y2 )
518 //
519 // Clear parts ((x1,y1) to (x2,y2)) of the background in color (bgr,bgg,bgb).
520 //--------------------------------------------------------------------------
522 {
523  if ( !m_dc )
524  return;
525 
526  x1 = x1 < 0 ? 0 : x1;
527  x2 = x2 < 0 ? m_plplotEdgeLength : x2;
528  y1 = y1 < 0 ? 0 : y1;
529  y2 = y2 < 0 ? m_plplotEdgeLength : y2;
530 
531  PLINT x = MIN( x1, x2 );
532  PLINT y = MIN( y1, y2 );
533  PLINT width = abs( x1 - x2 );
534  PLINT height = abs( y1 - y2 );
535 
536  if ( width > 0 && height > 0 )
537  {
538  PLINT r, g, b;
539  PLFLT a;
540  plgcolbga( &r, &g, &b, &a );
541  wxColour bgColour( r, g, b, a * 255 );
542  DrawingObjectsChanger changer( m_dc, wxPen( bgColour, 0 ), wxBrush( bgColour ) );
543  m_dc->DrawRectangle( x, y, width, height );
544  }
545 }
546 
547 
548 //--------------------------------------------------------------------------
549 // void wxPLDevice::FillPolygon( PLStream *pls )
550 //
551 // Draw a filled polygon.
552 //--------------------------------------------------------------------------
554 {
555  if ( !m_dc )
556  return;
557 
558  //edge the polygon with a 0.5 pixel line to avoid seams. This is a
559  //bit of a bodge really but this is a difficult problem
560  wxPen edgePen( m_brush.GetColour(), m_scale, wxSOLID );
561  DrawingObjectsChanger changer( m_dc, edgePen, m_brush );
562  //DrawingObjectsChanger changer(m_dc, wxNullPen, m_brush );
563  Scaler scaler( m_dc, 1.0 / m_scale, 1.0 / m_scale );
564  wxPoint *points = new wxPoint[pls->dev_npts];
565  wxCoord xoffset = 0;
566  wxCoord yoffset = 0;
567 
568  for ( int i = 0; i < pls->dev_npts; i++ )
569  {
570  points[i].x = (int) ( m_xAspect * pls->dev_x[i] );
571  points[i].y = (int) ( m_yAspect * ( m_plplotEdgeLength - pls->dev_y[i] ) );
572  }
573 
574  if ( pls->dev_eofill )
575  {
576  m_dc->DrawPolygon( pls->dev_npts, points, xoffset, yoffset, wxODDEVEN_RULE );
577  }
578  else
579  {
580  m_dc->DrawPolygon( pls->dev_npts, points, xoffset, yoffset, wxWINDING_RULE );
581  }
582  delete[] points;
583 }
584 
585 
586 //--------------------------------------------------------------------------
587 // void wxPLDevice::SetWidth( PLStream *pls )
588 //
589 // Set the width of the drawing pen.
590 //--------------------------------------------------------------------------
592 {
593  PLFLT width = ( pls->width > 0.0 ? pls->width : 1.0 ) * m_scale;
594  m_pen = wxPen( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b,
595  pls->curcolor.a * 255 ), width, wxSOLID );
596 }
597 
598 
599 //--------------------------------------------------------------------------
600 // void wxPLDevice::SetColor( PLStream *pls )
601 //
602 // Set color from PLStream.
603 //--------------------------------------------------------------------------
605 {
606  PLFLT width = ( pls->width > 0.0 ? pls->width : 1.0 ) * m_scale;
607  m_pen = wxPen( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b,
608  pls->curcolor.a * 255 ), width, wxSOLID );
609  m_brush = wxBrush( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b,
610  pls->curcolor.a * 255 ) );
611 }
612 
613 
614 //--------------------------------------------------------------------------
615 // void wxPLDevice::SetDC( PLStream *pls, void* dc )
616 //
617 // Adds a dc to the device. In that case, the drivers doesn't provide
618 // a GUI.
619 //--------------------------------------------------------------------------
620 void wxPLDevice::SetDC( PLStream *pls, wxDC* dc )
621 {
622  if ( m_outputMemoryMap.isValid() )
623  throw( "wxPLDevice::SetDC The DC must be set before initialisation. The device is outputting to a separate viewer" );
624  m_dc = dc; // Add the dc to the device
625  m_useDcTextTransform = false;
626  m_gc = NULL;
627  if ( m_dc )
628  {
629 #if wxVERSION_NUMBER >= 2902
630  m_useDcTextTransform = m_dc->CanUseTransformMatrix();
631 #endif
632  //If we don't have wxDC tranforms we can use the
633  //underlying wxGCDC if this is a wxGCDC
634  if ( !m_useDcTextTransform )
635  {
636  //check to see if m_dc is a wxGCDC by using RTTI
637  wxGCDC *gcdc = NULL;
638  try
639  {
640  //put this in a try block as I'm not sure if it will throw if
641  //RTTI is switched off
642  gcdc = dynamic_cast< wxGCDC* >( m_dc );
643  }
644  catch ( ... )
645  {
646  }
647  if ( gcdc )
648  m_gc = gcdc->GetGraphicsContext();
649  }
650 
651  strcpy( m_mfo, "" );
652  SetSize( pls, m_width, m_height ); //call with our current size to set the scaling
653  pls->has_string_length = 1; // Driver supports string length calculations, if we have a dc to draw on
654  }
655  else
656  {
657  pls->has_string_length = 0; //if we have no device to draw on we cannot check string size
658  }
659 }
660 
661 PLFLT getTextOffset( PLINT superscriptLevel, PLFLT baseFontSize )
662 {
663  if ( superscriptLevel == 0 )
664  return 0;
665  else
666  {
667  PLFLT fontScale = pow( 0.8, abs( superscriptLevel ) );
668  if ( superscriptLevel > 0 )
669  return getTextOffset( superscriptLevel - 1, baseFontSize ) + baseFontSize * fontScale / 2.;
670  else
671  return getTextOffset( superscriptLevel + 1, baseFontSize ) - baseFontSize * fontScale * 0.8 / 2.;
672  }
673 }
674 
675 //--------------------------------------------------------------------------
676 // void wxPLDevice::DrawText( PLUNICODE* ucs4, int ucs4Len, bool drawText )
677 //
678 // This is the function to draw text. Pass in a unicde string and its
679 // length and set drawText to true to actually draw the text or false to
680 // just get text metrics. This function will process the string for inline
681 // style change escapes and newlines.
682 //--------------------------------------------------------------------------
683 void wxPLDevice::DrawTextLine( PLUNICODE* ucs4, int ucs4Len, PLFLT baseFontSize, bool drawText, PLINT &superscriptLevel, bool &underlined )
684 {
685  if ( !m_dc && drawText )
686  return;
687 
688  char utf8_string[m_max_string_length];
689  char utf8[5];
690  memset( utf8_string, '\0', m_max_string_length );
691 
692  // Get PLplot escape character
693  char plplotEsc;
694  plgesc( &plplotEsc );
695 
696  //Reset the size metrics
697  m_textWidth = 0;
698  m_textHeight = 0;
700  m_subscriptDepth = 0;
701 
702  int i = 0;
703  while ( i < ucs4Len )
704  {
705  if ( ucs4[i] < PL_FCI_MARK ) // not a font change
706  {
707  if ( ucs4[i] != (PLUNICODE) plplotEsc ) // a character to display
708  {
709  ucs4_to_utf8( ucs4[i], utf8 );
710  strncat( utf8_string, utf8,
711  sizeof ( utf8_string ) - strlen( utf8_string ) - 1 );
712  i++;
713  continue;
714  }
715  i++;
716  if ( ucs4[i] == (PLUNICODE) plplotEsc ) // a escape character to display
717  {
718  ucs4_to_utf8( ucs4[i], utf8 );
719  strncat( utf8_string, utf8,
720  sizeof ( utf8_string ) - strlen( utf8_string ) - 1 );
721  i++;
722  continue;
723  }
724  else
725  {
726  if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript
727  { // draw string so far
728  PLFLT fontScale = pow( 0.8, abs( superscriptLevel ) );
729  PLFLT yOffset = getTextOffset( superscriptLevel, baseFontSize ) * m_yScale;
730  DrawTextSection( utf8_string, baseFontSize * fontScale, yOffset, underlined, drawText );
731 
732  ++superscriptLevel;
733  fontScale = pow( 0.8, abs( superscriptLevel ) );
734  if ( m_dc )
735  m_dc->SetFont( m_fontGrabber.GetFont( m_fci, baseFontSize * fontScale, underlined ) );
736  }
737  if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript
738  { // draw string so far
739  PLFLT fontScale = pow( 0.8, abs( superscriptLevel ) );
740  PLFLT yOffset = getTextOffset( superscriptLevel, baseFontSize ) * m_yScale;
741  DrawTextSection( utf8_string, baseFontSize * fontScale, yOffset, underlined, drawText );
742 
743  --superscriptLevel;
744  fontScale = pow( 0.8, abs( superscriptLevel ) );
745  if ( m_dc )
746  m_dc->SetFont( m_fontGrabber.GetFont( m_fci, baseFontSize * fontScale, underlined ) );
747  }
748  if ( ucs4[i] == (PLUNICODE) '-' ) // underline
749  { // draw string so far
750  PLFLT fontScale = pow( 0.8, abs( superscriptLevel ) );
751  PLFLT yOffset = getTextOffset( superscriptLevel, baseFontSize ) * m_yScale;
752  DrawTextSection( utf8_string, baseFontSize * fontScale, yOffset, underlined, drawText );
753 
754  underlined = !underlined;
755  if ( m_dc )
756  m_dc->SetFont( m_fontGrabber.GetFont( m_fci, baseFontSize * fontScale, underlined ) );
757  }
758  if ( ucs4[i] == (PLUNICODE) '+' ) // overline
759  { // not implemented yet
760  }
761  i++;
762  }
763  }
764  else // a font change
765  {
766  // draw string so far
767  PLFLT fontScale = pow( 0.8, abs( superscriptLevel ) );
768  PLFLT yOffset = getTextOffset( superscriptLevel, baseFontSize ) * m_yScale;
769  DrawTextSection( utf8_string, baseFontSize * fontScale, yOffset, underlined, drawText );
770 
771  // get new font
772  m_fci = ucs4[i];
773  if ( m_dc )
774  m_dc->SetFont( m_fontGrabber.GetFont( m_fci, baseFontSize * fontScale, underlined ) );
775  i++;
776  }
777  }
778 
779  //we have reached the end of the string. Draw the remainder.
780  PLFLT fontScale = pow( 0.8, abs( superscriptLevel ) );
781  PLFLT yOffset = getTextOffset( superscriptLevel, baseFontSize ) * m_yScale;
782  DrawTextSection( utf8_string, baseFontSize * fontScale, yOffset, underlined, drawText );
783 }
784 
785 
786 //--------------------------------------------------------------------------
787 // void wxPLDevice::DrawTextSection( char* utf8_string, bool drawText )
788 //
789 // Draw a section of text. This should already have been processed and
790 // split into sections so that the text passed in has no style changes or
791 // newines.
792 //--------------------------------------------------------------------------
793 void wxPLDevice::DrawTextSection( char* utf8_string, PLFLT scaledFontSize, PLFLT yOffset, bool underlined, bool drawText )
794 {
795  if ( !m_dc && drawText )
796  return;
797 
798  wxCoord w, h, d, l;
799 
800  wxString str = wxString::FromUTF8( utf8_string );
801  //wxString str( wxConvUTF8.cMB2WC( utf8_string ), *wxConvCurrent );
802 
803  if ( m_dc )
804  m_dc->GetTextExtent( str, &w, &h, &d, &l );
805 
806  if ( !m_dc && m_outputMemoryMap.isValid() )
807  {
809  header->textSizeInfo.written = false;
810  plFontToWxFontParameters( m_fci, scaledFontSize, header->textSizeInfo.family, header->textSizeInfo.style,
811  header->textSizeInfo.weight, header->textSizeInfo.pt );
812  header->textSizeInfo.underlined = underlined;
813  bool gotResponse = false;
815  size_t counter = 0;
816  while ( !gotResponse && counter < 1000 )
817  {
818  gotResponse = header->textSizeInfo.written;
819  ++counter;
820  wxMilliSleep( 1 );
821  }
822  if ( counter == 1000 )
823  plwarn( "Failed to get text size from wxPLViewer - timeout" );
824 
825  w = header->textSizeInfo.width;
826  h = header->textSizeInfo.height;
827  }
828 
829  if ( drawText )
830  {
831  //if we are using wxDC transforms or the wxGC, then the transformations
832  //have already been applied
833  if ( m_gc )
834  m_gc->DrawText( str, m_textWidth, -yOffset / m_yScale );
835  else if ( m_useDcTextTransform )
836  m_dc->DrawText( str, m_textWidth, -yOffset / m_yScale );
837  else
838  {
839  //If we are stuck with a wxDC that has no transformation abilities then
840  // all we can really do is rotate the text - this is a bit of a poor state
841  // really, but to be honest it is better than defaulting to hershey for all
842  // text
843  if ( m_rotation == 0 )
844  m_dc->DrawRotatedText( str, (wxCoord) ( m_posX + m_textWidth ),
845  (wxCoord) ( m_height - (wxCoord) ( m_posY + yOffset / m_yScale ) ),
846  m_rotation * 180.0 / M_PI );
847  else
848  m_dc->DrawRotatedText( str,
849  (wxCoord) ( m_posX - yOffset / m_yScale * sin( m_rotation ) + m_textWidth * cos( m_rotation ) ),
850  (wxCoord) ( m_height - (wxCoord) ( m_posY + yOffset * cos( m_rotation ) / m_yScale ) - m_textWidth * sin( m_rotation ) ),
851  m_rotation * 180.0 / M_PI );
852  }
853  }
854 
855  m_textWidth += w;
856 
857  //keep track of the height of superscript text, the depth of subscript
858  //text and the height of regular text
859  if ( yOffset > 0.0001 )
860  {
861  //determine the height the text would have if it were full size
862  double currentOffset = yOffset;
863  double currentHeight = h;
864  while ( currentOffset > 0.0001 )
865  {
866  currentOffset -= m_yScale * scaledFontSize / 2.;
867  currentHeight *= 1.25;
868  }
869  m_textHeight = (wxCoord) m_textHeight > ( currentHeight )
870  ? m_textHeight
871  : currentHeight;
872  //work out the height including superscript
873  m_superscriptHeight = m_superscriptHeight > ( currentHeight + yOffset / m_yScale )
875  : static_cast<int>( ( currentHeight + yOffset / m_yScale ) );
876  }
877  else if ( yOffset < -0.0001 )
878  {
879  //determine the height the text would have if it were full size
880  double currentOffset = yOffset;
881  double currentHeight = h;
882  double currentDepth = d;
883  while ( currentOffset < -0.0001 )
884  {
885  currentOffset += m_yScale * scaledFontSize * 1.25 / 2.;
886  currentHeight *= 1.25;
887  currentDepth *= 1.25;
888  }
889  m_textHeight = (wxCoord) m_textHeight > currentHeight ? m_textHeight : currentHeight;
890  //work out the additional depth for subscript note an assumption has been made
891  //that the font size of (non-superscript and non-subscript) text is the same
892  //along a line. Currently there is no escape to change font size mid string
893  //so this should be fine
894  m_subscriptDepth = (wxCoord) m_subscriptDepth > ( ( -yOffset / m_yScale + h + d ) - ( currentDepth + m_textHeight ) )
896  : ( ( -yOffset / m_yScale + h + d ) - ( currentDepth + m_textHeight ) );
898  }
899  else
900  m_textHeight = (wxCoord) m_textHeight > ( h ) ? m_textHeight : h;
901 
902  memset( utf8_string, '\0', m_max_string_length );
903 }
904 
905 
906 //--------------------------------------------------------------------------
907 // void wxPLDevice::ProcessString( PLStream* pls, EscText* args )
908 //
909 // This is the main function which processes the unicode text strings.
910 // Font size, rotation and color are set, width and height of the
911 // text string is determined and then the string is drawn to the canvas.
912 //--------------------------------------------------------------------------
914 {
915  if ( !m_dc && !m_outputMemoryMap.isValid() )
916  return;
917 
918  m_textWidth = 0;
919  m_textHeight = 0;
920  m_textDescent = 0;
921  m_textLeading = 0;
922 
923  //for text, work in native coordinates, partly to avoid rewriting existing code
924  //but also because we should get better text hinting for screen display I think.
925  //The scaler object sets the scale to the new value until it is destroyed
926  //when this function exits.
927  Scaler scaler( m_dc, 1.0, 1.0 );
928 
929  //Also move the origin so the text region buts up to the dc top, not the bottom
930  OriginChanger originChanger( m_dc, 0, wxCoord( m_height - m_plplotEdgeLength / m_yScale ) );
931 
932  // Check that we got unicode, warning message and return if not
933  if ( args->unicode_array_len == 0 )
934  {
935  printf( "Non unicode string passed to the wxWidgets driver, ignoring\n" );
936  return;
937  }
938 
939  // Check that unicode string isn't longer then the max we allow
940  if ( args->unicode_array_len >= 500 )
941  {
942  printf( "Sorry, the wxWidgets drivers only handles strings of length < %d\n", 500 );
943  return;
944  }
945 
946  // Calculate the font size (in pt)
947  // Plplot saves it in mm (bizarre units!)
948  PLFLT baseFontSize = pls->chrht * 72.0 / 25.4;
949 
950  // Use PLplot core routine to get the corners of the clipping rectangle
951  PLINT rcx[4], rcy[4];
952  difilt_clip( rcx, rcy );
953 
954  wxPoint cpoints[4];
955  for ( int i = 0; i < 4; i++ )
956  {
957  cpoints[i].x = rcx[i] / m_xScale;
958  cpoints[i].y = m_height - rcy[i] / m_yScale;
959  }
960 
961  Clipper clipper( m_dc, wxRegion( 4, cpoints ).GetBox() );
962 
963  PLUNICODE *lineStart = args->unicode_array;
964  int lineLen = 0;
965  bool lineFeed = false;
966  bool carriageReturn = false;
967  wxCoord paraHeight = 0;
968  // Get the curent font
969  PLINT superscriptLevel = 0;
970  bool underlined = 0;
971  plgfci( &m_fci );
972  //set the font up, we use a textObjectChanger here so that the font returns
973  //to its original value on exit
974  TextObjectsChanger textObjectsChanger( m_dc, m_fontGrabber.GetFont( m_fci, baseFontSize, underlined ),
975  wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ),
976  wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ) );
977  bool lastFontWasCached = m_fontGrabber.lastWasCached();
978 
979  //draw each line of text individually and record the width of the paragraph
980  wxCoord paragraphWidth = 0;
981  while ( lineStart != args->unicode_array + args->unicode_array_len )
982  {
983  //remember the text parameters so they can be restored
984  double lineStartSuperscriptLevel = superscriptLevel;
985  PLUNICODE lineStartFci = m_fci;
986  bool lineStartUnderlined = underlined;
987 
988 
989  while ( lineStart + lineLen != args->unicode_array + args->unicode_array_len
990  && *( lineStart + lineLen ) != (PLUNICODE) '\n' )
991  {
992  lineLen++;
993  }
994 
995  //set line feed for the beginning of this line and
996  //carriage return for the end
997  lineFeed = carriageReturn;
998  carriageReturn = lineStart + lineLen != args->unicode_array + args->unicode_array_len
999  && *( lineStart + lineLen ) == (PLUNICODE) ( '\n' );
1000  if ( lineFeed )
1001  paraHeight += m_textHeight + m_subscriptDepth;
1002 
1003 
1004  // determine extent of text
1005  m_posX = args->x / m_xScale;
1006  m_posY = args->y / m_yScale;
1007 
1008  if ( args->unicode_array_len == 1
1009  && args->unicode_array[0] == m_prevSingleCharString
1010  && lastFontWasCached )
1011  {
1014  }
1015  else
1016  {
1017  DrawTextLine( lineStart, lineLen, baseFontSize, false, superscriptLevel, underlined );
1018  }
1019  paragraphWidth = MAX( paragraphWidth, m_textWidth );
1020 
1021  if ( lineFeed && m_superscriptHeight > m_textHeight )
1022  paraHeight += m_superscriptHeight - m_textHeight;
1023 
1024  // actually draw text, resetting the font first
1025  if ( !pls->get_string_length && m_dc )
1026  {
1027  superscriptLevel = lineStartSuperscriptLevel;
1028  m_fci = lineStartFci;
1029  underlined = lineStartUnderlined;
1030  m_dc->SetFont( m_fontGrabber.GetFont( m_fci, pow( 0.8, abs( superscriptLevel ) ) * baseFontSize, underlined ) );
1031 
1032 
1033  // calculate rotation of text
1034  PLFLT shear;
1035  PLFLT stride;
1036  plRotationShear( args->xform, &m_rotation, &shear, &stride );
1037  m_rotation -= pls->diorot * M_PI / 2.0;
1038  PLFLT cos_rot = cos( m_rotation );
1039  PLFLT sin_rot = sin( m_rotation );
1040  PLFLT cos_shear = cos( shear );
1041  PLFLT sin_shear = sin( shear );
1042 
1043  //Set the transform if possible and draw the text
1044  if ( m_gc )
1045  {
1046  wxGraphicsMatrix originalMatrix = m_gc->GetTransform();
1047 
1048  m_gc->Translate( args->x / m_xScale, m_height - args->y / m_yScale ); //move to text starting position
1049  wxGraphicsMatrix matrix = m_gc->CreateMatrix(
1050  cos_rot * stride, -sin_rot * stride,
1051  cos_rot * sin_shear + sin_rot * cos_shear,
1052  -sin_rot * sin_shear + cos_rot * cos_shear,
1053  0.0, 0.0 ); //create rotation transformation matrix
1054  m_gc->ConcatTransform( matrix ); //rotate
1055  m_gc->Translate( -args->just * m_textWidth, -0.5 * m_textHeight + paraHeight * m_lineSpacing ); //move to set alignment
1056 
1057  DrawTextLine( lineStart, lineLen, baseFontSize, true, superscriptLevel, underlined );
1058  m_gc->SetTransform( originalMatrix );
1059  }
1060 #if wxVERSION_NUMBER >= 2902
1061  else if ( m_useDcTextTransform )
1062  {
1063  wxAffineMatrix2D originalMatrix = m_dc->GetTransformMatrix();
1064 
1065  wxAffineMatrix2D newMatrix = originalMatrix;
1066  newMatrix.Translate( args->x / m_xScale, m_height - args->y / m_yScale );
1067  wxAffineMatrix2D textMatrix;
1068  textMatrix.Set( wxMatrix2D( cos_rot * stride, -sin_rot * stride,
1069  cos_rot * sin_shear + sin_rot * cos_shear,
1070  -sin_rot * sin_shear + cos_rot * cos_shear ),
1071  wxPoint2DDouble( 0.0, 0.0 ) );
1072  newMatrix.Concat( textMatrix );
1073  newMatrix.Translate( -args->just * m_textWidth, -0.5 * m_textHeight + paraHeight * m_lineSpacing );
1074 
1075  m_dc->SetTransformMatrix( newMatrix );
1076  DrawTextLine( lineStart, lineLen, baseFontSize, true, superscriptLevel, underlined );
1077  m_dc->SetTransformMatrix( originalMatrix );
1078  }
1079 #endif
1080  else
1081  {
1082  m_posX = (PLINT) ( args->x / m_xScale - ( args->just * m_textWidth ) * cos_rot - ( 0.5 * m_textHeight - paraHeight * m_lineSpacing ) * sin_rot ); //move to set alignment
1083  m_posY = (PLINT) ( args->y / m_yScale - ( args->just * m_textWidth ) * sin_rot + ( 0.5 * m_textHeight - paraHeight * m_lineSpacing ) * cos_rot );
1084  DrawTextLine( lineStart, lineLen, baseFontSize, true, superscriptLevel, underlined );
1085  }
1086  }
1087 
1088  lineStart += lineLen;
1089  if ( carriageReturn )
1090  lineStart++;
1091  lineLen = 0;
1092  }
1093 
1094  //set the size of the string in mm
1095  if ( pls->get_string_length )
1096  pls->string_length = paragraphWidth * m_xScale / pls->xpmm;
1097 
1098  //save the width if we only had one character
1099  if ( args->unicode_array_len == 1 )
1100  {
1101  m_prevSingleCharStringWidth = paragraphWidth;
1104  }
1105 }
1106 
1107 //--------------------------------------------------------------------------
1108 // void wxPLDevice::EndPage( PLStream* pls )
1109 // End the page. This is the point that we write the buffer to the memory
1110 // mapped file if needed
1111 //--------------------------------------------------------------------------
1113 {
1114  if ( !m_dc )
1115  {
1116  if ( pls->nopause )
1118  else
1120  return;
1121  }
1122 }
1123 
1124 //--------------------------------------------------------------------------
1125 // void wxPLDevice::BeginPage( PLStream* pls )
1126 // Sets up for transfer in case it is needed and sets the current state
1127 //--------------------------------------------------------------------------
1129 {
1130  if ( !m_dc )
1131  {
1134  }
1135 
1136  // Get the starting colour, width and font from the stream
1137  SetWidth( pls );
1138  SetColor( pls );
1139 
1140  //clear the page
1141  ClearBackground( pls );
1142 }
1143 
1144 //--------------------------------------------------------------------------
1145 // void wxPLDevice::SetSize( PLStream* pls )
1146 // Set the size of the page, scale parameters and the dpi
1147 //--------------------------------------------------------------------------
1148 void wxPLDevice::SetSize( PLStream* pls, int width, int height )
1149 {
1150  //we call BeginPage, before we fiddle with fixed aspect so that the
1151  //whole background gets filled
1152  // get boundary coordinates in plplot units
1153  PLINT xmin;
1154  PLINT xmax;
1155  PLINT ymin;
1156  PLINT ymax;
1157  plP_gphy( &xmin, &xmax, &ymin, &ymax );
1158  //split the scaling into an overall scale, the same in both dimensions
1159  //and an aspect part which differs in both directions.
1160  //We will apply the aspect ratio part, and let the DC do the overall
1161  //scaling. This gives us subpixel accuray, but ensures line thickness
1162  //remains consistent in both directions
1163  m_xScale = width > 0 ? (PLFLT) ( xmax - xmin ) / (PLFLT) width : 0.0;
1164  m_yScale = height > 0 ? (PLFLT) ( ymax - ymin ) / (PLFLT) height : 0.0;
1165  m_scale = MIN( m_xScale, m_yScale );
1166 
1167  if ( !m_fixedAspect )
1168  {
1171  }
1172  else
1173  {
1174  //now sort out the fixed aspect and reset the logical scale if needed
1175  if ( PLFLT( height ) / PLFLT( width ) > m_yAspect / m_xAspect )
1176  {
1179  }
1180  else
1181  {
1184  }
1185  }
1186 
1187  m_width = ( xmax - xmin ) / m_xScale;
1188  pls->xlength = PLINT( m_width + 0.5 );
1189  m_height = ( ymax - ymin ) / m_yScale;
1190  pls->ylength = PLINT( m_height + 0.5 );
1191 
1192  // Set the number of plplot pixels per mm
1193  plP_setpxl( m_plplotEdgeLength / m_width * pls->xdpi / 25.4, m_plplotEdgeLength / m_height * pls->ydpi / 25.4 );
1194 
1195  pls->aspect = m_xAspect / m_yAspect;
1196 
1197  // redraw the plot
1198  if ( m_dc && pls->plbuf_buffer )
1199  plreplot();
1200 }
1201 
1202 
1204 {
1205  m_fixedAspect = fix;
1206 }
1207 
1209 {
1210  if ( !m_dc )
1212 }
1213 
1214 //This function transmits the remaining buffer to the gui program via a memory map
1215 //If the buffer is too big for the memory map then it will loop round waiting for
1216 //the gui to catch up each time
1217 // note that this function can be called with pls set to NULL for transmissions
1218 //of jsut a flag for e.g. page end or begin
1219 void wxPLDevice::TransmitBuffer( PLStream* pls, unsigned char transmissionType )
1220 {
1221  if ( !m_outputMemoryMap.isValid() )
1222  return;
1223  size_t amountToCopy = pls ? pls->plbuf_top - m_localBufferPosition : 0;
1224  bool first = true;
1225  size_t counter = 0;
1226  const size_t counterLimit = 10000;
1227  const size_t headerSize = sizeof ( transmissionType ) + sizeof ( size_t );
1228  bool completed = false;
1229  while ( !completed && counter < counterLimit )
1230  {
1231  //if we are doing multiple loops then pause briefly before we
1232  //lock to give the reading application a chance to spot the
1233  //change.
1234  if ( !first )
1235  wxMilliSleep( 10 );
1236  first = false;
1237 
1238 
1239  size_t copyAmount = 0;
1240  size_t freeSpace = 0;
1241  //lock the mutex so reading and writing don't overlap
1242  try
1243  {
1244  PLNamedMutexLocker lock( &m_mutex );
1246 
1247  //check how much free space we have before the end of the buffer
1248  //or if we have looped round how much free space we have before
1249  //we reach the read point
1250  freeSpace = m_outputMemoryMap.getSize() - mapHeader.writeLocation;
1251  // if readLocation is at the beginning then don't quite fill up the buffer
1252  if ( mapHeader.readLocation == plMemoryMapReservedSpace )
1253  --freeSpace;
1254 
1255  //if the free space left in the file is less than that needed for the header then
1256  //just tell the GUI to skip the rest of the file so it can start again at the
1257  //beginning of the file.
1258  if ( freeSpace <= headerSize )
1259  {
1260  if ( mapHeader.readLocation > mapHeader.writeLocation ) //don't overtake the read buffer
1261  freeSpace = 0;
1262  else if ( mapHeader.readLocation == plMemoryMapReservedSpace ) // don't catch up exactly with the read buffer
1263  freeSpace = 0;
1264  else
1265  {
1266  //send a skip end of file command and move back to the beginning of the file
1267  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1268  (void *) ( &transmissionSkipFileEnd ), sizeof ( transmissionSkipFileEnd ) );
1270  counter = 0;
1271  plwarn( "wxWidgets wrapping buffer" );
1272  continue;
1273  }
1274  }
1275 
1276  //if this is a beginning of page then send a beginning of page flag first
1277  if ( transmissionType == transmissionBeginPage )
1278  {
1279  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1280  (void *) ( &transmissionBeginPage ), sizeof ( transmissionBeginPage ) );
1281  mapHeader.writeLocation += sizeof ( transmissionBeginPage );
1282  if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() )
1284  counter = 0;
1285  if ( amountToCopy == 0 )
1286  completed = true;
1287  transmissionType = transmissionRegular;
1288  continue;
1289  }
1290 
1291  //if this is a end of page and we have completed
1292  //the buffer then send a end of page flag first
1293  if ( ( transmissionType == transmissionEndOfPage || transmissionType == transmissionEndOfPageNoPause )
1294  && amountToCopy == 0 )
1295  {
1296  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1297  (void *) ( &transmissionType ), sizeof ( transmissionType ) );
1298  mapHeader.writeLocation += sizeof ( transmissionType );
1299  if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() )
1301  counter = 0;
1302  completed = true;
1303  continue;
1304  }
1305 
1306  if ( transmissionType == transmissionLocate && amountToCopy == 0 )
1307  {
1308  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1309  (void *) ( &transmissionLocate ), sizeof ( transmissionLocate ) );
1310  mapHeader.writeLocation += sizeof ( transmissionLocate );
1311  if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() )
1313  mapHeader.locateModeFlag = 1;
1314  counter = 0;
1315  completed = true;
1316  continue;
1317  }
1318 
1319  if ( transmissionType == transmissionRequestTextSize )
1320  {
1321  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1322  (void *) ( &transmissionRequestTextSize ), sizeof ( transmissionRequestTextSize ) );
1323  mapHeader.writeLocation += sizeof ( transmissionRequestTextSize );
1324  if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() )
1326  counter = 0;
1327  completed = true;
1328  continue;
1329  }
1330  if ( transmissionType == transmissionClose )
1331  {
1332  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1333  (void *) ( &transmissionType ), sizeof ( transmissionType ) );
1334  mapHeader.writeLocation += sizeof ( transmissionType );
1335  if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() )
1337  counter = 0;
1338  completed = true;
1339  continue;
1340  }
1341 
1342  //if we have looped round stay 1 character behind the read buffer - it makes it
1343  //easier to test whether the reading has caught up with the writing or vica versa
1344  if ( mapHeader.writeLocation < mapHeader.readLocation && mapHeader.readLocation > 0 )
1345  freeSpace = mapHeader.readLocation - mapHeader.writeLocation - 1;
1346 
1347  if ( freeSpace > headerSize )
1348  {
1349  //decide exactly how much to copy
1350  copyAmount = MIN( amountToCopy, freeSpace - headerSize );
1351 
1352  //copy the header and the amount we can to the buffer
1353  if ( copyAmount != amountToCopy )
1354  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1355  (char *) ( &transmissionPartial ), sizeof ( transmissionPartial ) );
1356  else
1357  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation,
1358  (char *) ( &transmissionComplete ), sizeof ( transmissionComplete ) );
1359  if ( pls )
1360  {
1361  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation + sizeof ( transmissionComplete ),
1362  (char *) ( &copyAmount ), sizeof ( copyAmount ) );
1363  memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation + headerSize,
1364  (char *) pls->plbuf_buffer + m_localBufferPosition, copyAmount );
1365  m_localBufferPosition += copyAmount;
1366  mapHeader.writeLocation += copyAmount + headerSize;
1367  if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() )
1369  amountToCopy -= copyAmount;
1370  counter = 0;
1371  }
1372  if ( amountToCopy == 0 && transmissionType != transmissionEndOfPage
1373  && transmissionType != transmissionLocate
1374  && transmissionType != transmissionEndOfPageNoPause )
1375  completed = true;
1376  }
1377  else
1378  {
1379  ++counter;
1380  }
1381  }
1382 #ifdef WIN32
1383  catch ( DWORD )
1384  {
1385  plwarn( "Locking mutex failed when trying to communicate with wxPLViewer." );
1386  break;
1387  }
1388 #endif
1389  catch ( ... )
1390  {
1391  plwarn( "Unknown error when trying to communicate with wxPLViewer." );
1392  break;
1393  }
1394  }
1395  if ( counter == counterLimit )
1396  {
1397  plwarn( "Communication timeout with wxPLViewer - disconnecting" );
1399  }
1400 }
1401 
1403 {
1404  if ( strlen( m_mfo ) > 0 )
1405  {
1406  const size_t mapSize = 1024 * 1024;
1407  //create a memory map to hold the data and add it to the array of maps
1408  int nTries = 0;
1409  char mapName[PLPLOT_MAX_PATH];
1410  char mutexName[PLPLOT_MAX_PATH];
1411  static Rand randomGenerator; // make this static so that rapid repeat calls don't use the same seed
1412  while ( nTries < 10 )
1413  {
1414  for ( int i = 0; i < strlen( m_mfo ); ++i )
1415  {
1416  if ( m_mfo[i] == '?' )
1417  mapName[i] = 'A' + (char) ( randomGenerator() % 26 );
1418  else
1419  mapName[i] = m_mfo[i];
1420  }
1421  mapName[strlen( m_mfo )] = '\0';
1422  //truncate it earlier if needed
1423  if ( strlen( m_mfo ) > PLPLOT_MAX_PATH - 4 )
1424  mapName[PLPLOT_MAX_PATH - 4] = '\0';
1425  pldebug( "wxPLDevice::SetupMemoryMap", "nTries = %d, mapName = %s\n", nTries, mapName );
1426  strcpy( mutexName, mapName );
1427  strcat( mutexName, "mut" );
1428  pldebug( "wxPLDevice::SetupMemoryMap", "nTries = %d, mutexName = %s\n", nTries, mutexName );
1429  m_outputMemoryMap.create( mapName, mapSize, false, true );
1430  if ( m_outputMemoryMap.isValid() )
1431  m_mutex.create( mutexName );
1432  if ( !m_mutex.isValid() )
1434  if ( m_outputMemoryMap.isValid() )
1435  break;
1436  ++nTries;
1437  }
1438  //m_outputMemoryMap.create( m_mfo, pls->plbuf_top, false, true );
1439  //check the memory map is valid
1440  if ( !m_outputMemoryMap.isValid() )
1441  {
1442  plwarn( "Error creating memory map for wxWidget instruction transmission. The plots will not be displayed" );
1443  return;
1444  }
1445 
1446  //zero out the reserved area
1450  header->viewerOpenFlag = 0;
1451  header->locateModeFlag = 0;
1452  header->completeFlag = 0;
1453 
1454  //try to find the wxPLViewer executable, in the first instance just assume it
1455  //is in the path.
1456  //wxString exeName = wxT( "/nfs/see-fs-02_users/earpros/usr/src/plplot-plplot/build/utils/wxPLViewer" );
1457  wxString exeName = wxT( "wxPLViewer" );
1458  if ( plInBuildTree() )
1459  {
1460  //if we are in the build tree check for the needed exe in there
1461  wxArrayString files;
1462  wxString utilsDir = wxString( wxT( BUILD_DIR ) ) + wxString( wxT( "/utils" ) );
1463  wxDir::GetAllFiles( utilsDir, &files, exeName, wxDIR_FILES | wxDIR_DIRS );
1464  if ( files.size() == 0 )
1465  wxDir::GetAllFiles( utilsDir, &files, exeName + wxT( ".exe" ), wxDIR_FILES | wxDIR_DIRS );
1466  if ( files.size() > 0 )
1467  exeName = files[0];
1468  }
1469  else
1470  {
1471  //check the plplot bin install directory
1472  wxArrayString files;
1473  wxDir::GetAllFiles( wxT( BIN_DIR ), &files, exeName, wxDIR_FILES | wxDIR_DIRS );
1474  if ( files.size() == 0 )
1475  wxDir::GetAllFiles( wxT( BIN_DIR ), &files, exeName + wxT( ".exe" ), wxDIR_FILES | wxDIR_DIRS );
1476  if ( files.size() > 0 )
1477  exeName = files[0];
1478  }
1479  //Run the wxPlViewer with command line parameters telling it the location and size of the buffer
1480  wxString command;
1481  command << wxT( "\"" ) << exeName << wxT( "\" " ) << wxString( mapName, wxConvUTF8 ) << wxT( " " ) <<
1482  mapSize << wxT( " " ) << m_width << wxT( " " ) << m_height;
1483 #ifndef WXPLVIEWER_DEBUG
1484 #ifdef WIN32
1485 
1486  if ( wxExecute( command, wxEXEC_ASYNC ) == 0 )
1487  plwarn( "Failed to run wxPLViewer - no plots will be shown" );
1488 #else //WIN32
1489  //Linux doesn't like using wxExecute without a wxApp, so use system instead
1490  command << wxT( " &" );
1491  system( command.mb_str() );
1492 #endif //WIN32
1493  size_t maxTries = 1000;
1494 #else //WXPLVIEWER_DEBUG
1495  wxString runMessage;
1496  runMessage << "Begin Running wxPLViewer in the debugger now to continue. Use the parameters: plplotMemoryMap " <<
1497  mapSize << " " << m_width << " " << m_height;
1498  fprintf( stdout, runMessage );
1499  size_t maxTries = 100000;
1500 #endif //WXPLVIEWER_DEBUG
1501  //wait until the viewer signals it has opened the map file
1502  size_t counter = 0;
1503  size_t &viewerSignal = header->viewerOpenFlag;
1504  while ( counter < maxTries && viewerSignal == 0 )
1505  {
1506  wxMilliSleep( 10 );
1507  ++counter;
1508  }
1509  if ( viewerSignal == 0 )
1510  plwarn( "wxPLViewer failed to signal it has found the shared memory." );
1511  }
1512 }
1513 
1515 {
1516  if ( !m_dc && m_outputMemoryMap.isValid() )
1517  {
1520  bool gotResponse = false;
1521  while ( !gotResponse )
1522  {
1523  wxMilliSleep( 100 );
1524  PLNamedMutexLocker lock( &m_mutex );
1525  gotResponse = header->locateModeFlag == 0;
1526  }
1527 
1528  PLNamedMutexLocker lock( &m_mutex );
1529  *graphicsIn = header->graphicsIn;
1530  }
1531  else
1532  {
1533  plwarn( "plGetCursor cannot be used when the user supplies a wxDC or until wxPLViewer is initialised" );
1534  graphicsIn->dX = -1;
1535  graphicsIn->dY = -1;
1536  graphicsIn->pX = -1;
1537  graphicsIn->pY = -1;
1538  }
1539 }
1540 
1541 //--------------------------------------------------------------------------
1542 // wxRegion wxPLDevice::GetClipRegion()
1543 // Gets the current clip region from plplot as a wxRegion
1544 //--------------------------------------------------------------------------
1545 
1547 {
1548  PLINT rcx[4], rcy[4];
1549  difilt_clip( rcx, rcy );
1550 
1551  wxPoint cpoints[4];
1552  for ( int i = 0; i < 4; i++ )
1553  {
1554  cpoints[i].x = rcx[i] / m_xScale;
1555  cpoints[i].y = m_height - rcy[i] / m_yScale;
1556  }
1557  return wxRegion( 4, cpoints );
1558 }