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 &region)
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