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 >
C/C++ Source or Header  |  1998-12-02  |  41KB  |  1,492 lines

  1. // $Id: plotter.C,v 1.38 1998/12/02 08:27:24 zeller Exp $ -*- C++ -*-
  2. // Create a plotter interface
  3.  
  4. // Copyright (C) 1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char plotter_rcsid[] = 
  30.     "$Id: plotter.C,v 1.38 1998/12/02 08:27:24 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include "plotter.h"
  37.  
  38. #include "assert.h"
  39. #include "charsets.h"
  40. #include "cook.h"
  41. #include "ddd.h"
  42. #include "exit.h"
  43. #include "findParent.h"
  44. #include "findWindow.h"
  45. #include "file.h"
  46. #include "filetype.h"
  47. #include "fonts.h"
  48. #include "post.h"
  49. #include "print.h"
  50. #include "regexps.h"
  51. #include "simpleMenu.h"
  52. #include "status.h"
  53. #include "strclass.h"
  54. #include "string-fun.h"
  55. #include "verify.h"
  56. #include "version.h"
  57. #include "wm.h"
  58. #include "AppData.h"
  59. #include "Command.h"
  60. #include "Delay.h"
  61. #include "HelpCB.h"
  62. #include "MakeMenu.h"
  63. #include "PlotArea.h"
  64. #include "Swallower.h"
  65. #include "DispValue.h"
  66. #include "DataDisp.h"
  67. #include "DestroyCB.h"
  68. #include "TimeOut.h"
  69.  
  70. #include <stdio.h>
  71. #include <fstream.h>
  72.  
  73. #include <Xm/Command.h>
  74. #include <Xm/MainW.h>
  75. #include <Xm/MessageB.h>
  76. #include <Xm/AtomMgr.h>
  77. #include <Xm/FileSB.h>
  78. #include <Xm/Protocols.h>
  79. #include <Xm/DrawingA.h>
  80. #include <Xm/ScrolledW.h>
  81. #include <Xm/SelectioB.h>
  82. #include <Xm/ScrollBar.h>
  83. #include <Xm/Text.h>
  84. #include <Xm/TextF.h>
  85. #include <Xm/ToggleB.h>
  86.  
  87. static void TraceInputHP (Agent *source, void *, void *call_data);
  88. static void TraceOutputHP(Agent *source, void *, void *call_data);
  89. static void TraceErrorHP (Agent *source, void *, void *call_data);
  90. static void SetStatusHP  (Agent *source, void *, void *call_data);
  91. static void PlotterNotFoundHP(Agent *source, void *, void *call_data);
  92.  
  93. static void CancelPlotCB(Widget, XtPointer, XtPointer);
  94. static void ExposePlotAreaCB(Widget, XtPointer, XtPointer);
  95. static void ResizePlotAreaCB(Widget, XtPointer, XtPointer);
  96.  
  97. static void SelectPlotCB(Widget, XtPointer, XtPointer);
  98. static void SelectAndPrintPlotCB(Widget, XtPointer, XtPointer);
  99.  
  100. static void ReplotCB(Widget, XtPointer, XtPointer);
  101. static void PlotCommandCB(Widget, XtPointer, XtPointer);
  102. static void ExportPlotCB(Widget, XtPointer, XtPointer);
  103.  
  104. static void ToggleOptionCB(Widget, XtPointer, XtPointer);
  105. static void ToggleLogscaleCB(Widget, XtPointer, XtPointer);
  106. static void SetStyleCB(Widget, XtPointer, XtPointer);
  107. static void SetContourCB(Widget, XtPointer, XtPointer);
  108. static void SetViewCB(Widget, XtPointer, XtPointer);
  109. static void SwallowCB(Widget, XtPointer, XtPointer);
  110.  
  111. struct PlotWindowInfo {
  112.     DispValue *source;        // The source we depend upon
  113.     string window_name;        // The window name
  114.     PlotAgent *plotter;        // The current Gnuplot instance
  115.     PlotArea *area;        // The area we're drawing in
  116.     Widget shell;        // The shell we're in
  117.     Widget working_dialog;    // The working dialog
  118.     Widget swallower;        // The Gnuplot window
  119.     Widget vsb;            // Vertical scroll bar
  120.     Widget hsb;            // Horizontal scroll bar
  121.     Widget command;        // Command widget
  122.     Widget command_dialog;      // Command dialog
  123.     Widget export_dialog;       // Export dialog
  124.     bool active;        // True if popped up
  125.     XtIntervalId swallow_timer;    // Wait for Window creation
  126.  
  127.     string settings;         // Plot settings
  128.     XtIntervalId settings_timer; // Wait for settings
  129.     string settings_file;     // File to get settings from
  130.     StatusDelay *settings_delay; // Delay while getting settings
  131.  
  132.     // Constructor - just initialize
  133.     PlotWindowInfo()
  134.     : source(0), window_name(""),
  135.       plotter(0), area(0), shell(0), working_dialog(0), swallower(0),
  136.       vsb(0), hsb(0), command(0), command_dialog(0), 
  137.       export_dialog(0), active(false), swallow_timer(0), 
  138.       settings(""), settings_timer(0), settings_file(""), settings_delay(0)
  139.     {}
  140. };
  141.  
  142.  
  143. //-------------------------------------------------------------------------
  144. // Menus
  145. //-------------------------------------------------------------------------
  146.  
  147. static MMDesc file_menu[] = 
  148. {
  149.     { "command", MMPush, { PlotCommandCB, 0 }, 0, 0, 0, 0 },
  150.     MMSep,
  151.     { "replot",  MMPush, { ReplotCB, 0 }, 0, 0, 0, 0 },
  152.     { "print",   MMPush, { SelectAndPrintPlotCB, 0 }, 0, 0, 0, 0 },
  153.     { "export",  MMPush, { ExportPlotCB, 0 }, 0, 0, 0, 0 },
  154.     MMSep,
  155.     { "close",   MMPush, { CancelPlotCB, 0 }, 0, 0, 0, 0 },
  156.     { "exit",    MMPush, { DDDExitCB, XtPointer(EXIT_SUCCESS) }, 0, 0, 0, 0},
  157.     MMEnd
  158. };
  159.  
  160. static MMDesc view_menu[] = 
  161. {
  162.     { "border",    MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  163.     { "time",      MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  164.     MMSep,
  165.     { "grid",      MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  166.     { "xzeroaxis", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  167.     { "yzeroaxis", MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  168.     MMEnd
  169. };
  170.  
  171. static MMDesc contour_menu[] = 
  172. {
  173.     { "base",      MMToggle, { SetContourCB, 0 }, 0, 0, 0, 0 },
  174.     { "surface",   MMToggle, { SetContourCB, 0 }, 0, 0, 0, 0 },
  175.     MMEnd
  176. };
  177.  
  178. static MMDesc scale_menu[] = 
  179. {
  180.     { "logscale",  MMToggle, { ToggleLogscaleCB, 0 }, 0, 0, 0, 0 },
  181.     MMSep,
  182.     { "xtics",     MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  183.     { "ytics",     MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  184.     { "ztics",     MMToggle, { ToggleOptionCB, 0 }, 0, 0, 0, 0 },
  185.     MMEnd
  186. };
  187.  
  188. static MMDesc plot_menu[] = 
  189. {
  190.     { "points",         MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  191.     { "lines",          MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  192.     { "lines3d",        MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  193.     { "linespoints",    MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  194.     { "linespoints3d",  MMToggle | MMUnmanaged, 
  195.                                   { SetStyleCB, 0 }, 0, 0, 0, 0 },
  196.     { "impulses",       MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  197.     { "dots",           MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  198.     { "steps2d",        MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  199.     { "boxes2d",        MMToggle, { SetStyleCB, 0 }, 0, 0, 0, 0 },
  200.     MMEnd
  201. };
  202.  
  203. static MMDesc menubar[] = 
  204. {
  205.     { "file",     MMMenu,          MMNoCB, file_menu,        0, 0, 0 },
  206.     { "edit",     MMMenu,          MMNoCB, simple_edit_menu, 0, 0, 0 },
  207.     { "plotView", MMMenu,          MMNoCB, view_menu,        0, 0, 0 },
  208.     { "plot",     MMRadioMenu,     MMNoCB, plot_menu,        0, 0, 0 },
  209.     { "scale",    MMMenu,          MMNoCB, scale_menu,       0, 0, 0 },
  210.     { "contour",  MMMenu,          MMNoCB, contour_menu,     0, 0, 0 },
  211.     { "help",     MMMenu | MMHelp, MMNoCB, simple_help_menu, 0, 0, 0 },
  212.     MMEnd
  213. };
  214.  
  215.  
  216. static void configure_plot(PlotWindowInfo *plot);
  217.  
  218.  
  219. //-------------------------------------------------------------------------
  220. // Plotter commands
  221. //-------------------------------------------------------------------------
  222.  
  223. static void send(PlotWindowInfo *plot, const string& cmd)
  224. {
  225.     data_disp->select(plot->source);
  226.     plot->plotter->write(cmd.chars(), cmd.length());
  227. }
  228.  
  229. static void send_and_replot(PlotWindowInfo *plot, string cmd)
  230. {
  231.     if (cmd.matches(rxwhite))
  232.     return;
  233.  
  234.     if (!cmd.contains('\n', -1))
  235.     cmd += "\n";
  236.     if (cmd.contains("help", 0))
  237.     cmd += "\n";        // Exit `help'
  238.     else
  239.     cmd += "replot\n";
  240.  
  241.     if (plot->area != 0)
  242.     plot->area->plot_pending();
  243.  
  244.     send(plot, cmd);
  245. }
  246.  
  247.  
  248. //-------------------------------------------------------------------------
  249. // Set up menu
  250. //-------------------------------------------------------------------------
  251.  
  252. static void slurp_file(const string& filename, string& target)
  253. {
  254.     ifstream is(filename);
  255.     if (is.bad())
  256.     {
  257.     target = "";
  258.     return;
  259.     }
  260.  
  261.     ostrstream s;
  262.     int c;
  263.     while ((c = is.get()) != EOF)
  264.     s << (unsigned char)c;
  265.  
  266.     target = s;
  267. }
  268.  
  269. static void GetPlotSettingsCB(XtPointer client_data, XtIntervalId *id)
  270. {
  271.     (void) id;            // Use it
  272.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  273.  
  274.     assert(plot->settings_timer == *id);
  275.     plot->settings_timer = 0;
  276.  
  277.     // Check for settings file to be created
  278.     string settings;
  279.     slurp_file(plot->settings_file, settings);
  280.  
  281.     if (settings.contains("set zero"))
  282.     {
  283.     // Settings are complete
  284.     unlink(plot->settings_file);
  285.     plot->settings = settings;
  286.  
  287.     configure_plot(plot);
  288.  
  289.     delete plot->settings_delay;
  290.     plot->settings_delay = 0;
  291.     }
  292.     else
  293.     {
  294.     // Try again in 500 ms
  295.     plot->settings_timer = 
  296.         XtAppAddTimeOut(XtWidgetToApplicationContext(plot->shell), 500, 
  297.                 GetPlotSettingsCB, XtPointer(plot));
  298.     }
  299. }
  300.  
  301. static void configure_options(PlotWindowInfo *plot, MMDesc *menu, 
  302.                   const string& settings)
  303. {
  304.     for (int i = 0; menu[i].name != 0; i++)
  305.     {
  306.     if ((menu[i].type & MMTypeMask) != MMToggle)
  307.         continue;
  308.  
  309.     string name = menu[i].name;
  310.  
  311.     Widget w = XtNameToWidget(plot->shell, "*" + name);
  312.     XtCallbackProc callback = menu[i].callback.callback;
  313.  
  314.     bool set = false;
  315.     if (callback == ToggleOptionCB)
  316.     {
  317.         set = settings.contains("\nset " + name + "\n");
  318.     }
  319.     else if (callback == SetContourCB)
  320.     {
  321.         if (name == "base")
  322.         set = settings.contains("\nset contour base\n") ||
  323.             settings.contains("\nset contour both\n");
  324.         else if (name == "surface")
  325.         set = settings.contains("\nset contour surface\n") ||
  326.             settings.contains("\nset contour both\n");
  327.     }
  328.     else if (callback == ToggleLogscaleCB)
  329.     {
  330.         set = settings.contains("\nset logscale ");
  331.     }
  332.  
  333.     XmToggleButtonSetState(w, set, False);
  334.     }
  335. }
  336.  
  337. static void configure_plot(PlotWindowInfo *plot)
  338. {
  339.     if (plot->plotter == 0)
  340.     return;
  341.  
  342.     int ndim = plot->plotter->dimensions();
  343.  
  344.     // Set up plot menu
  345.     int i;
  346.     for (i = 0; plot_menu[i].name != 0; i++)
  347.     {
  348.     if ((plot_menu[i].type & MMTypeMask) != MMToggle)
  349.         continue;
  350.  
  351.     string name = plot_menu[i].name;
  352.  
  353.     Widget w = XtNameToWidget(plot->shell, "*" + name);
  354.  
  355.     if (name.contains("2d", -1))
  356.         XtSetSensitive(w, ndim == 2);
  357.     else if (name.contains("3d", -1))
  358.         XtSetSensitive(w, ndim >= 3);
  359.     else
  360.         XtSetSensitive(w, ndim >= 2);
  361.     }
  362.  
  363.     // Log scale is available only iff all values are non-negative
  364.     Widget logscale = XtNameToWidget(plot->shell, "*logscale");
  365.     XtSetSensitive(logscale, plot->plotter->min_v() >= 0.0);
  366.  
  367.     // Axes can be toggled in 2d mode only
  368.     Widget xzeroaxis = XtNameToWidget(plot->shell, "*xzeroaxis");
  369.     Widget yzeroaxis = XtNameToWidget(plot->shell, "*yzeroaxis");
  370.     XtSetSensitive(xzeroaxis, ndim <= 2);
  371.     XtSetSensitive(yzeroaxis, ndim <= 2);
  372.  
  373.     // Z Tics are available in 3d mode only
  374.     Widget ztics = XtNameToWidget(plot->shell, "*ztics");
  375.     XtSetSensitive(ztics, ndim >= 3);
  376.  
  377.     // Contour drawing is available in 3d mode only
  378.     Widget base    = XtNameToWidget(plot->shell, "*base");
  379.     Widget surface = XtNameToWidget(plot->shell, "*surface");
  380.     XtSetSensitive(base,    ndim >= 3);
  381.     XtSetSensitive(surface, ndim >= 3);
  382.  
  383.     // Set scrollbars
  384.     manage_child(plot->hsb, ndim >= 3);
  385.     manage_child(plot->vsb, ndim >= 3);
  386.  
  387.     // Check if we can export something
  388.     bool have_source = false;
  389.     bool can_export  = false;
  390.     const StringArray& sources = plot->plotter->data_files();
  391.     for (i = 0; i < sources.size(); i++)
  392.     {
  393.     if (sources[i] != "")
  394.     {
  395.         if (have_source)
  396.         can_export  = false; // Multiple source files
  397.         else
  398.         can_export = have_source = true;
  399.     }
  400.     }
  401.  
  402.     Widget export_w = XtNameToWidget(plot->shell, "*export");
  403.     set_sensitive(export_w, can_export);
  404.  
  405.     // The remainder requires settings
  406.     if (plot->settings == "")
  407.     {
  408.     // No settings yet
  409.     if (plot->settings_timer == 0)
  410.     {
  411.         plot->settings_delay = 
  412.         new StatusDelay("Retrieving Plot Settings");
  413.  
  414.         // Save settings...
  415.         plot->settings_file = tmpnam(0);
  416.         string cmd = "save " + quote(plot->settings_file) + "\n";
  417.         send(plot, cmd);
  418.  
  419.         // ...and try again in 250ms
  420.         plot->settings_timer = 
  421.         XtAppAddTimeOut(XtWidgetToApplicationContext(plot->shell), 250,
  422.                 GetPlotSettingsCB, XtPointer(plot));
  423.  
  424.         // Set initial scrollbar defaults
  425.         XtVaSetValues(plot->vsb, XmNvalue, 60, NULL);
  426.         XtVaSetValues(plot->hsb, XmNvalue, 30, NULL);
  427.     }
  428.  
  429.     return;
  430.     }
  431.  
  432.     configure_options(plot, view_menu,    plot->settings);
  433.     configure_options(plot, contour_menu, plot->settings);
  434.     configure_options(plot, scale_menu,   plot->settings);
  435.  
  436.     // Get style
  437.     for (i = 0; plot_menu[i].name != 0; i++)
  438.     {
  439.     if ((plot_menu[i].type & MMTypeMask) != MMToggle)
  440.         continue;
  441.  
  442.     string name = plot_menu[i].name;
  443.  
  444.     Widget w = XtNameToWidget(plot->shell, "*" + name);
  445.  
  446.     bool set = plot->settings.contains("\nset data style " + name + "\n");
  447.     XmToggleButtonSetState(w, set, False);
  448.     }
  449.  
  450.     // Get position
  451.     int rot_x = 60;
  452.     int rot_z = 30;
  453.  
  454.     int view_index = plot->settings.index("set view ");
  455.     if (view_index >= 0)
  456.     {
  457.     // `set view <rot_x> {,{<rot_z>}{,{<scale>}{,<scale_z>}}}'
  458.     string view_setting = plot->settings.after("set view ");
  459.     rot_x = atoi(view_setting);
  460.     view_setting = view_setting.after(", ");
  461.     rot_z = atoi(view_setting);
  462.     }
  463.  
  464.     XtVaSetValues(plot->vsb, XmNvalue, rot_x, NULL);
  465.     XtVaSetValues(plot->hsb, XmNvalue, rot_z, NULL);
  466. }
  467.  
  468.  
  469.  
  470. //-------------------------------------------------------------------------
  471. // Decoration stuff
  472. //-------------------------------------------------------------------------
  473.  
  474. // Start plot
  475. static void popup_plot_shell(PlotWindowInfo *plot)
  476. {
  477.     if (!plot->active && plot->plotter != 0)
  478.     {
  479.     // We have the plot
  480.     plot->plotter->removeHandler(Died, PlotterNotFoundHP, 
  481.                      (void *)plot);
  482.  
  483.     // Fetch plot settings
  484.     configure_plot(plot);
  485.  
  486.     // Command and export dialogs are not needed (yet)
  487.     if (plot->command_dialog != 0)
  488.         XtUnmanageChild(plot->command_dialog);
  489.     if (plot->export_dialog != 0)
  490.         XtUnmanageChild(plot->export_dialog);
  491.  
  492.     // Pop down working dialog
  493.     if (plot->working_dialog != 0)
  494.         XtUnmanageChild(plot->working_dialog);
  495.  
  496.     // Pop up shell
  497.     XtSetSensitive(plot->shell, True);
  498.     XtPopup(plot->shell, XtGrabNone);
  499.     wait_until_mapped(plot->shell);
  500.  
  501.     plot->active = true;
  502.     }
  503. }
  504.  
  505. // Swallow WINDOW
  506. static void swallow(PlotWindowInfo *plot, Window window)
  507. {
  508.     assert(window != None);
  509.  
  510.     // We have the window
  511.     XtRemoveCallback(plot->swallower, XtNwindowCreatedCallback, 
  512.              SwallowCB, XtPointer(plot));
  513.  
  514.     if (plot->swallow_timer != 0)
  515.     {
  516.     XtRemoveTimeOut(plot->swallow_timer);
  517.     plot->swallow_timer = 0;
  518.     }
  519.  
  520.     XtVaSetValues(plot->swallower, XtNwindow, window, NULL);
  521.  
  522.     popup_plot_shell(plot);
  523. }
  524.  
  525. // Swallow new GNUPLOT window; search from window created on root.
  526. static void SwallowCB(Widget swallower, XtPointer client_data, 
  527.               XtPointer call_data)
  528. {
  529.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  530.     assert(plot->swallower == swallower);
  531.  
  532.     SwallowerInfo *info = (SwallowerInfo *)call_data;
  533.  
  534.     Window root = info->window;
  535.     Window window = None;
  536.     Display *display = XtDisplay(swallower);
  537.  
  538.     // Try the exact name as given
  539.     if (window == None)
  540.     window = findWindow(display, root, plot->window_name);
  541.  
  542.     // Try the capitalized name.  Gnuplot does this.
  543.     if (window == None)
  544.     window = findWindow(display, root, capitalize(plot->window_name));
  545.  
  546.     // Try any `Gnuplot' window just created
  547.     if (window == None)
  548.     window = findWindow(display, root, app_data.plot_window_class);
  549.  
  550.     if (window != None)
  551.     swallow(plot, window);
  552. }
  553.  
  554. // Swallow new GNUPLOT window; search from root window (expensive).
  555. static void SwallowTimeOutCB(XtPointer client_data, XtIntervalId *id)
  556. {
  557.     (void) id;
  558.  
  559.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  560.     assert(*id == plot->swallow_timer);
  561.     plot->swallow_timer = 0;
  562.  
  563.     Window root = RootWindowOfScreen(XtScreen(plot->swallower));
  564.     Window window = None;
  565.     Display *display = XtDisplay(plot->swallower);
  566.  
  567.     // Try the exact name as given
  568.     if (window == None)
  569.     window = findWindow(display, root, plot->window_name);
  570.  
  571.     // Try the capitalized name.  Gnuplot does this.
  572.     if (window == None)
  573.     window = findWindow(display, root, capitalize(plot->window_name));
  574.  
  575.     if (window == None)
  576.     {
  577.     // Try again later
  578.     plot->swallow_timer = 
  579.         XtAppAddTimeOut(XtWidgetToApplicationContext(plot->swallower),
  580.                 app_data.plot_window_delay, 
  581.                 SwallowTimeOutCB, XtPointer(plot));
  582.     }
  583.  
  584.     if (window != None)
  585.     swallow(plot, window);
  586. }
  587.  
  588. // Swallow again after window has gone.  This happens while printing.
  589. static void SwallowAgainCB(Widget swallower, XtPointer client_data, XtPointer)
  590. {
  591.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  592.     assert(plot->swallower == swallower);
  593.  
  594.     XtAddCallback(swallower, XtNwindowCreatedCallback, SwallowCB, 
  595.           XtPointer(plot));
  596.  
  597.     if (plot->swallow_timer != 0)
  598.     XtRemoveTimeOut(plot->swallow_timer);
  599.  
  600.     plot->swallow_timer = 
  601.     XtAppAddTimeOut(XtWidgetToApplicationContext(plot->swallower),
  602.             app_data.plot_window_delay, 
  603.             SwallowTimeOutCB, XtPointer(plot));
  604. }
  605.  
  606.  
  607. // Cancel plot
  608. static void popdown_plot_shell(PlotWindowInfo *plot)
  609. {
  610.     static bool entered = false;
  611.     if (entered)
  612.     return;
  613.  
  614.     entered = true;
  615.  
  616.     // Manage dialogs
  617.     if (plot->working_dialog != 0)
  618.     XtUnmanageChild(plot->working_dialog);
  619.     if (plot->command_dialog != 0)
  620.     XtUnmanageChild(plot->command_dialog);
  621.     if (plot->export_dialog != 0)
  622.     XtUnmanageChild(plot->export_dialog);
  623.  
  624.     if (plot->shell != 0)
  625.     {
  626.     XWithdrawWindow(XtDisplay(plot->shell), XtWindow(plot->shell),
  627.             XScreenNumberOfScreen(XtScreen(plot->shell)));
  628.     XtPopdown(plot->shell);
  629.  
  630.     // XtPopdown may not withdraw an iconified shell.  Hence, make
  631.     // sure the shell really becomes disabled.
  632.     XtSetSensitive(plot->shell, False);
  633.     }
  634.  
  635.     // Manage settings
  636.     if (plot->settings_timer != 0)
  637.     {
  638.     // Still waiting for settings
  639.     XtRemoveTimeOut(plot->settings_timer);
  640.     plot->settings_timer = 0;
  641.  
  642.     unlink(plot->settings_file);
  643.     }
  644.  
  645.     if (plot->settings_delay != 0)
  646.     {
  647.     plot->settings_delay->outcome = "canceled";
  648.     delete plot->settings_delay;
  649.     plot->settings_delay = 0;
  650.     }
  651.  
  652.     plot->settings = "";
  653.  
  654.     plot->active = false;
  655.  
  656.     entered = false;
  657. }
  658.  
  659. static void CancelPlotCB(Widget, XtPointer client_data, XtPointer)
  660. {
  661.     static bool entered = false;
  662.     if (entered)
  663.     return;
  664.  
  665.     entered = true;
  666.  
  667.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  668.     popdown_plot_shell(plot);
  669.  
  670.     if (plot->swallower != 0)
  671.     {
  672.     // Don't wait for window to swallow
  673.     XtRemoveAllCallbacks(plot->swallower, XtNwindowCreatedCallback);
  674.     XtRemoveAllCallbacks(plot->swallower, XtNwindowGoneCallback);
  675.     }
  676.  
  677.     if (plot->swallow_timer != 0)
  678.     {
  679.     XtRemoveTimeOut(plot->swallow_timer);
  680.     plot->swallow_timer = 0;
  681.     }
  682.  
  683.     if (plot->plotter != 0)
  684.     {
  685.     // Terminate plotter
  686.     plot->plotter->removeHandler(Died, PlotterNotFoundHP, client_data);
  687.     plot->plotter->terminate();
  688.     plot->plotter = 0;
  689.     }
  690.  
  691.     entered = false;
  692. }
  693.  
  694. static void DeletePlotterCB(XtPointer client_data, XtIntervalId *)
  695. {
  696.     Agent *plotter = (Agent *)client_data;
  697.     delete plotter;
  698. }
  699.  
  700. static void DeletePlotterHP(Agent *plotter, void *client_data, void *)
  701. {
  702.     // Plotter has died - delete memory
  703.     XtAppAddTimeOut(XtWidgetToApplicationContext(gdb_w), 0, 
  704.             DeletePlotterCB, XtPointer(plotter));
  705.  
  706.     plotter->removeHandler(Died, DeletePlotterHP, client_data);
  707.  
  708.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  709.     assert(plot->plotter == 0 || plot->plotter == plotter);
  710.     plot->plotter = 0;
  711.     popdown_plot_shell(plot);
  712. }
  713.  
  714. static void GetPlotHP(Agent *, void *client_data, void *call_data)
  715. {
  716.     // We got the plot commands
  717.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  718.  
  719.     // Pass commands to the plot area
  720.     DataLength *dl = (DataLength *)call_data;
  721.     plot->area->plot(dl->data, dl->length);
  722.  
  723.     // Popup shell
  724.     popup_plot_shell(plot);
  725. }
  726.  
  727. static void PlotterNotFoundHP(Agent *plotter, void *client_data, void *)
  728. {
  729.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  730.     (void) plot;        // Use it
  731.     assert(plot->plotter == 0 || plot->plotter == plotter);
  732.  
  733.     plotter->removeHandler(Died, PlotterNotFoundHP, client_data);
  734.  
  735.     string base = app_data.plot_command;
  736.     if (base.contains(' '))
  737.     base = base.before(' ');
  738.  
  739.     Arg args[10];
  740.     Cardinal arg = 0;
  741.     MString msg = rm(capitalize(base) + " could not be started.");
  742.     XtSetArg(args[arg], XmNmessageString, msg.xmstring()); arg++;
  743.     Widget dialog = 
  744.     verify(XmCreateErrorDialog(find_shell(),
  745.                    "no_plotter_dialog", args, arg));
  746.     XtUnmanageChild(XmMessageBoxGetChild
  747.             (dialog, XmDIALOG_CANCEL_BUTTON));
  748.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
  749.  
  750.     Delay::register_shell(dialog);
  751.     manage_and_raise(dialog);
  752. }
  753.  
  754.  
  755. #define SWALLOWER_NAME "swallower"
  756. #define PLOT_AREA_NAME "area"
  757.  
  758. static VoidArray plot_infos;
  759.  
  760. static PlotWindowInfo *new_decoration(const string& name)
  761. {
  762.     PlotWindowInfo *plot = 0;
  763.  
  764.     // Check whether we can reuse an existing decoration
  765.     for (int i = 0; i < plot_infos.size(); i++)
  766.     {
  767.     PlotWindowInfo *info = (PlotWindowInfo *)plot_infos[i];
  768.     if (info->plotter == 0)
  769.     {
  770.         // Shell is unused - use this one
  771.         plot = info;
  772.         break;
  773.     }
  774.     }
  775.  
  776.     if (plot == 0)
  777.     {
  778.     plot = new PlotWindowInfo;
  779.  
  780.     // Create decoration windows
  781.     Arg args[10];
  782.     Cardinal arg = 0;
  783.     XtSetArg(args[arg], XmNallowShellResize, True);       arg++;
  784.     XtSetArg(args[arg], XmNdeleteResponse, XmDO_NOTHING); arg++;
  785.  
  786.     // Mark shell as `used'
  787.     XtSetArg(args[arg], XmNuserData, XtPointer(True)); arg++;
  788.     plot->shell = verify(XtCreateWidget("plot", topLevelShellWidgetClass,
  789.                         find_shell(), args, arg));
  790.  
  791.     Atom WM_DELETE_WINDOW = 
  792.         XmInternAtom(XtDisplay(plot->shell), "WM_DELETE_WINDOW", False);
  793.     XmAddWMProtocolCallback(plot->shell, WM_DELETE_WINDOW, 
  794.                 CancelPlotCB, XtPointer(plot));
  795.  
  796.     arg = 0;
  797.     Widget main_window = XmCreateMainWindow(plot->shell, "main_window", 
  798.                         args, arg);
  799.     XtManageChild(main_window);
  800.  
  801.     MMcreateMenuBar(main_window, "menubar", menubar);
  802.     MMaddCallbacks(file_menu,    XtPointer(plot));
  803.     MMaddCallbacks(simple_edit_menu);
  804.     MMaddCallbacks(view_menu,    XtPointer(plot));
  805.     MMaddCallbacks(plot_menu,    XtPointer(plot));
  806.     MMaddCallbacks(scale_menu,   XtPointer(plot));
  807.     MMaddCallbacks(contour_menu, XtPointer(plot));
  808.     MMaddCallbacks(simple_help_menu);
  809.     MMaddHelpCallback(menubar, ImmediateHelpCB);
  810.  
  811.     arg = 0;
  812.     XtSetArg(args[arg], XmNscrollingPolicy, XmAPPLICATION_DEFINED); arg++;
  813.     XtSetArg(args[arg], XmNvisualPolicy,    XmVARIABLE);            arg++;
  814.     Widget scroll = 
  815.         XmCreateScrolledWindow(main_window, "scroll", args, arg);
  816.     XtManageChild(scroll);
  817.  
  818.     // Create work window
  819.     Widget work;
  820.     string plot_term_type = downcase(app_data.plot_term_type);
  821.     if (plot_term_type.contains("xlib", 0))
  822.     {
  823.         // xlib type - create plot area to draw plot commands
  824.         arg = 0;
  825.         work = XmCreateDrawingArea(scroll, PLOT_AREA_NAME, args, arg);
  826.         XtManageChild(work);
  827.  
  828.         plot->area = 
  829.         new PlotArea(work, make_font(app_data, FixedWidthDDDFont));
  830.         XtVaSetValues(work, XmNuserData, XtPointer(plot->area), NULL);
  831.     }
  832.     else if (plot_term_type.contains("x11", 0))
  833.     {
  834.         // x11 type - swallow Gnuplot window
  835.         arg = 0;
  836.         work = plot->swallower = 
  837.         XtCreateManagedWidget(SWALLOWER_NAME, swallowerWidgetClass, 
  838.                       scroll, args, arg);
  839.     }
  840.     else
  841.     {
  842.         // Unknown terminal type
  843.         post_error("Unknown plot terminal type " + 
  844.                quote(app_data.plot_term_type),
  845.                "unknown_plot_term_type_error");
  846.         return 0;
  847.     }
  848.  
  849.     // Create scroll bars
  850.     const int slider_size = 20;
  851.  
  852.     arg = 0;
  853.     XtSetArg(args[arg], XmNorientation, XmHORIZONTAL);      arg++;
  854.     XtSetArg(args[arg], XmNminimum,     0);                 arg++;
  855.     XtSetArg(args[arg], XmNmaximum,     360 + slider_size); arg++;
  856.     plot->hsb = XmCreateScrollBar(scroll, "hsb", args, arg);
  857.     XtManageChild(plot->hsb);
  858.  
  859.     arg = 0;
  860.     XtSetArg(args[arg], XmNorientation, XmVERTICAL);        arg++;
  861.     XtSetArg(args[arg], XmNminimum,     0);                 arg++;
  862.     XtSetArg(args[arg], XmNmaximum,     180 + slider_size); arg++;
  863.     plot->vsb = XmCreateScrollBar(scroll, "vsb", args, arg);
  864.     XtManageChild(plot->vsb);
  865.  
  866.     XtAddCallback(plot->hsb, XmNvalueChangedCallback,
  867.               SetViewCB, XtPointer(plot));
  868.     XtAddCallback(plot->vsb, XmNvalueChangedCallback,
  869.               SetViewCB, XtPointer(plot));
  870.  
  871. #if 0
  872.     XtAddCallback(plot->hsb, XmNdragCallback, SetViewCB, XtPointer(plot));
  873.     XtAddCallback(plot->vsb, XmNdragCallback, SetViewCB, XtPointer(plot));
  874. #endif
  875.  
  876.     XmScrolledWindowSetAreas(scroll, plot->hsb, plot->vsb, work);
  877.  
  878.     Delay::register_shell(plot->shell);
  879.     InstallButtonTips(plot->shell);
  880.  
  881.     plot_infos += plot;
  882.     }
  883.  
  884.     string title = DDD_NAME ": " + name;
  885.     XtVaSetValues(plot->shell,
  886.           XmNtitle, title.chars(),
  887.           XmNiconName, title.chars(),
  888.           NULL);
  889.  
  890.     if (plot->swallower != 0)
  891.     {
  892.     XtRemoveAllCallbacks(plot->swallower, XtNwindowCreatedCallback);
  893.     XtAddCallback(plot->swallower, XtNwindowCreatedCallback,
  894.               SwallowCB, XtPointer(plot));
  895.  
  896.     XtRemoveAllCallbacks(plot->swallower, XtNwindowGoneCallback);
  897.     XtAddCallback(plot->swallower, XtNwindowGoneCallback, 
  898.               SwallowAgainCB, XtPointer(plot));
  899.  
  900.     if (plot->swallow_timer != 0)
  901.         XtRemoveTimeOut(plot->swallow_timer);
  902.  
  903.     plot->swallow_timer = 
  904.         XtAppAddTimeOut(XtWidgetToApplicationContext(plot->swallower),
  905.                 app_data.plot_window_delay, SwallowTimeOutCB, 
  906.                 XtPointer(plot));
  907.     }
  908.  
  909.     plot->active = false;
  910.  
  911.     return plot;
  912. }
  913.  
  914. // Remove all unused decorations from cache
  915. void clear_plot_window_cache()
  916. {
  917.     for (int i = 0; i < plot_infos.size(); i++)
  918.     {
  919.     PlotWindowInfo *info = (PlotWindowInfo *)plot_infos[i];
  920.     if (info->plotter == 0)
  921.     {
  922.         // Shell is unused -- destroy it
  923.         XtDestroyWidget(info->shell);
  924.         info->shell = 0;
  925.     }
  926.     else
  927.     {
  928.         // A running shell should be destroyed after invocation.
  929.         // (FIXME)
  930.     }
  931.     }
  932.  
  933.     static VoidArray empty;
  934.     plot_infos = empty;
  935. }
  936.  
  937.  
  938. // Create a new plot window
  939. PlotAgent *new_plotter(string name, DispValue *source)
  940. {
  941.     static int tics = 1;
  942.  
  943.     string cmd = app_data.plot_command;
  944.     cmd.gsub("@FONT@", make_font(app_data, FixedWidthDDDFont));
  945.  
  946.     string window_name = ddd_NAME "plot" + itostring(tics++);
  947.     if (cmd.contains("@NAME@"))
  948.     cmd.gsub("@NAME@", window_name);
  949.     else
  950.     cmd += " -name " + window_name;
  951.  
  952.     // Create shell
  953.     PlotWindowInfo *plot = new_decoration(name);
  954.     if (plot == 0)
  955.     return 0;
  956.  
  957.     plot->source      = source;
  958.     plot->window_name = window_name;
  959.     XtVaSetValues(plot->shell, XmNuserData, XtPointer(True), NULL);
  960.  
  961.     // Pop up a working dialog
  962.     static Widget dialog = 0;
  963.     if (dialog == 0)
  964.     {
  965.     Arg args[10];
  966.     Cardinal arg = 0;
  967.     dialog = verify(XmCreateWorkingDialog(find_shell(),
  968.                           "launch_plot_dialog", 
  969.                           args, arg));
  970.     XtUnmanageChild(XmMessageBoxGetChild(dialog,
  971.                          XmDIALOG_OK_BUTTON));
  972.     XtUnmanageChild(XmMessageBoxGetChild(dialog,
  973.                          XmDIALOG_HELP_BUTTON));
  974.     }
  975.  
  976.     XtRemoveAllCallbacks(dialog, XmNcancelCallback);
  977.     XtAddCallback(dialog, XmNcancelCallback, CancelPlotCB, XtPointer(plot));
  978.     plot->working_dialog = dialog;
  979.  
  980.     string base = cmd;
  981.     if (base.contains(' '))
  982.     base = cmd.before(' ');
  983.     MString msg = rm("Starting ") + tt(base) + rm("...");
  984.     XtVaSetValues(dialog, XmNmessageString, msg.xmstring(), NULL);
  985.     manage_and_raise(dialog);
  986.     wait_until_mapped(dialog);
  987.  
  988.     // Invoke plot process
  989.     PlotAgent *plotter = 
  990.     new PlotAgent(XtWidgetToApplicationContext(plot->shell), cmd);
  991.  
  992.     XtAddCallback(plot->shell, XtNpopdownCallback,
  993.           CancelPlotCB, XtPointer(plot));
  994.  
  995.     if (plot->area != 0)
  996.     {
  997.     XtAddCallback(plot->area->widget(), XmNexposeCallback,
  998.               ExposePlotAreaCB, XtPointer(plot));
  999.     XtAddCallback(plot->area->widget(), XmNresizeCallback,
  1000.               ResizePlotAreaCB, XtPointer(plot));
  1001.     }
  1002.  
  1003.     string init = app_data.plot_init_commands;
  1004.     init.prepend("set term " + string(app_data.plot_term_type) + "\n");
  1005.     if (init != "" && !init.contains('\n', -1))
  1006.     init += '\n';
  1007.  
  1008.     // Add trace handlers
  1009.     plotter->addHandler(Input,  TraceInputHP);     // Gnuplot => DDD
  1010.     plotter->addHandler(Output, TraceOutputHP);    // DDD => Gnuplot
  1011.     plotter->addHandler(Error,  TraceErrorHP);     // Gnuplot Errors => DDD
  1012.  
  1013.     // Show Gnuplot Errors in status line
  1014.     plotter->addHandler(Error,  SetStatusHP,       (void *)plot);
  1015.  
  1016.     // Handle death
  1017.     plotter->addHandler(Died, PlotterNotFoundHP, (void *)plot);
  1018.     plotter->addHandler(Died, DeletePlotterHP,   (void *)plot);
  1019.  
  1020.     if (plot->area != 0)
  1021.     plotter->addHandler(Plot, GetPlotHP, (void *)plot);
  1022.  
  1023.     plotter->start_with(init);
  1024.     plot->plotter = plotter;
  1025.  
  1026.     return plotter;
  1027. }
  1028.  
  1029.  
  1030. //-------------------------------------------------------------------------
  1031. // Drawing stuff
  1032. //-------------------------------------------------------------------------
  1033.  
  1034. static void ExposePlotAreaCB(Widget, XtPointer client_data, XtPointer)
  1035. {
  1036.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1037.     plot->area->replot(false);
  1038. }
  1039.  
  1040. static void ResizePlotAreaCB(Widget, XtPointer client_data, XtPointer)
  1041. {
  1042.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1043.     plot->area->replot(true);
  1044. }
  1045.  
  1046.  
  1047. //-------------------------------------------------------------------------
  1048. // Selection stuff
  1049. //-------------------------------------------------------------------------
  1050.  
  1051. static void SelectPlotCB(Widget, XtPointer client_data, XtPointer)
  1052. {
  1053.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1054.  
  1055.     data_disp->select(plot->source);
  1056. }
  1057.  
  1058. static void SelectAndPrintPlotCB(Widget w, XtPointer client_data, 
  1059.                  XtPointer call_data)
  1060. {
  1061.     SelectPlotCB(w, client_data, call_data);
  1062.     PrintPlotCB(w, client_data, call_data);
  1063. }
  1064.  
  1065.  
  1066.  
  1067. //-------------------------------------------------------------------------
  1068. // Plot again
  1069. //-------------------------------------------------------------------------
  1070.  
  1071. static void ReplotCB(Widget, XtPointer client_data, XtPointer)
  1072. {
  1073.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1074.  
  1075.     // This transfers the data once again and replots the whole thing
  1076.     plot->source->plot();
  1077. }
  1078.  
  1079. //-------------------------------------------------------------------------
  1080. // Command
  1081. //-------------------------------------------------------------------------
  1082.  
  1083. // Selection from Command widget
  1084. static void DoPlotCommandCB(Widget, XtPointer client_data, XtPointer call_data)
  1085. {
  1086.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1087.     XmCommandCallbackStruct *cbs = (XmCommandCallbackStruct *)call_data;
  1088.  
  1089.     MString xcmd(cbs->value, true);
  1090.     string cmd = xcmd.str();
  1091.  
  1092.     send_and_replot(plot, cmd);
  1093. }
  1094.  
  1095. // Apply button
  1096. static void ApplyPlotCommandCB(Widget, XtPointer client_data, XtPointer)
  1097. {
  1098.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1099.  
  1100.     Widget text = XmCommandGetChild(plot->command, XmDIALOG_COMMAND_TEXT);
  1101.     String cmd_s = 0;
  1102.  
  1103.     if (XmIsTextField(text))
  1104.     cmd_s = XmTextFieldGetString(text);
  1105.     else if (XmIsText(text))
  1106.     cmd_s = XmTextGetString(text);
  1107.     else
  1108.     assert(0);
  1109.  
  1110.     string cmd = cmd_s;
  1111.     XtFree(cmd_s);
  1112.  
  1113.     send_and_replot(plot, cmd);
  1114. }
  1115.  
  1116. static void EnableApplyCB(Widget, XtPointer client_data, XtPointer call_data)
  1117. {
  1118.     Widget apply = (Widget)client_data;
  1119.     XmCommandCallbackStruct *cbs = (XmCommandCallbackStruct *)call_data;
  1120.  
  1121.     set_sensitive(apply, cbs->length > 0);
  1122. }
  1123.  
  1124. static void PlotCommandCB(Widget, XtPointer client_data, XtPointer)
  1125. {
  1126.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1127.  
  1128.     if (plot->command_dialog == 0)
  1129.     {
  1130.     Arg args[10];
  1131.     Cardinal arg = 0;
  1132.     Widget dialog = 
  1133.         verify(XmCreatePromptDialog(plot->shell, "plot_command_dialog",
  1134.                     args, arg));
  1135.     Delay::register_shell(dialog);
  1136.     plot->command_dialog = dialog;
  1137.  
  1138.     Widget apply = XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON);
  1139.     XtManageChild(apply);
  1140.     
  1141.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1142.                            XmDIALOG_OK_BUTTON));
  1143.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1144.                            XmDIALOG_SELECTION_LABEL));
  1145.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
  1146.  
  1147.     XtAddCallback(dialog, XmNapplyCallback,
  1148.               ApplyPlotCommandCB, XtPointer(client_data));
  1149.     XtAddCallback(dialog, XmNhelpCallback,
  1150.               ImmediateHelpCB, XtPointer(client_data));
  1151.  
  1152.     arg = 0;
  1153.     Widget command = 
  1154.         verify(XmCreateCommand(dialog, "plot_command", args, arg));
  1155.     plot->command = command;
  1156.     XtManageChild(command);
  1157.  
  1158.     XtAddCallback(command, XmNcommandEnteredCallback, 
  1159.               DoPlotCommandCB, XtPointer(client_data));
  1160.     XtAddCallback(command, XmNcommandChangedCallback, 
  1161.               EnableApplyCB, XtPointer(apply));
  1162.     set_sensitive(apply, false);
  1163.     }
  1164.  
  1165.     manage_and_raise(plot->command_dialog);
  1166. }
  1167.  
  1168.  
  1169. //-------------------------------------------------------------------------
  1170. // Export
  1171. //-------------------------------------------------------------------------
  1172.  
  1173. static void SetCB(Widget, XtPointer client_data, XtPointer)
  1174. {
  1175.     bool *value = (bool *)client_data;
  1176.     *value = true;
  1177. }
  1178.  
  1179. static void DoExportCB(Widget w, XtPointer client_data, XtPointer call_data)
  1180. {
  1181.     SelectPlotCB(w, client_data, call_data);
  1182.  
  1183.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1184.     string target = get_file(w, client_data, call_data);
  1185.     if (target == "")
  1186.     return;
  1187.  
  1188.     const StringArray& titles  = plot->plotter->data_titles();
  1189.     const StringArray& sources = plot->plotter->data_files();
  1190.  
  1191.     string source = "";
  1192.     string title  = "";
  1193.     for (int i = 0; source == "" && i < sources.size(); i++)
  1194.     {
  1195.     if (sources[i] != "")
  1196.     {
  1197.         source = sources[i];
  1198.         title  = titles[i];
  1199.     }
  1200.     }
  1201.     
  1202.     if (source == "")
  1203.     return;            // This should not happen
  1204.  
  1205.     if (access(target, W_OK) == 0 && is_regular_file(target))
  1206.     {
  1207.     // File exists - request confirmation
  1208.     static Widget confirm_overwrite_dialog = 0;
  1209.     if (confirm_overwrite_dialog != 0)
  1210.         DestroyWhenIdle(confirm_overwrite_dialog);
  1211.  
  1212.     Arg args[10];
  1213.     Cardinal arg = 0;
  1214.     XtSetArg(args[arg], XmNdialogStyle, 
  1215.          XmDIALOG_FULL_APPLICATION_MODAL); arg++;
  1216.     confirm_overwrite_dialog = 
  1217.         verify(XmCreateQuestionDialog(plot->shell,
  1218.                       "confirm_overwrite_dialog", 
  1219.                       args, arg));
  1220.     Delay::register_shell(confirm_overwrite_dialog);
  1221.  
  1222.     bool yes = false;
  1223.     bool no  = false;
  1224.        
  1225.     XtAddCallback(confirm_overwrite_dialog,
  1226.               XmNokCallback, SetCB, XtPointer(&yes));
  1227.     XtAddCallback(confirm_overwrite_dialog,
  1228.               XmNcancelCallback, SetCB, XtPointer(&no));
  1229.     XtAddCallback(confirm_overwrite_dialog, 
  1230.               XmNhelpCallback, ImmediateHelpCB, 0);
  1231.  
  1232.     MString question = rm("Overwrite existing file " 
  1233.                   + quote(target) + "?");
  1234.     XtVaSetValues (confirm_overwrite_dialog, XmNmessageString, 
  1235.                question.xmstring(), NULL);
  1236.     manage_and_raise(confirm_overwrite_dialog);
  1237.  
  1238.     XtAppContext app_context = XtWidgetToApplicationContext(plot->shell);
  1239.     while (!yes && !no)
  1240.         XtAppProcessEvent(app_context, XtIMAll);
  1241.  
  1242.     if (no)
  1243.         return;
  1244.     }
  1245.  
  1246.     StatusDelay delay("Saving " + title + " data to " + quote(target));
  1247.  
  1248.     // Copy SOURCE to TARGET
  1249.     ifstream is(source);
  1250.     ofstream os(target);
  1251.  
  1252.     if (os.bad())
  1253.     {
  1254.     FILE *fp = fopen(target, "w");
  1255.     post_error(string("Cannot open ") 
  1256.            + quote(target) + ": " + strerror(errno), 
  1257.            "export_failed_error", plot->shell);
  1258.     if (fp)
  1259.         fclose(fp);
  1260.     delay.outcome = strerror(errno);
  1261.     return;
  1262.     }
  1263.  
  1264.     int c;
  1265.     while ((c = is.get()) != EOF)
  1266.     os.put((unsigned char) c);
  1267.  
  1268.     XtUnmanageChild(plot->export_dialog);
  1269. }
  1270.  
  1271. static void ExportPlotCB(Widget w, XtPointer client_data, XtPointer call_data)
  1272. {
  1273.     SelectPlotCB(w, client_data, call_data);
  1274.  
  1275.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1276.  
  1277.     if (plot->export_dialog == 0)
  1278.     {
  1279.     Arg args[10];
  1280.     Cardinal arg = 0;
  1281.     Widget dialog = 
  1282.         verify(XmCreateFileSelectionDialog(plot->shell, 
  1283.                            "export_data", args, arg));
  1284.     plot->export_dialog = dialog;
  1285.  
  1286.     Delay::register_shell(dialog);
  1287.     XtAddCallback(dialog, XmNokCallback, DoExportCB, client_data);
  1288.     XtAddCallback(dialog, XmNcancelCallback, UnmanageThisCB, 
  1289.               XtPointer(dialog));
  1290.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, XtPointer(0));
  1291.     }
  1292.  
  1293.     manage_and_raise(plot->export_dialog);
  1294. }
  1295.  
  1296.  
  1297. //-------------------------------------------------------------------------
  1298. // Settings
  1299. //-------------------------------------------------------------------------
  1300.  
  1301. static void ToggleOptionCB(Widget w, XtPointer client_data, 
  1302.                XtPointer call_data)
  1303. {
  1304.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1305.     XmToggleButtonCallbackStruct *cbs = 
  1306.     (XmToggleButtonCallbackStruct *)call_data;
  1307.  
  1308.     string cmd;
  1309.     if (cbs->set)
  1310.     cmd = string("set ") + XtName(w);
  1311.     else
  1312.     cmd = string("set no") + XtName(w);
  1313.  
  1314.     send_and_replot(plot, cmd);
  1315. }
  1316.  
  1317. static void ToggleLogscaleCB(Widget, XtPointer client_data, 
  1318.                  XtPointer call_data)
  1319. {
  1320.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1321.     XmToggleButtonCallbackStruct *cbs = 
  1322.     (XmToggleButtonCallbackStruct *)call_data;
  1323.  
  1324.     string cmd;
  1325.     if (cbs->set)
  1326.     cmd = "set logscale ";
  1327.     else
  1328.     cmd = "set nologscale ";
  1329.  
  1330.     if (plot->plotter->dimensions() >= 3)
  1331.     cmd += "z";
  1332.     else
  1333.     cmd += "y";
  1334.  
  1335.     send_and_replot(plot, cmd);
  1336. }
  1337.  
  1338. static void SetStyleCB(Widget w, XtPointer client_data, XtPointer call_data)
  1339. {
  1340.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1341.     XmToggleButtonCallbackStruct *cbs = 
  1342.     (XmToggleButtonCallbackStruct *)call_data;
  1343.  
  1344.     if (cbs->set)
  1345.     {
  1346.     string style = XtName(w);
  1347.     string cmd;
  1348.     if (style.contains("3d", -1))
  1349.     {
  1350.         cmd = "set hidden3d\n";
  1351.         style = style.before("3d");
  1352.     }
  1353.     else
  1354.     {
  1355.         cmd = "set nohidden3d\n";
  1356.     }
  1357.     if (style.contains("2d", -1))
  1358.         style = style.before("2d");
  1359.     
  1360.     cmd += "set data style " + style;
  1361.  
  1362.     send_and_replot(plot, cmd);
  1363.     }
  1364. }
  1365.  
  1366. static void SetContourCB(Widget w, XtPointer client_data, XtPointer)
  1367. {
  1368.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1369.  
  1370.     Widget base    = XtNameToWidget(XtParent(w), "base");
  1371.     Widget surface = XtNameToWidget(XtParent(w), "surface");
  1372.  
  1373.     assert (base != 0 && surface != 0);
  1374.  
  1375.     bool base_set    = XmToggleButtonGetState(base);
  1376.     bool surface_set = XmToggleButtonGetState(surface);
  1377.  
  1378.     string cmd;
  1379.     if (base_set && surface_set)
  1380.     cmd = "set contour both";
  1381.     else if (base_set && !surface_set)
  1382.     cmd = "set contour base";
  1383.     else if (!base_set && surface_set)
  1384.     cmd = "set contour surface";
  1385.     else
  1386.     cmd = "set nocontour";
  1387.  
  1388.     send_and_replot(plot, cmd);
  1389. }
  1390.  
  1391. static void SetViewCB(Widget, XtPointer client_data, XtPointer)
  1392. {
  1393.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1394.  
  1395.     int rot_x = 60;
  1396.     int rot_z = 30;
  1397.  
  1398.     XtVaGetValues(plot->vsb, XmNvalue, &rot_x, NULL);
  1399.     XtVaGetValues(plot->hsb, XmNvalue, &rot_z, NULL);
  1400.  
  1401.     string cmd = 
  1402.     "set view " + itostring(rot_x) + ", " + itostring(rot_z);
  1403.  
  1404.     send_and_replot(plot, cmd);
  1405. }
  1406.  
  1407. //-------------------------------------------------------------------------
  1408. // Status line
  1409. //-------------------------------------------------------------------------
  1410.  
  1411. // Forward Gnuplot error messages to DDD status line
  1412. static void SetStatusHP(Agent *, void *client_data, void *call_data)
  1413. {
  1414.     PlotWindowInfo *plot = (PlotWindowInfo *)client_data;
  1415.     DataLength* dl = (DataLength *) call_data;
  1416.     string s(dl->data, dl->length);
  1417.  
  1418.     (void) plot;        // Use it
  1419. #if 0
  1420.     if (!plot->active)
  1421.     {
  1422.     // Probably an invocation problem
  1423.     post_gdb_message(s);
  1424.     return;
  1425.     }
  1426. #endif
  1427.  
  1428.     if (plot->command != 0)
  1429.     {
  1430.     string msg = s;
  1431.     strip_space(msg);
  1432.     MString xmsg = tb(msg);
  1433.     XmCommandError(plot->command, xmsg.xmstring());
  1434.     }
  1435.  
  1436.     while (s != "")
  1437.     {
  1438.     string line;
  1439.     if (s.contains('\n'))
  1440.         line = s.before('\n');
  1441.     else
  1442.         line = s;
  1443.     s = s.after('\n');
  1444.     strip_space(line);
  1445.  
  1446.     if (line != "")
  1447.         set_status(line);
  1448.     }
  1449. }
  1450.  
  1451. //-------------------------------------------------------------------------
  1452. // Trace communication
  1453. //-------------------------------------------------------------------------
  1454.  
  1455. static void trace(char *prefix, void *call_data)
  1456. {
  1457.     DataLength* dl = (DataLength *) call_data;
  1458.     string s(dl->data, dl->length);
  1459.  
  1460.     bool s_ends_with_nl = false;
  1461.     if (s.length() > 0 && s[s.length() - 1] == '\n')
  1462.     {
  1463.     s_ends_with_nl = true;
  1464.     s = s.before(int(s.length() - 1));
  1465.     }
  1466.  
  1467.     s = quote(s);
  1468.     string nl = string("\\n\"\n") + replicate(' ', strlen(prefix)) + "\"";
  1469.     s.gsub("\\n", nl);
  1470.  
  1471.     if (s_ends_with_nl)
  1472.     s(s.length() - 1, 0) = "\\n";
  1473.  
  1474.     dddlog << prefix << s << '\n';
  1475.     dddlog.flush();
  1476. }
  1477.  
  1478. static void TraceInputHP(Agent *, void *, void *call_data)
  1479. {
  1480.     trace("<< ", call_data);
  1481. }
  1482.  
  1483. static void TraceOutputHP(Agent *, void *, void *call_data)
  1484. {
  1485.     trace(">> ", call_data);
  1486. }
  1487.  
  1488. static void TraceErrorHP(Agent *, void *, void *call_data)
  1489. {
  1490.     trace("<= ", call_data);
  1491. }
  1492.