home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1999 mARCH
/
PCWK3A99.iso
/
Linux
/
DDD331
/
DDD-3_1_.000
/
DDD-3_1_
/
ddd-3.1.1
/
ddd
/
plotter.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-12-02
|
41KB
|
1,492 lines
// $Id: plotter.C,v 1.38 1998/12/02 08:27:24 zeller Exp $ -*- C++ -*-
// Create a plotter interface
// Copyright (C) 1998 Technische Universitaet Braunschweig, Germany.
// Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
//
// This file is part of DDD.
//
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page,
// `http://www.cs.tu-bs.de/softech/ddd/',
// or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
char plotter_rcsid[] =
"$Id: plotter.C,v 1.38 1998/12/02 08:27:24 zeller Exp $";
#ifdef __GNUG__
#pragma implementation
#endif
#include "plotter.h"
#include "assert.h"
#include "charsets.h"
#include "cook.h"
#include "ddd.h"
#include "exit.h"
#include "findParent.h"
#include "findWindow.h"
#include "file.h"
#include "filetype.h"
#include "fonts.h"
#include "post.h"
#include "print.h"
#include "regexps.h"
#include "simpleMenu.h"
#include "status.h"
#include "strclass.h"
#include "string-fun.h"
#include "verify.h"
#include "version.h"
#include "wm.h"
#include "AppData.h"
#include "Command.h"
#include "Delay.h"
#include "HelpCB.h"
#include "MakeMenu.h"
#include "PlotArea.h"
#include "Swallower.h"
#include "DispValue.h"
#include "DataDisp.h"
#include "DestroyCB.h"
#include "TimeOut.h"
#include <stdio.h>
#include <fstream.h>
#include <Xm/Command.h>
#include <Xm/MainW.h>
#include <Xm/MessageB.h>
#include <Xm/AtomMgr.h>
#include <Xm/FileSB.h>
#include <Xm/Protocols.h>
#include <Xm/DrawingA.h>
#include <Xm/ScrolledW.h>
#include <Xm/SelectioB.h>
#include <Xm/ScrollBar.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
static void TraceInputHP (Agent *source, void *, void *call_data);
static void TraceOutputHP(Agent *source, void *, void *call_data);
static void TraceErrorHP (Agent *source, void *, void *call_data);
static void SetStatusHP (Agent *source, void *, void *call_data);
static void PlotterNotFoundHP(Agent *source, void *, void *call_data);
static void CancelPlotCB(Widget, XtPointer, XtPointer);
static void ExposePlotAreaCB(Widget, XtPointer, XtPointer);
static void ResizePlotAreaCB(Widget, XtPointer, XtPointer);
static void SelectPlotCB(Widget, XtPointer, XtPointer);
static void SelectAndPrintPlotCB(Widget, XtPointer, XtPointer);
static void ReplotCB(Widget, XtPointer, XtPointer);
static void PlotCommandCB(Widget, XtPointer, XtPointer);
static void ExportPlotCB(Widget, XtPointer, XtPointer);
static void ToggleOptionCB(Widget, XtPointer, XtPointer);
static void ToggleLogscaleCB(Widget, XtPointer, XtPointer);
static void SetStyleCB(Widget, XtPointer, XtPointer);
static void SetContourCB(Widget, XtPointer, XtPointer);
static void SetViewCB(Widget, XtPointer, XtPointer);
static void SwallowCB(Widget, XtPointer, XtPointer);
struct PlotWindowInfo {
DispValue *source; // The source we depend upon
string window_name; // The window name
PlotAgent *plotter; // The current Gnuplot instance
PlotArea *area; // The area we're drawing in
Widget shell; // The shell we're in
Widget working_dialog; // The working dialog
Widget swallower; // The Gnuplot window
Widget vsb; // Vertical scroll bar
Widget hsb; // Horizontal scroll bar
Widget command; // Command widget
Widget command_dialog; // Command dialog
Widget export_dialog; // Export dialog
bool active; // True if popped up
XtIntervalId swallow_timer; // Wait for Window creation
string settings; // Plot settings
XtIntervalId settings_timer; // Wait for settings
string settings_file; // File to get settings from
StatusDelay *settings_delay; // Delay while getting settings
// Constructor - just initialize
PlotWindowInfo()
: source(0), window_name(""),
plotter(0), area(0), shell(0), working_dialog(0), swallower(0),
vsb(0), hsb(0), command(0), command_dialog(0),
export_dialog(0), active(false), swallow_timer(0),
settings(""), settings_timer(0), settings_file(""), settings_delay(0)
{}
};
//-------------------------------------------------------------------------
// Menus
//-------------------------------------------------------------------------
static MMDesc file_menu[] =
{
{ "command", MMPush, { PlotCommandCB, 0 }, 0, 0, 0, 0 },
MMSep,
{ "replot", MMPush, { ReplotCB, 0 }, 0, 0, 0, 0 },
{ "print", MMPush, { SelectAndPrintPlotCB, 0 }, 0, 0, 0, 0 },
{ "export", MMPush, { ExportPlotCB, 0 }, 0, 0, 0, 0 },
MMSep,
{ "close", MMPush, { CancelPlotCB, 0 }, 0, 0, 0, 0 },
{ "exit", MMPush, { DDDExitCB, XtPointer(EXIT_SUCCESS) }, 0, 0, 0, 0},
MMEnd
};
static MMDesc view_menu[] =
{
{ "border", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
{ "time", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
MMSep,
{ "grid", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
{ "xzeroaxis", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
{ "yzeroaxis", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
MMEnd
};
static MMDesc contour_menu[] =
{
{ "base", MMToggle, { SetContourCB, 0 }, 0, 0, 0, 0 },
{ "surface", MMToggle, { SetContourCB, 0 }, 0, 0, 0, 0 },
MMEnd
};
static MMDesc scale_menu[] =
{
{ "logscale", MMToggle, { ToggleLogscaleCB, 0 }, 0, 0, 0, 0 },
MMSep,
{ "xtics", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
{ "ytics", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
{ "ztics", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
MMEnd
};
static MMDesc plot_menu[] =
{
{ "points", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "lines", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "lines3d", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "linespoints", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "linespoints3d", MMToggle | MMUnmanaged,
{ SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "impulses", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "dots", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "steps2d", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
{ "boxes2d", MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
MMEnd
};
static MMDesc menubar[] =
{
{ "file", MMMenu, MMNoCB, file_menu, 0, 0, 0 },
{ "edit", MMMenu, MMNoCB, simple_edit_menu, 0, 0, 0 },
{ "plotView", MMMenu, MMNoCB, view_menu, 0, 0, 0 },
{ "plot", MMRadioMenu, MMNoCB, plot_menu, 0, 0, 0 },
{ "scale", MMMenu, MMNoCB, scale_menu, 0, 0, 0 },
{ "contour", MMMenu, MMNoCB, contour_menu, 0, 0, 0 },
{ "help", MMMenu | MMHelp, MMNoCB, simple_help_menu, 0, 0, 0 },
MMEnd
};
static void configure_plot(PlotWindowInfo *plot);
//-------------------------------------------------------------------------
// Plotter commands
//-------------------------------------------------------------------------
static void send(PlotWindowInfo *plot, const string& cmd)
{
data_disp->select(plot->source);
plot->plotter->write(cmd.chars(), cmd.length());
}
static void send_and_replot(PlotWindowInfo *plot, string cmd)
{
if (cmd.matches(rxwhite))
return;
if (!cmd.contains('\n', -1))
cmd += "\n";
if (cmd.contains("help", 0))
cmd += "\n"; // Exit `help'
else
cmd += "replot\n";
if (plot->area != 0)
plot->area->plot_pending();
send(plot, cmd);
}
//-------------------------------------------------------------------------
// Set up menu
//-------------------------------------------------------------------------
static void slurp_file(const string& filename, string& target)
{
ifstream is(filename);
if (is.bad())
{
target = "";
return;
}
ostrstream s;
int c;
while ((c = is.get()) != EOF)
s << (unsigned char)c;
target = s;
}
static void GetPlotSettingsCB(XtPointer client_data, XtIntervalId *id)
{
(void) id; // Use it
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
assert(plot->settings_timer == *id);
plot->settings_timer = 0;
// Check for settings file to be created
string settings;
slurp_file(plot->settings_file, settings);
if (settings.contains("set zero"))
{
// Settings are complete
unlink(plot->settings_file);
plot->settings = settings;
configure_plot(plot);
delete plot->settings_delay;
plot->settings_delay = 0;
}
else
{
// Try again in 500 ms
plot->settings_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(plot->shell), 500,
GetPlotSettingsCB, XtPointer(plot));
}
}
static void configure_options(PlotWindowInfo *plot, MMDesc *menu,
const string& settings)
{
for (int i = 0; menu[i].name != 0; i++)
{
if ((menu[i].type & MMTypeMask) != MMToggle)
continue;
string name = menu[i].name;
Widget w = XtNameToWidget(plot->shell, "*" + name);
XtCallbackProc callback = menu[i].callback.callback;
bool set = false;
if (callback == ToggleOptionCB)
{
set = settings.contains("\nset " + name + "\n");
}
else if (callback == SetContourCB)
{
if (name == "base")
set = settings.contains("\nset contour base\n") ||
settings.contains("\nset contour both\n");
else if (name == "surface")
set = settings.contains("\nset contour surface\n") ||
settings.contains("\nset contour both\n");
}
else if (callback == ToggleLogscaleCB)
{
set = settings.contains("\nset logscale ");
}
XmToggleButtonSetState(w, set, False);
}
}
static void configure_plot(PlotWindowInfo *plot)
{
if (plot->plotter == 0)
return;
int ndim = plot->plotter->dimensions();
// Set up plot menu
int i;
for (i = 0; plot_menu[i].name != 0; i++)
{
if ((plot_menu[i].type & MMTypeMask) != MMToggle)
continue;
string name = plot_menu[i].name;
Widget w = XtNameToWidget(plot->shell, "*" + name);
if (name.contains("2d", -1))
XtSetSensitive(w, ndim == 2);
else if (name.contains("3d", -1))
XtSetSensitive(w, ndim >= 3);
else
XtSetSensitive(w, ndim >= 2);
}
// Log scale is available only iff all values are non-negative
Widget logscale = XtNameToWidget(plot->shell, "*logscale");
XtSetSensitive(logscale, plot->plotter->min_v() >= 0.0);
// Axes can be toggled in 2d mode only
Widget xzeroaxis = XtNameToWidget(plot->shell, "*xzeroaxis");
Widget yzeroaxis = XtNameToWidget(plot->shell, "*yzeroaxis");
XtSetSensitive(xzeroaxis, ndim <= 2);
XtSetSensitive(yzeroaxis, ndim <= 2);
// Z Tics are available in 3d mode only
Widget ztics = XtNameToWidget(plot->shell, "*ztics");
XtSetSensitive(ztics, ndim >= 3);
// Contour drawing is available in 3d mode only
Widget base = XtNameToWidget(plot->shell, "*base");
Widget surface = XtNameToWidget(plot->shell, "*surface");
XtSetSensitive(base, ndim >= 3);
XtSetSensitive(surface, ndim >= 3);
// Set scrollbars
manage_child(plot->hsb, ndim >= 3);
manage_child(plot->vsb, ndim >= 3);
// Check if we can export something
bool have_source = false;
bool can_export = false;
const StringArray& sources = plot->plotter->data_files();
for (i = 0; i < sources.size(); i++)
{
if (sources[i] != "")
{
if (have_source)
can_export = false; // Multiple source files
else
can_export = have_source = true;
}
}
Widget export_w = XtNameToWidget(plot->shell, "*export");
set_sensitive(export_w, can_export);
// The remainder requires settings
if (plot->settings == "")
{
// No settings yet
if (plot->settings_timer == 0)
{
plot->settings_delay =
new StatusDelay("Retrieving Plot Settings");
// Save settings...
plot->settings_file = tmpnam(0);
string cmd = "save " + quote(plot->settings_file) + "\n";
send(plot, cmd);
// ...and try again in 250ms
plot->settings_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(plot->shell), 250,
GetPlotSettingsCB, XtPointer(plot));
// Set initial scrollbar defaults
XtVaSetValues(plot->vsb, XmNvalue, 60, NULL);
XtVaSetValues(plot->hsb, XmNvalue, 30, NULL);
}
return;
}
configure_options(plot, view_menu, plot->settings);
configure_options(plot, contour_menu, plot->settings);
configure_options(plot, scale_menu, plot->settings);
// Get style
for (i = 0; plot_menu[i].name != 0; i++)
{
if ((plot_menu[i].type & MMTypeMask) != MMToggle)
continue;
string name = plot_menu[i].name;
Widget w = XtNameToWidget(plot->shell, "*" + name);
bool set = plot->settings.contains("\nset data style " + name + "\n");
XmToggleButtonSetState(w, set, False);
}
// Get position
int rot_x = 60;
int rot_z = 30;
int view_index = plot->settings.index("set view ");
if (view_index >= 0)
{
// `set view <rot_x> {,{<rot_z>}{,{<scale>}{,<scale_z>}}}'
string view_setting = plot->settings.after("set view ");
rot_x = atoi(view_setting);
view_setting = view_setting.after(", ");
rot_z = atoi(view_setting);
}
XtVaSetValues(plot->vsb, XmNvalue, rot_x, NULL);
XtVaSetValues(plot->hsb, XmNvalue, rot_z, NULL);
}
//-------------------------------------------------------------------------
// Decoration stuff
//-------------------------------------------------------------------------
// Start plot
static void popup_plot_shell(PlotWindowInfo *plot)
{
if (!plot->active && plot->plotter != 0)
{
// We have the plot
plot->plotter->removeHandler(Died, PlotterNotFoundHP,
(void *)plot);
// Fetch plot settings
configure_plot(plot);
// Command and export dialogs are not needed (yet)
if (plot->command_dialog != 0)
XtUnmanageChild(plot->command_dialog);
if (plot->export_dialog != 0)
XtUnmanageChild(plot->export_dialog);
// Pop down working dialog
if (plot->working_dialog != 0)
XtUnmanageChild(plot->working_dialog);
// Pop up shell
XtSetSensitive(plot->shell, True);
XtPopup(plot->shell, XtGrabNone);
wait_until_mapped(plot->shell);
plot->active = true;
}
}
// Swallow WINDOW
static void swallow(PlotWindowInfo *plot, Window window)
{
assert(window != None);
// We have the window
XtRemoveCallback(plot->swallower, XtNwindowCreatedCallback,
SwallowCB, XtPointer(plot));
if (plot->swallow_timer != 0)
{
XtRemoveTimeOut(plot->swallow_timer);
plot->swallow_timer = 0;
}
XtVaSetValues(plot->swallower, XtNwindow, window, NULL);
popup_plot_shell(plot);
}
// Swallow new GNUPLOT window; search from window created on root.
static void SwallowCB(Widget swallower, XtPointer client_data,
XtPointer call_data)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
assert(plot->swallower == swallower);
SwallowerInfo *info = (SwallowerInfo *)call_data;
Window root = info->window;
Window window = None;
Display *display = XtDisplay(swallower);
// Try the exact name as given
if (window == None)
window = findWindow(display, root, plot->window_name);
// Try the capitalized name. Gnuplot does this.
if (window == None)
window = findWindow(display, root, capitalize(plot->window_name));
// Try any `Gnuplot' window just created
if (window == None)
window = findWindow(display, root, app_data.plot_window_class);
if (window != None)
swallow(plot, window);
}
// Swallow new GNUPLOT window; search from root window (expensive).
static void SwallowTimeOutCB(XtPointer client_data, XtIntervalId *id)
{
(void) id;
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
assert(*id == plot->swallow_timer);
plot->swallow_timer = 0;
Window root = RootWindowOfScreen(XtScreen(plot->swallower));
Window window = None;
Display *display = XtDisplay(plot->swallower);
// Try the exact name as given
if (window == None)
window = findWindow(display, root, plot->window_name);
// Try the capitalized name. Gnuplot does this.
if (window == None)
window = findWindow(display, root, capitalize(plot->window_name));
if (window == None)
{
// Try again later
plot->swallow_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(plot->swallower),
app_data.plot_window_delay,
SwallowTimeOutCB, XtPointer(plot));
}
if (window != None)
swallow(plot, window);
}
// Swallow again after window has gone. This happens while printing.
static void SwallowAgainCB(Widget swallower, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
assert(plot->swallower == swallower);
XtAddCallback(swallower, XtNwindowCreatedCallback, SwallowCB,
XtPointer(plot));
if (plot->swallow_timer != 0)
XtRemoveTimeOut(plot->swallow_timer);
plot->swallow_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(plot->swallower),
app_data.plot_window_delay,
SwallowTimeOutCB, XtPointer(plot));
}
// Cancel plot
static void popdown_plot_shell(PlotWindowInfo *plot)
{
static bool entered = false;
if (entered)
return;
entered = true;
// Manage dialogs
if (plot->working_dialog != 0)
XtUnmanageChild(plot->working_dialog);
if (plot->command_dialog != 0)
XtUnmanageChild(plot->command_dialog);
if (plot->export_dialog != 0)
XtUnmanageChild(plot->export_dialog);
if (plot->shell != 0)
{
XWithdrawWindow(XtDisplay(plot->shell), XtWindow(plot->shell),
XScreenNumberOfScreen(XtScreen(plot->shell)));
XtPopdown(plot->shell);
// XtPopdown may not withdraw an iconified shell. Hence, make
// sure the shell really becomes disabled.
XtSetSensitive(plot->shell, False);
}
// Manage settings
if (plot->settings_timer != 0)
{
// Still waiting for settings
XtRemoveTimeOut(plot->settings_timer);
plot->settings_timer = 0;
unlink(plot->settings_file);
}
if (plot->settings_delay != 0)
{
plot->settings_delay->outcome = "canceled";
delete plot->settings_delay;
plot->settings_delay = 0;
}
plot->settings = "";
plot->active = false;
entered = false;
}
static void CancelPlotCB(Widget, XtPointer client_data, XtPointer)
{
static bool entered = false;
if (entered)
return;
entered = true;
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
popdown_plot_shell(plot);
if (plot->swallower != 0)
{
// Don't wait for window to swallow
XtRemoveAllCallbacks(plot->swallower, XtNwindowCreatedCallback);
XtRemoveAllCallbacks(plot->swallower, XtNwindowGoneCallback);
}
if (plot->swallow_timer != 0)
{
XtRemoveTimeOut(plot->swallow_timer);
plot->swallow_timer = 0;
}
if (plot->plotter != 0)
{
// Terminate plotter
plot->plotter->removeHandler(Died, PlotterNotFoundHP, client_data);
plot->plotter->terminate();
plot->plotter = 0;
}
entered = false;
}
static void DeletePlotterCB(XtPointer client_data, XtIntervalId *)
{
Agent *plotter = (Agent *)client_data;
delete plotter;
}
static void DeletePlotterHP(Agent *plotter, void *client_data, void *)
{
// Plotter has died - delete memory
XtAppAddTimeOut(XtWidgetToApplicationContext(gdb_w), 0,
DeletePlotterCB, XtPointer(plotter));
plotter->removeHandler(Died, DeletePlotterHP, client_data);
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
assert(plot->plotter == 0 || plot->plotter == plotter);
plot->plotter = 0;
popdown_plot_shell(plot);
}
static void GetPlotHP(Agent *, void *client_data, void *call_data)
{
// We got the plot commands
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
// Pass commands to the plot area
DataLength *dl = (DataLength *)call_data;
plot->area->plot(dl->data, dl->length);
// Popup shell
popup_plot_shell(plot);
}
static void PlotterNotFoundHP(Agent *plotter, void *client_data, void *)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
(void) plot; // Use it
assert(plot->plotter == 0 || plot->plotter == plotter);
plotter->removeHandler(Died, PlotterNotFoundHP, client_data);
string base = app_data.plot_command;
if (base.contains(' '))
base = base.before(' ');
Arg args[10];
Cardinal arg = 0;
MString msg = rm(capitalize(base) + " could not be started.");
XtSetArg(args[arg], XmNmessageString, msg.xmstring()); arg++;
Widget dialog =
verify(XmCreateErrorDialog(find_shell(),
"no_plotter_dialog", args, arg));
XtUnmanageChild(XmMessageBoxGetChild
(dialog, XmDIALOG_CANCEL_BUTTON));
XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
Delay::register_shell(dialog);
manage_and_raise(dialog);
}
#define SWALLOWER_NAME "swallower"
#define PLOT_AREA_NAME "area"
static VoidArray plot_infos;
static PlotWindowInfo *new_decoration(const string& name)
{
PlotWindowInfo *plot = 0;
// Check whether we can reuse an existing decoration
for (int i = 0; i < plot_infos.size(); i++)
{
PlotWindowInfo *info = (PlotWindowInfo *)plot_infos[i];
if (info->plotter == 0)
{
// Shell is unused - use this one
plot = info;
break;
}
}
if (plot == 0)
{
plot = new PlotWindowInfo;
// Create decoration windows
Arg args[10];
Cardinal arg = 0;
XtSetArg(args[arg], XmNallowShellResize, True); arg++;
XtSetArg(args[arg], XmNdeleteResponse, XmDO_NOTHING); arg++;
// Mark shell as `used'
XtSetArg(args[arg], XmNuserData, XtPointer(True)); arg++;
plot->shell = verify(XtCreateWidget("plot", topLevelShellWidgetClass,
find_shell(), args, arg));
Atom WM_DELETE_WINDOW =
XmInternAtom(XtDisplay(plot->shell), "WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback(plot->shell, WM_DELETE_WINDOW,
CancelPlotCB, XtPointer(plot));
arg = 0;
Widget main_window = XmCreateMainWindow(plot->shell, "main_window",
args, arg);
XtManageChild(main_window);
MMcreateMenuBar(main_window, "menubar", menubar);
MMaddCallbacks(file_menu, XtPointer(plot));
MMaddCallbacks(simple_edit_menu);
MMaddCallbacks(view_menu, XtPointer(plot));
MMaddCallbacks(plot_menu, XtPointer(plot));
MMaddCallbacks(scale_menu, XtPointer(plot));
MMaddCallbacks(contour_menu, XtPointer(plot));
MMaddCallbacks(simple_help_menu);
MMaddHelpCallback(menubar, ImmediateHelpCB);
arg = 0;
XtSetArg(args[arg], XmNscrollingPolicy, XmAPPLICATION_DEFINED); arg++;
XtSetArg(args[arg], XmNvisualPolicy, XmVARIABLE); arg++;
Widget scroll =
XmCreateScrolledWindow(main_window, "scroll", args, arg);
XtManageChild(scroll);
// Create work window
Widget work;
string plot_term_type = downcase(app_data.plot_term_type);
if (plot_term_type.contains("xlib", 0))
{
// xlib type - create plot area to draw plot commands
arg = 0;
work = XmCreateDrawingArea(scroll, PLOT_AREA_NAME, args, arg);
XtManageChild(work);
plot->area =
new PlotArea(work, make_font(app_data, FixedWidthDDDFont));
XtVaSetValues(work, XmNuserData, XtPointer(plot->area), NULL);
}
else if (plot_term_type.contains("x11", 0))
{
// x11 type - swallow Gnuplot window
arg = 0;
work = plot->swallower =
XtCreateManagedWidget(SWALLOWER_NAME, swallowerWidgetClass,
scroll, args, arg);
}
else
{
// Unknown terminal type
post_error("Unknown plot terminal type " +
quote(app_data.plot_term_type),
"unknown_plot_term_type_error");
return 0;
}
// Create scroll bars
const int slider_size = 20;
arg = 0;
XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
XtSetArg(args[arg], XmNminimum, 0); arg++;
XtSetArg(args[arg], XmNmaximum, 360 + slider_size); arg++;
plot->hsb = XmCreateScrollBar(scroll, "hsb", args, arg);
XtManageChild(plot->hsb);
arg = 0;
XtSetArg(args[arg], XmNorientation, XmVERTICAL); arg++;
XtSetArg(args[arg], XmNminimum, 0); arg++;
XtSetArg(args[arg], XmNmaximum, 180 + slider_size); arg++;
plot->vsb = XmCreateScrollBar(scroll, "vsb", args, arg);
XtManageChild(plot->vsb);
XtAddCallback(plot->hsb, XmNvalueChangedCallback,
SetViewCB, XtPointer(plot));
XtAddCallback(plot->vsb, XmNvalueChangedCallback,
SetViewCB, XtPointer(plot));
#if 0
XtAddCallback(plot->hsb, XmNdragCallback, SetViewCB, XtPointer(plot));
XtAddCallback(plot->vsb, XmNdragCallback, SetViewCB, XtPointer(plot));
#endif
XmScrolledWindowSetAreas(scroll, plot->hsb, plot->vsb, work);
Delay::register_shell(plot->shell);
InstallButtonTips(plot->shell);
plot_infos += plot;
}
string title = DDD_NAME ": " + name;
XtVaSetValues(plot->shell,
XmNtitle, title.chars(),
XmNiconName, title.chars(),
NULL);
if (plot->swallower != 0)
{
XtRemoveAllCallbacks(plot->swallower, XtNwindowCreatedCallback);
XtAddCallback(plot->swallower, XtNwindowCreatedCallback,
SwallowCB, XtPointer(plot));
XtRemoveAllCallbacks(plot->swallower, XtNwindowGoneCallback);
XtAddCallback(plot->swallower, XtNwindowGoneCallback,
SwallowAgainCB, XtPointer(plot));
if (plot->swallow_timer != 0)
XtRemoveTimeOut(plot->swallow_timer);
plot->swallow_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(plot->swallower),
app_data.plot_window_delay, SwallowTimeOutCB,
XtPointer(plot));
}
plot->active = false;
return plot;
}
// Remove all unused decorations from cache
void clear_plot_window_cache()
{
for (int i = 0; i < plot_infos.size(); i++)
{
PlotWindowInfo *info = (PlotWindowInfo *)plot_infos[i];
if (info->plotter == 0)
{
// Shell is unused -- destroy it
XtDestroyWidget(info->shell);
info->shell = 0;
}
else
{
// A running shell should be destroyed after invocation.
// (FIXME)
}
}
static VoidArray empty;
plot_infos = empty;
}
// Create a new plot window
PlotAgent *new_plotter(string name, DispValue *source)
{
static int tics = 1;
string cmd = app_data.plot_command;
cmd.gsub("@FONT@", make_font(app_data, FixedWidthDDDFont));
string window_name = ddd_NAME "plot" + itostring(tics++);
if (cmd.contains("@NAME@"))
cmd.gsub("@NAME@", window_name);
else
cmd += " -name " + window_name;
// Create shell
PlotWindowInfo *plot = new_decoration(name);
if (plot == 0)
return 0;
plot->source = source;
plot->window_name = window_name;
XtVaSetValues(plot->shell, XmNuserData, XtPointer(True), NULL);
// Pop up a working dialog
static Widget dialog = 0;
if (dialog == 0)
{
Arg args[10];
Cardinal arg = 0;
dialog = verify(XmCreateWorkingDialog(find_shell(),
"launch_plot_dialog",
args, arg));
XtUnmanageChild(XmMessageBoxGetChild(dialog,
XmDIALOG_OK_BUTTON));
XtUnmanageChild(XmMessageBoxGetChild(dialog,
XmDIALOG_HELP_BUTTON));
}
XtRemoveAllCallbacks(dialog, XmNcancelCallback);
XtAddCallback(dialog, XmNcancelCallback, CancelPlotCB, XtPointer(plot));
plot->working_dialog = dialog;
string base = cmd;
if (base.contains(' '))
base = cmd.before(' ');
MString msg = rm("Starting ") + tt(base) + rm("...");
XtVaSetValues(dialog, XmNmessageString, msg.xmstring(), NULL);
manage_and_raise(dialog);
wait_until_mapped(dialog);
// Invoke plot process
PlotAgent *plotter =
new PlotAgent(XtWidgetToApplicationContext(plot->shell), cmd);
XtAddCallback(plot->shell, XtNpopdownCallback,
CancelPlotCB, XtPointer(plot));
if (plot->area != 0)
{
XtAddCallback(plot->area->widget(), XmNexposeCallback,
ExposePlotAreaCB, XtPointer(plot));
XtAddCallback(plot->area->widget(), XmNresizeCallback,
ResizePlotAreaCB, XtPointer(plot));
}
string init = app_data.plot_init_commands;
init.prepend("set term " + string(app_data.plot_term_type) + "\n");
if (init != "" && !init.contains('\n', -1))
init += '\n';
// Add trace handlers
plotter->addHandler(Input, TraceInputHP); // Gnuplot => DDD
plotter->addHandler(Output, TraceOutputHP); // DDD => Gnuplot
plotter->addHandler(Error, TraceErrorHP); // Gnuplot Errors => DDD
// Show Gnuplot Errors in status line
plotter->addHandler(Error, SetStatusHP, (void *)plot);
// Handle death
plotter->addHandler(Died, PlotterNotFoundHP, (void *)plot);
plotter->addHandler(Died, DeletePlotterHP, (void *)plot);
if (plot->area != 0)
plotter->addHandler(Plot, GetPlotHP, (void *)plot);
plotter->start_with(init);
plot->plotter = plotter;
return plotter;
}
//-------------------------------------------------------------------------
// Drawing stuff
//-------------------------------------------------------------------------
static void ExposePlotAreaCB(Widget, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
plot->area->replot(false);
}
static void ResizePlotAreaCB(Widget, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
plot->area->replot(true);
}
//-------------------------------------------------------------------------
// Selection stuff
//-------------------------------------------------------------------------
static void SelectPlotCB(Widget, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
data_disp->select(plot->source);
}
static void SelectAndPrintPlotCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
SelectPlotCB(w, client_data, call_data);
PrintPlotCB(w, client_data, call_data);
}
//-------------------------------------------------------------------------
// Plot again
//-------------------------------------------------------------------------
static void ReplotCB(Widget, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
// This transfers the data once again and replots the whole thing
plot->source->plot();
}
//-------------------------------------------------------------------------
// Command
//-------------------------------------------------------------------------
// Selection from Command widget
static void DoPlotCommandCB(Widget, XtPointer client_data, XtPointer call_data)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
XmCommandCallbackStruct *cbs = (XmCommandCallbackStruct *)call_data;
MString xcmd(cbs->value, true);
string cmd = xcmd.str();
send_and_replot(plot, cmd);
}
// Apply button
static void ApplyPlotCommandCB(Widget, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
Widget text = XmCommandGetChild(plot->command, XmDIALOG_COMMAND_TEXT);
String cmd_s = 0;
if (XmIsTextField(text))
cmd_s = XmTextFieldGetString(text);
else if (XmIsText(text))
cmd_s = XmTextGetString(text);
else
assert(0);
string cmd = cmd_s;
XtFree(cmd_s);
send_and_replot(plot, cmd);
}
static void EnableApplyCB(Widget, XtPointer client_data, XtPointer call_data)
{
Widget apply = (Widget)client_data;
XmCommandCallbackStruct *cbs = (XmCommandCallbackStruct *)call_data;
set_sensitive(apply, cbs->length > 0);
}
static void PlotCommandCB(Widget, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
if (plot->command_dialog == 0)
{
Arg args[10];
Cardinal arg = 0;
Widget dialog =
verify(XmCreatePromptDialog(plot->shell, "plot_command_dialog",
args, arg));
Delay::register_shell(dialog);
plot->command_dialog = dialog;
Widget apply = XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON);
XtManageChild(apply);
XtUnmanageChild(XmSelectionBoxGetChild(dialog,
XmDIALOG_OK_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(dialog,
XmDIALOG_SELECTION_LABEL));
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
XtAddCallback(dialog, XmNapplyCallback,
ApplyPlotCommandCB, XtPointer(client_data));
XtAddCallback(dialog, XmNhelpCallback,
ImmediateHelpCB, XtPointer(client_data));
arg = 0;
Widget command =
verify(XmCreateCommand(dialog, "plot_command", args, arg));
plot->command = command;
XtManageChild(command);
XtAddCallback(command, XmNcommandEnteredCallback,
DoPlotCommandCB, XtPointer(client_data));
XtAddCallback(command, XmNcommandChangedCallback,
EnableApplyCB, XtPointer(apply));
set_sensitive(apply, false);
}
manage_and_raise(plot->command_dialog);
}
//-------------------------------------------------------------------------
// Export
//-------------------------------------------------------------------------
static void SetCB(Widget, XtPointer client_data, XtPointer)
{
bool *value = (bool *)client_data;
*value = true;
}
static void DoExportCB(Widget w, XtPointer client_data, XtPointer call_data)
{
SelectPlotCB(w, client_data, call_data);
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
string target = get_file(w, client_data, call_data);
if (target == "")
return;
const StringArray& titles = plot->plotter->data_titles();
const StringArray& sources = plot->plotter->data_files();
string source = "";
string title = "";
for (int i = 0; source == "" && i < sources.size(); i++)
{
if (sources[i] != "")
{
source = sources[i];
title = titles[i];
}
}
if (source == "")
return; // This should not happen
if (access(target, W_OK) == 0 && is_regular_file(target))
{
// File exists - request confirmation
static Widget confirm_overwrite_dialog = 0;
if (confirm_overwrite_dialog != 0)
DestroyWhenIdle(confirm_overwrite_dialog);
Arg args[10];
Cardinal arg = 0;
XtSetArg(args[arg], XmNdialogStyle,
XmDIALOG_FULL_APPLICATION_MODAL); arg++;
confirm_overwrite_dialog =
verify(XmCreateQuestionDialog(plot->shell,
"confirm_overwrite_dialog",
args, arg));
Delay::register_shell(confirm_overwrite_dialog);
bool yes = false;
bool no = false;
XtAddCallback(confirm_overwrite_dialog,
XmNokCallback, SetCB, XtPointer(&yes));
XtAddCallback(confirm_overwrite_dialog,
XmNcancelCallback, SetCB, XtPointer(&no));
XtAddCallback(confirm_overwrite_dialog,
XmNhelpCallback, ImmediateHelpCB, 0);
MString question = rm("Overwrite existing file "
+ quote(target) + "?");
XtVaSetValues (confirm_overwrite_dialog, XmNmessageString,
question.xmstring(), NULL);
manage_and_raise(confirm_overwrite_dialog);
XtAppContext app_context = XtWidgetToApplicationContext(plot->shell);
while (!yes && !no)
XtAppProcessEvent(app_context, XtIMAll);
if (no)
return;
}
StatusDelay delay("Saving " + title + " data to " + quote(target));
// Copy SOURCE to TARGET
ifstream is(source);
ofstream os(target);
if (os.bad())
{
FILE *fp = fopen(target, "w");
post_error(string("Cannot open ")
+ quote(target) + ": " + strerror(errno),
"export_failed_error", plot->shell);
if (fp)
fclose(fp);
delay.outcome = strerror(errno);
return;
}
int c;
while ((c = is.get()) != EOF)
os.put((unsigned char) c);
XtUnmanageChild(plot->export_dialog);
}
static void ExportPlotCB(Widget w, XtPointer client_data, XtPointer call_data)
{
SelectPlotCB(w, client_data, call_data);
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
if (plot->export_dialog == 0)
{
Arg args[10];
Cardinal arg = 0;
Widget dialog =
verify(XmCreateFileSelectionDialog(plot->shell,
"export_data", args, arg));
plot->export_dialog = dialog;
Delay::register_shell(dialog);
XtAddCallback(dialog, XmNokCallback, DoExportCB, client_data);
XtAddCallback(dialog, XmNcancelCallback, UnmanageThisCB,
XtPointer(dialog));
XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, XtPointer(0));
}
manage_and_raise(plot->export_dialog);
}
//-------------------------------------------------------------------------
// Settings
//-------------------------------------------------------------------------
static void ToggleOptionCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
XmToggleButtonCallbackStruct *cbs =
(XmToggleButtonCallbackStruct *)call_data;
string cmd;
if (cbs->set)
cmd = string("set ") + XtName(w);
else
cmd = string("set no") + XtName(w);
send_and_replot(plot, cmd);
}
static void ToggleLogscaleCB(Widget, XtPointer client_data,
XtPointer call_data)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
XmToggleButtonCallbackStruct *cbs =
(XmToggleButtonCallbackStruct *)call_data;
string cmd;
if (cbs->set)
cmd = "set logscale ";
else
cmd = "set nologscale ";
if (plot->plotter->dimensions() >= 3)
cmd += "z";
else
cmd += "y";
send_and_replot(plot, cmd);
}
static void SetStyleCB(Widget w, XtPointer client_data, XtPointer call_data)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
XmToggleButtonCallbackStruct *cbs =
(XmToggleButtonCallbackStruct *)call_data;
if (cbs->set)
{
string style = XtName(w);
string cmd;
if (style.contains("3d", -1))
{
cmd = "set hidden3d\n";
style = style.before("3d");
}
else
{
cmd = "set nohidden3d\n";
}
if (style.contains("2d", -1))
style = style.before("2d");
cmd += "set data style " + style;
send_and_replot(plot, cmd);
}
}
static void SetContourCB(Widget w, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
Widget base = XtNameToWidget(XtParent(w), "base");
Widget surface = XtNameToWidget(XtParent(w), "surface");
assert (base != 0 && surface != 0);
bool base_set = XmToggleButtonGetState(base);
bool surface_set = XmToggleButtonGetState(surface);
string cmd;
if (base_set && surface_set)
cmd = "set contour both";
else if (base_set && !surface_set)
cmd = "set contour base";
else if (!base_set && surface_set)
cmd = "set contour surface";
else
cmd = "set nocontour";
send_and_replot(plot, cmd);
}
static void SetViewCB(Widget, XtPointer client_data, XtPointer)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
int rot_x = 60;
int rot_z = 30;
XtVaGetValues(plot->vsb, XmNvalue, &rot_x, NULL);
XtVaGetValues(plot->hsb, XmNvalue, &rot_z, NULL);
string cmd =
"set view " + itostring(rot_x) + ", " + itostring(rot_z);
send_and_replot(plot, cmd);
}
//-------------------------------------------------------------------------
// Status line
//-------------------------------------------------------------------------
// Forward Gnuplot error messages to DDD status line
static void SetStatusHP(Agent *, void *client_data, void *call_data)
{
PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
DataLength* dl = (DataLength *) call_data;
string s(dl->data, dl->length);
(void) plot; // Use it
#if 0
if (!plot->active)
{
// Probably an invocation problem
post_gdb_message(s);
return;
}
#endif
if (plot->command != 0)
{
string msg = s;
strip_space(msg);
MString xmsg = tb(msg);
XmCommandError(plot->command, xmsg.xmstring());
}
while (s != "")
{
string line;
if (s.contains('\n'))
line = s.before('\n');
else
line = s;
s = s.after('\n');
strip_space(line);
if (line != "")
set_status(line);
}
}
//-------------------------------------------------------------------------
// Trace communication
//-------------------------------------------------------------------------
static void trace(char *prefix, void *call_data)
{
DataLength* dl = (DataLength *) call_data;
string s(dl->data, dl->length);
bool s_ends_with_nl = false;
if (s.length() > 0 && s[s.length() - 1] == '\n')
{
s_ends_with_nl = true;
s = s.before(int(s.length() - 1));
}
s = quote(s);
string nl = string("\\n\"\n") + replicate(' ', strlen(prefix)) + "\"";
s.gsub("\\n", nl);
if (s_ends_with_nl)
s(s.length() - 1, 0) = "\\n";
dddlog << prefix << s << '\n';
dddlog.flush();
}
static void TraceInputHP(Agent *, void *, void *call_data)
{
trace("<< ", call_data);
}
static void TraceOutputHP(Agent *, void *, void *call_data)
{
trace(">> ", call_data);
}
static void TraceErrorHP(Agent *, void *, void *call_data)
{
trace("<= ", call_data);
}