Logo Search packages:      
Sourcecode: ddd version File versions  Download package

settings.C

// $Id$ -*- C++ -*-
// Modify debugger settings

// Copyright (C) 1996-1998 Technische Universitaet Braunschweig, Germany.
// Copyright (C) 2000-2001 Universitaet Passau, Germany.
// Written by Andreas Zeller <zeller@gnu.org>.
// 
// 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.gnu.org/software/ddd/',
// or send a mail to the DDD developers <ddd@gnu.org>.

char settings_rcsid[] = 
    "$Id$";

#include "settings.h"

#include <Xm/Xm.h>
#include <Xm/SelectioB.h>
#include <Xm/DialogS.h>
#include <Xm/ScrolledW.h>
#include <Xm/RowColumn.h>
#include <Xm/ToggleB.h>
#include <Xm/PanedW.h>
#include <Xm/PushB.h>
#include <Xm/TextF.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/LabelG.h>
#include <Xm/MwmUtil.h>
#include <Xm/Separator.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>

#include "AppData.h"
#include "Assoc.h"
#include "ComboBox.h"
#include "Command.h"
#include "DataDisp.h"
#include "Delay.h"
#include "DestroyCB.h"
#include "DispBox.h"
#include "EntryType.h"
#include "GDBAgent.h"
#include "HelpCB.h"
#include "LessTifH.h"
#include "MakeMenu.h"
#include "SmartC.h"
#include "SourceView.h"
#include "StringSA.h"
#include "UndoBuffer.h"
#include "ThemeP.h"
#include "VarArray.h"
#include "WidgetSA.h"
#include "basename.h"
#include "buttons.h"
#include "cook.h"
#include "comm-manag.h"
#include "ddd.h"
#include "editing.h"
#include "glob.h"
#include "mydialogs.h"
#include "logo.h"
#include "options.h"
#include "post.h"
#include "question.h"
#include "regexps.h"
#include "session.h"
#include "shell.h"
#include "status.h"
#include "string-fun.h"
#include "verify.h"
#include "version.h"
#include "vsldoc.h"
#include "wm.h"
#include "charsets.h"

#if !HAVE_PCLOSE_DECL
extern "C" int pclose(FILE *stream);
#endif

//-----------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------

const Dimension EXTRA_SPACE     = 10;   // Minimum space between label / entry
const Dimension EXTRA_WIDTH     =  6;   // Additional space for safety
const Dimension MAX_HEIGHT     = 300;   // Maximum height of window

enum SettingsType { SETTINGS, INFOS, SIGNALS, THEMES };


//-----------------------------------------------------------------------
// Data
//-----------------------------------------------------------------------

static Widget            settings_panel = 0;
static Widget            settings_form  = 0;
static Widget            reset_settings_button = 0;
static Widget            apply_settings_button = 0;
static WidgetArray       settings_entries;
static EntryTypeArray    settings_entry_types;
static WidgetStringAssoc settings_values;
static WidgetStringAssoc settings_initial_values;
static bool              need_reload_settings = false;

static Widget            signals_panel = 0;
static Widget            signals_form  = 0;
static Widget            reset_signals_button = 0;
static WidgetArray       signals_entries;
static WidgetStringAssoc signals_values;
static WidgetStringAssoc signals_initial_values;
static bool              need_reload_signals = false;

static Widget            themes_panel = 0;
static Widget            reset_themes_button = 0;
static Widget            apply_themes_button = 0;
static WidgetArray       themes_entries;
static WidgetArray       themes_labels;

static Widget            infos_panel        = 0;
static Widget            reset_infos_button = 0;
static WidgetArray       infos_entries;



//-----------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------

static void get_setting(std::ostream& os, DebuggerType type,
                  const string& base, string value,
                  unsigned long flags = SAVE_DEFAULT | SAVE_SESSION);
static void set_arg();


// Find widget for command COMMAND
static Widget command_to_widget(Widget ref, string command)
{
    Widget found = 0;
    while (!command.empty() && (found = XtNameToWidget(ref, command.chars())) == 0)
    {
      // Strip last word (command argument)
      int index = command.index(rxwhite, -1);
      command   = command.before(index);
    }

    return found;
}

// Issue `set' command
static void gdb_set_command(const string& set_command, string value)
{
    if (gdb->type() == GDB && value == "unlimited")
      value = "0";

    if (gdb->type() == GDB && set_command == "dir" && !value.empty())
    {
      // `dir' in GDB works somewhat special: it prepends its
      // argument to the source path instead of simply setting it.
      // Hence, first reset `dir' to some initial value.

      string confirm_value = "on";
      Widget confirm_w = command_to_widget(settings_form, "set confirm");
      if (confirm_w)
          confirm_value = settings_values[confirm_w];

      if (confirm_value == "on")
          gdb_command("set confirm off");
      gdb_command(set_command);
      if (confirm_value == "on")
          gdb_command("set confirm on");
    }

    if (set_command.contains("o ", 0))
    {
      if (value.contains('\'', 0) && value.contains('\'', -1))
          value = unquote(value);
// removed for perl-5.8.x
//    if (value == "N/A")
//        value = "";

      gdb_command(set_command + "=" + value); // Perl
    }
    else if (set_command.contains("set $", 0) && !set_command.contains(" = "))
    {
      gdb_command(set_command + " = " + value); // DBX
    }
    else if (!value.empty())
    {
      gdb_command(set_command + " " + value); // GDB
    }
    else
    {
      gdb_command(set_command);
    }
}

// OptionMenu reply
static void SetOptionCB(Widget w, XtPointer client_data, XtPointer)
{
    gdb_set_command((const _XtString)client_data, XtName(w));
}

// ToggleButton reply
static void SetOnOffCB(Widget, XtPointer client_data, XtPointer call_data)
{
    XmToggleButtonCallbackStruct *cbs = 
      (XmToggleButtonCallbackStruct *)call_data;

    gdb_set_command((const _XtString)client_data, cbs->set ? "on":"off");
}

// ToggleButton reply
static void SetTrueFalseCB(Widget, XtPointer client_data, XtPointer call_data)
{
    XmToggleButtonCallbackStruct *cbs = 
      (XmToggleButtonCallbackStruct *)call_data;

    gdb_set_command((const _XtString)client_data, cbs->set ? "true":"false");
}

// ToggleButton reply
static void SetSensitiveCB(Widget, XtPointer client_data, XtPointer call_data)
{
    XmToggleButtonCallbackStruct *cbs = 
      (XmToggleButtonCallbackStruct *)call_data;

    gdb_set_command((const _XtString)client_data,
                cbs->set ? "sensitive":"insensitive");
}

// ToggleButton reply
static void SetNumCB(Widget, XtPointer client_data, XtPointer call_data)
{
    XmToggleButtonCallbackStruct *cbs = 
      (XmToggleButtonCallbackStruct *)call_data;

    gdb_set_command((const _XtString)client_data, cbs->set ? "1":"0");
}

// ToggleButton reply
static void SetNoNumCB(Widget, XtPointer client_data, XtPointer call_data)
{
    XmToggleButtonCallbackStruct *cbs = 
      (XmToggleButtonCallbackStruct *)call_data;

    gdb_set_command((const _XtString)client_data, cbs->set ? "0":"1");
}

// ToggleButton reply
static void SetDisplayCB(Widget, XtPointer client_data, XtPointer call_data)
{
    XmToggleButtonCallbackStruct *cbs = 
      (XmToggleButtonCallbackStruct *)call_data;

    if (cbs->set)
      data_disp->new_user_display((const _XtString)client_data);
    else
      data_disp->delete_user_display((const _XtString)client_data);
}

// ToggleButton reply
static void SendSignalCB(Widget, XtPointer client_data, XtPointer)
{
    gdb_command(string("signal ") + XtName(Widget(client_data)));
}


static string handle_command(Widget w, bool set)
{
    string sig    = string(XtName(w)).before('-');
    string action = string(XtName(w)).after('-');
    if (!set)
      action.prepend("no");

    if (action == "nopass")
      action = "ignore";
    else if (action == "noignore")
      action = "pass";

    return "handle " + sig + " " + action;
}

// ToggleButton reply
static void SignalCB(Widget w, XtPointer, XtPointer call_data)
{
    XmToggleButtonCallbackStruct *cbs = 
      (XmToggleButtonCallbackStruct *)call_data;

    gdb_command(handle_command(w, cbs->set));
}

// Get help on signal - using `info' on `libc'
static void HelpOnSignalCB(Widget w, XtPointer client_data, 
                     XtPointer call_data)
{
    Widget label = Widget(client_data);
    string sig = XtName(label);

    string s = "";

    if (sig == "all")
    {
      HelpOnThisCB(w, XtPointer(label), call_data);
      return;
    }

    StatusDelay delay("Retrieving Signal Documentation");

    // libc info has one common node for SIGUSR1 and SIGUSR2
    string sigindex = sig;
    if (sigindex == "SIGUSR1")
      sigindex = "SIGUSR2";

    const string cmd = "info -f libc -n 'Variable Index' " + sigindex + " -o -";

    const string s1 = sh_command(cmd); 
    FILE *fp = popen(s1.chars(), "r");
    if (fp != 0)
    {
      std::ostringstream info;
      int c;
      while ((c = getc(fp)) != EOF)
          info << char(c);
      pclose(fp);
      
      s = info;
      int start = s.index(" - Macro: int " + sig);
      start = s.index('\n', start);
      s = s.after(start);
      int end = s.index("\n - ");
      if (end >= 0)
          s = s.before(end);

      s.gsub("ee\n*Note\n", "ee\n`");
      s.gsub("ee *Note\n",  "ee\n`");
      s.gsub("ee\n*Note ",  "ee\n`");
      s.gsub("ee *Note ",   "ee `");
      s.gsub("*Note ",      "See `");
      s.gsub("*Note\n",     "See\n`");
      s.gsub("::", "' in libc info");
      s.gsub("`C-", "`Ctrl+");

      strip_space(s);
      while (s.contains("  "))
          s.gsub("  ", " ");
      s.gsub("\n ", "\n");
    }

    if (s.empty())
      s = "No help available on this signal.";

    XmString xmdoc = 0;
    XtVaGetValues(label, XmNlabelString, &xmdoc, XtPointer(0));
    MString doc(xmdoc, true);
    XmStringFree(xmdoc);

    MString text = bf(sig + ": ") + doc;
    text += cr() + cr() + rm(s);

    MStringHelpCB(w, XtPointer(text.xmstring()), call_data);
}


// Update state of `reset' button
static void update_reset_settings_button()
{
    if (reset_settings_button == 0)
      return;

    check_options_file();

    for (int i = 0; i < settings_entries.size(); i++)
    {
      Widget entry = settings_entries[i];

      string value = settings_values[entry];
      if (settings_entry_types[i] == TextFieldEntry)
      {
          String value_s = XmTextFieldGetString(entry);
          value = value_s;
          XtFree(value_s);

          if (value != settings_values[entry])
          {
            set_sensitive(reset_settings_button, true);
            return;
          }
      }

      if (value != settings_initial_values[entry])
      {
          set_sensitive(reset_settings_button, true);
          return;
      }
    }

    set_sensitive(reset_settings_button, false);
}

static void update_apply_settings_button()
{
    if (apply_settings_button == 0)
      return;

    for (int i = 0; i < settings_entries.size(); i++)
    {
      if (settings_entry_types[i] != TextFieldEntry)
          continue;

      Widget entry = settings_entries[i];

      String value_s = XmTextFieldGetString(entry);
      string value(value_s);
      XtFree(value_s);

      if (value != settings_values[entry])
      {
          set_sensitive(apply_settings_button, true);
          return;
      }
    }

    set_sensitive(apply_settings_button, false);
}

static void update_reset_signals_button()
{
    if (reset_signals_button == 0)
      return;

    check_options_file();

    for (int i = 0; i < signals_entries.size(); i++)
    {
      Widget entry = signals_entries[i];
      if (signals_initial_values[entry] != signals_values[entry])
      {
          set_sensitive(reset_signals_button, true);
          return;
      }
    }

    set_sensitive(reset_signals_button, false);
}

static void update_themes_buttons()
{
    if (apply_themes_button == 0 || reset_themes_button == 0)
      return;

    bool apply_is_sensitive = false;
    bool reset_is_sensitive = false;

    int i;

    ThemeManager old_tm(app_data.themes);

    for (i = 0; i < themes_entries.size(); i++)
    {
      Widget entry = themes_entries[i];
      string theme = basename(XtName(entry));
      ThemePattern p;

      if (DispBox::theme_manager.has_pattern(theme))
          p = DispBox::theme_manager.pattern(theme);

      std::ostringstream os;
      os << p;
      string current_value = string(os);

      String value_s = XmTextFieldGetString(entry);
      string value(value_s);
      XtFree(value_s);

      string old_value;

      bool have_pattern = old_tm.has_pattern(theme);
      if (have_pattern)
      {
          ThemePattern p = old_tm.pattern(theme);
          std::ostringstream os;
          os << p;
          old_value = string(os);
      }

      if (value != current_value)
          apply_is_sensitive = True;

      if (value != old_value)
          reset_is_sensitive = True;

      if (apply_is_sensitive && reset_is_sensitive)
          break;
    }

    if (!reset_is_sensitive)
    {
      for (i = 0; i < themes_labels.size(); i++)
      {
          Widget button = themes_labels[i];
          string theme = basename(XtName(button));
          bool old_set = old_tm.has_pattern(theme);

          if (old_set)
          {
            ThemePattern p = old_tm.pattern(theme);
            if (!p.active())
                old_set = false;
          }

          if (XmToggleButtonGetState(button) != old_set)
          {
            reset_is_sensitive = True;
            break;
          }
      }
    }

    set_sensitive(apply_themes_button, apply_is_sensitive);
    set_sensitive(reset_themes_button, reset_is_sensitive);
}



// Update states of `info' buttons
void update_infos()
{
    bool have_info = false;

    for (int i = 0; i < infos_entries.size(); i++)
    {
      Widget button = infos_entries[i];
      bool set = data_disp->have_user_display(XtName(button));
      have_info = have_info || set;
      XtVaSetValues(button, XmNset, set, XtPointer(0));
    }

    if (reset_infos_button != 0)
      set_sensitive(reset_infos_button, have_info);
}

static void UpdateSettingsButtonsNowCB(XtPointer, XtIntervalId *)
{
    update_apply_settings_button();
    update_reset_settings_button();
}

// TextField reply
static void UpdateSettingsButtonsCB(Widget w, XtPointer client_data, 
                            XtPointer)
{
    // The TextField value has not yet changed.  Call again later.
    XtAppAddTimeOut(XtWidgetToApplicationContext(w), 0,
                UpdateSettingsButtonsNowCB, client_data);
}


static void UpdateThemesButtonsNowCB(XtPointer, XtIntervalId *)
{
    update_themes_buttons();
}

// TextField reply
static void UpdateThemesButtonsCB(Widget w, XtPointer client_data, 
                          XtPointer)
{
    // The TextField value has not yet changed.  Call again later.
    XtAppAddTimeOut(XtWidgetToApplicationContext(w), 0,
                UpdateThemesButtonsNowCB, client_data);
}

static void HelpOnThemeCB(Widget w, XtPointer client_data, 
                    XtPointer call_data)
{
    // Fetch text from file
    string file = XtName((Widget)client_data);
    string text = vsldoc(file, DispBox::vsllib_path);
    if (text.empty())
      text = "No help available on this theme.";

    MString mtext = bf(basename(file.chars())) + cr() + cr() + rm(text);
    MStringHelpCB(w, XtPointer(mtext.xmstring()), call_data);
}

static void ApplyThemesCB(Widget, XtPointer, XtPointer)
{
    Delay d;

    ThemeManager t;

    for (int i = 0; i < themes_entries.size(); i++)
    {
      Widget button = themes_labels[i];
      bool active = XmToggleButtonGetState(button);

      Widget entry  = themes_entries[i];
      String value_s = XmTextFieldGetString(entry);
      string value = value_s;
      XtFree(value_s);

      t.add(basename(XtName(entry)), ThemePattern(value, active));
    }

    data_disp->set_theme_manager(t);
    update_themes_buttons();
}

static void ResetThemesCB(Widget w, XtPointer client_data, 
                    XtPointer call_data)
{
    CommandGroup cg;

    data_disp->set_theme_manager(ThemeManager(app_data.themes));
    update_themes();
    ApplyThemesCB(w, client_data, call_data);
}


// Register additional info button
void register_info_button(Widget w)
{
    infos_entries += w;
}

// Save `settings' state
void save_settings_state()
{
    if (settings_form == 0)
      return;

    for (int i = 0; i < settings_entries.size(); i++)
    {
      Widget entry = settings_entries[i];
      settings_initial_values[entry] = settings_values[entry];
    }

    update_reset_settings_button();
    set_need_save_defines(false);
}

// Save `signals' state
void save_signals_state()
{
    if (signals_form == 0)
      return;

    for (int i = 0; i < signals_entries.size(); i++)
    {
      Widget entry = signals_entries[i];
      signals_initial_values[entry] = signals_values[entry];
    }

    update_reset_signals_button();
}

// Process output of `show' command
void process_show(const string& command, string value, bool init)
{
    if (settings_form == 0)
      return;

    if (gdb->type() == DBX && value.freq('\n') > 1)
    {
      // In DBX, we always get all values at once.  Find the
      // appropriate line.
      string base = command.after(rxwhite);
      if (base.contains(rxwhite))
          base = base.before(rxwhite);

      int column = 0;
      int start_value = -1;
      for (int i = 0; i < int(value.length()); i++)
      {
          if (column == 0
            && value.contains(base, i) 
            && value.contains(rxwhite, i + base.length()))
          {
            start_value = i;
            break;
          }

          if (value[i] == '\n')
            column = 0;
          else
            column++;
      }

      if (start_value < 0)
      {
#if 0
          std::cerr << "Warning: cannot set " << quote(base)
             << " to " << quote(value) << "\n";
#endif
          return;
      }

      value = value.from(start_value);
      value = value.after(base);
      if (value.contains('\n'))
          value = value.before('\n');
      strip_space(value);
    }

    if (value.contains('\n'))
      value = value.before('\n');

    if (!init)
      set_status(value);

    if (gdb->type() == BASH || gdb->type() == DBG || gdb->type() == GDB)
    {
      if (value.contains(" is "))
          value = value.after(" is ", -1);
        if (value.contains(" is: "))
            value = value.after(" is: ", -1);
      if (value.contains(".", -1))
          value = value.before(int(value.length()) - 1);
      if (value.contains(". "))
          value = value.before(". ");

      if (value.contains("assumed to be "))
          value = value.after("assumed to be ");

      if (value.contains("\"auto;", 0) || 
          value.contains("set automatically", 0) ||
          value.contains("auto-detected", 0) ||
            value.contains("auto -", 0) ||
            value.contains("\"auto\"", 0))
      {
          value = "auto";
      }

      if (value.contains('"', 0))
          value = unquote(value);
      else if (value.contains(": "))
          value = value.after(": ");
    }

    if (gdb->type() == PERL)
    {
      if (value.contains('\''))
      {
          value = value.after('\'');
          value = value.before('\'');
      }
    }

    static string empty;
    value.gsub(gdb_out_ignore, empty);

    string set_command = command;
    if (set_command.contains("o ", 0))
    {
      // Perl
      if (set_command.contains('?'))
          set_command = set_command.before('?');
      else if (set_command.contains('='))
          set_command = set_command.before('=');
    }
    else if (!set_command.contains("set ", 0))
    {
      // GDB
      set_command = "set " + set_command.after(rxwhite);
    }

    Widget button = command_to_widget(settings_form, set_command);
    if (button == 0)
      button = command_to_widget(settings_form, command);
    if (button == 0)
      button = command_to_widget(settings_form, command.after(rxwhite));

    if (button != 0)
    {
      if (!init)
      {
          // Save current state in undo buffer
          std::ostringstream command;
          get_setting(command, gdb->type(), XtName(button), 
                  settings_values[button]);
          undo_buffer.add_command(string(command));
      }

      settings_values[button] = value;
      if (init)
          settings_initial_values[button] = value;
    }

    if (!init)
      update_reset_settings_button();

    if (button != 0 && XmIsRowColumn(button))
    {
      Widget menu = 0;
      XtVaGetValues(button, XmNsubMenuId, &menu, XtPointer(0));
      if (menu != 0)
      {
          // Option menu
          Widget active = XtNameToWidget(menu, value.chars());
          if (active != 0)
          {
            XtVaSetValues(button, XmNmenuHistory, active, XtPointer(0));
            return;
          }
      }
    }
    else if (button != 0 && XmIsToggleButton(button))
    {
      bool set = value != "off" && value != "0" && value != "unlimited" 
          && value != "false" && value != "insensitive";

      for (int i = 0; i < settings_entries.size(); i++)
      {
          if (settings_entries[i] == button
            && settings_entry_types[i] == NoNumToggleButtonEntry)
          {
            set = !set;
            break;
          }
      }

      XtVaSetValues(button, XmNset, set, XtPointer(0));
      return;
    }
    else if (button != 0 && XmIsTextField(button))
    {
      XtVaSetValues(button, 
                  XmNvalue,                 value.chars(),
                  XmNcursorPosition,        0,
                  XmNcursorPositionVisible, True,
                  XtPointer(0));
      return;
    }

#if 0
    std::cerr << "Warning: cannot set " << quote(set_command)
       << " to " << quote(value) << "\n";
#endif
}

// Process output of `handle' command
void process_handle(string output, bool init)
{
    if (signals_form == 0)
      return;

    string undo_command = "";

    while (!output.empty())
    {
      string line = output;
      if (line.contains('\n'))
          line = line.before('\n');
      output = output.after('\n');

      string base = line.before(rxwhite);
      if (!base.contains("SIG", 0))
          continue;

      line = line.after(rxwhite);
      static const _XtString titles[3] = { "stop", "print", "pass" };

      for (int word = 0; word <= 2; word++)
      {
          string value = line.before(rxwhite);
          line = line.after(rxwhite);

          value.downcase();
          bool set = (value == "yes");
          string name = base + "-" + titles[word];
          Widget w = XtNameToWidget(signals_form, name.chars());

          if (w == 0)
          {
#if 0
            std::cerr << "Warning: cannot set " << base << " " << titles[word]
                 << " to " << quote(value) << "\n";
#endif
            continue;
          }

          if (!init && signals_values[w] != value)
          {
            // Save undoing command
            if (!undo_command.empty())
                undo_command += '\n';
            undo_command += handle_command(w, !set);
          }

          XtVaSetValues(w, XmNset, set, XtPointer(0));

          signals_values[w] = value;
          if (init)
            signals_initial_values[w] = value;
      }
    }

    if (!undo_command.empty())
      undo_buffer.add_command(undo_command);

    update_reset_signals_button();
}



// Create settings form

// Return TRUE if doc begins with a boolean verb
static bool is_verb(const string& doc)
{
    if (doc.contains("whether to"))
      return true;
    int ing = doc.index("ing");
    if (ing < 0)
      return false;

    int first_space  = doc.index(' ');
    int second_space = doc.index(' ', first_space + 1);
    int third_space  = doc.index(' ', second_space + 1);

    if (ing < third_space || third_space < 0)
      return true;

    return false;
}

// Determine entry type
static EntryType entry_type(DebuggerType type,
                      const string& base, 
                      const string& doc,
                      const string& value)
{
#if RUNTIME_REGEX
    static regex rxnonzero1("no[nt][ -]?(0|zero|null)");
    static regex rxnonzero2("!= *(0|zero|null)");
#endif

    switch (type)
    {
    case GDB:
      if (base.contains("check", 0))
          return CheckOptionMenuEntry;
      if (base.contains("follow-fork-mode", 0))
          return ForkOptionMenuEntry;
      if (base.contains("endian", 0) ||
          base.contains("architecture", 0))
          return TargetOptionMenuEntry;
      if (base.contains("disassembly-flavor", 0))
          return DisassemblyOptionMenuEntry;
      if (base.contains("scheduler-locking", 0))
          return SchedulerOptionMenuEntry;
      if (base.contains("language", 0) || 
          base.contains("demangle", 0)
#if GDB_ABI_OPTIONS
            || base.contains("cp-abi", 0)
            || base.contains("osabi", 0)
#endif
          )
          return OtherOptionMenuEntry;
      if (value.contains("on.\n", -1) || value.contains("off.\n", -1))
          return OnOffToggleButtonEntry;
      if ((value.contains("0.\n", -1) || value.contains("1.\n", -1))
          && is_verb(doc))
          return NumToggleButtonEntry;
      break;

    case BASH:
      if (value.contains("on.\n", -1) || value.contains("off.\n", -1))
          return OnOffToggleButtonEntry;
      if ((value.contains("0.\n", -1) || value.contains("1.\n", -1))
          && is_verb(doc))
          return NumToggleButtonEntry;
      break;

    case DBX:
      if ((value == "0" || value == "1") &&
          !doc.contains("number") && !doc.contains(" > 1"))
      {
          if (doc.contains(" 1") && !doc.contains(" 2"))
            return NumToggleButtonEntry;
          if (doc.contains(rxnonzero1) || doc.contains(rxnonzero2))
            return NumToggleButtonEntry;
      }
      if (value.contains("on", 0) || value.contains("off", 0))
          return OnOffToggleButtonEntry;
      if (value.contains("true", 0) || value.contains("false", 0))
          return TrueFalseToggleButtonEntry;
      if (value.contains("sensitive", 0) 
          || value.contains("insensitive", 0))
          return SensitiveToggleButtonEntry;
      if (has_nr(value))
          return TextFieldEntry;
      if (base.contains("version")
          || base.contains("run_io")
          || base.contains("follow_fork_mode"))
          return OtherOptionMenuEntry;
      break;

    case DBG:
      if (base.contains("mode", 0))
          return OnOffToggleButtonEntry;
      break;

    case XDB:
    case JDB:
    case PYDB:
    case PERL:
      break;                  // FIXME
    }

    // When all else fails, display a text
    return TextFieldEntry;
}


// The GDB question cache
static StringStringAssoc gdb_question_cache;

static string cached_gdb_question(const string& question, int timeout = 0)
{
    string& answer = gdb_question_cache[question];
    if (answer.empty())
      answer = gdb_question(question, timeout);
    return answer;
}

static void clear_gdb_question_cache()
{
    static StringStringAssoc empty;
    gdb_question_cache = empty;
}

static void strip_leading(string& doc, const char *key)
{
    if (doc.contains(key, 0))
      doc = doc.after(key);
    strip_leading_space(doc);
}

static void strip_from(string& doc, const char *key)
{
    if (doc.contains(key))
      doc = doc.before(key);
    strip_leading_space(doc);
}

static void munch_doc(string& doc)
{
    strip_leading_space(doc);

    // Sun DBX 3.0
    strip_leading(doc, "#");
    strip_leading(doc, "If on,");
    strip_leading(doc, "If true,");
    strip_leading(doc, "When on,");
    strip_leading(doc, "When `on',");
    strip_leading(doc, "Set ");
    strip_leading(doc, "Sets ");
    strip_leading(doc, "Governs ");
    strip_leading(doc, "Limit ");
    strip_leading(doc, "Enable/disable ");
    strip_leading(doc, "Allow/disallow ");
    strip_leading(doc, "whether the ");
    strip_leading(doc, "debugger will ");
    strip_leading(doc, "Automatically ");
    strip_leading(doc, "Name of ");

    // SGI DBX
    strip_leading(doc, "-");

    if (doc.contains("0 =>"))
      doc = "Don't" + doc.after("0 =>");

    // DEC and SGI DBX
    strip_leading(doc, "$");
    strip_leading(doc, "if");
    strip_leading(doc, "If");
    strip_leading(doc, "when");
    strip_leading(doc, "When");
    strip_leading(doc, "this ");
    strip_leading(doc, "is ");
    strip_leading(doc, "==");
    strip_leading(doc, "!=");
    strip_leading(doc, "0");
    strip_leading(doc, "1");
    strip_leading(doc, "set");
    strip_leading(doc, "zero");
    strip_leading(doc, "non-zero");
    strip_leading(doc, "non-0");
    strip_leading(doc, "(default)");
    strip_leading(doc, "(the default)");
    strip_leading(doc, ",");
    strip_leading(doc, "=>");
    strip_leading(doc, "implies ");
    strip_leading(doc, "then ");
    strip_leading(doc, "contains ");
    strip_leading(doc, "the ");
    strip_leading(doc, "name of ");
    strip_leading(doc, "specify ");
    strip_leading(doc, "which ");
    strip_leading(doc, "after ");
    strip_leading(doc, "String with ");

    // GDB
    strip_leading(doc, "whether to ");
    strip_leading(doc, "the ");

    // More DEC DBX
    strip_from(doc, "we're looking at");
    strip_from(doc, "we're debugging");
    strip_from(doc, "Default:");
    strip_from(doc, ", default");
    strip_from(doc, " to $DBX");
    strip_from(doc, " entirely");
    strip_from(doc, " rather than");
    strip_from(doc, ", else");
    strip_from(doc, " next to");
    strip_from(doc, " used to");
    strip_from(doc, " that was");
    strip_from(doc, " accessing");

    // More SGI DBX
    strip_from(doc, " if none");
    strip_from(doc, " (init");
    strip_from(doc, " (default");
    strip_from(doc, " ($");
    strip_from(doc, " ( $");
    strip_from(doc, " (with");
    strip_from(doc, " However ");
    strip_from(doc, " This ");
    strip_from(doc, " Defaults ");
    strip_from(doc, " $");

    doc.gsub(" " + downcase(gdb->title()), " " + gdb->title());
    if (doc.contains(downcase(gdb->title()), 0))
      doc = gdb->title() + doc.after(downcase(gdb->title()));

    if (doc.length() > 0)
      doc[0] = toupper(doc[0]);

    if (gdb->type() == GDB)
      doc.gsub('_', ' ');

    if (doc.length() > 60)
      doc = doc.before(57) + "...";
}


// Get DBX documentation
static string _get_dbx_help(const string& dbxenv, const string& base)
{
    string dbx_help;
    if (dbxenv == "dbxenv")
      dbx_help = cached_gdb_question("help dbxenv", -1);
    if (dbx_help.freq('\n') <= 2)
      dbx_help = cached_gdb_question("help $variable", -1);
    if (dbx_help.freq('\n') <= 2)
      dbx_help = cached_gdb_question("help variable", -1);
    if (dbx_help.freq('\n') <= 2)
      dbx_help = cached_gdb_question("help $variables", -1);

    // Find documentation in DBX_HELP
    int column = 0;
    int start_help = -1;
    int i;
    for (i = 0; i < int(dbx_help.length()); i++)
    {
      if (column == 0 
          && (dbx_help.contains(base, i) 
            || dbx_help.contains(dbxenv + " " + base, i)))
      {
          start_help = i;
          break;
      }

      if (dbx_help[i] == '\n')
          column = 0;
      else
          column++;
    }

    if (start_help < 0)
    {
      dbx_help = base;
      dbx_help.gsub('_', ' ');
      return dbx_help;        // Simple default
    }

    int end_help = -1;
    for (i++, column++; i < int(dbx_help.length()); i++)
    {
      if (column == 0 && !isspace(dbx_help[i]))
      {
          end_help = i;
          break;
      }

      if (dbx_help[i] == '\n')
          column = 0;
      else
          column++;
    }

    if (end_help >= 0)
      dbx_help = dbx_help.at(start_help, end_help - start_help);
    else
      dbx_help = dbx_help.from(start_help);

    return dbx_help;
}

static void compress_spaces(string& s, int width)
{
    char *buffer = new char[s.length() + 1];
    char *t = buffer;

    int column = 0;
    for (int i = 0; i < int(s.length()); i++)
    {
      if (isspace(s[i]))
      {
          while (i < int(s.length()) && isspace(s[i]))
            i++;

          if (width > 0 && column >= width)
          {
            *t++ = '\n';
            column = 0;
          }
          else
          {
            *t++ = ' ';
            column++;
          }
      }

      if (i < int(s.length()))
      {
          *t++ = s[i];
          column++;
      }
    }
    *t++ = '\0';

    s = buffer;
    delete[] buffer;
}

// Same, but for usage in help messages
static string get_dbx_help(const string& dbxenv, const string& base, int width)
{
    string dbx_help = _get_dbx_help(dbxenv, base);
    dbx_help = dbx_help.after(base);
    strip_leading_space(dbx_help);

    if (dbx_help.contains("  - "))
    {
      dbx_help = dbx_help.after("  - ");
      strip_leading_space(dbx_help);
    }
    else if (dbx_help.contains(" # "))
    {
      dbx_help = dbx_help.after(" # ");
      strip_leading_space(dbx_help);
    }

    // Remove remaining `# ' prefixes (Solaris DBX)
    dbx_help.gsub("  # ", "");
    dbx_help.gsub(" # ", " number ");

    // Reduce spaces (Solaris DBX & DEC DBX)
    compress_spaces(dbx_help, width);

    return dbx_help;
}

string get_dbx_help(const string& dbxenv, const string& base)
{
    return get_dbx_help(dbxenv, base, 60);
}

01301 struct DBXTranslation {
    const _XtString base;
    const _XtString doc;
};

static DBXTranslation const dbx_translations[] = 
{
    {"$addrfmt64", "C format for address printing (64bit)"},
    {"$assignverify", "Verify assignments"},
    {"$byteaccess", "Access memory items less than 4 bytes"},
    {"$ctypenames", "Support C type names"},
    {"$dispix", "Display pixie instructions"},
    {"$fp_precise", "Floating point precise mode"},
    {"$framereg", "Registers are with respect to the current frame"},
    {"$groupforktoo", "Group fork too"},
    {"$hexdoubles", "Display float values as float and hex"},
    {"$newevent", "Last new event number"},
    {"$newpgrpevent", "Last new pgrp event number"},
    {"$newrecord", "Last record number"},
    {"$nonstop", "Stop the debugged process"},
    {"$pager", "Pager"},
    {"$pendingtraps", "Pending traps"},
    {"$piaddtohist", "Add playback commands to history"},
    {"$print_exception_frame", "Print exception frames"},
    {"$shellparameters", "Shell parameters"},
    {"$showbreakaddrs", "Show the address of placed breakpoints"},
    {"$showfilename", "Show file name when stopping"},
    {"$sourcepathrule", "Rule for accessing source paths"},
    {"$stacktracelimit", "Stack trace limit"},
    {"$stdc", "Support Standard C"},
    {"$stepintoall", "Step into all procedures"},
    {"$stopformat", "Stop format"},
    {"$whereisdsolimit", "Whereis limit"},
    {"disassembler_version", "Disassembler version"},
    {"follow_fork_inherit", "When following child, inherit events"},
    {"follow_fork_mode", "When process forks"},
    {"output_list_size", "Lines to print in the `list' command"},
    {"rtc_biu_at_exit", "Produce memory report at exit"},
    {"run_autostart", "Let `step' and `next' implicitly start the program"},
    {"run_io", "Redirect I/O to"},
    {"suppress_startup_message", "Suppress startup message below release"}
};

static string get_dbx_doc(const string& dbxenv, const string& base)
{
    // Some specials
    for (int i = 0; i < int(XtNumber(dbx_translations)); i++)
    {
      if (base == dbx_translations[i].base)
          return dbx_translations[i].doc;
    }
    
    // Generic help
    string dbx_doc = get_dbx_help(dbxenv, base, -1);
    strip_leading_space(dbx_doc);

    dbx_doc.gsub("etc. ", "etc ");
    if (dbx_doc.contains(". "))
      dbx_doc = dbx_doc.before(". ");
    if (dbx_doc.contains("; "))
      dbx_doc = dbx_doc.before("; ");
    dbx_doc.gsub("etc ", "etc. ");

#if 0
    if (dbx_doc.empty())
    {
      dbx_doc = base;
      dbx_doc.gsub('_', ' ');
    }
#endif

    munch_doc(dbx_doc);

    return dbx_doc;
}

static Dimension preferred_width(Widget w)
{
    if (w == 0)
      return 0;

    XtWidgetGeometry size;
    size.request_mode = CWWidth;
    XtQueryGeometry(w, (XtWidgetGeometry *)0, &size);

    int left_offset  = 0;
    int right_offset = 0;
    XtVaGetValues(w, XmNleftOffset, &left_offset, 
              XmNrightOffset, &right_offset, XtPointer(0));

    return size.width + left_offset + right_offset;
}

static Dimension preferred_height(Widget w)
{
    if (w == 0)
      return 0;

    XtWidgetGeometry size;
    size.request_mode = CWHeight;
    XtQueryGeometry(w, (XtWidgetGeometry *)0, &size);
    return size.height;
}

static void add_settings(Widget form, int& row, Dimension& max_width,
                   DebuggerType type, EntryType entry_filter, 
                   const string& gdb_class = "set");

static Widget create_signal_button(Widget label,
                           const string& name,
                           int row,
                           Widget rightmost = 0)
{
    Arg args[15];
    Cardinal arg = 0;

    MString lbl(capitalize(name));
    XtSetArg(args[arg], XmNlabelString, lbl.xmstring()); arg++;

    if (rightmost != 0)
    {
      XtSetArg(args[arg], XmNrightAttachment, XmATTACH_WIDGET); arg++;
      XtSetArg(args[arg], XmNrightWidget,     rightmost);       arg++;
    }
    else
    {
      XtSetArg(args[arg], XmNrightAttachment, XmATTACH_FORM); arg++;
    }

    XtSetArg(args[arg], XmNtopAttachment,    XmATTACH_POSITION);  arg++;
    XtSetArg(args[arg], XmNtopPosition,      row);                arg++;
    XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_POSITION);  arg++;
    XtSetArg(args[arg], XmNbottomPosition,   row + 1);            arg++;
    XtSetArg(args[arg], XmNalignment,        XmALIGNMENT_CENTER); arg++;

    Widget w = 0;
    if (name == "send")
    {
      w = verify(XmCreatePushButton(XtParent(label), XMST(name.chars()), args, arg));
      XtManageChild(w);

      XtAddCallback(w, XmNactivateCallback, SendSignalCB, XtPointer(label));
    }
    else
    {
      string fullname = string(XtName(label)) + "-" + name;
      w = verify(XmCreateToggleButton(XtParent(label), XMST(fullname.chars()), args, arg));
      XtManageChild(w);

      XtAddCallback(w, XmNvalueChangedCallback, SignalCB, XtPointer(label));
    }

    return w;
}

// Get `show' command for settings command CMD
string show_command(const string& cmd, DebuggerType type)
{
    string show = "";
    switch (type)
    {
    case GDB:
    case BASH:
    case PYDB:
    case DBG:
      show = "show ";
      if (cmd.contains("set ", 0))
          show += cmd.after("set ");
      else if (cmd.contains("directory ", 0))
          show += "directories";
      else if (cmd.contains("path ", 0))
          show += "paths";
      else
          show += cmd;

      if (show.freq(' ') >= 2)
      {
          // Strip last argument from `show' command
          int index = show.index(' ', -1);
          show = show.before(index);
      }
      break;

    case DBX:
      show = cmd.before(rxwhite);
      break;

    case PERL:
      if (cmd.contains('='))
          show = cmd.before('=') + "?";
      else
          show = cmd + "?";
      break;

    case JDB:
    case XDB:
      break;
    }

    return show;
}

// In Perl, make these options insensitive
static const _XtString const perl_taboos[] = 
{
    "TTY",
    "noTTY",
    "ReadLine",
    "NonStop",
    "ornaments",
    "compactDump",
    "veryCompact",
    "hashDepth",
    "arrayDepth",
    "dumpDepth"
};

// Add single button
static void add_button(Widget form, int& row, Dimension& max_width,
                   DebuggerType type, EntryType entry_filter,
                   string line)
{
    if (line.empty())
      return;

    string set_command;       // Command to create the setting
    string show_command;      // Command to display the value
    string doc;               // Documentation string
    string base;        // Common base of set_command and show_command
    string value;       // Current variable value
    EntryType e_type;         // Type of the entry

    bool is_set = true;       // Command sets a value
    bool is_add = false;      // Command adds a value

    if (entry_filter == ThemeEntry)
    {
      set_command = show_command = line;
      e_type = entry_filter;
      doc = vsldoc(line, DispBox::vsllib_path);
      if (doc.empty())           // No documentation:
          doc = basename(line.chars());  // Use base name of file instead
      else if (doc.contains("."))
          doc = doc.before("."); // Use first sentence only
    }
    else
    {
      switch (type)
      {
      case BASH:
      case GDB:
      case PYDB:
      case DBG:
      {
          if (!line.contains(" -- ") && 
            (entry_filter != SignalEntry || (!line.contains("SIG", 0) &&
                                     !line.contains("all", 0))))
            return;                 // No help line

          set_command  = line.before(" -- ");
          doc          = line.after(" -- ");
          base         = set_command.after(' ');
          if (base.empty())
            base = set_command;
          show_command = "show " + base;

#if GDB_AMBIGUOUS_SHOW_PATH
          if (base == "path")
               show_command = "show paths";
#endif

          if (entry_filter == SignalEntry)
          {
            if (!line.contains("all", 0) && !line.contains("SIG", 0))
                return;       // No signal line

            // Special handling for signal tables
            base = line.before(rxwhite);
            doc  = line.after(rxwhite);
            doc  = doc.after(rxwhite);
            doc  = doc.after(rxwhite);
            doc  = doc.after(rxwhite);
            e_type = SignalEntry;
          }
          else if (entry_filter != DisplayToggleButtonEntry)
          {
            if (base == "args")
                return; // Already handled in `Run...' editor

            if (base == "radix")
                return; // Already handled in input- and output-radix

#if GDB_BROKEN_SET_INTERPRETER
                // Terry Teague <terry_teague@users.sourceforge.net> reports
            // that GDB 5.x on Mac OS X (prior to 10.4) chokes on
            // "set interpreter console" when sourcing the gdbSettings on
            // initialization.
                if (base == "interpreter")
                    return;
#endif

            // GDB 4.18 provides `set extension-language', but not the
            // equivalent `show extension-language'.
            if (base == "extension-language")
                return;

            if (doc.contains("deprecated"))
                return; // Won't support this

            is_set = doc.contains("Set ", 0);
            is_add = doc.contains("Add ", 0);

            if (!is_set && !is_add)
            {
                // Generic command or `set variable' - list `set'
                // subcommands
                add_settings(form, row, max_width, 
                         type, entry_filter, set_command);
                return;
            }

            value = cached_gdb_question(show_command);
            if (is_set && value.freq('\n') > 1)
            {
                // Generic command - list `set' subcommands
                add_settings(form, row, max_width,
                         type, entry_filter, set_command);
                return;
            }

            if (is_set && 
                !value.contains(" is ") && 
                !value.contains(".\n", -1))
                return;

            e_type = entry_type(type, base, doc, value);
            if (e_type != entry_filter)
                return;

            if (is_set)
                doc = doc.after("Set ");
            else if (is_add)
                doc = doc.after("Add ");

            if (is_add && doc.contains("of "))
                doc = doc.after("of ");
          }
          else
          {
            if (base == "signals")
                return; // We already have `info handle'.
            if (base == "watchpoints")
                return; // We already have `info breakpoints'.
            if (base == "target")
                return; // We already have `info files'.
            if (base == "address")
                return; // `info address' requires an argument.
            if (base == "line")
                return; // `info line' requires an argument.

            // These infos produce too much output for the data window
            // to be of any use.  Even worse, a data display as large
            // as the one generated by `info functions' causes my X
            // terminal to crash. - AZ
            if (base == "types")
                return;
            if (base == "functions")
                return;
            if (base == "variables")
                return;

            e_type = DisplayToggleButtonEntry;
            if (e_type != entry_filter)
                return;

            strip_leading(doc, "Show ");
            strip_leading(doc, "Print ");
            strip_leading(doc, "out ");
            strip_leading(doc, "the ");
          }
          munch_doc(doc);
          break;
      }

      case DBX:
      {
          if (!line.contains("$", 0) 
            && !(line.contains(rxidentifier, 0) && islower(line[0])))
            return;                 // No help line

          base = line.before(rxwhite);
          if (base.empty())
            return;
          if (base == "run_savetty")
            return; // Makes no sense under a GUI

          value = line.after(rxwhite);
          if (value.contains('=', 0))
          {
            // Ladebug `set' output is `$VAR = VALUE'
            value = value.after('=');
            value = value.after(rxwhite);
          }


          string dbxenv;
          if (base[0] == '$')
            dbxenv = "set";
          else
            dbxenv = "dbxenv";

          e_type = entry_type(type, base, 
                        get_dbx_help(dbxenv, base), value);
          if (e_type != entry_filter)
            return;

          set_command = dbxenv + " " + base;
          show_command = set_command + " " + value;
          doc = get_dbx_doc(dbxenv, base);

#if RUNTIME_REGEX
          static regex rxdont("Do ?n['o]t");
#endif
          if (doc.contains(rxdont, 0) && e_type == NumToggleButtonEntry)
          {
            doc = doc.after(rxdont);
            munch_doc(doc);
            e_type = NoNumToggleButtonEntry;
          }
          break;
      }

      case JDB:
      {
          // All we have is the `use' command.
          e_type = TextFieldEntry;
          set_command  = "use";
          show_command = "use";
          doc          = "Class path";
          value = source_view->class_path();
          break;
      }

      case XDB:
          return;             // FIXME

      case PERL:
      {
          e_type = TextFieldEntry;
          base  = line.before(" = ");
          strip_space(base);

          value = unquote(line.after(" = "));

// removed for perl-5.8.x
//        if (value == "N/A")
//          value = "";

          set_command  = "o " + base;
          show_command = "o " + base + "?";
          doc = base;
          break;
      }
      }

      if (e_type != entry_filter)
          return;

      if (doc.empty())
          return;       // No need to support undocumented stuff
    }

    Arg args[15];
    int arg;

    // Add label
    Widget label = 0;
    Widget entry = 0;

    String set_command_s = new char[set_command.length() + 1];
    strcpy(set_command_s, set_command.chars());

    MString labelString(doc);
    arg = 0;
    XtSetArg(args[arg], XmNlabelString,      labelString.xmstring()); arg++;
    XtSetArg(args[arg], XmNleftAttachment,   XmATTACH_FORM);          arg++;
    XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_POSITION);      arg++;
    XtSetArg(args[arg], XmNbottomPosition,   row + 1);                arg++;
    XtSetArg(args[arg], XmNalignment,        XmALIGNMENT_BEGINNING);  arg++;

    XtCallbackProc callback = 0;

    switch (e_type)
    {
    case OnOffToggleButtonEntry:
      callback = SetOnOffCB;
      break;

    case TrueFalseToggleButtonEntry:
      callback = SetTrueFalseCB;
      break;

    case SensitiveToggleButtonEntry:
      callback = SetSensitiveCB;
      break;

    case NumToggleButtonEntry:
      callback = SetNumCB;
      break;

    case NoNumToggleButtonEntry:
      callback = SetNoNumCB;
      break;

    case DisplayToggleButtonEntry:
      callback = SetDisplayCB;
      break;

    case ThemeEntry:
      callback = ApplyThemesCB;
      break;

    default:
      callback = 0;
      break;
    }

    if (callback == 0)
    {
      if (is_set)
          label = verify(XmCreateLabel(form, XMST(base.chars()), args, arg));
      else {
        const string s1 = string("the") + base;
        label = verify(XmCreateLabel(form, XMST(s1.chars()), args, arg));
      }

      XtManageChild(label);
    }
    else
    {
      entry = label = 
          verify(XmCreateToggleButton(form, XMST(set_command.chars()), args, arg));
      XtManageChild(label);

      XtAddCallback(entry, XmNvalueChangedCallback,
                  callback, XtPointer(set_command_s));
    }

    // Add help button
    arg = 0;
    XtSetArg(args[arg], XmNrightAttachment,  XmATTACH_FORM);      arg++;
    XtSetArg(args[arg], XmNtopAttachment,    XmATTACH_POSITION);  arg++;
    XtSetArg(args[arg], XmNtopPosition,      row);                arg++;
    XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_POSITION);  arg++;
    XtSetArg(args[arg], XmNbottomPosition,   row + 1);            arg++;
    XtSetArg(args[arg], XmNalignment,        XmALIGNMENT_CENTER); arg++;
    Widget help = verify(XmCreatePushButton(form, XMST("help"), args, arg));
    XtManageChild(help);

    Widget send  = 0;
    Widget pass  = 0;
    Widget print = 0;
    Widget stop  = 0;
    if (e_type == SignalEntry)
    {
      send  = create_signal_button(label, "send",  row, help);
      pass  = create_signal_button(label, "pass",  row, send);
      print = create_signal_button(label, "print", row, pass);
      stop  = create_signal_button(label, "stop",  row, print);

      if (base == "all")
      {
          set_sensitive(send, false);
          XtVaSetValues(pass,  XmNset, True, XtPointer(0));
          XtVaSetValues(print, XmNset, True, XtPointer(0));
          XtVaSetValues(stop,  XmNset, True, XtPointer(0));
      }
    }

    // Add entry
    switch (e_type)
    {
    case OnOffToggleButtonEntry:
    case TrueFalseToggleButtonEntry:
    case SensitiveToggleButtonEntry:
    case NumToggleButtonEntry:
    case NoNumToggleButtonEntry:
    case DisplayToggleButtonEntry:
    case SignalEntry:
      // All is done
      break;

    case CheckOptionMenuEntry:
    {
      // `set check'
      arg = 0;
      Widget menu = verify(XmCreatePulldownMenu(form, 
                                      XMST("menu"), args, arg));

      // Possible options are contained in the help string
      string options = cached_gdb_question("help " + set_command);
      options = options.from('(');
      options = options.before(')');

      while (!options.empty())
      {
          string option = options.after(0);
          option = option.through(rxalpha);
          options = options.after(rxalpha);

          arg = 0;
          Widget button = 
            verify(XmCreatePushButton(menu, XMST(option.chars()), args, arg));
          XtManageChild(button);
          XtAddCallback(button, XmNactivateCallback, SetOptionCB, 
                    set_command_s);
      }

      MString empty;
      arg = 0;
      XtSetArg(args[arg], XmNrightAttachment,  XmATTACH_WIDGET);   arg++;
      XtSetArg(args[arg], XmNrightWidget,      help);              arg++;
      XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_POSITION); arg++;
      XtSetArg(args[arg], XmNbottomPosition,   row + 1);           arg++;
      XtSetArg(args[arg], XmNlabelString,      empty.xmstring());  arg++;
      XtSetArg(args[arg], XmNmarginWidth,      0);                 arg++;
      XtSetArg(args[arg], XmNmarginHeight,     0);                 arg++;
      XtSetArg(args[arg], XmNspacing,          0);                 arg++;
      XtSetArg(args[arg], XmNsubMenuId,        menu);              arg++;
      entry = verify(XmCreateOptionMenu(form, XMST(set_command.chars()), args, arg));
      XtManageChild(entry);

      Widget option_label = XmOptionLabelGadget(entry);
      XtUnmanageChild(option_label);
      break;
    }

    case OtherOptionMenuEntry:
    case TargetOptionMenuEntry:
    case ForkOptionMenuEntry:
    case DisassemblyOptionMenuEntry:
    case SchedulerOptionMenuEntry:
    {
#if GDB_ABI_OPTIONS
        // set cp-abi / set osabi /
#endif
      // set language / set demangle / set architecture / set endian /
      // set follow-fork-mode / set disassembly-flavor / 
      // set scheduler-locking
      arg = 0;
      Widget menu = verify(XmCreatePulldownMenu(form, 
                                      XMST("menu"), args, arg));

      string options;
      char separator = '\n';

      switch (gdb->type())
      {
      case GDB:
          options = cached_gdb_question("set " + base);
          if((base == "architecture")
          || (base == "demangle-style")
          || (base == "disassembly-flavor")
          || (base == "endian")
          || (base == "follow-fork-mode")
          || (base == "scheduler-locking")
#if GDB_ABI_OPTIONS
            || (base == "osabi")
#endif
             )
          {
            // First look for the reponse in the format of GDB-5.x or
            // newer. Possible options are listed upon `set endian'
            // (or whatever) and separated by `,'.
            if (options.contains("Requires an argument. Valid arguments are"))
            {
                strip_leading(options, "Requires an argument. Valid arguments are");
                // remove trailing .\n (if any)
                if (options.contains(".\n"))
                {
                  options = options.before(".\n");
                }
                separator = ',';
            }
                else if (options.contains("Requires an argument. Valid values are"))
                {
                    strip_leading(options, "Requires an argument. Valid values are");
                    // remove trailing .\n (if any)
                    if (options.contains(".\n"))
                    {
                        options = options.before(".\n");
                    }
                    separator = ',';
                }
            // OK, so it doesn't match GDB-5.x output; go with 4.x format
            else if (base == "architecture")
            {
                // Possible options are listed upon `info architecture'
                // and separated by ` '.
                options = cached_gdb_question("info " + base);
                options = "auto" + options.from('\n');
                separator = ' ';
            }
            else if (base == "disassembly-flavor")
            {
                // Hardwired options
                options = "intel\natt\n";
            }
            else if (base == "endian")
            {
                // Hardwired options
                options = "auto\nbig endian\nlittle endian\n";
            }
            else if (base == "follow-fork-mode")
            {
                // Hardwired options
                options = "parent\nchild\nask\n";
            }
            else if (base == "scheduler-locking")
            {
                // Hardwired options
                options = "off\non\nstep\n";
            }
#if GDB_ABI_OPTIONS
                else if (base == "cp-abi")
                {
                    strip_leading(options, "The available C++ ABIs are:");
                }
#endif
          }
          break;

      case DBX:
          options = _get_dbx_help("dbxenv", base);
          options = options.after('<');
          options = options.before('>');

          if (options.empty())
          {
            if (base == "follow_fork_mode")
                options = "parent|child|both|ask";
          }
          separator = '|';
          break;

      case BASH:
      case DBG:
      case JDB:
      case PERL:
      case PYDB:
      case XDB:
          return;       // FIXME
      }

      while (!options.empty())
      {
          string option = options;
          if (option.contains(separator))
            option = option.before(separator);
          options = options.after(separator);

          strip_space(option);

          string label = option;
          if (gdb->type() == GDB && option.contains("  "))
          {
            label = option.after("  ");
            label = label.after(rxwhite);

            if (option.contains(" auto"))
                option = "auto";
            else
                option = option.before(rxwhite);
          }

#if GDB_ABI_OPTIONS
            else if (gdb->type() == GDB && option.contains(" - "))
            {
                 label = option.after(" - ");
                 option = option.before(" - ");
                 if (option.contains("auto"))
                label = "auto";
            }
#endif

          if (option.empty() || option.contains(':', -1))
            continue;

          MString xmlabel(label);
          arg = 0;
          if (gdb->type() == GDB)
          {
            XtSetArg(args[arg], XmNlabelString, 
                   xmlabel.xmstring()); arg++;
          }
          Widget button = 
            verify(XmCreatePushButton(menu, XMST(option.chars()), args, arg));
          XtManageChild(button);
          XtAddCallback(button, XmNactivateCallback, SetOptionCB, 
                    set_command_s);
      }

      MString empty;
      arg = 0;
      XtSetArg(args[arg], XmNrightAttachment,  XmATTACH_WIDGET);   arg++;
      XtSetArg(args[arg], XmNrightWidget,      help);              arg++;
      XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_POSITION); arg++;
      XtSetArg(args[arg], XmNbottomPosition,   row + 1);           arg++;
      XtSetArg(args[arg], XmNlabelString,      empty.xmstring());  arg++;
      XtSetArg(args[arg], XmNmarginWidth,      0);                 arg++;
      XtSetArg(args[arg], XmNmarginHeight,     0);                 arg++;
      XtSetArg(args[arg], XmNspacing,          0);                 arg++;
      XtSetArg(args[arg], XmNsubMenuId,        menu);              arg++;
      entry = verify(XmCreateOptionMenu(form, XMST(set_command.chars()), args, arg));
      XtManageChild(entry);

      Widget option_label = XmOptionLabelGadget(entry);
      XtUnmanageChild(option_label);
      break;
    }

    case ThemeEntry:
    case TextFieldEntry:
    {
      // Some other value
      arg = 0;
      XtSetArg(args[arg], XmNrightAttachment,  XmATTACH_WIDGET);   arg++;
      XtSetArg(args[arg], XmNrightWidget,      help);              arg++;
      XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_POSITION); arg++;
      XtSetArg(args[arg], XmNbottomPosition,   row + 1);           arg++;
      entry = verify(XmCreateTextField(form, XMST(set_command.chars()), args, arg));
      XtManageChild(entry);

      if (e_type == TextFieldEntry)
          XtAddCallback(entry, XmNvalueChangedCallback, 
                    UpdateSettingsButtonsCB, XtPointer(0));
      else // ThemeEntry
          XtAddCallback(entry, XmNvalueChangedCallback, 
                    UpdateThemesButtonsCB, XtPointer(0));
    }
    }

    Widget rightmost = help;
    if (entry != label)
      rightmost = entry;
    if (stop != 0)
        rightmost = stop;

    Dimension width = preferred_width(label);
    if (entry != label)
      width += preferred_width(entry);
    width += preferred_width(send);
    width += preferred_width(stop);
    width += preferred_width(print);
    width += preferred_width(pass);
    width += preferred_width(help);

    // Add leader

    // Align leader with label font baseline
    XmFontList font_list   = 0;
    XmString xmlabel       = 0;
    Dimension marginHeight = 0;
    Dimension marginTop    = 0;
    XtVaGetValues(label,
              XmNfontList,     &font_list,
              XmNlabelString,  &xmlabel,
              XmNmarginHeight, &marginHeight,
              XmNmarginTop,    &marginTop,
              XtPointer(0));

    Dimension baseline = XmStringBaseline(font_list, xmlabel);
    static const Dimension leaderHeight = 2;

    Dimension top_offset = baseline + marginHeight + marginTop - leaderHeight;
    XmStringFree(xmlabel);

    arg = 0;
    XtSetArg(args[arg], XmNseparatorType,    XmSINGLE_DASHED_LINE); arg++;
    XtSetArg(args[arg], XmNleftAttachment,   XmATTACH_WIDGET);   arg++;
    XtSetArg(args[arg], XmNleftWidget,       label);             arg++;
    XtSetArg(args[arg], XmNrightAttachment,  XmATTACH_WIDGET);   arg++;
    XtSetArg(args[arg], XmNrightWidget,      rightmost);         arg++;
    XtSetArg(args[arg], XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET); arg++;
    XtSetArg(args[arg], XmNtopWidget,        label);             arg++;
    XtSetArg(args[arg], XmNtopOffset,        top_offset);        arg++;
    Widget leader = verify(XmCreateSeparator(form, 
                                   XMST("leader"), args, arg));
    XtManageChild(leader);

    // Add help callback
    switch (e_type)
    {
    case SignalEntry:
      XtAddCallback(help, XmNactivateCallback, 
                  HelpOnSignalCB, XtPointer(label));
      break;

    case ThemeEntry:
      XtAddCallback(help, XmNactivateCallback, 
                  HelpOnThemeCB, XtPointer(label));
      break;

    default:
      XtAddCallback(help, XmNactivateCallback, 
                  HelpOnThisCB, XtPointer(entry));
      break;
    }

    if (e_type == SignalEntry)
    {
      if (base != "all")
      {
          // Initialize button
          process_handle(line, true);

          // Register buttons
          signals_entries += stop;
          signals_entries += print;
          signals_entries += pass;
      }
    }
    else if (e_type == ThemeEntry)
    {
      // Register entry
      themes_entries += entry;
      themes_labels  += label;
    }
    else if (e_type != DisplayToggleButtonEntry)
    {
      // Make entry insensitive if part of initialization commands.
      string init = app_data.gdb_init_commands;
      int idx = init.index(set_command);
      bool insensitive = (idx == 0 || idx > 0 && init[idx - 1] == '\n');

      // Make entry insensitive if one of Perl taboos
      if (!insensitive && gdb->type() == PERL)
      {
          for (int i = 0; i < int(XtNumber(perl_taboos)); i++)
            if (base == perl_taboos[i])
            {
                insensitive = true;
                break;
            }
      }

      if (insensitive)
      {
          set_sensitive(entry,  false);
          set_sensitive(label,  false);
          set_sensitive(leader, false);
      }

      // Initialize button
      process_show(show_command, value, true);

      // Register entry
      settings_entries     += entry;
      settings_entry_types += e_type;
    }
    else
    {
      // Register entry
      infos_entries += entry;
    }

    max_width = max(width, max_width);

    row++;
}

// Add separator
static void add_separator(Widget form, int& row)
{
    Arg args[10];
    int arg = 0;
    XtSetArg(args[arg], XmNleftAttachment,   XmATTACH_FORM);     arg++;
    XtSetArg(args[arg], XmNrightAttachment,  XmATTACH_FORM);     arg++;
    XtSetArg(args[arg], XmNtopAttachment,    XmATTACH_POSITION); arg++;
    XtSetArg(args[arg], XmNtopPosition,      row);               arg++;
    XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_POSITION); arg++;
    XtSetArg(args[arg], XmNbottomPosition,   row + 1);           arg++;
    Widget sep = verify(XmCreateSeparator(form, XMST("sep"), args, arg));
    XtManageChild(sep);
    row++;
}

// Add buttons
static void add_settings(Widget form, int& row, Dimension& max_width,
                   DebuggerType type, EntryType entry_filter,
                   const string& gdb_class)
{
    string commands;

    switch (type)
    {
    case GDB:
    case BASH:
    case PYDB:
    case DBG:
      if (entry_filter == SignalEntry)
          commands = cached_gdb_question("info handle");
      else
          commands = cached_gdb_question("help " + gdb_class);
      break;

    case XDB:
      commands = cached_gdb_question("I");
      break;

    case DBX:
    {
      // Some DBXes know `dbxenv'; others know `set'.  We cannot use
      // is_valid() or likewise here, since the output may contain
      // `error' strings that would cause is_valid() to fail.  Just
      // try both and use the longer reply (may take time).

      // Exception: on Sun DBX, `set' causes a dump of all
      // environment variables.  Don't use this.
      string dbxenv = cached_gdb_question("dbxenv", -1);
      string set    = cached_gdb_question("set", -1);
      if (dbxenv.length() > set.length() ||
          set.contains(DDD_NAME "=" ddd_NAME))
      {
          commands = dbxenv;
      }
      else
      {
          // Ladebug may echo `set'
          if (set.contains("set ", 0))
            set = set.after("set ");
          strip_leading_space(set);
          commands = set;
      }
      break;
    }

    case JDB:
    {
      // In JDB, all we have is the `use' command.
      add_button(form, row, max_width, type, entry_filter, "use");
      break;
    }

    case PERL:
      commands = cached_gdb_question("o");
      break;
    }

    if (type == GDB && entry_filter == SignalEntry)
    {
      // Add an entry for `handle all'
      add_button(form, row, max_width, type, entry_filter,
               "all\tYes\tYes\tYes\tAll Signals");
      // add_separator(form, row);
    }

    if (commands.contains('\n'))
    {
      while (!commands.empty())
      {
          string line = commands.before('\n');
          commands    = commands.after('\n');
          add_button(form, row, max_width, type, entry_filter, line);
      }
    }
    else
    {
      // Ladebug gives us all settings in one line.
      commands += '$';
      while (!commands.empty())
      {
          commands = commands.from('$');
          int idx = commands.index('$');
          idx = commands.index('$', idx + 1);
          string line = commands.before(idx);
          commands    = commands.after(line);

          add_button(form, row, max_width, type, entry_filter, line);
      }
    }
}

// Reload all settings
static void reload_all_settings()
{
    for (int i = 0; i < settings_entries.size(); i++)
    {
      Widget entry = settings_entries[i];
      string cmd = string(XtName(entry)) + " dummy";
      string show = show_command(cmd, gdb->type());
      string value = gdb_question(show);
      if (value != NO_GDB_ANSWER)
          process_show(show, value, true);
    }
}

void update_settings()
{
    if (settings_panel != 0 && XtIsManaged(settings_panel))
    {
      reload_all_settings();
      need_reload_settings = false;
    }
}

// Reset settings
static void ResetSettingsCB(Widget, XtPointer, XtPointer)
{
    CommandGroup cg;

    for (int i = 0; i < settings_entries.size(); i++)
    {
      Widget entry = settings_entries[i];

      string value = settings_values[entry];
      if (settings_entry_types[i] == TextFieldEntry)
      {
          String value_s = XmTextFieldGetString(entry);
          value = value_s;
          XtFree(value_s);

          if (value != settings_values[entry])
          {
            value = settings_values[entry];
            XmTextFieldSetString(entry, XMST(value.chars()));
          }
      }

      if (value != settings_initial_values[entry])
          gdb_set_command(XtName(entry), settings_initial_values[entry]);
    }
}

// Reset signals
static void ResetSignalsCB(Widget, XtPointer, XtPointer)
{
    CommandGroup cg;

    string command = "";

    for (int i = 0; i < signals_entries.size(); i++)
    {
      Widget entry = signals_entries[i];
      if (signals_initial_values[entry] != signals_values[entry])
      {
          bool set = (signals_initial_values[entry] == "yes");
          command += handle_command(entry, set) + "\n";
      }
    }

    gdb_command(command);
}

// Apply settings
static void ApplySettingsCB(Widget, XtPointer, XtPointer)
{
    CommandGroup cg;

    for (int i = 0; i < settings_entries.size(); i++)
    {
      if (settings_entry_types[i] != TextFieldEntry)
          continue;

      Widget entry = settings_entries[i];

      String value_s = XmTextFieldGetString(entry);
      string value(value_s);
      XtFree(value_s);

      if (value != settings_values[entry])
          gdb_set_command(XtName(entry), value);
    }
}

// Delete all infos
static void DeleteAllInfosCB (Widget, XtPointer, XtPointer)
{
    for (int i = 0; i < infos_entries.size(); i++)
    {
      Widget entry = infos_entries[i];
      XmToggleButtonSetState(entry, False, True);
    }
}

// Fetch help for specific COMMAND
static string get_help_line(const string& command, DebuggerType /*type*/)
{
    string reply = cached_gdb_question("help " + command);
    reply = reply.before('\n');
    if (reply.contains('.'))
      reply = reply.before('.');
    reply = command + " -- " + reply;
    return reply;
}

// ClipWindow translation stuff
static void ClipDo(Widget w, XEvent *event, 
               String *params, Cardinal *num_params)
{
    Widget scroll = XtParent(w);
    Widget scrollbar = 0;
    XtVaGetValues(scroll, XmNverticalScrollBar, &scrollbar, XtPointer(0));
    XtCallActionProc(scrollbar, params[0], event, params + 1, *num_params - 1);
}

static void fix_clip_window_translations(Widget scroll)
{
    // The scrolled clip window grabs translations for osfBeginLine
    // and osfEndLine, which we need in our text fields.  Since we
    // cannot selectively disable these translations, we disable all
    // translations ...

    Widget clip = 0;
    XtVaGetValues(scroll, XmNclipWindow, &clip, XtPointer(0));
//  XtUninstallTranslations(clip);

    // ... and provide a minimal set instead.
    static bool have_clip_actions = false;
    if (!have_clip_actions)
    {
      static XtActionsRec clip_actions[] = {
          {XTARECSTR("clip-do"), ClipDo}
      };

      XtAppAddActions(XtWidgetToApplicationContext(scroll), 
                  clip_actions, XtNumber(clip_actions));
      have_clip_actions = true;
    }

    const _XtString const clip_translations =
      "<Key>osfPageUp:         clip-do(PageUpOrLeft, 0)\n"
      "<Key>osfPageDown:       clip-do(PageDownOrRight, 0)\n"
      "Ctrl <Key>osfBeginLine: clip-do(TopOrBottom)\n"
      "Ctrl <Key>osfEndLine:   clip-do(TopOrBottom)\n"
        "<Key>osfUp:             clip-do(IncrementUpOrLeft, 0)\n"
        "<Key>osfDown:           clip-do(IncrementDownOrRight, 0)\n";


    XtTranslations tr = XtParseTranslationTable(clip_translations);
    XtOverrideTranslations(clip, tr);
}


// Themes

// Return all available themes
void get_themes(StringArray& themes)
{
    StringArray bases;

    string path = DispBox::vsllib_path;
    int n = path.freq(':');
    string *dirs = new string[n + 1];
    split(path, dirs, n + 1, ':');

    for (int i = 0; i < n; i++)
    {
      string mask = dirs[i] + "/*.[vV][sS][lL]";
      char **files = glob_filename(mask.chars());
      if (files == (char **)0)
      {
          std::cerr << mask << ": glob failed\n";
      }
      else if (files == (char **)-1)
      {
          if (errno != 0)
            post_warning(string(mask) + ": " + strerror(errno), 
                       "no_themes_warning");
      }
      else
      {
          int count;
          for (count = 0; files[count] != 0; count++)
            ;
          smart_sort(files, count);

          for (int i = 0; i < count; i++)
          {
            string base = basename(files[i]);

            bool duplicate = false;
            for (int j = 0; !duplicate && j < bases.size(); j++)
                if (bases[j] == base)
                  duplicate = true;

            if (!duplicate)
            {
                themes += files[i];
                bases  += base;
            }

            free(files[i]);
          }
          free((char *)files);
      }
    }

    delete[] dirs;
}

static void add_themes(Widget form, int& row, Dimension& max_width)
{
    clear_vsldoc_cache();
    StringArray themes;
    get_themes(themes);

    for (int i = 0; i < themes.size(); i++)
      add_button(form, row, max_width, gdb->type(), ThemeEntry, themes[i]);
}


// Create settings or infos editor
static Widget create_panel(DebuggerType type, SettingsType stype)
{
    string title_msg;
    string dialog_name;
    switch (stype)
    {
    case SETTINGS:
      title_msg = gdb->title() + " Settings";
      dialog_name = "settings";
      break;

    case INFOS:
      title_msg = gdb->title() + " Status Displays";
      dialog_name = "infos";
      break;

    case SIGNALS:
      title_msg = gdb->title() + " Signal Handling";
      dialog_name = "signals";
      break;

    case THEMES:
      title_msg   = "Themes";
      dialog_name = "themes";
      break;
    }

    StatusDelay delay("Retrieving " + title_msg);

    Arg args[10];
    int arg=0;
    XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
    Widget panel = verify(XmCreatePromptDialog(find_shell(), 
                                     CONST_CAST(char*,dialog_name.chars()), args, arg));
    Delay::register_shell(panel);

    Widget apply_button = XmSelectionBoxGetChild(panel, XmDIALOG_OK_BUTTON);
    set_sensitive(apply_button, False);

    Widget reset_button = XmSelectionBoxGetChild(panel, XmDIALOG_APPLY_BUTTON);
    XtManageChild(reset_button);

    // Remove old prompt
    XtUnmanageChild(XmSelectionBoxGetChild(panel, XmDIALOG_TEXT));
    XtUnmanageChild(XmSelectionBoxGetChild(panel, XmDIALOG_SELECTION_LABEL));

    XtAddCallback(panel, XmNhelpCallback, ImmediateHelpCB, 0);
    XtAddCallback(panel, XmNcancelCallback, UnmanageThisCB, XtPointer(panel));

    switch (stype)
    {
    case SETTINGS:
      XtAddCallback(panel, XmNokCallback, ApplySettingsCB, 0);
      XtAddCallback(panel, XmNapplyCallback, ResetSettingsCB, 0);
      apply_settings_button = apply_button;
      break;

    case INFOS:
      XtAddCallback(panel, XmNapplyCallback, DeleteAllInfosCB, 0);
      XtUnmanageChild(apply_button); // No text entries
      break;

    case SIGNALS:
      XtAddCallback(panel, XmNapplyCallback, ResetSignalsCB, 0);
      XtUnmanageChild(apply_button); // No text entries
      break;

    case THEMES:
      XtAddCallback(panel, XmNokCallback,    ApplyThemesCB, 0);
      XtAddCallback(panel, XmNapplyCallback, ResetThemesCB, 0);
      apply_themes_button = apply_button;
      break;
    }

    // Add a rowcolumn widget
    arg = 0;
    XtSetArg(args[arg], XmNborderWidth,  0); arg++;
    XtSetArg(args[arg], XmNmarginWidth,  0); arg++;
    XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
    XtSetArg(args[arg], XmNspacing,      0); arg++;
    Widget column =
        verify(XmCreateRowColumn(panel, CONST_CAST(char *,"column"), args, arg));
    XtManageChild(column);

    // Add a label
    arg = 0;
    MString xmtitle(title_msg);
    XtSetArg(args[arg], XmNlabelString, xmtitle.xmstring()); arg++;
    Widget title = verify(XmCreateLabel(column, CONST_CAST(char *,"title"), args, arg));
    XtManageChild(title);

    // Add a scrolled window.
    arg = 0;
    XtSetArg(args[arg], XmNvisualPolicy, XmCONSTANT); arg++;
    XtSetArg(args[arg], XmNscrollingPolicy, XmAUTOMATIC); arg++;
    Widget scroll = 
      verify(XmCreateScrolledWindow(column, CONST_CAST(char *,"scroll"), args, arg));
    fix_clip_window_translations(scroll);

    // Add a form.
    arg = 0;
    Widget form = verify(XmCreateForm(scroll,
                            XMST("form"), args, arg));

    switch (stype)
    {
    case SETTINGS:
      settings_form  = form;
      break;

    case SIGNALS:
      signals_form = form;
      break;

    case INFOS:
    case THEMES:
      break;
    }

    // Add setting buttons to the button box.
    Dimension max_width = 0;
    int row = 0;
    int last_row = row;

    switch (stype)
    {
    case SETTINGS:
      add_settings(form, row, max_width, type, OnOffToggleButtonEntry);
      add_settings(form, row, max_width, type, TrueFalseToggleButtonEntry);
      add_settings(form, row, max_width, type, SensitiveToggleButtonEntry);
      add_settings(form, row, max_width, type, NumToggleButtonEntry);
      add_settings(form, row, max_width, type, NoNumToggleButtonEntry);
      if (row != last_row)
          add_separator(form, row);

      last_row = row;
      add_settings(form, row, max_width, type, TargetOptionMenuEntry);
      if (row != last_row)
          add_separator(form, row);

      last_row = row;
      add_settings(form, row, max_width, type, OtherOptionMenuEntry);
      add_settings(form, row, max_width, type, DisassemblyOptionMenuEntry);
      if (row != last_row)
          add_separator(form, row);

      last_row = row;
      add_settings(form, row, max_width, type, ForkOptionMenuEntry);
      add_settings(form, row, max_width, type, SchedulerOptionMenuEntry);
      if (row != last_row)
          add_separator(form, row);

      last_row = row;
      add_settings(form, row, max_width, type, CheckOptionMenuEntry);
      if (row != last_row)
          add_separator(form, row);

      if (type == GDB)
      {
          last_row = row;
          add_button(form, row, max_width, type, TextFieldEntry, 
                   get_help_line("dir", type));
          add_button(form, row, max_width, type, TextFieldEntry, 
                   get_help_line("path", type));
          if (row != last_row)
            add_separator(form, row);
      }

      add_settings(form, row, max_width, type, TextFieldEntry);
      break;

    case INFOS:
      add_settings(form, row, max_width, type, 
                 DisplayToggleButtonEntry, "info");
      break;

    case SIGNALS:
      add_settings(form, row, max_width, type, SignalEntry, "signals");
      break;

    case THEMES:
      add_themes(form, row, max_width);
      break;
    }

    // Clean up cached documentation stuff
    clear_gdb_question_cache();

    // Setup values
    switch (stype)
    {
    case SETTINGS:
      reset_settings_button = reset_button;
      update_reset_settings_button();
      break;

    case INFOS:
      reset_infos_button = reset_button;
      update_infos();
      break;

    case SIGNALS:
      reset_signals_button = reset_button;
      update_reset_signals_button();
      break;

    case THEMES:
      reset_themes_button = reset_button;
      update_themes();
      update_themes_buttons();
      break;
    }

    // Set number of rows
    if (row > 0)
      XtVaSetValues(form, XmNfractionBase, row, XtPointer(0));
    XtManageChild(form);

    // Set sizes
    max_width += EXTRA_SPACE;
    XtVaSetValues(form, 
              XmNwidth, max_width, XtPointer(0));

#if XmVersion > 1001
    Dimension height = preferred_height(form);
#else
    // OSF/Motif 1.1 returns a bad value in PREFERRED_HEIGHT
    (void) preferred_height;
    Dimension height = MAX_HEIGHT + 1;
#endif

    Widget vertical_scroll_bar = 0;
    Dimension spacing = 4;
    XtVaGetValues(scroll, 
              XmNverticalScrollBar, &vertical_scroll_bar,
              XmNspacing, &spacing,
              XtPointer(0));


    // Ask Xt for the main shell's height;
    // we'll make our dialog no taller than 0.75 of this value
    Dimension recommended_height;
    XtVaGetValues(find_shell(), XmNheight, &recommended_height, XtPointer(0));
    recommended_height = recommended_height * 3 / 4;
    
    if (height + spacing > recommended_height )
    {
      // Form must be scrolled
      Dimension scrollbar_width = 15;   // Additional space for scrollbar

      if (vertical_scroll_bar != 0)
      {
          XtWidgetGeometry size;
          size.request_mode = CWWidth;
          XtQueryGeometry(vertical_scroll_bar, (XtWidgetGeometry *)0, &size);
          scrollbar_width = size.width;
      }
      
      XtVaSetValues(scroll,
                  XmNheight, recommended_height,
                  XmNwidth, max_width + 
                            spacing + scrollbar_width + EXTRA_WIDTH,
                  XtPointer(0));
    }
    else
    {
      // Form need not be scrolled
      XtVaSetValues(scroll,
                  XmNheight, height + spacing,
                  XmNwidth, max_width + spacing,
                  XtPointer(0));
    }

    XtVaSetValues(scroll,
                  XmNtopAttachment,    XmATTACH_WIDGET,
                  XmNtopWidget,        title,
                  XmNleftAttachment,   XmATTACH_FORM,
                  XmNrightAttachment,  XmATTACH_FORM,
                  XmNbottomAttachment, XmATTACH_FORM,
                  XtPointer(0));

    XtManageChild(scroll);

    // prevent the action area from growing when the window is resized
    Dimension h, w, x, y;

    // prevent the window from being made smaller or bigger than it initially is
    XtVaGetValues(panel,
                  XmNwidth,  &w,
                  XmNheight, &h,
                  XtPointer(0));

    // XtGetValues sometimes returns rubbish in the higher-order bits
    w &= 0x7fff;
    h &= 0x7fff;

    XtVaSetValues(panel,
                  XmNminWidth,  w,
                  XmNminHeight, h,
                  XmNmaxWidth,  w,
                  XmNmaxHeight, h,
                  XtPointer(0));

    // Center this dialog window in it's parent widget
    XtVaGetValues(XtParent(panel),
                  XmNwidth,  &x,
                  XmNheight, &y,
                  XtPointer(0));

    x &= 0x7fff;  // "32767 pixels should be enough for just about anybody"
    y &= 0x7fff;  //                            -- Andrew Gaylard, 2003/10/13

    XtVaSetValues(panel,
                  XmNx, x/2 - w/2,
                  XmNy, y/2 - h/2,
                  XtPointer(0));

    InstallButtonTips(panel);

    return panel;
}


// Create settings editor
static Widget create_settings(DebuggerType type)
{
    check_options_file();

    if (settings_panel == 0 && can_do_gdb_command() && gdb->type() == type)
    {
      // We place a delay here such that we show only one delay for
      // both getting the settings and the command definitions.
      StatusDelay delay("Retrieving " + gdb->title() + " Settings");

      settings_panel = create_panel(type, SETTINGS);

      // Get the command definitions, too.  These must be included
      // in saving GDB state.
      (void) get_defines(type);
    }
    else if (settings_panel != 0 && need_reload_settings)
    {
      reload_all_settings();
      need_reload_settings = false;
    }

    return settings_panel;
}

// Create infos editor
static Widget create_infos(DebuggerType type)
{
    check_options_file();

    if (infos_panel == 0 && can_do_gdb_command() && gdb->type() == type)
      infos_panel = create_panel(type, INFOS);

    return infos_panel;
}

// Reload all signals
static void reload_all_signals()
{
    check_options_file();

    string info = gdb_question("info handle");
    if (info != NO_GDB_ANSWER)
      process_handle(info, true);
}

// Create signal editor
static Widget create_signals(DebuggerType type)
{
    check_options_file();

    if (signals_panel == 0 && can_do_gdb_command() && gdb->type() == type)
    {
      signals_panel = create_panel(type, SIGNALS);
    }
    else if (signals_panel != 0 && need_reload_signals)
    {
      reload_all_signals();
      need_reload_signals = false;
    }

    return signals_panel;
}

void update_signals()
{
    if (signals_panel != 0 && XtIsManaged(signals_panel))
    {
      reload_all_signals();
      need_reload_signals = false;
    }
}

// Create themes editor
static Widget create_themes(DebuggerType type)
{
    check_options_file();

    if (themes_panel != 0)
      XtDestroyWidget(themes_panel);

    // Reset variables
    static const WidgetArray empty;
    themes_panel        = 0;
    reset_themes_button = 0;
    apply_themes_button = 0;
    themes_entries = empty;
    themes_labels  = empty;

    themes_panel = create_panel(type, THEMES);

    update_themes();
    return themes_panel;
}

// Update themes
void update_themes()
{
    assert(themes_entries.size() == themes_labels.size());

    for (int i = 0; i < themes_entries.size(); i++)
    {
      Widget entry  = themes_entries[i];
      Widget button = themes_labels[i];
      string theme = basename(XtName(entry));
      ThemePattern p;
      bool set = false;

      if (DispBox::theme_manager.has_pattern(theme))
      {
          p = DispBox::theme_manager.pattern(theme);
          set = DispBox::theme_manager.pattern(theme).active();
      }

      std::ostringstream os;
      os << p;
      string value = string(os);
      if (value.empty())
      {
          value = "*";
          set = false;
      }

      XmTextFieldSetString(entry, XMST(value.chars()));
      XtVaSetValues(button, XmNset, set, XtPointer(0));
    }
}



// Popup editor for debugger settings
void dddPopupSettingsCB (Widget, XtPointer, XtPointer)
{
    Widget settings = create_settings(gdb->type());
    if (settings == 0)
      return;

    manage_and_raise(settings);
}

// Popup editor for debugger infos
void dddPopupInfosCB (Widget, XtPointer, XtPointer)
{
    Widget infos = create_infos(gdb->type());
    if (infos == 0)
      return;

    manage_and_raise(infos);
}

// Popup editor for debugger infos
void dddPopupSignalsCB (Widget, XtPointer, XtPointer)
{
    Widget signals = create_signals(gdb->type());
    if (signals == 0)
      return;

    manage_and_raise(signals);
}

// Popup editor for display themes
void dddPopupThemesCB (Widget, XtPointer, XtPointer)
{
    Widget themes = create_themes(gdb->type());
    if (themes == 0)
      return;

    manage_and_raise(themes);
}

// True iff settings might have changed
bool need_settings()
{
    return settings_panel != 0;
}

// Reset it all
void reset_settings()
{
    if (settings_panel != 0)
      need_reload_settings = true;
}

// True iff signals might have changed
bool need_signals()
{
    return signals_panel != 0;
}

// Reset it all
void reset_signals()
{
    if (signals_panel != 0)
      need_reload_signals = true;
}

static void get_setting(std::ostream& os, DebuggerType type,
                  const string& base, string value,
                  unsigned long flags)
{
    if (value == "unlimited")
      value = "0";

    switch (type)
    {
    case GDB:
    case DBX:
      if (base == "dbxenv disassembler_version" ||
          base == "dbxenv rtc_error_log_file_name" ||
          base == "dbxenv output_log_file_name")
      {
          // Do nothing (DBX) - dependent on the current machine etc.
          break;
      }

      if (base == "set remotelogfile" && value.empty())
      {
          // This is the default setting - do nothing (GDB)
          break;
      }

      if (base == "set remotedevice" && value.empty())
      {
          // This is the default setting - do nothing (GDB)
          break;
      }

      if (base == "set targetdebug")
      {
          // We cannot set this without a debuggee (GDB 5.0).
          break;
      }

      if (base == "set scheduler-locking")
      {
          // Architecture-dependent (GDB 5.0).
          break;
      }

      if (base == "set solib-absolute-prefix" && value.empty())
      {
          // GDB 4.17 bug: `set solib-absolute-prefix' without arg
          // does not work.  Just leave it as default setting.
          break;
      }

      if (base.contains("set $cur", 0) ||
          base.contains("set $new", 0) ||
          base.contains("set $pid", 0))
      {
          // Do nothing - dependent on the current file (DEC DBX)
          break;
      }

      if (base == "set $defaultin" ||
          base == "set $defaultout" ||
          base == "set $historyevent")
      {
          // Do nothing - dependent on the current state (SGI DBX)
          break;
      }

      if (base.contains("set $", 0))
      {
          // Add setting (DBX).
          os << base << " = " << value << '\n';
          break;
      }

      if (base.contains("set ", 0))
      {
          // Add setting (GDB).

          // Jonathan Edwards <edwards@intranet.com> states:
          // DDD gets all confused with gdb radix settings. When
          // it configures gdb it assumes a decimal radix. But
          // if you specify a non-decimal radix, then all
          // settings made after that are incorrect.  I would
          // suggest prefixing "0d" to all decimal numbers.
          if (value.matches(rxint) && atoi(value.chars()) > 1)
            value.prepend("0d");

          os << base << " " << value << '\n';
          break;
      }

      if (base.contains("dbxenv ", 0))
      {
          // Add setting (DBX).
          os << base << " " << value << '\n';
          break;
      }

      if (base == "dir")
      {
          if (flags & SAVE_SESSION)
          {
            // `dir' values are only saved within a session.

            os << base << "\n"      // Clear old value
               << base << " " << value << '\n';
          }
          break;
      }

#if 0                   // FIXME: How do we reset the path within GDB?
      if (base == "path")
      {
          if (flags & SAVE_SESSION)
          {
            // `path' values are only saved within a session.

            os << base << "\n"      // Clear old value
               << base << " " << value << '\n';
          }
          break;
      }
#endif

      // Unknown setting -- ignore
      break;

    case PERL:
    {
// removed for perl-5.8.x
//    if (value == "N/A")
//        value = "";

      string option = base.after(rxwhite);
      bool taboo = false;
      for (int i = 0; !taboo && i < int(XtNumber(perl_taboos)); i++)
          taboo = (option == perl_taboos[i]);

      if (!taboo)
          os << base << '=' << value << '\n';

      break;
    }

    case JDB:
      if (base == "use")
      {
          if (flags & SAVE_SESSION)
          {
            // `use' values are only saved within a session.
            os << base << ' ' << value << '\n';
          }
          break;
      }

      // Any other setting (which ones?)
      os << base << ' ' << value << '\n';
      break;

    case BASH:
    case XDB:
    case PYDB:
    case DBG:
      // Add setting
      os << base << ' ' << value << '\n';
      break;
    }
    
}

// Fetch GDB settings string
string get_settings(DebuggerType type, unsigned long flags)
{
    Widget settings = create_settings(type);
    if (settings == 0)
      return "";

    std::ostringstream command;
    for (int i = 0; i < settings_entries.size(); i++)
    {
      Widget entry = settings_entries[i];
      string value = settings_values[entry];

      get_setting(command, type, XtName(entry), value, flags);
    }

    return string(command);
}

// Fetch GDB signal handling string
string get_signals(DebuggerType type, unsigned long /* flags */)
{
    if (type != GDB)
      return "";        // Not supported yet

    create_signals(type);
    string commands = "";

    for (int i = 0; i < signals_entries.size(); i++)
    {
      Widget entry = signals_entries[i];
      bool set = (signals_values[entry] == "yes");
      commands += handle_command(entry, set) + "\n";
    }

    return commands;
}


//-----------------------------------------------------------------------
// Trace Command Definitions
//-----------------------------------------------------------------------

static bool save_defines_needed = false;
static bool load_defines_needed = true;

// Call this function if command definitions have changed
void set_need_save_defines(bool val)
{
    save_defines_needed = val;
}

bool need_save_defines()
{
    return save_defines_needed;
}

void set_need_load_defines(bool val)
{
    load_defines_needed = val;
}

bool need_load_defines()
{
    return load_defines_needed;
}

static StringStringAssoc defs;

static bool update_define(const string& command, bool undo = false)
{
    // We have a new command - update user buttons.
    clear_help_cache(command);
    update_user_buttons();

    string text = gdb_question("show user " + command);
    if (text == NO_GDB_ANSWER)
      return false;

    if (undo)
    {
      // Add `undoing' command
      undo_buffer.add_command("define " + command + "\n" + 
                        defs[command] + "end");
    }

    // `Undefined' means command is undefined; `Not a user command'
    // means this is a built-in command.
    if (text.contains("Undefined", 0) || text.contains("Not a", 0))
    {
      defs.remove(command);
      return true;
    }

    string def  = "";

    while (!text.empty())
    {
      string line = text.before('\n');
      text        = text.after('\n');

      if (line.length() > 0 && isspace(line[0]))
      {
          line = line.after(rxwhite);
          strip_auto_command_prefix(line);
          def += line + "\n";
      }
    }

    defs[command] = def;
    return true;
}

static void update_defines()
{
    if (!need_load_defines())
      return;

    StatusDelay delay("Retrieving Command Definitions");

    string commands = gdb_question("help user-defined");
    if (commands == NO_GDB_ANSWER)
      return;

    while (!commands.empty())
    {
      string line = commands.before('\n');
      commands    = commands.after('\n');

      if (!line.contains(" -- "))
          continue;                 // No help line

      string command = line.before(" -- ");
      bool ok = update_define(command);
      if (!ok)
          return;
    }

    set_need_load_defines(false);
}

// Get current definitions
string get_defines(DebuggerType type, unsigned long /* flags */)
{
    if (type != GDB)
      return "";        // Not supported yet

    update_defines();

    // We have to get the current value of the `confirm' setting
    create_settings(type);

    string defines = "";

    // We have to turn off confirmation during command (re-)definition
    string confirm_value = "on";
    if (settings_form != 0)
    {
      Widget confirm_w = command_to_widget(settings_form, "set confirm");
      if (confirm_w != 0)
          confirm_value = settings_values[confirm_w];
    }
    if (confirm_value == "on")
      defines += "set confirm off\n";

    for (StringStringAssocIter iter(defs); iter.ok(); ++iter)
    {
      string cmd = iter.key();
      cmd.downcase();         // GDB 5.0 makes all commands lower case

      string def = iter.value();
      if (def.empty())
      {
          // We don't save empty definitions, such that users have a
          // way to get rid of them.
      }
      else
      {
          defines += "define " + cmd + "\n" + def + "end\n";
      }
    }

    if (confirm_value == "on")
      defines += "set confirm on\n";

    return defines;
}

// True iff COMMAND is a defined command
bool is_defined_cmd(const string& command)
{
    if (gdb->type() != GDB)
      return false;           // Not supported yet

    update_defines();

    string cmd = command;
    strip_space(cmd);
    if (cmd.contains(' '))
      cmd = cmd.before(' ');
    cmd.downcase();           // GDB 5.0 makes all commands lower case

    return defs.has(cmd);
}


//-----------------------------------------------------------------------
// Edit Command Definitions
//-----------------------------------------------------------------------

// Data

static Widget name_w;         // Name of defined command
static Widget arg_w;          // `()' toggle
static Widget record_w;       // `Record' button
static Widget end_w;          // `End' button
static Widget edit_w;         // `Edit>>' button
static Widget editor_w;       // Command definition editor
static Widget apply_w;        // `Apply' button

static string current_name()
{
    String name_s = XmTextFieldGetString(name_w);
    string name(name_s);
    XtFree(name_s);
    strip_space(name);
    return name;
}


// Button stuff

// Name of GDB argument
static const string arg0 = "$arg0";

static bool is_arg_command(const string& name)
{
    return defs.has(name) && defs[name].contains(arg0);
}

static void add_button(string name, const _XtString& menu)
{
    if (XmToggleButtonGetState(arg_w) || is_arg_command(name))
      name += " ()";

    string s = menu;
    if (!s.empty() && !s.contains('\n', -1))
      s += '\n';
    s += name + "\n";
    menu = (String)XtNewString(s.chars());
}

static void remove_button(const string& name, const _XtString& menu)
{
    string s = string("\n") + menu;
    s.gsub("\n" + name + "\n", string("\n"));
    s.gsub("\n" + name + " ()\n", string("\n"));
    menu = (String)XtNewString(s.chars() + 1);
}

enum ButtonTarget { ConsoleTarget, SourceTarget, DataTarget };

static const _XtString &target_string(ButtonTarget t)
{
    switch (t)
    {
    case ConsoleTarget:
      return app_data.console_buttons;

    case SourceTarget:
      return app_data.source_buttons;

    case DataTarget:
      return app_data.data_buttons;
    }

    static const _XtString null = 0;
    return null;
}

static void ToggleButtonCB(Widget, XtPointer client_data, XtPointer call_data)
{
    string name = current_name();
    ButtonTarget target = (ButtonTarget) (long) client_data;

    XmToggleButtonCallbackStruct *info = 
      (XmToggleButtonCallbackStruct *)call_data;

    const _XtString& str = target_string(target);

    if (info->set)
    {
      add_button(name, str);
    }
    else
    {
      remove_button(name, str);
    }

    update_user_buttons();
}

static MMDesc button_menu[] =
{
    { "console", MMToggle, 
      { ToggleButtonCB, XtPointer(ConsoleTarget) }, 0, 0, 0, 0},
    { "source",  MMToggle, 
      { ToggleButtonCB, XtPointer(SourceTarget) }, 0, 0, 0, 0},
    { "data",    MMToggle, 
      { ToggleButtonCB, XtPointer(DataTarget) }, 0, 0, 0, 0},
    MMEnd
};

static void refresh_toggle(ButtonTarget t)
{
    Widget& w = button_menu[t].widget;
    string s = string("\n") + target_string(t);
    string name = current_name();

    Boolean old_state;
    XtVaGetValues(w, XmNset, &old_state, XtPointer(0));

    Boolean new_state = 
      s.contains("\n" + name + "\n") || s.contains("\n" + name + " ()\n");
    if (old_state != new_state)
      XtVaSetValues(w, XmNset, new_state, XtPointer(0));

#if 1
    set_sensitive(w, !name.empty());
#else
    string answer = gdbHelp(name);
    if (answer != NO_GDB_ANSWER)
    {
      set_sensitive(w, is_known_command(answer));
    }
#endif
}

static void refresh_toggles()
{
    refresh_toggle(SourceTarget);
    refresh_toggle(DataTarget);
    refresh_toggle(ConsoleTarget);
}

static void refresh_combo_box()
{
    if (name_w == 0)
      return;                 // Not yet created

    // Refresh combo box
    StringArray commands;
    for (StringStringAssocIter iter(defs); iter.ok(); ++iter)
      commands += iter.key();
    smart_sort(commands);
    ComboBoxSetList(name_w, commands);
}

// Editing stuff

// Text field has changed -- update buttons
void UpdateDefinePanelCB(Widget w, XtPointer, XtPointer)
{
    if (name_w == 0)
      return;                 // Not yet created

    string name = current_name();

    set_sensitive(record_w, !gdb->recording() && !name.empty());
    set_sensitive(apply_w,  !gdb->recording() && defs.has(name));
    set_sensitive(end_w,    gdb->recording());
    set_sensitive(edit_w,   !gdb->recording() && !name.empty());

    set_sensitive(name_w, !gdb->recording());
    set_sensitive(XtParent(name_w), !gdb->recording());
    set_sensitive(editor_w, !gdb->recording());

    set_arg();

    if (w != 0 && !gdb->recording() && defs.has(name))
      XmToggleButtonSetState(arg_w, is_arg_command(name), False);

    refresh_toggles();
}

static void update_defineHP(Agent *, void *client_data, void *call_data)
{
    bool gdb_ready = bool(call_data);
    if (gdb_ready && !gdb->recording())
    {
      char *c = (char *)client_data;
      bool ok = update_define(c, true);

      if (ok)
      {
          // Update buttons
          UpdateDefinePanelCB();
          refresh_combo_box();

          // Don't get called again
          gdb->removeHandler(ReadyForQuestion, update_defineHP, client_data);
          delete[] c;
      }
    }
    else
    {
      UpdateDefinePanelCB();
    }
}

// This one is called by send_gdb_command() when seeing `define'
void update_define_later(const string& command)
{
    char *c = new char[command.length() + 1];
    strcpy(c, command.chars());

    gdb->addHandler(ReadyForQuestion, update_defineHP, (void *)c);

    UpdateDefinePanelCB();
}

static void RecordCommandDefinitionCB(Widget w, XtPointer, XtPointer)
{
    string name = current_name();
    gdb_command("define " + name, w);
}

static void EndCommandDefinitionCB(Widget w, XtPointer, XtPointer)
{
    if (gdb->recording())
      gdb_command("end", w);
}

static void DoneEditCommandDefinitionCB(Widget w, XtPointer, XtPointer)
{
    if (!XtIsManaged(XtParent(editor_w)))
      return;

    string name = current_name();

    XtUnmanageChild(XtParent(editor_w));
    set_sensitive(name_w, true);
    set_sensitive(XtParent(name_w), true);

    MString label = "Edit " + MString(">>", CHARSET_SMALL);
    set_label(edit_w, label);

    String _commands = XmTextGetString(editor_w);
    string cmd = _commands;
    XtFree(_commands);

    if (!cmd.contains('\n', -1))
      cmd += '\n';

    if ((!cmd.empty() && !defs.has(name)) || cmd != defs[name])
    {
      StringArray commands;
      while (!cmd.empty())
      {
          string c = cmd.before('\n');
          if (!c.empty())
            commands += c;
          cmd = cmd.after('\n');
      }

      // This might require confirmation.  Don't change anything.
      set_sensitive(edit_w,   false);
      set_sensitive(record_w, false);
      set_sensitive(end_w,    false);

      gdb_command("define " + name, w);
      for (int j = 0; j < commands.size(); j++)
          gdb_command(commands[j], w);
      gdb_command("end", w);

      update_define_later(name);
    }
}

static void EditCommandDefinitionCB(Widget, XtPointer, XtPointer)
{
    if (XtIsManaged(XtParent(editor_w)))
      return;

    string name = current_name();

    // update_define(name);
    set_sensitive(name_w, false);
    set_sensitive(XtParent(name_w), false);

    string def = "";
    if (defs.has(name))
      def = defs[name];

    XmTextSetString(editor_w, XMST(def.chars()));

    XtManageChild(XtParent(editor_w));
    MString label = "Edit " + MString("<<", CHARSET_SMALL);
    set_label(edit_w, label);
}

static void ToggleEditCommandDefinitionCB(Widget w, XtPointer client_data, 
                                XtPointer call_data)
{
    if (XtIsManaged(XtParent(editor_w)))
      DoneEditCommandDefinitionCB(w, client_data, call_data);
    else
      EditCommandDefinitionCB(w, client_data, call_data);
}

// Apply the given command
static void ApplyCB(Widget w, XtPointer client_data, XtPointer call_data)
{
    if (gdb->recording())
      EndCommandDefinitionCB(w, client_data, call_data);

    DoneEditCommandDefinitionCB(w, client_data, call_data);

    string cmd = current_name();
    if (!cmd.empty())
    {
      if (XmToggleButtonGetState(arg_w))
          cmd += " " + source_arg->get_string();

      gdb_command(cmd, w);
    }
}

// Force argument to `()'
static void ForceArg0HP(void *, void *, void *)
{
    static bool called = false;
    if (called)
      return;

    called = true;
    set_arg();
    called = false;
}

static void set_arg()
{
    static string saved_arg;
    static bool have_saved_arg = false;

    if (gdb->recording() && XmToggleButtonGetState(arg_w))
    {
      if (!have_saved_arg)
      {
          saved_arg = source_arg->get_string();
          have_saved_arg = true;

          source_arg->set_string(arg0);

          reset_status_lock();
          MString msg = 
            rm("Using ") + bf("()") + rm(" as symbolic argument");
          set_status_mstring(msg);
      }

      source_arg->addHandler(Changed, ForceArg0HP);
      source_arg->lock();
    }
    else
    {
      source_arg->removeHandler(Changed, ForceArg0HP);
      source_arg->lock(false);

      if (have_saved_arg)
      {
          source_arg->set_string(saved_arg);
          have_saved_arg = false;

          reset_status_lock();
          MString msg = 
            rm("Using ") + bf("()") + rm(" as literal argument");
          set_status_mstring(msg);
      }
    }
}

static void ToggleArgCB(Widget, XtPointer, XtPointer)
{
    set_arg();
}

static MMDesc commands_menu[] =
{
    { "record", MMPush, 
      { RecordCommandDefinitionCB, 0 }, 0, &record_w, 0, 0 },
    { "end",    MMPush | MMInsensitive, 
      { EndCommandDefinitionCB, 0 }, 0, &end_w, 0, 0},
    { "edit",   MMPush, 
      { ToggleEditCommandDefinitionCB, 0 }, 0, &edit_w, 0, 0 },
    MMEnd
};

static MMDesc name_menu[] =
{
    { "name",     MMComboBox | MMUnmanagedLabel, 
      { UpdateDefinePanelCB, 0 }, 0, &name_w, 0, 0 },
    { "arg",      MMToggle, { ToggleArgCB, 0 }, 0, &arg_w, 0, 0 },
    MMEnd
};

static MMDesc panel_menu[] = 
{
    { "name",     MMButtonPanel, MMNoCB, name_menu, 0, 0, 0 },
    { "commands", MMButtonPanel, MMNoCB, commands_menu, 0, 0, 0 },
    { "button",   MMButtonPanel, MMNoCB, button_menu, 0, 0, 0 },
    MMEnd
};


// Define command
void dddDefineCommandCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 0;

    if (dialog == 0)
    {
      Arg args[10];
      int arg = 0;
      XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
      dialog = verify(XmCreatePromptDialog(find_shell(w),
                                   XMST("define_command"),
                                   args, arg));

      // Remove old prompt
      Widget text = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
      XtUnmanageChild(text);
      Widget old_label = 
          XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL);
      XtUnmanageChild(old_label);

      apply_w = XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON);
      XtVaSetValues(dialog, XmNdefaultButton, apply_w, XtPointer(0));
      XtManageChild(apply_w);

      XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_OK_BUTTON));

      Delay::register_shell(dialog);

      arg = 0;
      XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
      Widget form = XmCreateRowColumn(dialog, XMST("form"), args, arg);
      XtManageChild(form);

      Widget panel = MMcreatePanel(form, "panel", panel_menu);

      XtVaSetValues(panel,
                  XmNmarginWidth,    0,
                  XmNmarginHeight,   0,
                  XtPointer(0));

      arg = 0;
      XtSetArg(args[arg], XmNeditMode, XmMULTI_LINE_EDIT); arg++;
        editor_w = XmCreateScrolledText(form, XMST("text"), args, arg);
      XtUnmanageChild(XtParent(editor_w));
      XtManageChild(editor_w);

      MMaddCallbacks(panel_menu);
      InstallButtonTips(panel);

      MMadjustPanel(panel_menu);

      XtAddCallback(dialog, XmNokCallback, UnmanageThisCB, 
                  XtPointer(dialog));
      XtAddCallback(dialog, XmNokCallback, DoneEditCommandDefinitionCB, 
                  XtPointer(0));

      XtAddCallback(dialog, XmNapplyCallback,
                  ApplyCB, XtPointer(0));
      XtAddCallback(dialog, XmNcancelCallback, 
                  EndCommandDefinitionCB, XtPointer(0));
      XtAddCallback(dialog, XmNcancelCallback, UnmanageThisCB, 
                  XtPointer(dialog));

      XtAddCallback(dialog, XmNhelpCallback,
                  ImmediateHelpCB, XtPointer(0));

      set_need_load_defines(true);
      update_defines();
    }

    UpdateDefinePanelCB();
    refresh_combo_box();
    manage_and_raise(dialog);
}

Generated by  Doxygen 1.6.0   Back to index