/*
* $Id: wxt_gui.cpp,v 1.72 2009/03/26 00:49:18 sfeam Exp $
*/
/* GNUPLOT - wxt_gui.cpp */
/*[
* Copyright 2005,2006 Timothee Lecomte
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
*
*
* Alternatively, the contents of this file, apart from one portion
* that originates from other gnuplot files and is designated as such,
* may be used under the terms of the GNU General Public License
* Version 2 or later (the "GPL"), in which case the provisions of GPL
* are applicable instead of those above. If you wish to allow
* use of your version of the appropriate portion of this file only
* under the terms of the GPL and not to allow others to use your version
* of this file under the above gnuplot license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not
* delete the provisions above, a recipient may use your version of this file
* under either the GPL or the gnuplot license.
]*/
/* -----------------------------------------------------
* The following code uses the wxWidgets library, which is
* distributed under its own licence (derivated from the LGPL).
*
* You can read it at the following address :
* http://www.wxwidgets.org/licence.htm
* -----------------------------------------------------*/
/* ------------------------------------------------------
* This file implements in C++ the functions which are called by wxt.trm :
*
* It depends on the generic cairo functions,
* declared in gp_cairo.h for all the drawing work.
*
* Here is the interactive part :
* - rescaling according to the window's size,
* - mouse support (cursor position, zoom, rotation, ruler, clipboard...),
* - a toolbar to give additionnal capabilities (similar to the OS/2 terminal),
* - multiple plot windows.
*
* ------------------------------------------------------*/
/* PORTING NOTES
* Since it uses wxWidgets and Cairo routines, this code is mostly cross-platform.
* However some details have to be implemented or tweaked for each platform :
*
* 1) A generic 'image' surface is implemented as the destination surface
* for cairo drawing. But for optimal results, cairo should draw to a native
* surface corresponding to the graphical system.
* Examples :
* - a gdkpixmap when compiling for wxGTK (currently disabled because of a bug in CAIRO_OPERATOR_SATURATE),
* - a HDC when compiling for wxMSW
* - [insert your contribution here !]
*
* 2) You have to be careful with the gui main loop.
* As far as I understand :
* Some platforms (Windows ?) require that it is in the main thread.
* When compiling for Windows (wxMSW), the text window already implements it, so we
* don't have to do it, but wxWidgets still have to be initialized correctly.
* When compiling for Unix (wxGTK), we don't have one, so we launch it in a separate thread.
* For new platforms, it is necessary to figure out what is necessary.
*/
/* define DEBUG here to have debugging messages in stderr */
#include "wxt_gui.h"
/* frame icon composed of three icons of different resolutions */
#include "bitmaps/xpm/icon16x16.xpm"
#include "bitmaps/xpm/icon32x32.xpm"
#include "bitmaps/xpm/icon64x64.xpm"
/* cursors */
#include "bitmaps/xpm/cross.xpm"
#include "bitmaps/xpm/right.xpm"
#include "bitmaps/xpm/rotate.xpm"
#include "bitmaps/xpm/size.xpm"
/* Toolbar icons
* Those are embedded PNG icons previously converted to an array.
* See bitmaps/png/README for details */
#include "bitmaps/png/clipboard_png.h"
#include "bitmaps/png/replot_png.h"
#include "bitmaps/png/grid_png.h"
#include "bitmaps/png/previouszoom_png.h"
#include "bitmaps/png/nextzoom_png.h"
#include "bitmaps/png/autoscale_png.h"
#include "bitmaps/png/config_png.h"
#include "bitmaps/png/help_png.h"
/* ---------------------------------------------------------------------------
* event tables and other macros for wxWidgets
* --------------------------------------------------------------------------*/
/* the event tables connect the wxWidgets events with the functions (event
* handlers) which process them. It can be also done at run-time, but for the
* simple menu events like this the static method is much simpler.
*/
BEGIN_EVENT_TABLE( wxtApp, wxApp )
EVT_COMMAND( wxID_ANY, wxCreateWindowEvent, wxtApp::OnCreateWindow )
EVT_COMMAND( wxID_ANY, wxExitLoopEvent, wxtApp::OnExitLoop )
END_EVENT_TABLE()
BEGIN_EVENT_TABLE( wxtFrame, wxFrame )
EVT_CLOSE( wxtFrame::OnClose )
EVT_SIZE( wxtFrame::OnSize )
EVT_TOOL( Toolbar_CopyToClipboard, wxtFrame::OnCopy )
#ifdef USE_MOUSE
EVT_TOOL( Toolbar_Replot, wxtFrame::OnReplot )
EVT_TOOL( Toolbar_ToggleGrid, wxtFrame::OnToggleGrid )
EVT_TOOL( Toolbar_ZoomPrevious, wxtFrame::OnZoomPrevious )
EVT_TOOL( Toolbar_ZoomNext, wxtFrame::OnZoomNext )
EVT_TOOL( Toolbar_Autoscale, wxtFrame::OnAutoscale )
EVT_COMMAND( wxID_ANY, wxStatusTextEvent, wxtFrame::OnSetStatusText )
#endif /*USE_MOUSE*/
EVT_TOOL( Toolbar_Config, wxtFrame::OnConfig )
EVT_TOOL( Toolbar_Help, wxtFrame::OnHelp )
END_EVENT_TABLE()
BEGIN_EVENT_TABLE( wxtPanel, wxPanel )
EVT_PAINT( wxtPanel::OnPaint )
EVT_ERASE_BACKGROUND( wxtPanel::OnEraseBackground )
EVT_SIZE( wxtPanel::OnSize )
#ifdef USE_MOUSE
EVT_MOTION( wxtPanel::OnMotion )
EVT_LEFT_DOWN( wxtPanel::OnLeftDown )
EVT_LEFT_UP( wxtPanel::OnLeftUp )
EVT_MIDDLE_DOWN( wxtPanel::OnMiddleDown )
EVT_MIDDLE_UP( wxtPanel::OnMiddleUp )
EVT_RIGHT_DOWN( wxtPanel::OnRightDown )
EVT_RIGHT_UP( wxtPanel::OnRightUp )
EVT_CHAR( wxtPanel::OnKeyDownChar )
#endif /*USE_MOUSE*/
END_EVENT_TABLE()
BEGIN_EVENT_TABLE( wxtConfigDialog, wxDialog )
EVT_CLOSE( wxtConfigDialog::OnClose )
EVT_CHOICE( Config_Rendering, wxtConfigDialog::OnRendering )
EVT_COMMAND_RANGE( Config_OK, Config_CANCEL,
wxEVT_COMMAND_BUTTON_CLICKED, wxtConfigDialog::OnButton )
END_EVENT_TABLE()
#ifdef WXT_MULTITHREADED
/* ----------------------------------------------------------------------------
* gui thread
* ----------------------------------------------------------------------------*/
/* What really happens in the thread
* Just before it returns, wxEntry will call a whole bunch of wxWidgets-cleanup functions */
void *wxtThread::Entry()
{
FPRINTF((stderr,"secondary thread entry\n"));
/* don't answer to SIGINT in this thread - avoids LONGJMP problems */
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
pthread_sigmask(SIG_BLOCK, &set, NULL);
/* gui loop */
wxTheApp->OnRun();
/* Workaround for a deadlock when the main thread will Wait() for this one.
* This issue comes from the fact that our gui main loop is not in the
* main thread as wxWidgets was written for. */
wxt_MutexGuiLeave();
FPRINTF((stderr,"secondary thread finished\n"));
return NULL;
}
#endif /* WXT_MULTITHREADED */
/* ----------------------------------------------------------------------------
* `Main program' equivalent: the program execution "starts" here
* ----------------------------------------------------------------------------*/
/* Create a new application object */
IMPLEMENT_APP_NO_MAIN(wxtApp)
bool wxtApp::OnInit()
{
/* Usually wxWidgets apps create their main window here.
* However, in the context of multiple plot windows, the same code is written in wxt_init().
* So, to avoid duplication of the code, we do only what is strictly necessary.*/
FPRINTF((stderr, "OnInit\n"));
/* initialize frames icons */
icon.AddIcon(wxIcon(icon16x16_xpm));
icon.AddIcon(wxIcon(icon32x32_xpm));
icon.AddIcon(wxIcon(icon64x64_xpm));
/* we load the image handlers, needed to copy the plot to clipboard, and to load icons */
::wxInitAllImageHandlers();
#ifdef __WXMSW__
/* allow the toolbar to display properly png icons with an alpha channel */
wxSystemOptions::SetOption(wxT("msw.remap"), 0);
#endif /* __WXMSW__ */
/* load toolbar icons */
LoadPngIcon(clipboard_png, sizeof(clipboard_png), 0);
LoadPngIcon(replot_png, sizeof(replot_png), 1);
LoadPngIcon(grid_png, sizeof(grid_png), 2);
LoadPngIcon(previouszoom_png, sizeof(previouszoom_png), 3);
LoadPngIcon(nextzoom_png, sizeof(nextzoom_png), 4);
LoadPngIcon(autoscale_png, sizeof(autoscale_png), 5);
LoadPngIcon(config_png, sizeof(config_png), 6);
LoadPngIcon(help_png, sizeof(help_png), 7);
/* load cursors */
LoadCursor(wxt_cursor_cross, cross);
LoadCursor(wxt_cursor_right, right);
LoadCursor(wxt_cursor_rotate, rotate);
LoadCursor(wxt_cursor_size, size);
/* Initialize the config object */
/* application and vendor name are used by wxConfig to construct the name
* of the config file/registry key and must be set before the first call
* to Get() */
SetVendorName(wxT("gnuplot"));
SetAppName(wxT("gnuplot-wxt"));
wxConfigBase *pConfig = wxConfigBase::Get();
/* this will force writing back of the defaults for all values
* if they're not present in the config - this can give the user an idea
* of all possible settings */
pConfig->SetRecordDefaults();
FPRINTF((stderr, "OnInit finished\n"));
return true; /* means that process must continue */
}
/* load an icon from a PNG file embedded as a C array */
void wxtApp::LoadPngIcon(const unsigned char *embedded_png, int length, int icon_number)
{
wxMemoryInputStream pngstream(embedded_png, length);
toolBarBitmaps[icon_number] = new wxBitmap(wxImage(pngstream, wxBITMAP_TYPE_PNG));
}
/* load a cursor */
void wxtApp::LoadCursor(wxCursor &cursor, const char* xpm_bits[])
{
int hotspot_x, hotspot_y;
wxBitmap cursor_bitmap = wxBitmap(xpm_bits);
wxImage cursor_image = cursor_bitmap.ConvertToImage();
/* XPM spec : first string is :
* width height ncolors charperpixel hotspotx hotspoty */
sscanf(xpm_bits[0], "%*d %*d %*d %*d %d %d", &hotspot_x, &hotspot_y);
cursor_image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, hotspot_x);
cursor_image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, hotspot_y);
cursor = wxCursor(cursor_image);
}
/* cleanup on exit
* In a pure wxWidgets app, the returned int is the exit status of the app.
* Here it is not used. */
int wxtApp::OnExit()
{
FPRINTF((stderr,"wxtApp::OnExit\n"));
/* clean up: Set() returns the active config object as Get() does, but unlike
* Get() it doesn't try to create one if there is none (definitely not what
* we want here!) */
delete wxConfigBase::Set((wxConfigBase *) NULL);
return 0;
}
/* will gently terminate the gui thread */
void wxtApp::OnExitLoop( wxCommandEvent& WXUNUSED(event) )
{
FPRINTF((stderr,"wxtApp::OnExitLoop\n"));
wxTheApp->ExitMainLoop();
}
void wxtApp::OnCreateWindow( wxCommandEvent& event )
{
/* retrieve the pointer to wxt_window_t */
wxt_window_t *window = (wxt_window_t*) event.GetClientData();
FPRINTF((stderr,"wxtApp::OnCreateWindow\n"));
window->frame = new wxtFrame( window->title, window->id, 50, 50, 640, 480 );
window->frame->Show(true);
FPRINTF((stderr,"new plot window opened\n"));
/* make the panel able to receive keyboard input */
window->frame->panel->SetFocus();
/* set the default crosshair cursor */
window->frame->panel->SetCursor(wxt_cursor_cross);
/* creating the true context (at initialization, it may be a fake one).
* Note : the frame must be shown for this to succeed */
if (!window->frame->panel->plot.success)
window->frame->panel->wxt_cairo_create_context();
/* tell the other thread we have finished */
wxMutexLocker lock(*(window->mutex));
window->condition->Broadcast();
}
/* wrapper for AddPendingEvent or ProcessEvent */
void wxtApp::SendEvent( wxEvent &event)
{
#ifdef WXT_MULTITHREADED
AddPendingEvent(event);
#else /* !WXT_MULTITHREADED */
ProcessEvent(event);
#endif /* !WXT_MULTITHREADED */
}
/* ---------------------------------------------------------------------------
* Frame : the main windows (one for each plot)
* ----------------------------------------------------------------------------*/
/* frame constructor*/
wxtFrame::wxtFrame( const wxString& title, wxWindowID id, int xpos, int ypos, int width, int height )
: wxFrame((wxFrame *)NULL, id, title, wxPoint(xpos,ypos),
wxSize(width,height), wxDEFAULT_FRAME_STYLE|wxWANTS_CHARS)
{
FPRINTF((stderr,"wxtFrame constructor\n"));
/* used to check for panel initialization */
panel = NULL;
/* initialize the state of the configuration dialog */
config_displayed = false;
/* set up the window icon, in several resolutions */
SetIcons(icon);
/* set up the status bar, and fill it with an empty
* string. It will be immediately overriden by gnuplot. */
CreateStatusBar();
SetStatusText( wxT("") );
/* set up the toolbar */
wxToolBar * toolbar = CreateToolBar();
/* With wxMSW, default toolbar size is only 16x15. */
// toolbar->SetToolBitmapSize(wxSize(16,16));
toolbar->AddTool(Toolbar_CopyToClipboard, wxT("Copy"),
*(toolBarBitmaps[0]), wxT("Copy the plot to clipboard"));
#ifdef USE_MOUSE
toolbar->AddSeparator();
toolbar->AddTool(Toolbar_Replot, wxT("Replot"),
*(toolBarBitmaps[1]), wxT("Replot"));
toolbar->AddTool(Toolbar_ToggleGrid, wxT("Toggle grid"),
*(toolBarBitmaps[2]),wxNullBitmap,wxITEM_NORMAL, wxT("Toggle grid"));
toolbar->AddTool(Toolbar_ZoomPrevious, wxT("Previous zoom"),
*(toolBarBitmaps[3]), wxT("Apply the previous zoom settings"));
toolbar->AddTool(Toolbar_ZoomNext, wxT("Next zoom"),
*(toolBarBitmaps[4]), wxT("Apply the next zoom settings"));
toolbar->AddTool(Toolbar_Autoscale, wxT("Autoscale"),
*(toolBarBitmaps[5]), wxT("Apply autoscale"));
#endif /*USE_MOUSE*/
toolbar->AddSeparator();
toolbar->AddTool(Toolbar_Config, wxT("Terminal configuration"),
*(toolBarBitmaps[6]), wxT("Open configuration dialog"));
toolbar->AddTool(Toolbar_Help, wxT("Help"),
*(toolBarBitmaps[7]), wxT("Open help dialog"));
toolbar->Realize();
FPRINTF((stderr,"wxtFrame constructor 2\n"));
SetClientSize( wxSize(wxt_width, wxt_height) );
/* build the panel, which will contain the visible device context */
panel = new wxtPanel( this, this->GetId(), this->GetClientSize() );
/* setting minimum height and width for the window */
SetSizeHints(100, 100);
FPRINTF((stderr,"wxtFrame constructor 3\n"));
}
/* toolbar event : Copy to clipboard
* We will copy the panel to a bitmap, using platform-independant wxWidgets functions */
void wxtFrame::OnCopy( wxCommandEvent& WXUNUSED( event ) )
{
FPRINTF((stderr,"Copy to clipboard\n"));
int width = panel->plot.device_xmax, height = panel->plot.device_ymax;
wxBitmap cp_bitmap(width,height);
wxMemoryDC cp_dc;
wxClientDC dc(panel);
cp_dc.SelectObject(cp_bitmap);
cp_dc.Blit(0,0,width,height,&dc,0,0);
cp_dc.SelectObject(wxNullBitmap);
wxTheClipboard->UsePrimarySelection(false);
/* SetData clears the clipboard */
if ( wxTheClipboard->Open() ) {
wxTheClipboard->SetData(new wxBitmapDataObject(cp_bitmap));
wxTheClipboard->Close();
}
wxTheClipboard->Flush();
}
#ifdef USE_MOUSE
/* toolbar event : Replot */
void wxtFrame::OnReplot( wxCommandEvent& WXUNUSED( event ) )
{
wxt_exec_event(GE_keypress, 0, 0, 'e' , 0, this->GetId());
}
/* toolbar event : Toggle Grid */
void wxtFrame::OnToggleGrid( wxCommandEvent& WXUNUSED( event ) )
{
wxt_exec_event(GE_keypress, 0, 0, 'g', 0, this->GetId());
}
/* toolbar event : Previous Zoom in history */
void wxtFrame::OnZoomPrevious( wxCommandEvent& WXUNUSED( event ) )
{
wxt_exec_event(GE_keypress, 0, 0, 'p', 0, this->GetId());
}
/* toolbar event : Next Zoom in history */
void wxtFrame::OnZoomNext( wxCommandEvent& WXUNUSED( event ) )
{
wxt_exec_event(GE_keypress, 0, 0, 'n', 0, this->GetId());
}
/* toolbar event : Autoscale */
void wxtFrame::OnAutoscale( wxCommandEvent& WXUNUSED( event ) )
{
wxt_exec_event(GE_keypress, 0, 0, 'a', 0, this->GetId());
}
/* set the status text from the event data */
void wxtFrame::OnSetStatusText( wxCommandEvent& event )
{
SetStatusText(event.GetString());
}
#endif /*USE_MOUSE*/
/* toolbar event : Config */
void wxtFrame::OnConfig( wxCommandEvent& WXUNUSED( event ) )
{
/* if we have already opened a dialog, just raise it */
if (config_displayed) {
config_dialog->Raise();
return;
}
/* otherwise, open a dialog */
config_displayed = true;
config_dialog = new wxtConfigDialog(this);
config_dialog->Show(true);
}
/* toolbar event : Help */
void wxtFrame::OnHelp( wxCommandEvent& WXUNUSED( event ) )
{
wxMessageBox( wxString(wxT("You are using an interactive terminal "\
"based on wxWidgets for the interface, Cairo "\
"for the drawing facilities, and Pango for the text layouts.\n"\
"Please note that toolbar icons in the terminal "\
"don't reflect the whole range of mousing "\
"possibilities in the terminal.\n"\
"Hit 'h' in the plot window "\
"and a help message for mouse commands "\
"will appear in the gnuplot console.\n"\
"See also 'help mouse'.\n")),
wxT("wxWidgets terminal help"), wxOK | wxICON_INFORMATION, this );
}
/* called on Close() (menu or window manager) */
void wxtFrame::OnClose( wxCloseEvent& event )
{
FPRINTF((stderr,"OnClose\n"));
if ( event.CanVeto() && !wxt_handling_persist) {
/* Default behaviour when Quit is clicked, or the window cross X */
event.Veto();
this->Hide();
#ifdef USE_MOUSE
/* Pass it through mouse handling to check for "bind Close" */
wxt_exec_event(GE_reset, 0, 0, 0, 0, this->GetId());
#endif /* USE_MOUSE */
}
else /* in "persist" mode */ {
/* declare the iterator */
std::vector::iterator wxt_iter;
for(wxt_iter = wxt_window_list.begin();
wxt_iter != wxt_window_list.end(); wxt_iter++)
{
if (this == wxt_iter->frame) {
wxt_window_list.erase(wxt_iter);
break;
}
}
this->Destroy();
}
}
/* when the window is resized,
* resize the panel to fit in the frame.
* Note : can't simply use "replot", as it doesn't work with multiplot mode */
void wxtFrame::OnSize( wxSizeEvent& event )
{
FPRINTF((stderr,"frame OnSize\n"));
/* Under Windows the frame receives an OnSize event before being completely initialized.
* So we must check for the panel to be properly initialized before.*/
if (panel)
panel->SetSize( this->GetClientSize() );
}
/* wrapper for AddPendingEvent or ProcessEvent */
void wxtFrame::SendEvent( wxEvent &event)
{
#ifdef WXT_MULTITHREADED
AddPendingEvent(event);
#else /* !WXT_MULTITHREADED */
ProcessEvent(event);
#endif /* !WXT_MULTITHREADED */
}
/* ---------------------------------------------------------------------------
* Panel : the space used for the plot, between the toolbar and the statusbar
* ----------------------------------------------------------------------------*/
/* panel constructor
* Note : under Windows, wxDefaultPosition makes the panel hide the toolbar */
wxtPanel::wxtPanel( wxWindow *parent, wxWindowID id, const wxSize& size )
: wxPanel( parent, id, wxPoint(0,0) /*wxDefaultPosition*/, size, wxWANTS_CHARS )
{
FPRINTF((stderr,"panel constructor\n"));
/* initialisations */
gp_cairo_initialize_plot(&plot);
GetSize(&(plot.device_xmax),&(plot.device_ymax));
plot.polygons_saturate = TRUE;
settings_queued = false;
#ifdef USE_MOUSE
mouse_x = 0;
mouse_y = 0;
wxt_zoombox = false;
zoom_x1 = 0;
zoom_y1 = 0;
zoom_string1 = wxT("");
zoom_string2 = wxT("");
wxt_ruler = false;
wxt_ruler_x = 0;
wxt_ruler_y = 0;
modifier_mask = 0;
#endif /*USE_MOUSE*/
#if defined(GTK_SURFACE)
gdkpixmap = NULL;
#elif defined(__WXMSW__)
hdc = NULL;
hbm = NULL;
#else /* IMAGE_SURFACE */
cairo_bitmap = NULL;
data32 = NULL;
#endif /* SURFACE */
FPRINTF((stderr,"panel constructor4\n"));
/* create the device context to be drawn */
wxt_cairo_create_context();
FPRINTF((stderr,"panel constructor5\n"));
#ifdef IMAGE_SURFACE
wxt_cairo_create_bitmap();
#endif
FPRINTF((stderr,"panel constructor6\n"));
}
/* destructor */
wxtPanel::~wxtPanel()
{
FPRINTF((stderr,"panel destructor\n"));
wxt_cairo_free_context();
/* clear the command list, free the allocated memory */
ClearCommandlist();
}
/* temporary store new settings values to be applied for the next plot */
void wxtPanel::wxt_settings_queue(TBOOLEAN antialiasing,
TBOOLEAN oversampling,
int hinting_setting)
{
mutex_queued.Lock();
settings_queued = true;
antialiasing_queued = antialiasing;
oversampling_queued = oversampling;
hinting_queued = hinting_setting;
mutex_queued.Unlock();
}
/* apply queued settings */
void wxtPanel::wxt_settings_apply()
{
mutex_queued.Lock();
if (settings_queued) {
plot.antialiasing = antialiasing_queued;
plot.oversampling = oversampling_queued;
plot.hinting = hinting_queued;
settings_queued = false;
}
mutex_queued.Unlock();
}
/* clear the command list, free the allocated memory */
void wxtPanel::ClearCommandlist()
{
command_list_mutex.Lock();
command_list_t::iterator iter; /*declare the iterator*/
/* run through the list, and free allocated memory */
for(iter = command_list.begin(); iter != command_list.end(); ++iter) {
if (iter->command == command_enhanced_put_text ||
iter->command == command_put_text ||
iter->command == command_set_font)
delete[] iter->string;
if (iter->command == command_filled_polygon)
delete[] iter->corners;
if (iter->command == command_image)
free(iter->image);
}
command_list.clear();
command_list_mutex.Unlock();
}
/* method called when the panel has to be painted
* -> Refresh(), window dragged, dialogs over the window, etc. */
void wxtPanel::OnPaint( wxPaintEvent &WXUNUSED(event) )
{
/* Constructor of the device context */
wxPaintDC dc(this);
DrawToDC(dc, GetUpdateRegion());
}
/* same as OnPaint, but can be directly called by a user function */
void wxtPanel::Draw()
{
wxClientDC dc(this);
wxBufferedDC buffered_dc(&dc, wxSize(plot.device_xmax, plot.device_ymax));
wxRegion region(0, 0, plot.device_xmax, plot.device_ymax);
DrawToDC(buffered_dc, region);
}
/* copy the plot to the panel, draw zoombow and ruler needed */
void wxtPanel::DrawToDC(wxDC &dc, wxRegion ®ion)
{
wxPen tmp_pen;
/* TODO extend the region mechanism to surfaces other than GTK_SURFACE */
#ifdef GTK_SURFACE
wxRegionIterator upd(region);
int vX,vY,vW,vH; /* Dimensions of client area in pixels */
while (upd) {
vX = upd.GetX();
vY = upd.GetY();
vW = upd.GetW();
vH = upd.GetH();
FPRINTF((stderr,"OnPaint %d,%d,%d,%d\n",vX,vY,vW,vH));
/* Repaint this rectangle */
if (gdkpixmap)
gdk_draw_drawable(dc.GetWindow(),
dc.m_penGC,
gdkpixmap,
vX,vY,
vX,vY,
vW,vH);
++upd;
}
#elif defined(__WXMSW__)
BitBlt((HDC) dc.GetHDC(), 0, 0, plot.device_xmax, plot.device_ymax, hdc, 0, 0, SRCCOPY);
#else
dc.DrawBitmap(*cairo_bitmap, 0, 0, false);
#endif
/* fill in gray when the aspect ratio conservation has let empty space in the panel */
if (plot.device_xmax*plot.ymax > plot.device_ymax*plot.xmax) {
dc.SetPen( *wxTRANSPARENT_PEN );
dc.SetBrush( wxBrush( wxT("LIGHT GREY"), wxSOLID ) );
dc.DrawRectangle((int) (plot.xmax/plot.oversampling_scale*plot.xscale),
0,
plot.device_xmax - (int) (plot.xmax/plot.oversampling_scale*plot.xscale),
plot.device_ymax);
} else if (plot.device_xmax*plot.ymax < plot.device_ymax*plot.xmax) {
dc.SetPen( *wxTRANSPARENT_PEN );
dc.SetBrush( wxBrush( wxT("LIGHT GREY"), wxSOLID ) );
dc.DrawRectangle(0,
(int) (plot.ymax/plot.oversampling_scale*plot.yscale),
plot.device_xmax,
(int) (plot.device_ymax - plot.ymax/plot.oversampling_scale*plot.yscale));
}
#ifdef USE_MOUSE
if (wxt_zoombox) {
tmp_pen = wxPen(wxT("black"), 1, wxSOLID);
tmp_pen.SetCap( wxCAP_ROUND );
dc.SetPen( tmp_pen );
dc.SetLogicalFunction( wxINVERT );
dc.DrawLine( zoom_x1, zoom_y1, mouse_x, zoom_y1 );
dc.DrawLine( mouse_x, zoom_y1, mouse_x, mouse_y );
dc.DrawLine( mouse_x, mouse_y, zoom_x1, mouse_y );
dc.DrawLine( zoom_x1, mouse_y, zoom_x1, zoom_y1 );
dc.SetPen( *wxTRANSPARENT_PEN );
dc.SetBrush( wxBrush( wxT("LIGHT BLUE"), wxSOLID ) );
dc.SetLogicalFunction( wxAND );
dc.DrawRectangle( zoom_x1, zoom_y1, mouse_x -zoom_x1, mouse_y -zoom_y1);
dc.SetLogicalFunction( wxCOPY );
dc.SetFont( wxFont( (int) plot.fontsize, wxFONTFAMILY_DEFAULT,
wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false,
wxString(plot.fontname, wxConvLocal) ) );
dc.DrawText( zoom_string1.BeforeFirst(wxT('\r')),
zoom_x1, zoom_y1 - term->v_char/plot.oversampling_scale);
dc.DrawText( zoom_string1.AfterFirst(wxT('\r')),
zoom_x1, zoom_y1);
dc.DrawText( zoom_string2.BeforeFirst(wxT('\r')),
mouse_x, mouse_y - term->v_char/plot.oversampling_scale);
dc.DrawText( zoom_string2.AfterFirst(wxT('\r')),
mouse_x, mouse_y);
/* if we have to redraw the zoombox, it is with another size,
* so it will be issued later and we can disable it now */
wxt_zoombox = false;
}
if (wxt_ruler) {
tmp_pen = wxPen(wxT("black"), 1, wxSOLID);
tmp_pen.SetCap(wxCAP_BUTT);
dc.SetPen( tmp_pen );
dc.SetLogicalFunction( wxINVERT );
dc.CrossHair( (int)wxt_ruler_x, (int)wxt_ruler_y );
dc.SetLogicalFunction( wxCOPY );
}
if (wxt_ruler && wxt_ruler_lineto) {
tmp_pen = wxPen(wxT("black"), 1, wxSOLID);
tmp_pen.SetCap(wxCAP_BUTT);
dc.SetPen( tmp_pen );
dc.SetLogicalFunction( wxINVERT );
dc.DrawLine((int)wxt_ruler_x, (int)wxt_ruler_y, mouse_x, mouse_y);
dc.SetLogicalFunction( wxCOPY );
}
#endif /*USE_MOUSE*/
}
/* avoid flickering under win32 */
void wxtPanel::OnEraseBackground( wxEraseEvent &WXUNUSED(event) )
{
}
/* when the window is resized */
void wxtPanel::OnSize( wxSizeEvent& event )
{
/* don't do anything if term variables are not initialized */
if (plot.xmax == 0 || plot.ymax == 0)
return;
/* update window size, and scaling variables */
GetSize(&(plot.device_xmax),&(plot.device_ymax));
double new_xscale, new_yscale;
new_xscale = ((double) plot.device_xmax)*plot.oversampling_scale/((double) plot.xmax);
new_yscale = ((double) plot.device_ymax)*plot.oversampling_scale/((double) plot.ymax);
/* We will keep the aspect ratio constant */
if (new_yscale < new_xscale) {
plot.xscale = new_yscale;
plot.yscale = new_yscale;
} else {
plot.xscale = new_xscale;
plot.yscale = new_xscale;
}
FPRINTF((stderr,"panel OnSize %d %d %lf %lf\n",
plot.device_xmax, plot.device_ymax, plot.xscale,plot.yscale));
/* create a new cairo context of the good size */
wxt_cairo_create_context();
/* redraw the plot with the new scaling */
wxt_cairo_refresh();
}
#ifdef USE_MOUSE
/* when the mouse is moved over the panel */
void wxtPanel::OnMotion( wxMouseEvent& event )
{
/* Get and store mouse position for _put_tmp_text() and key events (ruler) */
mouse_x = event.GetX();
mouse_y = event.GetY();
UpdateModifiers(event);
/* update the ruler_lineto thing */
if (wxt_ruler && wxt_ruler_lineto)
Draw();
/* informs gnuplot */
wxt_exec_event(GE_motion,
(int)gnuplot_x( &plot, mouse_x ),
(int)gnuplot_y( &plot, mouse_y ),
0, 0, this->GetId());
}
/* mouse "click" event */
void wxtPanel::OnLeftDown( wxMouseEvent& event )
{
int x,y;
x = (int) gnuplot_x( &plot, event.GetX() );
y = (int) gnuplot_y( &plot, event.GetY() );
UpdateModifiers(event);
wxt_exec_event(GE_buttonpress, x, y, 1, 0, this->GetId());
}
/* mouse "click" event */
void wxtPanel::OnLeftUp( wxMouseEvent& event )
{
int x,y;
x = (int) gnuplot_x( &plot, event.GetX() );
y = (int) gnuplot_y( &plot, event.GetY() );
UpdateModifiers(event);
if ( wxt_exec_event(GE_buttonrelease, x, y, 1,
(int) left_button_sw.Time(), this->GetId()) ) {
/* start a watch to send the time elapsed between up and down */
left_button_sw.Start();
}
}
/* mouse "click" event */
void wxtPanel::OnMiddleDown( wxMouseEvent& event )
{
int x,y;
x = (int) gnuplot_x( &plot, event.GetX() );
y = (int) gnuplot_y( &plot, event.GetY() );
UpdateModifiers(event);
wxt_exec_event(GE_buttonpress, x, y, 2, 0, this->GetId());
}
/* mouse "click" event */
void wxtPanel::OnMiddleUp( wxMouseEvent& event )
{
int x,y;
x = (int) gnuplot_x( &plot, event.GetX() );
y = (int) gnuplot_y( &plot, event.GetY() );
UpdateModifiers(event);
if ( wxt_exec_event(GE_buttonrelease, x, y, 2,
(int) middle_button_sw.Time(), this->GetId()) ) {
/* start a watch to send the time elapsed between up and down */
middle_button_sw.Start();
}
}
/* mouse "click" event */
void wxtPanel::OnRightDown( wxMouseEvent& event )
{
int x,y;
x = (int) gnuplot_x( &plot, event.GetX() );
y = (int) gnuplot_y( &plot, event.GetY() );
UpdateModifiers(event);
wxt_exec_event(GE_buttonpress, x, y, 3, 0, this->GetId());
}
/* mouse "click" event */
void wxtPanel::OnRightUp( wxMouseEvent& event )
{
int x,y;
x = (int) gnuplot_x( &plot, event.GetX() );
y = (int) gnuplot_y( &plot, event.GetY() );
UpdateModifiers(event);
if ( wxt_exec_event(GE_buttonrelease, x, y, 3,
(int) right_button_sw.Time(), this->GetId()) ) {
/* start a watch to send the time elapsed between up and down */
right_button_sw.Start();
}
}
/* the state of the modifiers is checked each time a key is pressed instead of
* tracking the press and release events of the modifiers keys, because the
* window manager catches some combinations, like ctrl+F1, and thus we do not
* receive a release event in this case */
void wxtPanel::UpdateModifiers( wxMouseEvent& event )
{
int current_modifier_mask = 0;
/* retrieve current modifier mask from the wxEvent */
current_modifier_mask |= (event.AltDown() ? (1