1 /* GNUPLOT - wxt_gui.cpp */
2
3 /*[
4 * Copyright 2005,2006 Timothee Lecomte
5 *
6 * Permission to use, copy, and distribute this software and its
7 * documentation for any purpose with or without fee is hereby granted,
8 * provided that the above copyright notice appear in all copies and
9 * that both that copyright notice and this permission notice appear
10 * in supporting documentation.
11 *
12 * Permission to modify the software is granted, but not the right to
13 * distribute the complete modified source code. Modifications are to
14 * be distributed as patches to the released version. Permission to
15 * distribute binaries produced by compiling modified sources is granted,
16 * provided you
17 * 1. distribute the corresponding source modifications from the
18 * released version in the form of a patch file along with the binaries,
19 * 2. add special version identification to distinguish your version
20 * in addition to the base release version number,
21 * 3. provide your name and address as the primary contact for the
22 * support of your modified version, and
23 * 4. retain our contact information in regard to use of the base
24 * software.
25 * Permission to distribute the released version of the source code along
26 * with corresponding source modifications in the form of a patch file is
27 * granted with same provisions 2 through 4 for binary distributions.
28 *
29 * This software is provided "as is" without express or implied warranty
30 * to the extent permitted by applicable law.
31 *
32 *
33 * Alternatively, the contents of this file, apart from one portion
34 * that originates from other gnuplot files and is designated as such,
35 * may be used under the terms of the GNU General Public License
36 * Version 2 or later (the "GPL"), in which case the provisions of GPL
37 * are applicable instead of those above. If you wish to allow
38 * use of your version of the appropriate portion of this file only
39 * under the terms of the GPL and not to allow others to use your version
40 * of this file under the above gnuplot license, indicate your decision
41 * by deleting the provisions above and replace them with the notice
42 * and other provisions required by the GPL. If you do not
43 * delete the provisions above, a recipient may use your version of this file
44 * under either the GPL or the gnuplot license.
45 ]*/
46
47 /* -----------------------------------------------------
48 * The following code uses the wxWidgets library, which is
49 * distributed under its own licence (derivated from the LGPL).
50 *
51 * You can read it at the following address :
52 * http://www.wxwidgets.org/licence.htm
53 * -----------------------------------------------------*/
54
55 /* ------------------------------------------------------
56 * This file implements in C++ the functions which are called by wxt.trm :
57 *
58 * It depends on the generic cairo functions,
59 * declared in gp_cairo.h for all the drawing work.
60 *
61 * Here is the interactive part :
62 * - rescaling according to the window's size,
63 * - mouse support (cursor position, zoom, rotation, ruler, clipboard...),
64 * - a toolbar to give additional capabilities (similar to the OS/2 terminal),
65 * - multiple plot windows.
66 *
67 * ------------------------------------------------------*/
68
69 /* PORTING NOTES
70 * Since it uses wxWidgets and Cairo routines, this code is mostly cross-platform.
71 * However some details have to be implemented or tweaked for each platform :
72 *
73 * 1) A generic 'image' surface is implemented as the destination surface
74 * for cairo drawing. But for optimal results, cairo should draw to a native
75 * surface corresponding to the graphical system.
76 * Examples :
77 * - a gdkpixmap when compiling for wxGTK (currently disabled because of a bug in CAIRO_OPERATOR_SATURATE),
78 * - a HDC when compiling for wxMSW
79 * - [insert your contribution here !]
80 *
81 * 2) You have to be careful with the gui main loop.
82 * As far as I understand :
83 * Some platforms (Windows ?) require that it is in the main thread.
84 * When compiling for Windows (wxMSW), the text window already implements it, so we
85 * don't have to do it, but wxWidgets still have to be initialized correctly.
86 * When compiling for Unix (wxGTK), we don't have one, so we launch it in a separate thread.
87 * For new platforms, it is necessary to figure out whether to configure for single
88 * or multi-threaded operation.
89 */
90
91
92 /* define DEBUG here to have debugging messages in stderr */
93 #include "wxt_gui.h"
94
95 /* frame icon composed of three icons of different resolutions */
96 #include "bitmaps/xpm/icon16x16.xpm"
97 #include "bitmaps/xpm/icon32x32.xpm"
98 #include "bitmaps/xpm/icon64x64.xpm"
99 /* cursors */
100 #include "bitmaps/xpm/cross.xpm"
101 #include "bitmaps/xpm/right.xpm"
102 #include "bitmaps/xpm/rotate.xpm"
103 #include "bitmaps/xpm/size.xpm"
104 /* Toolbar icons
105 * Those are embedded PNG icons previously converted to an array.
106 * See bitmaps/png/README for details */
107 #include "bitmaps/png/clipboard_png.h"
108 #include "bitmaps/png/replot_png.h"
109 #include "bitmaps/png/grid_png.h"
110 #include "bitmaps/png/previouszoom_png.h"
111 #include "bitmaps/png/nextzoom_png.h"
112 #include "bitmaps/png/autoscale_png.h"
113 #include "bitmaps/png/config_png.h"
114 #include "bitmaps/png/help_png.h"
115
116 /* standard icon art from wx */
117 #include <wx/artprov.h>
118 #include <wx/printdlg.h>
119
120 extern "C" {
121 #ifdef HAVE_CONFIG_H
122 # include "config.h"
123 #endif
124 #ifdef HAVE_SYS_SELECT_H
125 # include <sys/select.h>
126 #endif
127 }
128
129 /* Interactive toggle control variables
130 */
131 static int wxt_cur_plotno = 0;
132 static TBOOLEAN wxt_in_key_sample = FALSE;
133 static TBOOLEAN wxt_in_plot = FALSE;
134 #ifdef USE_MOUSE
135 typedef struct {
136 unsigned int left;
137 unsigned int right;
138 unsigned int ytop;
139 unsigned int ybot;
140 TBOOLEAN hidden;
141 } wxtBoundingBox;
142 wxtBoundingBox *wxt_key_boxes = NULL;
143 int wxt_max_key_boxes = 0;
144
145 /* Hypertext tracking variables */
146 typedef struct {
147 unsigned int x;
148 unsigned int y;
149 unsigned int size;
150 } wxtAnchorPoint;
151 wxtAnchorPoint *wxt_anchors = NULL;
152 int wxt_n_anchors = 0;
153 int wxt_max_anchors = 0;
154 TBOOLEAN pending_href = FALSE;
155 const char *wxt_display_hypertext = NULL;
156 wxtAnchorPoint wxt_display_anchor = {0,0,0};
157 char * wxt_hypertext_fontname = NULL;
158 double wxt_hypertext_fontsize = 10;
159 int wxt_hypertext_fontstyle = 10;
160 int wxt_hypertext_fontweight = 10;
161
162 #else
163 #define wxt_update_key_box(x,y)
164 #define wxt_update_anchors(x,y,size)
165 #endif
166
167 #if defined(WXT_MONOTHREADED) && !defined(_WIN32)
168 static int wxt_yield = 0; /* used in wxt_waitforinput() */
169 static TBOOLEAN wxt_interlock = FALSE; /* used to prevent recursive OnCreateWindow */
170 #endif
171
172 char *wxt_enhanced_fontname = NULL;
173
174 #ifdef __WXMAC__
175 #include <ApplicationServices/ApplicationServices.h>
176 #endif
177
178 /* ---------------------------------------------------------------------------
179 * event tables and other macros for wxWidgets
180 * --------------------------------------------------------------------------*/
181
182 /* the event tables connect the wxWidgets events with the functions (event
183 * handlers) which process them. It can be also done at run-time, but for the
184 * simple menu events like this the static method is much simpler.
185 */
186
BEGIN_EVENT_TABLE(wxtApp,wxApp)187 BEGIN_EVENT_TABLE( wxtApp, wxApp )
188 EVT_COMMAND( wxID_ANY, wxCreateWindowEvent, wxtApp::OnCreateWindow )
189 EVT_COMMAND( wxID_ANY, wxExitLoopEvent, wxtApp::OnExitLoop )
190 END_EVENT_TABLE()
191
192 BEGIN_EVENT_TABLE( wxtFrame, wxFrame )
193 EVT_CLOSE( wxtFrame::OnClose )
194 EVT_SIZE( wxtFrame::OnSize )
195 EVT_TOOL( Toolbar_ExportToFile, wxtFrame::OnExport )
196 #ifdef WXT_PRINT
197 EVT_TOOL( Toolbar_Print, wxtFrame::OnPrint )
198 #endif
199 /* Clipboard widget (should consolidate this with Export to File) */
200 EVT_TOOL( Toolbar_CopyToClipboard, wxtFrame::OnCopy )
201 #ifdef USE_MOUSE
202 EVT_TOOL( Toolbar_Replot, wxtFrame::OnReplot )
203 EVT_TOOL( Toolbar_ToggleGrid, wxtFrame::OnToggleGrid )
204 EVT_TOOL( Toolbar_ZoomPrevious, wxtFrame::OnZoomPrevious )
205 EVT_TOOL( Toolbar_ZoomNext, wxtFrame::OnZoomNext )
206 EVT_TOOL( Toolbar_Autoscale, wxtFrame::OnAutoscale )
207 EVT_COMMAND( wxID_ANY, wxStatusTextEvent, wxtFrame::OnSetStatusText )
208 #endif /*USE_MOUSE*/
209 EVT_TOOL( Toolbar_Config, wxtFrame::OnConfig )
210 EVT_TOOL( Toolbar_Help, wxtFrame::OnHelp )
211 END_EVENT_TABLE()
212
213 BEGIN_EVENT_TABLE( wxtPanel, wxPanel )
214 EVT_LEAVE_WINDOW( wxtPanel::OnMouseLeave )
215 EVT_PAINT( wxtPanel::OnPaint )
216 EVT_ERASE_BACKGROUND( wxtPanel::OnEraseBackground )
217 EVT_SIZE( wxtPanel::OnSize )
218 #ifdef USE_MOUSE
219 EVT_MOTION( wxtPanel::OnMotion )
220 EVT_LEFT_DOWN( wxtPanel::OnLeftDown )
221 EVT_LEFT_UP( wxtPanel::OnLeftUp )
222 EVT_MIDDLE_DOWN( wxtPanel::OnMiddleDown )
223 EVT_MIDDLE_UP( wxtPanel::OnMiddleUp )
224 EVT_RIGHT_DOWN( wxtPanel::OnRightDown )
225 EVT_RIGHT_UP( wxtPanel::OnRightUp )
226 EVT_MOUSEWHEEL( wxtPanel::OnMouseWheel )
227 EVT_CHAR( wxtPanel::OnKeyDownChar )
228 #endif /*USE_MOUSE*/
229 END_EVENT_TABLE()
230
231 BEGIN_EVENT_TABLE( wxtConfigDialog, wxDialog )
232 EVT_CLOSE( wxtConfigDialog::OnClose )
233 EVT_CHOICE( Config_Rendering, wxtConfigDialog::OnRendering )
234 EVT_COMMAND_RANGE( Config_OK, Config_CANCEL,
235 wxEVT_COMMAND_BUTTON_CLICKED, wxtConfigDialog::OnButton )
236 END_EVENT_TABLE()
237
238 #ifdef WXT_MULTITHREADED
239 /* ----------------------------------------------------------------------------
240 * gui thread
241 * ----------------------------------------------------------------------------*/
242
243 /* What really happens in the thread
244 * Just before it returns, wxEntry will call a whole bunch of wxWidgets-cleanup functions */
245 void *wxtThread::Entry()
246 {
247 FPRINTF((stderr,"secondary thread entry\n"));
248
249 /* don't answer to SIGINT in this thread - avoids LONGJMP problems */
250 sigset_t set;
251 sigemptyset(&set);
252 sigaddset(&set, SIGINT);
253 pthread_sigmask(SIG_BLOCK, &set, NULL);
254
255 /* gui loop */
256 wxTheApp->OnRun();
257
258 /* Workaround for a deadlock when the main thread will Wait() for this one.
259 * This issue comes from the fact that our gui main loop is not in the
260 * main thread as wxWidgets was written for. */
261 wxt_MutexGuiLeave();
262
263 FPRINTF((stderr,"secondary thread finished\n"));
264 return NULL;
265 }
266 #endif /* WXT_MULTITHREADED */
267
268
269
270 /* ----------------------------------------------------------------------------
271 * `Main program' equivalent: the program execution "starts" here
272 * ----------------------------------------------------------------------------*/
273
274 /* Create a new application object */
IMPLEMENT_APP_NO_MAIN(wxtApp)275 IMPLEMENT_APP_NO_MAIN(wxtApp)
276
277 bool wxtApp::OnInit()
278 {
279 #ifdef __WXMAC__
280 ProcessSerialNumber PSN;
281 GetCurrentProcess(&PSN);
282 TransformProcessType(&PSN, kProcessTransformToForegroundApplication);
283 #endif
284 static TBOOLEAN image_handlers_loaded = FALSE;
285
286 /* Usually wxWidgets apps create their main window here.
287 * However, in the context of multiple plot windows, the same code is written in wxt_init().
288 * So, to avoid duplication of the code, we do only what is strictly necessary.*/
289
290 FPRINTF((stderr, "OnInit\n"));
291
292 /* initialize frames icons */
293 icon.AddIcon(wxIcon(icon16x16_xpm));
294 icon.AddIcon(wxIcon(icon32x32_xpm));
295 icon.AddIcon(wxIcon(icon64x64_xpm));
296
297 /* we load the image handlers, needed to copy the plot to clipboard, and to load icons */
298 /* Only load once (else wxt 3.0 complains noisily) */
299 if (!image_handlers_loaded) {
300 ::wxInitAllImageHandlers();
301 image_handlers_loaded = TRUE;
302 }
303
304 #ifdef __WXMSW__
305 /* allow the toolbar to display properly png icons with an alpha channel */
306 wxSystemOptions::SetOption(wxT("msw.remap"), 0);
307 #endif /* __WXMSW__ */
308
309 /* load toolbar icons */
310 LoadPngIcon(clipboard_png, sizeof(clipboard_png), 0);
311 LoadPngIcon(replot_png, sizeof(replot_png), 1);
312 LoadPngIcon(grid_png, sizeof(grid_png), 2);
313 LoadPngIcon(previouszoom_png, sizeof(previouszoom_png), 3);
314 LoadPngIcon(nextzoom_png, sizeof(nextzoom_png), 4);
315 LoadPngIcon(autoscale_png, sizeof(autoscale_png), 5);
316 LoadPngIcon(config_png, sizeof(config_png), 6);
317 LoadPngIcon(help_png, sizeof(help_png), 7);
318
319 /* load cursors */
320 LoadCursor(wxt_cursor_cross, cross);
321 LoadCursor(wxt_cursor_right, right);
322 LoadCursor(wxt_cursor_rotate, rotate);
323 LoadCursor(wxt_cursor_size, size);
324
325 /* Initialize the config object */
326 /* application and vendor name are used by wxConfig to construct the name
327 * of the config file/registry key and must be set before the first call
328 * to Get() */
329 SetVendorName(wxT("gnuplot"));
330 SetAppName(wxT("gnuplot-wxt"));
331 wxConfigBase *pConfig = wxConfigBase::Get();
332 /* this will force writing back of the defaults for all values
333 * if they're not present in the config - this can give the user an idea
334 * of all possible settings */
335 pConfig->SetRecordDefaults();
336
337 FPRINTF((stderr, "OnInit finished\n"));
338
339 return true; /* means that process must continue */
340 }
341
342 /* load an icon from a PNG file embedded as a C array */
LoadPngIcon(const unsigned char * embedded_png,int length,int icon_number)343 void wxtApp::LoadPngIcon(const unsigned char *embedded_png, int length, int icon_number)
344 {
345 wxMemoryInputStream pngstream(embedded_png, length);
346 #ifdef __WXOSX_COCOA__
347 /* 16x16 bitmaps on wxCocoa cause blurry toolbar images, resize them to 24x24 */
348 toolBarBitmaps[icon_number] = new wxBitmap(wxImage(pngstream, wxBITMAP_TYPE_PNG).Resize(wxSize(24, 24), wxPoint(4, 4)));
349 #else
350 toolBarBitmaps[icon_number] = new wxBitmap(wxImage(pngstream, wxBITMAP_TYPE_PNG));
351 #endif
352 }
353
354 /* load a cursor */
LoadCursor(wxCursor & cursor,const char * xpm_bits[])355 void wxtApp::LoadCursor(wxCursor &cursor, const char* xpm_bits[])
356 {
357 int hotspot_x, hotspot_y;
358 wxBitmap cursor_bitmap = wxBitmap(xpm_bits);
359 wxImage cursor_image = cursor_bitmap.ConvertToImage();
360 /* XPM spec : first string is :
361 * width height ncolors charperpixel hotspotx hotspoty */
362 sscanf(xpm_bits[0], "%*d %*d %*d %*d %d %d", &hotspot_x, &hotspot_y);
363 cursor_image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, hotspot_x);
364 cursor_image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, hotspot_y);
365 cursor = wxCursor(cursor_image);
366 }
367
368 /* cleanup on exit
369 * In a pure wxWidgets app, the returned int is the exit status of the app.
370 * Here it is not used. */
OnExit()371 int wxtApp::OnExit()
372 {
373 FPRINTF((stderr,"wxtApp::OnExit\n"));
374 /* clean up: Set() returns the active config object as Get() does, but unlike
375 * Get() it doesn't try to create one if there is none (definitely not what
376 * we want here!) */
377 delete wxConfigBase::Set((wxConfigBase *) NULL);
378 return 0;
379 }
380
381 /* will gently terminate the gui thread */
OnExitLoop(wxCommandEvent & WXUNUSED (event))382 void wxtApp::OnExitLoop( wxCommandEvent& WXUNUSED(event) )
383 {
384 FPRINTF((stderr,"wxtApp::OnExitLoop\n"));
385 wxTheApp->ExitMainLoop();
386 }
387
OnCreateWindow(wxCommandEvent & event)388 void wxtApp::OnCreateWindow( wxCommandEvent& event )
389 {
390 /* retrieve the pointer to wxt_window_t */
391 wxt_window_t *window = (wxt_window_t*) event.GetClientData();
392
393 FPRINTF((stderr,"wxtApp::OnCreateWindow\n"));
394 #if defined(WXT_MONOTHREADED) && !defined(_WIN32)
395 wxt_interlock = TRUE;
396 #endif
397 window->frame = new wxtFrame( window->title, window->id );
398 window->frame->Show(true);
399 #ifdef __WXMSW__
400 // If gnuplot is invoked "hidden", the very first Show() is ignored.
401 if (!window->frame->IsShown())
402 window->frame->Show(true);
403 #endif
404
405 FPRINTF((stderr,"new plot window opened\n"));
406 /* make the panel able to receive keyboard input */
407 window->frame->panel->SetFocus();
408 /* set the default crosshair cursor */
409 window->frame->panel->SetCursor(wxt_cursor_cross);
410 /* creating the true context (at initialization, it may be a fake one).
411 * Note : the frame must be shown for this to succeed */
412 if (!window->frame->panel->plot.success)
413 window->frame->panel->wxt_cairo_create_context();
414
415 /* tell the other thread we have finished */
416 wxMutexLocker lock(*(window->mutex));
417 window->condition->Broadcast();
418 #if defined(WXT_MONOTHREADED) && !defined(_WIN32)
419 wxt_interlock = FALSE;
420 #endif
421 }
422
423 /* wrapper for AddPendingEvent or ProcessEvent */
SendEvent(wxEvent & event)424 void wxtApp::SendEvent( wxEvent &event)
425 {
426 #ifdef WXT_MULTITHREADED
427 AddPendingEvent(event);
428 #else /* !WXT_MULTITHREADED */
429 ProcessEvent(event);
430 #endif /* !WXT_MULTITHREADED */
431 }
432
433 /* ---------------------------------------------------------------------------
434 * Frame : the main windows (one for each plot)
435 * ----------------------------------------------------------------------------*/
436
437 /* frame constructor*/
wxtFrame(const wxString & title,wxWindowID id)438 wxtFrame::wxtFrame( const wxString& title, wxWindowID id )
439 : wxFrame((wxFrame *)NULL, id, title, wxPoint(wxt_posx, wxt_posy), wxDefaultSize, wxDEFAULT_FRAME_STYLE|wxWANTS_CHARS)
440 {
441 FPRINTF((stderr,"wxtFrame constructor\n"));
442
443 /* used to check for panel initialization */
444 panel = NULL;
445
446 /* initialize the state of the configuration dialog */
447 config_displayed = false;
448
449 /* set up the window icon, in several resolutions */
450 SetIcons(icon);
451
452 /* set up the status bar, and fill it with an empty
453 * string. It will be immediately overridden by gnuplot. */
454 CreateStatusBar();
455 SetStatusText( wxT("") );
456
457 /* set up the toolbar */
458 toolbar = CreateToolBar();
459
460 /* With wxMSW, default toolbar size is only 16x15. */
461 // toolbar->SetToolBitmapSize(wxSize(16,16));
462
463 toolbar->AddTool(Toolbar_CopyToClipboard, wxT("Copy"),
464 wxArtProvider::GetBitmap(wxART_PASTE, wxART_TOOLBAR),
465 wxT("Copy plot to clipboard"));
466 toolbar->AddTool(Toolbar_ExportToFile, wxT("Export"),
467 wxArtProvider::GetBitmap(wxART_FILE_SAVE_AS, wxART_TOOLBAR),
468 wxT("Export plot to file"));
469 #ifdef WXT_PRINT
470 toolbar->AddTool(Toolbar_Print, wxT("Print"),
471 wxArtProvider::GetBitmap(wxART_PRINT, wxART_TOOLBAR),
472 wxT("Print plot"));
473 #endif
474 #ifdef USE_MOUSE
475 #ifdef __WXOSX_COCOA__
476 /* wx 2.9 Cocoa bug & crash workaround for Lion, which does not have toolbar separators anymore */
477 toolbar->AddStretchableSpace();
478 #else
479 toolbar->AddSeparator();
480 #endif
481 toolbar->AddTool(Toolbar_Replot, wxT("Replot"),
482 *(toolBarBitmaps[1]), wxT("Replot"));
483 toolbar->AddTool(Toolbar_ToggleGrid, wxT("Toggle grid"),
484 *(toolBarBitmaps[2]),wxNullBitmap,wxITEM_NORMAL, wxT("Toggle grid"));
485 toolbar->AddTool(Toolbar_ZoomPrevious, wxT("Previous zoom"),
486 *(toolBarBitmaps[3]), wxT("Apply the previous zoom settings"));
487 toolbar->AddTool(Toolbar_ZoomNext, wxT("Next zoom"),
488 *(toolBarBitmaps[4]), wxT("Apply the next zoom settings"));
489 toolbar->AddTool(Toolbar_Autoscale, wxT("Autoscale"),
490 *(toolBarBitmaps[5]), wxT("Apply autoscale"));
491 #endif /*USE_MOUSE*/
492 #ifdef __WXOSX_COCOA__
493 /* wx 2.9 Cocoa bug & crash workaround for Lion, which does not have toolbar separators anymore */
494 toolbar->AddStretchableSpace();
495 #else
496 toolbar->AddSeparator();
497 #endif
498 toolbar->AddTool(Toolbar_Config, wxT("Terminal configuration"),
499 *(toolBarBitmaps[6]), wxT("Open configuration dialog"));
500 toolbar->AddTool(Toolbar_Help, wxT("Help"),
501 *(toolBarBitmaps[7]), wxT("Open help dialog"));
502 toolbar->Realize();
503
504 FPRINTF((stderr,"wxtFrame constructor 2\n"));
505
506 SetClientSize( wxSize(wxt_width, wxt_height) );
507
508 /* build the panel, which will contain the visible device context */
509 panel = new wxtPanel( this, this->GetId(), this->GetClientSize() );
510
511 /* setting minimum height and width for the window */
512 SetSizeHints(100, 100);
513
514 FPRINTF((stderr,"wxtFrame constructor 3\n"));
515 }
516
517
~wxtFrame()518 wxtFrame::~wxtFrame()
519 {
520 /* Automatically remove frame from window list. */
521 std::vector<wxt_window_t>::iterator wxt_iter;
522
523 for(wxt_iter = wxt_window_list.begin();
524 wxt_iter != wxt_window_list.end(); wxt_iter++) {
525 if (this == wxt_iter->frame) {
526 wxt_window_list.erase(wxt_iter);
527 break;
528 }
529 }
530 }
531
532
533 /* toolbar event : Export to file
534 * We will create a file dialog, using platform-independent wxWidgets functions
535 */
OnExport(wxCommandEvent & WXUNUSED (event))536 void wxtFrame::OnExport( wxCommandEvent& WXUNUSED( event ) )
537 {
538 static int userFilterIndex = 0;
539 static wxString saveDir;
540
541 if (saveDir.IsEmpty())
542 saveDir = wxGetCwd();
543
544 wxFileDialog exportFileDialog (this, wxT("Exported File Format"),
545 saveDir, wxT(""),
546 #ifdef __WXMSW__
547 wxT("PNG files (*.png)|*.png|PDF files (*.pdf)|*.pdf|SVG files (*.svg)|*.svg|Enhanced Metafile (*.emf)|*.emf"),
548 #else
549 wxT("PNG files (*.png)|*.png|PDF files (*.pdf)|*.pdf|SVG files (*.svg)|*.svg"),
550 #endif
551 wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
552 exportFileDialog.SetFilterIndex(userFilterIndex);
553
554 if (exportFileDialog.ShowModal() == wxID_CANCEL)
555 return;
556
557 /* wxID_OK: User wants to save to a file. */
558
559 saveDir = exportFileDialog.GetDirectory();
560
561 wxString fullpathFilename = exportFileDialog.GetPath();
562 wxString fileExt = fullpathFilename.AfterLast ('.');
563
564 cairo_status_t ierr;
565 cairo_surface_t *surface;
566 cairo_t* save_cr;
567
568 switch (exportFileDialog.GetFilterIndex()) {
569 case 0 :
570 /* Save as PNG file. */
571 surface = cairo_get_target(panel->plot.cr);
572 ierr = cairo_surface_write_to_png(surface, fullpathFilename.mb_str(wxConvUTF8));
573 if (ierr != CAIRO_STATUS_SUCCESS)
574 fprintf(stderr,"error writing PNG file: %s\n", cairo_status_to_string(ierr));
575 break;
576
577 case 1 :
578 /* Save as PDF file. */
579 save_cr = panel->plot.cr;
580 cairo_save(save_cr);
581 surface = cairo_pdf_surface_create(
582 fullpathFilename.mb_str(wxConvUTF8),
583 panel->plot.device_xmax, panel->plot.device_ymax);
584 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
585 fprintf(stderr, "Cairo error: could not create surface for file %s.\n", (const char *)fullpathFilename.mb_str());
586 cairo_surface_destroy(surface);
587 break;
588 }
589 panel->plot.cr = cairo_create(surface);
590 cairo_surface_destroy(surface);
591
592 cairo_scale(panel->plot.cr,
593 1./(double)panel->plot.oversampling_scale,
594 1./(double)panel->plot.oversampling_scale);
595 panel->wxt_cairo_refresh();
596
597 cairo_show_page(panel->plot.cr);
598 cairo_surface_finish(surface);
599 panel->plot.cr = save_cr;
600 cairo_restore(panel->plot.cr);
601 break;
602
603 case 2 :
604 #ifdef CAIRO_HAS_SVG_SURFACE
605 /* Save as SVG file. */
606 save_cr = panel->plot.cr;
607 cairo_save(save_cr);
608 surface = cairo_svg_surface_create(
609 fullpathFilename.mb_str(wxConvUTF8),
610 panel->plot.device_xmax, panel->plot.device_ymax);
611 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
612 fprintf(stderr, "Cairo error: could not create surface for file %s.\n", (const char *)fullpathFilename.mb_str());
613 cairo_surface_destroy(surface);
614 break;
615 }
616 panel->plot.cr = cairo_create(surface);
617 cairo_surface_destroy(surface);
618
619 cairo_scale(panel->plot.cr,
620 1./(double)panel->plot.oversampling_scale,
621 1./(double)panel->plot.oversampling_scale);
622 panel->wxt_cairo_refresh();
623
624 cairo_show_page(panel->plot.cr);
625 cairo_surface_finish(surface);
626 panel->plot.cr = save_cr;
627 cairo_restore(panel->plot.cr);
628 break;
629 #endif
630
631 #ifdef __WXMSW__
632 case 3: {
633 /* Save as Enhanced Metafile. */
634 save_cr = panel->plot.cr;
635 cairo_save(save_cr);
636
637 RECT rect;
638 rect.left = rect.top = 0;
639 unsigned dpi = GetDPI();
640 rect.right = MulDiv(panel->plot.device_xmax, dpi, 10);
641 rect.bottom = MulDiv(panel->plot.device_ymax, dpi, 10);
642 HDC hmf = CreateEnhMetaFileW(NULL, fullpathFilename.wc_str(), &rect, NULL);
643 // The win32_printing surface makes an effort to use the GDI API wherever possible,
644 // which should reduce the file size in many cases.
645 surface = cairo_win32_printing_surface_create(hmf);
646 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
647 fprintf(stderr, "Cairo error: could not create surface for metafile.\n");
648 cairo_surface_destroy(surface);
649 } else {
650 panel->plot.cr = cairo_create(surface);
651 cairo_scale(panel->plot.cr,
652 1. / panel->plot.oversampling_scale,
653 1. / panel->plot.oversampling_scale);
654
655 panel->wxt_cairo_refresh();
656 cairo_show_page(panel->plot.cr);
657 cairo_surface_destroy(surface);
658 cairo_surface_finish(surface);
659 panel->plot.cr = save_cr;
660 cairo_restore(panel->plot.cr);
661 }
662 HENHMETAFILE hemf = CloseEnhMetaFile(hmf);
663 DeleteEnhMetaFile(hemf);
664 break;
665 }
666 #endif
667
668 default :
669 fprintf(stderr, "Can't save in that file type.\n");
670 break;
671 }
672
673 /* Save user environment selections. */
674 userFilterIndex = exportFileDialog.GetFilterIndex();
675 }
676
677 #ifdef WXT_PRINT
678 /* toolbar event : Print
679 */
OnPrint(wxCommandEvent & WXUNUSED (event))680 void wxtFrame::OnPrint( wxCommandEvent& WXUNUSED( event ) )
681 {
682 wxPrintDialogData printDialogData(printData);
683 printDialogData.EnablePageNumbers(false);
684 wxPrintDialog printDialog(this, &printDialogData);
685
686 if (printDialog.ShowModal() == wxID_CANCEL)
687 return;
688
689 wxDC* wxdc = printDialog.GetPrintDC();
690 wxdc->StartDoc(GetTitle());
691 wxdc->StartPage();
692
693 cairo_t* save_cr = panel->plot.cr;
694 cairo_save(save_cr);
695 cairo_surface_t *surface = NULL;
696 #ifdef _WIN32
697 HDC hdc = (HDC) wxdc->GetHDC();
698 surface = cairo_win32_printing_surface_create(hdc);
699 #endif
700 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
701 fprintf(stderr, "Cairo error: could not create surface for printer.\n");
702 cairo_surface_destroy(surface);
703 } else {
704 panel->plot.cr = cairo_create(surface);
705 // scale the plot according to the ratio of printer and screen dpi
706 wxSize ppi = wxdc->GetPPI();
707 unsigned dpi = 96;
708 #ifdef _WIN32
709 dpi = GetDPI();
710 #endif
711 double scaleX = ppi.GetWidth() / (double) dpi;
712 double scaleY = ppi.GetHeight() / (double) dpi;
713 cairo_surface_set_fallback_resolution(surface, ppi.GetWidth(), ppi.GetHeight());
714 cairo_scale(panel->plot.cr,
715 scaleX / panel->plot.oversampling_scale,
716 scaleY / panel->plot.oversampling_scale);
717 panel->wxt_cairo_refresh();
718 cairo_show_page(panel->plot.cr);
719 cairo_surface_destroy(surface);
720 cairo_surface_finish(surface);
721
722 panel->plot.cr = save_cr;
723 cairo_restore(panel->plot.cr);
724 }
725 wxdc->EndPage();
726 wxdc->EndDoc();
727 delete wxdc;
728 }
729 #endif
730
731 /* toolbar event : Copy to clipboard
732 * We will copy the panel to a bitmap, using platform-independant wxWidgets functions */
OnCopy(wxCommandEvent & WXUNUSED (event))733 void wxtFrame::OnCopy( wxCommandEvent& WXUNUSED( event ) )
734 {
735 FPRINTF((stderr,"Copy to clipboard\n"));
736 int width = panel->plot.device_xmax, height = panel->plot.device_ymax;
737 wxBitmap cp_bitmap(width,height);
738 wxMemoryDC cp_dc;
739 wxClientDC dc(panel);
740
741 cp_dc.SelectObject(cp_bitmap);
742 cp_dc.Blit(0,0,width,height,&dc,0,0);
743 cp_dc.SelectObject(wxNullBitmap);
744
745 wxTheClipboard->UsePrimarySelection(false);
746 /* SetData clears the clipboard */
747 if ( wxTheClipboard->Open() ) {
748 wxTheClipboard->SetData(new wxBitmapDataObject(cp_bitmap));
749 wxTheClipboard->Close();
750 }
751 wxTheClipboard->Flush();
752 }
753
754 #ifdef USE_MOUSE
755 /* toolbar event : Replot */
OnReplot(wxCommandEvent & WXUNUSED (event))756 void wxtFrame::OnReplot( wxCommandEvent& WXUNUSED( event ) )
757 {
758 wxt_exec_event(GE_keypress, 0, 0, 'e' , 0, this->GetId());
759 }
760
761 /* toolbar event : Toggle Grid */
OnToggleGrid(wxCommandEvent & WXUNUSED (event))762 void wxtFrame::OnToggleGrid( wxCommandEvent& WXUNUSED( event ) )
763 {
764 wxt_exec_event(GE_keypress, 0, 0, 'g', 0, this->GetId());
765 }
766
767 /* toolbar event : Previous Zoom in history */
OnZoomPrevious(wxCommandEvent & WXUNUSED (event))768 void wxtFrame::OnZoomPrevious( wxCommandEvent& WXUNUSED( event ) )
769 {
770 wxt_exec_event(GE_keypress, 0, 0, 'p', 0, this->GetId());
771 }
772
773 /* toolbar event : Next Zoom in history */
OnZoomNext(wxCommandEvent & WXUNUSED (event))774 void wxtFrame::OnZoomNext( wxCommandEvent& WXUNUSED( event ) )
775 {
776 wxt_exec_event(GE_keypress, 0, 0, 'n', 0, this->GetId());
777 }
778
779 /* toolbar event : Autoscale */
OnAutoscale(wxCommandEvent & WXUNUSED (event))780 void wxtFrame::OnAutoscale( wxCommandEvent& WXUNUSED( event ) )
781 {
782 wxt_exec_event(GE_keypress, 0, 0, 'a', 0, this->GetId());
783 }
784
785 /* set the status text from the event data */
OnSetStatusText(wxCommandEvent & event)786 void wxtFrame::OnSetStatusText( wxCommandEvent& event )
787 {
788 SetStatusText(event.GetString());
789 }
790 #endif /*USE_MOUSE*/
791
792 /* toolbar event : Config */
OnConfig(wxCommandEvent & WXUNUSED (event))793 void wxtFrame::OnConfig( wxCommandEvent& WXUNUSED( event ) )
794 {
795 /* if we have already opened a dialog, just raise it */
796 if (config_displayed) {
797 config_dialog->Raise();
798 return;
799 }
800
801 /* otherwise, open a dialog */
802 config_displayed = true;
803 config_dialog = new wxtConfigDialog(this);
804 config_dialog->Show(true);
805 }
806
807
808 /* toolbar event : Help */
OnHelp(wxCommandEvent & WXUNUSED (event))809 void wxtFrame::OnHelp( wxCommandEvent& WXUNUSED( event ) )
810 {
811 wxMessageBox( wxString(wxT("You are using an interactive terminal based on ")
812 #ifdef WXT_MULTITHREADED
813 wxT("multithreaded ")
814 #endif
815 wxT("wxWidgets for the interface, Cairo ")
816 wxT("for the drawing facilities, and Pango for the text layouts.\n")
817 wxT("Please note that toolbar icons in the terminal ")
818 wxT("don't reflect the whole range of mousing ")
819 wxT("possibilities in the terminal.\n")
820 wxT("Hit 'h' in the plot window ")
821 wxT("and a help message for mouse commands ")
822 wxT("will appear in the gnuplot console.\n")
823 wxT("See also 'help mouse'.\n")),
824 wxT("wxWidgets terminal help"), wxOK | wxICON_INFORMATION, this );
825 }
826
827 /* called on Close() (menu or window manager) */
OnClose(wxCloseEvent & event)828 void wxtFrame::OnClose( wxCloseEvent& event )
829 {
830 FPRINTF((stderr,"OnClose\n"));
831 if ( event.CanVeto() && !wxt_handling_persist) {
832 /* Default behaviour when Quit is clicked, or the window cross X */
833 event.Veto();
834 this->Hide();
835 #ifdef USE_MOUSE
836 /* Pass it through mouse handling to check for "bind Close" */
837 wxt_exec_event(GE_reset, 0, 0, 0, 0, this->GetId());
838 #endif /* USE_MOUSE */
839 }
840 else /* in "persist" mode */ {
841 /* declare the iterator */
842 std::vector<wxt_window_t>::iterator wxt_iter;
843
844 for(wxt_iter = wxt_window_list.begin();
845 wxt_iter != wxt_window_list.end(); wxt_iter++)
846 {
847 if (this == wxt_iter->frame) {
848 wxt_window_list.erase(wxt_iter);
849 break;
850 }
851 }
852 this->Destroy();
853 }
854
855 #if defined(_WIN32) && !defined(WGP_CONSOLE)
856 /* Close text window if this was the last plot window. */
857 WinPersistTextClose();
858 #endif
859 }
860
861 /* when the window is resized,
862 * resize the panel to fit in the frame.
863 * If the tool widget setting for "redraw on resize" is set, replot in new size.
864 * FIXME : Loses all but most recent component of a multiplot.
865 */
OnSize(wxSizeEvent & event)866 void wxtFrame::OnSize( wxSizeEvent& event )
867 {
868 FPRINTF((stderr,"frame OnSize\n"));
869
870 /* Under Windows the frame receives an OnSize event before being completely initialized.
871 * So we must check for the panel to be properly initialized before.*/
872 if (panel)
873 panel->SetSize( this->GetClientSize() );
874 #ifdef __WXOSX_COCOA__
875 /* wx 2.9 Cocoa bug workaround, that does not adjust layout for status bar on resize */
876 PositionStatusBar();
877 #endif
878
879 /* Note: On some platforms OnSize() might get called before the settings have been initialized in wxt_init(). */
880 if (wxt_redraw == yes)
881 wxt_exec_event(GE_replot, 0, 0, 0 , 0, this->GetId());
882 }
883
884 /* wrapper for AddPendingEvent or ProcessEvent */
SendEvent(wxEvent & event)885 void wxtFrame::SendEvent( wxEvent &event)
886 {
887 #ifdef WXT_MULTITHREADED
888 AddPendingEvent(event);
889 #else /* !WXT_MULTITHREADED */
890 ProcessEvent(event);
891 #endif /* !WXT_MULTITHREADED */
892 }
893
894 /* ---------------------------------------------------------------------------
895 * Panel : the space used for the plot, between the toolbar and the statusbar
896 * ----------------------------------------------------------------------------*/
897
898 /* panel constructor
899 * Note : under Windows, wxDefaultPosition makes the panel hide the toolbar */
wxtPanel(wxWindow * parent,wxWindowID id,const wxSize & size)900 wxtPanel::wxtPanel( wxWindow *parent, wxWindowID id, const wxSize& size )
901 : wxPanel( parent, id, wxPoint(0,0) /*wxDefaultPosition*/, size, wxWANTS_CHARS )
902 {
903 FPRINTF((stderr,"panel constructor\n"));
904
905 /* initialisations */
906 gp_cairo_initialize_plot(&plot);
907 GetSize(&(plot.device_xmax),&(plot.device_ymax));
908 plot.polygons_saturate = TRUE;
909
910 settings_queued = false;
911
912 #ifdef USE_MOUSE
913 mouse_x = 0;
914 mouse_y = 0;
915 wxt_zoombox = false;
916 zoom_x1 = 0;
917 zoom_y1 = 0;
918 zoom_string1 = wxT("");
919 zoom_string2 = wxT("");
920
921 wxt_ruler = false;
922 wxt_ruler_lineto = false;
923 wxt_ruler_x = 0;
924 wxt_ruler_y = 0;
925
926 modifier_mask = 0;
927 #endif /*USE_MOUSE*/
928
929 #if defined(GTK_SURFACE)
930 gdkpixmap = NULL;
931 #elif defined(__WXMSW__)
932 #else /* IMAGE_SURFACE */
933 cairo_bitmap = NULL;
934 data32 = NULL;
935 #endif /* SURFACE */
936
937 FPRINTF((stderr,"panel constructor4\n"));
938
939 /* create the device context to be drawn */
940 wxt_cairo_create_context();
941
942 FPRINTF((stderr,"panel constructor5\n"));
943
944 #ifdef IMAGE_SURFACE
945 wxt_cairo_create_bitmap();
946 #endif
947 FPRINTF((stderr,"panel constructor6\n"));
948 }
949
950
951 /* destructor */
~wxtPanel()952 wxtPanel::~wxtPanel()
953 {
954 FPRINTF((stderr,"panel destructor\n"));
955 wxt_cairo_free_context();
956
957 /* clear the command list, free the allocated memory */
958 ClearCommandlist();
959 }
960
961 /* temporary store new settings values to be applied for the next plot */
wxt_settings_queue(TBOOLEAN antialiasing,TBOOLEAN oversampling,int hinting_setting)962 void wxtPanel::wxt_settings_queue(TBOOLEAN antialiasing,
963 TBOOLEAN oversampling,
964 int hinting_setting)
965 {
966 mutex_queued.Lock();
967 settings_queued = true;
968 antialiasing_queued = antialiasing;
969 oversampling_queued = oversampling;
970 hinting_queued = hinting_setting;
971 mutex_queued.Unlock();
972 }
973
974 /* apply queued settings */
wxt_settings_apply()975 void wxtPanel::wxt_settings_apply()
976 {
977 mutex_queued.Lock();
978 if (settings_queued) {
979 plot.antialiasing = antialiasing_queued;
980 plot.oversampling = oversampling_queued;
981 plot.hinting = hinting_queued;
982 settings_queued = false;
983 }
984 mutex_queued.Unlock();
985 }
986
987 /* clear the command list, free the allocated memory */
ClearCommandlist()988 void wxtPanel::ClearCommandlist()
989 {
990 command_list_mutex.Lock();
991
992 command_list_t::iterator iter; /*declare the iterator*/
993
994 /* run through the list, and free allocated memory */
995 for(iter = command_list.begin(); iter != command_list.end(); ++iter) {
996 if ( iter->command == command_put_text ||
997 iter->command == command_hypertext ||
998 iter->command == command_set_font)
999 delete[] iter->string;
1000 if (iter->command == command_enhanced_open)
1001 delete[] iter->string;
1002 if (iter->command == command_filled_polygon)
1003 delete[] iter->corners;
1004 if (iter->command == command_image)
1005 free(iter->image);
1006 if (iter->command == command_dashtype)
1007 free(iter->dashpattern);
1008 }
1009
1010 command_list.clear();
1011 command_list_mutex.Unlock();
1012 }
1013
1014
1015 /* method called when the panel has to be painted
1016 * -> Refresh(), window dragged, dialogs over the window, etc. */
OnPaint(wxPaintEvent & WXUNUSED (event))1017 void wxtPanel::OnPaint( wxPaintEvent &WXUNUSED(event) )
1018 {
1019 /* Constructor of the device context */
1020 wxPaintDC dc(this);
1021 DrawToDC(dc, GetUpdateRegion());
1022 }
1023
1024 /* same as OnPaint, but can be directly called by a user function */
Draw()1025 void wxtPanel::Draw()
1026 {
1027 wxClientDC dc(this);
1028 wxBufferedDC buffered_dc(&dc, wxSize(plot.device_xmax, plot.device_ymax));
1029 wxRegion region(0, 0, plot.device_xmax, plot.device_ymax);
1030 DrawToDC(buffered_dc, region);
1031 }
1032
1033 /* copy the plot to the panel, draw zoombow and ruler needed */
DrawToDC(wxDC & dc,wxRegion & region)1034 void wxtPanel::DrawToDC(wxDC &dc, wxRegion ®ion)
1035 {
1036 wxPen tmp_pen;
1037
1038 /* TODO extend the region mechanism to surfaces other than GTK_SURFACE */
1039 #ifdef GTK_SURFACE
1040 wxRegionIterator upd(region);
1041 int vX,vY,vW,vH; /* Dimensions of client area in pixels */
1042
1043 while (upd) {
1044 vX = upd.GetX();
1045 vY = upd.GetY();
1046 vW = upd.GetW();
1047 vH = upd.GetH();
1048
1049 FPRINTF((stderr,"OnPaint %d,%d,%d,%d\n",vX,vY,vW,vH));
1050 /* Repaint this rectangle */
1051 if (gdkpixmap)
1052 gdk_draw_drawable(dc.GetWindow(),
1053 dc.m_penGC,
1054 gdkpixmap,
1055 vX,vY,
1056 vX,vY,
1057 vW,vH);
1058 ++upd;
1059 }
1060 #elif defined(__WXMSW__)
1061 // Need to flush to make sure the bitmap is fully drawn.
1062 cairo_surface_flush(cairo_get_target(plot.cr));
1063 BitBlt((HDC) dc.GetHDC(), 0, 0, plot.device_xmax, plot.device_ymax, cairo_win32_surface_get_dc(cairo_get_target(plot.cr)), 0, 0, SRCCOPY);
1064 #else
1065 dc.DrawBitmap(*cairo_bitmap, 0, 0, false);
1066 #endif
1067
1068 /* fill in gray when the aspect ratio conservation has let empty space in the panel */
1069 if (plot.device_xmax*plot.ymax > plot.device_ymax*plot.xmax) {
1070 dc.SetPen( *wxTRANSPARENT_PEN );
1071 dc.SetBrush( wxBrush( wxT("LIGHT GREY"), wxSOLID ) );
1072 dc.DrawRectangle((int) (plot.xmax/plot.oversampling_scale*plot.xscale),
1073 0,
1074 plot.device_xmax - (int) (plot.xmax/plot.oversampling_scale*plot.xscale),
1075 plot.device_ymax);
1076 } else if (plot.device_xmax*plot.ymax < plot.device_ymax*plot.xmax) {
1077 dc.SetPen( *wxTRANSPARENT_PEN );
1078 dc.SetBrush( wxBrush( wxT("LIGHT GREY"), wxSOLID ) );
1079 dc.DrawRectangle(0,
1080 (int) (plot.ymax/plot.oversampling_scale*plot.yscale),
1081 plot.device_xmax,
1082 (int) (plot.device_ymax - plot.ymax/plot.oversampling_scale*plot.yscale));
1083 }
1084
1085 #ifdef USE_MOUSE
1086 if (wxt_zoombox) {
1087 tmp_pen = wxPen(wxT("black"), 1, wxSOLID);
1088 tmp_pen.SetCap( wxCAP_ROUND );
1089 dc.SetPen( tmp_pen );
1090 #ifndef __WXOSX_COCOA__
1091 /* wx 2.9 Cocoa bug workaround, which has no logical functions support */
1092 #if (GTK_MAJOR_VERSION < 3)
1093 dc.SetLogicalFunction( wxINVERT );
1094 #endif
1095 #endif
1096 dc.DrawLine( zoom_x1, zoom_y1, mouse_x, zoom_y1 );
1097 dc.DrawLine( mouse_x, zoom_y1, mouse_x, mouse_y );
1098 dc.DrawLine( mouse_x, mouse_y, zoom_x1, mouse_y );
1099 dc.DrawLine( zoom_x1, mouse_y, zoom_x1, zoom_y1 );
1100 dc.SetPen( *wxTRANSPARENT_PEN );
1101 dc.SetBrush( wxBrush( wxT("LIGHT BLUE"), wxSOLID ) );
1102 dc.SetLogicalFunction( wxAND );
1103 dc.DrawRectangle( zoom_x1, zoom_y1, mouse_x -zoom_x1, mouse_y -zoom_y1);
1104 dc.SetLogicalFunction( wxCOPY );
1105
1106 dc.SetFont( wxFont( (int) plot.fontsize, wxFONTFAMILY_DEFAULT,
1107 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false,
1108 wxString(plot.fontname, wxConvLocal) ) );
1109
1110 dc.DrawText( zoom_string1.BeforeFirst(wxT('\r')),
1111 zoom_x1, zoom_y1 - term->v_char/plot.oversampling_scale);
1112 dc.DrawText( zoom_string1.AfterFirst(wxT('\r')),
1113 zoom_x1, zoom_y1);
1114
1115 dc.DrawText( zoom_string2.BeforeFirst(wxT('\r')),
1116 mouse_x, mouse_y - term->v_char/plot.oversampling_scale);
1117 dc.DrawText( zoom_string2.AfterFirst(wxT('\r')),
1118 mouse_x, mouse_y);
1119
1120 /* if we have to redraw the zoombox, it is with another size,
1121 * so it will be issued later and we can disable it now */
1122 wxt_zoombox = false;
1123 }
1124
1125 if (wxt_ruler) {
1126 tmp_pen = wxPen(wxT("black"), 1, wxSOLID);
1127 tmp_pen.SetCap(wxCAP_BUTT);
1128 dc.SetPen( tmp_pen );
1129 #ifndef __WXOSX_COCOA__
1130 /* wx 2.9 Cocoa bug workaround, which has no logical functions support */
1131 #if (GTK_MAJOR_VERSION < 3)
1132 dc.SetLogicalFunction( wxINVERT );
1133 #endif
1134 #endif
1135 #ifdef __WXMSW__
1136 dc.DrawLine(0, (int)wxt_ruler_y, plot.device_xmax, (int)wxt_ruler_y);
1137 dc.DrawLine((int)wxt_ruler_x, 0, (int)wxt_ruler_x, plot.device_ymax);
1138 #else
1139 dc.CrossHair( (int)wxt_ruler_x, (int)wxt_ruler_y );
1140 #endif
1141 dc.SetLogicalFunction( wxCOPY );
1142 }
1143
1144 if (wxt_ruler && wxt_ruler_lineto) {
1145 tmp_pen = wxPen(wxT("black"), 1, wxSOLID);
1146 tmp_pen.SetCap(wxCAP_BUTT);
1147 dc.SetPen( tmp_pen );
1148 #ifndef __WXOSX_COCOA__
1149 /* wx 2.9 Cocoa bug workaround, which has no logical functions support */
1150 #if (GTK_MAJOR_VERSION < 3)
1151 dc.SetLogicalFunction( wxINVERT );
1152 #endif
1153 #endif
1154 dc.DrawLine((int)wxt_ruler_x, (int)wxt_ruler_y, mouse_x, mouse_y);
1155 dc.SetLogicalFunction( wxCOPY );
1156 }
1157 #endif /*USE_MOUSE*/
1158 }
1159
1160 /* avoid flickering under win32 */
OnEraseBackground(wxEraseEvent & WXUNUSED (event))1161 void wxtPanel::OnEraseBackground( wxEraseEvent &WXUNUSED(event) )
1162 {
1163 }
1164
1165 /* avoid leaving cross cursor when leaving window on Mac */
OnMouseLeave(wxMouseEvent & WXUNUSED (event))1166 void wxtPanel::OnMouseLeave( wxMouseEvent &WXUNUSED(event) )
1167 {
1168 #ifdef __WXMAC__
1169 ::wxSetCursor(wxNullCursor);
1170 #endif
1171 }
1172
1173 /* when the window is resized */
OnSize(wxSizeEvent & event)1174 void wxtPanel::OnSize( wxSizeEvent& event )
1175 {
1176 /* don't do anything if term variables are not initialized */
1177 if (plot.xmax == 0 || plot.ymax == 0)
1178 return;
1179
1180 /* update window size, and scaling variables */
1181 GetSize(&(plot.device_xmax),&(plot.device_ymax));
1182
1183 double new_xscale, new_yscale;
1184
1185 new_xscale = ((double) plot.device_xmax)*plot.oversampling_scale/((double) plot.xmax);
1186 new_yscale = ((double) plot.device_ymax)*plot.oversampling_scale/((double) plot.ymax);
1187
1188 /* We will keep the aspect ratio constant */
1189 if (new_yscale < new_xscale) {
1190 plot.xscale = new_yscale;
1191 plot.yscale = new_yscale;
1192 } else {
1193 plot.xscale = new_xscale;
1194 plot.yscale = new_xscale;
1195 }
1196 FPRINTF((stderr,"panel OnSize %d %d %lf %lf\n",
1197 plot.device_xmax, plot.device_ymax, plot.xscale,plot.yscale));
1198
1199 /* create a new cairo context of the good size */
1200 wxt_cairo_create_context();
1201 /* redraw the plot with the new scaling */
1202 wxt_cairo_refresh();
1203 }
1204
1205 #ifdef USE_MOUSE
1206 /* when the mouse is moved over the panel */
OnMotion(wxMouseEvent & event)1207 void wxtPanel::OnMotion( wxMouseEvent& event )
1208 {
1209 /* Get and store mouse position for _put_tmp_text() and key events (ruler) */
1210 mouse_x = event.GetX();
1211 mouse_y = event.GetY();
1212 int xnow = (int)gnuplot_x( &plot, mouse_x );
1213 int ynow = (int)gnuplot_y( &plot, mouse_y );
1214 bool buttondown = event.LeftIsDown() || event.RightIsDown() || event.MiddleIsDown();
1215
1216 UpdateModifiers(event);
1217
1218 /* update the ruler_lineto thing */
1219 if (wxt_ruler && wxt_ruler_lineto)
1220 Draw();
1221
1222 /* informs gnuplot */
1223 wxt_exec_event(GE_motion,
1224 xnow, ynow,
1225 0, 0, this->GetId());
1226
1227 /* Check to see if the mouse is over a hypertext anchor point */
1228 if (wxt_n_anchors > 0 && !buttondown)
1229 wxt_check_for_anchors(xnow, ynow);
1230 }
1231
1232 /* mouse "click" event */
OnLeftDown(wxMouseEvent & event)1233 void wxtPanel::OnLeftDown( wxMouseEvent& event )
1234 {
1235 int x,y;
1236 x = (int) gnuplot_x( &plot, event.GetX() );
1237 y = (int) gnuplot_y( &plot, event.GetY() );
1238
1239 UpdateModifiers(event);
1240 if (wxt_toggle)
1241 wxt_check_for_toggle(x, y);
1242
1243 wxt_exec_event(GE_buttonpress, x, y, 1, 0, this->GetId());
1244 }
1245
1246 /* mouse "click" event */
OnLeftUp(wxMouseEvent & event)1247 void wxtPanel::OnLeftUp( wxMouseEvent& event )
1248 {
1249 int x,y;
1250 x = (int) gnuplot_x( &plot, event.GetX() );
1251 y = (int) gnuplot_y( &plot, event.GetY() );
1252
1253 UpdateModifiers(event);
1254
1255 if ( wxt_exec_event(GE_buttonrelease, x, y, 1,
1256 (int) left_button_sw.Time(), this->GetId()) ) {
1257 /* start a watch to send the time elapsed between up and down */
1258 left_button_sw.Start();
1259
1260 /* EXPERIMENTAL: send associated hypertext to clipboard */
1261 if (wxt_display_hypertext)
1262 wxt_set_clipboard(wxt_display_hypertext);
1263 }
1264 }
1265
1266 /* mouse "click" event */
OnMiddleDown(wxMouseEvent & event)1267 void wxtPanel::OnMiddleDown( wxMouseEvent& event )
1268 {
1269 int x,y;
1270 x = (int) gnuplot_x( &plot, event.GetX() );
1271 y = (int) gnuplot_y( &plot, event.GetY() );
1272
1273 UpdateModifiers(event);
1274
1275 wxt_exec_event(GE_buttonpress, x, y, 2, 0, this->GetId());
1276 }
1277
1278 /* mouse "click" event */
OnMiddleUp(wxMouseEvent & event)1279 void wxtPanel::OnMiddleUp( wxMouseEvent& event )
1280 {
1281 int x,y;
1282 x = (int) gnuplot_x( &plot, event.GetX() );
1283 y = (int) gnuplot_y( &plot, event.GetY() );
1284
1285 UpdateModifiers(event);
1286
1287 if ( wxt_exec_event(GE_buttonrelease, x, y, 2,
1288 (int) middle_button_sw.Time(), this->GetId()) ) {
1289 /* start a watch to send the time elapsed between up and down */
1290 middle_button_sw.Start();
1291 }
1292 }
1293
1294 /* mouse "click" event */
OnRightDown(wxMouseEvent & event)1295 void wxtPanel::OnRightDown( wxMouseEvent& event )
1296 {
1297 int x,y;
1298 x = (int) gnuplot_x( &plot, event.GetX() );
1299 y = (int) gnuplot_y( &plot, event.GetY() );
1300
1301 UpdateModifiers(event);
1302
1303 wxt_exec_event(GE_buttonpress, x, y, 3, 0, this->GetId());
1304 }
1305
1306 /* mouse "click" event */
OnRightUp(wxMouseEvent & event)1307 void wxtPanel::OnRightUp( wxMouseEvent& event )
1308 {
1309 int x,y;
1310 x = (int) gnuplot_x( &plot, event.GetX() );
1311 y = (int) gnuplot_y( &plot, event.GetY() );
1312
1313 UpdateModifiers(event);
1314
1315 if ( wxt_exec_event(GE_buttonrelease, x, y, 3,
1316 (int) right_button_sw.Time(), this->GetId()) ) {
1317 /* start a watch to send the time elapsed between up and down */
1318 right_button_sw.Start();
1319 }
1320 }
1321
1322 /* mouse wheel event */
OnMouseWheel(wxMouseEvent & event)1323 void wxtPanel::OnMouseWheel( wxMouseEvent& event )
1324 {
1325 int mouse_button;
1326 int x,y;
1327
1328 x = (int) gnuplot_x( &plot, event.GetX() );
1329 y = (int) gnuplot_y( &plot, event.GetY() );
1330
1331 UpdateModifiers(event);
1332 mouse_button = (event.GetWheelRotation() > 0 ? 4 : 5);
1333 #if wxCHECK_VERSION(2, 9, 0)
1334 /* GetWheelAxis: 0 is the Y axis, 1 is the X axis. */
1335 if (event.GetWheelAxis() > 0)
1336 mouse_button += 2;
1337 #endif
1338 wxt_exec_event(GE_buttonpress, x, y, mouse_button, 0, this->GetId());
1339 }
1340
1341 /* the state of the modifiers is checked each time a key is pressed instead of
1342 * tracking the press and release events of the modifiers keys, because the
1343 * window manager catches some combinations, like ctrl+F1, and thus we do not
1344 * receive a release event in this case */
UpdateModifiers(wxMouseEvent & event)1345 void wxtPanel::UpdateModifiers( wxMouseEvent& event )
1346 {
1347 int current_modifier_mask = 0;
1348
1349 /* retrieve current modifier mask from the wxEvent */
1350 current_modifier_mask |= (event.AltDown() ? (1<<2) : 0);
1351 current_modifier_mask |= (event.ControlDown() ? (1<<1) : 0);
1352 current_modifier_mask |= (event.ShiftDown() ? (1) : 0);
1353
1354 /* update if changed */
1355 if (modifier_mask != current_modifier_mask) {
1356 modifier_mask = current_modifier_mask;
1357 wxt_exec_event(GE_modifier, 0, 0, modifier_mask, 0, this->GetId());
1358 }
1359 }
1360
1361 /* a key has been pressed, modifiers have already been handled.
1362 * We receive keycodes here, and we send corresponding events to gnuplot main thread */
OnKeyDownChar(wxKeyEvent & event)1363 void wxtPanel::OnKeyDownChar( wxKeyEvent &event )
1364 {
1365 int keycode = event.GetKeyCode();
1366 int gp_keycode;
1367
1368 /* this is the same code as in UpdateModifiers(), but the latter method cannot be
1369 * used here because wxKeyEvent and wxMouseEvent are different classes, both of them
1370 * derive from wxEvent, but wxEvent does not have the necessary AltDown() and friends */
1371 int current_modifier_mask = 0;
1372
1373 /* retrieve current modifier mask from the wxEvent */
1374 current_modifier_mask |= (event.AltDown() ? (1<<2) : 0);
1375 current_modifier_mask |= (event.ControlDown() ? (1<<1) : 0);
1376 current_modifier_mask |= (event.ShiftDown() ? (1) : 0);
1377
1378 /* update if changed */
1379 if (modifier_mask != current_modifier_mask) {
1380 modifier_mask = current_modifier_mask;
1381 wxt_exec_event(GE_modifier, 0, 0, modifier_mask, 0, this->GetId());
1382 }
1383
1384 #define WXK_GPKEYCODE(wxkey,kcode) case wxkey : gp_keycode=kcode; break;
1385
1386 if (keycode<256) {
1387 // event.GetKeyCode() already applied <control> and <shift>
1388 // we need to undo that for normal alphabetic characters
1389 // before sending the character to event_keypress()
1390 if (event.ControlDown() && (1 <= keycode && keycode <= 26))
1391
1392 #ifdef wxHAS_RAW_KEY_CODES
1393 // <ctrl><tab> is distinguishable from <ctrl>i only via
1394 // the raw key code. There are other weird ones as well,
1395 // but TAB is the one most likely to cause problems.
1396 if (!(keycode == WXK_TAB && ((0xff00 & event.GetRawKeyCode()) != 0)))
1397 #endif
1398 {
1399 if (event.ShiftDown())
1400 keycode += 'A' - 1;
1401 else
1402 keycode += 'a' - 1;
1403 }
1404
1405
1406 switch (keycode) {
1407
1408 #ifndef DISABLE_SPACE_RAISES_CONSOLE
1409 case WXK_SPACE :
1410 if ((wxt_ctrl==yes && event.ControlDown())
1411 || wxt_ctrl!=yes) {
1412 RaiseConsoleWindow();
1413 return;
1414 } else {
1415 gp_keycode = ' ';
1416 break;
1417 }
1418 #endif /* DISABLE_SPACE_RAISES_CONSOLE */
1419
1420 case 'q' :
1421 /* ctrl+q does not send 113 but 17 */
1422 case 17 :
1423 if ((wxt_ctrl==yes && event.ControlDown())
1424 || wxt_ctrl!=yes) {
1425 /* closes terminal window */
1426 this->GetParent()->Close(false);
1427 return;
1428 } else {
1429 gp_keycode = 'q';
1430 break;
1431 }
1432 WXK_GPKEYCODE(WXK_BACK,GP_BackSpace);
1433 WXK_GPKEYCODE(WXK_TAB,GP_Tab);
1434 WXK_GPKEYCODE(WXK_RETURN,GP_Return);
1435 WXK_GPKEYCODE(WXK_ESCAPE,GP_Escape);
1436 WXK_GPKEYCODE(WXK_DELETE,GP_Delete);
1437 default : gp_keycode = keycode; break; /* exact solution */
1438 }
1439 } else {
1440 switch( keycode ) {
1441 WXK_GPKEYCODE(WXK_PAUSE,GP_Pause);
1442 WXK_GPKEYCODE(WXK_SCROLL,GP_Scroll_Lock);
1443 WXK_GPKEYCODE(WXK_INSERT,GP_Insert);
1444 WXK_GPKEYCODE(WXK_HOME,GP_Home);
1445 WXK_GPKEYCODE(WXK_LEFT,GP_Left);
1446 WXK_GPKEYCODE(WXK_UP,GP_Up);
1447 WXK_GPKEYCODE(WXK_RIGHT,GP_Right);
1448 WXK_GPKEYCODE(WXK_DOWN,GP_Down);
1449 WXK_GPKEYCODE(WXK_PAGEUP,GP_PageUp);
1450 WXK_GPKEYCODE(WXK_PAGEDOWN,GP_PageDown);
1451 WXK_GPKEYCODE(WXK_END,GP_End);
1452 WXK_GPKEYCODE(WXK_NUMPAD_SPACE,GP_KP_Space);
1453 WXK_GPKEYCODE(WXK_NUMPAD_TAB,GP_KP_Tab);
1454 WXK_GPKEYCODE(WXK_NUMPAD_ENTER,GP_KP_Enter);
1455 WXK_GPKEYCODE(WXK_NUMPAD_F1,GP_KP_F1);
1456 WXK_GPKEYCODE(WXK_NUMPAD_F2,GP_KP_F2);
1457 WXK_GPKEYCODE(WXK_NUMPAD_F3,GP_KP_F3);
1458 WXK_GPKEYCODE(WXK_NUMPAD_F4,GP_KP_F4);
1459
1460 WXK_GPKEYCODE(WXK_NUMPAD_INSERT,GP_KP_Insert);
1461 WXK_GPKEYCODE(WXK_NUMPAD_END,GP_KP_End);
1462 WXK_GPKEYCODE(WXK_NUMPAD_DOWN,GP_KP_Down);
1463 WXK_GPKEYCODE(WXK_NUMPAD_PAGEDOWN,GP_KP_Page_Down);
1464 WXK_GPKEYCODE(WXK_NUMPAD_LEFT,GP_KP_Left);
1465 WXK_GPKEYCODE(WXK_NUMPAD_BEGIN,GP_KP_Begin);
1466 WXK_GPKEYCODE(WXK_NUMPAD_RIGHT,GP_KP_Right);
1467 WXK_GPKEYCODE(WXK_NUMPAD_HOME,GP_KP_Home);
1468 WXK_GPKEYCODE(WXK_NUMPAD_UP,GP_KP_Up);
1469 WXK_GPKEYCODE(WXK_NUMPAD_PAGEUP,GP_KP_Page_Up);
1470
1471 WXK_GPKEYCODE(WXK_NUMPAD_DELETE,GP_KP_Delete);
1472 WXK_GPKEYCODE(WXK_NUMPAD_EQUAL,GP_KP_Equal);
1473 WXK_GPKEYCODE(WXK_NUMPAD_MULTIPLY,GP_KP_Multiply);
1474 WXK_GPKEYCODE(WXK_NUMPAD_ADD,GP_KP_Add);
1475 WXK_GPKEYCODE(WXK_NUMPAD_SEPARATOR,GP_KP_Separator);
1476 WXK_GPKEYCODE(WXK_NUMPAD_SUBTRACT,GP_KP_Subtract);
1477 WXK_GPKEYCODE(WXK_NUMPAD_DECIMAL,GP_KP_Decimal);
1478 WXK_GPKEYCODE(WXK_NUMPAD_DIVIDE,GP_KP_Divide);
1479 WXK_GPKEYCODE(WXK_NUMPAD0,GP_KP_0);
1480 WXK_GPKEYCODE(WXK_NUMPAD1,GP_KP_1);
1481 WXK_GPKEYCODE(WXK_NUMPAD2,GP_KP_2);
1482 WXK_GPKEYCODE(WXK_NUMPAD3,GP_KP_3);
1483 WXK_GPKEYCODE(WXK_NUMPAD4,GP_KP_4);
1484 WXK_GPKEYCODE(WXK_NUMPAD5,GP_KP_5);
1485 WXK_GPKEYCODE(WXK_NUMPAD6,GP_KP_6);
1486 WXK_GPKEYCODE(WXK_NUMPAD7,GP_KP_7);
1487 WXK_GPKEYCODE(WXK_NUMPAD8,GP_KP_8);
1488 WXK_GPKEYCODE(WXK_NUMPAD9,GP_KP_9);
1489 WXK_GPKEYCODE(WXK_F1,GP_F1);
1490 WXK_GPKEYCODE(WXK_F2,GP_F2);
1491 WXK_GPKEYCODE(WXK_F3,GP_F3);
1492 WXK_GPKEYCODE(WXK_F4,GP_F4);
1493 WXK_GPKEYCODE(WXK_F5,GP_F5);
1494 WXK_GPKEYCODE(WXK_F6,GP_F6);
1495 WXK_GPKEYCODE(WXK_F7,GP_F7);
1496 WXK_GPKEYCODE(WXK_F8,GP_F8);
1497 WXK_GPKEYCODE(WXK_F9,GP_F9);
1498 WXK_GPKEYCODE(WXK_F10,GP_F10);
1499 WXK_GPKEYCODE(WXK_F11,GP_F11);
1500 WXK_GPKEYCODE(WXK_F12,GP_F12);
1501 default : return; /* probably not ideal */
1502 }
1503 }
1504
1505 /* only send char events to gnuplot if we are the active window */
1506 if ( wxt_exec_event(GE_keypress, (int) gnuplot_x( &plot, mouse_x ),
1507 (int) gnuplot_y( &plot, mouse_y ),
1508 gp_keycode, 0, this->GetId()) ) {
1509 FPRINTF((stderr,"sending char event\n"));
1510 }
1511
1512 /* The following wxWidgets keycodes are not mapped :
1513 * WXK_ALT, WXK_CONTROL, WXK_SHIFT,
1514 * WXK_LBUTTON, WXK_RBUTTON, WXK_CANCEL, WXK_MBUTTON,
1515 * WXK_CLEAR, WXK_MENU,
1516 * WXK_NUMPAD_PRIOR, WXK_NUMPAD_NEXT,
1517 * WXK_CAPITAL, WXK_PRIOR, WXK_NEXT, WXK_SELECT,
1518 * WXK_PRINT, WXK_EXECUTE, WXK_SNAPSHOT, WXK_HELP,
1519 * WXK_MULTIPLY, WXK_ADD, WXK_SEPARATOR, WXK_SUBTRACT,
1520 * WXK_DECIMAL, WXK_DIVIDE, WXK_NUMLOCK, WXK_WINDOWS_LEFT,
1521 * WXK_WINDOWS_RIGHT, WXK_WINDOWS_MENU, WXK_COMMAND
1522 * The following gnuplot keycodes are not mapped :
1523 * GP_Linefeed, GP_Clear, GP_Sys_Req, GP_Begin
1524 */
1525 }
1526
1527 /* --------------------------------------------------------
1528 * Bookkeeping for clickable hot spots and hypertext anchors
1529 * --------------------------------------------------------*/
1530
1531 /* Initialize boxes starting from i */
wxt_initialize_key_boxes(int i)1532 static void wxt_initialize_key_boxes(int i)
1533 {
1534 for (; i<wxt_max_key_boxes; i++) {
1535 wxt_key_boxes[i].left = wxt_key_boxes[i].ybot = INT_MAX;
1536 wxt_key_boxes[i].right = wxt_key_boxes[i].ytop = 0;
1537 }
1538 }
1539
wxt_initialize_hidden(int i)1540 static void wxt_initialize_hidden(int i)
1541 {
1542 for (; i<wxt_max_key_boxes; i++)
1543 wxt_key_boxes[i].hidden = FALSE;
1544 }
1545
1546
1547 /* Update the box enclosing the key sample for the current plot
1548 * so that later we can detect mouse clicks in that area
1549 */
wxt_update_key_box(unsigned int x,unsigned int y)1550 static void wxt_update_key_box( unsigned int x, unsigned int y )
1551 {
1552 if (wxt_max_key_boxes <= wxt_cur_plotno) {
1553 wxt_max_key_boxes = wxt_cur_plotno + 10;
1554 wxt_key_boxes = (wxtBoundingBox *)realloc(wxt_key_boxes,
1555 wxt_max_key_boxes * sizeof(wxtBoundingBox));
1556 wxt_initialize_key_boxes(wxt_cur_plotno);
1557 wxt_initialize_hidden(wxt_cur_plotno);
1558 }
1559 wxtBoundingBox *bb = &(wxt_key_boxes[wxt_cur_plotno]);
1560 y = term->ymax - y;
1561 if (x < bb->left) bb->left = x;
1562 if (x > bb->right) bb->right = x;
1563 if (y < bb->ybot) bb->ybot = y;
1564 if (y > bb->ytop) bb->ytop = y;
1565 }
1566
1567 /* Keep a list of hypertext anchor points so that we can
1568 * detect if the mouse is hovering over one of them.
1569 * Called when we build the display list, not when we execute it.
1570 */
wxt_update_anchors(unsigned int x,unsigned int y,unsigned int size)1571 static void wxt_update_anchors( unsigned int x, unsigned int y, unsigned int size )
1572 {
1573 if (wxt_n_anchors >= wxt_max_anchors) {
1574 wxt_max_anchors += 10;
1575 wxt_anchors = (wxtAnchorPoint *)realloc(wxt_anchors,
1576 wxt_max_anchors * sizeof(wxtAnchorPoint));
1577 }
1578 wxt_anchors[wxt_n_anchors].x = x;
1579 wxt_anchors[wxt_n_anchors].y = y;
1580 wxt_anchors[wxt_n_anchors].size = size;
1581 wxt_n_anchors++;
1582 }
1583
1584 /* Called from wxtPanel::OnLeftDown
1585 * If the mouse click was on top of a key sample then toggle the
1586 * corresponding plot on/off
1587 */
wxt_check_for_toggle(unsigned int x,unsigned int y)1588 static void wxt_check_for_toggle(unsigned int x, unsigned int y)
1589 {
1590 int i;
1591 for (i=1; i<=wxt_cur_plotno && i<wxt_max_key_boxes; i++) {
1592 if (wxt_key_boxes[i].left == INT_MAX)
1593 continue;
1594 if (x < wxt_key_boxes[i].left)
1595 continue;
1596 if (x > wxt_key_boxes[i].right)
1597 continue;
1598 if (y < wxt_key_boxes[i].ybot)
1599 continue;
1600 if (y > wxt_key_boxes[i].ytop)
1601 continue;
1602 wxt_key_boxes[i].hidden = !wxt_key_boxes[i].hidden;
1603 wxt_current_panel->wxt_cairo_refresh();
1604
1605 }
1606 }
1607
1608 /* Called from wxtPanel::OnMotion
1609 * If the mouse is hovering over a hypertext anchor point,
1610 * trigger a refresh so that the text box will be drawn.
1611 */
wxt_check_for_anchors(unsigned int x,unsigned int y)1612 static void wxt_check_for_anchors(unsigned int x, unsigned int y)
1613 {
1614 int i;
1615 TBOOLEAN refresh = FALSE;
1616 for (i=0; i<wxt_n_anchors; i++) {
1617 if ((abs((int)x - (int)wxt_anchors[i].x) < (int)wxt_anchors[i].size)
1618 && (abs((int)y - (int)wxt_anchors[i].y) < (int)wxt_anchors[i].size)) {
1619 refresh = TRUE;
1620 }
1621 }
1622 /* FIXME: Surely we can be more clever than refreshing every time! */
1623 if (refresh)
1624 wxt_current_panel->wxt_cairo_refresh();
1625 }
1626
1627 #endif /*USE_MOUSE*/
1628
1629 #ifndef DISABLE_SPACE_RAISES_CONSOLE
1630 /* ====license information====
1631 * The following code originates from other gnuplot files,
1632 * and is not subject to the alternative license statement.
1633 */
1634
1635
1636 /* FIXME : this code should be deleted, and the feature removed or handled differently,
1637 * because it is highly platform-dependant, is not reliable because
1638 * of a lot of factors (WINDOWID not set, multiple tabs in gnome-terminal, mechanisms
1639 * to prevent focus stealing) and is inconsistent with global bindings mechanism ) */
RaiseConsoleWindow()1640 void wxtPanel::RaiseConsoleWindow()
1641 {
1642 #if defined(USE_GTK) && (GTK_MAJOR_VERSION == 2)
1643 char *window_env;
1644 unsigned long windowid = 0;
1645 /* retrieve XID of gnuplot window */
1646 window_env = getenv("WINDOWID");
1647 if (window_env)
1648 sscanf(window_env, "%lu", &windowid);
1649
1650 #ifdef USE_KDE3_DCOP
1651 /* NOTE: This code uses DCOP, a KDE3 mechanism that no longer exists in KDE4 */
1652 char *ptr = getenv("KONSOLE_DCOP_SESSION"); /* Try KDE's Konsole first. */
1653 if (ptr) {
1654 /* We are in KDE's Konsole, or in a terminal window detached from a Konsole.
1655 * In order to active a tab:
1656 * 1. get environmental variable KONSOLE_DCOP_SESSION: it includes konsole id and session name
1657 * 2. if
1658 * $WINDOWID is defined and it equals
1659 * `dcop konsole-3152 konsole-mainwindow#1 getWinID`
1660 * (KDE 3.2) or when $WINDOWID is undefined (KDE 3.1), then run commands
1661 * dcop konsole-3152 konsole activateSession session-2; \
1662 * dcop konsole-3152 konsole-mainwindow#1 raise
1663 * Note: by $WINDOWID we mean gnuplot's text console WINDOWID.
1664 * Missing: focus is not transferred unless $WINDOWID is defined (should be fixed in KDE 3.2).
1665 *
1666 * Implementation and tests on KDE 3.1.4: Petr Mikulik.
1667 */
1668 char *konsole_name = NULL;
1669 char *cmd = NULL;
1670 /* use 'while' to easily break out (aka catch exception) */
1671 while (1) {
1672 char *konsole_tab;
1673 unsigned long w;
1674 FILE *p;
1675 ptr = strchr(ptr, '(');
1676 /* the string for tab nb 4 looks like 'DCOPRef(konsole-2866, session-4)' */
1677 if (!ptr) break;
1678 konsole_name = strdup(ptr+1);
1679 konsole_tab = strchr(konsole_name, ',');
1680 if (!konsole_tab) break;
1681 *konsole_tab++ = 0;
1682 ptr = strchr(konsole_tab, ')');
1683 if (ptr) *ptr = 0;
1684 cmd = (char*) malloc(strlen(konsole_name) + strlen(konsole_tab) + 64);
1685 sprintf(cmd, "dcop %s konsole-mainwindow#1 getWinID 2>/dev/null", konsole_name);
1686 /* is 2>/dev/null portable among various shells? */
1687 p = popen(cmd, "r");
1688 if (p) {
1689 fscanf(p, "%lu", &w);
1690 pclose(p);
1691 }
1692 if (windowid) { /* $WINDOWID is known */
1693 if (w != windowid) break;
1694 /* `dcop getWinID`==$WINDOWID thus we are running in a window detached from Konsole */
1695 } else {
1696 windowid = w;
1697 /* $WINDOWID has not been known (KDE 3.1), thus set it up */
1698 }
1699 sprintf(cmd, "dcop %s konsole activateSession %s", konsole_name, konsole_tab);
1700 system(cmd);
1701 }
1702 if (konsole_name) free(konsole_name);
1703 if (cmd) free(cmd);
1704 }
1705 #endif /* USE_KDE3_DCOP */
1706
1707 /* now test for GNOME multitab console */
1708 /* ... if somebody bothers to implement it ... */
1709 /* we are not running in any known (implemented) multitab console */
1710
1711 if (windowid) {
1712 gdk_window_raise(gdk_window_foreign_new(windowid));
1713 gdk_window_focus(gdk_window_foreign_new(windowid), GDK_CURRENT_TIME);
1714 }
1715 #endif /* USE_GTK */
1716
1717 #ifdef _WIN32
1718 WinRaiseConsole();
1719 #endif
1720
1721 #ifdef OS2
1722 /* we assume that the console window is managed by PM, not by a X server */
1723 HSWITCH hSwitch = 0;
1724 SWCNTRL swGnu;
1725 HWND hw;
1726 /* get details of command-line window */
1727 hSwitch = WinQuerySwitchHandle(0, getpid());
1728 WinQuerySwitchEntry(hSwitch, &swGnu);
1729 hw = WinQueryWindow(swGnu.hwnd, QW_BOTTOM);
1730 WinSetFocus(HWND_DESKTOP, hw);
1731 WinSwitchToProgram(hSwitch);
1732 #endif /* OS2 */
1733 }
1734
1735 /* ====license information====
1736 * End of the non-relicensable portion.
1737 */
1738 #endif /* DISABLE_SPACE_RAISES_CONSOLE */
1739
1740
1741 /* ------------------------------------------------------
1742 * Configuration dialog
1743 * ------------------------------------------------------*/
1744
1745 /* configuration dialog : handler for a close event */
OnClose(wxCloseEvent & WXUNUSED (event))1746 void wxtConfigDialog::OnClose( wxCloseEvent& WXUNUSED( event ) )
1747 {
1748 wxtFrame *parent = (wxtFrame *) GetParent();
1749 parent->config_displayed = false;
1750 this->Destroy();
1751 }
1752
1753 /* configuration dialog : handler for a button event */
OnButton(wxCommandEvent & event)1754 void wxtConfigDialog::OnButton( wxCommandEvent& event )
1755 {
1756 TBOOLEAN antialiasing;
1757 TBOOLEAN oversampling;
1758
1759 wxConfigBase *pConfig = wxConfigBase::Get();
1760 Validate();
1761 TransferDataFromWindow();
1762
1763 wxtFrame *parent = (wxtFrame *) GetParent();
1764
1765 switch (event.GetId()) {
1766 case Config_OK :
1767 Close(true);
1768 /* continue */
1769 case Config_APPLY :
1770 /* changes are applied immediately */
1771 wxt_raise = raise_setting?yes:no;
1772 wxt_persist = persist_setting?yes:no;
1773 wxt_ctrl = ctrl_setting?yes:no;
1774 wxt_toggle = toggle_setting?yes:no;
1775 wxt_redraw = redraw_setting?yes:no;
1776
1777 switch (rendering_setting) {
1778 case 0 :
1779 antialiasing = FALSE;
1780 oversampling = FALSE;
1781 break;
1782 case 1 :
1783 antialiasing = TRUE;
1784 oversampling = FALSE;
1785 break;
1786 case 2 :
1787 default :
1788 antialiasing = TRUE;
1789 oversampling = TRUE;
1790 break;
1791 }
1792
1793 /* we cannot apply the new settings right away, because it would mess up
1794 * the plot in case of a window resize.
1795 * Instead, we queue the settings until the next plot. */
1796 parent->panel->wxt_settings_queue(antialiasing, oversampling, hinting_setting);
1797
1798 if (!pConfig->Write(wxT("raise"), raise_setting))
1799 wxLogError(wxT("Cannot write raise"));
1800 if (!pConfig->Write(wxT("persist"), persist_setting))
1801 wxLogError(wxT("Cannot write persist"));
1802 if (!pConfig->Write(wxT("ctrl"), ctrl_setting))
1803 wxLogError(wxT("Cannot write ctrl"));
1804 if (!pConfig->Write(wxT("toggle"), toggle_setting))
1805 wxLogError(wxT("Cannot write toggle"));
1806 if (!pConfig->Write(wxT("redraw"), redraw_setting))
1807 wxLogError(wxT("Cannot write redraw_setting"));
1808 if (!pConfig->Write(wxT("rendering"), rendering_setting))
1809 wxLogError(wxT("Cannot write rendering_setting"));
1810 if (!pConfig->Write(wxT("hinting"), hinting_setting))
1811 wxLogError(wxT("Cannot write hinting_setting"));
1812 break;
1813 case Config_CANCEL :
1814 default :
1815 Close(true);
1816 break;
1817 }
1818 }
1819
1820 /* Configuration dialog constructor */
wxtConfigDialog(wxWindow * parent)1821 wxtConfigDialog::wxtConfigDialog(wxWindow* parent)
1822 : wxDialog(parent, -1, wxT("Terminal configuration"), wxDefaultPosition, wxDefaultSize,
1823 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1824 {
1825 /* wxStaticBox *sb = new wxStaticBox( this, wxID_ANY, wxT("&Explanation"),
1826 wxDefaultPosition, wxDefaultSize );
1827 wxStaticBoxSizer *wrapping_sizer = new wxStaticBoxSizer( sb, wxVERTICAL );
1828 wxStaticText *text1 = new wxStaticText(this, wxID_ANY,
1829 wxT("Options remembered between sessions, ")
1830 wxT("overridden by `set term wxt <options>`.\n\n"),
1831 wxDefaultPosition, wxSize(300, wxDefaultCoord));
1832 wrapping_sizer->Add(text1,wxSizerFlags(0).Align(0).Expand().Border(wxALL) );*/
1833
1834 wxConfigBase *pConfig = wxConfigBase::Get();
1835 pConfig->Read(wxT("raise"),&raise_setting);
1836 pConfig->Read(wxT("persist"),&persist_setting);
1837 pConfig->Read(wxT("ctrl"),&ctrl_setting);
1838 pConfig->Read(wxT("toggle"),&toggle_setting);
1839 pConfig->Read(wxT("redraw"),&redraw_setting);
1840 pConfig->Read(wxT("rendering"),&rendering_setting);
1841 pConfig->Read(wxT("hinting"),&hinting_setting);
1842
1843 wxCheckBox *check1 = new wxCheckBox (this, wxID_ANY,
1844 wxT("Put the window at the top of your desktop after each plot (raise)"),
1845 wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&raise_setting));
1846 wxCheckBox *check2 = new wxCheckBox (this, wxID_ANY,
1847 wxT("Don't quit until all windows are closed (persist)"),
1848 wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&persist_setting));
1849 wxCheckBox *check3 = new wxCheckBox (this, wxID_ANY,
1850 wxT("Replace 'q' by <ctrl>+'q' and <space> by <ctrl>+<space> (ctrl)"),
1851 wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&ctrl_setting));
1852
1853 wxCheckBox *check4 = new wxCheckBox (this, wxID_ANY,
1854 wxT("Toggle plots on/off when key sample is clicked"),
1855 wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&toggle_setting));
1856
1857 wxCheckBox *check5 = new wxCheckBox (this, wxID_ANY,
1858 wxT("Redraw continuously as plot is resized"),
1859 wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&redraw_setting));
1860
1861 wxString choices[3];
1862 choices[0] = wxT("No antialiasing");
1863 choices[1] = wxT("Antialiasing");
1864 choices[2] = wxT("Antialiasing and oversampling");
1865
1866 wxStaticBox *sb2 = new wxStaticBox( this, wxID_ANY,
1867 wxT("Rendering options (applied to the next plot)"),
1868 wxDefaultPosition, wxDefaultSize );
1869 wxStaticBoxSizer *box_sizer2 = new wxStaticBoxSizer( sb2, wxVERTICAL );
1870
1871 wxStaticText *text_rendering = new wxStaticText(this, wxID_ANY,
1872 wxT("Rendering method :"));
1873 wxChoice *box = new wxChoice (this, Config_Rendering, wxDefaultPosition, wxDefaultSize,
1874 3, choices, 0, wxGenericValidator(&rendering_setting));
1875
1876 text_hinting = new wxStaticText(this, wxID_ANY,
1877 wxT("Hinting (100=full,0=none) :"));
1878
1879 slider = new wxSlider(this, wxID_ANY, 0, 0, 100,
1880 wxDefaultPosition, wxDefaultSize,
1881 wxSL_HORIZONTAL|wxSL_LABELS,
1882 wxGenericValidator(&hinting_setting));
1883
1884 if (rendering_setting != 2) {
1885 slider->Enable(false);
1886 text_hinting->Enable(false);
1887 }
1888 box_sizer2->Add(text_rendering,wxSizerFlags().Align(0).Border(wxALL));
1889 box_sizer2->Add(box,wxSizerFlags().Align(0).Border(wxALL));
1890 box_sizer2->Add(text_hinting,wxSizerFlags().Align(0).Expand().Border(wxALL));
1891 box_sizer2->Add(slider,wxSizerFlags().Align(0).Expand().Border(wxALL));
1892
1893 wxBoxSizer *hsizer = new wxBoxSizer( wxHORIZONTAL );
1894 hsizer->Add( new wxButton(this, Config_OK, wxT("OK")),
1895 wxSizerFlags().Align(0).Expand().Border(wxALL));
1896 hsizer->Add( new wxButton(this, Config_APPLY, wxT("Apply")),
1897 wxSizerFlags().Align(0).Expand().Border(wxALL));
1898 hsizer->Add( new wxButton(this, Config_CANCEL, wxT("Cancel")),
1899 wxSizerFlags().Align(0).Expand().Border(wxALL));
1900
1901 wxBoxSizer *vsizer = new wxBoxSizer( wxVERTICAL );
1902 vsizer->Add(check1,wxSizerFlags().Align(0).Expand().Border(wxALL));
1903 vsizer->Add(check2,wxSizerFlags().Align(0).Expand().Border(wxALL));
1904 vsizer->Add(check3,wxSizerFlags().Align(0).Expand().Border(wxALL));
1905 vsizer->Add(check4,wxSizerFlags().Align(0).Expand().Border(wxALL));
1906 vsizer->Add(check5,wxSizerFlags().Align(0).Expand().Border(wxALL));
1907 vsizer->Add(box_sizer2,wxSizerFlags().Align(0).Expand().Border(wxALL));
1908 /*vsizer->Add(CreateButtonSizer(wxOK|wxCANCEL),wxSizerFlags().Align(0).Expand().Border(wxALL));*/
1909 vsizer->Add(hsizer,wxSizerFlags().Align(0).Expand().Border(wxALL));
1910
1911 /* use the sizer for layout */
1912 SetSizer( vsizer );
1913 /* set size hints to honour minimum size */
1914 vsizer->SetSizeHints( this );
1915 }
1916
1917 /* enable or disable the hinting slider depending on the selection of the oversampling method */
OnRendering(wxCommandEvent & event)1918 void wxtConfigDialog::OnRendering( wxCommandEvent& event )
1919 {
1920 if (event.GetInt() != 2) {
1921 slider->Enable(false);
1922 text_hinting->Enable(false);
1923 } else {
1924 slider->Enable(true);
1925 text_hinting->Enable(true);
1926 }
1927 }
1928
1929 /* ------------------------------------------------------
1930 * functions that are called by gnuplot
1931 * ------------------------------------------------------*/
1932
1933 /* "Called once, when the device is first selected."
1934 * Is the 'main' function of the terminal. */
wxt_init()1935 void wxt_init()
1936 {
1937 FPRINTF((stderr,"Init\n"));
1938
1939 if ( wxt_abort_init ) {
1940 fprintf(stderr,"Previous attempt to initialize wxWidgets has failed. Not retrying.\n");
1941 return;
1942 }
1943
1944 wxt_sigint_init();
1945
1946 if ( wxt_status == STATUS_UNINITIALIZED ) {
1947 FPRINTF((stderr,"First Init\n"));
1948
1949 #if !defined(DEVELOPMENT_VERSION) && wxCHECK_VERSION(2, 9, 0)
1950 // disable all assert()s
1951 // affects in particular the strcmp(setlocale(LC_ALL, NULL), "C") == 0
1952 // assertion in wxLocale::GetInfo
1953 wxSetAssertHandler(NULL);
1954 #endif
1955
1956 #ifdef __WXMSW__
1957 /* the following is done in wxEntry() with wxMSW only */
1958 WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
1959 wxSetInstance(GetModuleHandle(NULL));
1960 wxApp::m_nCmdShow = SW_SHOWNORMAL;
1961 #endif
1962
1963 if (!wxInitialize()) {
1964 fprintf(stderr,"Failed to initialize wxWidgets.\n");
1965 wxt_abort_init = true;
1966 if (interactive) {
1967 change_term("unknown",7);
1968 int_error(NO_CARET,"wxt init failure");
1969 } else
1970 gp_exit(EXIT_FAILURE);
1971 }
1972
1973 /* app initialization */
1974 wxTheApp->CallOnInit();
1975
1976
1977 #ifdef WXT_MULTITHREADED
1978 /* Three commands to create the thread and run it.
1979 * We do this at first init only.
1980 * If the user sets another terminal and goes back to wxt,
1981 * the gui thread is already in action. */
1982 thread = new wxtThread();
1983 thread->Create();
1984 thread->Run();
1985
1986 # ifdef USE_MOUSE
1987 int filedes[2];
1988
1989 if (pipe(filedes) == -1) {
1990 fprintf(stderr, "Pipe error, mousing will not work\n");
1991 }
1992
1993 wxt_event_fd = filedes[0];
1994 wxt_sendevent_fd = filedes[1];
1995 # endif /* USE_MOUSE */
1996 #endif /* WXT_MULTITHREADED */
1997
1998 FPRINTF((stderr,"First Init2\n"));
1999
2000 term_interlock = (void *)wxt_init;
2001
2002 /* register call for "persist" effect and cleanup */
2003 gp_atexit(wxt_atexit);
2004 }
2005
2006 wxt_sigint_check();
2007
2008 /* try to find the requested window in the list of existing ones */
2009 wxt_current_window = wxt_findwindowbyid(wxt_window_number);
2010
2011 /* open a new plot window if it does not exist */
2012 if ( wxt_current_window == NULL ) {
2013
2014 FPRINTF((stderr,"opening a new plot window\n"));
2015 #if defined(WXT_MONOTHREADED) && !defined(_WIN32)
2016 if (wxt_interlock)
2017 return;
2018 #endif
2019 /* create a new plot window and show it */
2020 wxt_window_t window;
2021 window.id = wxt_window_number;
2022 if (strlen(wxt_title))
2023 /* NOTE : this assumes that the title is encoded in the locale charset.
2024 * This is probably a good assumption, but it is not guaranteed !
2025 * May be improved by using gnuplot encoding setting. */
2026 window.title << wxString(wxt_title, wxConvLocal);
2027 else
2028 window.title.Printf(wxT("Gnuplot (window id : %d)"), window.id);
2029
2030 window.mutex = new wxMutex();
2031 window.condition = new wxCondition(*(window.mutex));
2032
2033 wxCommandEvent event(wxCreateWindowEvent);
2034 event.SetClientData((void*) &window);
2035
2036 #ifdef WXT_MULTITHREADED
2037 window.mutex->Lock();
2038 #endif /* WXT_MULTITHREADED */
2039 #if defined(WXT_MONOTHREADED) && !defined(_WIN32)
2040 wxt_status = STATUS_UNINITIALIZED;
2041 #endif
2042
2043 wxt_MutexGuiEnter();
2044 dynamic_cast<wxtApp*>(wxTheApp)->SendEvent( event );
2045 wxt_MutexGuiLeave();
2046 #ifdef WXT_MULTITHREADED
2047 /* While we are waiting, the other thread is busy mangling */
2048 /* our locale settings. We will have to restore them later. */
2049 window.condition->Wait();
2050 #endif /* WXT_MULTITHREADED */
2051
2052 /* store the plot structure in the list and keep shortcuts */
2053 wxt_window_list.push_back(window);
2054 wxt_current_window = &(wxt_window_list.back());
2055 }
2056
2057 /* initialize helper pointers */
2058 wxt_current_panel = wxt_current_window->frame->panel;
2059 wxt_current_plot = &(wxt_current_panel->plot);
2060
2061 wxt_sigint_check();
2062
2063 bool raise_setting;
2064 bool persist_setting;
2065 bool ctrl_setting;
2066 bool toggle_setting;
2067 bool redraw_setting;
2068 int rendering_setting;
2069 int hinting_setting;
2070
2071 /* if needed, restore the setting from the config file/registry keys.
2072 * Unset values are set to default reasonable values. */
2073 wxConfigBase *pConfig = wxConfigBase::Get();
2074
2075 if (!pConfig->Read(wxT("raise"), &raise_setting)) {
2076 pConfig->Write(wxT("raise"), true);
2077 raise_setting = true;
2078 }
2079 if (wxt_raise==UNSET)
2080 wxt_raise = raise_setting?yes:no;
2081
2082 if (!pConfig->Read(wxT("persist"), &persist_setting))
2083 pConfig->Write(wxT("persist"), false);
2084
2085 if (!pConfig->Read(wxT("ctrl"), &ctrl_setting)) {
2086 pConfig->Write(wxT("ctrl"), false);
2087 ctrl_setting = false;
2088 }
2089 if (wxt_ctrl==UNSET)
2090 wxt_ctrl = ctrl_setting?yes:no;
2091
2092 if (!pConfig->Read(wxT("toggle"), &toggle_setting)) {
2093 pConfig->Write(wxT("toggle"), true);
2094 toggle_setting = true;
2095 }
2096 if (wxt_toggle==UNSET)
2097 wxt_toggle = toggle_setting?yes:no;
2098
2099 if (!pConfig->Read(wxT("redraw"), &redraw_setting)) {
2100 pConfig->Write(wxT("redraw"), false);
2101 redraw_setting = false;
2102 }
2103 if (wxt_redraw==UNSET)
2104 wxt_redraw = redraw_setting?yes:no;
2105
2106 if (!pConfig->Read(wxT("rendering"), &rendering_setting)) {
2107 pConfig->Write(wxT("rendering"), 2);
2108 rendering_setting = 2;
2109 }
2110 switch (rendering_setting) {
2111 case 0 :
2112 wxt_current_plot->antialiasing = FALSE;
2113 wxt_current_plot->oversampling = FALSE;
2114 break;
2115 case 1 :
2116 wxt_current_plot->antialiasing = TRUE;
2117 wxt_current_plot->oversampling = FALSE;
2118 break;
2119 case 2 :
2120 default :
2121 wxt_current_plot->antialiasing = TRUE;
2122 wxt_current_plot->oversampling = TRUE;
2123 break;
2124 }
2125
2126 if (!pConfig->Read(wxT("hinting"), &hinting_setting)) {
2127 pConfig->Write(wxT("hinting"), 100);
2128 hinting_setting = 100;
2129 }
2130 wxt_current_plot->hinting = hinting_setting;
2131
2132 #ifdef HAVE_LOCALE_H
2133 /* when wxGTK was initialised above, GTK+ also set the locale of the
2134 * program itself; we must revert it */
2135 setlocale(LC_NUMERIC, "C");
2136 setlocale(LC_TIME, time_locale);
2137 #endif
2138
2139 /* accept the following commands from gnuplot */
2140 wxt_status = STATUS_OK;
2141 wxt_current_plot->interrupt = FALSE;
2142
2143 wxt_sigint_check();
2144 wxt_sigint_restore();
2145
2146 FPRINTF((stderr,"Init finished \n"));
2147 }
2148
2149
2150 /* "Called just before a plot is going to be displayed."
2151 * Should clear the terminal. */
wxt_graphics()2152 void wxt_graphics()
2153 {
2154 if (wxt_status != STATUS_OK)
2155 return;
2156
2157 /* The sequence of gnuplot commands is critical as it involves mutexes.
2158 * We replace the original interrupt handler with a custom one. */
2159 wxt_sigint_init();
2160
2161 /* update the window scale factor first, cairo needs it */
2162 wxt_current_plot->xscale = 1.0;
2163 wxt_current_plot->yscale = 1.0;
2164
2165 /* set the line properties */
2166 /* FIXME: should this be in wxt_settings_apply() ? */
2167 wxt_current_plot->linecap = wxt_linecap;
2168 wxt_current_plot->dashlength = wxt_dashlength;
2169
2170 /* background as given by set term */
2171 wxt_current_plot->background = wxt_rgb_background;
2172 gp_cairo_set_background(wxt_rgb_background);
2173
2174 /* apply the queued rendering settings */
2175 wxt_current_panel->wxt_settings_apply();
2176
2177 wxt_MutexGuiEnter();
2178 /* set the transformation matrix of the context, and other details */
2179 /* depends on plot->xscale and plot->yscale */
2180 gp_cairo_initialize_context(wxt_current_plot);
2181
2182 /* set or refresh terminal size according to the window size */
2183 /* oversampling_scale is updated in gp_cairo_initialize_context */
2184 wxt_current_plot->xmax = wxt_current_plot->device_xmax*wxt_current_plot->oversampling_scale;
2185 wxt_current_plot->ymax = wxt_current_plot->device_ymax*wxt_current_plot->oversampling_scale;
2186 /* initialize encoding */
2187 wxt_current_plot->encoding = encoding;
2188
2189 /* Adjust for the mismatch of floating point coordinate range 0->max */
2190 /* and integer terminal pixel coordinates [0:max-1]. */
2191 term->xmax = (wxt_current_plot->device_xmax - 1) * wxt_current_plot->oversampling_scale;
2192 term->ymax = (wxt_current_plot->device_ymax - 1) * wxt_current_plot->oversampling_scale;
2193 term->tscale = wxt_current_plot->oversampling_scale;
2194
2195 wxt_MutexGuiLeave();
2196
2197 /* set font details (h_char, v_char) according to settings */
2198 wxt_set_font("");
2199
2200 term->v_tic = (unsigned int) (term->v_char/2.5);
2201 term->h_tic = (unsigned int) (term->v_char/2.5);
2202
2203 /* clear the command list, and free the allocated memory */
2204 wxt_current_panel->ClearCommandlist();
2205
2206 /* Clear the count of hypertext anchor points */
2207 wxt_n_anchors = 0;
2208
2209 wxt_sigint_check();
2210 wxt_sigint_restore();
2211
2212 FPRINTF((stderr,"Graphics xmax %d ymax %d v_char %d h_char %d\n",
2213 term->xmax, term->ymax, term->v_char, term->h_char));
2214 }
2215
wxt_text()2216 void wxt_text()
2217 {
2218 if (wxt_status != STATUS_OK) {
2219 #ifdef USE_MOUSE
2220 /* Inform gnuplot that we have finished plotting.
2221 * This avoids to lose the mouse after a interrupt.
2222 * Do this immediately, instead of posting an event which may not be processed. */
2223 event_plotdone();
2224 #endif
2225 return;
2226 }
2227
2228 #ifdef USE_MOUSE
2229 /* Save a snapshot of the axis state so that we can continue
2230 * to update mouse cursor coordinates even though the plot is not active */
2231 wxt_current_window->axis_mask = wxt_axis_mask;
2232 memcpy( wxt_current_window->axis_state,
2233 wxt_axis_state, sizeof(wxt_axis_state) );
2234 #endif
2235
2236 wxt_sigint_init();
2237
2238 /* translates the command list to a bitmap */
2239 wxt_MutexGuiEnter();
2240 wxt_current_panel->wxt_cairo_refresh();
2241 wxt_MutexGuiLeave();
2242
2243 wxt_sigint_check();
2244
2245 /* raise the window, depending on the user's choice */
2246 wxt_MutexGuiEnter();
2247 wxt_raise_window(wxt_current_window, false);
2248 wxt_MutexGuiLeave();
2249
2250 #ifdef USE_MOUSE
2251 /* Inform gnuplot that we have finished plotting */
2252 wxt_exec_event(GE_plotdone, 0, 0, 0, 0, wxt_window_number );
2253 #endif /*USE_MOUSE*/
2254
2255 wxt_sigint_check();
2256 wxt_sigint_restore();
2257 }
2258
wxt_reset()2259 void wxt_reset()
2260 {
2261 /* sent when gnuplot exits and when the terminal or the output change.*/
2262 FPRINTF((stderr,"wxt_reset\n"));
2263
2264 #if defined(WXT_MONOTHREADED) && !defined(_WIN32)
2265 wxt_yield = 0;
2266 #endif
2267
2268 if (wxt_status == STATUS_UNINITIALIZED)
2269 return;
2270
2271 #ifdef USE_MOUSE
2272 if (wxt_status == STATUS_INTERRUPT) {
2273 /* send "reset" event to restore the mouse system in a well-defined state.
2274 * Send it directly, not with wxt_exec_event(), which would only enqueue it,
2275 * but not process it. */
2276 FPRINTF((stderr,"send reset event to the mouse system\n"));
2277 event_reset((gp_event_t *)1); /* cancel zoombox etc. */
2278 }
2279 #endif /*USE_MOUSE*/
2280
2281 FPRINTF((stderr,"wxt_reset ends\n"));
2282 }
2283
wxt_move(unsigned int x,unsigned int y)2284 void wxt_move(unsigned int x, unsigned int y)
2285 {
2286 if (wxt_status != STATUS_OK)
2287 return;
2288
2289 gp_command temp_command;
2290
2291 temp_command.command = command_move;
2292 temp_command.x1 = x;
2293 temp_command.y1 = term->ymax - y;
2294
2295 wxt_command_push(temp_command);
2296 }
2297
wxt_vector(unsigned int x,unsigned int y)2298 void wxt_vector(unsigned int x, unsigned int y)
2299 {
2300 if (wxt_status != STATUS_OK)
2301 return;
2302
2303 gp_command temp_command;
2304
2305 temp_command.command = command_vector;
2306 temp_command.x1 = x;
2307 temp_command.y1 = term->ymax - y;
2308
2309 wxt_command_push(temp_command);
2310 }
2311
wxt_enhanced_flush()2312 void wxt_enhanced_flush()
2313 {
2314 if (wxt_status != STATUS_OK)
2315 return;
2316
2317 gp_command temp_command;
2318 temp_command.command = command_enhanced_flush;
2319
2320 wxt_command_push(temp_command);
2321 }
2322
wxt_enhanced_writec(int c)2323 void wxt_enhanced_writec(int c)
2324 {
2325 if (wxt_status != STATUS_OK)
2326 return;
2327
2328 gp_command temp_command;
2329 temp_command.command = command_enhanced_writec;
2330 temp_command.integer_value = c;
2331
2332 wxt_command_push(temp_command);
2333 }
2334
wxt_enhanced_open(char * fontname,double fontsize,double base,TBOOLEAN widthflag,TBOOLEAN showflag,int overprint)2335 void wxt_enhanced_open(char* fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint)
2336 {
2337 if (wxt_status != STATUS_OK)
2338 return;
2339
2340 gp_command temp_command;
2341 temp_command.command = command_enhanced_open;
2342 temp_command.string = new char[strlen(fontname)+1];
2343 strcpy(temp_command.string, fontname);
2344 temp_command.double_value = fontsize;
2345 temp_command.double_value2 = base;
2346 temp_command.integer_value = overprint;
2347 temp_command.integer_value2 = widthflag + (showflag << 1);
2348
2349 wxt_command_push(temp_command);
2350 }
2351
wxt_put_text(unsigned int x,unsigned int y,const char * string)2352 void wxt_put_text(unsigned int x, unsigned int y, const char * string)
2353 {
2354 if (wxt_status != STATUS_OK)
2355 return;
2356
2357 gp_command temp_command;
2358
2359 /* if ignore_enhanced_text is set, draw with the normal routine.
2360 * This is meant to avoid enhanced syntax when the enhanced mode is on */
2361 if (wxt_enhanced_enabled && !ignore_enhanced_text) {
2362 /* Uses enhanced_recursion() to analyse the string to print.
2363 * enhanced_recursion() calls _enhanced_open() to initialize the text drawing,
2364 * then it calls _enhanced_writec() which buffers the characters to draw,
2365 * and finally _enhanced_flush() to draw the buffer with the correct justification. */
2366
2367 /* init */
2368 temp_command.command = command_enhanced_init;
2369 temp_command.integer_value = strlen(string);
2370 temp_command.x1 = x;
2371 temp_command.y1 = term->ymax - y;
2372 wxt_command_push(temp_command);
2373
2374 /* set up the global variables needed by enhanced_recursion() */
2375 enhanced_fontscale = wxt_set_fontscale;
2376 strncpy(enhanced_escape_format, "%c", sizeof(enhanced_escape_format));
2377
2378 /* Set the recursion going. We say to keep going until a
2379 * closing brace, but we don't really expect to find one.
2380 * If the return value is not the nul-terminator of the
2381 * string, that can only mean that we did find an unmatched
2382 * closing brace in the string. We increment past it (else
2383 * we get stuck in an infinite loop) and try again. */
2384
2385 while (*(string = enhanced_recursion((char*)string, TRUE,
2386 wxt_enhanced_fontname,
2387 wxt_current_plot->fontsize * wxt_set_fontscale,
2388 0.0, TRUE, TRUE, 0))) {
2389 wxt_enhanced_flush();
2390
2391 /* we can only get here if *str == '}' */
2392 enh_err_check(string);
2393
2394 if (!*++string)
2395 break; /* end of string */
2396 /* else carry on and process the rest of the string */
2397 }
2398
2399 /* finish */
2400 temp_command.command = command_enhanced_finish;
2401 temp_command.x1 = x;
2402 temp_command.y1 = term->ymax - y;
2403 wxt_command_push(temp_command);
2404 return;
2405 }
2406
2407 temp_command.command = command_put_text;
2408
2409 temp_command.x1 = x;
2410 temp_command.y1 = term->ymax - y;
2411 /* Note : we must take '\0' (EndOfLine) into account */
2412 temp_command.string = new char[strlen(string)+1];
2413 strcpy(temp_command.string, string);
2414
2415 wxt_command_push(temp_command);
2416 }
2417
wxt_linetype(int lt)2418 void wxt_linetype(int lt)
2419 {
2420 if (wxt_status != STATUS_OK)
2421 return;
2422
2423 gp_command temp_command;
2424 gp_command temp_command2;
2425
2426 temp_command2.command = command_linestyle;
2427 if (lt == LT_AXIS)
2428 temp_command2.integer_value = GP_CAIRO_DOTS;
2429 else
2430 temp_command2.integer_value = GP_CAIRO_SOLID;
2431 wxt_command_push(temp_command2);
2432
2433 temp_command.command = command_linetype;
2434 temp_command.integer_value = lt;
2435 wxt_command_push(temp_command);
2436
2437 temp_command.command = command_color;
2438 temp_command.color = gp_cairo_linetype2color( lt );
2439 temp_command.double_value = 0.0; // alpha
2440 wxt_command_push(temp_command);
2441 }
2442
wxt_dashtype(int type,t_dashtype * custom_dash_pattern)2443 void wxt_dashtype(int type, t_dashtype *custom_dash_pattern)
2444 {
2445 if (wxt_status != STATUS_OK)
2446 return;
2447
2448 gp_command temp_command;
2449 temp_command.command = command_dashtype;
2450 temp_command.integer_value = type;
2451 if (type == DASHTYPE_CUSTOM) {
2452 temp_command.dashpattern = (t_dashtype *)malloc(sizeof(t_dashtype));
2453 memcpy(temp_command.dashpattern, custom_dash_pattern, sizeof(t_dashtype));
2454 } else {
2455 temp_command.dashpattern = NULL;
2456 }
2457 wxt_command_push(temp_command);
2458 }
2459
2460 /* - fonts are selected as strings "name,size".
2461 * - _set_font("") restores the terminal's default font.*/
wxt_set_font(const char * font)2462 int wxt_set_font (const char *font)
2463 {
2464 if (wxt_status != STATUS_OK)
2465 return 1;
2466
2467 char *fontname = NULL;
2468 gp_command temp_command;
2469 int fontsize = 0;
2470
2471 temp_command.command = command_set_font;
2472
2473 if (font && (*font)) {
2474 int sep = strcspn(font,",");
2475 fontname = strdup(font);
2476 if (font[sep] == ',') {
2477 sscanf(&(font[sep+1]), "%d", &fontsize);
2478 fontname[sep] = '\0';
2479 }
2480 } else {
2481 fontname = strdup("");
2482 }
2483
2484 wxt_sigint_init();
2485 wxt_MutexGuiEnter();
2486
2487 if ( strlen(fontname) == 0 ) {
2488 if ( !wxt_set_fontname || strlen(wxt_set_fontname) == 0 ) {
2489 free(fontname);
2490 fontname = strdup(gp_cairo_default_font());
2491 } else {
2492 free(fontname);
2493 fontname = strdup(wxt_set_fontname);
2494 }
2495 }
2496
2497 if ( fontsize == 0 ) {
2498 if ( wxt_set_fontsize == 0 )
2499 fontsize = 10;
2500 else
2501 fontsize = wxt_set_fontsize;
2502 }
2503
2504 /* Reset the term variables (hchar, vchar, h_tic, v_tic).
2505 * They may be taken into account in next plot commands */
2506 gp_cairo_set_font(wxt_current_plot, fontname, fontsize * wxt_set_fontscale);
2507 gp_cairo_set_termvar(wxt_current_plot, &(term->v_char),
2508 &(term->h_char));
2509 gp_cairo_set_font(wxt_current_plot, fontname, fontsize);
2510
2511 wxt_MutexGuiLeave();
2512 wxt_sigint_check();
2513 wxt_sigint_restore();
2514
2515 /* Note : we must take '\0' (EndOfLine) into account */
2516 temp_command.string = new char[strlen(fontname)+1];
2517 strcpy(temp_command.string, fontname);
2518 temp_command.integer_value = fontsize * wxt_set_fontscale;
2519
2520 wxt_command_push(temp_command);
2521
2522 /* Enhanced text processing needs to know the new font also */
2523 if (*fontname) {
2524 free(wxt_enhanced_fontname);
2525 wxt_enhanced_fontname = strdup(fontname);
2526 }
2527 free(fontname);
2528
2529 /* the returned int is not used anywhere */
2530 return 1;
2531 }
2532
2533
wxt_justify_text(enum JUSTIFY mode)2534 int wxt_justify_text(enum JUSTIFY mode)
2535 {
2536 if (wxt_status != STATUS_OK)
2537 return 1;
2538
2539 gp_command temp_command;
2540
2541 temp_command.command = command_justify;
2542 temp_command.mode = mode;
2543
2544 wxt_command_push(temp_command);
2545 return 1; /* we can justify */
2546 }
2547
wxt_point(unsigned int x,unsigned int y,int pointstyle)2548 void wxt_point(unsigned int x, unsigned int y, int pointstyle)
2549 {
2550 if (wxt_status != STATUS_OK)
2551 return;
2552
2553 gp_command temp_command;
2554
2555 temp_command.command = command_point;
2556 temp_command.x1 = x;
2557 temp_command.y1 = term->ymax - y;
2558 temp_command.integer_value = pointstyle;
2559
2560 wxt_command_push(temp_command);
2561
2562 if (pending_href) {
2563 /* FIXME: How to convert current pointsize to term coords? */
2564 int size = 400;
2565 wxt_update_anchors(x, y, size);
2566 pending_href = FALSE;
2567 }
2568 }
2569
wxt_pointsize(double ptsize)2570 void wxt_pointsize(double ptsize)
2571 {
2572 if (wxt_status != STATUS_OK)
2573 return;
2574
2575 /* same behaviour as x11 terminal */
2576 if (ptsize<0) ptsize = 1;
2577
2578 gp_command temp_command;
2579 temp_command.command = command_pointsize;
2580 temp_command.double_value = ptsize;
2581
2582 wxt_command_push(temp_command);
2583 }
2584
wxt_linewidth(double lw)2585 void wxt_linewidth(double lw)
2586 {
2587 if (wxt_status != STATUS_OK)
2588 return;
2589
2590 gp_command temp_command;
2591
2592 temp_command.command = command_linewidth;
2593 temp_command.double_value = lw * wxt_lw;
2594
2595 wxt_command_push(temp_command);
2596 }
2597
wxt_text_angle(int angle)2598 int wxt_text_angle(int angle)
2599 {
2600 if (wxt_status != STATUS_OK)
2601 return 1;
2602
2603 gp_command temp_command;
2604
2605 temp_command.command = command_text_angle;
2606 /* a double is needed to compute cos, sin, etc. */
2607 temp_command.double_value = (double) angle;
2608
2609 wxt_command_push(temp_command);
2610 return 1; /* 1 means we can rotate */
2611 }
2612
wxt_fillbox(int style,unsigned int x,unsigned int y,unsigned int width,unsigned int height)2613 void wxt_fillbox(int style, unsigned int x, unsigned int y, unsigned int width, unsigned int height)
2614 {
2615 if (wxt_status != STATUS_OK)
2616 return;
2617
2618 gp_command temp_command;
2619
2620 temp_command.command = command_fillbox;
2621 temp_command.x1 = x;
2622 temp_command.y1 = term->ymax - y;
2623 temp_command.x2 = width;
2624 temp_command.y2 = height;
2625 temp_command.integer_value = style;
2626
2627 wxt_command_push(temp_command);
2628 }
2629
wxt_make_palette(t_sm_palette * palette)2630 int wxt_make_palette(t_sm_palette * palette)
2631 {
2632 /* we can do continuous colors */
2633 return 0;
2634 }
2635
wxt_set_color(t_colorspec * colorspec)2636 void wxt_set_color(t_colorspec *colorspec)
2637 {
2638 if (wxt_status != STATUS_OK)
2639 return;
2640
2641 rgb_color rgb1;
2642 gp_command temp_command;
2643 double alpha = 0.0;
2644
2645 if (colorspec->type == TC_LT) {
2646 rgb1 = gp_cairo_linetype2color(colorspec->lt);
2647 } else if (colorspec->type == TC_FRAC)
2648 rgb1maxcolors_from_gray( colorspec->value, &rgb1 );
2649 else if (colorspec->type == TC_RGB) {
2650 rgb1.r = (double) ((colorspec->lt >> 16) & 0xff)/255;
2651 rgb1.g = (double) ((colorspec->lt >> 8) & 0xff)/255;
2652 rgb1.b = (double) ((colorspec->lt) & 0xff)/255;
2653 alpha = (double) ((colorspec->lt >> 24) & 0xff)/255;
2654 } else return;
2655
2656 temp_command.command = command_color;
2657 temp_command.color = rgb1;
2658 temp_command.double_value = alpha;
2659
2660 wxt_command_push(temp_command);
2661 }
2662
2663
2664 /* here we send the polygon command */
wxt_filled_polygon(int n,gpiPoint * corners)2665 void wxt_filled_polygon(int n, gpiPoint *corners)
2666 {
2667 if (wxt_status != STATUS_OK)
2668 return;
2669
2670 gp_command temp_command;
2671
2672 temp_command.command = command_filled_polygon;
2673 temp_command.integer_value = n;
2674 temp_command.corners = new gpiPoint[n];
2675 /* can't use memcpy() here, as we have to mirror the y axis */
2676 gpiPoint *corners_copy = temp_command.corners;
2677 while (corners_copy < (temp_command.corners + n)) {
2678 *corners_copy = *corners++;
2679 corners_copy->y = term->ymax - corners_copy->y;
2680 ++corners_copy;
2681 }
2682
2683 wxt_command_push(temp_command);
2684 }
2685
wxt_image(unsigned int M,unsigned int N,coordval * image,gpiPoint * corner,t_imagecolor color_mode)2686 void wxt_image(unsigned int M, unsigned int N, coordval * image, gpiPoint * corner, t_imagecolor color_mode)
2687 {
2688 /* This routine is to plot a pixel-based image on the display device.
2689 'M' is the number of pixels along the y-dimension of the image and
2690 'N' is the number of pixels along the x-dimension of the image. The
2691 coordval pointer 'image' is the pixel values normalized to the range
2692 [0:1]. These values should be scaled accordingly for the output
2693 device. They 'image' data starts in the upper left corner and scans
2694 along rows finishing in the lower right corner. If 'color_mode' is
2695 IC_PALETTE, the terminal is to use palette lookup to generate color
2696 information. In this scenario the size of 'image' is M*N. If
2697 'color_mode' is IC_RGB, the terminal is to use RGB components. In
2698 this scenario the size of 'image' is 3*M*N. The data appears in RGB
2699 tripples, i.e., image[0] = R(1,1), image[1] = G(1,1), image[2] =
2700 B(1,1), image[3] = R(1,2), image[4] = G(1,2), ..., image[3*M*N-1] =
2701 B(M,N). The 'image' is actually an "input" image in the sense that
2702 it must also be properly resampled for the output device. Many output
2703 mediums, e.g., PostScript, do this work via various driver functions.
2704 To determine the appropriate rescaling, the 'corner' information
2705 should be used. There are four entries in the gpiPoint data array.
2706 'corner[0]' is the upper left corner (in terms of plot location) of
2707 the outer edge of the image. Similarly, 'corner[1]' is the lower
2708 right corner of the outer edge of the image. (Outer edge means the
2709 outer extent of the corner pixels, not the middle of the corner
2710 pixels.) 'corner[2]' is the upper left corner of the visible part
2711 of the image, and 'corner[3]' is the lower right corner of the visible
2712 part of the image. The information is provided in this way because
2713 often it is necessary to clip a portion of the outer pixels of the
2714 image. */
2715
2716 /* we will draw an image, scale and resize it, copy to bitmap,
2717 * give a pointer to it in the list, and when processing the list we will use DrawBitmap */
2718 /* FIXME add palette support ??? */
2719
2720 if (wxt_status != STATUS_OK)
2721 return;
2722
2723 gp_command temp_command;
2724
2725 temp_command.command = command_image;
2726 temp_command.x1 = corner[0].x;
2727 temp_command.y1 = term->ymax - corner[0].y;
2728 temp_command.x2 = corner[1].x;
2729 temp_command.y2 = term->ymax - corner[1].y;
2730 temp_command.x3 = corner[2].x;
2731 temp_command.y3 = term->ymax - corner[2].y;
2732 temp_command.x4 = corner[3].x;
2733 temp_command.y4 = term->ymax - corner[3].y;
2734 temp_command.integer_value = M;
2735 temp_command.integer_value2 = N;
2736
2737 temp_command.image = gp_cairo_helper_coordval_to_chars(image, M, N, color_mode);
2738
2739 wxt_command_push(temp_command);
2740 }
2741
2742 /* This is meta-information about the plot state
2743 */
wxt_layer(t_termlayer layer)2744 void wxt_layer(t_termlayer layer)
2745 {
2746 /* There are two classes of meta-information. The first class */
2747 /* is tied to the current state of the user interface or the */
2748 /* main gnuplot thread. Any action on these must be done here, */
2749 /* immediately. The second class relates to the sequence of */
2750 /* operations in the plot itself. These are buffered for later */
2751 /* execution in sequential order. */
2752 if (layer == TERM_LAYER_BEFORE_ZOOM) {
2753 return;
2754 }
2755 if (layer == TERM_LAYER_RESET || layer == TERM_LAYER_RESET_PLOTNO) {
2756 if (multiplot)
2757 return;
2758 }
2759
2760 gp_command temp_command;
2761 temp_command.command = command_layer;
2762 temp_command.integer_value = layer;
2763 wxt_command_push(temp_command);
2764 }
2765
wxt_hypertext(int type,const char * text)2766 void wxt_hypertext(int type, const char * text)
2767 {
2768 if (wxt_status != STATUS_OK)
2769 return;
2770
2771 if (type != TERM_HYPERTEXT_TOOLTIP)
2772 return;
2773
2774 gp_command temp_command;
2775
2776 temp_command.command = command_hypertext;
2777 temp_command.integer_value = type;
2778 temp_command.string = new char[strlen(text)+1];
2779 strcpy(temp_command.string, text);
2780
2781 wxt_command_push(temp_command);
2782 pending_href = TRUE;
2783 }
2784
2785
2786 #ifdef USE_MOUSE
2787 /* Display temporary text, after
2788 * erasing any temporary text displayed previously at this location.
2789 * The int determines where: 0=statusline, 1,2: at corners of zoom
2790 * box, with \r separating text above and below the point. */
wxt_put_tmptext(int n,const char str[])2791 void wxt_put_tmptext(int n, const char str[])
2792 {
2793 if (wxt_status == STATUS_UNINITIALIZED)
2794 return;
2795
2796 wxt_sigint_init();
2797 wxt_MutexGuiEnter();
2798
2799 switch ( n ) {
2800 case 0:
2801 {
2802 wxCommandEvent event(wxStatusTextEvent);
2803 event.SetString(wxString(str, wxConvLocal));
2804 wxt_current_window->frame->SendEvent( event );
2805 }
2806 break;
2807 case 1:
2808 wxt_current_panel->zoom_x1 = wxt_current_panel->mouse_x;
2809 wxt_current_panel->zoom_y1 = wxt_current_panel->mouse_y;
2810 wxt_current_panel->zoom_string1 = wxString(str, wxConvLocal);
2811 break;
2812 case 2:
2813 if ( strlen(str)==0 )
2814 wxt_current_panel->wxt_zoombox = false;
2815 else {
2816 wxt_current_panel->wxt_zoombox = true;
2817 wxt_current_panel->zoom_string2 = wxString(str, wxConvLocal);
2818 }
2819 wxt_current_panel->Draw();
2820 break;
2821 default :
2822 break;
2823 }
2824
2825 wxt_MutexGuiLeave();
2826 wxt_sigint_check();
2827 wxt_sigint_restore();
2828 }
2829
2830 /* c selects the action:
2831 * -4=don't draw (erase) line between ruler and current mouse position,
2832 * -3=draw line between ruler and current mouse position,
2833 * -2=warp the cursor to the given point,
2834 * -1=start zooming,
2835 * 0=standard cross-hair cursor,
2836 * 1=cursor during rotation,
2837 * 2=cursor during scaling,
2838 * 3=cursor during zooming. */
wxt_set_cursor(int c,int x,int y)2839 void wxt_set_cursor(int c, int x, int y)
2840 {
2841 if (wxt_status == STATUS_UNINITIALIZED)
2842 return;
2843
2844 wxt_sigint_init();
2845 wxt_MutexGuiEnter();
2846
2847 switch ( c ) {
2848 case -4:
2849 wxt_current_panel->wxt_ruler_lineto = false;
2850 wxt_current_panel->Draw();
2851 break;
2852 case -3:
2853 wxt_current_panel->wxt_ruler_lineto = true;
2854 wxt_current_panel->Draw();
2855 break;
2856 case -2: /* warp the pointer to the given position */
2857 wxt_current_panel->WarpPointer(
2858 (int) device_x(wxt_current_plot, x),
2859 (int) device_y(wxt_current_plot, y) );
2860 break;
2861 case -1: /* start zooming */
2862 wxt_current_panel->SetCursor(wxt_cursor_right);
2863 break;
2864 case 0: /* cross-hair cursor, also cancel zoombox when Echap is pressed */
2865 wxt_current_panel->wxt_zoombox = false;
2866 wxt_current_panel->SetCursor(wxt_cursor_cross);
2867 wxt_current_panel->Draw();
2868 break;
2869 case 1: /* rotation */
2870 wxt_current_panel->SetCursor(wxt_cursor_rotate);
2871 break;
2872 case 2: /* scaling */
2873 wxt_current_panel->SetCursor(wxt_cursor_size);
2874 break;
2875 case 3: /* zooming */
2876 wxt_current_panel->SetCursor(wxt_cursor_right);
2877 break;
2878 default:
2879 wxt_current_panel->SetCursor(wxt_cursor_cross);
2880 break;
2881 }
2882
2883 wxt_MutexGuiLeave();
2884 wxt_sigint_check();
2885 wxt_sigint_restore();
2886 }
2887
2888
2889 /* Draw a ruler (crosshairs) centered at the
2890 * indicated screen coordinates. If x<0, switch ruler off. */
wxt_set_ruler(int x,int y)2891 void wxt_set_ruler(int x, int y)
2892 {
2893 if (wxt_status == STATUS_UNINITIALIZED)
2894 return;
2895
2896 wxt_sigint_init();
2897 wxt_MutexGuiEnter();
2898
2899 if (x<0) {
2900 wxt_current_panel->wxt_ruler = false;
2901 wxt_current_panel->Draw();
2902 } else {
2903 wxt_current_panel->wxt_ruler = true;
2904 wxt_current_panel->wxt_ruler_x = device_x(wxt_current_plot, x);
2905 wxt_current_panel->wxt_ruler_y = device_y(wxt_current_plot, y);
2906 wxt_current_panel->Draw();
2907 }
2908
2909 wxt_MutexGuiLeave();
2910 wxt_sigint_check();
2911 wxt_sigint_restore();
2912 }
2913
2914 /* Write a string to the clipboard */
wxt_set_clipboard(const char s[])2915 void wxt_set_clipboard(const char s[])
2916 {
2917 if (wxt_status == STATUS_UNINITIALIZED)
2918 return;
2919
2920 wxt_sigint_init();
2921 wxt_MutexGuiEnter();
2922
2923 if (wxTheClipboard->Open()) {
2924 wxTheClipboard->SetData( new wxTextDataObject(wxString(s, wxConvLocal)) );
2925 wxTheClipboard->Flush();
2926 wxTheClipboard->Close();
2927 }
2928
2929 wxt_MutexGuiLeave();
2930 wxt_sigint_check();
2931 wxt_sigint_restore();
2932 }
2933 #endif /*USE_MOUSE*/
2934
2935 /* Pass through the boxed text options to cairo */
wxt_boxed_text(unsigned int x,unsigned int y,int option)2936 void wxt_boxed_text(unsigned int x, unsigned int y, int option)
2937 {
2938 gp_command temp_command;
2939 if (option != 3)
2940 y = term->ymax - y;
2941 temp_command.command = command_boxed_text;
2942 temp_command.x1 = x;
2943 temp_command.y1 = y;
2944 temp_command.integer_value = option;
2945 wxt_command_push(temp_command);
2946 }
2947
wxt_modify_plots(unsigned int ops,int plotno)2948 void wxt_modify_plots(unsigned int ops, int plotno)
2949 {
2950 int i;
2951 plotno++;
2952
2953 if (wxt_status == STATUS_UNINITIALIZED)
2954 return;
2955
2956 for (i=1; i<=wxt_cur_plotno && i<wxt_max_key_boxes; i++) {
2957 if (plotno > 0 && i != plotno)
2958 continue;
2959 if ((ops & MODPLOTS_INVERT_VISIBILITIES) == MODPLOTS_INVERT_VISIBILITIES) {
2960 wxt_key_boxes[i].hidden = !wxt_key_boxes[i].hidden;
2961 } else if (ops & MODPLOTS_SET_VISIBLE) {
2962 wxt_key_boxes[i].hidden = FALSE;
2963 } else if (ops & MODPLOTS_SET_INVISIBLE) {
2964 wxt_key_boxes[i].hidden = TRUE;
2965 }
2966 }
2967 wxt_MutexGuiEnter();
2968 wxt_current_panel->wxt_cairo_refresh();
2969 // Empirically, without this Update() the plots are toggled correctly but the
2970 // change may not show on the screen until a mouse or other event next arrives
2971 wxt_current_panel->Update();
2972 wxt_MutexGuiLeave();
2973 }
2974
2975 /* ===================================================================
2976 * Command list processing
2977 * =================================================================*/
2978
2979 /* push a command in the current commands list */
wxt_command_push(gp_command command)2980 void wxt_command_push(gp_command command)
2981 {
2982 wxt_sigint_init();
2983 wxt_current_panel->command_list_mutex.Lock();
2984 wxt_current_panel->command_list.push_back(command);
2985 wxt_current_panel->command_list_mutex.Unlock();
2986 wxt_sigint_check();
2987 wxt_sigint_restore();
2988 }
2989
2990 /* refresh the plot by (re)processing the plot commands list */
wxt_cairo_refresh()2991 void wxtPanel::wxt_cairo_refresh()
2992 {
2993 /* This check may prevent the assert+die behavior seen with wxgtk3.0 */
2994 /* Symptom:
2995 ./src/gtk/dcclient.cpp(2043): assert "m_window" failed in DoGetSize(): GetSize() doesn't work without window [in thread 7fb21f386700]
2996 Call stack:
2997 [00] wxOnAssert(char const*, int, char const*, char const*, wchar_t const*)
2998 [01] wxClientDCImpl::DoGetSize(int*, int*) const
2999 [02] wxBufferedDC::UnMask()
3000 wxwidgets documentation to the contrary, panel->IsShownOnScreen() is unreliable
3001 */
3002 if (!wxt_current_window) {
3003 FPRINTF((stderr,"wxt_cairo_refresh called before window exists\n"));
3004 return;
3005 }
3006
3007 /* Clear background. */
3008 gp_cairo_solid_background(&plot);
3009
3010 /* Initialize toggle in keybox mechanism */
3011 wxt_cur_plotno = 0;
3012 wxt_in_key_sample = FALSE;
3013 #ifdef USE_MOUSE
3014 wxt_initialize_key_boxes(0);
3015 #endif
3016
3017 /* Initialize the hypertext tracking mechanism */
3018 wxt_display_hypertext = NULL;
3019 wxt_display_anchor.x = 0;
3020 wxt_display_anchor.y = 0;
3021 free(wxt_hypertext_fontname);
3022 wxt_hypertext_fontname = NULL;
3023
3024 command_list_mutex.Lock();
3025 command_list_t::iterator wxt_iter; /*declare the iterator*/
3026 for(wxt_iter = command_list.begin(); wxt_iter != command_list.end(); ++wxt_iter) {
3027 if (wxt_status == STATUS_INTERRUPT_ON_NEXT_CHECK) {
3028 FPRINTF((stderr,"interrupt detected inside drawing loop\n"));
3029 #ifdef IMAGE_SURFACE
3030 wxt_cairo_create_bitmap();
3031 #endif /* IMAGE_SURFACE */
3032 /* draw the pixmap to the screen */
3033 Draw();
3034 return;
3035 }
3036
3037 /* Skip the plot commands, but not the key sample commands,
3038 * if the plot was toggled off by a mouse click in the GUI
3039 */
3040 if (wxt_in_plot && !wxt_in_key_sample
3041 && wxt_iter->command != command_layer
3042 && wxt_cur_plotno < wxt_max_key_boxes
3043 && wxt_key_boxes[wxt_cur_plotno].hidden)
3044 continue;
3045
3046 if (wxt_in_key_sample
3047 && wxt_iter->command == command_layer /* catches TERM_LAYER_END_KEYSAMPLE */
3048 && wxt_cur_plotno < wxt_max_key_boxes
3049 && wxt_key_boxes[wxt_cur_plotno].hidden)
3050 gp_cairo_boxed_text(&plot, 0, 0, TEXTBOX_GREY);
3051
3052 wxt_cairo_exec_command( *wxt_iter );
3053
3054 }
3055 command_list_mutex.Unlock();
3056
3057 /* don't forget to stroke the last path if vector was the last command */
3058 gp_cairo_stroke(&plot);
3059 /* and don't forget to draw the polygons if draw_polygon was the last command */
3060 gp_cairo_end_polygon(&plot);
3061
3062 /* If we detected the mouse over a hypertext anchor, draw it now. */
3063 if (wxt_display_hypertext)
3064 wxt_cairo_draw_hypertext();
3065
3066 #ifdef IMAGE_SURFACE
3067 command_list_mutex.Lock();
3068 wxt_cairo_create_bitmap();
3069 command_list_mutex.Unlock();
3070 #endif /* !have_gtkcairo */
3071
3072 /* draw the pixmap to the screen */
3073 Draw();
3074 }
3075
3076
wxt_cairo_exec_command(gp_command command)3077 void wxtPanel::wxt_cairo_exec_command(gp_command command)
3078 {
3079 static JUSTIFY text_justification_mode = LEFT;
3080 static char *current_href = NULL;
3081
3082 switch ( command.command ) {
3083 case command_color :
3084 gp_cairo_set_color(&plot,command.color,command.double_value);
3085 return;
3086 case command_filled_polygon :
3087 if (wxt_in_key_sample) {
3088 wxt_update_key_box(command.x1 - term->h_tic, command.y1 - term->v_tic);
3089 wxt_update_key_box(command.x1 + term->h_tic, command.y1 + term->v_tic);
3090 }
3091 gp_cairo_draw_polygon(&plot, command.integer_value, command.corners);
3092 return;
3093 case command_move :
3094 if (wxt_in_key_sample)
3095 wxt_update_key_box(command.x1, command.y1);
3096 gp_cairo_move(&plot, command.x1, command.y1);
3097 return;
3098 case command_vector :
3099 if (wxt_in_key_sample) {
3100 wxt_update_key_box(command.x1, command.y1+term->v_tic);
3101 wxt_update_key_box(command.x1, command.y1-term->v_tic);
3102 }
3103 gp_cairo_vector(&plot, command.x1, command.y1);
3104 return;
3105 case command_linestyle :
3106 gp_cairo_set_linestyle(&plot, command.integer_value);
3107 return;
3108 case command_linetype :
3109 gp_cairo_set_linetype(&plot, command.integer_value);
3110 return;
3111 case command_pointsize :
3112 gp_cairo_set_pointsize(&plot, command.double_value);
3113 return;
3114 case command_dashtype :
3115 gp_cairo_set_dashtype(&plot, command.integer_value, command.dashpattern);
3116 return;
3117 case command_hypertext :
3118 current_href = command.string;
3119 free(wxt_hypertext_fontname);
3120 wxt_hypertext_fontname = strdup(plot.fontname);
3121 wxt_hypertext_fontsize = plot.fontsize;
3122 wxt_hypertext_fontstyle = plot.fontstyle;
3123 wxt_hypertext_fontweight = plot.fontweight;
3124 return;
3125 case command_point :
3126 if (wxt_in_key_sample) {
3127 wxt_update_key_box(command.x1 - term->h_tic, command.y1 - term->v_tic);
3128 wxt_update_key_box(command.x1 + term->h_tic, command.y1 + term->v_tic);
3129 }
3130 gp_cairo_draw_point(&plot, command.x1, command.y1, command.integer_value);
3131 /*
3132 * If we detect that we have just drawn a point with active hypertext,
3133 * save the position and the text to draw after everything else.
3134 */
3135 if (current_href && !wxt_in_key_sample) {
3136 int xnow = gnuplot_x(&plot, mouse_x);
3137 int ynow = term->ymax - gnuplot_y(&plot, mouse_y);
3138 int size = 3 * plot.pointsize * plot.oversampling_scale;
3139 if (abs(xnow - (int)command.x1) < size && abs(ynow - (int)command.y1) < size) {
3140 wxt_display_hypertext = current_href;
3141 wxt_display_anchor.x = command.x1;
3142 wxt_display_anchor.y = command.y1;
3143 }
3144 current_href = NULL;
3145 }
3146 return;
3147 case command_justify :
3148 gp_cairo_set_justify(&plot,command.mode);
3149 text_justification_mode = command.mode;
3150 return;
3151 case command_put_text :
3152 if (wxt_in_key_sample) {
3153 int slen = gp_strlen(command.string) * term->h_char * 0.75;
3154 if (text_justification_mode == RIGHT) slen = -slen;
3155 wxt_update_key_box(command.x1, command.y1);
3156 wxt_update_key_box(command.x1 + slen, command.y1 - term->v_tic);
3157 }
3158 gp_cairo_draw_text(&plot, command.x1, command.y1, command.string, NULL, NULL);
3159 return;
3160 case command_enhanced_init :
3161 if (wxt_in_key_sample) {
3162 int slen = command.integer_value * term->h_char * 0.75;
3163 if (text_justification_mode == RIGHT) slen = -slen;
3164 wxt_update_key_box(command.x1, command.y1);
3165 wxt_update_key_box(command.x1 + slen, command.y1 - term->v_tic);
3166 }
3167 gp_cairo_enhanced_init(&plot, command.integer_value);
3168 return;
3169 case command_enhanced_finish :
3170 gp_cairo_enhanced_finish(&plot, command.x1, command.y1);
3171 return;
3172 case command_enhanced_flush :
3173 gp_cairo_enhanced_flush(&plot);
3174 return;
3175 case command_enhanced_open :
3176 gp_cairo_enhanced_open(&plot, command.string, command.double_value,
3177 command.double_value2, command.integer_value2 & 1, (command.integer_value2 & 2) >> 1, command.integer_value);
3178 return;
3179 case command_enhanced_writec :
3180 gp_cairo_enhanced_writec(&plot, command.integer_value);
3181 return;
3182 case command_set_font :
3183 gp_cairo_set_font(&plot, command.string, command.integer_value);
3184 return;
3185 case command_linewidth :
3186 gp_cairo_set_linewidth(&plot, command.double_value);;
3187 return;
3188 case command_text_angle :
3189 gp_cairo_set_textangle(&plot, command.double_value);
3190 return;
3191 case command_fillbox :
3192 if (wxt_in_key_sample) {
3193 wxt_update_key_box(command.x1, command.y1);
3194 wxt_update_key_box(command.x1+command.x2, command.y1-command.y2);
3195 }
3196 gp_cairo_draw_fillbox(&plot, command.x1, command.y1,
3197 command.x2, command.y2,
3198 command.integer_value);
3199 return;
3200 case command_image :
3201 gp_cairo_draw_image(&plot, command.image,
3202 command.x1, command.y1,
3203 command.x2, command.y2,
3204 command.x3, command.y3,
3205 command.x4, command.y4,
3206 command.integer_value, command.integer_value2);
3207 return;
3208 case command_boxed_text :
3209 gp_cairo_boxed_text(&plot, command.x1, command.y1, command.integer_value);
3210 return;
3211 case command_layer :
3212 switch (command.integer_value)
3213 {
3214 case TERM_LAYER_RESET:
3215 case TERM_LAYER_RESET_PLOTNO:
3216 wxt_cur_plotno = 0;
3217 break;
3218 case TERM_LAYER_BEFORE_PLOT:
3219 wxt_cur_plotno++;
3220 wxt_in_plot = TRUE;
3221 current_href = NULL;
3222 break;
3223 case TERM_LAYER_AFTER_PLOT:
3224 wxt_in_plot = FALSE;
3225 break;
3226 case TERM_LAYER_BEGIN_KEYSAMPLE:
3227 wxt_in_key_sample = TRUE;
3228 gp_cairo_boxed_text(&plot, -1, -1, TEXTBOX_INIT);
3229 break;
3230 case TERM_LAYER_END_KEYSAMPLE:
3231 wxt_in_key_sample = FALSE;
3232 break;
3233 default:
3234 break;
3235 }
3236 return;
3237 }
3238 }
3239
wxt_cairo_draw_hypertext()3240 void wxtPanel::wxt_cairo_draw_hypertext()
3241 {
3242 /* FIXME: Properly, we should save and restore all plot properties, */
3243 /* but since this box is the very last thing in the plot.... */
3244 double save_fontsize = plot.fontsize;
3245 int save_fontstyle = plot.fontstyle;
3246 int save_fontweight = plot.fontweight;
3247
3248 rgb_color grey = {.9, .9, .9};
3249 int width = 0;
3250 int height = 0;
3251
3252 /* Text beginning "image(options):foo" is a request to pop-up foo */
3253 const char *display_text = wxt_display_hypertext;
3254 if (!strncmp("image", wxt_display_hypertext, 5)) {
3255 const char *imagefile = strchr(wxt_display_hypertext, ':');
3256 if (imagefile) {
3257 wxt_cairo_draw_hyperimage();
3258 display_text = imagefile+1;
3259 }
3260 }
3261
3262 if (wxt_hypertext_fontname) {
3263 gp_cairo_set_font(&plot, wxt_hypertext_fontname, wxt_hypertext_fontsize);
3264 plot.fontstyle = wxt_hypertext_fontstyle;
3265 plot.fontweight = wxt_hypertext_fontweight;
3266 }
3267
3268 plot.justify_mode = LEFT;
3269 gp_cairo_draw_text(&plot,
3270 wxt_display_anchor.x + term->h_char,
3271 wxt_display_anchor.y + term->v_char / 2,
3272 display_text, &width, &height);
3273
3274 gp_cairo_set_color(&plot, grey, 0.3);
3275 gp_cairo_draw_fillbox(&plot,
3276 wxt_display_anchor.x + term->h_char,
3277 wxt_display_anchor.y + height,
3278 width, height, FS_OPAQUE);
3279
3280 gp_cairo_set_color(&plot, gp_cairo_linetype2color(-1), 0.0);
3281 gp_cairo_draw_text(&plot,
3282 wxt_display_anchor.x + term->h_char,
3283 wxt_display_anchor.y + term->v_char / 2,
3284 display_text, NULL, NULL);
3285
3286 /* FIXME: Incomplete restoration of previous state */
3287 plot.fontsize = save_fontsize;
3288 plot.fontstyle = save_fontstyle;
3289 plot.fontweight = save_fontweight;
3290 }
3291
wxt_cairo_draw_hyperimage()3292 void wxtPanel::wxt_cairo_draw_hyperimage()
3293 {
3294 unsigned int width=0, height=0;
3295 double scale_x, scale_y;
3296 double anchor_x, anchor_y;
3297 char *imagefile;
3298 cairo_surface_t *image;
3299 cairo_pattern_t *pattern;
3300 cairo_matrix_t matrix;
3301
3302 /* Optional width and height from hypertext string */
3303 if (wxt_display_hypertext[5] == '(')
3304 sscanf( &wxt_display_hypertext[6], "%5u,%5u", &width, &height );
3305 if (width == 0) width = 300;
3306 if (height == 0) height = 200;
3307
3308 /* Extract file name from string of form image(options):filename */
3309 imagefile = (char *)strchr(wxt_display_hypertext, ':');
3310 if (!imagefile)
3311 return;
3312 else do { imagefile++; }
3313 while (*imagefile == ' ');
3314
3315 /* Open png file */
3316 imagefile = strdup(imagefile);
3317 if (strchr(imagefile,'\n'))
3318 *strchr(imagefile,'\n') = '\0';
3319 image = cairo_image_surface_create_from_png(imagefile);
3320 free(imagefile);
3321 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
3322 cairo_surface_destroy(image);
3323 return;
3324 }
3325
3326 /* Rescale image to allocated size on screen */
3327 scale_x = (double)cairo_image_surface_get_width(image) / (double)(width);
3328 scale_y = (double)cairo_image_surface_get_height(image) / (double)(height);
3329 scale_x /= (double)GP_CAIRO_SCALE;
3330 scale_y /= (double)GP_CAIRO_SCALE;
3331
3332 /* Bounce off right and bottom edges of frame */
3333 anchor_x = wxt_display_anchor.x;
3334 anchor_y = wxt_display_anchor.y;
3335 if (anchor_x + width*GP_CAIRO_SCALE > term->xmax)
3336 anchor_x -= width*GP_CAIRO_SCALE;
3337 if (anchor_y + height*GP_CAIRO_SCALE > term->ymax)
3338 anchor_y -= height*GP_CAIRO_SCALE;
3339
3340 cairo_save(plot.cr);
3341 pattern = cairo_pattern_create_for_surface(image);
3342 cairo_pattern_set_filter( pattern, CAIRO_FILTER_BEST );
3343 cairo_matrix_init_scale( &matrix, scale_x, scale_y );
3344 cairo_matrix_translate( &matrix, -anchor_x, -anchor_y );
3345 cairo_pattern_set_matrix( pattern, &matrix );
3346 cairo_set_source( plot.cr, pattern );
3347
3348 cairo_paint(plot.cr);
3349 cairo_restore(plot.cr);
3350 cairo_pattern_destroy(pattern);
3351 cairo_surface_destroy(image);
3352 }
3353
3354
3355 /* given a plot number (id), return the associated plot structure */
wxt_findwindowbyid(wxWindowID id)3356 wxt_window_t* wxt_findwindowbyid(wxWindowID id)
3357 {
3358 size_t i;
3359 for(i=0;i<wxt_window_list.size();++i) {
3360 if (wxt_window_list[i].id == id)
3361 return &(wxt_window_list[i]);
3362 }
3363 return NULL;
3364 }
3365
3366 /*----------------------------------------------------------------------------
3367 * raise-lower functions
3368 *----------------------------------------------------------------------------*/
3369
wxt_raise_window(wxt_window_t * window,bool force)3370 void wxt_raise_window(wxt_window_t* window, bool force)
3371 {
3372 FPRINTF((stderr,"raise window\n"));
3373
3374 window->frame->Show(true);
3375
3376 if (wxt_raise != no || force) {
3377 #ifdef USE_GTK
3378 /* Raise() in wxGTK call wxTopLevelGTK::Raise()
3379 * which also gives the focus to the window.
3380 * Refresh() also must be called, otherwise
3381 * the raise won't happen immediately */
3382 window->frame->panel->Refresh(false);
3383 gdk_window_raise(gtk_widget_get_window(window->frame->GetHandle()));
3384 #else
3385 #ifdef __WXMSW__
3386 // If gnuplot is invoked "hidden", the very first Show() is ignored.
3387 // FIXME: Revert to the Windows API, since I could not manage to get
3388 // it right using wxWidget class methods only.
3389 if (!IsWindowVisible(window->frame->GetHandle()))
3390 ShowWindow(window->frame->GetHandle(), SW_SHOWNORMAL);
3391 // Only restore the window if it is iconized. In particular
3392 // leave it alone if it is maximized or "snapped".
3393 if (window->frame->IsIconized())
3394 #endif
3395 window->frame->Restore();
3396 window->frame->Raise();
3397 #endif /*USE_GTK */
3398 }
3399 }
3400
3401
wxt_lower_window(wxt_window_t * window)3402 void wxt_lower_window(wxt_window_t* window)
3403 {
3404 #ifdef USE_GTK
3405 window->frame->panel->Refresh(false);
3406 gdk_window_lower(gtk_widget_get_window(window->frame->GetHandle()));
3407 #else
3408 window->frame->Lower();
3409 #endif /* USE_GTK */
3410 }
3411
3412
3413 /* raise the plot with given number */
wxt_raise_terminal_window(int number)3414 void wxt_raise_terminal_window(int number)
3415 {
3416 wxt_window_t *window;
3417
3418 if (wxt_status != STATUS_OK)
3419 return;
3420
3421 wxt_sigint_init();
3422
3423 wxt_MutexGuiEnter();
3424 if ((window = wxt_findwindowbyid(number))) {
3425 FPRINTF((stderr,"wxt : raise window %d\n", number));
3426 wxt_raise_window(window, true);
3427 }
3428 wxt_MutexGuiLeave();
3429
3430 wxt_sigint_check();
3431 wxt_sigint_restore();
3432 }
3433
3434 /* raise the plot the whole group */
wxt_raise_terminal_group()3435 void wxt_raise_terminal_group()
3436 {
3437 /* declare the iterator */
3438 std::vector<wxt_window_t>::iterator wxt_iter;
3439
3440 if (wxt_status != STATUS_OK)
3441 return;
3442
3443 wxt_sigint_init();
3444
3445 wxt_MutexGuiEnter();
3446 for(wxt_iter = wxt_window_list.begin(); wxt_iter != wxt_window_list.end(); wxt_iter++) {
3447 FPRINTF((stderr,"wxt : raise window %d\n", wxt_iter->id));
3448 /* FIXME Why does wxt_iter not work directly? */
3449 wxt_raise_window(&(*wxt_iter), true);
3450 }
3451 wxt_MutexGuiLeave();
3452
3453 wxt_sigint_check();
3454 wxt_sigint_restore();
3455 }
3456
3457 /* lower the plot with given number */
wxt_lower_terminal_window(int number)3458 void wxt_lower_terminal_window(int number)
3459 {
3460 wxt_window_t *window;
3461
3462 if (wxt_status != STATUS_OK)
3463 return;
3464
3465 wxt_sigint_init();
3466
3467 wxt_MutexGuiEnter();
3468 if ((window = wxt_findwindowbyid(number))) {
3469 FPRINTF((stderr,"wxt : lower window %d\n",number));
3470 wxt_lower_window(window);
3471 }
3472 wxt_MutexGuiLeave();
3473
3474 wxt_sigint_check();
3475 wxt_sigint_restore();
3476 }
3477
3478 /* lower the plot the whole group */
wxt_lower_terminal_group()3479 void wxt_lower_terminal_group()
3480 {
3481 /* declare the iterator */
3482 std::vector<wxt_window_t>::iterator wxt_iter;
3483
3484 if (wxt_status != STATUS_OK)
3485 return;
3486
3487 wxt_sigint_init();
3488
3489 wxt_MutexGuiEnter();
3490 for(wxt_iter = wxt_window_list.begin(); wxt_iter != wxt_window_list.end(); wxt_iter++) {
3491 FPRINTF((stderr,"wxt : lower window %d\n",wxt_iter->id));
3492 wxt_lower_window(&(*wxt_iter));
3493 }
3494 wxt_MutexGuiLeave();
3495
3496 wxt_sigint_check();
3497 wxt_sigint_restore();
3498 }
3499
3500 /* close the specified window */
wxt_close_terminal_window(int number)3501 void wxt_close_terminal_window(int number)
3502 {
3503 wxt_window_t *window;
3504
3505 if (wxt_status != STATUS_OK)
3506 return;
3507
3508 if ((window = wxt_findwindowbyid(number))) {
3509 FPRINTF((stderr,"wxt : close window %d\n",number));
3510
3511 wxCloseEvent event(wxEVT_CLOSE_WINDOW, window->id);
3512 event.SetCanVeto(true);
3513
3514 wxt_sigint_init();
3515 wxt_MutexGuiEnter();
3516
3517 window->frame->SendEvent(event);
3518
3519 wxt_MutexGuiLeave();
3520 wxt_sigint_check();
3521 wxt_sigint_restore();
3522 }
3523 }
3524
3525
3526 /* The following two routines allow us to update the cursor position
3527 * in the specified window even if the window is not active
3528 */
3529
mouse_to_axis(int mouse_coord,wxt_axis_state_t * axis)3530 static double mouse_to_axis(int mouse_coord, wxt_axis_state_t *axis)
3531 {
3532 double axis_coord;
3533
3534 if (axis->term_scale == 0.0)
3535 return 0;
3536 axis_coord = axis->min
3537 + ((double)mouse_coord - axis->term_lower) / axis->term_scale;
3538 if (axis->logbase > 0)
3539 axis_coord = exp(axis_coord * axis->logbase);
3540
3541 return axis_coord;
3542 }
3543
wxt_update_mousecoords_in_window(int number,int mx,int my)3544 static void wxt_update_mousecoords_in_window(int number, int mx, int my)
3545 {
3546 wxt_window_t *window;
3547
3548 if (wxt_status != STATUS_OK)
3549 return;
3550
3551 if ((window = wxt_findwindowbyid(number))) {
3552
3553 /* TODO: rescale mx and my using stored per-plot scale info */
3554 char mouse_format[66];
3555 char *m = mouse_format;
3556 double x, y, x2, y2;
3557
3558 if (window->axis_mask & (1<<0)) {
3559 x = mouse_to_axis(mx, &window->axis_state[0]);
3560 sprintf(m, "x= %10g %c", x, '\0');
3561 m += 17;
3562 }
3563 if (window->axis_mask & (1<<1)) {
3564 y = mouse_to_axis(my, &window->axis_state[1]);
3565 sprintf(m, "y= %10g %c", y, '\0');
3566 m += 17;
3567 }
3568 if (window->axis_mask & (1<<2)) {
3569 x2 = mouse_to_axis(mx, &window->axis_state[2]);
3570 sprintf(m, "x2= %10g %c", x2, '\0');
3571 m += 17;
3572 }
3573 if (window->axis_mask & (1<<3)) {
3574 y2 = mouse_to_axis(my, &window->axis_state[3]);
3575 sprintf(m, "y2= %10g %c", y2, '\0');
3576 m += 15;
3577 }
3578
3579 FPRINTF((stderr,"wxt : update mouse coords in window %d\n",number));
3580 window->frame->SetStatusText(wxString(mouse_format, wxConvLocal));
3581 }
3582
3583 }
3584
3585 /* update the window title */
wxt_update_title(int number)3586 void wxt_update_title(int number)
3587 {
3588 wxt_window_t *window;
3589 wxString title;
3590
3591 if (wxt_status != STATUS_OK)
3592 return;
3593
3594 wxt_sigint_init();
3595
3596 wxt_MutexGuiEnter();
3597
3598 if ((window = wxt_findwindowbyid(number))) {
3599 FPRINTF((stderr,"wxt : close window %d\n",number));
3600 if (strlen(wxt_title)) {
3601 /* NOTE : this assumes that the title is encoded in the locale charset.
3602 * This is probably a good assumption, but it is not guaranteed !
3603 * May be improved by using gnuplot encoding setting. */
3604 title << wxString(wxt_title, wxConvLocal);
3605 } else
3606 title.Printf(wxT("Gnuplot (window id : %d)"), window->id);
3607
3608 window->frame->SetTitle(title);
3609 }
3610
3611 wxt_MutexGuiLeave();
3612
3613 wxt_sigint_check();
3614 wxt_sigint_restore();
3615 }
3616
3617 /* update the size of the plot area, resizes the whole window consequently */
wxt_update_size(int number)3618 void wxt_update_size(int number)
3619 {
3620 wxt_window_t *window;
3621
3622 if (wxt_status != STATUS_OK)
3623 return;
3624
3625 wxt_sigint_init();
3626
3627 wxt_MutexGuiEnter();
3628
3629 if ((window = wxt_findwindowbyid(number))) {
3630 FPRINTF((stderr,"wxt : update size of window %d\n",number));
3631 window->frame->SetClientSize( wxSize(wxt_width, wxt_height) );
3632 }
3633
3634 wxt_MutexGuiLeave();
3635
3636 wxt_sigint_check();
3637 wxt_sigint_restore();
3638 }
3639
3640 /* update the position of the plot window */
wxt_update_position(int number)3641 void wxt_update_position(int number)
3642 {
3643 wxt_window_t *window;
3644
3645 if (wxt_status != STATUS_OK)
3646 return;
3647
3648 wxt_sigint_init();
3649
3650 wxt_MutexGuiEnter();
3651
3652 if ((window = wxt_findwindowbyid(number))) {
3653 FPRINTF((stderr,"wxt : update position of window %d\n",number));
3654 window->frame->SetPosition( wxPoint(wxt_posx, wxt_posy) );
3655 }
3656
3657 wxt_MutexGuiLeave();
3658
3659 wxt_sigint_check();
3660 wxt_sigint_restore();
3661 }
3662
3663
3664 /* print the current plot */
wxt_screen_dump(void)3665 void wxt_screen_dump(void)
3666 {
3667 #ifdef WXT_PRINT
3668 wxCommandEvent event;
3669 if (wxt_current_window && wxt_current_window->frame && wxt_current_window->frame->IsShown())
3670 wxt_current_window->frame->OnPrint(event);
3671 else
3672 int_error(c_token, "No active plot.");
3673 #else
3674 int_error(c_token, "Printing support for the wxt terminal is not available on this platform.");
3675 #endif
3676 }
3677
3678
3679 /* --------------------------------------------------------
3680 * Cairo stuff
3681 * --------------------------------------------------------*/
3682
wxt_cairo_create_context()3683 void wxtPanel::wxt_cairo_create_context()
3684 {
3685 cairo_surface_t *fake_surface;
3686
3687 if ( plot.cr )
3688 cairo_destroy(plot.cr);
3689
3690 if ( wxt_cairo_create_platform_context() ) {
3691 /* we are not able to create a true cairo context,
3692 * but will create a fake one to give proper initialisation */
3693 FPRINTF((stderr,"creating temporary fake surface\n"));
3694 fake_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
3695 plot.cr = cairo_create( fake_surface );
3696 cairo_surface_destroy( fake_surface );
3697 /* this flag will make the program retry later */
3698 plot.success = FALSE;
3699 } else {
3700 plot.success = TRUE;
3701 }
3702
3703 /* set the transformation matrix of the context, and other details */
3704 gp_cairo_initialize_context(&plot);
3705 }
3706
wxt_cairo_free_context()3707 void wxtPanel::wxt_cairo_free_context()
3708 {
3709 if (plot.cr)
3710 cairo_destroy(plot.cr);
3711
3712 if (plot.success)
3713 wxt_cairo_free_platform_context();
3714 }
3715
3716
3717 #ifdef GTK_SURFACE
3718 /* create a cairo context, where the plot will be drawn on
3719 * If there is an error, return 1, otherwise return 0 */
wxt_cairo_create_platform_context()3720 int wxtPanel::wxt_cairo_create_platform_context()
3721 {
3722 cairo_surface_t *surface;
3723 wxClientDC dc(this);
3724
3725 FPRINTF((stderr,"wxt_cairo_create_context\n"));
3726
3727 /* free gdkpixmap */
3728 wxt_cairo_free_platform_context();
3729
3730 /* GetWindow is a wxGTK specific wxDC method that returns
3731 * the GdkWindow on which painting should be done */
3732
3733 if ( !GDK_IS_DRAWABLE(dc.GetWindow()) )
3734 return 1;
3735
3736 gdkpixmap = gdk_pixmap_new(dc.GetWindow(), plot.device_xmax, plot.device_ymax, -1);
3737
3738 if ( !GDK_IS_DRAWABLE(gdkpixmap) )
3739 return 1;
3740
3741 plot.cr = gdk_cairo_create(gdkpixmap);
3742 return 0;
3743 }
3744
wxt_cairo_free_platform_context()3745 void wxtPanel::wxt_cairo_free_platform_context()
3746 {
3747 if (gdkpixmap)
3748 g_object_unref(gdkpixmap);
3749 }
3750
3751 #elif defined(__WXMSW__)
3752 /* create a cairo context, where the plot will be drawn on
3753 * If there is an error, return 1, otherwise return 0 */
wxt_cairo_create_platform_context()3754 int wxtPanel::wxt_cairo_create_platform_context()
3755 {
3756 cairo_surface_t *surface;
3757 wxClientDC dc(this);
3758
3759 FPRINTF((stderr,"wxt_cairo_create_context\n"));
3760
3761 wxt_cairo_free_platform_context();
3762
3763 /* GetHDC is a wxMSW specific wxDC method that returns
3764 * the HDC on which painting should be done */
3765 surface = cairo_win32_surface_create_with_ddb(
3766 (HDC) dc.GetHDC(), CAIRO_FORMAT_RGB24,
3767 plot.device_xmax, plot.device_ymax);
3768 plot.cr = cairo_create(surface);
3769 cairo_surface_destroy(surface);
3770 return 0;
3771 }
3772
wxt_cairo_free_platform_context()3773 void wxtPanel::wxt_cairo_free_platform_context()
3774 {
3775 }
3776
3777 #else /* generic image surface */
3778 /* create a cairo context, where the plot will be drawn on
3779 * If there is an error, return 1, otherwise return 0 */
wxt_cairo_create_platform_context()3780 int wxtPanel::wxt_cairo_create_platform_context()
3781 {
3782 int width, height;
3783 cairo_surface_t *surface;
3784
3785 FPRINTF((stderr,"wxt_cairo_create_context\n"));
3786
3787 if (data32)
3788 delete[] data32;
3789
3790 width = plot.device_xmax;
3791 height = plot.device_ymax;
3792
3793 if (width<1||height<1)
3794 return 1;
3795
3796 data32 = new unsigned int[width*height];
3797
3798 surface = cairo_image_surface_create_for_data((unsigned char*) data32,
3799 CAIRO_FORMAT_ARGB32, width, height, 4*width);
3800 plot.cr = cairo_create(surface);
3801 cairo_surface_destroy( surface );
3802 return 0;
3803 }
3804
3805
3806 /* create a wxBitmap (to be painted to the screen) from the buffered cairo surface. */
wxt_cairo_create_bitmap()3807 void wxtPanel::wxt_cairo_create_bitmap()
3808 {
3809 int width, height;
3810 unsigned char *data24;
3811 wxImage *image;
3812 wxBitmap *old_bitmap = NULL;
3813
3814 if (!data32)
3815 return;
3816
3817 width = plot.device_xmax;
3818 height = plot.device_ymax;
3819
3820 data24 = new unsigned char[3*width*height];
3821
3822 /* data32 is the cairo image buffer, upper bits are alpha, then r, g and b
3823 * Depends on endianess !
3824 * It is converted to RGBRGB... in data24 */
3825 for (int i=0; i<width*height; ++i) {
3826 *(data24+3*i)=*(data32+i)>>16;
3827 *(data24+3*i+1)=*(data32+i)>>8;
3828 *(data24+3*i+2)=*(data32+i);
3829 }
3830
3831 /* create a wxImage from data24 */
3832 image = new wxImage(width, height, data24, true);
3833
3834 /* In wxWidgets 2.8 it was fine to delete the old bitmap right here */
3835 /* but in wxWidgets 3.0 this causes a use-after-free fault. */
3836 /* So now we delay deletion until after a new bitmap is assigned. */
3837 old_bitmap = cairo_bitmap;
3838
3839 /* create a wxBitmap from the wxImage. */
3840 cairo_bitmap = new wxBitmap( *image );
3841
3842 /* free memory */
3843 delete image;
3844 delete[] data24;
3845 delete old_bitmap;
3846 }
3847
3848
wxt_cairo_free_platform_context()3849 void wxtPanel::wxt_cairo_free_platform_context()
3850 {
3851 if (data32) {
3852 delete[] data32;
3853 data32 = NULL;
3854 }
3855 if (cairo_bitmap) {
3856 delete cairo_bitmap;
3857 cairo_bitmap = NULL;
3858 }
3859 }
3860 #endif /* IMAGE_SURFACE */
3861
3862 /* --------------------------------------------------------
3863 * events handling
3864 * --------------------------------------------------------*/
3865
3866 /* Debugging events and _waitforinput adds a lot of lines of output
3867 * (~4 lines for an input character, and a few lines for each mouse move)
3868 * To debug it, define DEBUG and WXTDEBUGINPUT */
3869
3870 #ifdef WXTDEBUGINPUT
3871 # define FPRINTF2(a) FPRINTF(a)
3872 #else
3873 # define FPRINTF2(a)
3874 #endif
3875
3876 #ifdef USE_MOUSE
3877 /* process one event, returns true if it ends the pause */
wxt_process_one_event(struct gp_event_t * event)3878 bool wxt_process_one_event(struct gp_event_t *event)
3879 {
3880 do_event( event );
3881 if (event->type == GE_buttonrelease && (paused_for_mouse & PAUSE_CLICK)) {
3882 int button = event->par1;
3883 if (button == 1 && (paused_for_mouse & PAUSE_BUTTON1))
3884 paused_for_mouse = 0;
3885 if (button == 2 && (paused_for_mouse & PAUSE_BUTTON2))
3886 paused_for_mouse = 0;
3887 if (button == 3 && (paused_for_mouse & PAUSE_BUTTON3))
3888 paused_for_mouse = 0;
3889 if (paused_for_mouse == 0)
3890 return true;
3891 }
3892 if (event->type == GE_keypress && (paused_for_mouse & PAUSE_KEYSTROKE)) {
3893 /* Ignore NULL keycode */
3894 if (event->par1 > '\0') {
3895 paused_for_mouse = 0;
3896 return true;
3897 }
3898 }
3899 return false;
3900 }
3901
3902 /* Similar to gp_exec_event(),
3903 * put the event sent by the terminal in a list,
3904 * to be processed by the main thread.
3905 * returns true if the event has really been processed.
3906 */
wxt_exec_event(int type,int mx,int my,int par1,int par2,wxWindowID id)3907 bool wxt_exec_event(int type, int mx, int my, int par1, int par2, wxWindowID id)
3908 {
3909 struct gp_event_t event;
3910
3911 /* Allow a distinction between keys attached to "bind" or "bind all" */
3912 if ( id != wxt_window_number ) {
3913
3914 if (type == GE_motion) {
3915 /* Update mouse coordinates locally and echo back to originating window */
3916 wxt_update_mousecoords_in_window(id, mx, my);
3917 return true;
3918 }
3919
3920 if (type == GE_keypress)
3921 type = GE_keypress_old;
3922 if (type == GE_buttonpress)
3923 type = GE_buttonpress_old;
3924 if (type == GE_buttonrelease)
3925 type = GE_buttonrelease_old;
3926 else /* Other special cases? */
3927 return false;
3928 }
3929
3930 event.type = type;
3931 event.mx = mx;
3932 event.my = my;
3933 event.par1 = par1;
3934 event.par2 = par2;
3935 event.winid = id;
3936
3937 #if defined(_WIN32)
3938 wxt_process_one_event(&event);
3939 return true;
3940 #elif defined(WXT_MONOTHREADED)
3941 if (wxt_process_one_event(&event))
3942 if (debug == 44) ungetc('\n',stdin);
3943 return true;
3944 #else
3945 if (!wxt_handling_persist)
3946 {
3947 if (wxt_sendevent_fd<0) {
3948 FPRINTF((stderr,"not sending event, wxt_sendevent_fd error\n"));
3949 return false;
3950 }
3951
3952 if (write(wxt_sendevent_fd, (char*) &event, sizeof(event))<0) {
3953 wxt_sendevent_fd = -1;
3954 fprintf(stderr,"not sending event, write error on wxt_sendevent_fd\n");
3955 return false;
3956 }
3957 }
3958 else
3959 {
3960 wxt_process_one_event(&event);
3961 }
3962
3963 return true;
3964 #endif /* Windows or single-threaded or default */
3965 }
3966
3967 #ifdef WXT_MULTITHREADED
3968 /* Implements waitforinput used in wxt.trm
3969 * Returns the next input character, meanwhile treats terminal events */
wxt_waitforinput(int options)3970 int wxt_waitforinput(int options)
3971 {
3972 /* wxt_waitforinput *is* launched immediately after the wxWidgets terminal
3973 * is set using 'set term wxt' whereas wxt_init has not been called.
3974 * So we must ensure that the library has been initialized
3975 * before using any wxwidgets functions.
3976 * When we just come back from SIGINT,
3977 * we must process window events, so the check is not
3978 * wxt_status != STATUS_OK */
3979 if (wxt_status == STATUS_UNINITIALIZED)
3980 return (options == TERM_ONLY_CHECK_MOUSING) ? '\0' : getc(stdin);
3981
3982 if (wxt_event_fd<0) {
3983 if (paused_for_mouse)
3984 int_error(NO_CARET, "wxt communication error, wxt_event_fd<0");
3985 FPRINTF((stderr,"wxt communication error, wxt_event_fd<0\n"));
3986 return (options == TERM_ONLY_CHECK_MOUSING) ? '\0' : getc(stdin);
3987 }
3988
3989 int stdin_fd = fileno(stdin);
3990 fd_set read_fds;
3991 struct timeval one_msec;
3992
3993 do {
3994 struct timeval *timeout = NULL;
3995 FD_ZERO(&read_fds);
3996 FD_SET(wxt_event_fd, &read_fds);
3997 if (!paused_for_mouse)
3998 FD_SET(stdin_fd, &read_fds);
3999
4000 /* When taking input from the console, we are willing to wait */
4001 /* here until the next character is typed. But if input is from */
4002 /* a script we just want to check for hotkeys or mouse input */
4003 /* and then leave again without waiting for stdin. */
4004 if (options == TERM_ONLY_CHECK_MOUSING) {
4005 timeout = &one_msec;
4006 one_msec.tv_sec = 0;
4007 one_msec.tv_usec = TERM_EVENT_POLL_TIMEOUT;
4008 }
4009
4010 /* HBB FIXME 2015-05-03: why no test for autoconf HAVE_SELECT here ? */
4011 int n_changed_fds = select(wxt_event_fd+1, &read_fds,
4012 NULL /* not watching for write-ready */,
4013 NULL /* not watching for exceptions */,
4014 timeout );
4015 if (n_changed_fds < 0 && errno == EINTR) {
4016 wrap_readline_signal_handler();
4017 continue;
4018 }
4019
4020 if (n_changed_fds < 0) {
4021 if (paused_for_mouse)
4022 int_error(NO_CARET, "wxt communication error: select() error");
4023 FPRINTF((stderr, "wxt communication error: select() error\n"));
4024 break;
4025 }
4026
4027 if (FD_ISSET(wxt_event_fd, &read_fds)) {
4028 /* terminal event coming */
4029 struct gp_event_t wxt_event;
4030 int n_bytes_read = read(wxt_event_fd, (void*) &wxt_event, sizeof(wxt_event));
4031 if (n_bytes_read < (int)sizeof(wxt_event)) {
4032 if (paused_for_mouse)
4033 int_error(NO_CARET, "wxt communication error, not enough bytes read");
4034 FPRINTF((stderr, "wxt communication error, not enough bytes read\n"));
4035 break;
4036 }
4037 if (wxt_process_one_event(&wxt_event)) {
4038 /* exit from paused_for_mouse */
4039 return '\0';
4040 }
4041 } else if (options == TERM_ONLY_CHECK_MOUSING) {
4042 return '\0';
4043 }
4044 } while ( paused_for_mouse || !FD_ISSET(stdin_fd, &read_fds) );
4045
4046 if (options == TERM_ONLY_CHECK_MOUSING)
4047 return '\0';
4048
4049 return getchar();
4050 }
4051 #else /* WXT_MONOTHREADED */
4052 /* Implements waitforinput used in wxt.trm
4053 * the terminal events are directly processed when they are received */
wxt_waitforinput(int options)4054 int wxt_waitforinput(int options)
4055 {
4056 #ifdef _WIN32
4057 if (options == TERM_ONLY_CHECK_MOUSING) {
4058 WinMessageLoop();
4059 return NUL;
4060 } else if (paused_for_mouse) {
4061 MSG msg;
4062 BOOL ret;
4063
4064 /* wait for next event */
4065 while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) {
4066 if (ret == -1)
4067 break;
4068 TranslateMessage(&msg);
4069 DispatchMessage(&msg);
4070 if (!paused_for_mouse)
4071 break;
4072 }
4073 return NUL;
4074 } else
4075 return getch();
4076
4077 #else /* !_WIN32 */
4078 /* Generic hybrid GUI & console message loop */
4079 /* (used mainly on MacOSX - still single threaded) */
4080 if (wxt_yield)
4081 return '\0';
4082
4083 if (wxt_status == STATUS_UNINITIALIZED)
4084 return (options == TERM_ONLY_CHECK_MOUSING) ? '\0' : getc(stdin);
4085
4086 if (options==TERM_ONLY_CHECK_MOUSING) {
4087 // If we're just checking mouse status, yield to the app for a while
4088 if (wxTheApp) {
4089 wxTheApp->Yield();
4090 wxt_yield = 0;
4091 }
4092 return '\0'; // gets dropped on floor
4093 }
4094
4095 while (wxTheApp) {
4096 // Loop with timeout of 10ms until stdin is ready to read,
4097 // while also handling window events.
4098 int retval;
4099 int was_paused_for_mouse = paused_for_mouse;
4100
4101 wxt_yield = 1;
4102 wxTheApp->Yield();
4103 wxt_yield = 0;
4104
4105 struct timeval tv;
4106 fd_set read_fd;
4107 tv.tv_sec = 0;
4108 tv.tv_usec = 10000;
4109 FD_ZERO(&read_fd);
4110 FD_SET(0, &read_fd);
4111 retval = select(1, &read_fd, NULL, NULL, &tv);
4112 if (retval == -1)
4113 int_error(NO_CARET, "input select error");
4114 else if (was_paused_for_mouse && !paused_for_mouse)
4115 /* The wxTheApp event loop caught a signal */
4116 return '\0';
4117 else if (paused_for_mouse && !isatty(0))
4118 /* We are still paused for mouse but input is from a pipe */
4119 usleep(50000);
4120 else if (retval)
4121 /* select indicated something to read on stdin */
4122 return getchar();
4123 }
4124 return getchar();
4125 #endif
4126 }
4127 #endif /* WXT_MONOTHREADED || WXT_MULTITHREADED */
4128
4129 #endif /*USE_MOUSE*/
4130
4131 /* --------------------------------------------------------
4132 * 'persist' option handling
4133 * --------------------------------------------------------*/
4134
wxt_active_window_opened(void)4135 TBOOLEAN wxt_active_window_opened(void)
4136 {
4137 return ((wxt_current_window != NULL) &&
4138 (wxt_current_window->id == wxt_window_number) &&
4139 wxt_current_window->frame->IsShown());
4140 }
4141
4142
4143 /* returns true if at least one plot window is opened.
4144 * Used to handle 'persist' */
wxt_window_opened(void)4145 TBOOLEAN wxt_window_opened(void)
4146 {
4147 std::vector<wxt_window_t>::iterator wxt_iter; /*declare the iterator*/
4148
4149 wxt_MutexGuiEnter();
4150 for (wxt_iter = wxt_window_list.begin(); wxt_iter != wxt_window_list.end(); wxt_iter++) {
4151 if (wxt_iter->frame->IsShown()) {
4152 wxt_MutexGuiLeave();
4153 return TRUE;
4154 }
4155 }
4156 wxt_MutexGuiLeave();
4157 return FALSE;
4158 }
4159
4160
4161 /* Called when gnuplot exits.
4162 * Handle the 'persist' setting, ie will continue
4163 * to handle events and will return when
4164 * all the plot windows are closed. */
wxt_atexit()4165 void wxt_atexit()
4166 {
4167 #ifndef _WIN32
4168 int openwindows = 0;
4169 #endif
4170 int persist_setting;
4171
4172 if (wxt_status == STATUS_UNINITIALIZED)
4173 return;
4174
4175 #ifdef WXT_MULTITHREADED
4176 /* send a message to exit the main loop */
4177 /* protect the following from interrupt */
4178 wxt_sigint_init();
4179
4180 wxCommandEvent event(wxExitLoopEvent);
4181 wxt_MutexGuiEnter();
4182 dynamic_cast<wxtApp*>(wxTheApp)->SendEvent( event );
4183 wxt_MutexGuiLeave();
4184
4185 /* handle eventual interrupt, and restore original sigint handler */
4186 wxt_sigint_check();
4187 wxt_sigint_restore();
4188
4189 FPRINTF((stderr,"gui thread status %d %d %d\n",
4190 thread->IsDetached(),
4191 thread->IsAlive(),
4192 thread->IsRunning() ));
4193
4194 /* wait for the gui thread to exit */
4195 thread->Wait();
4196 delete thread;
4197 #endif /* WXT_MULTITHREADED */
4198
4199 /* first look for command_line setting */
4200 if (wxt_persist==UNSET && persist_cl)
4201 wxt_persist = TRUE;
4202
4203 wxConfigBase *pConfig = wxConfigBase::Get();
4204
4205 /* then look for persistent configuration setting */
4206 if (wxt_persist==UNSET) {
4207 if (pConfig->Read(wxT("persist"),&persist_setting))
4208 wxt_persist = persist_setting?yes:no;
4209 }
4210
4211 /* and let's go ! */
4212 if (wxt_persist==UNSET|| wxt_persist==no) {
4213 FPRINTF((stderr,"wxt_atexit: no \"persist\" setting, exit\n"));
4214 wxt_cleanup();
4215 return;
4216 }
4217
4218 /* if the user hits ctrl-c and quits again, really quit */
4219 wxt_persist = no;
4220
4221 FPRINTF((stderr,"wxt_atexit: handling \"persist\" setting\n"));
4222
4223 #ifdef _WIN32
4224 if (!persist_cl) {
4225 interactive = TRUE;
4226 while (!com_line());
4227 }
4228 #else /*_WIN32*/
4229
4230 /* process events directly */
4231 wxt_handling_persist = true;
4232
4233 /* if fork() is available, use it so that the initial gnuplot process
4234 * exits and the child process continues in the background.
4235 */
4236 /* NB:
4237 * If there are no plot windows open, then once the parent process
4238 * exits the child can receive no input and will become a zombie.
4239 * So destroy any closed window first, and only fork if some remain open.
4240 */
4241 /* declare the iterator */
4242 std::vector<wxt_window_t>::iterator wxt_iter;
4243
4244 int i;
4245 for(wxt_iter = wxt_window_list.begin(), i=0;
4246 wxt_iter != wxt_window_list.end(); wxt_iter++, i++)
4247 {
4248 TBOOLEAN state = wxt_iter->frame->IsShown();
4249 FPRINTF((stderr,"\tChecking window %d : %s shown\n", i, state?"":"not "));
4250 if (state) {
4251 openwindows++;
4252 /* Disable any toolbar widgets that would require parental help */
4253 wxt_iter->frame->toolbar->EnableTool(Toolbar_Replot, false);
4254 wxt_iter->frame->toolbar->EnableTool(Toolbar_ToggleGrid, false);
4255 wxt_iter->frame->toolbar->EnableTool(Toolbar_ZoomPrevious, false);
4256 wxt_iter->frame->toolbar->EnableTool(Toolbar_ZoomNext, false);
4257 wxt_iter->frame->toolbar->EnableTool(Toolbar_Autoscale, false);
4258 wxt_iter->frame->toolbar->EnableTool(Toolbar_ExportToFile, false);
4259 } else {
4260 wxt_iter->frame->Destroy();
4261 }
4262 }
4263
4264 # ifdef HAVE_WORKING_FORK
4265 /* fork */
4266 pid_t pid;
4267
4268 if (openwindows > 0)
4269 pid = fork();
4270 else
4271 pid = -1;
4272
4273 /* the parent just exits, the child keeps going */
4274 if (!pid) {
4275 FPRINTF((stderr,"child process: running\n"));
4276 # endif /* HAVE_WORKING_FORK */
4277
4278 FPRINTF((stderr,"child process: restarting its event loop\n"));
4279 /* Some programs executing gnuplot -persist may wait for all default
4280 * handles to be closed before they consider the sub-process finished.
4281 * Using freopen() ensures that debug fprintf()s won't crash. */
4282 freopen("/dev/null","w",stdout);
4283 freopen("/dev/null","w",stderr);
4284
4285 /* (re)start gui loop */
4286 wxTheApp->OnRun();
4287
4288 FPRINTF((stderr,"child process: event loop exited\n"));
4289 # ifdef HAVE_WORKING_FORK
4290 }
4291 else
4292 {
4293 FPRINTF((stderr,"parent process: exiting, child process "\
4294 "has PID %d\n", pid));
4295 }
4296 # endif /* HAVE_WORKING_FORK */
4297 #endif /* !_WIN32 */
4298
4299 /* cleanup and quit */
4300 wxt_cleanup();
4301 }
4302
4303
4304 /* destroy everything related to wxWidgets */
wxt_cleanup()4305 void wxt_cleanup()
4306 {
4307 std::vector<wxt_window_t>::iterator wxt_iter; /*declare the iterator*/
4308
4309 if (wxt_status == STATUS_UNINITIALIZED)
4310 return;
4311
4312 FPRINTF((stderr,"wxt_cleanup: start\n"));
4313
4314 /* prevent wxt_reset (for example) from doing anything bad after that */
4315 wxt_status = STATUS_UNINITIALIZED;
4316
4317 /* protect the following from interrupt */
4318 wxt_sigint_init();
4319
4320 /* Close all open terminal windows */
4321 for(wxt_iter = wxt_window_list.begin();
4322 wxt_iter != wxt_window_list.end(); wxt_iter++)
4323 wxt_iter->frame->Destroy();
4324
4325 wxTheApp->OnExit();
4326 wxUninitialize();
4327
4328 /* handle eventual interrupt, and restore original sigint handler */
4329 wxt_sigint_check();
4330 wxt_sigint_restore();
4331
4332 FPRINTF((stderr,"wxt_cleanup: finished\n"));
4333 }
4334
4335 /* -------------------------------------
4336 * GUI Mutex helper functions for porting
4337 * ----------------------------------------*/
4338
wxt_MutexGuiEnter()4339 void wxt_MutexGuiEnter()
4340 {
4341 FPRINTF2((stderr,"locking gui mutex\n"));
4342 #ifdef WXT_MULTITHREADED
4343 if (!wxt_handling_persist)
4344 wxMutexGuiEnter();
4345 #endif /* WXT_MULTITHREADED */
4346 }
4347
wxt_MutexGuiLeave()4348 void wxt_MutexGuiLeave()
4349 {
4350 FPRINTF2((stderr,"unlocking gui mutex\n"));
4351 #ifdef WXT_MULTITHREADED
4352 if (!wxt_handling_persist)
4353 wxMutexGuiLeave();
4354 #endif /* WXT_MULTITHREADED */
4355 }
4356
4357 /* ---------------------------------------------------
4358 * SIGINT handling : as the terminal is multithreaded, it needs several mutexes.
4359 * To avoid inconsistencies and deadlock when the user hits ctrl-c,
4360 * each critical set of instructions (implying mutexes for example) should be written :
4361 * wxt_sigint_init();
4362 * < critical instructions >
4363 * wxt_sigint_check();
4364 * wxt_sigint_restore();
4365 * Or, if the critical instructions are in a loop, wxt_sigint_check() should be
4366 * called regularly in the loop.
4367 * ---------------------------------------------------*/
4368
4369
4370 /* our custom SIGINT handler, that just sets a flag */
wxt_sigint_handler(int WXUNUSED (sig))4371 void wxt_sigint_handler(int WXUNUSED(sig))
4372 {
4373 FPRINTF((stderr,"custom interrupt handler called\n"));
4374 signal(SIGINT, wxt_sigint_handler);
4375
4376 /* If this happens, it's bad. We already flagged that we want */
4377 /* to quit but nobody acted on it. This can happen if the */
4378 /* connection to the X-server is lost, which can be forced by */
4379 /* using xkill on the display window. See bug #991. */
4380 if (wxt_status == STATUS_INTERRUPT_ON_NEXT_CHECK) {
4381 fprintf(stderr,"wxt display server shutting down - no response\n");
4382 exit(-1);
4383 }
4384
4385 /* routines must check regularly for wxt_status,
4386 * and abort cleanly on STATUS_INTERRUPT_ON_NEXT_CHECK */
4387 wxt_status = STATUS_INTERRUPT_ON_NEXT_CHECK;
4388 if (wxt_current_plot)
4389 wxt_current_plot->interrupt = TRUE;
4390 }
4391
4392 /* To be called when the function has finished cleaning after facing STATUS_INTERRUPT_ON_NEXT_CHECK */
4393 /* Provided for flexibility, but use wxt_sigint_check instead directly */
wxt_sigint_return()4394 void wxt_sigint_return()
4395 {
4396 FPRINTF((stderr,"calling original interrupt handler\n"));
4397 wxt_status = STATUS_INTERRUPT;
4398 wxt_sigint_counter = 0;
4399 /* call the original sigint handler */
4400 /* this will not return !! */
4401 (*original_siginthandler)(SIGINT);
4402 }
4403
4404 /* A critical function should call this from a safe zone (no locked mutex, objects destroyed).
4405 * If the interrupt is asked, this function will not return (longjmp) */
wxt_sigint_check()4406 void wxt_sigint_check()
4407 {
4408 FPRINTF2((stderr,"checking interrupt status\n"));
4409 if (wxt_status == STATUS_INTERRUPT_ON_NEXT_CHECK)
4410 wxt_sigint_return();
4411 }
4412
4413 /* initialize our custom SIGINT handler */
4414 /* this uses a usage counter, so that it can be encapsulated without problem */
wxt_sigint_init()4415 void wxt_sigint_init()
4416 {
4417 /* put our custom sigint handler, store the original one */
4418 if (wxt_sigint_counter == 0)
4419 original_siginthandler = signal(SIGINT, wxt_sigint_handler);
4420 ++wxt_sigint_counter;
4421 FPRINTF2((stderr,"initialize custom interrupt handler %d\n",wxt_sigint_counter));
4422 }
4423
4424 /* restore the original SIGINT handler */
wxt_sigint_restore()4425 void wxt_sigint_restore()
4426 {
4427 if (wxt_sigint_counter==1)
4428 signal(SIGINT, original_siginthandler);
4429 --wxt_sigint_counter;
4430 FPRINTF2((stderr,"restore custom interrupt handler %d\n",wxt_sigint_counter));
4431 if (wxt_sigint_counter<0)
4432 fprintf(stderr,"sigint counter < 0 : error !\n");
4433 }
4434