home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / buttons.C < prev    next >
C/C++ Source or Header  |  1998-11-24  |  40KB  |  1,616 lines

  1. // $Id: buttons.C,v 1.123 1998/11/24 15:08:29 zeller Exp $ -*- C++ -*-
  2. // DDD buttons
  3.  
  4. // Copyright (C) 1996-1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char buttons_rcsid[] = 
  30.     "$Id: buttons.C,v 1.123 1998/11/24 15:08:29 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include "buttons.h"
  37.  
  38. #include "AppData.h"
  39. #include "Command.h"
  40. #include "DataDisp.h"
  41. #include "Delay.h"
  42. #include "DestroyCB.h"
  43. #include "GDBAgent.h"
  44. #include "HelpCB.h"
  45. #include "LessTifH.h"
  46. #include "MakeMenu.h"
  47. #include "SourceView.h"
  48. #include "StringSA.h"
  49. #include "TimeOut.h"
  50. #include "UndoBuffer.h"
  51. #include "args.h"
  52. #include "bool.h"
  53. #include "charsets.h"
  54. #include "comm-manag.h"
  55. #include "cook.h"
  56. #include "ctrl.h"
  57. #include "ddd.h"
  58. #include "disp-read.h"
  59. #include "editing.h"
  60. #include "fortranize.h"
  61. #include "history.h"
  62. #include "isid.h"
  63. #include "question.h"
  64. #include "regexps.h"
  65. #include "select.h"
  66. #include "settings.h"
  67. #include "shorten.h"
  68. #include "source.h"
  69. #include "status.h"
  70. #include "string-fun.h"
  71. #include "verify.h"
  72. #include "windows.h"
  73. #include "wm.h"
  74.  
  75. #include <Xm/Xm.h>
  76. #include <Xm/Label.h>
  77. #include <Xm/Frame.h>
  78. #include <Xm/RowColumn.h>
  79. #include <Xm/SelectioB.h>
  80. #include <Xm/PushB.h>
  81. #include <Xm/ToggleB.h>
  82. #include <Xm/Text.h>
  83. #include <ctype.h>
  84.  
  85.  
  86. //-----------------------------------------------------------------------------
  87. // Data
  88. //-----------------------------------------------------------------------------
  89.  
  90. // Maximum length of value in value tip and in status line
  91. int max_value_tip_length = 20;
  92. int max_value_doc_length = 128;
  93.  
  94.  
  95. //-----------------------------------------------------------------------------
  96. // Button callbacks
  97. //-----------------------------------------------------------------------------
  98.  
  99. static void YnButtonCB(Widget dialog, 
  100.                XtPointer client_data, 
  101.                XtPointer call_data)
  102. {
  103.     _gdb_out(string((char *)client_data) + '\n');
  104.     gdbCommandCB(dialog, client_data, call_data);
  105.     gdb_keyboard_command = true;
  106. }
  107.  
  108.  
  109.  
  110. //-----------------------------------------------------------------------------
  111. // Version stuff
  112. //-----------------------------------------------------------------------------
  113.  
  114. // Return true iff old button separators (`:') are used.  These were
  115. // introduced in DDD 1997-10 or 2.1.
  116. static bool old_button_format()
  117. {
  118.     if (app_data.dddinit_version == 0)
  119.     return true;
  120.     string v = app_data.dddinit_version;
  121.     int major = atoi(v);
  122.  
  123.     if (major > 1900)
  124.     {
  125.     // YYYY-MM-DD format
  126.     if (major <= 1996)
  127.         return true;    // 1996 or earlier
  128.     if (major >= 1998)
  129.         return false;    // 1998 or later
  130.  
  131.     assert(major == 1997);
  132.     string v2 = v.after('-');
  133.     int minor = atoi(v2);
  134.     if (minor <= 9)        // 1997-09 or earlier
  135.         return true;
  136.  
  137.     return false;        // 1997-10 or later
  138.     }
  139.     else
  140.     {
  141.     // MAJOR.MINOR format
  142.     if (major <= 1)
  143.         return true;    // 1.x or earlier
  144.     if (major >= 3)
  145.         return false;    // 3.x or later
  146.  
  147.     assert(major == 2);
  148.     string v2 = v.after('.');
  149.     int minor = atoi(v2);
  150.     if (minor <= 1)
  151.         return true;    // 2.1 or earlier
  152.  
  153.     return false;        // 2.2 or later
  154.     }
  155. }
  156.  
  157.  
  158. //-----------------------------------------------------------------------------
  159. // Show documentation string in status line
  160. //-----------------------------------------------------------------------------
  161.  
  162. static void showDocumentationInStatusLine(const MString& doc)
  163. {
  164.     static MString current_status_message(0, true);
  165.     static MString saved_status_message(0, true);
  166.     static bool doc_shown_in_status = false;
  167.  
  168.     if (doc.isNull() || doc.isEmpty())
  169.     {
  170.     // Button has been left - restore previous message unless overwritten
  171.     if (!current_status_message.isNull())
  172.     {
  173.         // Button has been left
  174.         if (current_status_message == current_status())
  175.         {
  176.         // Restore previous message
  177.         set_status_mstring(saved_status_message, true);
  178.         }
  179.         else
  180.         {
  181.         // Message has been overwritten.
  182. #if 0 
  183.         // This is a button effect, hence clear the message.
  184.         set_status("");
  185. #endif
  186.         }
  187.         static MString empty(0, true);
  188.         current_status_message = empty;
  189.     }
  190.  
  191.     doc_shown_in_status = false;
  192.     }
  193.     else
  194.     {
  195.     // Button has been entered - save old message
  196.     if (!doc_shown_in_status)
  197.     {
  198.         saved_status_message = current_status();
  199.         doc_shown_in_status = true;
  200.     }
  201.  
  202.     set_status_mstring(doc, true);
  203.     current_status_message = doc;
  204.     }
  205. }
  206.  
  207.  
  208. //-----------------------------------------------------------------------------
  209. // Default help texts (especially buttons)
  210. //-----------------------------------------------------------------------------
  211.  
  212. const int help_timeout = 2;    // Timeout for short queries (in s)
  213.  
  214. static StringStringAssoc help_cache;
  215.  
  216. static string gdbHelpName(Widget widget)
  217. {
  218.     string name = XtName(widget);
  219.     name.gsub('_', ' ');
  220.     strip_trailing_space(name);
  221.  
  222.     return name;
  223. }
  224.  
  225. static string gdbHelp(string original_command)
  226. {
  227.     translate_command(original_command);
  228.  
  229.     string command = original_command;
  230.     if (gdb->type() == JDB && original_command == "next")
  231.     {
  232.     // JDB 1.1 has an undocumented `next' command.  Treat it like `step'.
  233.     command = "step";
  234.     }
  235.  
  236.     if (gdb->type() == DBX && original_command == "step up")
  237.     {
  238.     // Don't ask for `step up'; ask for `step' instead.
  239.     command = "step";
  240.     }
  241.  
  242.     if (gdb->type() == PERL)
  243.     {
  244.     // Perl help has only one argument
  245.     if (command.contains(rxwhite))
  246.         command = command.before(rxwhite);
  247.     }
  248.  
  249.     string help = NO_GDB_ANSWER;
  250.  
  251.     if (is_graph_cmd(command))
  252.     {
  253.     // Use own help texts
  254.     string cmd = command.after("graph ") + " dummy";
  255.     if (is_refresh_cmd(cmd))
  256.         help = "Refresh data window.";
  257.     else if (is_display_cmd(cmd))
  258.         help = "Display expression in the data window.";
  259.     }
  260.  
  261.     if (command == "graph" || command.contains("graph ", 0))
  262.     help = "Data window operation.";
  263.  
  264.     if (help == NO_GDB_ANSWER)
  265.     {
  266.     // Lookup cache
  267.     if (help_cache.has(command))
  268.         help = help_cache[command];
  269.     }
  270.  
  271.     if (help == NO_GDB_ANSWER && gdb->type() == JDB)
  272.     {
  273.     // JDB has a single static `help' command.
  274.     string& all_help = help_cache["<ALL>"];
  275.     if (all_help == NO_GDB_ANSWER || all_help == "")
  276.     {
  277.         all_help = gdb_question("help", -1, true);
  278.         if (all_help == NO_GDB_ANSWER)
  279.         return NO_GDB_ANSWER; // try again later
  280.     }
  281.  
  282.     int index = all_help.index("\n" + command) + 1;
  283.     if (all_help.contains(command, index))
  284.     {
  285.         help = all_help.from(index);
  286.         help = help.after("--");
  287.         help = help.before('\n');
  288.     }
  289.     else
  290.     {
  291.         help = "Undefined command: " + quote(command) 
  292.         + ".  Try \"help\"\n";
  293.     }
  294.     }
  295.  
  296.     if (help == NO_GDB_ANSWER && gdb->type() == DBX)
  297.     {
  298.     string cmd  = command.before(rxwhite);
  299.     string base = command.after(rxwhite);
  300.     base.gsub(' ', '_');
  301.  
  302.     if (cmd == "set" || cmd == "dbxenv")
  303.     {
  304.         // Ask DBX for help on DBXENV command
  305.         help = get_dbx_help(cmd, base);
  306.     }
  307.     }
  308.  
  309.     if (help == NO_GDB_ANSWER && gdb->type() != JDB)
  310.     {
  311.     // Ask debugger for help
  312.     string help_command;
  313.     if (gdb->type() == PERL)
  314.         help_command = "h " + command;
  315.     else
  316.         help_command = "help " + command;
  317.  
  318.     help = gdb_question(help_command, help_timeout, true);
  319.     }
  320.  
  321.     strip_space(help);
  322.  
  323.     // `step up' help is part of `step' help.
  324.     if (original_command == "step up" && !help.contains(original_command))
  325.     return "undefined command";
  326.  
  327.     if (help != NO_GDB_ANSWER)
  328.     help_cache[command] = help;
  329.  
  330.     return help;
  331. }
  332.  
  333. void clear_help_cache(const string& command)
  334. {
  335.     if (help_cache.has(command))
  336.     help_cache.remove(command);
  337. }
  338.  
  339.  
  340. // Return DBX 3.2 one-liner help.
  341. static string gdbTip(string command)
  342. {
  343.     static bool entered = false;
  344.     if (entered)
  345.     return NO_GDB_ANSWER;
  346.     entered = true;
  347.  
  348.     string tip = NO_GDB_ANSWER;
  349.  
  350.     if (gdb->type() == DBX)
  351.     {
  352.     static string commands = NO_GDB_ANSWER;
  353.     if (commands == NO_GDB_ANSWER)
  354.     {
  355.         commands = gdb_question("commands", -1, true);
  356.         if (!is_known_command(commands))
  357.         commands = "";
  358.     }
  359.  
  360.     int start = 0;
  361.     for (;;)
  362.     {
  363.         int i = commands.index(command, start);
  364.         if (i < 0)
  365.         break;
  366.         while (i > 0 
  367.            && commands[i - 1] != '\n' 
  368.            && isspace(commands[i - 1]))
  369.         i--;
  370.         if (i == 0 || commands[i - 1] == '\n')
  371.         {
  372.         string t = commands.from(i);
  373.         t = t.before('\n');
  374.         strip_space(t);
  375.  
  376.         if (t.length() > command.length()) // Simple sanity check
  377.             tip = t;
  378.         break;
  379.         }
  380.  
  381.         start = i + 1;
  382.     }
  383.     }
  384.  
  385.     entered = false;
  386.     return tip;
  387. }
  388.  
  389.  
  390. static string gdbSettingsValue(string command)
  391. {
  392.     switch (gdb->type())
  393.     {
  394.     case GDB:
  395.     if (command.contains("set ", 0))
  396.     {
  397.         string value = gdb_question("show " + command.after(rxwhite));
  398.         if (!value.contains("current"))
  399.         value.gsub(" is ", " is currently ");
  400.         strip_trailing_space(value);
  401.         return value;
  402.     }
  403.     break;
  404.  
  405.     case DBX:
  406.     case XDB:
  407.     case JDB:
  408.     case PYDB:
  409.     case PERL:
  410.     return NO_GDB_ANSWER;        // FIXME
  411.     }
  412.  
  413.     return NO_GDB_ANSWER;
  414. }
  415.  
  416. static MString gdbDefaultHelpText(Widget widget)
  417. {
  418.     const string name = gdbHelpName(widget);
  419.  
  420.     MString msg;
  421.     if (name != "" && islower(name[0]))
  422.     msg = bf(toupper(name[0]) + name.after(0));
  423.     else
  424.     msg = bf(name);
  425.     msg += cr();
  426.     msg += cr();
  427.  
  428.     string help = gdbHelp(name);
  429.     if (help == NO_GDB_ANSWER)
  430.     {
  431.     msg += rm("No help available now.") + cr();
  432.     msg += rm("Please try again when " +  gdb->title() + " is ready.");
  433.     }
  434.     else
  435.     {
  436.     msg += rm(help);
  437.  
  438.     // Add current settings state, if any
  439.     string state = gdbSettingsValue(name);
  440.     if (state != NO_GDB_ANSWER)
  441.     {
  442.         msg += cr();
  443.         msg += cr();
  444.         msg += rm(state);
  445.     }
  446.     }
  447.  
  448.     return msg;
  449. }
  450.  
  451.  
  452. static StringStringAssoc value_cache;
  453.  
  454. void clear_value_cache()
  455. {
  456.     static StringStringAssoc empty;
  457.     value_cache = empty;
  458. }
  459.  
  460. string gdbValue(const string& expr)
  461. {
  462.     if (undo_buffer.showing_earlier_state())
  463.     return NO_GDB_ANSWER;    // We don't know about earlier values
  464.  
  465.     string value = NO_GDB_ANSWER;
  466.     if (value == NO_GDB_ANSWER)
  467.     {
  468.     // Lookup cache
  469.     if (value_cache.has(expr))
  470.         value = value_cache[expr];
  471.     }
  472.  
  473.     if (value == NO_GDB_ANSWER)
  474.     {
  475.     // Ask debugger for value.  In case of secondary prompts, use
  476.     // the default choice.
  477.     gdb->removeHandler(ReplyRequired, gdb_selectHP);
  478.     value = gdb_question(gdb->print_command(expr), help_timeout);
  479.     if (value != NO_GDB_ANSWER)
  480.         gdb->munch_value(value, expr);
  481.     gdb->addHandler(ReplyRequired, gdb_selectHP);
  482.  
  483.     strip_space(value);
  484.     }
  485.  
  486.     if (value != NO_GDB_ANSWER)
  487.     value_cache[expr] = value;
  488.  
  489.     return value;
  490. }
  491.  
  492. string assignment_value(const string& expr)
  493. {
  494.     if (expr == NO_GDB_ANSWER)
  495.     return NO_GDB_ANSWER;
  496.  
  497.     string value = expr;
  498.  
  499.     // Replace whitespace
  500. #if RUNTIME_REGEX
  501.     static regex rxnl(" *\n *");
  502. #endif
  503.     value.gsub(rxnl, " ");
  504.     value.gsub("\n", " ");
  505.     value.gsub("\t", " ");
  506.     value.gsub("  ", " ");
  507.  
  508.     // Strip member name from structs
  509.     int eq_index = -1;
  510.     while ((eq_index = value.index(" = ")) >= 0)
  511.     {
  512.     int member_name_index = eq_index;
  513.     while (member_name_index > 0 &&
  514.            value[member_name_index - 1] != '{' &&
  515.            value[member_name_index - 1] != '(' &&
  516.            value[member_name_index - 1] != ',')
  517.         member_name_index--;
  518.  
  519.     if (member_name_index > 0 &&
  520.         value[member_name_index - 1] == ',')
  521.     {
  522.         // Keep the space after ','
  523.         while (member_name_index < eq_index && 
  524.            isspace(value[member_name_index]))
  525.         member_name_index++;
  526.     }
  527.  
  528.     value = value.before(member_name_index) + value.from(eq_index + 3);
  529.     }
  530.  
  531.     strip_space(value);
  532.  
  533.     return value;
  534. }
  535.  
  536. static void strip_through(string& s, string key)
  537. {
  538.     int key_index = s.index(key);
  539.     int nl_index  = s.index('\n');
  540.  
  541.     if (key_index >= 0 && (nl_index < 0 || key_index < nl_index))
  542.     s = s.from(int(key_index + key.length()));
  543. }
  544.  
  545. static XmTextPosition textPosOfEvent(Widget widget, XEvent *event)
  546. {
  547.     XmTextPosition startpos, endpos;
  548.     string expr = 
  549.     source_view->get_word_at_event(widget, event, startpos, endpos);
  550.  
  551. #if 0                // We might point at a text breakpoint
  552.     if (expr == "")
  553.     return XmTextPosition(-1);
  554. #endif
  555.  
  556.     return startpos;
  557. }
  558.  
  559. // Get tip string for text widget WIDGET.
  560. static MString gdbDefaultValueText(Widget widget, XEvent *event, 
  561.                    bool for_documentation)
  562. {
  563.     assert (XmIsText(widget));
  564.  
  565.     XmTextPosition startpos, endpos;
  566.     string expr = 
  567.     source_view->get_word_at_event(widget, event, startpos, endpos);
  568.  
  569. #if LOG_VALUE_TIPS
  570.     clog << "Pointing at " << quote(expr) << "\n";
  571. #endif
  572.  
  573.     // If we're at a breakpoint, return appropriate help
  574.     MString bp_help = 
  575.     source_view->help_on_pos(widget, startpos, endpos, for_documentation);
  576.  
  577.     if (bp_help.xmstring() == 0 && expr == "")
  578.     return MString(0, true); // Nothing pointed at
  579.  
  580. #if RUNTIME_REGEX
  581.     static regex rxchain("[a-zA-Z0-9_](([.]|->|::)[a-zA-Z0-9_])*");
  582. #endif
  583.  
  584.     // Don't invoke the debugger if EXPR is not an identifier.
  585.     // Otherwise, we might point at `i++' or `f()' and have weird side
  586.     // effects.
  587.     MString clear = for_documentation ? rm(" ") : MString(0, true);
  588.     if (bp_help.xmstring() == 0 && !expr.matches(rxchain))
  589.     return clear;
  590.  
  591.     // Change EVENT such that the popup tip will remain at the same
  592.     // position
  593.     Position x, y;
  594.     if (XmTextPosToXY(widget, endpos, &x, &y))
  595.     {
  596.     switch (event->type)
  597.     {
  598.     case MotionNotify:
  599.         event->xmotion.x = x;
  600.         event->xmotion.y = y;
  601.         break;
  602.  
  603.     case EnterNotify:
  604.     case LeaveNotify:
  605.         event->xcrossing.x = x;
  606.         event->xcrossing.y = y;
  607.         break;
  608.     }
  609.     }
  610.  
  611.     if (bp_help.xmstring() != 0)
  612.     return bp_help;
  613.  
  614.     if (gdb->program_language() == LANGUAGE_PERL)
  615.     {
  616.     // In Perl, all variables begin with `$', `@', or `%'.
  617.     if (expr != "" &&  !is_perl_prefix(expr[0]))
  618.         return clear;
  619.     }
  620.  
  621.     // Get value of ordinary variable
  622.     string name = fortranize(expr);
  623.     string tip = gdbValue(name);
  624.     if (tip == NO_GDB_ANSWER)
  625.     return MString(0, true);
  626.  
  627.     if (!is_valid(tip, gdb) && widget == source_view->code())
  628.     {
  629.     // Get register value - look up `$pc' when pointing at `pc'
  630.     name = expr;
  631.     name.prepend("$");
  632.     tip = gdbValue(name);
  633.     if (tip == NO_GDB_ANSWER)
  634.         return MString(0, true);
  635.  
  636.     if (tip.matches(rxint))
  637.     {
  638.         // Show hex value as well.  We don't do a local
  639.         // conversion here, but ask GDB instead, since the hex
  640.         // format may be language-dependent.
  641.         string hextip = gdbValue("/x " + name);
  642.         if (hextip != NO_GDB_ANSWER)
  643.         tip = hextip + " (" + tip + ")";
  644.     }
  645.     }
  646.  
  647.     if (!is_valid(tip, gdb))
  648.     return clear;
  649.  
  650.     tip = get_disp_value_str(tip, gdb);
  651.     if (tip == "void")
  652.     return clear;        // Empty variable
  653.  
  654. #if 0
  655.     if (gdb->program_language() == LANGUAGE_PERL && tip == "")
  656.     tip = "undef";
  657. #endif
  658.  
  659.     if (for_documentation)
  660.     {
  661.     shorten(tip, max_value_doc_length - name.length());
  662.  
  663.     // The status line also shows the name we're pointing at
  664.     MString mtip = tt(tip);
  665.     mtip.prepend(rm(name + " = "));
  666.     return mtip;
  667.     }
  668.     else
  669.     {
  670.     // The value tip just shows the value
  671.     shorten(tip, max_value_tip_length);
  672.     return tt(tip);
  673.     }
  674. }
  675.  
  676. // Get tip string for button widget WIDGET.
  677. static MString gdbDefaultButtonText(Widget widget, XEvent *, 
  678.                     bool for_documentation)
  679. {
  680.     MString bp_help = source_view->help_on_glyph(widget, for_documentation);
  681.     if (!bp_help.isNull())
  682.     return bp_help;
  683.  
  684.     MString shortcut_help = data_disp->shortcut_help(widget);
  685.     if (!shortcut_help.isNull())
  686.     return shortcut_help;
  687.  
  688.     string help_name = gdbHelpName(widget);
  689.  
  690.     string command = help_name;
  691.     translate_command(command);
  692.     string base = command;
  693.     if (base.contains(' '))
  694.     base = command.before(' ');
  695.  
  696.     if (help_name.length() == 2 && 
  697.     help_name[0] == 'r' && isdigit(help_name[1]))
  698.     {
  699.     // Return help on `recent file' item
  700.     StringArray recent_files;
  701.     get_recent(recent_files);
  702.     int index = help_name[1] - '1';
  703.     if (index >= 0 && index < recent_files.size())
  704.         return rm(recent_files[index]);
  705.     }
  706.  
  707.     if (help_name == "Undo")
  708.     {
  709.     string action = undo_buffer.undo_action();
  710.     if (action != NO_GDB_ANSWER)
  711.         return rm("Undo " + action);
  712.     else
  713.         return rm("Undo last action");
  714.     }
  715.  
  716.     if (help_name == "Redo")
  717.     {
  718.     string action = undo_buffer.redo_action();
  719.     if (action != NO_GDB_ANSWER)
  720.         return rm("Redo " + action);
  721.     else
  722.         return rm("Redo next action");
  723.     }
  724.  
  725.     string tip = NO_GDB_ANSWER;
  726.     if (tip == NO_GDB_ANSWER)
  727.     tip = gdbTip(help_name);
  728.     if (tip == NO_GDB_ANSWER)
  729.     tip = gdbHelp(help_name);
  730.  
  731.     if (tip == NO_GDB_ANSWER)
  732.     return MString(0, true);
  733.  
  734.     strip_leading_space(tip);
  735.  
  736.     if (gdb->type() == DBX)
  737.     {
  738.     if (!tip.contains(command, 0))
  739.     {
  740.         // Sometimes, multiple command are listed in one help text.
  741.         // Be sure to fetch the correct variant.
  742.         int index = tip.index("\n" + command);
  743.         if (index > 0)
  744.         tip = tip.after(index);
  745.  
  746.         // Fix Sun DBX `step up' help
  747.         tip.gsub("... and ", "Step ");
  748.     }
  749.  
  750.     if (tip.contains(command + " -", 0))
  751.     {
  752.         // Solaris DBX first describes `kill -l', then `kill'.
  753.         // Check for this.
  754.         string t = tip.from(command + "  ", 0);
  755.         if (t != "")
  756.         tip = t;
  757.     }
  758.     }
  759.  
  760.     if (gdb->type() == PYDB)
  761.     {
  762.     // PYDB states the command in the first line
  763.     tip = tip.after('\n');
  764.     strip_leading_space(tip);
  765.     }
  766.  
  767.     if (gdb->type() == PERL)
  768.     {
  769.     // In Perl, the help has the form `COMMAND\tTEXT'.
  770.     tip = tip.after('\t');
  771.     strip_leading_space(tip);
  772.  
  773.     if (tip.contains('['))
  774.         tip = tip.before('[');
  775.     }
  776.  
  777.     // DBX (and others) restate the command name at the beginning.
  778.     if (tip.contains(command, 0))
  779.     {
  780.     string t = tip.after(command);
  781.     if (t != "" && !isalpha(t[0]))
  782.     {
  783.         tip = t;
  784.         strip_leading_space(tip);
  785.     }
  786.     }
  787.     
  788.     strip_through(tip, " # ");
  789.     strip_through(tip, " - ");
  790.  
  791.     if (gdb->type() == DBX && tip != "" && !isupper(tip[0]))
  792.     {
  793.     // Avoid giving help like `step <count>' on `step'.  This happens
  794.     // with AIX DBX, where the help looks like
  795.     // "run [<arguments>] [< <filename>] [> <filename>] \n"
  796.     // "                    [>> <filename>] [>! <filename>] \n"
  797.     // "                    [2> <filename>] [2>> <filename>] \n"
  798.     // "                    [>& <filename>] [>>& <filename>] \n"
  799.     // "\tStart executing the object file, passing arguments as\n"
  800.     // "\tcommand line arguments [...]"
  801.  
  802.     string t = tip.from(rxuppercase);
  803.     if (t.contains("\n" + base))
  804.         t = t.before("\n" + base);
  805.  
  806.     t.gsub('\n', ' ');
  807.     t.gsub('\t', ' ');
  808.     t.gsub("  ", " ");
  809.  
  810.     if (t.contains('.'))
  811.         t = t.before('.');
  812.     if (t.contains(';'))
  813.         t = t.before(';');
  814.  
  815.     // Fix AIX `down' help
  816.     t.gsub(", which is used for resolving names,", "");
  817.  
  818.     if (t.length() > 0)
  819.         tip = t;
  820.     }
  821.  
  822.     if (gdb->type() == XDB)
  823.     {
  824.     // Get rid of XXX [number] as in `S [number] Single step, step...'
  825.     // Bob Wiegand <robert.e.wiegand.1@gsfc.nasa.gov>
  826.     strip_through(tip, "]");
  827.     }
  828.  
  829.     tip = tip.from(rxalpha);
  830.     strip_space(tip);
  831.  
  832.     if (tip.length() > 0)
  833.     tip = toupper(tip[0]) + tip.after(0);
  834.  
  835.     if (tip.contains('\n'))
  836.     tip = tip.before('\n');
  837.     if (tip.contains('.'))
  838.     tip = tip.before('.');
  839.     if (tip.contains(';'))
  840.     tip = tip.before(';');
  841.  
  842.     // Sun DBX 3.2 sometimes forgets the newline after the 80th character
  843.     if (tip.length() > 80)
  844.     tip = tip.before(80);
  845.  
  846.     return rm(tip);
  847. }
  848.  
  849.  
  850. static MString gdbDefaultText(Widget widget, XEvent *event, 
  851.                   bool for_documentation)
  852. {
  853.     if (XmIsText(widget))
  854.     return gdbDefaultValueText(widget, event, for_documentation);
  855.     else
  856.     return gdbDefaultButtonText(widget, event, for_documentation);
  857. }
  858.  
  859. static MString gdbDefaultTipText(Widget widget, XEvent *event)
  860. {
  861.     return gdbDefaultText(widget, event, false);
  862. }
  863.  
  864. static MString gdbDefaultDocumentationText(Widget widget, XEvent *event)
  865. {
  866.     return gdbDefaultText(widget, event, true);
  867. }
  868.  
  869.  
  870. //-----------------------------------------------------------------------------
  871. // Button Verification
  872. //-----------------------------------------------------------------------------
  873.  
  874. // Buttons to be verified
  875. static WidgetArray buttons_to_be_verified;
  876.  
  877.  
  878. static void VerifyButtonWorkProc(XtPointer client_data, XtIntervalId *id)
  879. {
  880.     (void) id;            // Use it
  881.  
  882.     XtIntervalId& verify_id = *((XtIntervalId *)client_data);
  883.     assert(*id == verify_id);
  884.     verify_id = 0;
  885.  
  886.     for (int i = 0; i < buttons_to_be_verified.size(); i++)
  887.     {
  888.     Widget& button = buttons_to_be_verified[i];
  889.     if (button == 0)
  890.         continue;
  891.  
  892.     XtCallbackList callbacks = 0;
  893.     XtVaGetValues(button, 
  894.               XmNactivateCallback, &callbacks,
  895.               NULL);
  896.  
  897.     for (int j = 0; callbacks != 0 && callbacks[j].callback != 0; j++)
  898.     {
  899.         string cmd = "";
  900.  
  901.         if (callbacks[j].callback == gdbCommandCB)
  902.         {
  903.         cmd = (String)(callbacks[j].closure);
  904.         cmd = cmd.through(rxidentifier);
  905.         }
  906.  
  907.         if (cmd != "")
  908.         {
  909.         int next_invocation = 0;
  910.         XtAppContext app_context = 
  911.             XtWidgetToApplicationContext(button);
  912.         
  913.         string answer;
  914.         if (!emptyCommandQueue())
  915.         {
  916.             // Still commands in queue - try later
  917.             answer = NO_GDB_ANSWER;
  918.         }
  919.         else
  920.         {
  921.             answer = gdbHelp(cmd);
  922.         }
  923.  
  924.         if (answer == NO_GDB_ANSWER)
  925.         {
  926.             // No answer -- try again later
  927.             next_invocation = 250;
  928.         }
  929.         else
  930.         {
  931.             set_sensitive(button, is_known_command(answer));
  932.             button = 0;               // Don't process this one again
  933.             next_invocation = 50;  // Process next button in 50ms
  934.         }
  935.  
  936.         verify_id = XtAppAddTimeOut(app_context, next_invocation,
  937.                         VerifyButtonWorkProc, 
  938.                         client_data);
  939.         return;
  940.         }
  941.     }
  942.     }
  943. }
  944.  
  945. static void DontVerifyButtonCB(Widget w, XtPointer, XtPointer)
  946. {
  947.     // W is being destroyed - remove all references
  948.     for (int i = 0; i < buttons_to_be_verified.size(); i++)
  949.     if (buttons_to_be_verified[i] == w)
  950.         buttons_to_be_verified[i] = 0;
  951. }
  952.  
  953. // Make BUTTON insensitive if it is not supported
  954. void verify_button(Widget button)
  955. {
  956.     if (!app_data.verify_buttons)
  957.     return;
  958.     if (button == 0)
  959.     return;
  960.     if (!XtIsSubclass(button, xmPushButtonWidgetClass))
  961.     return;
  962.  
  963. #if 0
  964.     set_sensitive(button, False);
  965. #endif
  966.  
  967.     buttons_to_be_verified += button;
  968.     XtAddCallback(button, XtNdestroyCallback, 
  969.           DontVerifyButtonCB, XtPointer(0));
  970.  
  971.     // Procedure id
  972.     static XtIntervalId verify_id = 0;
  973.     if (verify_id == 0)
  974.     {
  975.     verify_id = XtAppAddTimeOut(XtWidgetToApplicationContext(button),
  976.                     0, VerifyButtonWorkProc, 
  977.                     XtPointer(&verify_id));
  978.     }
  979. }
  980.  
  981.  
  982. //-----------------------------------------------------------------------------
  983. // Button Creation
  984. //-----------------------------------------------------------------------------
  985.  
  986. static WidgetArray up_buttons;
  987. static WidgetArray down_buttons;
  988. static WidgetArray undo_buttons;
  989. static WidgetArray redo_buttons;
  990. static WidgetArray edit_buttons;
  991.  
  992. void refresh_buttons()
  993. {
  994.     int i;
  995.     for (i = 0; i < up_buttons.size(); i++)
  996.     set_sensitive(up_buttons[i], source_view->can_go_up());
  997.     for (i = 0; i < down_buttons.size(); i++)
  998.     set_sensitive(down_buttons[i], source_view->can_go_down());
  999.     for (i = 0; i < undo_buttons.size(); i++)
  1000.     set_sensitive(undo_buttons[i], 
  1001.               undo_buffer.undo_action() != NO_GDB_ANSWER);
  1002.     for (i = 0; i < redo_buttons.size(); i++)
  1003.     set_sensitive(redo_buttons[i], 
  1004.               undo_buffer.redo_action() != NO_GDB_ANSWER);
  1005.     for (i = 0; i < edit_buttons.size(); i++)
  1006.     set_sensitive(edit_buttons[i], source_view->have_source());
  1007.  
  1008.     update_edit_menus();
  1009. }
  1010.  
  1011. static void RemoveFromArrayCB(Widget w, XtPointer client_data, XtPointer)
  1012. {
  1013.     WidgetArray& arr = *((WidgetArray *)client_data);
  1014.     arr -= w;
  1015. }
  1016.  
  1017. static void register_button(WidgetArray& arr, Widget w)
  1018. {
  1019.     arr += w;
  1020.     XtAddCallback(w, XtNdestroyCallback, RemoveFromArrayCB, XtPointer(&arr));
  1021. }
  1022.  
  1023. // Create a button work area from BUTTON_LIST named NAME
  1024. Widget make_buttons(Widget parent, const string& name, 
  1025.             String button_list)
  1026. {
  1027.     Arg args[10];
  1028.     int arg = 0;
  1029.     XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
  1030.     XtSetArg(args[arg], XmNuserData, 0);              arg++;
  1031.     XtSetArg(args[arg], XmNmarginWidth, 0);            arg++;
  1032.     XtSetArg(args[arg], XmNmarginHeight, 0);           arg++;
  1033.     XtSetArg(args[arg], XmNspacing, 0);                arg++;
  1034.     XtSetArg(args[arg], XmNborderWidth, 0);            arg++;
  1035.     XtSetArg(args[arg], XmNhighlightThickness, 0);     arg++;
  1036.     XtSetArg(args[arg], XmNshadowThickness, 0);        arg++;
  1037.     Widget buttons = verify(XmCreateRowColumn(parent, name, args, arg));
  1038.  
  1039.     set_buttons(buttons, button_list);
  1040.  
  1041.     if (XtIsManaged(buttons))
  1042.     {
  1043.     XtWidgetGeometry size;
  1044.     size.request_mode = CWHeight;
  1045.     XtQueryGeometry(buttons, NULL, &size);
  1046.     XtVaSetValues(buttons,
  1047.               XmNpaneMaximum, size.height,
  1048.               XmNpaneMinimum, size.height,
  1049.               NULL);
  1050.     }
  1051.  
  1052.     return buttons;
  1053. }
  1054.  
  1055. void set_buttons(Widget buttons, String _button_list, bool manage)
  1056. {
  1057.     XtPointer user_data   = 0;
  1058.     WidgetList children   = 0;
  1059.     Cardinal num_children = 0;
  1060.  
  1061.     XtVaGetValues(buttons,
  1062.           XmNuserData, &user_data,
  1063.           XtNchildren, &children,
  1064.           XtNnumChildren, &num_children,
  1065.           NULL);
  1066.  
  1067.     string *sp = (string *)user_data;
  1068.     if (sp != 0 && *sp == string(_button_list))
  1069.     {
  1070.     // Unchanged value - only re-verify all buttons
  1071.     for (int i = 0; i < int(num_children); i++)
  1072.         verify_button(children[i]);
  1073.     return;
  1074.     }
  1075.     delete sp;
  1076.  
  1077.     StatusDelay *delay = 0;
  1078.     if (gdb_initialized)
  1079.     delay = new StatusDelay("Setting buttons");
  1080.  
  1081.     // Destroy all existing children (= buttons)
  1082.     int i;
  1083.     for (i = 0; i < int(num_children); i++)
  1084.     {
  1085.     XtUnmanageChild(children[i]);
  1086.     DestroyWhenIdle(children[i]);
  1087.     }
  1088.  
  1089.     // Add new buttons
  1090.     string button_list = _button_list;
  1091.  
  1092.     if (button_list.contains(':') && old_button_format())
  1093.     {
  1094.     // DDD 2.1 and earlier used `:' to separate buttons
  1095.     button_list.gsub(':', '\n');
  1096.  
  1097.     cerr << "Warning: converting " << quote(_button_list) << "\n"
  1098.          << "to new format " << quote(button_list) << "\n";
  1099.     }
  1100.  
  1101.     int lines = button_list.freq('\n') + 1;
  1102.     string *commands = new string[lines];
  1103.     split(button_list, commands, lines, '\n');
  1104.  
  1105.     int number_of_buttons = 0;
  1106.     for (i = 0; i < lines; i++)
  1107.     {
  1108.     XtCallbackProc callback = gdbCommandCB;
  1109.  
  1110.     string name = commands[i];
  1111.     strip_space(name);
  1112.  
  1113.     if (name == "")
  1114.         continue;
  1115.  
  1116.     MString label(0, true);
  1117.     if (name.contains(app_data.label_delimiter))
  1118.     {
  1119.         string label_s = name.after(app_data.label_delimiter);
  1120.         name = name.before(app_data.label_delimiter);
  1121.         strip_space(label_s);
  1122.         strip_space(name);
  1123.         label = MString(label_s);
  1124.     }
  1125.  
  1126.     string command = name;
  1127.     if (name.contains("..."))
  1128.     {
  1129.         name = name.before("...");
  1130.     }
  1131.     else if (name.contains('^'))
  1132.     {
  1133.         command = ctrl(name.from('^'));
  1134.         name = name.before('^');
  1135.     }
  1136.     else if (name != "" && iscntrl(name[name.length() - 1]))
  1137.     {
  1138.         command = string(name[name.length() - 1]);
  1139.         name = name.before(-1);
  1140.     }
  1141.  
  1142.     if (label.isNull())
  1143.     {
  1144.         // Create default label from name
  1145.         string label_s = name;
  1146.         if (label_s != "")
  1147.         label_s[0] = toupper(label_s[0]);
  1148.         label = MString(label_s);
  1149.     }
  1150.  
  1151.     // Make sure the widget name does not contain invalid characters
  1152. #if RUNTIME_REGEX
  1153.     static regex rxsep("[^-_a-zA-Z0-9]");
  1154. #endif
  1155.     name.gsub(rxsep, '_');
  1156.  
  1157. #if 0
  1158.     Widget button = verify(create_flat_button(buttons, name));
  1159. #else
  1160.     Arg args[10];
  1161.     Cardinal arg = 0;
  1162.     XtSetArg(args[arg], XmNborderWidth, 0);        arg++;
  1163.     XtSetArg(args[arg], XmNhighlightThickness, 1); arg++;
  1164.     Widget button = verify(XmCreatePushButton(buttons, name, args, arg));
  1165. #endif
  1166.     XtManageChild(button);
  1167.     number_of_buttons++;
  1168.  
  1169.     // A user-specified labelString overrides the given label
  1170.     XmString xmlabel;
  1171.     XtVaGetValues(button, XmNlabelString, &xmlabel, NULL);
  1172.     MString foundLabel(xmlabel, true);
  1173.     XmStringFree(xmlabel);
  1174.  
  1175.     if (foundLabel.str() == name)
  1176.     {
  1177.         // User did not specify a specific labelString - 
  1178.         // use the specified button command as label
  1179.         XtVaSetValues(button, XmNlabelString, label.xmstring(), NULL);
  1180.     }
  1181.  
  1182.     if (name == "Yes")
  1183.     {
  1184.         command = "yes";
  1185.         XtUnmanageChild(button);
  1186.         callback = YnButtonCB;
  1187.     }
  1188.     else if (name == "No")
  1189.     {
  1190.         command = "no";
  1191.         XtUnmanageChild(button);
  1192.         callback = YnButtonCB;
  1193.     }
  1194.     else if (name == "Prev")
  1195.         callback = gdbPrevCB;
  1196.     else if (name == "Next")
  1197.         callback = gdbNextCB;
  1198.     else if (name == "Clear")
  1199.         callback = gdbClearCB;
  1200.     else if (name == "Complete")
  1201.         callback = gdbCompleteCB;
  1202.     else if (name == "Apply")
  1203.         callback = gdbApplyCB;
  1204.     else if (name == "Make")
  1205.         callback = gdbMakeAgainCB;
  1206.     else if (name == "Undo" || name == "Back")
  1207.     {
  1208.         callback = gdbUndoCB;
  1209.         register_button(undo_buttons, button);
  1210.     }
  1211.     else if (name == "Redo" || name == "Forward")
  1212.     {
  1213.         callback = gdbRedoCB;
  1214.         register_button(redo_buttons, button);
  1215.     }
  1216.     else if (name == "Edit")
  1217.     {
  1218.         callback = gdbEditSourceCB;
  1219.         register_button(edit_buttons, button);
  1220.     }
  1221.     else if (name == "Reload")
  1222.     {
  1223.         callback = gdbReloadSourceCB;
  1224.         register_button(edit_buttons, button);
  1225.     }
  1226.  
  1227.     if (name == "up")
  1228.         register_button(up_buttons, button);
  1229.     else if (name == "down")
  1230.         register_button(down_buttons, button);
  1231.  
  1232.     // Be sure to verify whether the button actually exists
  1233.     verify_button(button);
  1234.  
  1235.     // We remove all callbacks to avoid popping down DialogShells
  1236.     XtRemoveAllCallbacks(button, XmNactivateCallback);
  1237.     XtAddCallback(button, XmNactivateCallback, callback,
  1238.               (XtPointer)XtNewString(command.chars()));
  1239.  
  1240.     // Add a help callback
  1241.     XtAddCallback(button, XmNhelpCallback, ImmediateHelpCB, XtPointer(0));
  1242.     }
  1243.     delete[] commands;
  1244.  
  1245.     if (manage)
  1246.     {
  1247.     if (number_of_buttons > 0)
  1248.     {
  1249.         // Manage buttons, giving them their preferred height
  1250.         XtWidgetGeometry size;
  1251.         size.request_mode = CWHeight;
  1252.         XtQueryGeometry(buttons, NULL, &size);
  1253.  
  1254.         XtVaSetValues(buttons,
  1255.               XmNpaneMinimum, size.height, 
  1256.               XmNpaneMaximum, size.height,
  1257.               NULL);
  1258.         
  1259.         manage_paned_child(buttons);
  1260.     }
  1261.     else
  1262.     {
  1263.         // No buttons at all
  1264.         unmanage_paned_child(buttons);
  1265.     }
  1266.     }
  1267.  
  1268.     sp = new string(_button_list);
  1269.     XtVaSetValues(buttons, XmNuserData, XtPointer(sp), NULL);
  1270.  
  1271.     // Register default help command
  1272.     DefaultHelpText           = gdbDefaultHelpText;
  1273.     DefaultTipText            = gdbDefaultTipText;
  1274.     DefaultDocumentationText  = gdbDefaultDocumentationText;
  1275.     TextPosOfEvent            = textPosOfEvent;
  1276.  
  1277.     DisplayDocumentation      = showDocumentationInStatusLine;
  1278.  
  1279.     // Set sensitivity
  1280.     refresh_buttons();
  1281.  
  1282.     // Install tips
  1283.     InstallButtonTips(buttons);
  1284.  
  1285.     // Update `define' panel
  1286.     UpdateDefinePanelCB();
  1287.  
  1288.     delete delay;
  1289. }
  1290.  
  1291.  
  1292.  
  1293. //-----------------------------------------------------------------------------
  1294. // Button Editor
  1295. //-----------------------------------------------------------------------------
  1296.  
  1297. // Remove garbage from S
  1298. static string normalize(string s)
  1299. {
  1300.     if (s.contains(':') && old_button_format())
  1301.     {
  1302.     // DDD 2.1 and earlier used `:' to separate buttons
  1303.     s.gsub(':', '\n');
  1304.     }
  1305.  
  1306.     int lines = s.freq('\n') + 1;
  1307.     string *commands = new string[lines];
  1308.     split(s, commands, lines, '\n');
  1309.  
  1310.     string ret = "";
  1311.     for (int i = 0; i < lines; i++)
  1312.     {
  1313.     string& cmd = commands[i];
  1314.     strip_space(cmd);
  1315.     if (cmd == "")
  1316.         continue;
  1317.  
  1318.     if (ret.length() > 0)
  1319.         ret += '\n';
  1320.     ret += cmd;
  1321.     }
  1322.  
  1323.     return ret;
  1324. }
  1325.  
  1326. static Widget buttons_dialog = 0;
  1327. static Widget button_box     = 0;
  1328. static Widget shortcut_label = 0;
  1329. static Widget console_w, shortcut_w;
  1330.  
  1331. struct ChangeTextInfo {
  1332.     String *str;
  1333.     Widget dialog;
  1334.     Widget text;
  1335.     Widget vfy;
  1336.     bool shortcuts;
  1337. };
  1338.  
  1339. static ChangeTextInfo *active_info = 0;
  1340.  
  1341. static void SetTextCB(Widget, XtPointer, XtPointer)
  1342. {
  1343.     if (active_info == 0)
  1344.     return;
  1345.  
  1346.     String _str = XmTextGetString(active_info->text);
  1347.     string str(_str);
  1348.     XtFree(_str);
  1349.  
  1350.     str = normalize(str);
  1351.  
  1352.     XmTextSetString(active_info->text, str);
  1353.  
  1354.     *active_info->str = (String)XtNewString(str.chars());
  1355.     update_user_buttons();
  1356. }
  1357.  
  1358. static void ResetTextCB(Widget, XtPointer, XtPointer)
  1359. {
  1360.     if (active_info == 0)
  1361.     return;
  1362.  
  1363.     XmTextSetString(active_info->text, *active_info->str);
  1364.     update_user_buttons();
  1365. }
  1366.  
  1367. static void ChangeTextCB(Widget w, XtPointer client_data, XtPointer call_data)
  1368. {
  1369.     XmToggleButtonCallbackStruct *cbs = 
  1370.     (XmToggleButtonCallbackStruct *)call_data;
  1371.     ChangeTextInfo *info = (ChangeTextInfo *)client_data;
  1372.  
  1373.     if (cbs->set)
  1374.     {
  1375.     // When changing, treat like `Apply'
  1376.     SetTextCB(w, XtPointer(0), XtPointer(0));
  1377.  
  1378.     active_info = info;
  1379.  
  1380.     string str = normalize(*info->str);
  1381.     XmTextSetString(info->text, (char *)str.chars());
  1382.     XtAddCallback(info->dialog, XmNhelpCallback, 
  1383.               HelpOnThisCB, XtPointer(w));
  1384.  
  1385.     if (info->shortcuts)
  1386.         XtUnmanageChild(info->vfy);
  1387.     else
  1388.         XtManageChild(info->vfy);
  1389.     }
  1390.     else
  1391.     {
  1392.     XtRemoveCallback(info->dialog, XmNhelpCallback, 
  1393.              HelpOnThisCB, XtPointer(w));
  1394.     }
  1395. }
  1396.  
  1397. static void SetVerifyButtonsCB(Widget, XtPointer, XtPointer call_data)
  1398. {
  1399.     XmToggleButtonCallbackStruct *cbs = 
  1400.     (XmToggleButtonCallbackStruct *)call_data;
  1401.  
  1402.     app_data.verify_buttons = cbs->set;
  1403. }
  1404.  
  1405.  
  1406. static Widget add_button(String name, 
  1407.              Widget dialog, Widget buttons, 
  1408.              Widget text, Widget vfy,
  1409.              String& str, bool shortcuts = false)
  1410. {
  1411.     Arg args[10];
  1412.     Cardinal arg = 0;
  1413.     Widget button = XmCreateToggleButton(buttons, name, args, arg);
  1414.     XtManageChild(button);
  1415.  
  1416.     ChangeTextInfo *info = new ChangeTextInfo;
  1417.     info->dialog    = dialog;
  1418.     info->str       = &str;
  1419.     info->text      = text;
  1420.     info->vfy       = vfy;
  1421.     info->shortcuts = shortcuts;
  1422.  
  1423.     XtAddCallback(button, XmNvalueChangedCallback, ChangeTextCB, 
  1424.           XtPointer(info));
  1425.  
  1426.     return button;
  1427. }
  1428.  
  1429. static void create_buttons_dialog(Widget parent)
  1430. {
  1431.     if (buttons_dialog != 0)
  1432.     return;
  1433.  
  1434.     Arg args[10];
  1435.     Cardinal arg = 0;
  1436.     XtSetArg(args[arg], XmNvisibleItemCount, 0); arg++;
  1437.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  1438.     buttons_dialog = 
  1439.     verify(XmCreatePromptDialog(find_shell(parent), 
  1440.                     "edit_buttons", args, arg));
  1441.  
  1442.     XtAddCallback(buttons_dialog, XmNokCallback,     SetTextCB, 0);
  1443.     XtAddCallback(buttons_dialog, XmNokCallback,     
  1444.           UnmanageThisCB, buttons_dialog);
  1445.     XtAddCallback(buttons_dialog, XmNapplyCallback,  SetTextCB, 0);
  1446.     XtAddCallback(buttons_dialog, XmNcancelCallback, ResetTextCB, 0);
  1447.  
  1448.     XtUnmanageChild(XmSelectionBoxGetChild(buttons_dialog,
  1449.                        XmDIALOG_SELECTION_LABEL));
  1450.     XtUnmanageChild(XmSelectionBoxGetChild(buttons_dialog, 
  1451.                        XmDIALOG_TEXT));
  1452.     Delay::register_shell(buttons_dialog);
  1453.  
  1454.     arg = 0;
  1455.     XtSetArg(args[arg], XmNmarginWidth,  0); arg++;
  1456.     XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
  1457.     XtSetArg(args[arg], XmNborderWidth,  0); arg++;
  1458.     XtSetArg(args[arg], XmNadjustMargin, False); arg++;
  1459.     Widget box = 
  1460.     verify(XmCreateRowColumn(buttons_dialog, "box", args, arg));
  1461.     XtManageChild(box);
  1462.  
  1463.     arg = 0;
  1464.     XtSetArg(args[arg], XmNmarginWidth,  0); arg++;
  1465.     XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
  1466.     XtSetArg(args[arg], XmNborderWidth,  0); arg++;
  1467.     XtSetArg(args[arg], XmNalignment, XmALIGNMENT_BEGINNING); arg++;
  1468.     shortcut_label = verify(XmCreateLabel(box, "shortcuts", args, arg));
  1469.     XtManageChild(shortcut_label);
  1470.  
  1471.     arg = 0;
  1472.     XtSetArg(args[arg], XmNmarginWidth,  0); arg++;
  1473.     XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
  1474.     XtSetArg(args[arg], XmNborderWidth,  0); arg++;
  1475.     XtSetArg(args[arg], XmNorientation,  XmHORIZONTAL); arg++;
  1476.     button_box = 
  1477.     verify(XmCreateRadioBox(box, "buttons", args, arg));
  1478.     XtManageChild(button_box);
  1479.  
  1480.     arg = 0;
  1481.     XtSetArg(args[arg], XmNeditMode, XmMULTI_LINE_EDIT); arg++;
  1482.     Widget text = verify(XmCreateScrolledText(box, "text", args, arg));
  1483.     XtManageChild(text);
  1484.  
  1485.     arg = 0;
  1486.     XtSetArg(args[arg], XmNset, app_data.verify_buttons); arg++;
  1487.     Widget vfy = verify(XmCreateToggleButton(box, "verify", args, arg));
  1488.     XtManageChild(vfy);
  1489.     XtAddCallback(vfy, XmNvalueChangedCallback, SetVerifyButtonsCB, 0);
  1490.  
  1491.     console_w = 
  1492.     add_button("console", buttons_dialog, button_box, text, vfy,
  1493.            app_data.console_buttons);
  1494.     Widget source_w = 
  1495.     add_button("source", buttons_dialog, button_box, text, vfy,
  1496.            app_data.source_buttons);
  1497.     Widget data_w = 
  1498.     add_button("data", buttons_dialog, button_box, text, vfy,
  1499.            app_data.data_buttons);
  1500.  
  1501.     String *str = 0;
  1502.     switch (gdb->type())
  1503.     {
  1504.     case GDB:  str = &app_data.gdb_display_shortcuts;  break;
  1505.     case DBX:  str = &app_data.dbx_display_shortcuts;  break;
  1506.     case XDB:  str = &app_data.xdb_display_shortcuts;  break;
  1507.     case JDB:  str = &app_data.jdb_display_shortcuts;  break;
  1508.     case PYDB: str = &app_data.pydb_display_shortcuts; break;
  1509.     case PERL: str = &app_data.perl_display_shortcuts; break;
  1510.     }
  1511.  
  1512.     shortcut_w = 
  1513.     add_button("shortcuts", buttons_dialog, button_box, text, vfy, 
  1514.            *str, true);
  1515.  
  1516.     XmToggleButtonSetState(source_w, True, False);
  1517.     (void) data_w;
  1518. }
  1519.  
  1520. // We use one single editor for both purposes, since this saves space.
  1521. void dddEditButtonsCB(Widget w, XtPointer, XtPointer)
  1522. {
  1523.     create_buttons_dialog(w);
  1524.     XtUnmanageChild(buttons_dialog);
  1525.  
  1526.     XtManageChild(button_box);
  1527.     XtManageChild(shortcut_w);
  1528.  
  1529.     XmToggleButtonSetState(console_w, True, True);
  1530.     ResetTextCB(w, 0, 0);
  1531.  
  1532.     XtManageChild(button_box);
  1533.     XtUnmanageChild(shortcut_w);
  1534.     XtUnmanageChild(shortcut_label);
  1535.  
  1536.     XtVaSetValues(XtParent(buttons_dialog), XmNtitle, 
  1537.           DDD_NAME ": Button Editor", NULL);
  1538.  
  1539.     manage_and_raise(buttons_dialog);
  1540. }
  1541.  
  1542. void dddEditShortcutsCB(Widget w, XtPointer, XtPointer)
  1543. {
  1544.     create_buttons_dialog(w);
  1545.     XtUnmanageChild(buttons_dialog);
  1546.  
  1547.     XtManageChild(button_box);
  1548.     XtManageChild(shortcut_w);
  1549.  
  1550.     XmToggleButtonSetState(shortcut_w, True, True);
  1551.     ResetTextCB(w, 0, 0);
  1552.  
  1553.     XtManageChild(shortcut_label);
  1554.     XtUnmanageChild(button_box);
  1555.  
  1556.     XtVaSetValues(XtParent(buttons_dialog), XmNtitle, 
  1557.           DDD_NAME ": Shortcut Editor", NULL);
  1558.  
  1559.     manage_and_raise(buttons_dialog);
  1560. }
  1561.  
  1562. void refresh_button_editor()
  1563. {
  1564.     StringArray exprs;
  1565.     StringArray labels;
  1566.  
  1567.     data_disp->get_shortcut_menu(exprs, labels);
  1568.     string expr;
  1569.     for (int i = 0; i < exprs.size(); i++)
  1570.     {
  1571.     if (i > 0)
  1572.         expr += '\n';
  1573.     expr += exprs[i];
  1574.     if (labels[i] != "")
  1575.         expr += string('\t') + app_data.label_delimiter + ' ' + labels[i];
  1576.     }
  1577.  
  1578.     String *str = 0;
  1579.     switch (gdb->type())
  1580.     {
  1581.     case GDB:  str = &app_data.gdb_display_shortcuts;  break;
  1582.     case DBX:  str = &app_data.dbx_display_shortcuts;  break;
  1583.     case XDB:  str = &app_data.xdb_display_shortcuts;  break;
  1584.     case JDB:  str = &app_data.jdb_display_shortcuts;  break;
  1585.     case PYDB: str = &app_data.pydb_display_shortcuts; break;
  1586.     case PERL: str = &app_data.perl_display_shortcuts; break;
  1587.     }
  1588.  
  1589.     *str = (String)XtNewString(expr.chars());
  1590.  
  1591.     if (active_info != 0 && active_info->str == str)
  1592.     XmTextSetString(active_info->text, *str);
  1593. }
  1594.  
  1595.  
  1596. //-----------------------------------------------------------------------------
  1597. // Flat Buttons
  1598. //-----------------------------------------------------------------------------
  1599.  
  1600. static void nop(Widget, XtPointer, XtPointer) {}
  1601.  
  1602. static MMDesc desc[] = 
  1603. {
  1604.     { "", MMFlatPush, { nop, 0 }, 0, 0, 0, 0 },
  1605.     MMEnd
  1606. };
  1607.  
  1608. // Create a flat PushButton named NAME
  1609. Widget create_flat_button(Widget parent, const string& name)
  1610. {
  1611.     desc[0].name = (char *)name;
  1612.     MMaddItems(parent, desc);
  1613.     MMaddCallbacks(desc);
  1614.     return desc[0].widget;
  1615. }
  1616.