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 / SourceView.C < prev    next >
C/C++ Source or Header  |  1998-12-04  |  233KB  |  9,552 lines

  1. // $Id: SourceView.C,v 1.426.4.1 1998/12/04 10:39:04 zeller Exp $
  2. // Use the Source, Luke.
  3.  
  4. // Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
  6. // and Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  7. // 
  8. // This file is part of DDD.
  9. // 
  10. // DDD is free software; you can redistribute it and/or
  11. // modify it under the terms of the GNU General Public
  12. // License as published by the Free Software Foundation; either
  13. // version 2 of the License, or (at your option) any later version.
  14. // 
  15. // DDD is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  18. // See the GNU General Public License for more details.
  19. // 
  20. // You should have received a copy of the GNU General Public
  21. // License along with DDD -- see the file COPYING.
  22. // If not, write to the Free Software Foundation, Inc.,
  23. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24. // 
  25. // DDD is the data display debugger.
  26. // For details, see the DDD World-Wide-Web page, 
  27. // `http://www.cs.tu-bs.de/softech/ddd/',
  28. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  29.  
  30. #ifdef __GNUG__
  31. #pragma implementation
  32. #endif
  33.  
  34. char SourceView_rcsid[] =
  35.     "$Id: SourceView.C,v 1.426.4.1 1998/12/04 10:39:04 zeller Exp $";
  36.  
  37.  
  38. // Fixing Some Bugs on a Sunday Evening
  39. // ------------------------------------
  40. // 
  41. // Whose bugs these are I think I know,
  42. // But now he works at 3DO;
  43. // He will not see me working here
  44. // To fix his code and make it go.
  45. // 
  46. // The saner folk must think it queer
  47. // To trace without the source code near
  48. // After a launch and frozen mouse
  49. // The weirdest stack crawl of the year.
  50. // 
  51. // I give my nodding head a shake
  52. // To see if I can stay awake
  53. // The only other thing to do
  54. // Is find some more coffeine to take.
  55. // 
  56. // This bug is pretty hard to nip,
  57. // But I have other ones to fix,
  58. // And tons to go before we ship,
  59. // And tons to go before we ship.
  60. //
  61. //
  62. // Written by David A. Lyons <dlyons@apple.com>, January 1994
  63. // (with apologies to Robert Frost)
  64. // 
  65. // Hey, it's fiction.  Close to reality in spirit,
  66. // but does not refer to a specific project, bug, Sunday,
  67. // or brand of soft drink.
  68.  
  69. #ifndef LOG_GLYPHS
  70. #define LOG_GlYPHS 0
  71. #endif
  72.  
  73. //-----------------------------------------------------------------------------
  74.  
  75. #include "SourceView.h"
  76.  
  77. // DDD stuff
  78. #include "AppData.h"
  79. #include "ComboBox.h"
  80. #include "Command.h"
  81. #include "DataDisp.h"        // Only for `DataDisp::SelectionLostCB'
  82. #include "Delay.h"
  83. #include "DestroyCB.h"
  84. #include "HelpCB.h"
  85. #include "HistoryD.h"
  86. #include "InitImage.h"
  87. #include "IntArray.h"
  88. #include "MakeMenu.h"
  89. #include "PosBuffer.h"
  90. #include "RefreshDI.h"
  91. #include "TimeOut.h"
  92. #include "UndoBuffer.h"
  93. #include "assert.h"
  94. #include "buttons.h"
  95. #include "charsets.h"
  96. #include "cmdtty.h"
  97. #include "cook.h"
  98. #include "cwd.h"
  99. #include "dbx-lookup.h"
  100. #include "ddd.h"
  101. #include "deref.h"
  102. #include "disp-read.h"
  103. #include "editing.h"
  104. #include "events.h"
  105. #include "file.h"
  106. #include "filetype.h"
  107. #include "fortranize.h"
  108. #include "history.h"
  109. #include "index.h"
  110. #include "isid.h"
  111. #include "java.h"
  112. #include "logo.h"
  113. #include "misc.h"
  114. #include "mydialogs.h"
  115. #include "options.h"
  116. #include "post.h"
  117. #include "question.h"
  118. #include "regexps.h"
  119. #include "shell.h"
  120. #include "shorten.h"
  121. #include "status.h"
  122. #include "string-fun.h"
  123. #include "strtoul.h"
  124. #include "tabs.h"
  125. #include "verify.h"
  126. #include "version.h"
  127. #include "windows.h"
  128. #include "wm.h"
  129.  
  130. // Motif stuff
  131. #include <Xm/Xm.h>
  132. #include <Xm/Form.h>
  133. #include <Xm/Label.h>
  134. #include <Xm/MessageB.h>
  135. #include <Xm/Text.h>
  136. #include <Xm/TextF.h>
  137. #include <Xm/RowColumn.h>
  138. #include <Xm/PushB.h>
  139. #include <Xm/SelectioB.h>
  140. #include <Xm/List.h>
  141. #include <Xm/PanedW.h>
  142. #include <Xm/ToggleB.h>
  143. #include <X11/StringDefs.h>
  144. #include <X11/cursorfont.h>
  145.  
  146. #if XmVersion >= 2000
  147. #include <Xm/SpinB.h>
  148. #ifndef XmIsSpinBox
  149. #define XmIsSpinBox(w) XtIsSubclass((w), xmSpinBoxWidgetClass)
  150. #endif
  151. #endif
  152.  
  153. // LessTif hacks
  154. #include <X11/IntrinsicP.h>
  155. #include "LessTifH.h"
  156.  
  157. // System stuff
  158. extern "C" {
  159. #include <sys/types.h>
  160. #include <sys/stat.h>
  161. }
  162. #include <stdio.h>
  163. #include <fcntl.h>
  164. #include <iomanip.h>
  165. #include <stdlib.h>
  166. #include <string.h>
  167. #include <time.h>
  168. #include <errno.h>
  169. #include <limits.h>
  170.  
  171. #ifndef ULONG_MAX
  172. #define    ULONG_MAX    ((unsigned long)(~0L))        /* 0xFFFFFFFF */
  173. #endif
  174.  
  175. // Test for regular file - see stat(3)
  176. #ifndef S_ISREG
  177. #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
  178. #endif
  179.  
  180.  
  181. //-----------------------------------------------------------------------
  182. // Xt stuff
  183. //-----------------------------------------------------------------------
  184. XtActionsRec SourceView::actions [] = {
  185.     {"source-popup-menu",        SourceView::srcpopupAct        },
  186.     {"source-start-select-word", SourceView::startSelectWordAct },
  187.     {"source-end-select-word",   SourceView::endSelectWordAct   },
  188.     {"source-update-glyphs",     SourceView::updateGlyphsAct    },
  189.     {"source-drag-glyph",        SourceView::dragGlyphAct       },
  190.     {"source-follow-glyph",      SourceView::followGlyphAct     },
  191.     {"source-drop-glyph",        SourceView::dropGlyphAct       },
  192.     {"source-delete-glyph",      SourceView::deleteGlyphAct     },
  193.     {"source-double-click",      SourceView::doubleClickAct     },
  194.     {"source-set-arg",           SourceView::setArgAct          },
  195. };
  196.  
  197. //-----------------------------------------------------------------------
  198. // Menus
  199. //-----------------------------------------------------------------------
  200.  
  201. // Popup menus - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  202. struct LineItms { enum Itms {SetBP, SetTempBP, Sep1, TempNContBP, 
  203.                  Sep2, SetPC}; };
  204. MMDesc SourceView::line_popup[] = 
  205. {
  206.     {"set",         MMPush, {SourceView::line_popup_setCB, 0}, 0, 0, 0, 0},
  207.     {"set_temp",    MMPush, 
  208.      {SourceView::line_popup_set_tempCB, 0}, 0, 0, 0, 0},
  209.     MMSep,
  210.     {"temp_n_cont", MMPush, 
  211.      {SourceView::line_popup_temp_n_contCB, 0}, 0, 0, 0, 0},
  212.     MMSep,
  213.     {"set_pc",      MMPush, {SourceView::line_popup_set_pcCB, 0}, 0, 0, 0, 0},
  214.     MMEnd
  215. };
  216.  
  217. struct BPItms { enum Itms {Properties, Disable, Delete, Sep, SetPC}; };
  218. MMDesc SourceView::bp_popup[] =
  219. {
  220.     {"properties",   MMPush, 
  221.      {SourceView::EditBreakpointPropertiesCB, 0}, 0, 0, 0, 0},
  222.     {"disable",      MMPush, {SourceView::bp_popup_disableCB, 0}, 0, 0, 0, 0},
  223.     {"delete",       MMPush, {SourceView::bp_popup_deleteCB, 0}, 0, 0, 0, 0},
  224.     MMSep,
  225.     {"set_pc",       MMPush, {SourceView::bp_popup_set_pcCB, 0}, 0, 0, 0, 0},
  226.     MMEnd
  227. };
  228.  
  229. struct BPButtons { enum Itms {Properties, Lookup, NewBP, NewWP, Print, 
  230.                   Enable, Disable, Delete}; };
  231. MMDesc SourceView::bp_area[] =
  232. {
  233.     {"properties",   MMPush,   
  234.      {SourceView::EditBreakpointPropertiesCB, 0}, 0, 0, 0, 0},
  235.     {"lookup",       MMPush,   
  236.      {SourceView::LookupBreakpointCB, XtPointer(0) }, 0, 0, 0, 0},
  237.     {"new_bp",       MMPush,   {SourceView::NewBreakpointCB, 0}, 0, 0, 0, 0},
  238.     {"new_wp",       MMPush,   {SourceView::NewWatchpointCB, 0}, 0, 0, 0, 0},
  239.     {"print",        MMPush,   
  240.      {SourceView::PrintWatchpointCB, XtPointer(0) }, 0, 0, 0, 0},
  241.     {"enable",       MMPush,   
  242.      {SourceView::BreakpointCmdCB, "enable"  }, 0, 0, 0, 0},
  243.     {"disable",      MMPush,   
  244.      {SourceView::BreakpointCmdCB, "disable" }, 0, 0, 0, 0},
  245.     {"delete",       MMPush | MMHelp,   
  246.      {SourceView::BreakpointCmdCB, "delete" }, 0, 0, 0, 0},
  247.     MMEnd
  248. };
  249.  
  250.  
  251. struct TextItms { 
  252.     enum Itms {
  253.     Print, 
  254.     Disp, 
  255.     Watch,
  256.     Sep1, 
  257.     PrintRef, 
  258.     DispRef,
  259.     WatchRef,
  260.     Sep2,
  261.     Whatis,
  262.     Sep3,
  263.     Lookup, 
  264.     Break,
  265.     Clear
  266.     };
  267. };
  268.  
  269. static String text_cmd_labels[] =
  270. {
  271.     "Print ", 
  272.     "Display ", 
  273.     "Watch ",
  274.     "",
  275.     "Print ", 
  276.     "Display ", 
  277.     "Watch ",
  278.     "",
  279.     "What is ",
  280.     "",
  281.     "Lookup " ,
  282.     "Break at ",
  283.     "Clear at "
  284. };
  285.  
  286. MMDesc SourceView::text_popup[] =
  287. {
  288.     {"print",      MMPush, {SourceView::text_popup_printCB, 0}, 0, 0, 0, 0},
  289.     {"disp",       MMPush, {SourceView::text_popup_dispCB, 0}, 0, 0, 0, 0},
  290.     {"watch",      MMPush | MMUnmanaged, 
  291.      {SourceView::text_popup_watchCB, 0}, 0, 0, 0, 0},
  292.     MMSep,
  293.     {"printRef",   MMPush, 
  294.      {SourceView::text_popup_print_refCB, 0}, 0, 0, 0, 0},
  295.     {"dispRef",    MMPush, 
  296.      {SourceView::text_popup_disp_refCB, 0}, 0, 0, 0, 0},
  297.     {"watchRef",   MMPush | MMUnmanaged, 
  298.      {SourceView::text_popup_watch_refCB, 0}, 0, 0, 0, 0},
  299.     MMSep,
  300.     {"whatis",     MMPush, {SourceView::text_popup_whatisCB, 0}, 0, 0, 0, 0},
  301.     MMSep,
  302.     {"lookup",     MMPush, {SourceView::text_popup_lookupCB, 0}, 0, 0, 0, 0},
  303.     {"breakAt",    MMPush, {SourceView::text_popup_breakCB, 0}, 0, 0, 0, 0},
  304.     {"clearAt",    MMPush, {SourceView::text_popup_clearCB, 0}, 0, 0, 0, 0},
  305.     MMEnd
  306. };
  307.  
  308.  
  309. //-----------------------------------------------------------------------
  310. // Glyphs and images
  311. //-----------------------------------------------------------------------
  312.  
  313. #include "icons/glyphs/arrow.xbm"
  314. #include "icons/glyphs/greyarrow.xbm"
  315. #include "icons/glyphs/pastarrow.xbm"
  316. #include "icons/glyphs/signalarrow.xbm"
  317. #include "icons/glyphs/dragarrow.xbm"
  318. #include "icons/glyphs/stop.xbm"
  319. #include "icons/glyphs/greystop.xbm"
  320. #include "icons/glyphs/dragstop.xbm"
  321. #include "icons/glyphs/cond.xbm"
  322. #include "icons/glyphs/greycond.xbm"
  323. #include "icons/glyphs/dragcond.xbm"
  324. #include "icons/glyphs/temp.xbm"
  325. #include "icons/glyphs/greytemp.xbm"
  326. #include "icons/glyphs/dragtemp.xbm"
  327.  
  328.  
  329. //-----------------------------------------------------------------------
  330. // Data.  Lots of 'em.
  331. //-----------------------------------------------------------------------
  332.  
  333. Widget SourceView::toplevel_w                = 0;
  334. Widget SourceView::source_form_w             = 0;
  335. Widget SourceView::source_text_w             = 0;
  336. Widget SourceView::code_form_w               = 0;
  337. Widget SourceView::code_text_w               = 0;
  338. Widget SourceView::edit_breakpoints_dialog_w = 0;
  339. Widget SourceView::breakpoint_list_w         = 0;
  340. Widget SourceView::stack_dialog_w            = 0;
  341. Widget SourceView::frame_list_w              = 0;
  342. Widget SourceView::up_w                      = 0;
  343. Widget SourceView::down_w                    = 0;
  344. Widget SourceView::register_dialog_w         = 0;
  345. Widget SourceView::register_list_w           = 0;
  346. Widget SourceView::all_registers_w           = 0;
  347. Widget SourceView::int_registers_w           = 0;
  348. Widget SourceView::thread_dialog_w           = 0;
  349. Widget SourceView::thread_list_w             = 0;
  350.  
  351. bool SourceView::stack_dialog_popped_up    = false;
  352. bool SourceView::register_dialog_popped_up = false;
  353. bool SourceView::thread_dialog_popped_up   = false;
  354.  
  355. bool SourceView::cache_source_files     = true;
  356. bool SourceView::cache_machine_code     = true;
  357. bool SourceView::display_glyphs         = true;
  358. bool SourceView::display_line_numbers   = false;
  359. bool SourceView::disassemble            = true;
  360. bool SourceView::all_registers          = false;
  361.  
  362. int  SourceView::source_indent_amount = 4;
  363. int  SourceView::script_indent_amount = 4;
  364. int  SourceView::code_indent_amount   = 4;
  365. int  SourceView::line_indent_amount   = 4;
  366. int  SourceView::tab_width            = 8;
  367.  
  368. int  SourceView::lines_above_cursor   = 2;
  369. int  SourceView::lines_below_cursor   = 3;
  370.  
  371. SourceOrigin SourceView::current_origin = ORIGIN_NONE;
  372.  
  373. Map<int, BreakPoint> SourceView::bp_map;
  374.  
  375. string SourceView::current_file_name = "";
  376. int    SourceView::line_count = 0;
  377. IntIntArrayAssoc SourceView::bps_in_line;
  378. TextPositionArray SourceView::_pos_of_line;
  379. StringArray SourceView::bp_addresses;
  380. StringStringAssoc SourceView::file_cache;
  381. StringOriginAssoc SourceView::origin_cache;
  382. StringStringAssoc SourceView::source_name_cache;
  383. StringStringAssoc SourceView::file_name_cache;
  384. CodeCache SourceView::code_cache;
  385.  
  386. string SourceView::current_source;
  387. string SourceView::current_code;
  388. string SourceView::current_code_start;
  389. string SourceView::current_code_end;
  390.  
  391. string SourceView::current_pwd        = cwd();
  392. string SourceView::current_class_path = ".";
  393.  
  394. XmTextPosition SourceView::last_top                = 0;
  395. XmTextPosition SourceView::last_pos                = 0;
  396. XmTextPosition SourceView::last_start_highlight    = 0;
  397. XmTextPosition SourceView::last_end_highlight      = 0;
  398.  
  399. XmTextPosition SourceView::last_top_pc             = 0;
  400. XmTextPosition SourceView::last_pos_pc             = 0;
  401. XmTextPosition SourceView::last_start_highlight_pc = 0;
  402. XmTextPosition SourceView::last_end_highlight_pc   = 0;
  403.  
  404. string SourceView::last_execution_file = "";
  405. int    SourceView::last_execution_line = 0;
  406. string SourceView::last_execution_pc   = "";
  407. string SourceView::last_shown_pc       = "";
  408.  
  409. int SourceView::last_frame_pos = 0;
  410. bool SourceView::frame_pos_locked = false;
  411. int SourceView::current_frame = -1;
  412.  
  413. bool SourceView::checking_scroll = false;
  414.  
  415. bool SourceView::at_lowest_frame = true;
  416. bool SourceView::signal_received = false;
  417.  
  418. int SourceView::max_popup_expr_length = 20;
  419.  
  420. int SourceView::max_breakpoint_number_seen = 0;
  421.  
  422. //-----------------------------------------------------------------------
  423. // Selection stuff
  424. //-----------------------------------------------------------------------
  425.  
  426. static XmTextPosition selection_startpos;
  427. static XmTextPosition selection_endpos;
  428. static Time           selection_time;
  429. #if XtSpecificationRelease < 6
  430. static XEvent         selection_event;
  431. #endif
  432.  
  433.  
  434. //-----------------------------------------------------------------------
  435. // Helping functions.
  436. //-----------------------------------------------------------------------
  437.  
  438. // Sort A
  439. static void sort(IntArray& a)
  440. {
  441.     // Shell sort -- simple and fast
  442.     int h = 1;
  443.     do {
  444.     h = h * 3 + 1;
  445.     } while (h <= a.size());
  446.     do {
  447.     h /= 3;
  448.     for (int i = h; i < a.size(); i++)
  449.     {
  450.         int v = a[i];
  451.         int j;
  452.         for (j = i; j >= h && a[j - h] > v; j -= h)
  453.         a[j] = a[j - h];
  454.         if (i != j)
  455.         a[j] = v;
  456.     }
  457.     } while (h != 1);
  458. }
  459.  
  460. // Return index of RXADDRESS in S, beginning from POS.  Stop search at newline.
  461. static int address_index(const string& s, int pos)
  462. {
  463.     int eol = s.index('\n');
  464.     if (eol < 0)
  465.     eol = s.length();
  466.  
  467.     string first_line = ((string&)s).at(pos, eol - pos);
  468.     int i = 0;
  469.     while (i < int(first_line.length()) && isspace(first_line[i]))
  470.     i++;
  471.     i = first_line.index(rxaddress_start, i);
  472.     if (i < 0)
  473.     return -1;
  474.     else
  475.     return pos + i;
  476. }
  477.  
  478. // Return true if W is a descendant of code_form_w
  479. bool SourceView::is_code_widget(Widget w)
  480. {
  481.     while (w != 0)
  482.     {
  483.     if (w == code_form_w)
  484.         return true;
  485.     else
  486.         w = XtParent(w);
  487.     }
  488.     return false;
  489. }
  490.  
  491. // Return true if W is a descendant of source_form_w
  492. bool SourceView::is_source_widget(Widget w)
  493. {
  494.     while (w != 0)
  495.     {
  496.     if (w == source_form_w)
  497.         return true;
  498.     else
  499.         w = XtParent(w);
  500.     }
  501.     return false;
  502. }
  503.  
  504. string& SourceView::current_text(Widget w)
  505. {
  506.     assert(is_source_widget(w) || is_code_widget(w));
  507.  
  508.     if (is_code_widget(w))
  509.     return current_code;
  510.     else
  511.     return current_source;
  512. }
  513.  
  514.  
  515. static const int MAX_INDENT = 64;
  516.  
  517. int SourceView::indent_amount(Widget w, int pos)
  518. {
  519.     assert(is_source_widget(w) || is_code_widget(w));
  520.  
  521.     int indent = 0;
  522.     if (is_code_widget(w))
  523.     {
  524.     indent = code_indent_amount;
  525.     }
  526.     else
  527.     {
  528.     indent = source_indent_amount;
  529.  
  530.     if (display_line_numbers)
  531.         indent += line_indent_amount;
  532.  
  533.     // Set a minimum indentation for scripting languages
  534.     if (gdb->requires_script_indent())
  535.         indent = max(indent, script_indent_amount);
  536.     }
  537.  
  538.     // Make sure indentation stays within reasonable bounds
  539.     indent = min(max(indent, 0), MAX_INDENT);
  540.  
  541.     if (pos >= 0)
  542.     {
  543.     const string& text = current_text(w);
  544.     while (pos < int(text.length()) && text[pos] == ' ')
  545.     {
  546.         pos++;
  547.         indent++;
  548.     }
  549.     }
  550.  
  551.     return indent;
  552. }
  553.  
  554.  
  555. //-----------------------------------------------------------------------
  556. // Methods
  557. //-----------------------------------------------------------------------
  558.  
  559.  
  560. // ***************************************************************************
  561. //
  562. void SourceView::line_popup_setCB (Widget w,
  563.                    XtPointer client_data,
  564.                    XtPointer)
  565. {
  566.     string address = *((string *)client_data);
  567.     create_bp(address, w);
  568. }
  569.  
  570. void SourceView::line_popup_set_tempCB (Widget w,
  571.                     XtPointer client_data,
  572.                     XtPointer)
  573. {
  574.     string address = *((string *)client_data);
  575.     create_temp_bp(address, w);
  576. }
  577.  
  578. // Create or clear a breakpoint at position A.  If SET, create a
  579. // breakpoint; if not SET, delete it.  If TEMP, make the breakpoint
  580. // temporary.  If COND is given, break only iff COND evals to true. W
  581. // is the origin.
  582. void SourceView::set_bp(const string& a, bool set, bool temp, 
  583.             const string& cond, Widget w)
  584. {
  585.     int new_bps = max_breakpoint_number_seen + 1;
  586.     string address = a;
  587.  
  588.     if (address != "" && address[0] == '0')
  589.     address = "*" + address; // Address given
  590.  
  591.     if (!set)
  592.     {
  593.     // Clear bp
  594.     gdb_command(clear_command(address), w);
  595.     }
  596.     else
  597.     {
  598.     // Set bp
  599.     switch (gdb->type())
  600.     {
  601.     case GDB:
  602.     case PYDB:
  603.         if (temp)
  604.         gdb_command("tbreak " + address, w);
  605.         else
  606.         gdb_command("break " + address, w);
  607.         break;
  608.  
  609.     case DBX:
  610.     {
  611.         string cond_suffix = "";
  612.         if (cond != "")
  613.         {
  614.         if (gdb->has_handler_command())
  615.             cond_suffix = " -if " + cond;
  616.         else
  617.             cond_suffix = " if " + cond;
  618.         }
  619.  
  620.         if (address.contains('*', 0))
  621.         {
  622.         // Address given
  623.         address = address.after('*');
  624.         gdb_command("stopi at " + address + cond_suffix, w);
  625.  
  626.         if (temp)
  627.         {
  628.             syncCommandQueue();
  629.             gdb_command("when $pc == " + address + " "
  630.                 + command_list(clear_command(address, true, 
  631.                                  new_bps)),
  632.                 w);
  633.         }
  634.         }
  635.         else
  636.         {
  637.         string line = "";
  638.         if (address.matches(rxint))
  639.         {
  640.             // Line number given
  641.             line = address;
  642.             gdb_command("stop at " + address + cond_suffix, w);
  643.         }
  644.         else if (is_file_pos(address))
  645.         {
  646.             // FILE:LINE given
  647.             int colon_index = address.index(':', -1);
  648.             assert(colon_index >= 0);
  649.             string file = address.before(colon_index);
  650.             line = address.after(colon_index);
  651.  
  652.             gdb_command("file " + file, w);
  653.             gdb_command("stop at " + line + cond_suffix, w);
  654.         }
  655.         else
  656.         {
  657.             // Function given
  658.             string pos = dbx_lookup(address);
  659.  
  660.             if (pos.contains(':'))
  661.             {
  662.             string file = pos.before(':');
  663.             line = pos.after(':');
  664.  
  665.             gdb_command("file " + file, w);
  666.             gdb_command("stop at " + line + cond_suffix, w);
  667.             }
  668.             else
  669.             {
  670.             // Cannot determine function position - try this one
  671.             gdb_command("stop in " + address + cond_suffix, w);
  672.             }
  673.         }
  674.  
  675.         if (temp && line != "")
  676.         {
  677.             syncCommandQueue();
  678.             string clear_cmd = clear_command(line, true, new_bps);
  679.             gdb_command("when at " + line + " " 
  680.                 + command_list(clear_cmd), w);
  681.         }
  682.         }
  683.         break;
  684.     }
  685.  
  686.     case JDB:
  687.     {
  688.         if (is_file_pos(address))
  689.         gdb_command("stop at " + address);
  690.         else
  691.         gdb_command("stop in " + address);
  692.         break;
  693.     }
  694.  
  695.     case XDB:
  696.     {
  697.         string command;
  698.         if (address.contains('*', 0))
  699.         command = "ba " + address.after('*');
  700.         else
  701.         command = "b " + address;
  702.  
  703.         if (temp)
  704.         command += " \\1t";
  705.  
  706.         if (cond != "" && !gdb->has_condition_command())
  707.         command += " {if " + cond + " {} {Q;c}}";
  708.  
  709.         gdb_command(command, w);
  710.         break;
  711.     }
  712.  
  713.     case PERL:
  714.     {
  715.         if (is_file_pos(address))
  716.         {
  717.         string file = address.before(':');
  718.         address = address.after(':');
  719.  
  720.         if (!file_matches(file, current_file_name))
  721.             gdb_command("f " + file, w);
  722.         }
  723.  
  724.         string command = "b " + address;
  725.         if (cond != "" && !gdb->has_condition_command())
  726.         command += " " + cond;
  727.  
  728.         gdb_command(command, w);
  729.         break;
  730.     }
  731.     }
  732.  
  733.     if (cond != "" && gdb->has_condition_command())
  734.     {
  735.         // Add condition
  736.         gdb_command(gdb->condition_command(itostring(new_bps), cond), w);
  737.     }
  738.     }
  739. }
  740.  
  741. // ***************************************************************************
  742. //
  743. void SourceView::clearBP(XtPointer client_data, XtIntervalId *)
  744. {
  745.     int bp_nr = (int)(long)client_data;
  746.     BreakPoint *bp = bp_map.get(bp_nr);
  747.     if (bp != 0)
  748.     delete_bp(bp_nr);
  749. }
  750.  
  751. // Save last `jump' target for XDB
  752. static string last_jump_address;
  753.  
  754. void SourceView::clearJumpBP(const string& msg, void *data)
  755. {
  756.     set_status(msg);
  757.  
  758.     if (gdb->type() == XDB && msg == "")
  759.     {
  760.     // Moving PC was successful.
  761.     show_execution_position(last_jump_address, true);
  762.     }
  763.  
  764.     int old_max_breakpoint_number_seen = (int)(long)data;
  765.  
  766.     for (int i = old_max_breakpoint_number_seen + 1;
  767.      i <= max_breakpoint_number_seen; i++)
  768.     {
  769.     // Delete all recently created breakpoints
  770.     XtAppAddTimeOut(XtWidgetToApplicationContext(source_text_w),
  771.             0, clearBP, XtPointer(i));
  772.     }
  773. }
  774.  
  775. void SourceView::line_popup_temp_n_contCB (Widget w,
  776.                        XtPointer client_data,
  777.                        XtPointer)
  778. {
  779.     string address = *((string *)client_data);
  780.     temp_n_cont(address, w);
  781. }
  782.  
  783. void SourceView::temp_n_cont(const string& a, Widget w)
  784. {
  785.     string address = a;
  786.  
  787.     switch (gdb->type())
  788.     {
  789.     case GDB:
  790. #if 0                // GDB `until' only works in the current frame
  791.     gdb_command("until " + address, w);
  792.     break;
  793. #endif
  794.     
  795.     case DBX:
  796.     case JDB:
  797.     case PYDB:
  798.     {
  799.     int old_max_breakpoint_number_seen = max_breakpoint_number_seen;
  800.  
  801.     // Create a temporary breakpoint
  802.     create_temp_bp(address, w);
  803.  
  804.     // Make sure the temporary breakpoint is deleted after `cont'
  805.     Command c("cont", w);
  806.     c.callback = clearJumpBP;
  807.     c.data     = XtPointer(old_max_breakpoint_number_seen);
  808.     gdb_command(c);
  809.     break;
  810.     }
  811.  
  812.     case XDB:
  813.     if (address.contains('*', 0))
  814.         address = address.after('*');
  815.     gdb_command("c " + address, w);
  816.     break;
  817.  
  818.     case PERL:
  819.     if (is_file_pos(address))
  820.         address = address.after(':');
  821.     gdb_command("c " + address, w);
  822.     break;
  823.     }
  824. }
  825.  
  826. // ***************************************************************************
  827. //
  828. void SourceView::line_popup_set_pcCB(Widget w, 
  829.                      XtPointer client_data,
  830.                      XtPointer)
  831. {
  832.     string address = *((string *)client_data);
  833.     move_pc(address, w);
  834. }
  835.  
  836. // ***************************************************************************
  837. //
  838. bool SourceView::move_pc(const string& a, Widget w)
  839. {
  840.     string address = a;
  841.  
  842.     if (address.contains('*', 0))
  843.     {
  844.     if (compare_address(address.after('*'), last_execution_pc) == 0)
  845.         return false;    // PC already at address
  846.     }
  847.     else
  848.     {
  849.     string file = address.before(':');
  850.     int line    = get_positive_nr(address.after(':'));
  851.  
  852.     if (file_matches(file, last_execution_file)
  853.         && line == last_execution_line)
  854.         return false;    // PC already at address
  855.     }
  856.  
  857.     if (gdb->has_jump_command())
  858.     {
  859.     int old_max_breakpoint_number_seen = max_breakpoint_number_seen;
  860.  
  861.     // We prefer the GDB `jump' command to setting `$pc'
  862.     // immediately since `jump' requires confirmation when jumping
  863.     // out of the current function.
  864.  
  865.     switch (gdb->type())
  866.     {
  867.     case DBX:
  868.         // DBX immediately resumes execution - create a temp
  869.         // breakpoint at ADDRESS
  870.         create_temp_bp(address, w);
  871.  
  872.         // DBX `cont at ' requires a line number.
  873.         gdb_command("file " + address.before(':'));
  874.         // FALL THROUGH
  875.  
  876.     case XDB:
  877.         // XDB 'g' wants only a line number
  878.         address = address.after(':');
  879.         break;
  880.  
  881.     case GDB:
  882.         // GDB immediately resumes execution - create a temp
  883.         // breakpoint at ADDRESS
  884.         create_temp_bp(address, w);
  885.         break;
  886.  
  887.     case JDB:
  888.     case PYDB:
  889.     case PERL:
  890.         break;        // Never reached
  891.     }
  892.  
  893.     // Jump to the new address and clear the breakpoint again
  894.     last_jump_address = a;
  895.     Command c(gdb->jump_command(address), w);
  896.     c.callback = clearJumpBP;
  897.     c.data     = XtPointer(old_max_breakpoint_number_seen);
  898.     gdb_command(c);
  899.  
  900.     return true;
  901.     }
  902.     else if (gdb->type() != JDB && gdb->has_assign_command())
  903.     {
  904.     // Try the `set $pc = ADDR' alternative.
  905.     if (address.contains('*', 0))
  906.     {
  907.         address = address.after('*');
  908.     }
  909.     else
  910.     {
  911.         lookup(address, true);
  912.         syncCommandQueue();
  913.         address = last_shown_pc;
  914.     }
  915.  
  916.     if (address == "")
  917.     {
  918.         set_status("Cannot determine address of " + a);
  919.     }
  920.     else
  921.     {
  922.         gdb_command(gdb->assign_command("$pc", address), w);
  923.         return true;
  924.     }
  925.     }
  926.  
  927.     return false;
  928. }
  929.  
  930. bool SourceView::move_bp(int bp_nr, const string& a, Widget w, bool copy)
  931. {
  932.     string address = a;
  933.  
  934.     // clog << "Moving breakpoint " << bp_nr << " to " << address << '\n';
  935.  
  936.     BreakPoint *bp = bp_map.get(bp_nr);
  937.     if (bp == 0)
  938.     return false;        // No such breakpoint
  939.  
  940.     if (!copy)
  941.     {
  942.     if (address.contains('*', 0))
  943.     {
  944.         if (compare_address(address.after('*'), bp->address()) == 0)
  945.         return false;    // Breakpoint already at address
  946.     }
  947.     else
  948.     {
  949.         string file = address.before(':');
  950.         int line    = get_positive_nr(address.after(':'));
  951.  
  952.         if (bp_matches(bp, file, line))
  953.         return false;    // Breakpoint already at address
  954.     }
  955.     }
  956.  
  957.     // Create a new breakpoint at ADDRESS, making it inherit the
  958.     // current settings
  959.     ostrstream os;
  960.     bool ok = bp->get_state(os, 0, false, address);
  961.     if (!ok)
  962.     return false;        // Command failed
  963.  
  964.     int new_bp_nr = next_breakpoint_number();
  965.     string commands(os);
  966.     commands.gsub("@0@", itostring(new_bp_nr));
  967.  
  968.     while (commands != "")
  969.     {
  970.     string command = commands.before('\n');
  971.     gdb_command(command, w);
  972.     commands = commands.after('\n');
  973.     }
  974.  
  975.     if (copy)
  976.     {
  977.     // Copy properties
  978.     copy_breakpoint_properties(bp_nr, new_bp_nr);
  979.     }
  980.     else
  981.     {
  982.     // Rename properties
  983.     move_breakpoint_properties(bp_nr, new_bp_nr);
  984.  
  985.     // Delete old breakpoint
  986.     delete_bp(bp_nr);
  987.     }
  988.  
  989.     return true;
  990. }
  991.  
  992. void SourceView::_set_bps_cond(IntArray& _nrs, string cond,
  993.                    int make_false, Widget w)
  994. {
  995.     // _NRS might be changed via MOVE_BREAKPOINT_PROPERTIES, 
  996.     // so we make a copy
  997.     IntArray nrs(_nrs);
  998.  
  999.     int count = 0;
  1000.     for (int i = 0; i < nrs.size(); i++)
  1001.     {
  1002.     int bp_nr = nrs[i];
  1003.     BreakPoint *bp = bp_map.get(bp_nr);
  1004.     if (bp == 0)
  1005.         continue;        // No such breakpoint
  1006.  
  1007.     string c = cond;
  1008.     if (c == char(-1))
  1009.         c = bp->condition();
  1010.  
  1011.     int m = make_false;
  1012.     if (m < 0)
  1013.         m = (!bp->enabled() && !gdb->has_enable_command());
  1014.     if (m)
  1015.         c = BreakPoint::make_false(c);
  1016.  
  1017.     if (gdb->has_condition_command())
  1018.     {
  1019.         // Use the `cond' command to assign a condition
  1020.         gdb_command(gdb->condition_command(itostring(bp_nr), c), w);
  1021.     }
  1022.     else
  1023.     {
  1024.         // Create a new breakpoint with a new condition COND, making it
  1025.         // inherit the current settings
  1026.         ostrstream os;
  1027.         bool ok = bp->get_state(os, 0, false, "", c);
  1028.         if (!ok)
  1029.         continue;        // Command failed
  1030.  
  1031.         string commands(os);
  1032.  
  1033.         int new_bp_nr = bp_nr;
  1034.         if (gdb->has_numbered_breakpoints())
  1035.         {
  1036.         new_bp_nr = next_breakpoint_number() + count;
  1037.         commands.gsub("@0@", itostring(new_bp_nr));
  1038.         }
  1039.  
  1040.         while (commands != "")
  1041.         {
  1042.         string command = commands.before('\n');
  1043.         gdb_command(command, w);
  1044.         commands = commands.after('\n');
  1045.         }
  1046.  
  1047.         if (gdb->has_numbered_breakpoints())
  1048.         {
  1049.         // Copy properties to new breakpoint
  1050.         move_breakpoint_properties(bp_nr, new_bp_nr);
  1051.  
  1052.         // Delete old breakpoint
  1053.         delete_bp(bp_nr);
  1054.  
  1055.         // Next breakpoint will get the next number
  1056.         count++;
  1057.         }
  1058.     }
  1059.     }
  1060. }
  1061.  
  1062.  
  1063. // ***************************************************************************
  1064. //
  1065. void SourceView::bp_popup_deleteCB (Widget w,
  1066.                     XtPointer client_data,
  1067.                     XtPointer)
  1068. {
  1069.     int bp_nr = *((int *)client_data);
  1070.     delete_bp(bp_nr, w);
  1071. }
  1072.  
  1073.  
  1074. // ***************************************************************************
  1075. //
  1076. void SourceView::bp_popup_disableCB (Widget w, 
  1077.                      XtPointer client_data,
  1078.                      XtPointer)
  1079. {
  1080.     int bp_nr = *((int *)client_data);
  1081.     BreakPoint *bp = bp_map.get(bp_nr);
  1082.     if (bp != 0)
  1083.     {
  1084.     if (bp->enabled())
  1085.         disable_bp(bp_nr, w);
  1086.     else
  1087.         enable_bp(bp_nr, w);
  1088.     }
  1089. }
  1090.  
  1091. // Convert NRS to a list of numbers
  1092. string SourceView::numbers(const IntArray& nrs)
  1093. {
  1094.     string cmd = ""; 
  1095.     for (int i = 0; i < nrs.size(); i++)
  1096.     {
  1097.     if (i > 0)
  1098.         cmd += " ";
  1099.     cmd += itostring(nrs[i]);
  1100.     }
  1101.     return cmd;
  1102. }
  1103.  
  1104. // Same, but use "" if we have GDB and all numbers are used
  1105. string SourceView::all_numbers(const IntArray& nrs)
  1106. {
  1107.     if ((gdb->type() == GDB || gdb->type() == PYDB) && all_bps(nrs))
  1108.     return "";        // In GDB, no arg means `all'
  1109.     else
  1110.     return numbers(nrs);
  1111. }
  1112.  
  1113. // Return true if NRS contains all breakpoints and
  1114. // a GDB delete/disable/enable command can be given without args.
  1115. bool SourceView::all_bps(const IntArray& nrs)
  1116. {
  1117.     if ((gdb->type() != GDB && gdb->type() != PYDB) || nrs.size() < 2)
  1118.     return false;
  1119.  
  1120.     MapRef ref;
  1121.     BreakPoint *bp = 0;
  1122.     for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  1123.     {
  1124.     bool found = false;
  1125.     for (int i = 0; !found && i < nrs.size(); i++)
  1126.     {
  1127.         if (bp->number() == nrs[i])
  1128.         found = true;
  1129.     }
  1130.  
  1131.     if (!found)
  1132.         return false;
  1133.     }
  1134.  
  1135.     return true;
  1136. }
  1137.  
  1138. void SourceView::enable_bps(IntArray& nrs, Widget w)
  1139. {
  1140.     if (gdb->has_enable_command())
  1141.     {
  1142.     gdb_command(gdb->enable_command(all_numbers(nrs)), w);
  1143.     }
  1144.     else if (gdb->has_conditions())
  1145.     {
  1146.     // Unset `false' breakpoint condition
  1147.     enable_bps_cond(nrs, w);
  1148.     }
  1149. }
  1150.  
  1151. void SourceView::disable_bps(IntArray& nrs, Widget w)
  1152. {
  1153.     if (gdb->has_disable_command())
  1154.     {
  1155.     gdb_command(gdb->disable_command(all_numbers(nrs)), w);
  1156.     }
  1157.     else if (gdb->has_conditions())
  1158.     {
  1159.     // Set breakpoint condition to `false'
  1160.     disable_bps_cond(nrs, w);
  1161.     }
  1162. }
  1163.  
  1164. void SourceView::delete_bps(IntArray& nrs, Widget w)
  1165. {
  1166.     if (gdb->recording() && gdb->has_clear_command())
  1167.     {
  1168.     // While recording, prefer commands without explicit numbers.
  1169.         for (int i = 0; i < nrs.size(); i++)
  1170.     {
  1171.         BreakPoint *bp = bp_map.get(nrs[i]);
  1172.         if (bp != 0)
  1173.         gdb_command(clear_command(bp->pos()));
  1174.     }
  1175.     }
  1176.     else if (gdb->has_delete_command())
  1177.     {
  1178.     gdb_command(gdb->delete_command(all_numbers(nrs)), w);
  1179.     }
  1180.     else
  1181.     {
  1182.         for (int i = 0; i < nrs.size(); i++)
  1183.         gdb_command(delete_command(nrs[i]));
  1184.     }
  1185. }
  1186.  
  1187. // A generic deletion command for breakpoint BP_NR - either `clear' or `delete'
  1188. string SourceView::delete_command(int bp_nr)
  1189. {
  1190.     if (gdb->has_delete_command())
  1191.     {
  1192.     return gdb->delete_command(itostring(bp_nr));
  1193.     }
  1194.     else if (gdb->has_clear_command())
  1195.     {
  1196.     BreakPoint *bp = bp_map.get(bp_nr);
  1197.     if (bp != 0)
  1198.         return clear_command(bp->pos());
  1199.     }
  1200.  
  1201.     return "";            // No way to delete a breakpoint (*sigh*)
  1202. }
  1203.  
  1204. // Return `clear ARG' command.  If CLEAR_NEXT is set, attempt to guess
  1205. // the next event number and clear this one as well.  (This is useful
  1206. // for setting temporary breakpoints, as `delete' must also clear the
  1207. // event handler we're about to install.)  Consider only breakpoints
  1208. // whose number is >= FIRST_BP.
  1209. string SourceView::clear_command(string pos, bool clear_next, int first_bp)
  1210. {
  1211.     string file = current_file_name;
  1212.     string line = pos;
  1213.  
  1214.     if (gdb->type() == DBX && !pos.contains(':') && !pos.matches(rxint))
  1215.     pos = dbx_lookup(pos);
  1216.  
  1217.     if (pos.contains(':'))
  1218.     {
  1219.     file = pos.before(':');
  1220.     line = pos.after(':');
  1221.     }
  1222.  
  1223.     int line_no = atoi(line);
  1224.  
  1225.     if (!clear_next && gdb->has_clear_command())
  1226.     {
  1227.     switch (gdb->type())
  1228.     {
  1229.     case GDB:
  1230.     case JDB:
  1231.     case PYDB:
  1232.         return "clear " + pos;
  1233.  
  1234.     case PERL:
  1235.         if (line_no > 0 && file_matches(file, current_file_name))
  1236.         return "d " + line;
  1237.         break;
  1238.  
  1239.     case DBX:
  1240.         if (line_no > 0 && file_matches(file, current_file_name))
  1241.         return "clear " + line;
  1242.         break;
  1243.  
  1244.     case XDB:
  1245.         break;
  1246.     }
  1247.     }
  1248.  
  1249.     int max_bp_nr = -1;
  1250.     string bps = "";
  1251.     MapRef ref;
  1252.     for (BreakPoint* bp = bp_map.first(ref);
  1253.      bp != 0;
  1254.      bp = bp_map.next(ref))
  1255.     {
  1256.     if (bp->number() >= first_bp
  1257.         && bp_matches(bp, file, line_no))
  1258.         {
  1259.         if (bps != "")
  1260.             bps += gdb->wants_delete_comma() ? ", " : " ";
  1261.         bps += itostring(bp->number());
  1262.         max_bp_nr = max(max_bp_nr, bp->number());
  1263.         }
  1264.     }
  1265.  
  1266.     if (bps == "")
  1267.     return "";
  1268.  
  1269.     if (clear_next && max_bp_nr >= 0)
  1270.     {
  1271.     bps += (gdb->wants_delete_comma() ? ", " : " ");
  1272.     bps += itostring(max_bp_nr + 1);
  1273.     }
  1274.  
  1275.     return gdb->delete_command(bps);
  1276. }
  1277.  
  1278.  
  1279. // ***************************************************************************
  1280. //
  1281. void SourceView::bp_popup_set_pcCB(Widget w, XtPointer client_data, 
  1282.                    XtPointer call_data)
  1283. {
  1284.     int bp_nr = *((int *)client_data);
  1285.     BreakPoint *bp = bp_map.get(bp_nr);
  1286.     if (bp != 0 && bp->address() != "")
  1287.     {
  1288.     string address = string('*') + bp->address();
  1289.     line_popup_set_pcCB(w, XtPointer(&address), call_data);
  1290.     }
  1291. }
  1292.  
  1293. // ***************************************************************************
  1294. //
  1295. void SourceView::text_popup_breakCB (Widget w,
  1296.                      XtPointer client_data,
  1297.                      XtPointer)
  1298. {
  1299.     string* word_ptr = (string*)client_data;
  1300.     create_bp(fortranize(*word_ptr, true), w);
  1301. }
  1302.  
  1303. void SourceView::text_popup_clearCB (Widget w, 
  1304.                      XtPointer client_data, 
  1305.                      XtPointer)
  1306. {
  1307.     string* word_ptr = (string*)client_data;
  1308.     clear_bp(fortranize(*word_ptr, true), w);
  1309. }
  1310.  
  1311.  
  1312.  
  1313. // ***************************************************************************
  1314. //
  1315. void SourceView::text_popup_printCB (Widget w, 
  1316.                      XtPointer client_data, 
  1317.                      XtPointer)
  1318. {
  1319.     string* word_ptr = (string*)client_data;
  1320.     assert(word_ptr->length() > 0);
  1321.  
  1322.     gdb_command(gdb->print_command(fortranize(*word_ptr), false), w);
  1323. }
  1324.  
  1325. void SourceView::text_popup_print_refCB (Widget w, 
  1326.                      XtPointer client_data, XtPointer)
  1327. {
  1328.     string* word_ptr = (string*)client_data;
  1329.     assert(word_ptr->length() > 0);
  1330.  
  1331.     gdb_command(gdb->print_command(deref(fortranize(*word_ptr)), false), w);
  1332. }
  1333.  
  1334.  
  1335. // ***************************************************************************
  1336. //
  1337. void SourceView::text_popup_watchCB (Widget w, 
  1338.                      XtPointer client_data, 
  1339.                      XtPointer)
  1340. {
  1341.     string* word_ptr = (string*)client_data;
  1342.     assert(word_ptr->length() > 0);
  1343.  
  1344.     gdb_command(gdb->watch_command(fortranize(*word_ptr)), w);
  1345. }
  1346.  
  1347. void SourceView::text_popup_watch_refCB (Widget w, 
  1348.                      XtPointer client_data, XtPointer)
  1349. {
  1350.     string* word_ptr = (string*)client_data;
  1351.     assert(word_ptr->length() > 0);
  1352.  
  1353.     gdb_command(gdb->watch_command(deref(fortranize(*word_ptr))), w);
  1354. }
  1355.  
  1356.  
  1357. // ***************************************************************************
  1358. //
  1359. void SourceView::text_popup_dispCB (Widget w, XtPointer client_data, XtPointer)
  1360. {
  1361.     string* word_ptr = (string*)client_data;
  1362.     assert(word_ptr->length() > 0);
  1363.  
  1364.     gdb_command("graph display " + fortranize(*word_ptr), w);
  1365. }
  1366.  
  1367. void SourceView::text_popup_disp_refCB (Widget w, 
  1368.                     XtPointer client_data, XtPointer)
  1369. {
  1370.     string* word_ptr = (string*)client_data;
  1371.     assert(word_ptr->length() > 0);
  1372.  
  1373.     gdb_command("graph display " + deref(fortranize(*word_ptr)), w);
  1374. }
  1375.  
  1376. // ***************************************************************************
  1377. //
  1378. void SourceView::text_popup_whatisCB (Widget w, XtPointer client_data, 
  1379.                       XtPointer)
  1380. {
  1381.     string* word_ptr = (string*)client_data;
  1382.     assert(word_ptr->length() > 0);
  1383.  
  1384.     gdb_command(gdb->whatis_command(fortranize(*word_ptr)), w);
  1385. }
  1386.  
  1387. // ***************************************************************************
  1388. //
  1389. void SourceView::text_popup_lookupCB (Widget, XtPointer client_data, XtPointer)
  1390. {
  1391.     string* word_ptr = (string*)client_data;
  1392.     lookup(fortranize(*word_ptr, true));
  1393. }
  1394.  
  1395.  
  1396. // ***************************************************************************
  1397. //
  1398.  
  1399. // Return the normalized full path of FILE
  1400. string SourceView::full_path(string file)
  1401. {
  1402.     /* Chris van Engelen <engelen@lucent.com>, Jul 10, 1997
  1403.      *
  1404.      * The regular expression is used to remove parts from the full path
  1405.      * which look like "/aap/../". However, it should ***NOT*** remove
  1406.      * the sequence "/../../" from the path, obviously ! On the other
  1407.      * hand, sequences like "/.tmpdir.test/../" should be removed.
  1408.      * Therefore, the regular expression reads now like:
  1409.      *
  1410.      * - forward slash
  1411.      * - zero or more  characters other than forward slash (dot is allowed)
  1412.      * - any character other than forward slash or dot: this makes sure that
  1413.      *   a sequence like "/../../" is not modified.
  1414.      * - forward slash, two dots, and the final forward slash.
  1415.      *
  1416.      * The only valid patterns which are not normalized are patterns ending
  1417.      * in a dot: too bad, you can't win them all.
  1418.      */
  1419.  
  1420. #if RUNTIME_REGEX
  1421.     static regex rxdotdot("/[^/]*[^/.]/\\.\\./");
  1422. #endif
  1423.  
  1424.     file += '/';
  1425.  
  1426.     if (!file.contains('/', 0))
  1427.         file = current_pwd + "/" + file;
  1428.  
  1429.     /* CvE, Jul 10, 1997
  1430.      *
  1431.      * Repeatedly remove patterns like /dir1/../ from the file name.
  1432.      * Note that a number of /../ patterns may follow each other, like
  1433.      * in "/dir1/dir1/dir3/../../../"
  1434.      */
  1435.     unsigned int file_length = file.length();
  1436.     unsigned int prev_file_length;
  1437.     do {
  1438.         prev_file_length = file_length;
  1439.         file.gsub(rxdotdot, "/");
  1440.         file_length = file.length();
  1441.     } while (file_length != prev_file_length);
  1442.  
  1443.     /* CvE, Jul 10, 1997
  1444.      *
  1445.      * Repeatedly remove pattern /./ from the file name.
  1446.      * Note that a number of /./ patterns may follow each other.
  1447.      * Note that if the first parameter of gsub is a C-string,
  1448.      * the pattern is not regarded to be a regular expression,
  1449.      * so the dot in the pattern does not need to be escaped!
  1450.      */
  1451.     file_length = file.length();
  1452.     do {
  1453.         prev_file_length = file_length;
  1454.         file.gsub("/./", "/");
  1455.         file_length = file.length();
  1456.     } while (file_length != prev_file_length);
  1457.  
  1458.     file.gsub("//", "/");
  1459.  
  1460.     if (file.contains('/', -1))
  1461.     file = file.before(int(file.length() - 1));
  1462.  
  1463.     return file;
  1464. }
  1465.  
  1466. // Return the basename of FILE.  We don't use the default ::basename(),
  1467. // due to conflicts with the decl in <libiberty.h>.
  1468. const char *SourceView::basename(const char *name)
  1469. {
  1470.     const char *base = name;
  1471.  
  1472.     while (*name)
  1473.     {
  1474.     if (*name++ == '/')
  1475.         base = name;
  1476.     }
  1477.  
  1478.     return base;
  1479. }
  1480.  
  1481. bool SourceView::file_matches(const string& file1, const string& file2)
  1482. {
  1483.     if (gdb->type() == JDB)
  1484.     return file1 == file2;
  1485.  
  1486.     if (gdb->type() == GDB || app_data.use_source_path)
  1487.     return file1 == file2 || full_path(file1) == full_path(file2);
  1488.  
  1489.     return base_matches(file1, file2);
  1490. }
  1491.  
  1492. bool SourceView::is_current_file(const string& file)
  1493. {
  1494.     if (gdb->type() == JDB || gdb->type() == PYDB)
  1495.     return file == current_source_name();
  1496.     else
  1497.     return file_matches(file, current_file_name);
  1498. }
  1499.  
  1500. bool SourceView::base_matches(const string& file1, const string& file2)
  1501. {
  1502.     return string(basename(file1.chars())) == string(basename(file2.chars()));
  1503. }
  1504.  
  1505. // Check if BP occurs in the current source text
  1506. bool SourceView::bp_matches(BreakPoint *bp, int line)
  1507. {
  1508.     return bp_matches(bp, current_source_name(), line) || 
  1509.     bp_matches(bp, current_file_name, line);
  1510. }
  1511.  
  1512. bool SourceView::bp_matches(BreakPoint *bp, const string& file, int line)
  1513. {
  1514.     return bp->type() == BREAKPOINT && 
  1515.     (line == 0 || bp->line_nr() == line) &&
  1516.     (bp->file_name() == "" || file_matches(bp->file_name(), file));
  1517. }
  1518.  
  1519. // ***************************************************************************
  1520. //
  1521.  
  1522. // If this is true, no motion occurred while selecting
  1523. static bool selection_click = false;
  1524.  
  1525. static string last_info_output = "";
  1526.  
  1527. void SourceView::set_source_argCB(Widget text_w, 
  1528.                   XtPointer client_data, 
  1529.                   XtPointer call_data)
  1530. {
  1531.     string& text = current_text(text_w);
  1532.     if (text == "")
  1533.     return;
  1534.  
  1535.     XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)call_data;
  1536.     bool motion = bool((int)(long)client_data);
  1537.     if (motion)
  1538.     selection_click = false;
  1539.  
  1540.     XmTextPosition startPos, endPos;
  1541.     Boolean have_selection = 
  1542.     XmTextGetSelectionPosition(text_w, &startPos, &endPos);
  1543.  
  1544.     if (have_selection && lesstif_version < 1000)
  1545.     {
  1546.     // In LessTif 0.87, the unmanaged DataDisp::graph_selection_w
  1547.     // text widget is not notified that it just has lost the
  1548.     // selection.  The effect is that selecting an item from the
  1549.     // source (or a selection in any other window) does *not*
  1550.     // clear the selection in the data window, as it should.
  1551.     // As a workaround, notify explicitly.
  1552.     data_disp->SelectionLostCB();
  1553.     }
  1554.  
  1555.     if (!have_selection || (app_data.source_editing && startPos == endPos))
  1556.     {
  1557.     // No selection?  If the current motion was caused by a mouse
  1558.     // click, fetch word at current cursor position instead.
  1559.     if (cbs != 0 && cbs->reason == XmCR_MOVING_INSERT_CURSOR)
  1560.     {
  1561.         XEvent *event = cbs->event;
  1562.         if (event == 0)
  1563.         {
  1564.         // LessTif 0.79 may not pass a reasonable event here.
  1565. #if XtSpecificationRelease >= 6
  1566.         // Use the last processed event instead.
  1567.         event = XtLastEventProcessed(XtDisplay(text_w));
  1568. #else
  1569.         // Use the last recorded selection event instead.
  1570.         event = &selection_event;
  1571. #endif
  1572.         selection_click = true;
  1573.         }
  1574.  
  1575.         // Button4 and Button5 events are generated by wheel mouses.
  1576.         // Don't change the selection when the wheel is activated.
  1577.         if (event != 0 && 
  1578.         (event->type == ButtonPress || event->type == ButtonRelease) &&
  1579.         event->xbutton.button != Button4 &&
  1580.         event->xbutton.button != Button5)
  1581.         {
  1582.         find_word_bounds(text_w, cbs->newInsert, 
  1583.                  startPos, endPos);
  1584.         have_selection = True;
  1585.         }
  1586.     }
  1587.     }
  1588.  
  1589.     if (!have_selection && cbs == 0)
  1590.     {
  1591.     // Still no selection?  We're probably called from setSelection();
  1592.     // use the last selected word instead.
  1593.     startPos = selection_startpos;
  1594.     endPos   = selection_endpos;
  1595.     have_selection = True;
  1596.     }
  1597.  
  1598. #if XtSpecificationRelease < 6
  1599.     // Don't use this selection event again.
  1600.     selection_event.type = KeyPress;
  1601. #endif
  1602.  
  1603.     if (!have_selection)
  1604.     {
  1605.     // No selection - sorry
  1606.     return;
  1607.     }
  1608.  
  1609.     int startIndex = 0;
  1610.     if (startPos > 0)
  1611.     startIndex = text.index('\n', startPos - text.length()) + 1;
  1612.  
  1613.     int endIndex = 0;
  1614.     if (endPos > 0)
  1615.     endIndex = text.index('\n', endPos - text.length()) + 1;
  1616.  
  1617.     bool in_bp_area = false;
  1618.     if (selection_click && startIndex == endIndex)
  1619.     {
  1620.     string pos = "";
  1621.  
  1622.     if (text_w == source_text_w)
  1623.     {
  1624.         int line_nr = 0;
  1625.         bool in_text;
  1626.         int bp_nr;
  1627.         string address;
  1628.  
  1629.         if (get_line_of_pos(source_text_w, startPos, line_nr, address, 
  1630.                 in_text, bp_nr) 
  1631.         && !in_text)
  1632.         {
  1633.         in_bp_area = true;
  1634.  
  1635.         // Selection from line number area: prepend source file name
  1636.         pos = current_source_name() + ":" + itostring(line_nr);
  1637.         source_arg->set_string(pos);
  1638.  
  1639.         // If a breakpoint is here, select this one only
  1640.         MapRef ref;
  1641.         for (BreakPoint* bp = bp_map.first(ref);
  1642.              bp != 0;
  1643.              bp = bp_map.next(ref))
  1644.         {
  1645.             bp->selected() = (bp_matches(bp, line_nr));
  1646.         }
  1647.         }
  1648.     }
  1649.     else if (text_w == code_text_w
  1650.          && startPos - startIndex <= indent_amount(text_w)
  1651.          && endPos - endIndex <= indent_amount(text_w))
  1652.     {
  1653.         // Selection from address area
  1654.         int index = address_index(text, startPos);
  1655.         if (index >= 0)
  1656.         {
  1657.         in_bp_area = true;
  1658.  
  1659.         pos = text.from(index);
  1660.         pos = pos.through(rxaddress);
  1661.  
  1662.         source_arg->set_string(pos);
  1663.  
  1664.         // If a breakpoint is here, select this one only
  1665.         MapRef ref;
  1666.         for (BreakPoint* bp = bp_map.first(ref);
  1667.              bp != 0;
  1668.              bp = bp_map.next(ref))
  1669.         {
  1670.             bp->selected() = 
  1671.             (bp->type() == BREAKPOINT && 
  1672.              compare_address(pos, bp->address()) == 0);
  1673.         }
  1674.         }
  1675.     }
  1676.  
  1677.     selection_click = false;
  1678.     }
  1679.  
  1680.     if (in_bp_area)
  1681.     {
  1682.     // Update breakpoint selection
  1683.     process_breakpoints(last_info_output);
  1684.     }
  1685.     else if (!motion && !selection_click)
  1686.     {
  1687.     // Selection from source or code
  1688.     string s;
  1689.     if (startPos < XmTextPosition(text.length())
  1690.         && endPos < XmTextPosition(text.length()))
  1691.     {
  1692.         s = text(startPos, endPos - startPos);
  1693.     }
  1694.  
  1695.     while (s.contains('\n'))
  1696.         s = s.after('\n');
  1697.  
  1698.     if (s != "")
  1699.         source_arg->set_string(s);
  1700.     }
  1701. }
  1702.  
  1703.  
  1704. BreakPoint *SourceView::breakpoint_at(string arg)
  1705. {
  1706.     MapRef ref;
  1707.     for (BreakPoint* bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  1708.     {
  1709.     if (bp->type() != BREAKPOINT)
  1710.         continue;
  1711.  
  1712.     if (arg.matches(rxint))
  1713.     {
  1714.         // Line number for current source given
  1715.         if (bp_matches(bp, atoi(arg)))
  1716.         return bp;
  1717.     }
  1718.     else
  1719.     {
  1720.         string pos = arg;
  1721.  
  1722.         if (!is_file_pos(pos))
  1723.         {
  1724.         // Function given
  1725.         if (bp->arg() == pos)
  1726.             return bp;
  1727.  
  1728.         if (gdb->type() == DBX)
  1729.             pos = dbx_lookup(arg);
  1730.         }
  1731.         
  1732.         if (is_file_pos(pos))
  1733.         {
  1734.         // File:line given
  1735.         string file = pos.before(':');
  1736.         string line = pos.after(':');
  1737.  
  1738.         if (bp_matches(bp, file, atoi(line)))
  1739.             return bp;
  1740.         }
  1741.     }
  1742.     }
  1743.  
  1744.     return 0;
  1745. }
  1746.  
  1747. BreakPoint *SourceView::watchpoint_at(string expr)
  1748. {
  1749.     for (int trial = 0; trial <= 2; trial++)
  1750.     {
  1751.     MapRef ref;
  1752.     for (BreakPoint* bp = bp_map.first(ref); bp != 0; 
  1753.          bp = bp_map.next(ref))
  1754.     {
  1755.         if (bp->type() != WATCHPOINT)
  1756.         continue;
  1757.  
  1758.         switch (trial)
  1759.         {
  1760.         case 0:
  1761.         if (bp->expr() == expr)
  1762.         {
  1763.             // Expression matches exactly
  1764.             return bp;
  1765.         }
  1766.         break;
  1767.  
  1768.         case 1:
  1769.         if (bp->expr().contains('(') && bp->expr().before('(') == expr)
  1770.         {
  1771.             // Expr matches EXPR(...)  (e.g. a qualified function name)
  1772.             return bp;
  1773.         }
  1774.  
  1775.         case 2:
  1776.         if (bp->expr().contains("`" + expr, -1) ||
  1777.             bp->expr().contains("::" + expr, -1))
  1778.         {
  1779.             // Expr matches ...`EXPR (a Sun DBX identifier)
  1780.             // or ...::EXPR (an SGI DBX identifier)
  1781.             return bp;
  1782.         }
  1783.         }
  1784.     }
  1785.     }
  1786.  
  1787.     return 0;
  1788. }
  1789.  
  1790.  
  1791. // ***************************************************************************
  1792.  
  1793. // Show position POS in TEXT_W, scrolling nicely
  1794. void SourceView::ShowPosition(Widget text_w, XmTextPosition pos, bool fromTop)
  1795. {
  1796.     string& text = current_text(text_w);
  1797.     if (text.length() == 0)
  1798.     return;            // No position to show
  1799.  
  1800.     short rows = 0;
  1801.     XmTextPosition current_top = 0;
  1802.     XtVaGetValues(text_w,
  1803.           XmNrows, &rows,
  1804.           XmNtopCharacter, ¤t_top,
  1805.           NULL);
  1806.  
  1807.     // Find current relative row
  1808.     short relative_row = 1;
  1809.     for (XmTextPosition p = min(text.length() - 1, pos); p > current_top; p--)
  1810.     if (text[p] == '\n')
  1811.         relative_row++;
  1812.  
  1813.     if (relative_row <= lines_above_cursor 
  1814.     || relative_row >= rows - (lines_below_cursor + 1))
  1815.     {
  1816.     // Determine new TOP position
  1817.     short n = rows / 2;    // #Lines between new TOP and POS
  1818.     if (fromTop || relative_row <= lines_above_cursor)
  1819.         n = lines_above_cursor;
  1820.     else if (relative_row >= rows - (lines_below_cursor + 1))
  1821.         n = rows - (lines_below_cursor + 1);
  1822.  
  1823.     XmTextPosition new_top = pos;
  1824.     for (;;) {
  1825.         while (new_top > 0 && text[new_top - 1] != '\n')
  1826.         new_top--;
  1827.         if (new_top == 0 || n-- <= 0)
  1828.         break;
  1829.         new_top--;
  1830.     }
  1831.  
  1832.     XmTextSetTopCharacter(text_w, new_top);
  1833.     }
  1834.  
  1835.     XmTextShowPosition(text_w, pos);    // just to make sure
  1836. }
  1837.  
  1838. void SourceView::SetInsertionPosition(Widget text_w, 
  1839.                       XmTextPosition pos, bool fromTop)
  1840. {
  1841.     ShowPosition(text_w, pos, fromTop);
  1842.     XmTextSetInsertionPosition(text_w, pos);
  1843. }
  1844.  
  1845.  
  1846.  
  1847. //-----------------------------------------------------------------------
  1848. // Error handling
  1849. //-----------------------------------------------------------------------
  1850.  
  1851. StringArray SourceView::bad_files;
  1852. bool SourceView::new_bad_file(const string& file_name)
  1853. {
  1854.     for (int i = 0; i < bad_files.size(); i++)
  1855.     if (file_name == bad_files[i])
  1856.         return false;
  1857.     bad_files += file_name;
  1858.     return true;
  1859. }
  1860.  
  1861. void SourceView::post_file_error(const string& file_name,
  1862.                  string text, String name,
  1863.                  Widget origin)
  1864. {
  1865.     if (new_bad_file(file_name))
  1866.     post_error(text, name, origin);
  1867. }
  1868.  
  1869. void SourceView::post_file_warning(const string& file_name,
  1870.                    string text, String name,
  1871.                    Widget origin)
  1872. {
  1873.     if (new_bad_file(file_name))
  1874.     post_warning(text, name, origin);
  1875. }
  1876.  
  1877.  
  1878.  
  1879.  
  1880. //-----------------------------------------------------------------------
  1881. // Read file
  1882. //-----------------------------------------------------------------------
  1883.  
  1884. // Read local file from FILE_NAME
  1885. String SourceView::read_local(const string& file_name, long& length,
  1886.                   bool silent)
  1887. {
  1888.     StatusDelay delay("Reading file " + quote(file_name));
  1889.     length = 0;
  1890.  
  1891.     // Make sure the file is a regular text file and open it
  1892.     int fd;
  1893.     if ((fd = open(file_name, O_RDONLY)) < 0)
  1894.     {
  1895.     delay.outcome = strerror(errno);
  1896.     if (!silent)
  1897.         post_file_error(file_name, 
  1898.                 file_name + ": " + delay.outcome, 
  1899.                 "source_file_error", source_text_w);
  1900.         return 0;
  1901.     }
  1902.  
  1903.     struct stat statb;
  1904.     if (fstat(fd, &statb) < 0)
  1905.     {
  1906.     delay.outcome = strerror(errno);
  1907.     if (!silent)
  1908.         post_file_error(file_name,
  1909.                 file_name + ": " + delay.outcome, 
  1910.                 "source_file_error", source_text_w);
  1911.     return 0;
  1912.     }
  1913.  
  1914.     // Avoid loading from directory, socket, device, or otherwise.
  1915.     if (!S_ISREG(statb.st_mode))
  1916.     {
  1917.     delay.outcome = "not a regular file";
  1918.     if (!silent)
  1919.         post_file_error(file_name,
  1920.                 file_name + ": " + delay.outcome, 
  1921.                 "source_file_error", source_text_w);
  1922.     return 0;
  1923.     }
  1924.  
  1925.     // Put the contents of the file in the Text widget by allocating
  1926.     // enough space for the entire file and reading the file into the
  1927.     // allocated space.
  1928.     char* text = XtMalloc(unsigned(statb.st_size + 1));
  1929.     if ((length = read(fd, text, statb.st_size)) != statb.st_size)
  1930.     {
  1931.     delay.outcome = "truncated";
  1932.     if (!silent)
  1933.         post_file_error(file_name,
  1934.                 file_name + ": " + delay.outcome,
  1935.                 "source_trunc_error", source_text_w);
  1936.     }
  1937.     close(fd);
  1938.  
  1939.     text[statb.st_size] = '\0'; // be sure to null-terminate
  1940.  
  1941.     if (statb.st_size == 0)
  1942.     {
  1943.     delay.outcome = "empty file";
  1944.     if (!silent)
  1945.         post_file_warning(file_name,
  1946.                   file_name + ": " + delay.outcome,
  1947.                   "source_empty_warning", source_text_w);
  1948.     }
  1949.  
  1950.     return text;
  1951. }
  1952.  
  1953.  
  1954. // Read (possibly remote) file FILE_NAME; a little slower
  1955. String SourceView::read_remote(const string& file_name, long& length, 
  1956.                    bool silent)
  1957. {
  1958.     StatusDelay delay("Reading file " + 
  1959.               quote(file_name) + " from " + gdb_host);
  1960.     length = 0;
  1961.  
  1962.     string cat_command = sh_command("cat " + file_name);
  1963.  
  1964.     Agent cat(cat_command);
  1965.     cat.start();
  1966.  
  1967.     FILE *fp = cat.inputfp();
  1968.     if (fp == 0)
  1969.     {
  1970.     delay.outcome = "failed";
  1971.     return 0;
  1972.     }
  1973.  
  1974.     String text = XtMalloc(1);
  1975.  
  1976.     do {
  1977.     text = XtRealloc(text, length + BUFSIZ + 1);
  1978.     length += fread(text + length, sizeof(char), BUFSIZ, fp);
  1979.     } while (!feof(fp));
  1980.  
  1981.     text[length] = '\0';  // be sure to null-terminate
  1982.  
  1983.     if (length == 0)
  1984.     {
  1985.     if (!silent)
  1986.         post_file_error(file_name,
  1987.                 "Cannot access remote file " + quote(file_name), 
  1988.                 "remote_file_error", source_text_w);
  1989.     delay.outcome = "failed";
  1990.     }
  1991.  
  1992.     return text;
  1993. }
  1994.  
  1995. // Read class CLASS_NAME
  1996. String SourceView::read_class(const string& class_name, 
  1997.                   string& file_name, SourceOrigin& origin,
  1998.                   long& length, bool silent)
  1999. {
  2000.     StatusDelay delay("Loading class " + quote(class_name));
  2001.     
  2002.     String text = 0;
  2003.     length = 0;
  2004.  
  2005.     file_name = java_class_file(class_name);
  2006.  
  2007.     if (file_name != "")
  2008.     {
  2009.     if (remote_gdb())
  2010.         text = read_remote(file_name, length, true);
  2011.     else
  2012.     {
  2013.         file_name = full_path(file_name);
  2014.         text = read_local(file_name, length, true);
  2015.     }
  2016.     }
  2017.  
  2018.     if (text != 0 && length != 0)
  2019.     {
  2020.     // Save class name for further reference
  2021.     source_name_cache[file_name] = class_name;
  2022.     origin = remote_gdb() ? ORIGIN_REMOTE : ORIGIN_LOCAL;
  2023.     return text;
  2024.     }
  2025.     else
  2026.     {
  2027.     // Could not load class
  2028.     file_name = class_name;
  2029.     origin = ORIGIN_NONE;
  2030.     delay.outcome = "failed";
  2031.     if (!silent)
  2032.         post_file_error(class_name,
  2033.                 "Cannot access class " + quote(class_name),
  2034.                 "class_error", source_text_w);
  2035.  
  2036.     return 0;
  2037.     }
  2038. }
  2039.  
  2040. #define HUGE_LINE_NUMBER "1000000"
  2041.  
  2042. // Read file FILE_NAME via the GDB `list' function
  2043. // Really slow, is guaranteed to work for source files.
  2044. String SourceView::read_from_gdb(const string& file_name, long& length, 
  2045.                  bool /* silent */)
  2046. {
  2047.     length = 0;
  2048.     if (!gdb->isReadyWithPrompt())
  2049.     return 0;
  2050.     if (gdb->type() == JDB)
  2051.     return 0;        // Won't work with JDB
  2052.  
  2053.     StatusDelay delay("Reading file " + quote(file_name) + 
  2054.               " from " + gdb->title());
  2055.  
  2056.     string command;
  2057.     switch (gdb->type())
  2058.     {
  2059.     case GDB:
  2060.     command = "list " + file_name + ":1," HUGE_LINE_NUMBER;
  2061.     break;
  2062.  
  2063.     case DBX:
  2064.     case PYDB:
  2065.     command = "list 1," HUGE_LINE_NUMBER;
  2066.     break;
  2067.  
  2068.     case PERL:
  2069.     command = "l 1-" HUGE_LINE_NUMBER;
  2070.     break;
  2071.  
  2072.     case JDB:
  2073.     command = "list " + file_name;
  2074.     break;
  2075.  
  2076.     case XDB:
  2077.     command = "w " HUGE_LINE_NUMBER;
  2078.     break;
  2079.     }
  2080.     string listing = gdb_question(command, -1, true);
  2081.  
  2082.     // GDB listings have the format <NUMBER>\t<LINE>.
  2083.     // Copy LINE only; line numbers will be re-added later.
  2084.     // Note that tabs may be expanded to spaces due to a PTY interface.
  2085.     String text = XtMalloc(listing.length());
  2086.  
  2087.     int i = 0;
  2088.     length = 0;
  2089.     while (i < int(listing.length()))
  2090.     {
  2091.     int count = 0;
  2092.  
  2093.     // Skip leading spaces.  Some debuggers also issue `*', `=',
  2094.     // or `>' to indicate the current position.
  2095.     while (count < 8
  2096.            && i < int(listing.length())
  2097.            && (isspace(listing[i])
  2098.            || listing[i] == '=' 
  2099.            || listing[i] == '*'
  2100.            || listing[i] == '>'))
  2101.         i++, count++;
  2102.  
  2103.     if (i < int(listing.length()) && isdigit(listing[i]))
  2104.     {
  2105.         // Skip line number
  2106.         while (i < int(listing.length()) && isdigit(listing[i]))
  2107.         i++, count++;
  2108.  
  2109.         // Skip `:' (XDB output)
  2110.         if (count < 8 && i < int(listing.length()) && listing[i] == ':')
  2111.         i++, count++;
  2112.  
  2113.         // Break at first non-blank character or after 8 characters
  2114.         while (count < 8 && i < int(listing.length()) && listing[i] == ' ')
  2115.         i++, count++;
  2116.  
  2117.         // Skip tab character
  2118.         if (count < 8 && i < int(listing.length()) && listing[i] == '\t')
  2119.         i++;
  2120.  
  2121.         // Copy line
  2122.         while (i < int(listing.length()) && listing[i] != '\n')
  2123.         text[length++] = listing[i++];
  2124.  
  2125.         // Copy newline character
  2126.         text[length++] = '\n';
  2127.         i++;
  2128.     }
  2129.     else
  2130.     {
  2131.         int start = i;
  2132.  
  2133.         // Some other line -- the prompt, maybe?
  2134.         while (i < int(listing.length()) && listing[i] != '\n')
  2135.         i++;
  2136.         if (i < int(listing.length()))
  2137.         i++;
  2138.  
  2139.         string msg = listing.from(start);
  2140.         msg = msg.before('\n');
  2141.         if (!msg.contains("end of file")) // XDB issues this
  2142.         post_gdb_message(msg, true, source_text_w);
  2143.     }
  2144.     }
  2145.  
  2146.     text[length] = '\0';  // be sure to null-terminate
  2147.  
  2148.     if (length == 0)
  2149.     delay.outcome = "failed";
  2150.  
  2151.     return text;
  2152. }
  2153.  
  2154.  
  2155. // Read file FILE_NAME and format it
  2156. String SourceView::read_indented(string& file_name, long& length, 
  2157.                  SourceOrigin& origin, bool silent)
  2158. {
  2159.     length = 0;
  2160.     Delay delay;
  2161.     long t;
  2162.     
  2163.     String text = 0;
  2164.     origin = ORIGIN_NONE;
  2165.     string full_file_name = file_name;
  2166.  
  2167.     if (gdb->type() == JDB && !file_name.contains('/'))
  2168.     {
  2169.     // FILE_NAME is a class name.  Search class in JDB `use' path.
  2170.     text = read_class(file_name, full_file_name, origin, length, true);
  2171.     }
  2172.  
  2173.     if (gdb->type() == PERL && file_name.contains('-', 0))
  2174.     {
  2175.     // Attempt to load `-e' in Perl or likewise
  2176.     origin = ORIGIN_NONE;
  2177.     return 0;
  2178.     }
  2179.  
  2180.     if (text == 0 || length == 0)
  2181.     {
  2182.     for (int trial = 1; (text == 0 || length == 0) && trial <= 2; trial++)
  2183.     {
  2184.         switch (trial)
  2185.         {
  2186.         case 1:
  2187.         // Loop #1: use full path of file
  2188.         full_file_name = full_path(file_name);
  2189.         break;
  2190.  
  2191.         case 2:
  2192.         // Loop #2: ask debugger for full path, using `edit'
  2193.         full_file_name = full_path(dbx_path(file_name));
  2194.         if (full_file_name == full_path(file_name))
  2195.             continue;
  2196.         break;
  2197.         }
  2198.  
  2199.         // Attempt #1.  Try to read file from remote source.
  2200.         if ((text == 0 || length == 0) && remote_gdb())
  2201.         {
  2202.         text = read_remote(full_file_name, length, true);
  2203.         if (text != 0)
  2204.             origin = ORIGIN_REMOTE;
  2205.         }
  2206.  
  2207.         // Attempt #2.  Read file from local source.
  2208.         if ((text == 0 || length == 0) && !remote_gdb())
  2209.         {
  2210.         text = read_local(full_file_name, length, true);
  2211.         if (text != 0)
  2212.             origin = ORIGIN_LOCAL;
  2213.         }
  2214.  
  2215.         // Attempt #3.  Read file from local source, even if we are remote.
  2216.         if ((text == 0 || length == 0) && remote_gdb())
  2217.         {
  2218.         text = read_local(full_file_name, length, true);
  2219.         if (text != 0)
  2220.             origin = ORIGIN_LOCAL;
  2221.         }
  2222.     }
  2223.     }
  2224.  
  2225.     // Attempt #4.  Read file from GDB.
  2226.     if (text == 0 || length == 0)
  2227.     {
  2228.     string saved_current_file_name = current_file_name;
  2229.     current_file_name = full_file_name;
  2230.     string source_name = current_source_name();
  2231.     current_file_name = saved_current_file_name;
  2232.  
  2233.     text = read_from_gdb(source_name, length, silent);
  2234.  
  2235.     if (text != 0 && length != 0)
  2236.     {
  2237.         // Use the source name as file name
  2238.         full_file_name = source_name;
  2239.         if (text != 0)
  2240.         origin = ORIGIN_GDB;
  2241.     }
  2242.     }
  2243.  
  2244.     if ((text == 0 || length == 0) && !silent)
  2245.     {
  2246.     // All failed - produce an appropriate error message.
  2247.     if (gdb->type() == JDB)
  2248.         text = read_class(file_name, full_file_name, origin, 
  2249.                   length, false);
  2250.     else if (!remote_gdb())
  2251.         text = read_local(full_file_name, length, false);
  2252.     else
  2253.         text = read_remote(full_file_name, length, false);
  2254.     }
  2255.  
  2256.     if (text == 0 || length == 0)
  2257.     {
  2258.     origin = ORIGIN_NONE;
  2259.     return 0;
  2260.     }
  2261.  
  2262.     // At this point, we have a source text.
  2263.     file_name = full_file_name;
  2264.  
  2265.     // Determine text length and number of lines
  2266.     int lines = 0;
  2267.     for (t = 0; t < length; t++)
  2268.     if (text[t] == '\n')
  2269.         lines++;
  2270.  
  2271.     int indented_text_length = length;
  2272.     if (length > 0 && text[length - 1] != '\n')
  2273.     {
  2274.     // Text does not end in '\n':
  2275.     // Make room for final '\n'
  2276.     indented_text_length += 1;
  2277.  
  2278.     // Make room for final line
  2279.     lines++;
  2280.     }
  2281.  
  2282.     // Make room for line numbers
  2283.     int indent = indent_amount(source_text_w);
  2284.     indented_text_length += (indent + script_indent_amount) * lines;
  2285.  
  2286.     String indented_text = XtMalloc(indented_text_length + 1);
  2287.  
  2288.     string line_no_s = replicate(' ', indent);
  2289.  
  2290.     t = 0;
  2291.     char *pos_ptr = indented_text; // Writing position in indented_text
  2292.     while (t < length)
  2293.     {
  2294.     assert (pos_ptr - indented_text <= indented_text_length);
  2295.  
  2296.     // Increase line number
  2297.     int i;
  2298.     for (i = indent - 2; i >= 0; i--)
  2299.     {
  2300.         char& c = line_no_s[i];
  2301.         if (c == ' ')
  2302.         {
  2303.         c = '1';
  2304.         break;
  2305.         }
  2306.         else if (c < '9')
  2307.         {
  2308.         c++;
  2309.         break;
  2310.         }
  2311.         else
  2312.         c = '0';
  2313.     }
  2314.  
  2315.     // Copy line number
  2316.     for (i = 0; i < indent; i++)
  2317.         *pos_ptr++ = display_line_numbers ? line_no_s[i] : ' ';
  2318.  
  2319.     if (indent < script_indent_amount)
  2320.     {
  2321.         // Check for empty line or line starting with '\t'
  2322.         int spaces = 0;
  2323.         while (t + spaces < length && text[t + spaces] == ' ')
  2324.         spaces++;
  2325.  
  2326.         if (spaces < script_indent_amount)
  2327.         {
  2328.         if (t + spaces >= length ||
  2329.             text[t + spaces] == '\n' ||
  2330.             text[t + spaces] == '\t')
  2331.         {
  2332.             // Prepend a few space characters
  2333.             while (spaces < script_indent_amount)
  2334.             {
  2335.             *pos_ptr++ = ' ';
  2336.             spaces++;
  2337.             }
  2338.         }
  2339.         }
  2340.     }
  2341.  
  2342.     // Copy remainder of line
  2343.     while (t < length && text[t] != '\n')
  2344.         *pos_ptr++ = text[t++];
  2345.  
  2346.     // Copy '\n' or '\0'
  2347.     if (t == length)
  2348.     {
  2349.         // Text doesn't end in '\n'
  2350.         *pos_ptr++ = '\n';
  2351.     }
  2352.     else
  2353.     {
  2354.         *pos_ptr++ = text[t++];
  2355.     }
  2356.     }
  2357.     *pos_ptr = '\0';
  2358.  
  2359.     XtFree(text);
  2360.  
  2361.     length = pos_ptr - indented_text;
  2362.     return indented_text;
  2363. }
  2364.  
  2365.  
  2366. // Read file FILE_NAME into current_source; get it from the cache if possible
  2367. int SourceView::read_current(string& file_name, bool force_reload, bool silent)
  2368. {
  2369.     string requested_file_name = file_name;
  2370.  
  2371.     if (cache_source_files && !force_reload && file_cache.has(file_name))
  2372.     {
  2373.     current_source = file_cache[file_name];
  2374.     current_origin = origin_cache[file_name];
  2375.     file_name      = file_name_cache[file_name];
  2376.  
  2377.     if (gdb->type() == JDB)
  2378.     {
  2379.         // In JDB, a single source may contain multiple classes.
  2380.         // Store current class name FILE_NAME as source name.
  2381.         source_name_cache[file_name] = requested_file_name;
  2382.     }
  2383.     }
  2384.     else
  2385.     {
  2386.     long length = 0;
  2387.     SourceOrigin orig;
  2388.     String indented_text = read_indented(file_name, length, orig, silent);
  2389.     if (indented_text == 0 || length == 0)
  2390.         return -1;        // Failure
  2391.  
  2392.     current_source = string(indented_text, length);
  2393.     current_origin = orig;
  2394.     XtFree(indented_text);
  2395.  
  2396.     if (current_source.length() > 0)
  2397.     {
  2398.         file_cache[file_name]             = current_source;
  2399.         origin_cache[file_name]           = current_origin;
  2400.         file_name_cache[file_name]        = file_name;
  2401.  
  2402.         if (file_name != requested_file_name)
  2403.         {
  2404.         file_cache[requested_file_name]      = current_source;
  2405.         origin_cache[requested_file_name]    = current_origin;
  2406.         file_name_cache[requested_file_name] = file_name;
  2407.         }
  2408.     }
  2409.  
  2410.     int null_count = current_source.freq('\0');
  2411.     if (null_count > 0 && !silent)
  2412.         post_warning(file_name + ": binary file",
  2413.              "source_binary_warning", source_text_w);
  2414.     }
  2415.  
  2416.     // Untabify current source, using the current tab width
  2417.     untabify_if_needed(current_source, tab_width, 
  2418.                indent_amount(source_text_w));
  2419.  
  2420.     // Setup global parameters
  2421.  
  2422.     // Number of lines
  2423.     line_count   = current_source.freq('\n');
  2424.     _pos_of_line = TextPositionArray(line_count + 2);
  2425.     _pos_of_line.operator += (XmTextPosition(0));
  2426.     _pos_of_line.operator += (XmTextPosition(0));
  2427.  
  2428.     for (int i = 0; i < int(current_source.length()); i++)
  2429.     if (current_source[i] == '\n')
  2430.         _pos_of_line.operator += (XmTextPosition(i + 1));
  2431.  
  2432.     assert(_pos_of_line.size() == line_count + 2);
  2433.  
  2434.     if (current_source.length() == 0)
  2435.     return -1;
  2436.     else
  2437.     return 0;
  2438. }
  2439.  
  2440. // Return position of line LINE
  2441. XmTextPosition SourceView::pos_of_line(int line)
  2442. {
  2443.     if (line < 0 || line > line_count || line >= _pos_of_line.size())
  2444.     return 0;
  2445.     else
  2446.     return _pos_of_line[line];
  2447. }
  2448.  
  2449. // Clear the file cache
  2450. void SourceView::clear_file_cache()
  2451. {
  2452.     static StringStringAssoc string_empty;
  2453.     file_cache        = string_empty;
  2454.     source_name_cache = string_empty;
  2455.     file_name_cache   = string_empty;
  2456.  
  2457.     static StringOriginAssoc origin_empty;
  2458.     origin_cache      = origin_empty;
  2459.  
  2460.     static StringArray bad_files_empty;
  2461.     bad_files         = bad_files_empty;
  2462. }
  2463.  
  2464. void SourceView::reload()
  2465. {
  2466.     // Reload current file
  2467.     if (current_file_name == "")
  2468.     return;
  2469.  
  2470.     string file;
  2471.     if (gdb->type() == JDB)
  2472.     file = line_of_cursor();
  2473.     else
  2474.     file = file_of_cursor();
  2475.  
  2476.     string line = file.after(':');
  2477.     file        = file.before(':');
  2478.  
  2479.     // StatusDelay delay("Reloading " + quote(file));
  2480.  
  2481.     read_file(file, atoi(line), true);
  2482.  
  2483.     // Restore breakpoints
  2484.     refresh_bp_disp();
  2485.  
  2486.     // Restore execution position
  2487.     if (last_execution_file != "")
  2488.     show_execution_position(last_execution_file + ":" + 
  2489.                 itostring(last_execution_line),
  2490.                 at_lowest_frame, signal_received);
  2491. }
  2492.  
  2493.  
  2494. static const int MAX_TAB_WIDTH = 256;
  2495.  
  2496. // Change tab width
  2497. void SourceView::set_tab_width(int width)
  2498. {
  2499.     if (width <= 0)
  2500.     return;
  2501.  
  2502.     if (tab_width != width)
  2503.     {
  2504.     // Make sure the tab width stays within reasonable ranges
  2505.     tab_width = min(max(width, 1), MAX_TAB_WIDTH);
  2506.  
  2507.     if (current_file_name != "")
  2508.     {
  2509.         StatusDelay delay("Reformatting");
  2510.         reload();
  2511.     }
  2512.     }
  2513. }
  2514.  
  2515. // Change indentation
  2516. void SourceView::set_indent(int source_indent, int code_indent)
  2517. {
  2518.     if (source_indent < 0 || code_indent < 0)
  2519.     return;
  2520.  
  2521.     if (source_indent == source_indent_amount &&
  2522.     code_indent == code_indent_amount)
  2523.     return;
  2524.  
  2525.     if (source_indent != source_indent_amount)
  2526.     {
  2527.     source_indent_amount = min(max(source_indent, 0), MAX_INDENT);
  2528.     if (current_file_name != "")
  2529.     {
  2530.         StatusDelay delay("Reformatting");
  2531.         reload();
  2532.     }
  2533.     }
  2534.  
  2535.     if (code_indent != code_indent_amount)
  2536.     {
  2537.     code_indent_amount = min(max(code_indent, 0), MAX_INDENT);
  2538.  
  2539.     clear_code_cache();
  2540.     show_pc(last_shown_pc);
  2541.     }
  2542. }
  2543.  
  2544. void SourceView::read_file (string file_name, 
  2545.                 int initial_line,
  2546.                 bool force_reload,
  2547.                 bool silent)
  2548. {
  2549.     if (file_name == "")
  2550.     return;
  2551.  
  2552.     /*
  2553.       Yves Arrouye <Yves.Arrouye@marin.fdn.fr> states:
  2554.  
  2555.       Paths to filename are not `normalized' before being passed to
  2556.       emacslient (or gnuclient). I mean that if one of your source
  2557.       files was compiled as
  2558.  
  2559.                 /some/path//there/file.C
  2560.  
  2561.       (which happens fairly often due to empty paths components in
  2562.       Makefiles, for example), the file will not appear in Emacs
  2563.       because the Emacs visit-file function and friends do consider
  2564.       that when there is a // or a /~ in a path then everything
  2565.       before the first / is garbage. The file that will be looked
  2566.       for in the example is
  2567.  
  2568.                 /there/file.C
  2569.  
  2570.       (the GUD Emacs mode exhibits the same bug ;-)). Thus // in
  2571.       file pathes must be changed to / (or Emacs changed so that
  2572.       only interactively-called versions of visit-file and friends
  2573.       do that, but I don't think this will be done...).
  2574.     */
  2575.     file_name.gsub("//", "/");
  2576.  
  2577.     // Read in current_source
  2578.     int error = read_current(file_name, force_reload, silent);
  2579.     if (error)
  2580.     return;
  2581.  
  2582.     add_position_to_history(file_name, initial_line, false);
  2583.  
  2584.     // The remainder may take some time...
  2585.     Delay delay;
  2586.  
  2587.     // Set source and initial line
  2588.     XmTextSetString(source_text_w, (String)current_source);
  2589.  
  2590.     XmTextPosition initial_pos = 0;
  2591.     if (initial_line > 0 && initial_line <= line_count)
  2592.     initial_pos = pos_of_line(initial_line) + indent_amount(source_text_w);
  2593.  
  2594.     SetInsertionPosition(source_text_w, initial_pos, true);
  2595.  
  2596.     // Set current file name
  2597.     current_file_name = file_name;
  2598.  
  2599.     // Refresh title
  2600.     update_title();
  2601.  
  2602.     // Refresh breakpoints
  2603.     static IntIntArrayAssoc empty_bps;
  2604.     bps_in_line = empty_bps;
  2605.     static StringArray empty_addresses;
  2606.     bp_addresses = empty_addresses;
  2607.     refresh_bp_disp();
  2608.  
  2609.     XtManageChild(source_text_w);
  2610.  
  2611.     MString msg;
  2612.     switch (current_origin)
  2613.     {
  2614.     case ORIGIN_LOCAL:
  2615.     msg += rm("File " + quote(file_name));
  2616.     if (remote_gdb())
  2617.         msg += rm(" (from local host)");
  2618.     break;
  2619.  
  2620.     case ORIGIN_REMOTE:
  2621.     msg += rm("File " + quote(file_name));
  2622.     msg += rm(" (from " + gdb_host + ")");
  2623.     break;
  2624.  
  2625.     case ORIGIN_GDB:
  2626.     msg += rm("Source " + quote(file_name));
  2627.     msg += rm(" (from " + gdb->title() + ")");
  2628.     break;
  2629.  
  2630.     case ORIGIN_NONE:
  2631.     msg += tt(file_name);
  2632.     break;
  2633.     }
  2634.     msg += rm(" ");
  2635.  
  2636.     if (line_count == 1)
  2637.     msg += rm("1 line, ");
  2638.     else
  2639.     msg += rm(itostring(line_count) + " lines, ");
  2640.     if (current_source.length() == 1)
  2641.     msg += rm("1 character");
  2642.     else
  2643.     msg += rm(itostring(current_source.length()) + " characters");
  2644.  
  2645.     set_status_mstring(msg);
  2646.  
  2647.     XmTextClearSelection(source_text_w, 
  2648.              XtLastTimestampProcessed(XtDisplay(source_text_w)));
  2649.     XmTextSetHighlight(source_text_w,
  2650.                0, XmTextGetLastPosition(source_text_w),
  2651.                XmHIGHLIGHT_NORMAL);
  2652.     last_top = last_pos = last_start_highlight = last_end_highlight = 0;
  2653.     update_glyphs(source_text_w);
  2654.  
  2655.     if (app_data.source_window)
  2656.     {
  2657.     static bool popped_up = false;
  2658.  
  2659.     if (!popped_up)
  2660.     {
  2661.         // Make sure source is visible
  2662.         Widget shell = (source_view_shell != 0) ? 
  2663.         source_view_shell : command_shell;
  2664.  
  2665.         if (source_view_shell != 0 || app_data.tty_mode)
  2666.         {
  2667.         initial_popup_shell(shell);
  2668.         }
  2669.  
  2670.         if (!app_data.command_toolbar)
  2671.         initial_popup_shell(tool_shell);
  2672.  
  2673.         if (!started_iconified(shell))
  2674.         gdbOpenSourceWindowCB(source_text_w, 0, 0);
  2675.     }
  2676.  
  2677.     popped_up = true;
  2678.     }
  2679. }
  2680.  
  2681. void SourceView::update_title()
  2682. {
  2683.     if (toplevel_w == 0)
  2684.     return;
  2685.  
  2686.     string title   = DDD_NAME ": " + current_file_name;
  2687.     String title_s = title;
  2688.  
  2689.     string icon   = 
  2690.     DDD_NAME ": " + string(basename(current_file_name.chars()));
  2691.     String icon_s = icon;
  2692.  
  2693.     XtVaSetValues(toplevel_w,
  2694.           XmNtitle, title_s,
  2695.           XmNiconName, icon_s,
  2696.           NULL);
  2697. }
  2698.  
  2699.  
  2700.  
  2701. //-----------------------------------------------------------------------
  2702. // Breakpoint handling
  2703. //-----------------------------------------------------------------------
  2704.  
  2705. // Update breakpoint locations
  2706. void SourceView::refresh_bp_disp()
  2707. {
  2708.     refresh_source_bp_disp();
  2709.     refresh_code_bp_disp();
  2710.     update_glyphs();
  2711. }
  2712.  
  2713. void SourceView::refresh_source_bp_disp()
  2714. {
  2715.     // Overwrite old breakpoint displays - - - - - - - - - - - - -
  2716.     for (IntIntArrayAssocIter b_i_l_iter(bps_in_line);
  2717.      b_i_l_iter.ok(); 
  2718.      b_i_l_iter++)
  2719.     {
  2720.     int line_nr = b_i_l_iter.key();
  2721.     if (line_nr < 0 || line_nr > line_count)
  2722.         continue;
  2723.  
  2724.     int pos = pos_of_line(line_nr);
  2725.     int indent = indent_amount(source_text_w, pos);
  2726.  
  2727.     if (indent > 0)
  2728.     {
  2729.         string s(current_source.at(pos, indent - 1));
  2730.  
  2731.         if (s.length() > 0)
  2732.         XmTextReplace(source_text_w,
  2733.                   pos_of_line(line_nr),
  2734.                   pos_of_line(line_nr) + s.length(),
  2735.                   (String)s);
  2736.     }
  2737.     }
  2738.  
  2739.     static IntIntArrayAssoc empty_bps;
  2740.     bps_in_line = empty_bps;
  2741.  
  2742.     if (display_glyphs)
  2743.     return;
  2744.  
  2745.     // Find all breakpoints referring to this file
  2746.     MapRef ref;
  2747.     for (BreakPoint* bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  2748.     {
  2749.     if (bp_matches(bp))
  2750.         bps_in_line[bp->line_nr()] += bp->number();
  2751.     }
  2752.  
  2753.     // Show breakpoints in text
  2754.     for (IntIntArrayAssocIter b_i_l_iter2(bps_in_line);
  2755.      b_i_l_iter2.ok();
  2756.      b_i_l_iter2++)
  2757.     {
  2758.     int line_nr = b_i_l_iter2.key();
  2759.     if (line_nr < 0 || line_nr > line_count)
  2760.         continue;
  2761.  
  2762.     XmTextPosition pos = pos_of_line(line_nr);
  2763.     int indent = indent_amount(source_text_w, pos);
  2764.  
  2765.     if (indent > 0)
  2766.     {
  2767.         // Display all breakpoints in a line
  2768.         VarIntArray& bps = bps_in_line[line_nr];
  2769.  
  2770.         string insert_string = "";
  2771.         for (int i = 0; i < bps.size(); i++)
  2772.         {
  2773.         BreakPoint *bp = bp_map.get(bps[i]);
  2774.         insert_string += bp->symbol();
  2775.         }
  2776.  
  2777.         if (int(insert_string.length()) >= indent - 1)
  2778.         {
  2779.         insert_string = insert_string.before(indent - 1);
  2780.         }
  2781.         else
  2782.         {
  2783.         for (int i = insert_string.length(); i < indent - 1; i++)
  2784.         {
  2785.             insert_string += current_source[pos + i];
  2786.         }
  2787.         }
  2788.  
  2789.         assert(int(insert_string.length()) == indent - 1);
  2790.  
  2791.         if (insert_string.length() > 0)
  2792.         XmTextReplace(source_text_w, pos, 
  2793.                   pos + indent - 1,
  2794.                   (String)insert_string);
  2795.     }
  2796.     }
  2797. }
  2798.  
  2799. void SourceView::refresh_code_bp_disp()
  2800. {
  2801.     // Clear all addresses
  2802.     int i;
  2803.     for (i = 0; i < bp_addresses.size(); i++)
  2804.     {
  2805.     const string& address = bp_addresses[i];
  2806.     XmTextPosition pos = find_pc(address);
  2807.     if (pos == XmTextPosition(-1))
  2808.         continue;
  2809.  
  2810.     // Process all breakpoints at ADDRESS
  2811.     int indent = indent_amount(code_text_w, pos);
  2812.     if (indent > 0)
  2813.     {
  2814.         string spaces = replicate(' ', indent);
  2815.         XmTextReplace(code_text_w, pos, pos + indent, (String)spaces);
  2816.     }
  2817.     }
  2818.  
  2819.     static StringArray empty;
  2820.     bp_addresses = empty;
  2821.  
  2822.     if (display_glyphs)
  2823.     return;
  2824.  
  2825.     // Collect all addresses
  2826.     MapRef ref;
  2827.     for (BreakPoint *bp = bp_map.first(ref); bp != 0;
  2828.      bp = bp_map.next(ref))
  2829.     {
  2830.     if (bp->type() != BREAKPOINT)
  2831.         continue;
  2832.  
  2833.     bp_addresses += bp->address();
  2834.     }
  2835.  
  2836.     // Process all bp_addresses
  2837.     for (i = 0; i < bp_addresses.size(); i++)
  2838.     {
  2839.     const string& address = bp_addresses[i];
  2840.     XmTextPosition pos = find_pc(address);
  2841.     if (pos == XmTextPosition(-1))
  2842.         continue;
  2843.  
  2844.     // Process all breakpoints at ADDRESS
  2845.     string insert_string = "";
  2846.     for (BreakPoint *bp = bp_map.first(ref);
  2847.          bp != 0;
  2848.          bp = bp_map.next(ref))
  2849.     {
  2850.         if (bp->address() == address)
  2851.         insert_string += bp->symbol();
  2852.     }
  2853.  
  2854.     int indent = indent_amount(code_text_w, pos);
  2855.     if (indent > 0)
  2856.     {
  2857.         insert_string += replicate(' ', indent);
  2858.         insert_string = insert_string.before(indent);
  2859.  
  2860.         XmTextReplace(code_text_w, pos, pos + indent, 
  2861.               (String)insert_string);
  2862.     }
  2863.     }
  2864. }
  2865.  
  2866.  
  2867. //-----------------------------------------------------------------------
  2868. // Position management
  2869. //-----------------------------------------------------------------------
  2870.  
  2871. // Find the line number at POS
  2872. // LINE_NR becomes the line number at POS
  2873. // IN_TEXT becomes true iff POS is in the source area
  2874. // BP_NR is the number of the breakpoint at POS (none: 0)
  2875. // Return false iff failure
  2876. bool SourceView::get_line_of_pos (Widget   w,
  2877.                   XmTextPosition pos,
  2878.                   int&     line_nr,
  2879.                   string&  address,
  2880.                   bool&    in_text,
  2881.                   int&     bp_nr)
  2882. {
  2883.     bool found = false;
  2884.  
  2885.     line_nr = 0;
  2886.     address = "";
  2887.     in_text = true;
  2888.     bp_nr   = 0;
  2889.  
  2890.     Widget text_w;
  2891.     if (is_source_widget(w))
  2892.     text_w = source_text_w;
  2893.     else if (is_code_widget(w))
  2894.     text_w = code_text_w;
  2895.     else
  2896.     return false;
  2897.  
  2898.     if (w != text_w)
  2899.     {
  2900.     // Glyph selected
  2901.  
  2902.     MapRef ref;
  2903.     for (BreakPoint *bp = bp_map.first(ref);
  2904.          bp != 0;
  2905.          bp = bp_map.next(ref))
  2906.     {
  2907.         if (w == bp->source_glyph() || w == bp->code_glyph())
  2908.         {
  2909.         // Breakpoint glyph found
  2910.         line_nr = bp->line_nr();
  2911.         address = bp->address();
  2912.         in_text = false;
  2913.         bp_nr   = bp->number();
  2914.         return true;
  2915.         }
  2916.     }
  2917.     }
  2918.  
  2919.     if (pos >= int(current_text(text_w).length()))
  2920.     {
  2921.     // Position is on the right of text
  2922.     in_text = false;
  2923.     line_nr = line_count;
  2924.     return true;
  2925.     }
  2926.  
  2927.     if (text_w == source_text_w)
  2928.     {
  2929.     // Search in source code
  2930.     XmTextPosition line_pos = 0;
  2931.     XmTextPosition next_line_pos = 0;
  2932.  
  2933.     while (!found && line_count >= line_nr)
  2934.     {
  2935.         next_line_pos = (line_count >= line_nr + 1) ?
  2936.         pos_of_line(line_nr + 1) :
  2937.         XmTextGetLastPosition (text_w) + 1;
  2938.  
  2939.         bool left_of_first_nonblank = false;
  2940.         if (pos < next_line_pos)
  2941.         {
  2942.         // Check if we're left of first non-blank source character
  2943.         int first_nonblank = line_pos + indent_amount(text_w);
  2944.         const string& text = current_text(text_w);
  2945.         while (first_nonblank < next_line_pos
  2946.                && first_nonblank < int(text.length())
  2947.                && isspace(text[first_nonblank]))
  2948.             first_nonblank++;
  2949.         left_of_first_nonblank = (pos < first_nonblank);
  2950.         }
  2951.  
  2952.         if (pos == line_pos
  2953.         || left_of_first_nonblank
  2954.         || pos < (line_pos + indent_amount(text_w) - 1))
  2955.         {
  2956.         // Position in breakpoint area
  2957.         found = true;
  2958.         in_text = false;
  2959.         line_nr = max(line_nr, 1);
  2960.  
  2961.         // Check for breakpoints...
  2962.         VarIntArray& bps = bps_in_line[line_nr];
  2963.         if (bps.size() == 1)
  2964.         {
  2965.             // Return single breakpoint in this line
  2966.             bp_nr = bps[0];
  2967.         }
  2968.         else if (bps.size() > 1)
  2969.         {
  2970.             // Find which breakpoint was selected
  2971.             XmTextPosition bp_disp_pos = line_pos;
  2972.             int i;
  2973.             for (i = 0; i < bps.size(); i++)
  2974.             {
  2975.             BreakPoint* bp = bp_map.get(bps[i]);
  2976.             assert (bp);
  2977.  
  2978.             bp_disp_pos += 2; // respect '#' and '_';
  2979.             bp_disp_pos += itostring(bp->number()).length();
  2980.             if (pos < bp_disp_pos)
  2981.             {
  2982.                 bp_nr = bps[i];
  2983.                 break; // exit for loop
  2984.             }
  2985.             }
  2986.         }
  2987.         }
  2988.         else if (pos < next_line_pos)
  2989.         {
  2990.         // Position is in text
  2991.         found   = true;
  2992.         in_text = true;
  2993.         }
  2994.         else
  2995.         {
  2996.         // Position is in one of the following lines
  2997.         line_pos = next_line_pos;
  2998.         line_nr++;
  2999.         }
  3000.     }
  3001.     }
  3002.     else if (text_w == code_text_w)
  3003.     {
  3004.     // Search in machine code
  3005.     XmTextPosition line_pos = pos;
  3006.     while (line_pos >= 0 && current_code[line_pos] != '\n')
  3007.         line_pos--;
  3008.     line_pos++;
  3009.  
  3010.     if (pos == line_pos || pos - line_pos < indent_amount(text_w))
  3011.     {
  3012.         // Breakpoint area
  3013.         in_text = false;
  3014.  
  3015.         // Check if we have a breakpoint around here
  3016.         int index = address_index(current_code, pos);
  3017.         if (index >= 0)
  3018.         {
  3019.         address = current_code.from(index);
  3020.         address = address.through(rxaddress);
  3021.  
  3022.         VarIntArray bps;
  3023.  
  3024.         MapRef ref;
  3025.         for (BreakPoint *bp = bp_map.first(ref);
  3026.              bp != 0;
  3027.              bp = bp_map.next(ref))
  3028.         {
  3029.             if (compare_address(address, bp->address()) == 0)
  3030.             bps += bp->number();
  3031.         }
  3032.         if (bps.size() == 1)
  3033.         {
  3034.             // Return single breakpoint in this line
  3035.             bp_nr = bps[0];
  3036.         }
  3037.         else if (bps.size() > 1)
  3038.         {
  3039.             // Find which breakpoint was selected
  3040.             int i;
  3041.             XmTextPosition bp_disp_pos = line_pos;
  3042.             for (i = 0; i < bps.size(); i++)
  3043.             {
  3044.             BreakPoint* bp = bp_map.get(bps[i]);
  3045.             assert (bp);
  3046.             bp_disp_pos += 2; // respect '#' and '_';
  3047.             bp_disp_pos += itostring(bp->number()).length();
  3048.             if (pos < bp_disp_pos)
  3049.             {
  3050.                 bp_nr = bps[i];
  3051.                 break; // exit for loop
  3052.             }
  3053.             }
  3054.         }
  3055.         }
  3056.     }
  3057.  
  3058.     found = true;
  3059.     }
  3060.  
  3061.     return found;
  3062. }
  3063.  
  3064. // ***************************************************************************
  3065. // Find word around POS.  STARTPOS is the first character, ENDPOS + 1
  3066. // is the last character in the word.
  3067. void SourceView::find_word_bounds (Widget text_w,
  3068.                    const XmTextPosition pos,
  3069.                    XmTextPosition& startpos,
  3070.                    XmTextPosition& endpos)
  3071. {
  3072.     startpos = endpos = pos;
  3073.  
  3074.     string& text = current_text(text_w);
  3075.  
  3076.     XmTextPosition line_pos = pos;
  3077.     if (line_pos < XmTextPosition(text.length()))
  3078.     while (line_pos > 0 && text[line_pos - 1] != '\n')
  3079.         line_pos--;
  3080.  
  3081.     int offset = pos - line_pos;
  3082.     if (offset == 0 || offset < indent_amount(text_w))
  3083.     {
  3084.     // Do not select words in breakpoint area
  3085.     return;
  3086.     }
  3087.  
  3088.     // Find end of word
  3089.     while (endpos < XmTextPosition(text.length()) && isid(text[endpos]))
  3090.     endpos++;
  3091.  
  3092.     // Find start of word
  3093.     if (startpos >= XmTextPosition(text.length()))
  3094.     startpos = XmTextPosition(text.length() - 1);
  3095.  
  3096.     while (startpos > 0)
  3097.     {
  3098.     while (startpos > 0 && isid(text[startpos - 1]))
  3099.         startpos--;
  3100.  
  3101.     if (gdb->program_language() == LANGUAGE_PERL &&
  3102.         startpos > 1 &&
  3103.         is_perl_prefix(text[startpos - 1]))
  3104.     {
  3105.         // Include Perl prefix character
  3106.         startpos -= 1;
  3107.         break;
  3108.     }
  3109.     else if (startpos > 2 && 
  3110.         isid(text[startpos - 2]) &&
  3111.         text[startpos - 1] == '.')
  3112.     {
  3113.         // Select A.B as a whole
  3114.         startpos -= 1;
  3115.     }
  3116.     else if (startpos > 3 && 
  3117.          isid(text[startpos - 3]) &&
  3118.          text[startpos - 2] == '-' &&
  3119.          text[startpos - 1] == '>')
  3120.     {
  3121.         // Select A->B as a whole
  3122.         startpos -= 2;
  3123.     }
  3124.     else if (startpos > 3 && 
  3125.          isid(text[startpos - 3]) &&
  3126.          text[startpos - 2] == ':' &&
  3127.          text[startpos - 1] == ':')
  3128.     {
  3129.         // Select A::B as a whole
  3130.         startpos -= 2;
  3131.     }
  3132.     else
  3133.         break;
  3134.     }
  3135. }
  3136.  
  3137. // Get the word at event position
  3138. string SourceView::get_word_at_event(Widget text_w,
  3139.                      XEvent *event,
  3140.                      XmTextPosition& startpos,
  3141.                      XmTextPosition& endpos)
  3142. {
  3143.     BoxPoint event_pos = point(event);
  3144.     XmTextPosition pos = XmTextXYToPos(text_w, event_pos[X], event_pos[Y]);
  3145.  
  3146.     return get_word_at_pos(text_w, pos, startpos, endpos);
  3147. }
  3148.  
  3149.  
  3150. // Get the word at POS
  3151. string SourceView::get_word_at_pos(Widget text_w,
  3152.                    XmTextPosition pos,
  3153.                    XmTextPosition& startpos,
  3154.                    XmTextPosition& endpos)
  3155. {
  3156.     string& text = current_text(text_w);
  3157.     if (text == "")
  3158.     return "";
  3159.  
  3160.     if (!XmTextGetSelectionPosition(text_w, &startpos, &endpos)
  3161.     || pos < startpos
  3162.     || pos > endpos)
  3163.     {
  3164.     find_word_bounds(text_w, pos, startpos, endpos);
  3165.     }
  3166.  
  3167.     string word = "";
  3168.     if (startpos < XmTextPosition(text.length())
  3169.     && startpos < endpos)
  3170.     word = text.at(int(startpos), int(endpos - startpos));
  3171.  
  3172.     strip_space(word);
  3173.  
  3174.     return word;
  3175. }
  3176.  
  3177.  
  3178.  
  3179. //----------------------------------------------------------------------------
  3180. // Create GDB requests and evaluate replies
  3181. //----------------------------------------------------------------------------
  3182.  
  3183. //----------------------------------------------------------------------------
  3184. // Constructor
  3185. //----------------------------------------------------------------------------
  3186.  
  3187. // Install the given X bitmap as NAME
  3188. static void InstallImage(unsigned char *bits, int width, int height, 
  3189.              const string& name)
  3190. {
  3191.     XImage *image = CreateImageFromBitmapData(bits, width, height);
  3192.     Boolean ok = XmInstallImage(image, name);
  3193.     if (!ok)
  3194.     cerr << "Could not install " << quote(name) << " bitmap\n";
  3195. }
  3196.  
  3197.  
  3198. SourceView::SourceView(Widget parent)
  3199. {
  3200.     XtAppContext app_context = XtWidgetToApplicationContext(parent);
  3201.  
  3202.     // Find application shell
  3203.     toplevel_w = parent;
  3204.     while (toplevel_w != 0 && !XtIsWMShell(toplevel_w))
  3205.     toplevel_w = XtParent(toplevel_w);
  3206.  
  3207.     // Install glyph images
  3208.     InstallImage(arrow_bits, arrow_width, arrow_height, 
  3209.          "plain_arrow");
  3210.     InstallImage(grey_arrow_bits, grey_arrow_width, grey_arrow_height, 
  3211.          "grey_arrow");
  3212.     InstallImage(past_arrow_bits, past_arrow_width, past_arrow_height, 
  3213.          "past_arrow");
  3214.     InstallImage(signal_arrow_bits, signal_arrow_width, signal_arrow_height, 
  3215.          "signal_arrow");
  3216.     InstallImage(drag_arrow_bits, drag_arrow_width, drag_arrow_height, 
  3217.          "drag_arrow");
  3218.  
  3219.     InstallImage(stop_bits, stop_width, stop_height, 
  3220.          "plain_stop");
  3221.     InstallImage(cond_bits, cond_width, cond_height, 
  3222.          "plain_cond");
  3223.     InstallImage(temp_bits, temp_width, temp_height, 
  3224.          "plain_temp");
  3225.  
  3226.     InstallImage(grey_stop_bits, grey_stop_width, grey_stop_height, 
  3227.          "grey_stop");
  3228.     InstallImage(grey_cond_bits, grey_cond_width, grey_cond_height, 
  3229.          "grey_cond");
  3230.     InstallImage(grey_temp_bits, grey_temp_width, grey_temp_height, 
  3231.          "grey_temp");
  3232.  
  3233.     InstallImage(drag_stop_bits, drag_stop_width, drag_stop_height, 
  3234.          "drag_stop");
  3235.     InstallImage(drag_cond_bits, drag_cond_width, drag_cond_height, 
  3236.          "drag_cond");
  3237.     InstallImage(drag_temp_bits, drag_temp_width, drag_temp_height, 
  3238.          "drag_temp");
  3239.  
  3240.     // Setup actions
  3241.     XtAppAddActions (app_context, actions, XtNumber (actions));
  3242.  
  3243.     // Create source code window
  3244.     create_text(parent, "source", app_data.source_editing,
  3245.         source_form_w, source_text_w);
  3246.     XtManageChild(source_form_w);
  3247.  
  3248.     // Create machine code window
  3249.     create_text(parent, "code", false, code_form_w, code_text_w);
  3250.     if (disassemble)
  3251.     XtManageChild(code_form_w);
  3252. }
  3253.  
  3254. void SourceView::create_shells()
  3255. {
  3256.     Widget parent = XtParent(source_form_w);
  3257.     XtAppContext app_context = XtWidgetToApplicationContext(parent);
  3258.  
  3259.     // Create breakpoint editor
  3260.     Arg args[10];
  3261.     Cardinal arg = 0;
  3262.  
  3263.     arg = 0;
  3264.     XtSetArg(args[arg], XmNvisibleItemCount, 0); arg++;
  3265.     edit_breakpoints_dialog_w =
  3266.     verify(createTopLevelSelectionDialog(parent, "edit_breakpoints_dialog",
  3267.                          args, arg));
  3268.     Delay::register_shell(edit_breakpoints_dialog_w);
  3269.  
  3270.     XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
  3271.                        XmDIALOG_TEXT));
  3272.     XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
  3273.                        XmDIALOG_CANCEL_BUTTON));
  3274.     XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
  3275.                        XmDIALOG_APPLY_BUTTON));
  3276.     XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
  3277.                        XmDIALOG_SELECTION_LABEL));
  3278.     XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
  3279.                        XmDIALOG_LIST_LABEL));
  3280.  
  3281.     breakpoint_list_w = 
  3282.     XmSelectionBoxGetChild(edit_breakpoints_dialog_w, XmDIALOG_LIST);
  3283.  
  3284.     if (app_data.flat_dialog_buttons)
  3285.     {
  3286.     for (MMDesc *item = bp_area; item != 0 && item->name != 0; item++)
  3287.     {
  3288.         if ((item->type & MMTypeMask) == MMPush)
  3289.         item->type = (MMFlatPush | (item->type & ~MMTypeMask));
  3290.     }
  3291.     }
  3292.  
  3293.     Widget buttons = verify(MMcreateWorkArea(edit_breakpoints_dialog_w, 
  3294.                          "buttons", bp_area));
  3295.     XtVaSetValues(buttons,
  3296.           XmNmarginWidth,     0, 
  3297.           XmNmarginHeight,    0, 
  3298.           XmNborderWidth,     0,
  3299.           XmNshadowThickness, 0, 
  3300.           XmNspacing,         0,
  3301.           NULL);
  3302.  
  3303.     MMaddCallbacks(bp_area);
  3304.     MMaddHelpCallback(bp_area, ImmediateHelpCB);
  3305.  
  3306.     if (breakpoint_list_w != 0)
  3307.     {
  3308.     XtAddCallback(breakpoint_list_w,
  3309.               XmNsingleSelectionCallback,
  3310.               UpdateBreakpointButtonsCB,
  3311.               0);
  3312.     XtAddCallback(breakpoint_list_w,
  3313.               XmNmultipleSelectionCallback,
  3314.               UpdateBreakpointButtonsCB,
  3315.               0);
  3316. #if 0
  3317.     XtAddCallback(breakpoint_list_w,
  3318.               XmNmultipleSelectionCallback,
  3319.               LookupBreakpointCB,
  3320.               0);
  3321. #endif
  3322.     XtAddCallback(breakpoint_list_w,
  3323.               XmNextendedSelectionCallback,
  3324.               UpdateBreakpointButtonsCB,
  3325.               0);
  3326.     XtAddCallback(breakpoint_list_w,
  3327.               XmNbrowseSelectionCallback,
  3328.               UpdateBreakpointButtonsCB,
  3329.               0);
  3330.     }
  3331.  
  3332.     if (edit_breakpoints_dialog_w != 0)
  3333.     {
  3334.     XtAddCallback(edit_breakpoints_dialog_w,
  3335.               XmNokCallback,
  3336.               UnmanageThisCB,
  3337.               edit_breakpoints_dialog_w);
  3338.     XtAddCallback(edit_breakpoints_dialog_w,
  3339.               XmNhelpCallback,
  3340.               ImmediateHelpCB,
  3341.               0);
  3342.     }
  3343.  
  3344.     // Create stack view
  3345.     arg = 0;
  3346.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  3347.     stack_dialog_w =
  3348.     verify(createTopLevelSelectionDialog(parent, 
  3349.                          "stack_dialog", args, arg));
  3350.     Delay::register_shell(stack_dialog_w);
  3351.  
  3352.     XtUnmanageChild(XmSelectionBoxGetChild(stack_dialog_w, 
  3353.                        XmDIALOG_TEXT));
  3354.     XtUnmanageChild(XmSelectionBoxGetChild(stack_dialog_w, 
  3355.                        XmDIALOG_SELECTION_LABEL));
  3356.  
  3357.     up_w   = XmSelectionBoxGetChild(stack_dialog_w, XmDIALOG_OK_BUTTON);
  3358.     down_w = XmSelectionBoxGetChild(stack_dialog_w, XmDIALOG_APPLY_BUTTON);
  3359.  
  3360.     set_sensitive(up_w,   False);
  3361.     set_sensitive(down_w, False);
  3362.     refresh_buttons();
  3363.  
  3364.     arg = 0;
  3365.     frame_list_w = XmSelectionBoxGetChild(stack_dialog_w, XmDIALOG_LIST);
  3366.     XtVaSetValues(frame_list_w,
  3367.           XmNselectionPolicy, XmSINGLE_SELECT,
  3368.           NULL);
  3369.  
  3370.     XtAddCallback(frame_list_w,
  3371.           XmNsingleSelectionCallback, SelectFrameCB, 0);
  3372.     XtAddCallback(frame_list_w,
  3373.           XmNmultipleSelectionCallback, SelectFrameCB, 0);
  3374.     XtAddCallback(frame_list_w,
  3375.           XmNextendedSelectionCallback, SelectFrameCB, 0);
  3376.     XtAddCallback(frame_list_w,
  3377.           XmNbrowseSelectionCallback, SelectFrameCB, 0);
  3378.  
  3379.     XtAddCallback(stack_dialog_w,
  3380.           XmNokCallback, gdbCommandCB, "up");
  3381.     XtAddCallback(stack_dialog_w,
  3382.           XmNapplyCallback, gdbCommandCB, "down");
  3383.     XtAddCallback(stack_dialog_w,
  3384.           XmNcancelCallback, UnmanageThisCB, stack_dialog_w);
  3385.     XtAddCallback(stack_dialog_w,
  3386.           XmNcancelCallback, StackDialogPoppedDownCB, 0);
  3387.     XtAddCallback(stack_dialog_w,
  3388.           XmNhelpCallback, ImmediateHelpCB, 0);
  3389.  
  3390.     Widget cancel_w = XmSelectionBoxGetChild(stack_dialog_w, 
  3391.                          XmDIALOG_CANCEL_BUTTON);
  3392.  
  3393.     XtVaSetValues(stack_dialog_w, XmNdefaultButton, cancel_w, 0);
  3394.  
  3395.     // Create register view
  3396.     arg = 0;
  3397.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  3398.     register_dialog_w = 
  3399.     verify(createTopLevelSelectionDialog(parent, 
  3400.                          "register_dialog", args, arg));
  3401.     Delay::register_shell(register_dialog_w);
  3402.  
  3403.     XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w, 
  3404.                        XmDIALOG_TEXT));
  3405.     XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w, 
  3406.                        XmDIALOG_SELECTION_LABEL));
  3407.     XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w, 
  3408.                        XmDIALOG_APPLY_BUTTON));
  3409.     XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w, 
  3410.                        XmDIALOG_CANCEL_BUTTON));
  3411.  
  3412.     arg = 0;
  3413.     Widget box = XmCreateRadioBox(register_dialog_w, "box", args, arg);
  3414.     XtManageChild(box);
  3415.  
  3416.     arg = 0;
  3417.     XtSetArg(args[arg], XmNset, !all_registers); arg++;
  3418.     int_registers_w = 
  3419.     XmCreateToggleButton(box, "int_registers", args, arg);
  3420.     XtManageChild(int_registers_w);
  3421.  
  3422.     arg = 0;
  3423.     XtSetArg(args[arg], XmNset, all_registers); arg++;
  3424.     all_registers_w = 
  3425.     XmCreateToggleButton(box, "all_registers", args, arg);
  3426.     XtManageChild(all_registers_w);
  3427.  
  3428.     XtAddCallback(all_registers_w, XmNvalueChangedCallback, 
  3429.           sourceToggleAllRegistersCB, XtPointer(0));
  3430.  
  3431.     arg = 0;
  3432.     register_list_w = XmSelectionBoxGetChild(register_dialog_w, XmDIALOG_LIST);
  3433.     XtVaSetValues(register_list_w,
  3434.           XmNselectionPolicy, XmSINGLE_SELECT,
  3435.           NULL);
  3436.  
  3437.     XtAddCallback(register_list_w,
  3438.           XmNsingleSelectionCallback, SelectRegisterCB, 0);
  3439.     XtAddCallback(register_list_w,
  3440.           XmNmultipleSelectionCallback, SelectRegisterCB, 0);
  3441.     XtAddCallback(register_list_w,
  3442.           XmNextendedSelectionCallback, SelectRegisterCB, 0);
  3443.     XtAddCallback(register_list_w,
  3444.           XmNbrowseSelectionCallback, SelectRegisterCB, 0);
  3445.  
  3446.     XtAddCallback(register_dialog_w,
  3447.           XmNokCallback, UnmanageThisCB, register_dialog_w);
  3448.     XtAddCallback(register_dialog_w,
  3449.           XmNokCallback, RegisterDialogPoppedDownCB, 0);
  3450.     XtAddCallback(register_dialog_w,
  3451.           XmNhelpCallback, ImmediateHelpCB, 0);
  3452.  
  3453.  
  3454.     // Create thread view
  3455.     arg = 0;
  3456.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  3457.     thread_dialog_w = 
  3458.     verify(createTopLevelSelectionDialog(parent, 
  3459.                          "thread_dialog", args, arg));
  3460.     Delay::register_shell(thread_dialog_w);
  3461.  
  3462.     XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w, 
  3463.                        XmDIALOG_TEXT));
  3464.     XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w, 
  3465.                        XmDIALOG_SELECTION_LABEL));
  3466.  
  3467.     if (gdb->type() != JDB)
  3468.     {
  3469.     XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w, 
  3470.                            XmDIALOG_OK_BUTTON));
  3471.     XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w, 
  3472.                            XmDIALOG_APPLY_BUTTON));
  3473.     }
  3474.  
  3475.     arg = 0;
  3476.     thread_list_w = XmSelectionBoxGetChild(thread_dialog_w, XmDIALOG_LIST);
  3477.     XtVaSetValues(thread_list_w,
  3478.           XmNselectionPolicy, XmSINGLE_SELECT,
  3479.           NULL);
  3480.  
  3481.     XtAddCallback(thread_list_w,
  3482.           XmNsingleSelectionCallback, SelectThreadCB, 0);
  3483.     XtAddCallback(thread_list_w,
  3484.           XmNmultipleSelectionCallback, SelectThreadCB, 0);
  3485.     XtAddCallback(thread_list_w,
  3486.           XmNextendedSelectionCallback, SelectThreadCB, 0);
  3487.     XtAddCallback(thread_list_w,
  3488.           XmNbrowseSelectionCallback, SelectThreadCB, 0);
  3489.  
  3490.     XtAddCallback(thread_dialog_w,
  3491.           XmNcancelCallback, UnmanageThisCB, thread_dialog_w);
  3492.     XtAddCallback(thread_dialog_w,
  3493.           XmNcancelCallback, ThreadDialogPoppedDownCB, 0);
  3494.     XtAddCallback(thread_dialog_w,
  3495.           XmNokCallback,     ThreadCommandCB, "suspend");
  3496.     XtAddCallback(thread_dialog_w,
  3497.           XmNapplyCallback,  ThreadCommandCB, "resume");
  3498.     XtAddCallback(thread_dialog_w,
  3499.           XmNhelpCallback, ImmediateHelpCB, 0);
  3500.  
  3501.     // Create remaining glyphs in the background
  3502.     XtAppAddWorkProc (app_context, CreateGlyphsWorkProc, XtPointer(0));
  3503. }
  3504.  
  3505. // Check for modifications
  3506. void SourceView::CheckModificationCB(Widget, XtPointer client_data, 
  3507.                      XtPointer call_data)
  3508. {
  3509.     bool editable = bool((int)(long)client_data);
  3510.     XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)call_data;
  3511.     if (!editable && cbs != 0 && cbs->event != 0)
  3512.     {
  3513.     cbs->doit = False;
  3514.     return;
  3515.     }
  3516.  
  3517.     // Follow text modifications here... (FIXME)
  3518. }
  3519.  
  3520. // Create source or code window
  3521. void SourceView::create_text(Widget parent, const string& base, bool editable,
  3522.                  Widget& form, Widget& text)
  3523. {
  3524.     Arg args[15];
  3525.     int arg = 0;
  3526.  
  3527.     // Create text window
  3528.     arg = 0;
  3529.     XtSetArg(args[arg], XmNmarginHeight, 0);    arg++;
  3530.     XtSetArg(args[arg], XmNmarginWidth,  0);    arg++;
  3531.     string form_name = base + "_form_w";
  3532.     form = verify(XmCreateForm(parent, form_name, args, arg));
  3533.  
  3534.     arg = 0;
  3535.     XtSetArg(args[arg], XmNselectionArrayCount, 1);               arg++;
  3536.     XtSetArg(args[arg], XmNtopAttachment,     XmATTACH_FORM);     arg++;
  3537.     XtSetArg(args[arg], XmNbottomAttachment,  XmATTACH_FORM);     arg++;
  3538.     XtSetArg(args[arg], XmNleftAttachment,    XmATTACH_FORM);     arg++;
  3539.     XtSetArg(args[arg], XmNrightAttachment,   XmATTACH_FORM);     arg++;
  3540.     XtSetArg(args[arg], XmNallowResize,       True);              arg++;
  3541.     XtSetArg(args[arg], XmNeditMode,          XmMULTI_LINE_EDIT); arg++;
  3542.     XtSetArg(args[arg], XmNcursorPositionVisible, True);          arg++;
  3543.  
  3544.     if (lesstif_version <= 82)
  3545.     {
  3546.     // LessTif 0.81 has a bad implementation of auto-show
  3547.     // position: rather than scrolling only when needed, the line
  3548.     // containing the cursor is *always* scrolled such that it
  3549.     // becomes the first line.  Hence, disable the LessTif
  3550.     // auto-show mechanism and rely on the DDD ones.
  3551.     XtSetArg(args[arg], XmNautoShowCursorPosition, False);    arg++;
  3552.     }
  3553.     else
  3554.     {
  3555.     XtSetArg(args[arg], XmNautoShowCursorPosition, True);     arg++;
  3556.     }
  3557.  
  3558.     if (lesstif_version <= 86)
  3559.     {
  3560.     // LessTif 0.86 and earlier has trouble with non-editable text
  3561.     // windows: cursor movement is inhibited.  Rely on
  3562.     // `CheckModificationCB' instead.
  3563.     XtSetArg(args[arg], XmNeditable, True); arg++;
  3564.     }
  3565.     else
  3566.     {
  3567.     XtSetArg(args[arg], XmNeditable, editable); arg++;
  3568.     }
  3569.  
  3570.     string text_name = base + "_text_w";
  3571.     text = verify(XmCreateScrolledText(form, text_name, args, arg));
  3572.     XtManageChild(text);
  3573.  
  3574.     // Set up the scrolled window
  3575.     XtVaSetValues(XtParent(text),
  3576.           XmNspacing,         0,
  3577.           XmNborderWidth,     0,
  3578.           XmNshadowThickness, 0,
  3579.           NULL);
  3580.  
  3581.     // Give the form the size specified for the text
  3582.     set_scrolled_window_size(text, form);
  3583.  
  3584.     // Set callbacks
  3585.     XtAddCallback(text, XmNgainPrimaryCallback, 
  3586.           set_source_argCB, XtPointer(false));
  3587.     XtAddCallback(text, XmNmotionVerifyCallback,
  3588.           set_source_argCB, XtPointer(true));
  3589.     XtAddCallback(text, XmNmotionVerifyCallback, 
  3590.           CheckScrollCB, XtPointer(0));
  3591.     XtAddCallback(text, XmNmodifyVerifyCallback,
  3592.           CheckModificationCB, XtPointer(editable));
  3593.     InstallTextTips(text);
  3594.  
  3595.     // Fetch scrollbar ID and add callbacks
  3596.     Widget scrollbar = 0;
  3597.     XtVaGetValues(XtParent(text), XmNverticalScrollBar, &scrollbar, NULL);
  3598.     if (scrollbar != 0)
  3599.     {
  3600.     XtAddCallback(scrollbar, XmNincrementCallback,     CheckScrollCB, 0);
  3601.     XtAddCallback(scrollbar, XmNdecrementCallback,     CheckScrollCB, 0);
  3602.     XtAddCallback(scrollbar, XmNpageIncrementCallback, CheckScrollCB, 0);
  3603.     XtAddCallback(scrollbar, XmNpageDecrementCallback, CheckScrollCB, 0);
  3604.     XtAddCallback(scrollbar, XmNtoTopCallback,         CheckScrollCB, 0);
  3605.     XtAddCallback(scrollbar, XmNtoBottomCallback,      CheckScrollCB, 0);
  3606.     XtAddCallback(scrollbar, XmNdragCallback,          CheckScrollCB, 0);
  3607.     XtAddCallback(scrollbar, XmNvalueChangedCallback,  CheckScrollCB, 0);
  3608.     }
  3609. }
  3610.  
  3611.  
  3612.  
  3613. //-----------------------------------------------------------------------
  3614. // Position management
  3615. //-----------------------------------------------------------------------
  3616.  
  3617. // Set current execution position, based on the GDB position info
  3618. // POSITION; no arg means clear current position.
  3619. // STOPPED indicates that the program just stopped.
  3620. // SIGNALED indicates that the program received a signal.
  3621. void SourceView::show_execution_position (string position, bool stopped,
  3622.                       bool signaled, bool silent)
  3623. {
  3624.     if (stopped)
  3625.     {
  3626.     at_lowest_frame = true;
  3627.     signal_received = signaled;
  3628.     }
  3629.  
  3630.     if (position == "")
  3631.     {
  3632.     if (!display_glyphs)
  3633.     {
  3634.         // Remove old marker
  3635.         int indent = indent_amount(source_text_w);
  3636.         if (indent > 0)
  3637.         {
  3638.         static const string no_marker = " ";
  3639.         XmTextReplace (source_text_w,
  3640.                    last_pos + indent - no_marker.length(),
  3641.                    last_pos + indent,
  3642.                    (String)no_marker);
  3643.         }
  3644.  
  3645.         if (last_start_highlight)
  3646.         XmTextSetHighlight (source_text_w,
  3647.                     last_start_highlight, last_end_highlight,
  3648.                     XmHIGHLIGHT_NORMAL);
  3649.  
  3650.     }
  3651.     last_pos = last_start_highlight = last_end_highlight = 0;
  3652.     last_execution_file = "";
  3653.     last_execution_line = 0;
  3654.     update_glyphs();
  3655.  
  3656.     undo_buffer.remove_position();
  3657.     undo_buffer.add_state();
  3658.     return;
  3659.     }
  3660.  
  3661.     string file_name = current_file_name;
  3662.  
  3663.     if (position.contains(':'))
  3664.     {
  3665.     file_name = position.before(":");
  3666.     position  = position.after(":");
  3667.     }
  3668.  
  3669.     int line = get_positive_nr(position);
  3670.     if (line < 0)
  3671.     return;
  3672.  
  3673.     if (!is_current_file(file_name))
  3674.     read_file(file_name, line, silent);
  3675.  
  3676.     if (is_current_file(file_name))
  3677.     {
  3678.     int indent = indent_amount(source_text_w);
  3679.  
  3680.     if (!display_glyphs && indent > 0)
  3681.     {
  3682.         // Remove old marker
  3683.         static const string no_marker = " ";
  3684.         XmTextReplace (source_text_w,
  3685.                last_pos + indent - no_marker.length(),
  3686.                last_pos + indent,
  3687.                (String)no_marker);
  3688.     }
  3689.  
  3690.     // Show current position
  3691.     _show_execution_position(file_name, line, silent, stopped);
  3692.     }
  3693. }
  3694.  
  3695. // Unset current execution position (program terminated)
  3696. void SourceView::clear_execution_position()
  3697. {
  3698.     show_execution_position();
  3699.     last_execution_pc = "";
  3700.     last_shown_pc     = "";
  3701.     update_glyphs();
  3702.  
  3703.     undo_buffer.remove_address();
  3704.     undo_buffer.add_state();
  3705. }
  3706.  
  3707.  
  3708. void SourceView::_show_execution_position(string file, int line, 
  3709.                       bool silent, bool stopped)
  3710. {
  3711.     last_execution_file = file;
  3712.     last_execution_line = line;
  3713.  
  3714.     if (!is_current_file(file))
  3715.     read_file(file, line, silent);
  3716.  
  3717.     if (!is_current_file(file) || line < 1 || line > line_count)
  3718.     return;
  3719.  
  3720.     add_position_to_history(file, line, stopped);
  3721.  
  3722.     XmTextPosition pos = pos_of_line(line);
  3723.     int indent = indent_amount(source_text_w);
  3724.     SetInsertionPosition(source_text_w, pos + indent, false);
  3725.  
  3726.     // Mark current line
  3727.     if (!display_glyphs && indent > 0)
  3728.     {
  3729.     // Set new marker
  3730.     static const string marker = ">";
  3731.     XmTextReplace (source_text_w,
  3732.                pos + indent - marker.length(),
  3733.                pos + indent,
  3734.                (String)marker);
  3735.     }
  3736.  
  3737.     XmTextPosition pos_line_end = 0;
  3738.     if (current_source != "")
  3739.     pos_line_end = current_source.index('\n', pos) + 1;
  3740.  
  3741.     if (!display_glyphs && 
  3742.     (pos != last_start_highlight || pos_line_end != last_end_highlight))
  3743.     {
  3744.     if (last_start_highlight)
  3745.     {
  3746.         XmTextSetHighlight (source_text_w,
  3747.                 last_start_highlight, last_end_highlight,
  3748.                 XmHIGHLIGHT_NORMAL);
  3749.     }
  3750.  
  3751.     XmTextSetHighlight (source_text_w,
  3752.                 pos, pos_line_end,
  3753.                 XmHIGHLIGHT_SELECTED);
  3754.     }
  3755.  
  3756.     last_pos             = pos;
  3757.     last_start_highlight = pos;
  3758.     last_end_highlight   = pos_line_end;
  3759.  
  3760.     update_glyphs();
  3761. }
  3762.  
  3763.  
  3764. void SourceView::show_position(string position, bool silent)
  3765. {
  3766.     string file_name = current_file_name;
  3767.  
  3768.     if (position.contains(':'))
  3769.     {
  3770.     file_name = position.before(':');
  3771.     position  = position.after(':');
  3772.     }
  3773.     int line = get_positive_nr(position);
  3774.  
  3775.     // In case of `Open Source', we get FILE:1 positions.  Be sure to
  3776.     // reload the file in this case.
  3777.     bool force_reload = (line == 1);
  3778.     if (!is_current_file(file_name) || force_reload)
  3779.     read_file(file_name, line, force_reload, silent);
  3780.  
  3781.     // Have window scroll to correct position
  3782.     if (is_current_file(file_name))
  3783.     {
  3784.     if (line == 0 && gdb->type() == JDB)
  3785.     {
  3786.         // Scroll to current class
  3787.         int pos = java_class_start(current_source, current_source_name());
  3788.  
  3789.         if (pos >= 0)
  3790.         {
  3791.         int line_nr = 0;
  3792.         bool in_text;
  3793.         int bp_nr;
  3794.         string address;
  3795.  
  3796.         if (get_line_of_pos(source_text_w, pos, line_nr, address, 
  3797.                     in_text, bp_nr))
  3798.         {
  3799.             line = line_nr;
  3800.         }
  3801.         }
  3802.     }
  3803.        
  3804.     if (line > 0 && line <= line_count)
  3805.     {
  3806.          add_position_to_history(file_name, line, false);
  3807.     
  3808.         XmTextPosition pos = pos_of_line(line);
  3809.         int indent = indent_amount(source_text_w, pos);
  3810.         SetInsertionPosition(source_text_w, pos + indent, true);
  3811.         
  3812.         last_pos = pos;
  3813.     }
  3814.     }
  3815. }
  3816.  
  3817.  
  3818.  
  3819. //-----------------------------------------------------------------------
  3820. // Process GDB output
  3821. //-----------------------------------------------------------------------
  3822.  
  3823. // Process reply on 'info breakpoints'.
  3824. // Update breakpoints in BP_BAP, adding new ones or deleting existing ones.
  3825. // Update breakpoint display by calling REFRESH_BP_DISP.
  3826. void SourceView::process_info_bp (string& info_output,
  3827.                   const string& break_arg)
  3828. {
  3829.     // DEC DBX issues empty lines, which causes trouble
  3830.     info_output.gsub("\n\n", "\n");
  3831.  
  3832.     // SGI DBX issues `Process PID' before numbers
  3833. #if RUNTIME_REGEX
  3834.     static regex rxprocess1("Process[ \t]+[0-9]+:[ \t]*");
  3835. #endif
  3836.     info_output.gsub(rxprocess1, "");
  3837.  
  3838.     last_info_output = info_output;
  3839.     string keep_me = "";
  3840.  
  3841.     switch (gdb->type())
  3842.     {
  3843.     case GDB:
  3844.     // If this is no breakpoint info, process it as GDB message
  3845.     if (!info_output.contains("Num", 0) && 
  3846.         !info_output.contains("No breakpoints", 0))
  3847.         check_remainder(info_output);
  3848.     break;
  3849.  
  3850.     case DBX:
  3851.     case XDB:
  3852.     case JDB:
  3853.     case PYDB:
  3854.     case PERL:
  3855.     break;
  3856.     }
  3857.                     
  3858.     VarIntArray bps_not_read;
  3859.     MapRef ref;
  3860.     int i;
  3861.     for (i = bp_map.first_key(ref); i != 0; i = bp_map.next_key(ref))
  3862.     bps_not_read += i;
  3863.  
  3864.     bool changed = false;
  3865.     bool added   = false;
  3866.     ostrstream undo_commands;
  3867.     string file = current_file_name;
  3868.  
  3869.     while (info_output != "")
  3870.     {
  3871.     int bp_nr = -1;
  3872.     switch(gdb->type())
  3873.     {
  3874.     case GDB:
  3875.     case PYDB:
  3876.         if (!has_nr(info_output))
  3877.         {
  3878.         // Skip this line
  3879.         info_output = info_output.after('\n');
  3880.         continue;
  3881.         }
  3882.         bp_nr = get_positive_nr (info_output);
  3883.         break;
  3884.  
  3885.     case DBX:
  3886.         {
  3887.         // SGI IRIX DBX issues `Process PID: ' 
  3888.         // before status lines.
  3889. #if RUNTIME_REGEX
  3890.         static regex rxprocess2("Process[ \t]+[0-9]+:");
  3891. #endif
  3892.         if (info_output.contains(rxprocess2, 0))
  3893.             info_output = info_output.after(':');
  3894.         strip_leading_space(info_output);
  3895.             
  3896.         if (!info_output.contains('(', 0)
  3897.             && !info_output.contains('[', 0))
  3898.         {
  3899.             // No breakpoint info - skip this line
  3900.             info_output = info_output.after('\n');
  3901.             continue;
  3902.         }
  3903.         string bp_nr_s = info_output.after(0);
  3904.         bp_nr = get_positive_nr (bp_nr_s);
  3905.         }
  3906.         break;
  3907.  
  3908.  
  3909.     case XDB:
  3910.         bp_nr = get_positive_nr(info_output);
  3911.         break;
  3912.  
  3913.     case JDB:
  3914.     case PERL:
  3915.     {
  3916.         // JDB and Perl have no breakpoint numbers.
  3917.         // Check if we already have a breakpoint at this location.
  3918.         bp_nr = breakpoint_number(info_output);
  3919.         if (bp_nr == 0)
  3920.         bp_nr = max_breakpoint_number_seen + 1;    // new breakpoint
  3921.         if (bp_nr < 0)
  3922.         {
  3923.         // Not a breakpoint
  3924.         string line = info_output.before('\n');
  3925.         if (!line.contains("Current breakpoints set"))
  3926.             keep_me += line;
  3927.         
  3928.         // Skip this line
  3929.         info_output = info_output.after('\n');
  3930.         continue;
  3931.         }
  3932.         break;
  3933.     }
  3934.     }
  3935.  
  3936.     if (bp_nr <= 0)
  3937.     {
  3938.         info_output = info_output.after('\n');
  3939.         continue;
  3940.     }
  3941.  
  3942.     if (bp_map.contains (bp_nr))
  3943.     {
  3944.         // Update existing breakpoint
  3945.         bps_not_read -= bp_nr;
  3946.         BreakPoint *bp = bp_map.get(bp_nr);
  3947.  
  3948.         ostrstream old_state;
  3949.         undo_buffer.add_breakpoint_state(old_state, bp);
  3950.  
  3951.         ostrstream local_commands;
  3952.         bool need_total_undo = false;
  3953.  
  3954.         bool bp_changed = 
  3955.         bp->update(info_output, local_commands, need_total_undo);
  3956.  
  3957.         if (bp_changed)
  3958.         {
  3959.         if (bp->position_changed() || bp->enabled_changed())
  3960.         {
  3961.             changed = true;
  3962.         }
  3963.  
  3964.         if (need_total_undo)
  3965.         {
  3966.             // To undo this change, we must delete the old
  3967.             // breakpoint and create a new one.
  3968.             undo_commands << delete_command(bp->number()) << "\n"
  3969.                   << string(old_state);
  3970.         }
  3971.         else
  3972.         {
  3973.             // A simple command suffices to undo this change.
  3974.             undo_commands << string(local_commands);
  3975.         }
  3976.         }
  3977.     }
  3978.     else
  3979.     {
  3980.         // New breakpoint
  3981.         changed = true;
  3982.         BreakPoint *new_bp = 
  3983.         new BreakPoint(info_output, break_arg, bp_nr, file);
  3984.         bp_map.insert(bp_nr, new_bp);
  3985.  
  3986.         if (gdb->has_delete_command())
  3987.         {
  3988.         string num = "@" + itostring(bp_nr) + "@";
  3989.         undo_commands << gdb->delete_command(num) << '\n';
  3990.         }
  3991.         else
  3992.         {
  3993.         undo_commands << delete_command(bp_nr) << '\n';
  3994.         }
  3995.  
  3996.         if (!added)
  3997.         {
  3998.         added = true;
  3999.         // Select this breakpoint only
  4000.         MapRef ref;
  4001.         for (BreakPoint* bp = bp_map.first(ref);
  4002.              bp != 0;
  4003.              bp = bp_map.next(ref))
  4004.         {
  4005.             bp->selected() = false;
  4006.         }
  4007.         }
  4008.         new_bp->selected() = true;
  4009.     }
  4010.  
  4011.     max_breakpoint_number_seen = max(max_breakpoint_number_seen, bp_nr);
  4012.     }
  4013.  
  4014.     // Keep this stuff for further processing
  4015.     info_output = keep_me;
  4016.  
  4017.     // Delete all breakpoints not found now
  4018.     for (i = 0; i < bps_not_read.size(); i++)
  4019.     {
  4020.     BreakPoint *bp = bp_map.get(bps_not_read[i]);
  4021.  
  4022.     // Perl only lists breakpoints in the current file
  4023.     if (gdb->type() == PERL && !bp_matches(bp, current_file_name))
  4024.         continue;
  4025.  
  4026.     // Delete it
  4027.     undo_buffer.add_breakpoint_state(undo_commands, bp);
  4028.     delete bp;
  4029.     bp_map.del(bps_not_read[i]);
  4030.  
  4031.     changed = true;
  4032.     }
  4033.  
  4034.     if (changed)
  4035.     refresh_bp_disp();
  4036.  
  4037.     // Set up breakpoint editor contents
  4038.     process_breakpoints(last_info_output);
  4039.  
  4040.     undo_buffer.add_command(string(undo_commands));
  4041.  
  4042.     // Set up existing panels
  4043.     update_properties_panels();
  4044. }
  4045.  
  4046. int SourceView::next_breakpoint_number()
  4047. {
  4048.     return max_breakpoint_number_seen + 1;
  4049. }
  4050.  
  4051.  
  4052. // Process GDB `info line main' output
  4053. void SourceView::process_info_line_main(string& info_output)
  4054. {
  4055.     clear_file_cache();
  4056.     clear_code_cache();
  4057.     clear_dbx_lookup_cache();
  4058.  
  4059.     if (info_output == "")
  4060.     return;
  4061.  
  4062.     current_file_name = "";
  4063.  
  4064.     switch (gdb->type())
  4065.     {
  4066.     case GDB:
  4067.     case XDB:
  4068.     case JDB:
  4069.     case PYDB:
  4070.     case PERL:
  4071.     {
  4072.     PosBuffer pos_buffer;
  4073.     pos_buffer.filter(info_output);
  4074.     pos_buffer.answer_ended();
  4075.     if (pos_buffer.pos_found())
  4076.         show_position(pos_buffer.get_position());
  4077.     if (pos_buffer.pc_found())
  4078.         show_pc(pos_buffer.get_pc());
  4079.     if (!pos_buffer.pos_found() && !pos_buffer.pc_found())
  4080.         add_current_to_history();
  4081.     }
  4082.     break;
  4083.  
  4084.     case DBX:
  4085.     {
  4086.     show_position(info_output);
  4087.     info_output = "";
  4088.     }
  4089.     break;
  4090.     }
  4091.  
  4092.     // Strip 'Line <n> of <file> starts at <address>...' info
  4093.     // Strip 'No symbol table is loaded.' info
  4094.     String strips[] = {"Line ", "No symbol table is loaded."};
  4095.  
  4096.     for (int i = 0; i < int(XtNumber(strips)); i++)
  4097.     {
  4098.     int line = info_output.index(strips[i]);
  4099.     if (line >= 0)
  4100.     {
  4101.         int end_line = info_output.index('\n', line);
  4102.         if (end_line >= 0)
  4103.         info_output(line, end_line - line) = "";
  4104.     }
  4105.     }
  4106.  
  4107.     check_remainder(info_output);
  4108. }
  4109.  
  4110. void SourceView::check_remainder(string& info_output)
  4111. {
  4112.     // Any remaining input is an informative GDB message.
  4113.     // Strip newlines around the message and post it.
  4114.  
  4115.     while (info_output.length() > 0 && 
  4116.        info_output[0] == '\n')
  4117.     info_output = info_output.after(0);
  4118.     while (info_output.length() > 0 && 
  4119.        info_output[info_output.length() - 1] == '\n')
  4120.     info_output = info_output.before(int(info_output.length() - 1));
  4121.  
  4122.     post_gdb_message(info_output, true, source_text_w);
  4123. }
  4124.  
  4125.  
  4126.  
  4127. //-----------------------------------------------------------------------
  4128. // Locate position
  4129. //-----------------------------------------------------------------------
  4130.  
  4131. void SourceView::lookup(string s, bool silent)
  4132. {
  4133.     if (s != "" && isspace(s[0]))
  4134.     s = s.after(rxwhite);
  4135.  
  4136.     undo_buffer.set_source("lookup");
  4137.  
  4138.     if (s == "")
  4139.     {
  4140.     // Empty argument given
  4141.     if (last_execution_pc != "")
  4142.     {
  4143.         // Show last PC
  4144.         show_pc(last_execution_pc, XmHIGHLIGHT_SELECTED);
  4145.     }
  4146.     else
  4147.     {
  4148.         // Show cursor position
  4149.         SetInsertionPosition(code_text_w,
  4150.                  XmTextGetInsertionPosition(code_text_w));
  4151.     }
  4152.  
  4153.     if (last_execution_file != "")
  4154.     {
  4155.         // Show last execution position
  4156.         _show_execution_position(last_execution_file, 
  4157.                      last_execution_line,
  4158.                      silent, false);
  4159.     }
  4160.     else
  4161.     {
  4162.         // Show cursor position
  4163.         SetInsertionPosition(source_text_w,
  4164.                  XmTextGetInsertionPosition(source_text_w));
  4165.     }
  4166.     }
  4167.     else if (is_file_pos(s))
  4168.     {
  4169.     // FILE:LINE given
  4170.     add_current_to_history();
  4171.     if (gdb->type() == GDB)
  4172.     {
  4173.         Command c("list " + s);
  4174.         c.verbose = !silent;
  4175.         c.echo    = !silent;
  4176.         c.prompt  = !silent;
  4177.         gdb_command(c);
  4178.     }
  4179.     else
  4180.     {
  4181.         show_position(s);
  4182.     }
  4183.     }
  4184.     else if (s[0] != '0' && isdigit(s[0]))
  4185.     {
  4186.     // Line number given
  4187.     int line = atoi(s);
  4188.     if (line > 0 && line <= line_count)
  4189.     {
  4190.         add_current_to_history();
  4191.  
  4192.         switch (gdb->type())
  4193.         {
  4194.         case GDB:
  4195.         {
  4196.         Command c("list " + current_source_name() + ":" + 
  4197.               itostring(line));
  4198.         c.verbose = !silent;
  4199.         c.echo    = !silent;
  4200.         c.prompt  = !silent;
  4201.         gdb_command(c);
  4202.         break;
  4203.         }
  4204.         
  4205.         case JDB:
  4206.         show_position(current_source_name() + ":" + itostring(line));
  4207.         break;
  4208.  
  4209.         case DBX:
  4210.         case XDB:
  4211.         case PYDB:
  4212.         case PERL:
  4213.         show_position(full_path(current_file_name) 
  4214.                   + ":" + itostring(line));
  4215.         break;
  4216.         }
  4217.     }
  4218.     else
  4219.     {
  4220.         if (!silent)
  4221.         post_error("No line " 
  4222.                + itostring(line) + " in current source.",
  4223.                "no_such_line_error", source_text_w);
  4224.     }
  4225.     }
  4226.     else if (s[0] == '#')
  4227.     {
  4228.     // Breakpoint given
  4229.     string nr_str = s.after('#');
  4230.     int nr = get_positive_nr(nr_str);
  4231.     if (nr >= 0)
  4232.     {
  4233.         MapRef ref;
  4234.         BreakPoint *bp;
  4235.         for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  4236.         {
  4237.         if (nr == bp->number())
  4238.         {
  4239.             add_current_to_history();
  4240.             show_position(bp->pos());
  4241.             show_pc(bp->address());
  4242.             break;
  4243.         }
  4244.         }
  4245.  
  4246.         if (bp == 0 && !silent)
  4247.         post_error("No breakpoint number " + itostring(nr) + ".", 
  4248.                "no_such_breakpoint_error", source_text_w);
  4249.     }
  4250.     }
  4251.     else
  4252.     {
  4253.     // Function or *address given
  4254.     add_current_to_history();
  4255.     switch (gdb->type())
  4256.     {
  4257.     case GDB:
  4258.     case PYDB:
  4259.     {
  4260.         if (s[0] == '0')    // Address given
  4261.         s.prepend("*");
  4262.         if (gdb->has_quotes())
  4263.         {
  4264.         if (s.length() > 0 && s[0] != '\'' && s[0] != '*')
  4265.             s = string('\'') + s + '\'';
  4266.         }
  4267.  
  4268.         Command c("list " + s);
  4269.         c.verbose = !silent;
  4270.         c.echo    = !silent;
  4271.         c.prompt  = !silent;
  4272.         gdb_command(c);
  4273.         break;
  4274.     }
  4275.  
  4276.     case PERL:
  4277.     {
  4278.         Command c("l " + s);
  4279.         c.verbose = !silent;
  4280.         c.echo    = !silent;
  4281.         c.prompt  = !silent;
  4282.         gdb_command(c);
  4283.         break;
  4284.     }
  4285.  
  4286.     case DBX:
  4287.     case JDB:
  4288.     {
  4289.         string pos = dbx_lookup(s, silent);
  4290.         if (pos != "")
  4291.         show_position(pos);
  4292.         break;
  4293.     }
  4294.  
  4295.     case XDB:
  4296.     {
  4297.         Command c("v " + s);
  4298.         c.verbose = !silent;
  4299.         c.echo    = !silent;
  4300.         c.prompt  = !silent;
  4301.         gdb_command(c);
  4302.         break;
  4303.     }
  4304.     }
  4305.     }
  4306. }
  4307.  
  4308.  
  4309.  
  4310. //-----------------------------------------------------------------------
  4311. // Position history
  4312. //-----------------------------------------------------------------------
  4313.  
  4314. // Add current position to history
  4315. void SourceView::add_current_to_history()
  4316. {
  4317.     XmTextPosition pos;
  4318.     int line_nr;
  4319.     bool in_text;
  4320.     int bp_nr;
  4321.     string address;
  4322.     bool pos_found;
  4323.  
  4324.     // Get position in source code
  4325.     pos = XmTextGetInsertionPosition(source_text_w);
  4326.     pos_found = get_line_of_pos(source_text_w, pos, line_nr, address, 
  4327.                 in_text, bp_nr);
  4328.     if (pos_found)
  4329.     add_position_to_history(current_source_name(), line_nr, false);
  4330.  
  4331.     // Get position in machine code
  4332.     pos = XmTextGetInsertionPosition(code_text_w);
  4333.     pos_found = get_line_of_pos(code_text_w, pos, line_nr, address, 
  4334.                 in_text, bp_nr);
  4335.     if (pos_found && address != "")
  4336.     undo_buffer.add_address(address, false);
  4337. }
  4338.  
  4339. // Add position to history
  4340. void SourceView::add_position_to_history(const string& file_name, int line, 
  4341.                      bool stopped)
  4342. {
  4343.     string source_name = file_name;
  4344.     switch (gdb->type())
  4345.     {
  4346.     case GDB:
  4347.     case JDB:
  4348.     case PYDB:
  4349.     // Use source names instead.
  4350.     if (source_name_cache.has(file_name))
  4351.         source_name = source_name_cache[file_name];
  4352.     break;
  4353.  
  4354.     case DBX:
  4355.     case XDB:
  4356.     case PERL:
  4357.     break;
  4358.     }
  4359.  
  4360.     undo_buffer.add_position(source_name, line, stopped);
  4361.     undo_buffer.add_state();
  4362. }
  4363.  
  4364. // Lookup entry from position history
  4365. void SourceView::goto_entry(const string& file_name, int line, 
  4366.                 const string& address, bool exec_pos)
  4367. {
  4368. #if 0
  4369.     // Show position in status line
  4370.     string msg = "";
  4371.     if (file_name != "")
  4372.     msg = "File " + quote(file_name);
  4373.     if (line != 0)
  4374.     {
  4375.     if (msg == "")
  4376.         msg = "Line ";
  4377.     else
  4378.         msg += ", line ";
  4379.     msg += itostring(line);
  4380.     }
  4381.     if (address != "")
  4382.     {
  4383.     if (msg == "")
  4384.         msg = "Address ";
  4385.     else
  4386.         msg += ", address ";
  4387.     msg += address;
  4388.     }
  4389.     set_status(msg);
  4390. #endif
  4391.  
  4392.     if (file_name != "")
  4393.     {
  4394.     // Lookup source
  4395.     if (!is_current_file(file_name))
  4396.     {
  4397.         read_file(file_name, line);
  4398.     }
  4399.  
  4400.     if (is_current_file(file_name) && line > 0 && line <= line_count)
  4401.     {
  4402.         if (exec_pos)
  4403.         {
  4404.         _show_execution_position(file_name, line, true, true);
  4405.         }
  4406.         else
  4407.         {
  4408.         XmTextPosition pos = pos_of_line(line);
  4409.         int indent = indent_amount(source_text_w, pos);
  4410.         SetInsertionPosition(source_text_w, pos + indent, true);
  4411.         }
  4412.     }
  4413.     }
  4414.  
  4415.     if (address != "")
  4416.     {
  4417.     // Lookup address
  4418.     show_pc(address, 
  4419.         (exec_pos || address == last_execution_pc) ? 
  4420.         XmHIGHLIGHT_SELECTED : XmHIGHLIGHT_NORMAL);
  4421.     }
  4422. }
  4423.  
  4424.  
  4425.  
  4426. //-----------------------------------------------------------------------
  4427. // Process current working directory
  4428. //-----------------------------------------------------------------------
  4429.  
  4430. void SourceView::process_pwd(string& pwd_output)
  4431. {
  4432.     strip_space(pwd_output);
  4433.  
  4434.     while (pwd_output != "")
  4435.     {
  4436.     string pwd;
  4437.     if (pwd_output.contains('\n'))
  4438.     {
  4439.         pwd        = pwd_output.before('\n');
  4440.         pwd_output = pwd_output.after('\n');
  4441.     }
  4442.     else
  4443.     {
  4444.         pwd        = pwd_output;
  4445.         pwd_output = "";
  4446.     }
  4447.  
  4448.     switch (gdb->type())
  4449.     {
  4450.     case GDB:            // 'Working directory PATH.'
  4451.     case PYDB:
  4452.         if (pwd.contains("Working directory", 0))
  4453.         {
  4454.         pwd = pwd.before('.', -1);
  4455.         pwd = pwd.after(' ', -1);
  4456.         }
  4457.         // FALL THROUGH
  4458.  
  4459.     case XDB:
  4460.     case DBX:        // 'PATH'
  4461.     case JDB:
  4462.     case PERL:
  4463.         if (pwd.contains('/', 0) && !pwd.contains(" "))
  4464.         {
  4465.         current_pwd = pwd;
  4466.         process_cd(current_pwd);
  4467.         return;
  4468.         }
  4469.         break;
  4470.     }
  4471.     }
  4472. }
  4473.  
  4474.  
  4475. //-----------------------------------------------------------------------
  4476. // Process current use path
  4477. //-----------------------------------------------------------------------
  4478.  
  4479. void SourceView::process_use(string& use_output)
  4480. {
  4481.     strip_space(use_output);
  4482.     current_class_path = use_output;
  4483.  
  4484.     clear_file_cache();
  4485.     reload();
  4486. }
  4487.  
  4488.  
  4489.  
  4490. //-----------------------------------------------------------------------
  4491. // Searching
  4492. //-----------------------------------------------------------------------
  4493.  
  4494. void SourceView::find(const string& s, 
  4495.               SourceView::SearchDirection direction,
  4496.               bool words_only,
  4497.               bool case_sensitive,
  4498.               Time time)
  4499. {
  4500.     int matchlen = s.length();
  4501.     int pos = -1;
  4502.     XmTextPosition cursor = XmTextGetInsertionPosition(source_text_w);
  4503.     XmTextPosition initial_cursor = cursor;
  4504.     int wraps = 0;
  4505.  
  4506.     if (!have_source())
  4507.     {
  4508.     post_error("No source.", "no_source_error", source_text_w);
  4509.     return;
  4510.     }
  4511.  
  4512.     string key  = s;
  4513.     string text = current_source;
  4514.     if (!case_sensitive)
  4515.     {
  4516.     // FIXME: This should be done according to the current locale
  4517.     key.downcase();
  4518.     text.downcase();
  4519.     }
  4520.  
  4521.     // Make sure we don't re-find the currently found word
  4522.     XmTextPosition startpos;
  4523.     XmTextPosition endpos;
  4524.  
  4525.     if (XmTextGetSelectionPosition(source_text_w, &startpos, &endpos))
  4526.     {
  4527.     switch (direction)
  4528.     {
  4529.     case forward:
  4530.         if (cursor == startpos
  4531.         && cursor < XmTextPosition(text.length()))
  4532.         cursor++;
  4533.         break;
  4534.     case backward:
  4535.         if (cursor == endpos && cursor > 0)
  4536.         cursor--;
  4537.         break;
  4538.     }
  4539.     }
  4540.  
  4541.     // Go and find the word
  4542.     for (;;) {
  4543.     switch (direction)
  4544.     {
  4545.     case forward:
  4546.         pos = text.index(key, cursor);
  4547.         if (pos < 0)
  4548.         {
  4549.         if (wraps++)
  4550.             break;
  4551.         pos = text.index(key, 0);
  4552.         }
  4553.         break;
  4554.     case backward:
  4555.         pos = text.index(key, cursor - text.length() - 1);
  4556.         if (pos < 0)
  4557.         {
  4558.         if (wraps++)
  4559.             break;
  4560.         pos = text.index(key, -1);
  4561.         }
  4562.         break;
  4563.     }
  4564.  
  4565.     if (pos < 0)
  4566.         break;        // String not found or double wrap
  4567.  
  4568.     // Set the cursor to the appropriate position
  4569.     switch (direction)
  4570.     {
  4571.     case forward:
  4572.         cursor = pos + matchlen;
  4573.         break;
  4574.     case backward:
  4575.         cursor = pos;
  4576.         break;
  4577.     }
  4578.  
  4579.     if (words_only)
  4580.     {
  4581.         // Make sure the occurrence is not part of a larger word
  4582.         if (pos > 0 && pos < int(text.length()))
  4583.         {
  4584.         if (isid(text[pos]) && isid(text[pos - 1]))
  4585.             continue;
  4586.         }
  4587.  
  4588.         if (pos + matchlen < int(text.length()))
  4589.         {
  4590.         if (isid(text[pos + matchlen - 1]) && 
  4591.             isid(text[pos + matchlen]))
  4592.             continue;
  4593.         }
  4594.     }
  4595.  
  4596.     // We got it!
  4597.     break;
  4598.     }
  4599.  
  4600.     if (pos > 0)
  4601.     {
  4602.     string msg;
  4603.  
  4604.     // Highlight occurrence
  4605.     XmTextSetSelection(source_text_w, pos, pos + matchlen, time);
  4606.  
  4607.     // Set position
  4608.     SetInsertionPosition(source_text_w, cursor, false);
  4609.  
  4610.     if (cursor == initial_cursor)
  4611.     {
  4612.         // Unchanged position
  4613.         msg = "No other occurrences of " + quote(s) + ".";
  4614.     }
  4615.     else
  4616.     {
  4617.         int line_nr;
  4618.         bool in_text;
  4619.         int bp_nr;
  4620.         string address;
  4621.  
  4622.         if (!get_line_of_pos(source_text_w, pos, line_nr, 
  4623.                  address, in_text, bp_nr))
  4624.         line_nr = line_count;
  4625.  
  4626.         string occurrence = current_source(pos, matchlen);
  4627.         msg = "Found " + quote(occurrence) + " in " + line_of_cursor();
  4628.         if (wraps)
  4629.         msg += " (wrapped)";
  4630.     }
  4631.  
  4632.     set_status(msg);
  4633.     }
  4634.     else
  4635.     {
  4636.     post_warning(quote(s) + " not found.", "source_find_error", 
  4637.              source_text_w);
  4638.     }
  4639. }
  4640.  
  4641.  
  4642.  
  4643.  
  4644. //-----------------------------------------------------------------------
  4645. // Return source name
  4646. //-----------------------------------------------------------------------
  4647.  
  4648. string SourceView::current_source_name()
  4649. {
  4650.     string source = "";
  4651.  
  4652.     switch (gdb->type())
  4653.     {
  4654.     case GDB:
  4655.     // GDB internally recognizes only `source names', i.e., the
  4656.     // source file name as compiled into the executable.
  4657.     if (source_name_cache[current_file_name] == "")
  4658.     {
  4659.         // Try the current source.
  4660.         string ans = gdb_question("info source");
  4661.         if (ans != NO_GDB_ANSWER)
  4662.         {
  4663.         ans = ans.before('\n');
  4664.         ans = ans.after(' ', -1);
  4665.  
  4666.         if (base_matches(ans, current_file_name))
  4667.         {
  4668.             // For security, we request that source and current
  4669.             // file have the same basename.
  4670.             source_name_cache[current_file_name] = ans;
  4671.         }
  4672.         else
  4673.         {
  4674.             // The current source does not match the current file.
  4675.             // Try all sources.
  4676.             static string all_sources = "<ALL SOURCES>";
  4677.  
  4678.             if (source_name_cache[all_sources] == "")
  4679.             {
  4680.             StringArray sources;
  4681.             get_gdb_sources(sources);
  4682.  
  4683.             if (sources.size() > 0)
  4684.             {
  4685.                 ans = "";
  4686.                 for (int i = 0; i < sources.size(); i++)
  4687.                 ans += sources[i] + '\n';
  4688.  
  4689.                 source_name_cache[all_sources] = ans;
  4690.             }
  4691.             }
  4692.  
  4693.             ans = source_name_cache[all_sources];
  4694.             if (ans != "")
  4695.             {
  4696.             int n = ans.freq('\n');
  4697.             string *sources = new string[n + 1];
  4698.             split(ans, sources, n + 1, '\n');
  4699.  
  4700.             for (int i = 0; i < n + 1; i++)
  4701.             {
  4702.                 if (base_matches(sources[i], current_file_name))
  4703.                 {
  4704.                 const string& src = sources[i];
  4705.                 source_name_cache[current_file_name] = src;
  4706.                 break;
  4707.                 }
  4708.             }
  4709.             
  4710.             delete[] sources;
  4711.  
  4712.             if (source_name_cache[current_file_name] == "")
  4713.             {
  4714.                 // No such source text.  Store the base name
  4715.                 // such that GDB is not asked again.
  4716.                 string base = basename(current_file_name.chars());
  4717.                 source_name_cache[current_file_name] = base;
  4718.             }
  4719.             }
  4720.         }
  4721.         }
  4722.     }
  4723.  
  4724.     source = source_name_cache[current_file_name];
  4725.     break;
  4726.  
  4727.     case DBX:
  4728.     case XDB:
  4729.     case PYDB:
  4730.     case PERL:
  4731.     if (app_data.use_source_path)
  4732.     {
  4733.         // These debuggers use full file names.
  4734.         source = full_path(current_file_name);
  4735.     }
  4736.     break;
  4737.  
  4738.     case JDB:
  4739.     if (source_name_cache.has(current_file_name))
  4740.     {
  4741.         // Use the source name as stored by read_class()
  4742.         source = source_name_cache[current_file_name];
  4743.     }
  4744.     if (source == "")
  4745.     {
  4746.         source = basename(current_file_name.chars());
  4747.         strip_java_suffix(source);
  4748.     }
  4749.     break;
  4750.     }
  4751.  
  4752.     // In case this does not work, use the current base name.
  4753.     if (source == "")
  4754.     source = basename(current_file_name.chars());
  4755.  
  4756.     return source;
  4757. }
  4758.  
  4759. string SourceView::line_of_cursor()
  4760. {
  4761.     XmTextPosition pos = XmTextGetInsertionPosition(source_text_w);
  4762.  
  4763.     string s = current_source_name();
  4764.     if (s == "")
  4765.     return "";        // No source
  4766.  
  4767.     int line_nr;
  4768.     bool in_text;
  4769.     int bp_nr;
  4770.     string address;
  4771.  
  4772.     if (get_line_of_pos(source_text_w, pos, line_nr, address, in_text, bp_nr))
  4773.     return s + ":" + itostring(line_nr);    // Cursor within source
  4774.     else
  4775.     return s + ":" + itostring(line_count);    // Cursor in last line
  4776. }
  4777.  
  4778. string SourceView::file_of_cursor()
  4779. {
  4780.     string pos = line_of_cursor();
  4781.     return full_path(current_file_name) + pos.from(':');
  4782. }
  4783.  
  4784.  
  4785. //-----------------------------------------------------------------------------
  4786. // Handle mouse selections
  4787. //----------------------------------------------------------------------------
  4788.  
  4789. void SourceView::setSelection(XtPointer client_data, XtIntervalId *)
  4790. {
  4791.     Widget w = (Widget)client_data;
  4792.  
  4793.     assert(XmIsText(w));
  4794.  
  4795.     XmTextSetSelection(w, selection_startpos, selection_endpos, 
  4796.                selection_time);
  4797.  
  4798.     selection_time = 0;
  4799.     set_source_argCB(w, XtPointer(false), 0);
  4800. }
  4801.  
  4802. void SourceView::startSelectWordAct (Widget text_w, XEvent* e, 
  4803.                      String *params, Cardinal *num_params)
  4804. {
  4805. #if XtSpecificationRelease < 6
  4806.     selection_event = *e;
  4807. #endif
  4808.  
  4809.     XtCallActionProc(text_w, "grab-focus", e, params, *num_params);
  4810.  
  4811.     if (e->type != ButtonPress && e->type != ButtonRelease)
  4812.     return;
  4813.  
  4814.     XButtonEvent *event = &e->xbutton;
  4815.  
  4816.     XmTextPosition pos = XmTextXYToPos (text_w, event->x, event->y);
  4817.  
  4818.     XmTextPosition startpos, endpos;
  4819.     if (app_data.source_editing)
  4820.     startpos = endpos = pos;
  4821.     else
  4822.     find_word_bounds(text_w, pos, startpos, endpos);
  4823.  
  4824.     selection_click    = true;
  4825.     selection_startpos = startpos;
  4826.     selection_endpos   = endpos;
  4827.     selection_time     = time(e);
  4828.  
  4829.     XtAppAddTimeOut(XtWidgetToApplicationContext(text_w), 0, setSelection, 
  4830.             (XtPointer)text_w);
  4831. }
  4832.  
  4833. void SourceView::endSelectWordAct (Widget text_w, XEvent* e, 
  4834.                    String *params, Cardinal *num_params)
  4835. {
  4836. #if XtSpecificationRelease < 6
  4837.     selection_event = *e;
  4838. #endif
  4839.     selection_click = false;
  4840.  
  4841.     XtCallActionProc(text_w, "extend-end", e, params, *num_params);
  4842.  
  4843.     if (e->type != ButtonPress && e->type != ButtonRelease)
  4844.     return;
  4845.  
  4846.     XmTextPosition startpos, endpos;
  4847.     if (XmTextGetSelectionPosition(text_w, &startpos, &endpos))
  4848.     {
  4849.     selection_startpos = startpos;
  4850.     selection_endpos   = endpos;
  4851.     }
  4852.  
  4853.     selection_time = time(e);
  4854.  
  4855.     XtAppAddTimeOut(XtWidgetToApplicationContext(text_w), 0, setSelection,
  4856.            (XtPointer)text_w);
  4857. }
  4858.  
  4859.  
  4860. //-----------------------------------------------------------------------------
  4861. // Handle source popup
  4862. //----------------------------------------------------------------------------
  4863.  
  4864. void SourceView::set_text_popup_label(int item, const string& arg, bool sens)
  4865. {
  4866.     Widget w = text_popup[item].widget;
  4867.     MString label = MString(text_cmd_labels[item]) + tt(arg);
  4868.  
  4869.     XtVaSetValues(w, XmNlabelString, label.xmstring(), NULL);
  4870.     set_sensitive(w, sens);
  4871. }
  4872.  
  4873. void SourceView::set_text_popup_resource(int item, const string& arg)
  4874. {
  4875.     if (lesstif_version <= 82)
  4876.     {
  4877.     // Set up resources for yet-to-be-created popup menu
  4878.     string db = string(DDD_CLASS_NAME "*text_popup.") 
  4879.         + text_popup[item].name + "." + XmNlabelString + ": "
  4880.         + "@" CHARSET_RM " " + text_cmd_labels[item] 
  4881.         + " @" CHARSET_TT " " + arg;
  4882.  
  4883.     XrmDatabase res = XrmGetStringDatabase(db.chars());
  4884.     XrmDatabase target = XtDatabase(XtDisplay(source_text_w));
  4885.     XrmMergeDatabases(res, &target);
  4886.     }
  4887. }
  4888.  
  4889. // Get relative coordinates of GLYPH in TEXT
  4890. void SourceView::translate_glyph_pos(Widget glyph, Widget text, int& x, int& y)
  4891. {
  4892.     int dest_x, dest_y;
  4893.     Window child;
  4894.     XTranslateCoordinates(XtDisplay(glyph), 
  4895.               XtWindow(glyph), XtWindow(text), 
  4896.               x, y, &dest_x, &dest_y, &child);
  4897.  
  4898.     x = dest_x;
  4899.     y = dest_y;
  4900. }
  4901.  
  4902. // Popup button3 source menu
  4903. void SourceView::srcpopupAct (Widget w, XEvent* e, String *, Cardinal *)
  4904. {
  4905.     if (e->type != ButtonPress && e->type != ButtonRelease)
  4906.     return;
  4907.  
  4908.     Widget text_w;
  4909.     if (is_source_widget(w))
  4910.     text_w = source_text_w;
  4911.     else if (is_code_widget(w))
  4912.     text_w = code_text_w;
  4913.     else
  4914.     return;
  4915.  
  4916.     XButtonEvent* event = &e->xbutton;
  4917.  
  4918.     int x = event->x;
  4919.     int y = event->y;
  4920.  
  4921.     if (w != source_text_w && w != code_text_w)
  4922.     {
  4923.     // Called from a glyph: translate glyph position to text position
  4924.     translate_glyph_pos(w, text_w, x, y);
  4925.     }
  4926.  
  4927.     // Get the position
  4928.     XmTextPosition pos = XmTextXYToPos(text_w, x, y);
  4929.  
  4930.     // Move the insertion cursor to this position, but don't disturb the
  4931.     // selection
  4932.     XmTextPosition left, right;
  4933.     Boolean have_selection = XmTextGetSelectionPosition(text_w, &left, &right);
  4934.     if (have_selection && pos >= left && pos <= right)
  4935.     {
  4936.     // Do not scroll here.  Do not use SetInsertionPosition().
  4937.     XmTextSetInsertionPosition(text_w, pos);
  4938.     XmTextSetSelection(text_w, left, right, time(e));
  4939.     }
  4940.     else
  4941.     {
  4942.     // Do not scroll here.  Do not use SetInsertionPosition().
  4943.     XmTextClearSelection(text_w, time(e));
  4944.     XmTextSetInsertionPosition(text_w, pos);
  4945.     }
  4946.  
  4947.     int line_nr;
  4948.     bool in_text;
  4949.     static int bp_nr;
  4950.     static string address;
  4951.     bool pos_found = get_line_of_pos(w, pos, line_nr, address, in_text, bp_nr);
  4952.     bool right_of_text = 
  4953.     pos < XmTextPosition(current_text(w).length()) 
  4954.     && current_text(w)[pos] == '\n';
  4955.  
  4956.     if (pos_found && bp_nr != 0)
  4957.     {
  4958.     // Clicked on breakpoint: create breakpoint menu
  4959.     static Widget bp_popup_w      = 0;
  4960.     static Widget bp_popup_parent = 0;
  4961.  
  4962.     if (lesstif_version <= 84 && w != bp_popup_parent)
  4963.     {
  4964.         // LessTif 0.84 and earlier wants this menu re-created
  4965.         // every time the parent has changed.  Otherwise, it gets
  4966.         // insensitive.
  4967.         if (bp_popup_w != 0)
  4968.         XtDestroyWidget(bp_popup_w);
  4969.         bp_popup_w = 0;
  4970.     }
  4971.  
  4972.     if (bp_popup_w == 0)
  4973.     {
  4974.         bp_popup_parent = w;
  4975.         bp_popup_w = MMcreatePopupMenu(w, "bp_popup", bp_popup);
  4976.         MMaddCallbacks (bp_popup, XtPointer(&bp_nr));
  4977.         MMaddHelpCallback(bp_popup, ImmediateHelpCB);
  4978.         InstallButtonTips(bp_popup_w);
  4979.     }
  4980.  
  4981.     // Grey out unsupported functions
  4982.     set_sensitive(bp_popup[BPItms::Disable].widget, gdb->can_disable());
  4983.     set_sensitive(bp_popup[BPItms::SetPC].widget,
  4984.                gdb->has_jump_command() || gdb->has_assign_command());
  4985.  
  4986.     MString label(bp_map.get(bp_nr)->enabled() ? 
  4987.               "Disable Breakpoint" : "Enable Breakpoint");
  4988.     XtVaSetValues(bp_popup[BPItms::Disable].widget,
  4989.               XmNlabelString, label.xmstring(),
  4990.               NULL);
  4991.  
  4992.     XmMenuPosition(bp_popup_w, event);
  4993.     XtManageChild(bp_popup_w);
  4994.     }
  4995.     else if (pos_found 
  4996.          && (line_nr > 0 || address != "") 
  4997.          && (!in_text || right_of_text))
  4998.     {
  4999.     // Create popup menu for selected line
  5000.     static Widget line_popup_w = 0;
  5001.     if (line_popup_w == 0)
  5002.     {
  5003.         line_popup_w = MMcreatePopupMenu (w, "line_popup", line_popup);
  5004.         MMaddCallbacks(line_popup, XtPointer(&address));
  5005.         MMaddHelpCallback(line_popup, ImmediateHelpCB);
  5006.         InstallButtonTips(line_popup_w);
  5007.  
  5008.         set_sensitive(line_popup[LineItms::SetTempBP].widget, 
  5009.                gdb->has_temporary_breakpoints());
  5010.         set_sensitive(line_popup[LineItms::SetPC].widget,
  5011.                gdb->has_jump_command() || 
  5012.                gdb->has_assign_command());
  5013.     }
  5014.  
  5015.     if (is_source_widget(w))
  5016.         address = current_source_name() + ":" + itostring(line_nr);
  5017.     else
  5018.         address = string('*') + address;
  5019.     XmMenuPosition (line_popup_w, event);
  5020.     XtManageChild (line_popup_w);
  5021.     }
  5022.     else
  5023.     {
  5024.     // Determine surrounding token (or selection) and create popup
  5025.     static string word;
  5026.  
  5027.     XmTextPosition startpos = 0;
  5028.     XmTextPosition endpos   = 0;
  5029.  
  5030.     if (pos_found)
  5031.         word = get_word_at_pos(text_w, pos, startpos, endpos);
  5032.  
  5033.     // Popup specific word menu
  5034.     string current_arg = word;
  5035.     shorten(current_arg, max_popup_expr_length);
  5036.     string current_ref_arg = deref(current_arg);
  5037.  
  5038.     if (lesstif_version <= 82)
  5039.     {
  5040.         set_text_popup_resource(TextItms::Print,    current_arg);
  5041.         set_text_popup_resource(TextItms::Disp,     current_arg);
  5042.         set_text_popup_resource(TextItms::Watch,    current_arg);
  5043.         set_text_popup_resource(TextItms::PrintRef, current_ref_arg);
  5044.         set_text_popup_resource(TextItms::DispRef,  current_ref_arg);
  5045.         set_text_popup_resource(TextItms::WatchRef, current_ref_arg);
  5046.         set_text_popup_resource(TextItms::Whatis,   current_arg);
  5047.         set_text_popup_resource(TextItms::Lookup,   current_arg);
  5048.         set_text_popup_resource(TextItms::Break,    current_arg);
  5049.         set_text_popup_resource(TextItms::Clear,    current_arg);
  5050.     }
  5051.  
  5052.     Widget text_popup_w = 
  5053.         MMcreatePopupMenu(text_w, "text_popup", text_popup);
  5054.     MMaddCallbacks(text_popup, XtPointer(&word));
  5055.     MMaddHelpCallback(text_popup, ImmediateHelpCB);
  5056.     InstallButtonTips(text_popup_w);
  5057.  
  5058.     // The popup menu is destroyed immediately after having popped down.
  5059.     Widget shell = XtParent(text_popup_w);
  5060.     XtAddCallback(shell, XtNpopdownCallback, DestroyThisCB, shell);
  5061.  
  5062.     bool has_arg = (word.length() > 0);
  5063.     bool has_watch = has_arg && gdb->has_watch_command();
  5064.     set_text_popup_label(TextItms::Print,    current_arg, has_arg);
  5065.     set_text_popup_label(TextItms::Disp,     current_arg, has_arg);
  5066.     set_text_popup_label(TextItms::Watch,    current_arg, has_watch);
  5067.     set_text_popup_label(TextItms::PrintRef, current_ref_arg, has_arg);
  5068.     set_text_popup_label(TextItms::DispRef,  current_ref_arg, has_arg);
  5069.     set_text_popup_label(TextItms::WatchRef, current_ref_arg, has_watch);
  5070.     set_text_popup_label(TextItms::Whatis,   current_arg, has_arg);
  5071.     set_text_popup_label(TextItms::Lookup,   current_arg, has_arg);
  5072.     set_text_popup_label(TextItms::Break,    current_arg, has_arg);
  5073.     set_text_popup_label(TextItms::Clear,    current_arg, has_arg);
  5074.  
  5075.     if (current_arg != current_ref_arg)
  5076.     {
  5077.         XtManageChild(text_popup[TextItms::Sep1].widget);
  5078.         XtManageChild(text_popup[TextItms::PrintRef].widget);
  5079.         XtManageChild(text_popup[TextItms::DispRef].widget);
  5080.         // XtManageChild(text_popup[TextItms::WatchRef].widget);
  5081.     }
  5082.     else
  5083.     {
  5084.         XtUnmanageChild(text_popup[TextItms::Sep1].widget);
  5085.         XtUnmanageChild(text_popup[TextItms::PrintRef].widget);
  5086.         XtUnmanageChild(text_popup[TextItms::DispRef].widget);
  5087.         XtUnmanageChild(text_popup[TextItms::WatchRef].widget);
  5088.     }
  5089.  
  5090.     XmMenuPosition (text_popup_w, event);
  5091.     XtManageChild (text_popup_w);
  5092.     }
  5093. }
  5094.  
  5095.  
  5096. void SourceView::doubleClickAct(Widget w, XEvent *e, String *params, 
  5097.                 Cardinal *num_params)
  5098. {
  5099.     if (e->type != ButtonPress && e->type != ButtonRelease)
  5100.     return;
  5101.  
  5102.     Widget text_w;
  5103.     if (is_source_widget(w))
  5104.     text_w = source_text_w;
  5105.     else if (is_code_widget(w))
  5106.     text_w = code_text_w;
  5107.     else
  5108.     return;
  5109.  
  5110.     XButtonEvent* event = &e->xbutton;
  5111.  
  5112.     int x = event->x;
  5113.     int y = event->y;
  5114.     bool control = ((event->state & ControlMask) != 0);
  5115.  
  5116.     if (w != source_text_w && w != code_text_w)
  5117.     {
  5118.     // Called from a glyph: translate glyph position to text position
  5119.     translate_glyph_pos(w, text_w, x, y);
  5120.     }
  5121.  
  5122.     if (w == source_text_w || w == code_text_w)
  5123.     {
  5124.     // Called from text: check for double click
  5125.     Time selection_time = time(e);
  5126.     static Time last_selection_time = 0;
  5127.  
  5128.     bool double_click = 
  5129.         last_selection_time != 0 &&
  5130.         (Time(selection_time - last_selection_time) <= 
  5131.          Time(XtGetMultiClickTime(XtDisplay(text_w))));
  5132.  
  5133.     if (double_click)
  5134.         last_selection_time = 0;
  5135.     else
  5136.         last_selection_time = selection_time;
  5137.  
  5138.     if (!double_click)
  5139.         return;
  5140.     }
  5141.  
  5142.     // Get the position
  5143.     XmTextPosition pos = XmTextXYToPos(text_w, x, y);
  5144.  
  5145.     int line_nr;
  5146.     bool in_text;
  5147.     static int bp_nr;
  5148.     static string address;
  5149.     bool pos_found = get_line_of_pos(w, pos, line_nr, address, in_text, bp_nr);
  5150.  
  5151.     if (!pos_found)
  5152.     return;
  5153.  
  5154.     if (bp_nr != 0)
  5155.     {
  5156.     // Clicked on breakpoint
  5157.     if (control)
  5158.         delete_bp(bp_nr, text_w);
  5159.     else
  5160.         edit_bp(bp_nr, text_w);
  5161.     return;
  5162.     }
  5163.  
  5164.     XmTextPosition startpos = 0;
  5165.     XmTextPosition endpos   = 0;
  5166.  
  5167.     string arg  = source_arg->get_string();
  5168.     string word = get_word_at_pos(text_w, pos, startpos, endpos);
  5169.  
  5170.     if (in_text && arg == word)
  5171.     {
  5172.     // In text: do some action on current word
  5173.     if (text_w == source_text_w)
  5174.     {
  5175.         // Check for function call
  5176.         int p = endpos;
  5177.         while (p < (int)current_source.length() && 
  5178.            isspace(current_source[p]))
  5179.         p++;
  5180.  
  5181.         if (control || current_source.contains('(', p))
  5182.         {
  5183.         if (*num_params >= 3)
  5184.             gdb_button_command(params[2]);
  5185.         else
  5186.             gdb_button_command("list ()");
  5187.         return;
  5188.         }
  5189.     }
  5190.  
  5191.     if (*num_params >= 1)
  5192.         gdb_button_command(params[0]);
  5193.     else
  5194.         gdb_button_command("graph display ()");
  5195.     return;
  5196.     }
  5197.  
  5198.     if (!in_text)
  5199.     {
  5200.     // In breakpoint area
  5201.     IntArray bps;
  5202.     if (text_w == source_text_w)
  5203.     {
  5204.         MapRef ref;
  5205.         for (BreakPoint* bp = bp_map.first(ref);
  5206.          bp != 0;
  5207.          bp = bp_map.next(ref))
  5208.         {
  5209.         if (bp_matches(bp, line_nr))
  5210.             bps += bp->number();
  5211.         }
  5212.     }
  5213.     else
  5214.     {
  5215.         MapRef ref;
  5216.         for (BreakPoint* bp = bp_map.first(ref);
  5217.          bp != 0;
  5218.          bp = bp_map.next(ref))
  5219.         {
  5220.         if (bp->type() == BREAKPOINT && 
  5221.             compare_address(address, bp->address()) == 0)
  5222.             bps += bp->number();
  5223.         }
  5224.     }
  5225.  
  5226.     if (bps.size() > 0)
  5227.     {
  5228.         // In breakpoint area, and we already have a breakpoint.
  5229.         if (control)
  5230.         delete_bps(bps, text_w);
  5231.         else
  5232.         edit_bps(bps, text_w);
  5233.     }
  5234.     else
  5235.     {
  5236.         // In breakpoint area, and we have no breakpoint: create a new one
  5237.         if (*num_params >= 2)
  5238.         gdb_button_command(params[1]);
  5239.         else if (control)
  5240.         create_temp_bp(source_arg->get_string(), w);
  5241.         else
  5242.         create_bp(source_arg->get_string(), w);
  5243.     }
  5244.     }
  5245. }
  5246.  
  5247. void SourceView::setArgAct(Widget w, XEvent *, String *, Cardinal *)
  5248. {
  5249.     String s = 0;
  5250.     if (XmIsText(w))
  5251.     s = XmTextGetSelection(w);
  5252.     else if (XmIsTextField(w))
  5253.     s = XmTextFieldGetSelection(w);
  5254.  
  5255.     if (s != 0)
  5256.     {
  5257.     source_arg->set_string(s);
  5258.     XtFree(s);
  5259.     }
  5260. }
  5261.  
  5262.  
  5263. //-----------------------------------------------------------------------------
  5264. // Breakpoint selection
  5265. //----------------------------------------------------------------------------
  5266.  
  5267. void SourceView::NewBreakpointDCB(Widget w, XtPointer client_data, XtPointer)
  5268. {
  5269.     Widget text = Widget(client_data);
  5270.     String _input = XmTextFieldGetString(text);
  5271.     string input(_input);
  5272.     XtFree(_input);
  5273.     if (input == "")
  5274.     return;
  5275.  
  5276.     create_bp(input, w);
  5277. }
  5278.  
  5279. void SourceView::NewBreakpointCB(Widget w, XtPointer, XtPointer)
  5280. {
  5281.     static Widget dialog = 0;
  5282.     if (dialog == 0)
  5283.     {
  5284.     Arg args[10];
  5285.     Cardinal arg = 0;
  5286.     dialog = verify(XmCreatePromptDialog(find_shell(w),
  5287.                          "new_breakpoint_dialog",
  5288.                          args, arg));
  5289.     Delay::register_shell(dialog);
  5290.  
  5291.     if (lesstif_version <= 79)
  5292.         XtUnmanageChild(XmSelectionBoxGetChild(dialog,
  5293.                            XmDIALOG_APPLY_BUTTON));
  5294.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  5295.                            XmDIALOG_SELECTION_LABEL));
  5296.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
  5297.  
  5298.     arg = 0;
  5299.     XtSetArg(args[arg], XmNmarginWidth,  0); arg++;
  5300.     XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
  5301.     XtSetArg(args[arg], XmNborderWidth,  0); arg++;
  5302.     Widget box = XmCreateRowColumn(dialog, "box", args, arg);
  5303.     XtManageChild(box);
  5304.  
  5305.     Widget label = XmCreateLabel(box, "label", args, arg);
  5306.     XtManageChild(label);
  5307.  
  5308.     arg = 0;
  5309.     Widget text = CreateComboBox(box, "text", args, arg);
  5310.     tie_combo_box_to_history(text, break_history_filter);
  5311.  
  5312.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
  5313.     XtAddCallback(dialog, XmNokCallback, NewBreakpointDCB, 
  5314.               XtPointer(text));
  5315.     }
  5316.  
  5317.     manage_and_raise(dialog);
  5318. }
  5319.  
  5320. WatchMode SourceView::selected_watch_mode = WATCH_CHANGE;
  5321.  
  5322. void SourceView::SetWatchModeCB(Widget, XtPointer client_data, 
  5323.                 XtPointer call_data)
  5324. {
  5325.     XmToggleButtonCallbackStruct *info = 
  5326.     (XmToggleButtonCallbackStruct *)call_data;
  5327.  
  5328.     if (info->set)
  5329.     selected_watch_mode = WatchMode((int)(long)client_data);
  5330. }
  5331.  
  5332. void SourceView::NewWatchpointDCB(Widget w, XtPointer client_data, XtPointer)
  5333. {
  5334.     Widget text = Widget(client_data);
  5335.     String _input = XmTextFieldGetString(text);
  5336.     string input(_input);
  5337.     XtFree(_input);
  5338.  
  5339.     strip_space(input);
  5340.     if (input == "")
  5341.     return;
  5342.  
  5343.     gdb_command(gdb->watch_command(input, selected_watch_mode), w);
  5344. }
  5345.  
  5346. void SourceView::NewWatchpointCB(Widget w, XtPointer, XtPointer)
  5347. {
  5348.     static Widget dialog = 0;
  5349.     if (dialog == 0)
  5350.     {
  5351.     static Widget cwatch_w, rwatch_w, awatch_w;
  5352.  
  5353.     static MMDesc wp_modes[] =
  5354.     {
  5355.         { "cwatch",  MMPush, { SetWatchModeCB, XtPointer(WATCH_CHANGE) }, 
  5356.           NULL, &cwatch_w, 0, 0 },
  5357.         { "rwatch",  MMPush, { SetWatchModeCB, XtPointer(WATCH_READ) }, 
  5358.           NULL, &rwatch_w, 0, 0 },
  5359.         { "awatch", MMPush, { SetWatchModeCB, XtPointer(WATCH_ACCESS)},
  5360.           NULL, &awatch_w, 0, 0 },
  5361.         MMEnd
  5362.     };
  5363.  
  5364.     static MMDesc wp_menu[] = 
  5365.     {
  5366.         { "set",      MMLabel, MMNoCB, 0, 0, 0, 0 },
  5367.         { "method",   MMOptionMenu, MMNoCB, wp_modes, 0, 0, 0 },
  5368.         { "on",       MMLabel, MMNoCB, 0, 0, 0, 0 },
  5369.         MMEnd
  5370.     };
  5371.  
  5372.     Arg args[10];
  5373.     Cardinal arg = 0;
  5374.     dialog = verify(XmCreatePromptDialog(find_shell(w),
  5375.                          "new_watchpoint_dialog",
  5376.                          args, arg));
  5377.     Delay::register_shell(dialog);
  5378.  
  5379.     if (lesstif_version <= 79)
  5380.         XtUnmanageChild(XmSelectionBoxGetChild(dialog,
  5381.                            XmDIALOG_APPLY_BUTTON));
  5382.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  5383.                            XmDIALOG_SELECTION_LABEL));
  5384.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
  5385.  
  5386.     arg = 0;
  5387.     XtSetArg(args[arg], XmNmarginWidth,  0); arg++;
  5388.     XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
  5389.     XtSetArg(args[arg], XmNborderWidth,  0); arg++;
  5390.     Widget box = XmCreateRowColumn(dialog, "box", args, arg);
  5391.     XtManageChild(box);
  5392.  
  5393.     arg = 0;
  5394.     XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
  5395.     XtSetArg(args[arg], XmNborderWidth,  0); arg++;
  5396.     XtSetArg(args[arg], XmNentryBorder,  0); arg++;
  5397.     XtSetArg(args[arg], XmNspacing,      0); arg++;
  5398.     XtSetArg(args[arg], XmNmarginWidth,  0); arg++;
  5399.     XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
  5400.     Widget panel = MMcreateButtonPanel(box, "panel", wp_menu, args, arg);
  5401.     (void) panel;
  5402.     MMaddCallbacks(wp_menu);
  5403.     MMaddHelpCallback(wp_menu, ImmediateHelpCB);
  5404.  
  5405.     set_sensitive(cwatch_w, (gdb->has_watch_command() & WATCH_CHANGE) 
  5406.                == WATCH_CHANGE);
  5407.     set_sensitive(rwatch_w, (gdb->has_watch_command() & WATCH_READ) 
  5408.                == WATCH_READ);
  5409.     set_sensitive(awatch_w, (gdb->has_watch_command() & WATCH_ACCESS) 
  5410.                == WATCH_ACCESS);
  5411.  
  5412.     // Initialize: use CWATCH as default menu item
  5413.     XtCallActionProc(cwatch_w, "ArmAndActivate", 
  5414.              (XEvent *)0, (String *)0, 0);
  5415.  
  5416.     arg = 0;
  5417.     Widget text = CreateComboBox(box, "text", args, arg);
  5418.     tie_combo_box_to_history(text, watch_history_filter);
  5419.  
  5420.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
  5421.     XtAddCallback(dialog, XmNokCallback, NewWatchpointDCB, 
  5422.               XtPointer(text));
  5423.  
  5424.     }
  5425.  
  5426.     manage_and_raise(dialog);
  5427. }
  5428.  
  5429.  
  5430. //-----------------------------------------------------------------------------
  5431. // Breakpoint properties
  5432. //----------------------------------------------------------------------------
  5433.  
  5434. struct BreakpointPropertiesInfo {
  5435.     IntArray nrs;        // The affected breakpoints
  5436.  
  5437.     Widget dialog;        // The widgets of the properties panel
  5438.     Widget title;
  5439.     Widget lookup;
  5440.     Widget print;
  5441.     Widget enable;
  5442.     Widget disable;
  5443.     Widget temp;
  5444.     Widget del;
  5445.     Widget ignore;
  5446.     Widget condition;
  5447.     Widget record;
  5448.     Widget end;
  5449.     Widget edit;
  5450.     Widget editor;
  5451.  
  5452.     XtIntervalId timer;        // The spinbox timer
  5453.     bool spin_locked;        // If true, don't invoke spinbox callbacks
  5454.     int ignore_spin_update;    // If > 0, don't update spinbox from bp info
  5455.     bool sync_commands;        // If true, propagate cmd to other breakpoints
  5456.     BreakpointPropertiesInfo *next; // Next info in list
  5457.  
  5458.     static BreakpointPropertiesInfo *all; // List of all infos
  5459.  
  5460.     BreakpointPropertiesInfo()
  5461.     : nrs(),
  5462.       dialog(0), title(0), 
  5463.       lookup(0), enable(0), disable(0), temp(0), del(0),
  5464.       ignore(0), condition(0), record(0), edit(0), editor(0),
  5465.       timer(0), spin_locked(false), ignore_spin_update(0),
  5466.       sync_commands(false), next(all)
  5467.     {
  5468.     all = this;
  5469.     }
  5470.  
  5471.     ~BreakpointPropertiesInfo()
  5472.     {
  5473.     if (all == this)
  5474.     {
  5475.         all = next;
  5476.     }
  5477.     else
  5478.     {
  5479.         BreakpointPropertiesInfo *info = all;
  5480.         while (info->next != this)
  5481.         info = info->next;
  5482.         info->next = next;
  5483.     }
  5484.     }
  5485. };
  5486.  
  5487. BreakpointPropertiesInfo *BreakpointPropertiesInfo::all = 0;
  5488.  
  5489. void SourceView::DeleteInfoCB(Widget, XtPointer client_data, 
  5490.                   XtPointer call_data)
  5491. {
  5492.     BreakpointPropertiesInfo *info = 
  5493.     (BreakpointPropertiesInfo *)client_data;
  5494.  
  5495.     gdb->removeHandler(Recording, RecordingHP, (void *)info);
  5496.     if (gdb->recording())
  5497.     gdb_command("\003");    // Abort recording
  5498.  
  5499.     if (XtIsManaged(XtParent(info->editor)))
  5500.     {
  5501.     // Finish entering commands.  Since W is being destroyed, pass
  5502.     // SOURCE_TEXT_W as reference.
  5503.     EditBreakpointCommandsCB(source_text_w, client_data, call_data);
  5504.  
  5505.     // Update all remaining panels in the next run
  5506.     gdb->addHandler(Recording, RecordingHP, XtPointer(0));
  5507.     }
  5508.  
  5509.     delete info;
  5510. }
  5511.  
  5512. void SourceView::update_properties_panels()
  5513. {
  5514.     // Update all panels
  5515.     BreakpointPropertiesInfo *info = BreakpointPropertiesInfo::all;
  5516.     while (info != 0)
  5517.     {
  5518.     update_properties_panel(info);
  5519.     info = info->next;
  5520.     }
  5521. }
  5522.  
  5523. void SourceView::move_breakpoint_properties(int old_bp, int new_bp)
  5524. {
  5525.     // Update all panels
  5526.     bool update = false;
  5527.     BreakpointPropertiesInfo *info = BreakpointPropertiesInfo::all;
  5528.     while (info != 0)
  5529.     {
  5530.     bool changed = false;
  5531.     for (int i = 0; i < info->nrs.size(); i++)
  5532.     {
  5533.         if (info->nrs[i] == old_bp)
  5534.         {
  5535.         info->nrs[i] = new_bp;
  5536.         update = changed = true;
  5537.         }
  5538.     }
  5539.  
  5540.     if (changed)
  5541.         sort(info->nrs);    // Sort again
  5542.  
  5543.     info = info->next;
  5544.     }
  5545.  
  5546.     if (update)
  5547.     update_properties_panels();
  5548. }
  5549.  
  5550. void SourceView::copy_breakpoint_properties(int old_bp, int new_bp)
  5551. {
  5552.     // Update all panels
  5553.     bool update = false;
  5554.     BreakpointPropertiesInfo *info = BreakpointPropertiesInfo::all;
  5555.     while (info != 0)
  5556.     {
  5557.     for (int i = 0; i < info->nrs.size(); i++)
  5558.     {
  5559.         if (info->nrs[i] == old_bp)
  5560.         {
  5561.         info->nrs += new_bp;
  5562.         update = true;
  5563.         break;
  5564.         }
  5565.     }
  5566.  
  5567.     info = info->next;
  5568.     }
  5569.  
  5570.     if (update)
  5571.     update_properties_panels();
  5572. }
  5573.  
  5574. void SourceView::update_properties_panel(BreakpointPropertiesInfo *info)
  5575. {
  5576.     // Remove breakpoints from list
  5577.     bool future = false;
  5578.     int i;
  5579.     for (i = 0; i < info->nrs.size(); i++)
  5580.     {
  5581.     if (info->nrs[i] >= next_breakpoint_number())
  5582.     {
  5583.         // Panel for future (renamed) breakpoint
  5584.         future = true;
  5585.         continue;
  5586.     }
  5587.  
  5588.     BreakPoint *bp = bp_map.get(info->nrs[i]);
  5589.     if (bp == 0)
  5590.     {
  5591.         // Breakpoint not found -- mark as deleted
  5592.         info->nrs[i] = 0;
  5593.     }
  5594.     }
  5595.     IntArray new_nrs;
  5596.  
  5597.     for (i = 0; i < info->nrs.size(); i++)
  5598.     {
  5599.     if (info->nrs[i] != 0)
  5600.         new_nrs += info->nrs[i];
  5601.     }
  5602.  
  5603.     info->nrs = new_nrs;
  5604.  
  5605.     if (info->nrs.size() == 0)
  5606.     {
  5607.     // No breakpoint left -- destroy dialog shell
  5608.     XtUnmanageChild(info->dialog);
  5609.     return;
  5610.     }
  5611.  
  5612.     if (future)
  5613.     return;            // We cannot update yet
  5614.  
  5615.     // Use first breakpoint for getting values
  5616.     BreakPoint *bp = bp_map.get(info->nrs[0]);
  5617.     assert(bp != 0);
  5618.  
  5619.     // Set titles
  5620.     string what;
  5621.     switch (bp->type())
  5622.     {
  5623.     case BREAKPOINT:
  5624.     what = "Breakpoint";
  5625.     break;
  5626.  
  5627.     case WATCHPOINT:
  5628.     what = "Watchpoint";
  5629.     break;
  5630.     }
  5631.  
  5632.     string label;
  5633.     if (info->nrs.size() == 1)
  5634.     {
  5635.     label = what + " " + itostring(info->nrs[0]);
  5636.     }
  5637.     else
  5638.     {
  5639.     label = what + "s ";
  5640.     for (i = 0; i < info->nrs.size(); i++)
  5641.     {
  5642.         if (i > 0)
  5643.         {
  5644.         if (info->nrs.size() == 2)
  5645.             label += " and ";
  5646.         else if (i == info->nrs.size() - 1)
  5647.             label += ", and ";
  5648.         else
  5649.             label += ", ";
  5650.         }
  5651.         label += itostring(info->nrs[i]);
  5652.     }
  5653.     }
  5654.  
  5655.     set_label(info->title, label);
  5656.  
  5657.     MString title = string(DDD_NAME) + ": Properties: " + label;
  5658.     XtVaSetValues(info->dialog, XmNdialogTitle,
  5659.           title.xmstring(), NULL);
  5660.  
  5661.     // Set values
  5662.     string commands = "";
  5663.     for (i = 0; i < bp->commands().size(); i++)
  5664.     {
  5665.     string cmd = bp->commands()[i];
  5666.     strip_auto_command_prefix(cmd);
  5667.     commands += cmd + "\n";
  5668.     }
  5669.  
  5670.     XmTextSetString(info->editor, commands);
  5671.  
  5672.     if (info->ignore_spin_update > 0)
  5673.     {
  5674.     info->ignore_spin_update--;
  5675.     }
  5676.     else
  5677.     {
  5678.     bool lock = info->spin_locked;
  5679.     info->spin_locked = true;
  5680. #if XmVersion >= 2000
  5681.     if (XmIsSpinBox(XtParent(info->ignore)))
  5682.     {
  5683.         XtVaSetValues(info->ignore, XmNposition, bp->ignore_count(), NULL);
  5684.     }
  5685.     else
  5686. #endif
  5687.     {
  5688.         String old_ignore = XmTextFieldGetString(info->ignore);
  5689.         if (atoi(old_ignore) != bp->ignore_count())
  5690.         {
  5691.         string ignore = itostring(bp->ignore_count());
  5692.         if (ignore == "0")
  5693.             ignore = "";
  5694.  
  5695.         XmTextFieldSetString(info->ignore, (String)ignore);
  5696.         }
  5697.         XtFree(old_ignore);
  5698.     }
  5699.     info->spin_locked = lock;
  5700.     }
  5701.  
  5702.     // Don't update unchanged condition to prevent OSF/Motif 2.0
  5703.     // ComboBox from growing
  5704.     String old_condition = XmTextFieldGetString(info->condition);
  5705.     if (bp->condition() != old_condition)
  5706.     {
  5707.     XmTextFieldSetString(info->condition, (String)bp->condition());
  5708.     }
  5709.     XtFree(old_condition);
  5710.  
  5711.     bool can_enable   = false;
  5712.     bool can_disable  = false;
  5713.     bool can_maketemp = false;
  5714.     bool can_print    = false;
  5715.  
  5716.     for (i = 0; i < info->nrs.size(); i++)
  5717.     {
  5718.     BreakPoint *bp = bp_map.get(info->nrs[i]);
  5719.     if (bp->enabled())
  5720.         can_disable = gdb->can_disable();
  5721.     else
  5722.         can_enable  = gdb->can_enable();
  5723.  
  5724.     if (bp->dispo() != BPDEL)
  5725.         can_maketemp = (gdb->type() == GDB || gdb->type() == PYDB);
  5726.  
  5727.     if (bp->type() == WATCHPOINT)
  5728.         can_print = true;
  5729.     }
  5730.  
  5731.     if (can_print)
  5732.     XtManageChild(info->print);
  5733.     else
  5734.     XtUnmanageChild(info->print);
  5735.  
  5736.     set_sensitive(info->enable,  can_enable);
  5737.     set_sensitive(info->disable, can_disable);
  5738.     set_sensitive(info->temp,    can_maketemp);
  5739.  
  5740.     set_sensitive(info->ignore,           gdb->has_ignore_command());
  5741.     set_sensitive(XtParent(info->ignore), gdb->has_ignore_command());
  5742.  
  5743.     set_sensitive(info->condition,           gdb->has_breakpoint_conditions());
  5744.     set_sensitive(XtParent(info->condition), gdb->has_breakpoint_conditions());
  5745.  
  5746.     bool can_record = gdb->type() == GDB && !gdb->recording();
  5747.     set_sensitive(info->record,    can_record);
  5748.     set_sensitive(info->end,       gdb->recording());
  5749.     set_sensitive(info->edit,      can_record);
  5750.     set_sensitive(info->editor,    can_record);
  5751.  
  5752.     if (info->sync_commands)
  5753.     {
  5754.     for (i = 1; i < info->nrs.size(); i++)
  5755.         set_bp_commands(info->nrs[i], bp->commands());
  5756.     info->sync_commands = false;
  5757.     }
  5758. }
  5759.  
  5760. static string cond_filter(const string& cmd)
  5761. {
  5762.     switch (gdb->type())
  5763.     {
  5764.     case GDB:
  5765.     case PYDB:
  5766.     if (cmd.contains("cond", 0))
  5767.     {
  5768.         // Skip command
  5769.         string arg = cmd.after(rxwhite);
  5770.  
  5771.         // Skip breakpoint number
  5772.         arg = arg.after(rxwhite);
  5773.  
  5774.         strip_space(arg);
  5775.         return arg;
  5776.     }
  5777.     break;
  5778.  
  5779.     case DBX:
  5780.     if (cmd.contains("if "))
  5781.     {
  5782.         string arg = cmd.after("if ");
  5783.         strip_space(arg);
  5784.         return arg;
  5785.     }
  5786.     break;
  5787.  
  5788.     case XDB:
  5789.     if (cmd.contains("{if "))
  5790.     {
  5791.         string arg = cmd.after("if ");
  5792.         if (arg.contains("{}"))
  5793.         arg = arg.before("{}");
  5794.         strip_space(arg);
  5795.         return arg;
  5796.     }
  5797.     break;
  5798.  
  5799.     case JDB:
  5800.     // No conditions in JDB
  5801.     break;
  5802.  
  5803.     case PERL:
  5804.     {
  5805.     // FIXME
  5806.     break;
  5807.     }
  5808.     }
  5809.  
  5810.     return "";            // No condition
  5811. }
  5812.  
  5813.  
  5814. // Get selected breakpoint numbers
  5815. void SourceView::getBreakpointNumbers(IntArray& breakpoint_nrs)
  5816. {
  5817.     if (breakpoint_list_w == 0)
  5818.     return;
  5819.  
  5820.     IntArray numbers;
  5821.     getItemNumbers(breakpoint_list_w, numbers);
  5822.  
  5823.     // Double-check each number for safety.  One may define commands
  5824.     // as breakpoint numbers and cause DDD to crash.
  5825.     for (int i = 0; i < numbers.size(); i++)
  5826.     {
  5827.     BreakPoint *bp = bp_map.get(numbers[i]);
  5828.     if (bp != 0)
  5829.         breakpoint_nrs += numbers[i];
  5830.     }
  5831. }
  5832.  
  5833.  
  5834. // Edit breakpoint properties
  5835. void SourceView::EditBreakpointPropertiesCB(Widget, 
  5836.                         XtPointer client_data, 
  5837.                         XtPointer)
  5838. {
  5839.     IntArray breakpoint_nrs;
  5840.     if (client_data == 0)
  5841.     {
  5842.     getBreakpointNumbers(breakpoint_nrs);
  5843.     }
  5844.     else
  5845.     {
  5846.     breakpoint_nrs += *((int *)client_data);
  5847.     }
  5848.  
  5849.     edit_bps(breakpoint_nrs);
  5850. }
  5851.  
  5852. void SourceView::edit_bps(IntArray& breakpoint_nrs, Widget /* origin */)
  5853. {
  5854.     if (breakpoint_nrs.size() == 0)
  5855.     return;            // No breakpoints given
  5856.  
  5857.     sort(breakpoint_nrs);
  5858.  
  5859.     // Check for first breakpoint
  5860.     BreakPoint *bp = bp_map.get(breakpoint_nrs[0]);
  5861.     if (bp == 0)
  5862.     return;            // No such breakpoint
  5863.  
  5864.     BreakpointPropertiesInfo *info = new BreakpointPropertiesInfo;
  5865.     info->spin_locked = true;
  5866.     info->nrs = breakpoint_nrs;
  5867.  
  5868.     Arg args[10];
  5869.     int arg = 0;
  5870.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  5871.     info->dialog = 
  5872.     verify(XmCreatePromptDialog(source_text_w,
  5873.                     "breakpoint_properties",
  5874.                     args, arg));
  5875.     XtVaSetValues(info->dialog, XmNdefaultButton, Widget(0), NULL);
  5876.  
  5877.  
  5878.     // Remove old prompt
  5879.     Widget text = XmSelectionBoxGetChild(info->dialog, XmDIALOG_TEXT);
  5880.     XtUnmanageChild(text);
  5881.     Widget old_label = 
  5882.     XmSelectionBoxGetChild(info->dialog, XmDIALOG_SELECTION_LABEL);
  5883.     XtUnmanageChild(old_label);
  5884.  
  5885.     Delay::register_shell(info->dialog);
  5886.  
  5887.     if (lesstif_version <= 79)
  5888.     XtUnmanageChild(XmSelectionBoxGetChild(info->dialog, 
  5889.                            XmDIALOG_APPLY_BUTTON));
  5890.     XtUnmanageChild(XmSelectionBoxGetChild(info->dialog, 
  5891.                        XmDIALOG_CANCEL_BUTTON));
  5892.  
  5893.     MMDesc commands_menu[] =
  5894.     {
  5895.     { "record", MMPush,
  5896.       { RecordBreakpointCommandsCB, XtPointer(info) }, 
  5897.       0, &info->record, 0, 0 },
  5898.     { "end",    MMPush | MMInsensitive,
  5899.       { EndBreakpointCommandsCB, XtPointer(info) }, 
  5900.       0, &info->end, 0, 0 },
  5901.     { "edit",   MMPush | MMInsensitive,
  5902.       { EditBreakpointCommandsCB, XtPointer(info) }, 
  5903.       0, &info->edit, 0, 0 },
  5904.     MMEnd
  5905.     };
  5906.  
  5907.     MMDesc enabled_menu[] = 
  5908.     {
  5909.     { "lookup",    MMPush,
  5910.       { LookupBreakpointCB,    XtPointer(info) }, 0, &info->lookup, 0, 0 },
  5911.     { "print",     MMPush,
  5912.       { PrintWatchpointCB,     XtPointer(info) }, 0, &info->print, 0, 0 },
  5913.     { "enable",    MMPush,
  5914.       { EnableBreakpointsCB,   XtPointer(info) }, 0, &info->enable, 0, 0 },
  5915.     { "disable",   MMPush,
  5916.       { DisableBreakpointsCB,  XtPointer(info) }, 0, &info->disable, 0, 0},
  5917.     { "temporary", MMPush,
  5918.       { MakeBreakpointsTempCB, XtPointer(info) }, 0, &info->temp, 0, 0 },
  5919.     { "delete",    MMPush | MMHelp,
  5920.       { DeleteBreakpointsCB,   XtPointer(info) }, 0, &info->del, 0, 0 },
  5921.     MMEnd
  5922.     };
  5923.  
  5924.     if (app_data.flat_dialog_buttons)
  5925.     {
  5926.     for (MMDesc *item = enabled_menu; item != 0 && item->name != 0; item++)
  5927.     {
  5928.         if ((item->type & MMTypeMask) == MMPush)
  5929.         item->type = (MMFlatPush | (item->type & ~MMTypeMask));
  5930.     }
  5931.     }
  5932.  
  5933.     MMDesc panel_menu[] = 
  5934.     {
  5935.     { "title", MMButtonPanel, MMNoCB, enabled_menu, 0, 0, 0 },
  5936.     { "condition", MMComboBox,
  5937.       { SetBreakpointConditionCB, XtPointer(info) }, 
  5938.       0, &info->condition, 0, 0 },
  5939.     { "ignore", MMSpinBox,
  5940.       { SetBreakpointIgnoreCountCB, XtPointer(info) }, 
  5941.       0, &info->ignore, 0, 0 },
  5942.     { "commands", MMButtonPanel, MMNoCB, commands_menu, 0, 0, 0 },
  5943.     MMEnd
  5944.     };
  5945.  
  5946.     arg = 0;
  5947.     XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
  5948.     Widget form = XmCreateRowColumn(info->dialog, "form", args, arg);
  5949.     XtManageChild(form);
  5950.  
  5951.     Widget panel = MMcreatePanel(form, "panel", panel_menu);
  5952.  
  5953.     XtVaSetValues(panel,
  5954.           XmNmarginWidth,    0,
  5955.           XmNmarginHeight,   0,
  5956.           NULL);
  5957.  
  5958.     Widget buttons = XtParent(info->lookup);
  5959.     XtVaSetValues(buttons,
  5960.           XmNmarginWidth,     0, 
  5961.           XmNmarginHeight,    0, 
  5962.           XmNborderWidth,     0,
  5963.           XmNshadowThickness, 0, 
  5964.           XmNspacing,         0,
  5965.           NULL);
  5966.  
  5967.     arg = 0;
  5968.     XtSetArg(args[arg], XmNeditMode, XmMULTI_LINE_EDIT); arg++;
  5969.     info->editor = XmCreateScrolledText(form, "text", args, arg);
  5970.     XtUnmanageChild(XtParent(info->editor));
  5971.     XtManageChild(info->editor);
  5972.  
  5973.     info->title = panel_menu[0].label;
  5974.     MMaddCallbacks(panel_menu, XtPointer(info));
  5975.  
  5976.     update_properties_panel(info);
  5977.     InstallButtonTips(panel);
  5978.  
  5979.     MMadjustPanel(panel_menu);
  5980.  
  5981.     XtAddCallback(info->dialog, XmNokCallback,
  5982.           UnmanageThisCB, info->dialog);
  5983.     XtAddCallback(info->dialog, XmNhelpCallback,    
  5984.           ImmediateHelpCB, NULL);
  5985.     XtAddCallback(info->dialog, XmNunmapCallback,
  5986.           DestroyThisCB, XtParent(info->dialog));
  5987.     XtAddCallback(info->dialog, XmNdestroyCallback,
  5988.           DeleteInfoCB,  XtPointer(info));
  5989.  
  5990.     tie_combo_box_to_history(info->condition, cond_filter);
  5991.  
  5992.     manage_and_raise(info->dialog);
  5993.     info->spin_locked = false;
  5994. }
  5995.  
  5996. // Set breakpoint condition
  5997. void SourceView::SetBreakpointConditionCB(Widget w,
  5998.                       XtPointer client_data, 
  5999.                       XtPointer call_data)
  6000. {
  6001.     XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)call_data;
  6002.     switch (cbs->reason)
  6003.     {
  6004.     case XmCR_ACTIVATE:        // Pressed `RETURN'
  6005.     case XmCR_SINGLE_SELECT:    // Selection from ComboBox
  6006.     case XmCR_MULTIPLE_SELECT:
  6007.     case XmCR_EXTENDED_SELECT:
  6008.     case XmCR_BROWSE_SELECT:
  6009.     break;
  6010.  
  6011.     default:
  6012.     return;            // Value changed
  6013.     }
  6014.  
  6015.     BreakpointPropertiesInfo *info = 
  6016.     (BreakpointPropertiesInfo *)client_data;
  6017.  
  6018.     String cond = XmTextFieldGetString(info->condition);
  6019.     set_bps_cond(info->nrs, cond, w);
  6020.     XtFree(cond);
  6021. }
  6022.  
  6023. // Set breakpoint ignore count
  6024. void SourceView::SetBreakpointIgnoreCountCB(Widget w,
  6025.                         XtPointer client_data, 
  6026.                         XtPointer call_data)
  6027. {
  6028.     XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)call_data;
  6029.     BreakpointPropertiesInfo *info = 
  6030.     (BreakpointPropertiesInfo *)client_data;
  6031.  
  6032.     if (info->spin_locked)
  6033.     return;            // Ignore the SetValue change
  6034.  
  6035.     int delay = 500;        // Wait until the SpinBox stops spinning
  6036.     if (cbs->reason == XmCR_ACTIVATE)
  6037.     delay = 0;
  6038.  
  6039.     if (info->timer != 0)
  6040.     {
  6041.     XtRemoveTimeOut(info->timer);
  6042.     info->timer = 0;
  6043.     }
  6044.  
  6045.     info->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
  6046.                   delay,
  6047.                   SetBreakpointIgnoreCountNowCB, 
  6048.                   client_data);
  6049. }
  6050.  
  6051. void SourceView::SetBreakpointIgnoreCountNowCB(XtPointer client_data, 
  6052.                            XtIntervalId *id)
  6053. {
  6054.     BreakpointPropertiesInfo *info = 
  6055.     (BreakpointPropertiesInfo *)client_data;
  6056.  
  6057.     assert (info->timer == *id);
  6058.     (void) id;            // Use it
  6059.     info->timer = 0;
  6060.  
  6061.     String _count = XmTextFieldGetString(info->ignore);
  6062.     int count = atoi(_count);
  6063.     XtFree(_count);
  6064.  
  6065.     for (int i = 0; i < info->nrs.size(); i++)
  6066.     {
  6067.     gdb_command(gdb->ignore_command(itostring(info->nrs[i]), count));
  6068.     info->ignore_spin_update++;
  6069.     }
  6070. }
  6071.  
  6072.  
  6073. // Make breakpoint temporary
  6074. void SourceView::MakeBreakpointsTempCB(Widget, XtPointer client_data, 
  6075.                        XtPointer)
  6076. {
  6077.     BreakpointPropertiesInfo *info = 
  6078.     (BreakpointPropertiesInfo *)client_data;
  6079.  
  6080.     gdb_command("enable delete " + numbers(info->nrs));
  6081. }
  6082.  
  6083.  
  6084. // Delete Breakpoint
  6085. void SourceView::DeleteBreakpointsCB(Widget, XtPointer client_data, XtPointer)
  6086. {
  6087.     BreakpointPropertiesInfo *info = 
  6088.     (BreakpointPropertiesInfo *)client_data;
  6089.  
  6090.     delete_bps(info->nrs);
  6091. }
  6092.  
  6093. // Enable Breakpoints
  6094. void SourceView::EnableBreakpointsCB(Widget, XtPointer client_data, XtPointer)
  6095. {
  6096.     BreakpointPropertiesInfo *info = 
  6097.     (BreakpointPropertiesInfo *)client_data;
  6098.  
  6099.     enable_bps(info->nrs);
  6100. }
  6101.  
  6102. // Disable Breakpoints
  6103. void SourceView::DisableBreakpointsCB(Widget, XtPointer client_data, XtPointer)
  6104. {
  6105.     BreakpointPropertiesInfo *info = 
  6106.     (BreakpointPropertiesInfo *)client_data;
  6107.  
  6108.     disable_bps(info->nrs);
  6109. }
  6110.  
  6111. // Record breakpoint commands
  6112. void SourceView::RecordBreakpointCommandsCB(Widget w,
  6113.                         XtPointer client_data, 
  6114.                         XtPointer)
  6115. {
  6116.     BreakpointPropertiesInfo *info = 
  6117.     (BreakpointPropertiesInfo *)client_data;
  6118.  
  6119.     gdb->removeHandler(Recording, RecordingHP, (void *)info);
  6120.     gdb->addHandler(Recording, RecordingHP, (void *)info);
  6121.     gdb_command("commands " + itostring(info->nrs[0]), w);
  6122. }
  6123.  
  6124. // End recording breakpoint commands
  6125. void SourceView::EndBreakpointCommandsCB(Widget w, XtPointer, XtPointer)
  6126. {
  6127.     gdb_command("end", w);
  6128. }
  6129.  
  6130. void SourceView::RefreshBreakpointsHP(Agent *, void *, void *call_data)
  6131. {
  6132.     bool gdb_ready = bool(call_data);
  6133.     if (gdb_ready && !gdb->recording())
  6134.     {
  6135.     // Don't get called recursively
  6136.     gdb->removeHandler(ReadyForQuestion, RefreshBreakpointsHP);
  6137.  
  6138.     string breakpoints = gdb_question("info breakpoints");
  6139.     if (breakpoints == NO_GDB_ANSWER)
  6140.     {
  6141.         // Try again next time
  6142.         gdb->addHandler(ReadyForQuestion, RefreshBreakpointsHP);
  6143.     }
  6144.     else
  6145.     {
  6146.         process_info_bp(breakpoints);
  6147.     }
  6148.     }
  6149. }
  6150.  
  6151. // Log recording state
  6152. void SourceView::RecordingHP(Agent *, void *client_data, void *call_data)
  6153. {
  6154.     BreakpointPropertiesInfo *info = 
  6155.     (BreakpointPropertiesInfo *)client_data;
  6156.     bool recording = bool(call_data);
  6157.  
  6158.     // Refresh buttons
  6159.     if (info == 0)
  6160.     update_properties_panels();
  6161.     else
  6162.     update_properties_panel(info);
  6163.  
  6164.     if (!recording)
  6165.     {
  6166.     // Recording is over.  Don't get called again.
  6167.     gdb->removeHandler(Recording, RecordingHP, (void *)info);
  6168.  
  6169.     // Update breakpoints
  6170.     gdb->addHandler(ReadyForQuestion, RefreshBreakpointsHP);
  6171.  
  6172.     if (info != 0)
  6173.     {
  6174.         // Upon next panel update, propagate command to other breakpoints
  6175.         info->sync_commands = true;
  6176.     }
  6177.     }
  6178. }
  6179.  
  6180. // Set breakpoint commands
  6181. void SourceView::set_bp_commands(IntArray& nrs, const StringArray& commands,
  6182.                  Widget origin)
  6183. {
  6184.     for (int i = 0; i < nrs.size(); i++)
  6185.     {
  6186.     // Check for breakpoint
  6187.     MapRef ref;
  6188.     BreakPoint *bp = 0;
  6189.     for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  6190.     {
  6191.         if (bp->number() == nrs[i])
  6192.         break;
  6193.     }
  6194.     if (bp == 0)
  6195.         continue;        // No such breakpoint
  6196.  
  6197.     if (commands.size() == bp->commands().size())
  6198.     {
  6199.         bool same_commands = true;
  6200.         for (int j = 0; same_commands && j < bp->commands().size(); j++)
  6201.         {
  6202.         string c1 = bp->commands()[j];
  6203.         strip_auto_command_prefix(c1);
  6204.         string c2 = commands[j];
  6205.         strip_auto_command_prefix(c2);
  6206.  
  6207.         if (c1 != c2)
  6208.             same_commands = false;
  6209.         }
  6210.  
  6211.         if (same_commands)
  6212.         continue;    // Commands unchanged
  6213.     }
  6214.  
  6215.     gdb_command("commands " + itostring(nrs[i]), origin);
  6216.     for (int j = 0; j < commands.size(); j++)
  6217.         gdb_command(commands[j], origin);
  6218.     gdb_command("end", origin);
  6219.     }
  6220. }
  6221.  
  6222.  
  6223. // Edit breakpoint commands
  6224. void SourceView::EditBreakpointCommandsCB(Widget w,
  6225.                       XtPointer client_data, 
  6226.                       XtPointer)
  6227. {
  6228.     BreakpointPropertiesInfo *info = 
  6229.     (BreakpointPropertiesInfo *)client_data;
  6230.  
  6231.     if (XtIsManaged(XtParent(info->editor)))
  6232.     {
  6233.     XtUnmanageChild(XtParent(info->editor));
  6234.     MString label = "Edit " + MString(">>", "small");
  6235.     set_label(info->edit, label);
  6236.  
  6237.     String _commands = XmTextGetString(info->editor);
  6238.     string cmd = _commands;
  6239.     XtFree(_commands);
  6240.  
  6241.     if (!cmd.contains('\n', -1))
  6242.         cmd += '\n';
  6243.     StringArray commands;
  6244.     while (cmd != "")
  6245.     {
  6246.         string c = cmd.before('\n');
  6247.         if (c != "")
  6248.         commands += c;
  6249.         cmd = cmd.after('\n');
  6250.     }
  6251.  
  6252.     set_bp_commands(info->nrs, commands, w);
  6253.  
  6254.     // Update all panels in the next run
  6255.     gdb->addHandler(Recording, RecordingHP, (void *)0);
  6256.     }
  6257.     else
  6258.     {
  6259.     XtManageChild(XtParent(info->editor));
  6260.     MString label = "Edit " + MString("<<", "small");
  6261.     set_label(info->edit, label);
  6262.     }
  6263. }
  6264.  
  6265. void SourceView::edit_breakpoint_properties(int bp_nr)
  6266. {
  6267.     static int n;
  6268.     n = bp_nr;
  6269.     EditBreakpointPropertiesCB(source_text_w, XtPointer(&n), 0);
  6270. }
  6271.  
  6272.  
  6273.  
  6274.  
  6275.  
  6276. //-----------------------------------------------------------------------------
  6277. // Breakpoint commands
  6278. //----------------------------------------------------------------------------
  6279.  
  6280. void SourceView::BreakpointCmdCB(Widget,
  6281.                  XtPointer client_data,
  6282.                  XtPointer)
  6283. {
  6284.     if (breakpoint_list_w == 0)
  6285.     return;
  6286.  
  6287.     IntArray nrs;
  6288.     getBreakpointNumbers(nrs);
  6289.  
  6290.     if (nrs.size() == 0)
  6291.         return;
  6292.  
  6293.     string cmd = (String)client_data;
  6294.  
  6295.     if (cmd == "delete")
  6296.         delete_bps(nrs);
  6297.     else if (cmd == "enable")
  6298.     enable_bps(nrs);
  6299.     else if (cmd == "disable")
  6300.     disable_bps(nrs);
  6301. }
  6302.  
  6303. void SourceView::LookupBreakpointCB(Widget, XtPointer client_data, XtPointer)
  6304. {
  6305.     if (breakpoint_list_w == 0)
  6306.     return;
  6307.  
  6308.     IntArray breakpoint_nrs;
  6309.  
  6310.     if (client_data == 0)
  6311.     {
  6312.     getBreakpointNumbers(breakpoint_nrs);
  6313.     }
  6314.     else
  6315.     {
  6316.     BreakpointPropertiesInfo *info = 
  6317.         (BreakpointPropertiesInfo *)client_data;
  6318.     breakpoint_nrs = info->nrs;
  6319.     }
  6320.     if (breakpoint_nrs.size() == 0)
  6321.     return;
  6322.  
  6323.     BreakPoint *bp = bp_map.get(breakpoint_nrs[0]);
  6324.     if (bp == 0)
  6325.     return;
  6326.  
  6327.     switch (bp->type())
  6328.     {
  6329.     case BREAKPOINT:
  6330.     lookup("#" + itostring(breakpoint_nrs[0]));
  6331.     break;
  6332.  
  6333.     case WATCHPOINT:
  6334.     lookup(bp->expr());
  6335.     break;
  6336.     }
  6337. }
  6338.  
  6339. void SourceView::PrintWatchpointCB(Widget w, XtPointer client_data, XtPointer)
  6340. {
  6341.     if (breakpoint_list_w == 0)
  6342.     return;
  6343.  
  6344.     IntArray breakpoint_nrs;
  6345.  
  6346.     if (client_data == 0)
  6347.     {
  6348.     getBreakpointNumbers(breakpoint_nrs);
  6349.     }
  6350.     else
  6351.     {
  6352.     BreakpointPropertiesInfo *info = 
  6353.         (BreakpointPropertiesInfo *)client_data;
  6354.     breakpoint_nrs = info->nrs;
  6355.     }
  6356.     if (breakpoint_nrs.size() < 1)
  6357.     return;
  6358.  
  6359.     BreakPoint *bp = bp_map.get(breakpoint_nrs[0]);
  6360.     if (bp == 0)
  6361.     return;
  6362.  
  6363.     switch (bp->type())
  6364.     {
  6365.     case BREAKPOINT:
  6366.     // How should we print a breakpoint?  (FIXME)
  6367.     break;
  6368.  
  6369.     case WATCHPOINT:
  6370.     gdb_command(gdb->print_command(bp->expr(), false), w);
  6371.     break;
  6372.     }
  6373. }
  6374.  
  6375.  
  6376. // Return breakpoint of BP_INFO; 0 if new; -1 if none
  6377. int SourceView::breakpoint_number(const string& bp_info)
  6378.     string class_name = current_source_name();
  6379.     int line = 0;
  6380.  
  6381.     switch (gdb->type())
  6382.     {
  6383.     case JDB:
  6384.     {
  6385.         int colon = bp_info.index(':');
  6386.     if (colon < 0)
  6387.         return -1;        // No breakpoint
  6388.  
  6389.     class_name = bp_info.before(colon);
  6390.     line = get_positive_nr(bp_info.after(colon));
  6391.     break;
  6392.     }
  6393.     case PERL:
  6394.     {
  6395.     line = get_positive_nr(bp_info);
  6396.     break;
  6397.     }
  6398.  
  6399.     default:
  6400.     return -1;            // Never reached
  6401.     }
  6402.  
  6403.     strip_leading_space(class_name);
  6404.     if (line <= 0 || class_name.contains(' '))
  6405.     return -1;        // No breakpoint
  6406.  
  6407.     MapRef ref;
  6408.     for (BreakPoint* bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  6409.     if (bp_matches(bp, class_name, line))
  6410.         return bp->number(); // Existing breakpoint
  6411.  
  6412.     return 0;               // New breakpoint
  6413. }
  6414.  
  6415.  
  6416. // Handle breakpoint info
  6417. void SourceView::process_breakpoints(string& info_breakpoints_output)
  6418. {
  6419.     if (breakpoint_list_w == 0)
  6420.     return;
  6421.  
  6422.     strip_space(info_breakpoints_output);
  6423.     if (info_breakpoints_output == "")
  6424.     {
  6425.     if (gdb->has_watch_command())
  6426.         info_breakpoints_output = "No breakpoints or watchpoints.";
  6427.     else
  6428.         info_breakpoints_output = "No breakpoints.";
  6429.     }
  6430.  
  6431.     int count = info_breakpoints_output.freq('\n') + 1;
  6432.  
  6433.     string *breakpoint_list = new string[count];
  6434.     bool *selected          = new bool[count];
  6435.  
  6436.     split(info_breakpoints_output, breakpoint_list, count, '\n');
  6437.  
  6438.     while (count > 0 && breakpoint_list[count - 1] == "")
  6439.     count--;
  6440.  
  6441.     bool select = false;
  6442.     for (int i = 0; i < count; i++)
  6443.     {
  6444.     string& bp_info = breakpoint_list[i];
  6445.     if (!gdb->has_numbered_breakpoints())
  6446.     {
  6447.         // JDB has no breakpoint numbers -- insert our own
  6448.         int bp_nr = breakpoint_number(bp_info);
  6449.         if (bp_nr > 0)
  6450.         {
  6451.         string s = itostring(bp_nr) + "    ";
  6452.         bp_info.prepend(s.at(0, 4));
  6453.         }
  6454.     }
  6455.  
  6456.     // Select number
  6457.     int bp_number = get_positive_nr(bp_info);
  6458.     if (bp_number > 0)
  6459.     {
  6460.         MapRef ref;
  6461.         for (BreakPoint* bp = bp_map.first(ref);
  6462.          bp != 0;
  6463.          bp = bp_map.next(ref))
  6464.         {
  6465.         if (bp->number() == bp_number)
  6466.         {
  6467.             select = bp->selected();
  6468.             break;
  6469.         }
  6470.         }
  6471.     }
  6472.  
  6473.     selected[i] = select;
  6474.     strip_auto_command_prefix(bp_info);
  6475.     setup_where_line(bp_info);
  6476.     }
  6477.  
  6478.     setLabelList(breakpoint_list_w, breakpoint_list, selected, count,
  6479.          (gdb->type() == GDB || 
  6480.           gdb->type() == PYDB) && count > 1, false);
  6481.  
  6482.     UpdateBreakpointButtonsCB(breakpoint_list_w, XtPointer(0), XtPointer(0));
  6483.  
  6484.     delete[] breakpoint_list;
  6485.     delete[] selected;
  6486. }
  6487.  
  6488. void SourceView::UpdateBreakpointButtonsCB(Widget, XtPointer, 
  6489.                        XtPointer call_data)
  6490. {
  6491.     (void) call_data;        // Use it
  6492.  
  6493.     if (edit_breakpoints_dialog_w == 0)
  6494.     return;
  6495.  
  6496.     IntArray breakpoint_nrs;
  6497.     getBreakpointNumbers(breakpoint_nrs);
  6498.  
  6499.     // Update selection
  6500.     MapRef ref;
  6501.     BreakPoint *bp;
  6502.     for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  6503.     bp->selected() = false;
  6504.  
  6505.     for (int i = 0; i < breakpoint_nrs.size(); i++)
  6506.     {
  6507.     int bp_number = breakpoint_nrs[i];
  6508.     for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  6509.     {
  6510.         if (bp->number() == bp_number)
  6511.         {
  6512.         bp->selected() = true;
  6513.         break;
  6514.         }
  6515.     }
  6516.     }
  6517.  
  6518. #if 0
  6519.     if (call_data != 0)
  6520.     {
  6521.     // Update status line
  6522.     if (breakpoint_nrs.size() == 1)
  6523.         set_status_mstring(help_on_bp(breakpoint_nrs[0], true));
  6524.     else
  6525.         set_status("");
  6526.     }
  6527. #endif
  6528.  
  6529.     // Count selected ones
  6530.     BreakPoint *selected_bp = 0;
  6531.     int selected          = 0;
  6532.     int selected_enabled  = 0;
  6533.     int selected_disabled = 0;
  6534.     for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  6535.     {
  6536.     if (bp->selected())
  6537.     {
  6538.         selected_bp = bp;
  6539.         selected++;
  6540.  
  6541.         if (bp->enabled())
  6542.         selected_enabled++;
  6543.         else
  6544.         selected_disabled++;
  6545.     }
  6546.     }
  6547.  
  6548.     // Update buttons
  6549.     set_sensitive(bp_area[BPButtons::NewWP].widget, gdb->has_watch_command());
  6550.     set_sensitive(bp_area[BPButtons::Lookup].widget, selected == 1);
  6551.     set_sensitive(bp_area[BPButtons::Print].widget, 
  6552.            selected == 1 && selected_bp->type() == WATCHPOINT);
  6553.     set_sensitive(bp_area[BPButtons::Enable].widget,
  6554.            gdb->can_enable() && selected_disabled > 0);
  6555.     set_sensitive(bp_area[BPButtons::Disable].widget,
  6556.            gdb->can_disable() && selected_enabled > 0);
  6557.     set_sensitive(bp_area[BPButtons::Properties].widget, selected > 0);
  6558.     set_sensitive(bp_area[BPButtons::Delete].widget, selected > 0);
  6559. }
  6560.  
  6561. void SourceView::EditBreakpointsCB(Widget, XtPointer, XtPointer)
  6562. {
  6563.     manage_and_raise(edit_breakpoints_dialog_w);
  6564. }
  6565.  
  6566.  
  6567.  
  6568. //-----------------------------------------------------------------------------
  6569. // Stack frame selection
  6570. //----------------------------------------------------------------------------
  6571.  
  6572. void SourceView::StackDialogPoppedDownCB (Widget, XtPointer, XtPointer)
  6573. {
  6574.     stack_dialog_popped_up = false;
  6575.     refresh_buttons();
  6576. }
  6577.  
  6578. void SourceView::SelectFrameCB (Widget w, XtPointer, XtPointer call_data)
  6579. {
  6580.     XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
  6581.  
  6582.     int count = 0;
  6583.     XtVaGetValues(w,
  6584.           XmNitemCount, &count,
  6585.           NULL);
  6586.  
  6587.     set_sensitive(up_w,   cbs->item_position > 1);
  6588.     set_sensitive(down_w, cbs->item_position < count);
  6589.     refresh_buttons();
  6590.  
  6591.     switch (gdb->type())
  6592.     {
  6593.     case GDB:
  6594.     // GDB frame output is caught by our routines.
  6595.     gdb_command(gdb->frame_command(count - cbs->item_position));
  6596.     break;
  6597.     
  6598.     case XDB:
  6599.     // XDB frame output is caught by our routines.
  6600.     gdb_command(gdb->frame_command(cbs->item_position - 1));
  6601.     break;
  6602.  
  6603.     case DBX:
  6604.     case JDB:
  6605.     case PYDB:
  6606.     case PERL:
  6607.     if (gdb->has_frame_command())
  6608.     {
  6609.         // Issue `frame' command
  6610.         gdb_command(gdb->frame_command(count - cbs->item_position + 1));
  6611.     }
  6612.     else
  6613.     {
  6614.         // JDB, PYDB and some DBXes lack a `frame' command.
  6615.         // Use `up N'/`down N' instead.
  6616.         int offset = cbs->item_position - last_frame_pos;
  6617.         if (offset != 0)
  6618.         gdb_command(gdb->relative_frame_command(-offset));
  6619.  
  6620.         // Call `set_frame_pos' now.
  6621.         frame_pos_locked = false;
  6622.         set_frame_pos(0, cbs->item_position);
  6623.  
  6624.         // Ignore the `up'/`down' reply.
  6625.         frame_pos_locked = (offset != 0);
  6626.     }
  6627.     break;
  6628.     }
  6629. }
  6630.  
  6631. void SourceView::refresh_stack_frames()
  6632. {
  6633.     // Allow unlimited time to find out where we are
  6634.     string where_s = gdb_question(gdb->where_command(), -1, true);
  6635.     if (where_s == NO_GDB_ANSWER)
  6636.     where_s = "No stack.";
  6637.     process_where(where_s);
  6638.  
  6639.     if (gdb->has_frame_command())
  6640.     {
  6641.     string frame = gdb_question(gdb->frame_command());
  6642.     process_frame(frame);
  6643.     }
  6644. }
  6645.  
  6646. void SourceView::ViewStackFramesCB(Widget, XtPointer, XtPointer)
  6647. {
  6648.     refresh_stack_frames();
  6649.     manage_and_raise(stack_dialog_w);
  6650.     stack_dialog_popped_up = true;
  6651.     refresh_buttons();
  6652. }
  6653.  
  6654. // Remove file paths and argument lists from `where' output
  6655. void SourceView::setup_where_line(string& line)
  6656. {
  6657.     if (gdb->type() != JDB)
  6658.     {
  6659.     // Remove file paths (otherwise line can be too long for DBX)
  6660.     //   ... n.b. with templates, line can still be rather long
  6661. #if RUNTIME_REGEX
  6662.     static regex rxfilepath("[^\"\'` /]*/");
  6663. #endif
  6664.     line.gsub(rxfilepath, "");
  6665.     }
  6666.  
  6667.     if (gdb->type() != JDB)
  6668.     {
  6669.     // Shorten argument lists `(a = 1, b = 2, ...)' to `()'
  6670. #if RUNTIME_REGEX
  6671.     static regex rxarglist("[(][^0-9][^)]*[)]");
  6672. #endif
  6673.     int start = index(line, rxarglist, "(");
  6674.     if (start > 0)
  6675.     {
  6676.         int end = line.index(')', -1);
  6677.         if (end > start)
  6678.         line = line.through(start) + line.from(end);
  6679.     }
  6680.     }
  6681.  
  6682.     const int min_width = 40;
  6683.     if (int(line.length()) < min_width)
  6684.     line += replicate(' ', min_width - line.length());
  6685. }
  6686.  
  6687. // Return current JDB frame; 0 if none
  6688. inline int jdb_frame()
  6689. {
  6690.     return get_positive_nr(gdb->prompt().from("["));
  6691. }
  6692.  
  6693. // Return current JDB thread; "" if none
  6694. inline string jdb_thread()
  6695. {
  6696.     return gdb->prompt().before("[");
  6697. }
  6698.  
  6699. // Process `where' output
  6700. void SourceView::process_where(string& where_output)
  6701. {
  6702.     if (!where_output.contains("No ", 0))
  6703.     undo_buffer.add_where(where_output);
  6704.  
  6705.     int count          = where_output.freq('\n') + 1;
  6706.     string *frame_list = new string[count];
  6707.     bool *selected     = new bool[count];
  6708.  
  6709.     split(where_output, frame_list, count, '\n');
  6710.  
  6711.     StringArray frames;
  6712.     int i;
  6713.     for (i = 0; i < count; i++)
  6714.     {
  6715.     const string& frame = frame_list[i];
  6716.     if (frame == "")
  6717.         continue;        // Skip empty lines
  6718.     if (frame.contains("Reading ", 0))
  6719.         continue;        // Skip GDB `Reading in symbols' messages
  6720.  
  6721.     frames += frame;
  6722.     }
  6723.     delete[] frame_list;
  6724.     frame_list = frames;
  6725.     count = frames.size();
  6726.  
  6727.     if (gdb->type() != XDB)
  6728.     {
  6729.     // Invert list such that `Up' and `Down' make sense
  6730.     for (i = 0; i < count / 2; i++)
  6731.     {
  6732.         string tmp = frame_list[i];
  6733.         frame_list[i] = frame_list[count - i - 1];
  6734.         frame_list[count - i - 1] = tmp;
  6735.     }
  6736.     }
  6737.  
  6738.     // Make sure we have a minimum width
  6739.     for (i = 0; i < count; i++)
  6740.     {
  6741.     selected[i] = false;
  6742.     setup_where_line(frame_list[i]);
  6743.     }
  6744.  
  6745.     // JDB does not report frames above the current one.  Hence, we
  6746.     // only update the reported frames and others unchanged.
  6747.     if (gdb->type() == JDB && jdb_frame() != 1)
  6748.     updateLabelList(frame_list_w, frame_list, count);
  6749.     else
  6750.     setLabelList(frame_list_w, frame_list, selected, count, false, false);
  6751.     set_frame_pos(0, 0);
  6752.  
  6753.     delete[] selected;
  6754. }
  6755.  
  6756. // Give a hint whether we're showing earlier state
  6757. void SourceView::showing_earlier_state(bool set)
  6758. {
  6759. #if 0
  6760.     set_sensitive(stack_dialog_w, !set);
  6761.     set_sensitive(thread_dialog_w, !set);
  6762.     set_sensitive(register_dialog_w, !set);
  6763. #endif
  6764.  
  6765.     static bool up_state;
  6766.     static bool down_state;
  6767.  
  6768.     if (set)
  6769.     {
  6770.     up_state   = XtIsSensitive(up_w);
  6771.     down_state = XtIsSensitive(down_w);
  6772.  
  6773.     set_sensitive(up_w, False);
  6774.     set_sensitive(down_w, False);
  6775.     }
  6776.     else
  6777.     {
  6778.     set_sensitive(up_w, up_state);
  6779.     set_sensitive(down_w, down_state);
  6780.     }
  6781.     set_sensitive(all_registers_w, !set);
  6782.     set_sensitive(int_registers_w, !set);
  6783.  
  6784.     refresh_buttons();
  6785.     update_glyphs();
  6786. }
  6787.  
  6788. // Process `frame' (or `up'/`down') output
  6789. void SourceView::process_frame(string& frame_output)
  6790. {
  6791.     if (frame_output != "" 
  6792.     && (frame_output[0] == '#' || gdb->type() != GDB))
  6793.     {
  6794.     string frame_nr;
  6795.  
  6796.     switch (gdb->type())
  6797.     {
  6798.     case GDB:
  6799.     case PYDB:
  6800.         frame_nr = frame_output.after(0);
  6801.         break;
  6802.  
  6803.     case DBX:
  6804.         frame_nr = frame_output;
  6805.  
  6806.         // Sun DBX 4.0 issues `=>' before current frame
  6807.         if (frame_nr.contains("=>", 0))
  6808.         frame_nr = frame_nr.after("=>");
  6809.         break;
  6810.  
  6811.     case XDB:
  6812.         frame_nr = frame_output.after(" = ", -1);
  6813.         break;
  6814.  
  6815.     case JDB:
  6816.         frame_nr = frame_output.after("[");
  6817.         break;
  6818.  
  6819.     case PERL:
  6820.     {
  6821.         // FIXME
  6822.         break;
  6823.     }
  6824.     }
  6825.  
  6826.     int frame = get_positive_nr(frame_nr);
  6827.  
  6828.     // In GDB, the lowest frame is #0, in DBX and JDB, it is #1
  6829.     if (gdb->type() == DBX || gdb->type() == JDB)
  6830.         frame--;
  6831.  
  6832.     process_frame(frame);
  6833.     }
  6834.     else
  6835.     {
  6836.     process_frame(-1);    // No frame
  6837.     }
  6838. }
  6839.  
  6840. // Same, but accept a frame number
  6841. void SourceView::process_frame(int frame)
  6842. {
  6843.     if (frame >= 0)
  6844.     {
  6845.     at_lowest_frame = (frame == 0);
  6846.  
  6847.     if (current_frame < 0)
  6848.     {
  6849.         // We have not seen a `frame' output yet - assume we're at
  6850.         // the lowest frame.
  6851.         current_frame = 0;
  6852.     }
  6853.  
  6854.     // Save undoing command
  6855.     string c;
  6856.     if (gdb->has_frame_command())
  6857.         c = gdb->frame_command(current_frame);
  6858.     else
  6859.         c = gdb->relative_frame_command(current_frame - frame);
  6860.     undo_buffer.add_command(c, true);
  6861.  
  6862.     // Save state
  6863.     undo_buffer.add_frame(itostring(frame));
  6864.  
  6865.     int count         = 0;
  6866.     int top_item      = 0;
  6867.     int visible_items = 0;
  6868.     XtVaGetValues(frame_list_w,
  6869.               XmNitemCount, &count,
  6870.               XmNtopItemPosition, &top_item,
  6871.               XmNvisibleItemCount, &visible_items,
  6872.               NULL);
  6873.  
  6874.     int pos = 1;
  6875.     switch (gdb->type())
  6876.     {
  6877.     case GDB:
  6878.     case DBX:
  6879.     case JDB:
  6880.     case PYDB:
  6881.     case PERL:
  6882.         pos = count - frame;
  6883.         break;
  6884.  
  6885.     case XDB:
  6886.         pos = frame + 1;
  6887.         break;
  6888.     }
  6889.  
  6890.     ListSetAndSelectPos(frame_list_w, pos);
  6891.  
  6892.     set_sensitive(up_w,   pos > 1);
  6893.     set_sensitive(down_w, pos < count);
  6894.     refresh_buttons();
  6895.  
  6896.     update_glyphs();
  6897.     current_frame = frame;
  6898.     }
  6899.     else
  6900.     {
  6901.     set_sensitive(up_w,   False);
  6902.     set_sensitive(down_w, False);
  6903.     refresh_buttons();
  6904.     current_frame = -1;
  6905.     }
  6906. }
  6907.  
  6908. // Set frame manually to function FUNC; return TRUE if successful
  6909. bool SourceView::set_frame_func(const string& func)
  6910. {
  6911.     int count = 0;
  6912.     XmStringTable items;
  6913.  
  6914.     XtVaGetValues(frame_list_w,
  6915.           XmNitemCount, &count,
  6916.           XmNitems, &items,
  6917.           NULL);
  6918.  
  6919.     for (int i = count - 1; i >= 0; i--)
  6920.     {
  6921.     String _item;
  6922.     XmStringGetLtoR(items[i], LIST_CHARSET, &_item);
  6923.     string item(_item);
  6924.     XtFree(_item);
  6925.  
  6926.     int func_index  = item.index(func);
  6927.     int paren_index = item.index('(');
  6928.  
  6929.     if (func_index >= 0 &&
  6930.         (func_index < paren_index || paren_index < 0))
  6931.     {
  6932.         set_frame_pos(0, i + 1);
  6933.         return true;
  6934.     }
  6935.     }
  6936.  
  6937.     return false;
  6938. }
  6939.  
  6940. // Set frame manually: ARG = 0: POS, ARG = +/- N: down/up N levels
  6941. void SourceView::set_frame_pos(int arg, int pos)
  6942. {
  6943.     if (frame_pos_locked)
  6944.     {
  6945.     frame_pos_locked = false;
  6946.     return;
  6947.     }
  6948.  
  6949.     int items = 0;
  6950.     XtVaGetValues(frame_list_w, XmNitemCount, &items, NULL);
  6951.  
  6952.     if (pos == 0)
  6953.     pos = items;
  6954.     if (arg != 0)
  6955.     {
  6956.     int *position_list;
  6957.     int position_count;
  6958.     if (XmListGetSelectedPos(frame_list_w,
  6959.                  &position_list, &position_count))
  6960.     {
  6961.         if (position_count == 1)
  6962.         pos = position_list[0] + arg;
  6963.         XtFree((char *)position_list);
  6964.     } else
  6965.         return;
  6966.     if (position_count != 1 || pos < 1 || pos > items)
  6967.         return;
  6968.     }
  6969.  
  6970.     ListSetAndSelectPos(frame_list_w, pos);
  6971.  
  6972.     last_frame_pos = pos;
  6973.  
  6974.     set_sensitive(up_w,   pos > 1);
  6975.     set_sensitive(down_w, pos < items);
  6976.     refresh_buttons();
  6977. }
  6978.  
  6979. bool SourceView::where_required()    { return stack_dialog_popped_up; }
  6980. bool SourceView::register_required() { return register_dialog_popped_up; }
  6981. bool SourceView::thread_required()   { return thread_dialog_popped_up; }
  6982.  
  6983. bool SourceView::can_go_up()
  6984. {
  6985.     return gdb->relative_frame_command(1) != "" && 
  6986.     (!where_required() || XtIsSensitive(up_w));
  6987. }
  6988.  
  6989. bool SourceView::can_go_down()
  6990. {
  6991.     return gdb->relative_frame_command(-1) != "" && 
  6992.     (!where_required() || XtIsSensitive(down_w));
  6993. }
  6994.  
  6995.  
  6996. //-----------------------------------------------------------------------------
  6997. // Register stuff
  6998. //----------------------------------------------------------------------------
  6999.  
  7000. void SourceView::process_registers(string& register_output)
  7001. {
  7002.     register_output.gsub(";\n", "\n");
  7003.     register_output.gsub("; ", "\n");
  7004.     register_output.gsub(";", "\n");
  7005.     register_output.gsub("\n\n", "\n");
  7006.  
  7007.     if (!register_output.contains("No ", 0))
  7008.     undo_buffer.add_registers(register_output);
  7009.  
  7010.     int count             = register_output.freq('\n') + 1;
  7011.     string *register_list = new string[count];
  7012.     bool *selected        = new bool[count];
  7013.  
  7014.     split(register_output, register_list, count, '\n');
  7015.  
  7016.     while (count > 0 && register_list[count - 1] == "")
  7017.     count--;
  7018.  
  7019.     for (int i = 0; i < count; i++)
  7020.     {
  7021.     tabto(register_list[i], 26);
  7022.     untabify_if_needed(register_list[i]);
  7023.     selected[i] = false;
  7024.     }
  7025.  
  7026.     setLabelList(register_list_w, register_list, selected, count,
  7027.          false, false);
  7028.  
  7029.     delete[] register_list;
  7030.     delete[] selected;
  7031. }
  7032.  
  7033. void SourceView::refresh_registers()
  7034. {
  7035.     string registers = gdb_question(refresh_registers_command());
  7036.     if (registers == NO_GDB_ANSWER)
  7037.     registers = "No registers.";
  7038.     process_registers(registers);
  7039. }
  7040.  
  7041. string SourceView::refresh_registers_command()
  7042. {
  7043.     return gdb->regs_command(all_registers);
  7044. }
  7045.  
  7046. void SourceView::ViewRegistersCB(Widget, XtPointer, XtPointer)
  7047. {
  7048.     refresh_registers();
  7049.     manage_and_raise(register_dialog_w);
  7050.     register_dialog_popped_up = true;
  7051. }
  7052.  
  7053. void SourceView::RegisterDialogPoppedDownCB (Widget, XtPointer, XtPointer)
  7054. {
  7055.     register_dialog_popped_up = false;
  7056. }
  7057.  
  7058. void SourceView::SelectRegisterCB (Widget, XtPointer, XtPointer call_data)
  7059. {
  7060.     XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
  7061.  
  7062.     // Get the selected line
  7063.     String _item;
  7064.     XmStringGetLtoR(cbs->item, LIST_CHARSET, &_item);
  7065.     string item(_item);
  7066.     XtFree(_item);
  7067.  
  7068.     if (item != "" && item[item.length() - 1] != '.')
  7069.     {
  7070.     item = "/x $" + item.through(rxalphanum);
  7071.     source_arg->set_string(item);
  7072.     }
  7073. }
  7074.  
  7075. //-----------------------------------------------------------------------------
  7076. // Thread stuff
  7077. //----------------------------------------------------------------------------
  7078.  
  7079. string SourceView::current_threadgroup = "system";
  7080.  
  7081. void SourceView::process_threads(string& threads_output)
  7082. {
  7083.     bool valid_threads_output = true;
  7084.  
  7085.     if (threads_output == NO_GDB_ANSWER 
  7086.     || threads_output == ""
  7087.     || threads_output.contains("No ", 0)
  7088.     || threads_output.matches(rxwhite))
  7089.     {
  7090.     valid_threads_output = false;
  7091.     threads_output = "No threads.\n";
  7092.     }
  7093.  
  7094.     if (valid_threads_output)
  7095.     undo_buffer.add_threads(threads_output);
  7096.  
  7097.     int count           = threads_output.freq('\n') + 1;
  7098.     string *thread_list = new string[count];
  7099.     bool *selected      = new bool[count];
  7100.  
  7101.     split(threads_output, thread_list, count, '\n');
  7102.  
  7103.     while (count > 0 && thread_list[count - 1] == "")
  7104.     count--;
  7105.  
  7106.     switch (gdb->type())
  7107.     {
  7108.     case GDB:
  7109.     {
  7110.     for (int i = 0; i < count; i++)
  7111.     {
  7112.         selected[i] = thread_list[i].contains('*', 0);
  7113.         if (selected[i])
  7114.         thread_list[i] = thread_list[i].after(0);
  7115.         strip_leading_space(thread_list[i]);
  7116.         setup_where_line(thread_list[i]);
  7117.     }
  7118.     break;
  7119.     }
  7120.  
  7121.     case JDB:
  7122.     {
  7123.     string current_thread = jdb_thread();
  7124.     current_threadgroup = "";
  7125.     for (int i = 0; i < count; i++)
  7126.     {
  7127.         selected[i] = false;
  7128.         string& item = thread_list[i];
  7129.  
  7130.         if (item.contains("Group ", 0))
  7131.         {
  7132.         // Format is: `Group THREADGROUP:'
  7133.  
  7134.         if (current_threadgroup != "")
  7135.         {
  7136.             // Multiple threadgroups are shown
  7137.             current_threadgroup = "system";
  7138.         }
  7139.         else
  7140.         {
  7141.             current_threadgroup = item.after(" ");
  7142.             strip_leading_space(current_threadgroup);
  7143.             current_threadgroup = current_threadgroup.before(":");
  7144.         }
  7145.         }
  7146.         else
  7147.         {
  7148.         // Format is: ` NUMBER. (CLASS)ADDRESS NAME STATE'
  7149.  
  7150.         int addr_index = item.index("(");
  7151.         if (addr_index < 0)
  7152.             addr_index = item.index("0x");
  7153.  
  7154.         if (addr_index >= 0)
  7155.         {
  7156.             // If is the current thread, select it
  7157.             string thread = item.after("(");
  7158.             thread = thread.after(" ");
  7159.             strip_leading_space(thread);
  7160.  
  7161.             if (thread.contains(current_thread + " ", 0))
  7162.             selected[i] = true;
  7163.  
  7164.             // Leave only ` NUMBER. NAME STATE'
  7165.             int info_index = addr_index;
  7166.             while (info_index < int(item.length()) &&
  7167.                item[info_index] != ' ')
  7168.             info_index++;
  7169.             while (info_index < int(item.length()) &&
  7170.                item[info_index] == ' ')
  7171.             info_index++;
  7172.             item = item.before(addr_index) + item.from(info_index);
  7173.  
  7174.             // Give more verbose output on system threads
  7175.             if (item.contains("runni", -1))
  7176.             item += "ng";
  7177.             else if (item.contains("suspe", -1))
  7178.             item += "nded";
  7179.             else if (item.contains("cond.", -1))
  7180.             item += " waiting";
  7181.         }
  7182.         }
  7183.     }
  7184.     break;
  7185.     }
  7186.  
  7187.     case DBX:
  7188.     case XDB:
  7189.     case PYDB:
  7190.     case PERL:
  7191.     {
  7192.     for (int i = 0; i < count; i++)
  7193.         selected[i] = false;
  7194.     break;
  7195.     }
  7196.     }
  7197.  
  7198.     setLabelList(thread_list_w, thread_list, selected, count, false, false);
  7199.  
  7200.     delete[] thread_list;
  7201.     delete[] selected;
  7202. }
  7203.  
  7204. void SourceView::refresh_threads(bool all_threadgroups)
  7205. {
  7206.     switch (gdb->type())
  7207.     {
  7208.     case GDB:
  7209.     {
  7210.     string threads = gdb_question("info threads");
  7211.     process_threads(threads);
  7212.     break;
  7213.     }
  7214.     case JDB:
  7215.     {
  7216.     if (all_threadgroups)
  7217.     {
  7218.         // In JDB, `threadgroup system' seems to make `threads' list
  7219.         // the threads of *all* threadgroups, not only system threads.
  7220.         // This command will also automatically trigger an update.
  7221.         gdb_command("threadgroup system");
  7222.         syncCommandQueue();
  7223.     }
  7224.  
  7225.     string threads = gdb_question("threads");
  7226.     process_threads(threads);
  7227.     break;
  7228.     }
  7229.  
  7230.     case DBX:
  7231.     case XDB:
  7232.     case PYDB:
  7233.     case PERL:
  7234.     // No threads.
  7235.     break;
  7236.     }
  7237. }
  7238.  
  7239. void SourceView::ViewThreadsCB(Widget, XtPointer, XtPointer)
  7240. {
  7241.     refresh_threads(true);
  7242.     manage_and_raise(thread_dialog_w);
  7243.     thread_dialog_popped_up = true;
  7244. }
  7245.  
  7246. void SourceView::ThreadDialogPoppedDownCB(Widget, XtPointer, XtPointer)
  7247. {
  7248.     thread_dialog_popped_up = false;
  7249. }
  7250.  
  7251. void SourceView::ThreadCommandCB(Widget w, XtPointer client_data, XtPointer)
  7252. {
  7253.     string command = (char *)client_data;
  7254.  
  7255.     // Get the selected threads
  7256.     IntArray threads;
  7257.     getItemNumbers(thread_list_w, threads);
  7258.  
  7259.     for (int i = 0; i < threads.size(); i++)
  7260.     command += " " + itostring(threads[i]);
  7261.  
  7262.     gdb_command(command, w);
  7263. }
  7264.  
  7265. void SourceView::SelectThreadCB(Widget w, XtPointer, XtPointer)
  7266. {
  7267.     // Get the selected threads
  7268.     IntArray threads;
  7269.     getItemNumbers(thread_list_w, threads);
  7270.  
  7271.     if (threads.size() == 1)
  7272.     {
  7273.     // Make single thread the default thread.
  7274.     gdb_command("thread " + itostring(threads[0]), w);
  7275.     }
  7276.     else if (threads.size() == 0 && gdb->type() == JDB)
  7277.     {
  7278.     // Check if we have selected a threadgroup
  7279.     XmStringTable selected_items;
  7280.     int selected_items_count = 0;
  7281.  
  7282.     XtVaGetValues(thread_list_w,
  7283.               XmNselectedItemCount, &selected_items_count,
  7284.               XmNselectedItems, &selected_items,
  7285.               NULL);
  7286.  
  7287.     if (selected_items_count == 1)
  7288.     {
  7289.         String _item;
  7290.         XmStringGetLtoR(selected_items[0], LIST_CHARSET, &_item);
  7291.         string item(_item);
  7292.         XtFree(_item);
  7293.         
  7294.         // Output has the form `Group jtest.main:'
  7295.         if (item.contains("Group ", 0))
  7296.         {
  7297.         string threadgroup = item.after(" ");
  7298.         strip_leading_space(threadgroup);
  7299.         threadgroup = threadgroup.before(":");
  7300.  
  7301.         if (threadgroup == current_threadgroup)
  7302.             threadgroup = "system"; // show all threadgroups
  7303.  
  7304.         gdb_command("threadgroup " + threadgroup, w);
  7305.         }
  7306.     }
  7307.     }
  7308. }
  7309.  
  7310. //-----------------------------------------------------------------------------
  7311. // Get Line in GDB format
  7312. //----------------------------------------------------------------------------
  7313.  
  7314. string SourceView::get_line(string position)
  7315. {
  7316.     string file_name = current_file_name;
  7317.  
  7318.     if (position.contains(':'))
  7319.     {
  7320.     file_name = position.before(':');
  7321.     position  = position.after(':');
  7322.     }
  7323.     int line = get_positive_nr(position);
  7324.  
  7325.     // Sanity check: make sure the line # isn't too big
  7326.     line = min(line, line_count);
  7327.     if (line < 1)
  7328.     return "";
  7329.  
  7330.     if (!is_current_file(file_name))
  7331.     read_file(file_name, line);
  7332.     if (!is_current_file(file_name))
  7333.     return "";
  7334.  
  7335.     XmTextPosition start = pos_of_line(line) + indent_amount(source_text_w);
  7336.     XmTextPosition end   = current_source.index('\n', start);
  7337.     if (end < 0)
  7338.     end = current_source.length();
  7339.  
  7340.     string text = current_source.at(int(start), end - start);
  7341.     return itostring(line) + "\t" + text;
  7342. }
  7343.  
  7344.  
  7345. //----------------------------------------------------------------------------
  7346. // Glyph stuff
  7347. //----------------------------------------------------------------------------
  7348.  
  7349.  
  7350. // Whether to cache glyph images
  7351. bool SourceView::cache_glyph_images = true;
  7352.  
  7353. // Change number of glyphs
  7354. void SourceView::set_max_glyphs (int nmax)
  7355. {
  7356.     WidgetArray empty(nmax);
  7357.  
  7358.     for (int k = 0; k < 2; k++)
  7359.     {
  7360.     int i;
  7361.  
  7362.     // Destroy old widgets...
  7363.     for (i = 0; i < plain_stops[k].size(); i++)
  7364.     {
  7365.         if (plain_stops[k][i] != 0)
  7366.         XtDestroyWidget(plain_stops[k][i]);
  7367.     }
  7368.     for (i = 0; i < grey_stops[k].size(); i++)
  7369.     {
  7370.         if (grey_stops[k][i] != 0)
  7371.         XtDestroyWidget(grey_stops[k][i]);
  7372.     }
  7373.  
  7374.     for (i = 0; i < plain_conds[k].size(); i++)
  7375.     {
  7376.         if (plain_conds[k][i] != 0)
  7377.         XtDestroyWidget(plain_conds[k][i]);
  7378.     }
  7379.     for (i = 0; i < grey_conds[k].size(); i++)
  7380.     {
  7381.         if (grey_stops[k][i] != 0)
  7382.         XtDestroyWidget(grey_conds[k][i]);
  7383.     }
  7384.  
  7385.     for (i = 0; i < plain_temps[k].size(); i++)
  7386.     {
  7387.         if (plain_temps[k][i] != 0)
  7388.         XtDestroyWidget(plain_temps[k][i]);
  7389.     }
  7390.     for (i = 0; i < grey_temps[k].size(); i++)
  7391.     {
  7392.         if (grey_stops[k][i] != 0)
  7393.         XtDestroyWidget(grey_temps[k][i]);
  7394.     }
  7395.  
  7396.     // ...make array empty...
  7397.     plain_stops[k] = empty;
  7398.     grey_stops[k]  = empty;
  7399.  
  7400.     plain_conds[k] = empty;
  7401.     grey_conds[k]  = empty;
  7402.  
  7403.     plain_temps[k] = empty;
  7404.     grey_temps[k]  = empty;
  7405.  
  7406.     // ...and make room for new widgets.  The last one is a null pointer.
  7407.     for (i = 0; i < nmax + 1; i++)
  7408.     {
  7409.         plain_stops[k] += Widget(0);
  7410.         grey_stops[k]  += Widget(0);
  7411.  
  7412.         plain_conds[k] += Widget(0);
  7413.         grey_conds[k]  += Widget(0);
  7414.  
  7415.         plain_temps[k] += Widget(0);
  7416.         grey_temps[k]  += Widget(0);
  7417.     }
  7418.     }
  7419. }
  7420.  
  7421.  
  7422. // Glyph has been activated - catch the double click in Motif 1.x
  7423. void SourceView::ActivateGlyphCB(Widget glyph, XtPointer, XtPointer call_data)
  7424. {
  7425.     XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *)call_data;
  7426.     XEvent *e = cbs->event;
  7427.     if (e->type != ButtonRelease)
  7428.     return;
  7429.  
  7430.     String *params = { 0 };
  7431.     XtCallActionProc(glyph, "source-drop-glyph", e, params, 0);
  7432.  
  7433.     if (cbs->click_count > 1)
  7434.     XtCallActionProc(glyph, "source-double-click", e, params, 0);
  7435. }
  7436.  
  7437.  
  7438. // Create a pixmap from BITS suitable for the widget W
  7439. Pixmap SourceView::pixmap(Widget w, unsigned char *bits, int width, int height)
  7440. {
  7441.     Pixel foreground, background;
  7442.  
  7443.     XtVaGetValues(w,
  7444.           XmNforeground, &foreground,
  7445.           XmNbackground, &background,
  7446.           NULL);
  7447.  
  7448.     int depth = PlanesOfScreen(XtScreen(w));
  7449.     Pixmap pix = XCreatePixmapFromBitmapData(XtDisplay(w), XtWindow(w), 
  7450.                          (char *)bits, width, height, 
  7451.                          foreground, background, depth);
  7452.     return pix;
  7453. }
  7454.  
  7455.  
  7456. // Create glyph in FORM_W named NAME from given BITS
  7457. Widget SourceView::create_glyph(Widget form_w,
  7458.                 String name,
  7459.                 unsigned char *bits,
  7460.                 int width, int height)
  7461. {
  7462.     // Get background color from text
  7463.     Pixel background;
  7464.     Widget text_w;
  7465.     if (form_w == code_form_w)
  7466.     text_w = code_text_w;
  7467.     else
  7468.     text_w = source_text_w;
  7469.     XtVaGetValues(text_w, XmNbackground, &background, NULL);
  7470.  
  7471.     // Create push button
  7472.     Arg args[30];
  7473.     Cardinal arg = 0;
  7474.     XtSetArg(args[arg], XmNmappedWhenManaged,  False);         arg++;
  7475.     XtSetArg(args[arg], XmNtopAttachment,      XmATTACH_FORM); arg++;
  7476.     XtSetArg(args[arg], XmNleftAttachment,     XmATTACH_FORM); arg++;
  7477.     XtSetArg(args[arg], XmNrecomputeSize,      False);         arg++;
  7478.     XtSetArg(args[arg], XmNmarginBottom,       0);             arg++;
  7479.     XtSetArg(args[arg], XmNmarginTop,          0);             arg++;
  7480.     XtSetArg(args[arg], XmNmarginLeft,         0);             arg++;
  7481.     XtSetArg(args[arg], XmNmarginRight,        0);             arg++;
  7482.     XtSetArg(args[arg], XmNmarginWidth,        0);             arg++;
  7483.     XtSetArg(args[arg], XmNmarginHeight,       0);             arg++;
  7484.     XtSetArg(args[arg], XmNshadowThickness,    0);             arg++;
  7485.     XtSetArg(args[arg], XmNhighlightThickness, 0);             arg++;
  7486.     XtSetArg(args[arg], XmNborderWidth,        0);             arg++;
  7487.     XtSetArg(args[arg], XmNlabelType,  XmPIXMAP);              arg++;
  7488.     XtSetArg(args[arg], XmNmultiClick, XmMULTICLICK_KEEP);     arg++;
  7489.     XtSetArg(args[arg], XmNalignment, XmALIGNMENT_BEGINNING);  arg++;
  7490.     XtSetArg(args[arg], XmNuserData,           XtPointer(0));  arg++;
  7491.     XtSetArg(args[arg], XmNfillOnArm,          True);          arg++;
  7492.     XtSetArg(args[arg], XmNarmColor,           background);    arg++;
  7493.     XtSetArg(args[arg], XmNbackground,         background);    arg++;
  7494.     Widget w = verify(XmCreatePushButton(form_w, name, args, arg));
  7495.  
  7496.     if (XtIsRealized(form_w))
  7497.     XtRealizeWidget(w);
  7498.  
  7499.     XtManageChild(w);
  7500.  
  7501.     arg = 0;
  7502.     if (!cache_glyph_images)
  7503.     {
  7504.     Pixmap pix = pixmap(w, bits, width, height);
  7505.     XtSetArg(args[arg], XmNlabelPixmap, pix); arg++;
  7506.     }
  7507.     XtSetArg(args[arg], XmNwidth,  width);  arg++;
  7508.     XtSetArg(args[arg], XmNheight, height); arg++;
  7509.     XtSetValues(w, args, arg);
  7510.  
  7511.     XtAddCallback(w, XmNactivateCallback, ActivateGlyphCB, 0);
  7512.  
  7513.     InstallButtonTips(w);
  7514.     return w;
  7515. }
  7516.  
  7517. // Return height of a single line
  7518. int SourceView::line_height(Widget text_w)
  7519. {
  7520.     static int source_height = 0;
  7521.     static int code_height   = 0;
  7522.     if (text_w == source_text_w && source_height > 0)
  7523.     return source_height;
  7524.     else if (text_w == code_text_w && code_height > 0)
  7525.     return code_height;
  7526.  
  7527.     bool ok;
  7528.  
  7529.     XmTextPosition top = XmTextGetTopCharacter(text_w);
  7530.     Position top_x, top_y;
  7531.     ok = XmTextPosToXY(text_w, top, &top_x, &top_y);
  7532.     if (!ok)
  7533.     return 0;
  7534.  
  7535.     string& text = current_text(text_w);
  7536.     XmTextPosition second = text.index('\n', top) + 1;
  7537.     Position second_x, second_y;
  7538.     ok = XmTextPosToXY(text_w, second, &second_x, &second_y);
  7539.     if (!ok)
  7540.     return 0;
  7541.  
  7542.     int height = abs(second_y - top_y);
  7543.  
  7544.     if (text_w == source_text_w)
  7545.     source_height = height;
  7546.     else if (text_w == code_text_w)
  7547.     code_height = height;
  7548.  
  7549.     return height;
  7550. }
  7551.  
  7552.  
  7553. // If false, don't change glyphs - just check if they would change
  7554. bool SourceView::change_glyphs = true;
  7555.  
  7556. // All glyphs that have changed during update_glyphs_now()
  7557. WidgetArray SourceView::changed_glyphs;
  7558.  
  7559. // Unmap glyph W
  7560. void SourceView::unmap_glyph(Widget glyph)
  7561. {
  7562.     if (glyph == 0)
  7563.     return;
  7564.  
  7565.     assert(is_code_widget(glyph) || is_source_widget(glyph));
  7566.  
  7567.     XtPointer user_data;
  7568.     XtVaGetValues(glyph, XmNuserData, &user_data, NULL);
  7569.     if (user_data == 0)
  7570.     return;            // Already unmapped
  7571.  
  7572.     if (change_glyphs)
  7573.     {
  7574.     const Position invisible_x = -100;
  7575.     const Position invisible_y = -100;
  7576.  
  7577.     // Unmapping the glyph while dragging breaks the drag.
  7578.     // Move the glyph to an invisible position instead.
  7579.     XtVaSetValues(glyph,
  7580.               XmNleftOffset, invisible_x,
  7581.               XmNtopOffset,  invisible_y,
  7582.               XmNuserData, XtPointer(0),
  7583.               NULL);
  7584.  
  7585.     if (lesstif_version <= 85)
  7586.     {
  7587.         // LessTif 0.84 and earlier wants it the hard way.
  7588.         XtMoveWidget(glyph, invisible_x, invisible_y);
  7589.     }
  7590.     log_glyph(glyph);
  7591.     }
  7592.  
  7593.     changed_glyphs += glyph;
  7594. }
  7595.  
  7596. // Map glyph GLYPH in (X, Y)
  7597. void SourceView::map_glyph(Widget& glyph, Position x, Position y)
  7598. {
  7599.     while (glyph == 0)
  7600.     CreateGlyphsWorkProc(0);
  7601.  
  7602.     assert(is_code_widget(glyph) || is_source_widget(glyph));
  7603.  
  7604.     Widget text_w;
  7605.     if (is_source_widget(glyph))
  7606.     text_w = source_text_w;
  7607.     else
  7608.     text_w = code_text_w;
  7609.  
  7610.     XtPointer user_data;
  7611.     Dimension height              = 0;
  7612.     Dimension border_width        = 0;
  7613.     Dimension margin_height       = 0;
  7614.     Dimension shadow_thickness    = 0;
  7615.     Dimension highlight_thickness = 0;
  7616.     int old_x                     = 0;
  7617.     int old_y                     = 0; 
  7618.     XtVaGetValues(glyph,
  7619.           XmNheight,             &height,
  7620.           XmNborderWidth,        &border_width,
  7621.           XmNmarginHeight,       &margin_height,
  7622.           XmNshadowThickness,    &shadow_thickness,
  7623.           XmNhighlightThickness, &highlight_thickness,
  7624.           XmNuserData,           &user_data,
  7625.           XmNleftOffset,         &old_x,
  7626.           XmNtopOffset,          &old_y,
  7627.           NULL);
  7628.     Dimension glyph_height = 
  7629.     height + border_width + margin_height
  7630.     + shadow_thickness + highlight_thickness;
  7631.  
  7632.     y -= (line_height(text_w) + glyph_height) / 2 - 2;
  7633.  
  7634.     if (lesstif_version < 1000)
  7635.     x += 2;
  7636.  
  7637.     if (x != old_x || y != old_y)
  7638.     {
  7639.     if (change_glyphs)
  7640.     {
  7641.         if (lesstif_version <= 84)
  7642.         {
  7643.         // LessTif 0.84 and earlier want it the hard way.
  7644.         XtMoveWidget(glyph, x, y);
  7645.         }
  7646.  
  7647.         XtVaSetValues(glyph, XmNleftOffset, x, XmNtopOffset, y, NULL);
  7648.         log_glyph(glyph);
  7649.     }
  7650.     changed_glyphs += glyph;
  7651.     }
  7652.  
  7653.     if (user_data != 0)
  7654.     return;            // Already mapped
  7655.  
  7656.     if (change_glyphs)
  7657.     {
  7658.     XtMapWidget(glyph);
  7659.     XtVaSetValues(glyph, XmNuserData, XtPointer(1), NULL);
  7660.     log_glyph(glyph);
  7661.     changed_glyphs += glyph;
  7662.     }
  7663. }
  7664.  
  7665.  
  7666. // True if code/source glyphs need to be updated
  7667. bool SourceView::update_code_glyphs   = false;
  7668. bool SourceView::update_source_glyphs = false;
  7669.  
  7670. // Update glyphs for widget GLYPH (0: all)
  7671. void SourceView::update_glyphs(Widget glyph)
  7672. {
  7673.     static XtWorkProcId update_glyph_id = 0;
  7674.  
  7675.     if (glyph == 0)
  7676.     update_source_glyphs = update_code_glyphs = true;
  7677.     else if (is_source_widget(glyph))
  7678.     update_source_glyphs = true;
  7679.     else if (is_code_widget(glyph))
  7680.     update_code_glyphs = true;
  7681.  
  7682.     if (update_glyph_id != 0)
  7683.     XtRemoveTimeOut(update_glyph_id);
  7684.  
  7685.     // Chris van Engelen reports:
  7686.     // When reading in a new file, there is an infinite loop involving
  7687.     // function SourceView::UpdateGlyphsWorkProc: function
  7688.     // XtAppPending always returns a value indicating that there are
  7689.     // events pending, and since there always is at least one glyph to
  7690.     // be updated (the execution position arrow), the function returns
  7691.     // with a new call to UpdateGlyphsWorkProc scheduled. This problem
  7692.     // was solved by increasing the delay time for the first
  7693.     // scheduling to 50ms.
  7694.     update_glyph_id = 
  7695.     XtAppAddTimeOut(XtWidgetToApplicationContext(source_text_w), 50,
  7696.             UpdateGlyphsWorkProc, XtPointer(&update_glyph_id));
  7697. }
  7698.  
  7699.  
  7700. // Invoked by scrolling keys
  7701. void SourceView::updateGlyphsAct(Widget w, XEvent*, String *, Cardinal *)
  7702. {
  7703.     CheckScrollCB(w, 0, 0);
  7704. }
  7705.  
  7706. // Invoked whenever the text widget may be about to scroll
  7707. void SourceView::CheckScrollCB(Widget, XtPointer, XtPointer)
  7708. {
  7709.     static XtIntervalId check_scroll_id = 0;
  7710.  
  7711.     if (check_scroll_id != 0)
  7712.     {
  7713.     XtRemoveTimeOut(check_scroll_id);
  7714.     check_scroll_id = 0;
  7715.     }
  7716.  
  7717.     check_scroll_id = 
  7718.     XtAppAddTimeOut(XtWidgetToApplicationContext(source_text_w),
  7719.             app_data.glyph_update_delay,
  7720.             CheckScrollWorkProc, XtPointer(&check_scroll_id));
  7721. }
  7722.  
  7723. void SourceView::CheckScrollWorkProc(XtPointer client_data, XtIntervalId *id)
  7724. {
  7725.     (void) id;            // Use it
  7726.  
  7727.     XtIntervalId *timer = (XtIntervalId *)client_data;
  7728.     if (timer != 0)
  7729.     {
  7730.     assert(*timer == *id);
  7731.     *timer = 0;
  7732.     }
  7733.  
  7734.     XmTextPosition old_top = last_top;
  7735.     last_top = XmTextGetTopCharacter(source_text_w);
  7736.  
  7737.     XmTextPosition old_top_pc = last_top_pc;
  7738.     last_top_pc = XmTextGetTopCharacter(code_text_w);
  7739.  
  7740.     if (old_top != last_top && old_top_pc != last_top_pc)
  7741.     update_glyphs();
  7742.     else if (old_top != last_top)
  7743.     update_glyphs(source_text_w);
  7744.     else if (old_top_pc != last_top_pc)
  7745.     update_glyphs(code_text_w);
  7746. }
  7747.  
  7748.  
  7749. // Pixel offsets
  7750.  
  7751. // Horizontal arrow offset (pixels)
  7752. int SourceView::arrow_x_offset = -5;
  7753.  
  7754. // Horizontal breakpoint symbol offset (pixels)
  7755. int SourceView::stop_x_offset = +6;
  7756.  
  7757. // Additional offset for multiple breakpoints (pixels)
  7758. int SourceView::multiple_stop_x_offset = stop_width;
  7759.  
  7760.  
  7761. // Glyph locations: x[0] is source, x[1] is code
  7762. Widget SourceView::plain_arrows[2]  = {0, 0};
  7763. Widget SourceView::grey_arrows[2]   = {0, 0};
  7764. Widget SourceView::past_arrows[2]   = {0, 0};
  7765. Widget SourceView::signal_arrows[2] = {0, 0};
  7766. Widget SourceView::drag_arrows[2]   = {0, 0};
  7767.  
  7768. Widget SourceView::drag_stops[2]    = {0, 0};
  7769. Widget SourceView::drag_conds[2]    = {0, 0};
  7770. Widget SourceView::drag_temps[2]    = {0, 0};
  7771.  
  7772. WidgetArray SourceView::plain_stops[2];
  7773. WidgetArray SourceView::grey_stops[2];
  7774.  
  7775. WidgetArray SourceView::plain_conds[2];
  7776. WidgetArray SourceView::grey_conds[2];
  7777.  
  7778. WidgetArray SourceView::plain_temps[2];
  7779. WidgetArray SourceView::grey_temps[2];
  7780.  
  7781.  
  7782. // Create glyphs in the background
  7783. Boolean SourceView::CreateGlyphsWorkProc(XtPointer)
  7784. {
  7785.     int k;
  7786.     for (k = 0; k < 2; k++)
  7787.     {
  7788.     // On the Form widget, later children are displayed on top of
  7789.     // earlier children.  A stop sign hiding an arrow gives more
  7790.     // pleasing results than vice-versa, so place arrow glyph
  7791.     // below sign glyphs.
  7792.  
  7793.     Widget form_w = k ? code_form_w : source_form_w;
  7794.  
  7795.     if (form_w == 0)
  7796.         continue;
  7797.  
  7798.     if (past_arrows[k] == 0)
  7799.     {
  7800.         past_arrows[k] = 
  7801.         create_glyph(form_w, "past_arrow",
  7802.                  past_arrow_bits, 
  7803.                  past_arrow_width, 
  7804.                  past_arrow_height);
  7805.         return False;
  7806.     }
  7807.  
  7808.     if (plain_arrows[k] == 0)
  7809.     {
  7810.         plain_arrows[k] = 
  7811.         create_glyph(form_w, "plain_arrow",
  7812.                  arrow_bits, 
  7813.                  arrow_width,
  7814.                  arrow_height);
  7815.         return False;
  7816.     }
  7817.  
  7818.     if (grey_arrows[k] == 0)
  7819.     {
  7820.         grey_arrows[k] = 
  7821.         create_glyph(form_w, "grey_arrow",
  7822.                  grey_arrow_bits, 
  7823.                  grey_arrow_width, 
  7824.                  grey_arrow_height);
  7825.         return False;
  7826.     }
  7827.  
  7828.     if (signal_arrows[k] == 0)
  7829.     {
  7830.         signal_arrows[k] = 
  7831.         create_glyph(form_w, "signal_arrow",
  7832.                  signal_arrow_bits, 
  7833.                  signal_arrow_width,
  7834.                  signal_arrow_height);
  7835.         return False;
  7836.     }
  7837.  
  7838.     if (drag_arrows[k] == 0)
  7839.     {
  7840.         drag_arrows[k] = 
  7841.         create_glyph(form_w, "drag_arrow",
  7842.                  drag_arrow_bits, 
  7843.                  drag_arrow_width,
  7844.                  drag_arrow_height);
  7845.         return False;
  7846.     }
  7847.     }
  7848.    
  7849.     for (k = 0; k < 2; k++)
  7850.     {
  7851.     Widget form_w = k ? code_form_w : source_form_w;
  7852.  
  7853.     if (form_w == 0)
  7854.         continue;
  7855.  
  7856.     int i;
  7857.  
  7858.     for (i = 0; i < plain_stops[k].size() - 1; i++)
  7859.     {
  7860.         if (plain_stops[k][i] == 0)
  7861.         {
  7862.         plain_stops[k][i] = 
  7863.             create_glyph(form_w, "plain_stop",
  7864.                  stop_bits, 
  7865.                  stop_width,
  7866.                  stop_height);
  7867.         return False;
  7868.         }
  7869.     }
  7870.  
  7871.     for (i = 0; i < plain_temps[k].size() - 1; i++)
  7872.     {
  7873.         if (plain_temps[k][i] == 0)
  7874.         {
  7875.         plain_temps[k][i] = 
  7876.             create_glyph(form_w, "plain_temp",
  7877.                  temp_bits, 
  7878.                  temp_width,
  7879.                  temp_height);
  7880.         return False;
  7881.         }
  7882.     }
  7883.  
  7884.     for (i = 0; i < plain_conds[k].size() - 1; i++)
  7885.     {
  7886.         if (plain_conds[k][i] == 0)
  7887.         {
  7888.         plain_conds[k][i] = 
  7889.             create_glyph(form_w, "plain_cond",
  7890.                  cond_bits, 
  7891.                  cond_width,
  7892.                  cond_height);
  7893.         return False;
  7894.         }
  7895.     }
  7896.     for (i = 0; i < grey_stops[k].size() - 1; i++)
  7897.     {
  7898.         if (grey_stops[k][i] == 0)
  7899.         {
  7900.         grey_stops[k][i] = 
  7901.             create_glyph(form_w, "grey_stop",
  7902.                  grey_stop_bits, 
  7903.                  grey_stop_width,
  7904.                  grey_stop_height);
  7905.         return False;
  7906.         }
  7907.     }
  7908.  
  7909.     for (i = 0; i < grey_temps[k].size() - 1; i++)
  7910.     {
  7911.         if (grey_temps[k][i] == 0)
  7912.         {
  7913.         grey_temps[k][i] = 
  7914.             create_glyph(form_w, "grey_temp",
  7915.                  grey_temp_bits, 
  7916.                  grey_temp_width,
  7917.                  grey_temp_height);
  7918.         return False;
  7919.         }
  7920.     }
  7921.  
  7922.     for (i = 0; i < grey_conds[k].size() - 1; i++)
  7923.     {
  7924.         if (grey_conds[k][i] == 0)
  7925.         {
  7926.         grey_conds[k][i] = 
  7927.             create_glyph(form_w, "grey_cond",
  7928.                  grey_cond_bits, 
  7929.                  grey_cond_width,
  7930.                  grey_cond_height);
  7931.         return False;
  7932.         }
  7933.     }
  7934.  
  7935.     if (drag_stops[k] == 0)
  7936.     {
  7937.         drag_stops[k] = 
  7938.         create_glyph(form_w, "drag_stop",
  7939.                  drag_stop_bits, 
  7940.                  drag_stop_width,
  7941.                  drag_stop_height);
  7942.         return False;
  7943.     }
  7944.  
  7945.     if (drag_temps[k] == 0)
  7946.     {
  7947.         drag_temps[k] = 
  7948.         create_glyph(form_w, "drag_temp",
  7949.                  drag_temp_bits, 
  7950.                  drag_temp_width,
  7951.                  drag_temp_height);
  7952.         return False;
  7953.     }
  7954.  
  7955.     if (drag_conds[k] == 0)
  7956.     {
  7957.         drag_conds[k] = 
  7958.         create_glyph(form_w, "drag_cond",
  7959.                  drag_cond_bits, 
  7960.                  drag_cond_width,
  7961.                  drag_cond_height);
  7962.         return False;
  7963.     }
  7964.     }
  7965.  
  7966.     return True;        // all done
  7967. }
  7968.  
  7969. // Map stop sign GLYPH at position POS.  Get widget from STOPS[COUNT];
  7970. // store location in POSITIONS.  Return mapped widget (0 if none)
  7971. Widget SourceView::map_stop_at(Widget glyph, XmTextPosition pos,
  7972.                    WidgetArray& stops, int& count,
  7973.                    TextPositionArray& positions)
  7974. {
  7975.     assert (is_source_widget(glyph) || is_code_widget(glyph));
  7976.  
  7977.     Position x, y;
  7978.     Boolean pos_displayed = XmTextPosToXY(glyph, pos, &x, &y);
  7979.     if (pos_displayed)
  7980.     {
  7981.     while (stops[count] == 0)
  7982.     {
  7983.         if (CreateGlyphsWorkProc(0))
  7984.         break;
  7985.     }
  7986.  
  7987.     Widget glyph = stops[count] ? stops[count++] : 0;
  7988.  
  7989.     if (glyph != 0)
  7990.     {
  7991.         for (int i = 0; i < positions.size(); i++)
  7992.         if (pos == positions[i])
  7993.             x += multiple_stop_x_offset;
  7994.  
  7995.         map_glyph(glyph, x + stop_x_offset, y);
  7996.         positions += pos;
  7997.         return glyph;
  7998.     }
  7999.     else
  8000.     {
  8001.         // Max number of glyphs exceeded
  8002.         string msg = "Out of glyphs (used " + 
  8003.         itostring(stops.size() - 1) + " of " +
  8004.         itostring(stops.size() - 1) + ")";
  8005.  
  8006.         set_status(msg);
  8007.  
  8008.         static bool warning_posted = false;
  8009.  
  8010.         if (!warning_posted)
  8011.         {
  8012.         post_warning(msg, "out_of_glyphs_warning", glyph);
  8013.         warning_posted = true;
  8014.         }
  8015.     }
  8016.     }
  8017.  
  8018.     return 0;
  8019. }
  8020.  
  8021. // Map arrow in GLYPH at POS.  Return mapped arrow widget (0 if none)
  8022. Widget SourceView::map_arrow_at(Widget glyph, XmTextPosition pos)
  8023. {
  8024.     assert (is_source_widget(glyph) || is_code_widget(glyph));
  8025.  
  8026.     Position x, y;
  8027.     Boolean pos_displayed = (pos != XmTextPosition(-1) 
  8028.                  && XmTextPosToXY(glyph, pos, &x, &y));
  8029.  
  8030.     int k = int(is_code_widget(glyph));
  8031.  
  8032.     Widget& signal_arrow = signal_arrows[k];
  8033.     Widget& plain_arrow  = plain_arrows[k];
  8034.     Widget& grey_arrow   = grey_arrows[k];
  8035.     Widget& past_arrow   = past_arrows[k];
  8036.  
  8037.     while (signal_arrow == 0 || plain_arrow == 0 || 
  8038.        grey_arrow == 0 || past_arrow == 0)
  8039.     {
  8040.     if (CreateGlyphsWorkProc(0))
  8041.         break;
  8042.     }
  8043.  
  8044.     if (pos_displayed)
  8045.     {
  8046.     if (undo_buffer.showing_earlier_state())
  8047.     {
  8048.         map_glyph(past_arrow, x + arrow_x_offset, y);
  8049.         unmap_glyph(grey_arrow);
  8050.         unmap_glyph(signal_arrow);
  8051.         unmap_glyph(plain_arrow);
  8052.         return past_arrow;
  8053.     }
  8054.     else if (at_lowest_frame && signal_received)
  8055.     {
  8056.         map_glyph(signal_arrow, x + arrow_x_offset, y);
  8057.         unmap_glyph(plain_arrow);
  8058.         unmap_glyph(grey_arrow);
  8059.         unmap_glyph(past_arrow);
  8060.         return signal_arrow;
  8061.     }
  8062.     else if (at_lowest_frame)
  8063.     {
  8064.         map_glyph(plain_arrow, x + arrow_x_offset, y);
  8065.         unmap_glyph(signal_arrow);
  8066.         unmap_glyph(grey_arrow);
  8067.         unmap_glyph(past_arrow);
  8068.         return plain_arrow;
  8069.     }
  8070.     else
  8071.     {
  8072.         map_glyph(grey_arrow, x + arrow_x_offset, y);
  8073.         unmap_glyph(signal_arrow);
  8074.         unmap_glyph(plain_arrow);
  8075.         unmap_glyph(past_arrow);
  8076.         return grey_arrow;
  8077.     }
  8078.     }
  8079.     else
  8080.     {
  8081.     unmap_glyph(signal_arrow);
  8082.     unmap_glyph(plain_arrow);
  8083.     unmap_glyph(grey_arrow);
  8084.     unmap_glyph(past_arrow);
  8085.     }
  8086.     return 0;
  8087. }
  8088.  
  8089. // Copy glyph foreground and background colors from ORIGIN to GLYPH
  8090. void SourceView::copy_colors(Widget glyph, Widget origin)
  8091. {
  8092.     if (origin == 0)
  8093.     return;
  8094.  
  8095.     Pixel background, foreground;
  8096.     XtVaGetValues(origin,
  8097.           XmNforeground, &foreground,
  8098.           XmNbackground, &background,
  8099.           NULL);
  8100.  
  8101.     Pixmap pixmap = 
  8102.     XmGetPixmap(XtScreen(glyph), XtName(glyph), foreground, background);
  8103.     if (pixmap != XmUNSPECIFIED_PIXMAP)
  8104.     {
  8105.     Pixmap old_pixmap;
  8106.     XtVaGetValues(glyph, XmNlabelPixmap, &old_pixmap, NULL);
  8107.     XmDestroyPixmap(XtScreen(glyph), old_pixmap);
  8108.  
  8109.     XtVaSetValues(glyph, XmNlabelPixmap, pixmap, NULL);
  8110.     }
  8111. }
  8112.  
  8113. // Map temporary stop sign at position POS.  If ORIGIN is given, use
  8114. // colors from ORIGIN.
  8115. Widget SourceView::map_drag_stop_at(Widget glyph, XmTextPosition pos, 
  8116.                     Widget origin)
  8117. {
  8118.     assert (is_source_widget(glyph) || is_code_widget(glyph));
  8119.  
  8120.     Position x, y;
  8121.     Boolean pos_displayed = 
  8122.     (pos != XmTextPosition(-1) && XmTextPosToXY(glyph, pos, &x, &y));
  8123.  
  8124.     int k = int(is_code_widget(glyph));
  8125.  
  8126.     if (pos_displayed)
  8127.     {
  8128.     bool cond = (origin != 0 && string(XtName(origin)).contains("cond"));
  8129.     bool temp = (origin != 0 && string(XtName(origin)).contains("temp"));
  8130.  
  8131.     Widget& drag_stop = 
  8132.         temp ? drag_temps[k] : 
  8133.         cond ? drag_conds[k] : 
  8134.         drag_stops[k];
  8135.     
  8136.     while (drag_stop == 0)
  8137.     {
  8138.         if (CreateGlyphsWorkProc(0))
  8139.         break;
  8140.     }
  8141.  
  8142.     copy_colors(drag_stop, origin);
  8143.  
  8144.     if (origin != 0)
  8145.     {
  8146.         static Position last_x = x + stop_x_offset;
  8147.  
  8148.         Position origin_x = -1;
  8149.          XtVaGetValues(origin, XmNx, &origin_x, NULL);
  8150.         if (lesstif_version < 1000)
  8151.         origin_x -= 2;
  8152.  
  8153.         if (origin_x >= 0)
  8154.         {
  8155.         // Origin is mapped
  8156.         x = last_x = origin_x;
  8157.         }
  8158.         else
  8159.         {
  8160.         // Origin is unmapped - use last recorded value
  8161.         x = last_x;
  8162.         }
  8163.     }
  8164.     else
  8165.     {
  8166.         x += stop_x_offset;
  8167.     }
  8168.  
  8169.     map_glyph(drag_stop, x, y);
  8170.     if (temp)
  8171.     {
  8172.         unmap_glyph(drag_conds[k]);
  8173.         unmap_glyph(drag_stops[k]);
  8174.     }
  8175.     else if (cond)
  8176.     {
  8177.         unmap_glyph(drag_temps[k]);
  8178.         unmap_glyph(drag_stops[k]);
  8179.     }
  8180.     else
  8181.     {
  8182.         unmap_glyph(drag_conds[k]);
  8183.         unmap_glyph(drag_temps[k]);
  8184.     }
  8185.  
  8186.     return drag_stop;
  8187.     }
  8188.     else
  8189.     {
  8190.     unmap_glyph(drag_conds[k]);
  8191.     unmap_glyph(drag_temps[k]);
  8192.     unmap_glyph(drag_stops[k]);
  8193.  
  8194.     return 0;
  8195.     }
  8196. }
  8197.  
  8198. // Map temporary arrow at position POS.  If ORIGIN is given, use
  8199. // colors from ORIGIN.
  8200. Widget SourceView::map_drag_arrow_at(Widget glyph, XmTextPosition pos, 
  8201.                      Widget origin)
  8202. {
  8203.     assert (is_source_widget(glyph) || is_code_widget(glyph));
  8204.     Position x, y;
  8205.     Boolean pos_displayed = (pos != XmTextPosition(-1) 
  8206.                  && XmTextPosToXY(glyph, pos, &x, &y));
  8207.  
  8208.     int k = int(is_code_widget(glyph));
  8209.  
  8210.     Widget& drag_arrow = drag_arrows[k];
  8211.  
  8212.     while (drag_arrow == 0)
  8213.     {
  8214.     if (CreateGlyphsWorkProc(0))
  8215.         break;
  8216.     }
  8217.  
  8218.     copy_colors(drag_arrow, origin);
  8219.  
  8220.     if (pos_displayed)
  8221.     map_glyph(drag_arrow, x + arrow_x_offset, y);
  8222.     else
  8223.     unmap_glyph(drag_arrow);
  8224.  
  8225.     return drag_arrow;
  8226. }
  8227.  
  8228.  
  8229.  
  8230. // Update glyphs after interval
  8231. void SourceView::UpdateGlyphsWorkProc(XtPointer client_data, XtIntervalId *id)
  8232. {
  8233.     (void) id;            // Use it
  8234.  
  8235.     // Allow new invocations
  8236.     XtIntervalId *proc_id = ((XtIntervalId *) client_data);
  8237.     if (proc_id != 0)
  8238.     {
  8239.     assert(*proc_id == *id);
  8240.     *proc_id = 0;
  8241.     }
  8242.  
  8243.     XtAppContext app_context = XtWidgetToApplicationContext(source_text_w);
  8244.     if (XtAppPending(app_context) & (XtIMXEvent | XtIMAlternateInput))
  8245.     {
  8246.     // Other events pending - check if we shall change something
  8247.     const WidgetArray& glyphs = glyphs_to_be_updated();
  8248.  
  8249.     if (glyphs.size() > 0)
  8250.     {
  8251.         // Change is imminent - unmap all glyphs that will change
  8252.         // and try again in 50ms
  8253.         for (int i = 0; i < glyphs.size(); i++)
  8254.         unmap_glyph(glyphs[i]);
  8255.  
  8256.         XtIntervalId new_id = 
  8257.         XtAppAddTimeOut(app_context, 50,
  8258.                 UpdateGlyphsWorkProc, client_data);
  8259.         if (proc_id != 0)
  8260.         *proc_id = new_id;
  8261.         return;
  8262.     }
  8263.     }
  8264.  
  8265.     change_glyphs = true;
  8266.     update_glyphs_now();
  8267. }
  8268.  
  8269.  
  8270. // The function that does the real work
  8271. void SourceView::update_glyphs_now()
  8272. {
  8273.     // clog << "Updating glyphs...";
  8274.  
  8275.     WidgetArray empty;
  8276.     changed_glyphs = empty;
  8277.  
  8278.     if (update_source_glyphs)
  8279.     {
  8280.     // Show current execution position
  8281.     XmTextPosition pos = XmTextPosition(-1);
  8282.  
  8283.     if (display_glyphs &&
  8284.         (is_current_file(last_execution_file) ||
  8285.          base_matches(last_execution_file, current_file_name)) &&
  8286.          line_count > 0 &&
  8287.          last_execution_line > 0 &&
  8288.          last_execution_line <= line_count)
  8289.     {
  8290.         pos = pos_of_line(last_execution_line);
  8291.     }
  8292.  
  8293.     map_arrow_at(source_text_w, pos);
  8294.     }
  8295.  
  8296.     if (update_code_glyphs)
  8297.     {
  8298.     // Show current PC
  8299.     XmTextPosition pos = XmTextPosition(-1);
  8300.  
  8301.     if (display_glyphs && last_execution_pc != "")
  8302.         pos = find_pc(last_execution_pc);
  8303.  
  8304.     map_arrow_at(code_text_w, pos);
  8305.     }
  8306.  
  8307.     // Map breakpoint glyphs
  8308.     for (int k = 0; k < 2; k++)
  8309.     {
  8310.     if (k == 0 && !update_source_glyphs)
  8311.         continue;
  8312.     if (k == 1 && !update_code_glyphs)
  8313.         continue;
  8314.  
  8315.     int plain_stops_count = 0;
  8316.     int grey_stops_count  = 0;
  8317.  
  8318.     int plain_conds_count = 0;
  8319.     int grey_conds_count  = 0;
  8320.  
  8321.     int plain_temps_count = 0;
  8322.     int grey_temps_count  = 0;
  8323.  
  8324.     if (display_glyphs)
  8325.     {
  8326.         TextPositionArray positions;
  8327.         
  8328.         MapRef ref;
  8329.         for (BreakPoint *bp = bp_map.first(ref);
  8330.          bp != 0;
  8331.          bp = bp_map.next(ref))
  8332.         {
  8333.         Widget& bp_glyph = k ? bp->code_glyph() : bp->source_glyph();
  8334.         Widget text_w    = k ? code_text_w      : source_text_w;
  8335.         bp_glyph = 0;
  8336.  
  8337.         XmTextPosition pos;
  8338.         if (k == 0)
  8339.         {
  8340.             // Find source position
  8341.             if (!bp_matches(bp)
  8342.             || line_count <= 0
  8343.             || bp->line_nr() <= 0
  8344.             || bp->line_nr() > line_count)
  8345.             continue;
  8346.  
  8347.             pos = pos_of_line(bp->line_nr());
  8348.         }
  8349.         else
  8350.         {
  8351.             // Find code position
  8352.             if (bp->type() != BREAKPOINT)
  8353.             continue;
  8354.  
  8355.             pos = find_pc(bp->address());
  8356.         }
  8357.  
  8358.         if (bp->dispo() != BPKEEP)
  8359.         {
  8360.             // Temporary breakpoint
  8361.             if (bp->enabled())
  8362.             bp_glyph = map_stop_at(text_w, pos, plain_temps[k],
  8363.                            plain_temps_count, positions);
  8364.             else
  8365.             bp_glyph = map_stop_at(text_w, pos, grey_temps[k],
  8366.                            grey_temps_count, positions);
  8367.         }
  8368.         else if (bp->condition() != "" || bp->ignore_count() != 0)
  8369.         {
  8370.             // Conditional breakpoint
  8371.             if (bp->enabled())
  8372.             bp_glyph = map_stop_at(text_w, pos, plain_conds[k],
  8373.                            plain_conds_count, positions);
  8374.             else
  8375.             bp_glyph = map_stop_at(text_w, pos, grey_conds[k],
  8376.                            grey_conds_count, positions);
  8377.         }
  8378.         else
  8379.         {
  8380.             // Orindary breakpoint
  8381.             if (bp->enabled())
  8382.             bp_glyph = map_stop_at(text_w, pos, plain_stops[k],
  8383.                            plain_stops_count, positions);
  8384.             else
  8385.             bp_glyph = map_stop_at(text_w, pos, grey_stops[k],
  8386.                            grey_stops_count, positions);
  8387.         }
  8388.         }
  8389.     }
  8390.  
  8391.     // Unmap remaining breakpoint glyphs
  8392.     Widget glyph;
  8393.     while ((glyph = plain_stops[k][plain_stops_count++]))
  8394.         unmap_glyph(glyph);
  8395.     while ((glyph = grey_stops[k][grey_stops_count++]))
  8396.         unmap_glyph(glyph);
  8397.     while ((glyph = plain_conds[k][plain_conds_count++]))
  8398.         unmap_glyph(glyph);
  8399.     while ((glyph = grey_conds[k][grey_conds_count++]))
  8400.         unmap_glyph(glyph);
  8401.     while ((glyph = plain_temps[k][plain_temps_count++]))
  8402.         unmap_glyph(glyph);
  8403.     while ((glyph = grey_temps[k][grey_temps_count++]))
  8404.         unmap_glyph(glyph);
  8405.     }
  8406.  
  8407.     if (change_glyphs)
  8408.     {
  8409.     update_source_glyphs = false;
  8410.     update_code_glyphs   = false;
  8411.     }
  8412.  
  8413.     // clog << "done.\n";
  8414. }
  8415.  
  8416.  
  8417. // Return all glyphs that would change
  8418. const WidgetArray& SourceView::glyphs_to_be_updated()
  8419. {
  8420.     change_glyphs = false;
  8421.     update_glyphs_now();
  8422.     change_glyphs = true;
  8423.  
  8424.     // clog << "Glyphs to be updated:";
  8425.     // for (int i = 0; i < changed_glyphs.size(); i++)
  8426.     //     clog << " " << XtName(changed_glyphs[i]);
  8427.     // clog << "\n";
  8428.  
  8429.     return changed_glyphs;
  8430. }
  8431.  
  8432.  
  8433. // Change setting of display_glyphs
  8434. void SourceView::set_display_glyphs(bool set)
  8435. {
  8436.     if (display_glyphs != set)
  8437.     {
  8438.     // Save current execution position
  8439.     string file   = last_execution_file;
  8440.     int    line   = last_execution_line;
  8441.     string pc     = last_execution_pc;
  8442.     bool stopped  = at_lowest_frame;
  8443.     bool signaled = signal_received;
  8444.  
  8445.     if (XtIsRealized(source_text_w))
  8446.     {
  8447.         display_glyphs = false;    
  8448.         show_execution_position();
  8449.         UpdateGlyphsWorkProc(0, 0);
  8450.  
  8451.         display_glyphs = true;
  8452.         refresh_bp_disp();
  8453.     }
  8454.  
  8455.     display_glyphs = set;
  8456.  
  8457.     if (XtIsRealized(source_text_w))
  8458.     {
  8459.         StatusDelay delay(set ? "Enabling glyphs" : "Disabling glyphs");
  8460.  
  8461.         refresh_bp_disp();
  8462.         if (file != "")
  8463.         show_execution_position(file + ":" + itostring(line), 
  8464.                     stopped, signaled);
  8465.         if (pc != "")
  8466.         show_pc(pc, XmHIGHLIGHT_SELECTED);
  8467.     }
  8468.     }
  8469. }
  8470.  
  8471. // Change setting of display_line_numbers
  8472. void SourceView::set_display_line_numbers(bool set)
  8473. {
  8474.     if (display_line_numbers != set)
  8475.     {
  8476.     display_line_numbers = set;
  8477.  
  8478.     if (XtIsRealized(source_text_w))
  8479.     {
  8480.         StatusDelay delay(set ? "Enabling line numbers" : 
  8481.                   "Disabling line numbers");
  8482.         reload();
  8483.     }
  8484.     }
  8485. }
  8486.  
  8487. // Return help on a glyph
  8488. MString SourceView::help_on_glyph(Widget glyph, bool detailed)
  8489. {
  8490.     XmTextPosition dummy;
  8491.     return help_on_pos(glyph, 0, dummy, detailed);
  8492. }
  8493.  
  8494. // Return help on a breakpoint position
  8495. MString SourceView::help_on_pos(Widget w, XmTextPosition pos, 
  8496.                 XmTextPosition& ref, bool detailed)
  8497. {
  8498.     if (w == 0)
  8499.     return MString(0, true);
  8500.  
  8501.     int line_nr;
  8502.     bool in_text;
  8503.     int bp_nr;
  8504.     string address;
  8505.     bool pos_found = get_line_of_pos(w, pos, line_nr, address, in_text, bp_nr);
  8506.  
  8507.     if (!pos_found || bp_nr == 0)
  8508.     return MString(0, true);
  8509.  
  8510.     ref = pos_of_line(line_nr) + 2;
  8511.     return help_on_bp(bp_nr, detailed);
  8512. }
  8513.  
  8514. // Return help on a glyph
  8515. MString SourceView::help_on_bp(int bp_nr, bool detailed)
  8516. {
  8517.     BreakPoint *bp = bp_map.get(bp_nr);
  8518.     if (bp == 0)
  8519.     return MString(0, true);
  8520.  
  8521.     MString info;
  8522.     switch (bp->type())
  8523.     {
  8524.     case BREAKPOINT:
  8525.     info = rm("Breakpoint ");
  8526.     break;
  8527.     case WATCHPOINT:
  8528.     info = rm("Watchpoint ");
  8529.     break;
  8530.     }
  8531.     info += tt(itostring(bp->number()));
  8532.  
  8533.     if (detailed)
  8534.     {
  8535.     if (bp->enabled())
  8536.         info += rm(" (enabled");
  8537.     else
  8538.         info += rm(" (disabled");
  8539.  
  8540.     string infos = bp->infos();
  8541.     strip_space(infos);
  8542.     infos.gsub("\n", "; ");
  8543.  
  8544.     if (bp->infos() != "")
  8545.         info += rm("; " + infos);
  8546.  
  8547.     switch (bp->dispo())
  8548.     {
  8549.     case BPKEEP:
  8550.         break;
  8551.     case BPDEL:
  8552.         info += rm("; delete when hit");
  8553.         break;
  8554.     case BPDIS:
  8555.         info += rm("; disable when hit");
  8556.         break;
  8557.     }
  8558.     info += rm(")");
  8559.     }
  8560.  
  8561.     return info;
  8562. }
  8563.  
  8564.  
  8565. // Glyph drag & drop
  8566.  
  8567. XmTextPosition SourceView::glyph_position(Widget glyph, XEvent *e, 
  8568.                       bool normalize)
  8569. {
  8570.     Widget text_w;
  8571.     if (is_source_widget(glyph))
  8572.     text_w = source_text_w;
  8573.     else if (is_code_widget(glyph))
  8574.     text_w = code_text_w;
  8575.     else
  8576.     return XmTextPosition(-1);
  8577.  
  8578.     BoxPoint p = point(e);
  8579.     if (glyph != source_text_w && glyph != code_text_w)
  8580.     {
  8581.     // Called from a glyph: add glyph position to event position
  8582.     translate_glyph_pos(glyph, text_w, p[X], p[Y]);
  8583.     }
  8584.  
  8585.     // Get the position
  8586.     XmTextPosition pos = XmTextXYToPos(text_w, p[X], p[Y]);
  8587.  
  8588.     // Stay within viewable text +/-1 row, such that we don't scroll too fast
  8589.     short rows = 0;
  8590.     XmTextPosition current_top = 0;
  8591.     XtVaGetValues(text_w,
  8592.           XmNrows, &rows,
  8593.           XmNtopCharacter, ¤t_top,
  8594.           NULL);
  8595.  
  8596.     const string& text = current_text(text_w);
  8597.     XmTextPosition current_bottom = current_top;
  8598.     while (current_bottom < int(text.length()) && rows > 0)
  8599.     if (text[current_bottom++] == '\n')
  8600.         rows--;
  8601.  
  8602.     if (pos < current_top)
  8603.     pos = max(current_top - 1, 0);
  8604.     else if (pos > current_bottom)
  8605.     pos = min(current_bottom + 1, text.length());
  8606.  
  8607.     if (normalize)
  8608.     {
  8609.     const string& text = current_text(glyph);
  8610.     pos = min(pos, text.length());
  8611.     while (pos > 0 && text[pos - 1] != '\n')
  8612.         pos--;
  8613.     }
  8614.  
  8615.     return pos;
  8616. }
  8617.  
  8618. // Data associated with current drag operation
  8619. // The Glyph being dragged
  8620. Widget SourceView::current_drag_origin     = 0;
  8621.  
  8622. // The breakpoint being dragged, or 0 if execution position
  8623. int    SourceView::current_drag_breakpoint = -1;
  8624.  
  8625. void SourceView::dragGlyphAct(Widget glyph, XEvent *e, String *params, 
  8626.                   Cardinal *num_params)
  8627. {
  8628.     if (e->type != ButtonPress && e->type != ButtonRelease)
  8629.     return;
  8630.  
  8631.     Widget text_w;
  8632.     if (is_source_widget(glyph))
  8633.     text_w = source_text_w;
  8634.     else if (is_code_widget(glyph))
  8635.     text_w = code_text_w;
  8636.     else
  8637.     return;            // Bad widget
  8638.  
  8639.     if (!XtIsRealized(text_w))
  8640.     return;
  8641.  
  8642.     // Move cursor to glyph position
  8643.     XButtonEvent *event = &e->xbutton;
  8644.     translate_glyph_pos(glyph, text_w, event->x, event->y);
  8645.     event->window = XtWindow(text_w);
  8646.     XtCallActionProc(text_w, "source-start-select-word", e, 
  8647.              params, *num_params);
  8648.  
  8649.     // Check for double clicks
  8650.     XtCallActionProc(text_w, "source-double-click", e,
  8651.              params, *num_params);
  8652.  
  8653.     // Now start the drag
  8654.     int k;
  8655.     for (k = 0; k < 2; k++)
  8656.     {
  8657.     if (glyph == grey_arrows[k] || glyph == past_arrows[k])
  8658.     {
  8659.         // Cannot drag last execution position
  8660.         return;
  8661.     }
  8662.     else if (glyph == plain_arrows[k])
  8663.     {
  8664.         if (!gdb->has_jump_command() && !gdb->has_assign_command())
  8665.         {
  8666.         // Execution position cannot be dragged
  8667.         return;
  8668.         }
  8669.     }
  8670.     else if (glyph == drag_stops[k] || 
  8671.          glyph == drag_conds[k] || 
  8672.          glyph == drag_temps[k] || 
  8673.          glyph == drag_arrows[k])
  8674.     {
  8675.         // Temp glyph cannot be dragged
  8676.         return;
  8677.     }
  8678.     }
  8679.  
  8680.     static Cursor move_cursor = XCreateFontCursor(XtDisplay(glyph), XC_fleur);
  8681.  
  8682.     // clog << "Dragging " << XtName(glyph) << " [" << glyph << "]\n";
  8683.  
  8684.     XDefineCursor(XtDisplay(glyph), XtWindow(glyph), move_cursor);
  8685.  
  8686.     unmap_drag_stop(text_w);
  8687.     unmap_drag_arrow(text_w);
  8688.  
  8689.     current_drag_origin     = glyph;
  8690.     current_drag_breakpoint = 0;
  8691.  
  8692.     // Check for breakpoint
  8693.     MapRef ref;
  8694.     for (BreakPoint *bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  8695.     {
  8696.     if (glyph == bp->source_glyph() || glyph == bp->code_glyph())
  8697.     {
  8698.         current_drag_breakpoint = bp->number();
  8699.         break;
  8700.     }
  8701.     }
  8702. }
  8703.  
  8704. void SourceView::followGlyphAct(Widget glyph, XEvent *e, String *, Cardinal *)
  8705. {
  8706.     if (glyph != current_drag_origin)
  8707.     return;
  8708.  
  8709.     Widget text_w;
  8710.     if (is_source_widget(glyph))
  8711.     text_w = source_text_w;
  8712.     else if (is_code_widget(glyph))
  8713.     text_w = code_text_w;
  8714.     else
  8715.     return;            // Bad widget
  8716.  
  8717.     // Protect against more than one invocation per 50ms.
  8718.     static Time last_time = 0;
  8719.     if (time(e) - last_time < 50)
  8720.     return;
  8721.     last_time = time(e);
  8722.  
  8723.     XmTextPosition pos = glyph_position(glyph, e);
  8724.  
  8725.     // Make sure we see the position
  8726.     ShowPosition(text_w, pos);
  8727.  
  8728.     // Update glyphs in case we had to scroll
  8729.     CheckScrollCB(glyph, XtPointer(0), XtPointer(0));
  8730.  
  8731.     if (current_drag_breakpoint)
  8732.     map_drag_stop_at(text_w, pos, glyph);
  8733.     else
  8734.     map_drag_arrow_at(text_w, pos, glyph);
  8735. }
  8736.  
  8737. void SourceView::dropGlyphAct (Widget glyph, XEvent *e, 
  8738.                    String *params, Cardinal *num_params)
  8739. {
  8740.     if (e->type != ButtonPress && e->type != ButtonRelease)
  8741.     return;
  8742.  
  8743.     if (glyph != current_drag_origin)
  8744.     return;
  8745.  
  8746.     Widget text_w;
  8747.     if (is_source_widget(glyph))
  8748.     text_w = source_text_w;
  8749.     else if (is_code_widget(glyph))
  8750.     text_w = code_text_w;
  8751.     else
  8752.     return;            // Bad widget
  8753.  
  8754.     if (!XtIsRealized(text_w))
  8755.     return;
  8756.  
  8757.     XUndefineCursor(XtDisplay(glyph), XtWindow(glyph));
  8758.  
  8759.     // Unmap temp glyphs
  8760.     unmap_drag_stop(text_w);
  8761.     unmap_drag_arrow(text_w);
  8762.  
  8763.     // Show all other glyphs
  8764.     update_glyphs();
  8765.  
  8766.     int k;
  8767.     for (k = 0; k < 2; k++)
  8768.     if (glyph == grey_arrows[k] || 
  8769.         glyph == past_arrows[k] ||
  8770.         glyph == drag_stops[k] || 
  8771.         glyph == drag_conds[k] || 
  8772.         glyph == drag_temps[k] || 
  8773.         glyph == drag_arrows[k])
  8774.         return;
  8775.  
  8776.     XmTextPosition pos = glyph_position(glyph, e);
  8777.     if (pos == XmTextPosition(-1))
  8778.     return;            // No position
  8779.  
  8780.     int line_nr = 0;
  8781.     bool in_text;
  8782.     int bp_nr;
  8783.     string address;
  8784.     if (!get_line_of_pos(text_w, pos, line_nr, address, in_text, bp_nr))
  8785.     return;            // No location
  8786.  
  8787.     if (text_w == code_text_w)
  8788.     {
  8789.     // Selection from code
  8790.     if (address == "")
  8791.         return;        // No address
  8792.     }
  8793.     else
  8794.     {
  8795.     // Selection from source
  8796.     if (line_nr == 0)
  8797.         return;        // No line
  8798.     address = current_source_name() + ':' + itostring(line_nr);
  8799.     }
  8800.  
  8801.     // clog << "Dropping " << XtName(glyph) << " [" << glyph << "] at " 
  8802.     //      << address << "\n";
  8803.  
  8804.     if (text_w == code_text_w)
  8805.     {
  8806.     // Selection from code
  8807.     if (address == "")
  8808.         return;        // No address
  8809.     address = string('*') + address;
  8810.     }
  8811.     else
  8812.     {
  8813.     // Selection from source
  8814.     if (line_nr == 0)
  8815.         return;        // No line
  8816.     address = current_source_name() + ':' + itostring(line_nr);
  8817.     }
  8818.  
  8819.     string p = "move";
  8820.     if (num_params != 0 && *num_params == 1)
  8821.     p = params[0];
  8822.     if (num_params != 0 && *num_params > 1)
  8823.     cerr << "source-drop-glyph: too many parameters\n";
  8824.     p.downcase();
  8825.  
  8826.     bool copy = false;
  8827.     if (p == "move")
  8828.     copy = false;
  8829.     else if (p == "copy")
  8830.     copy = true;
  8831.     else
  8832.     cerr << "source-drop-glyph: unknown parameter " << quote(p) << "\n";
  8833.  
  8834.     bool changed = false;
  8835.     if (current_drag_breakpoint)
  8836.     {
  8837.     // Move breakpoint
  8838.     changed = move_bp(current_drag_breakpoint, address, text_w, copy);
  8839.     }
  8840.     else
  8841.     {
  8842.     // Move exec pos
  8843.     changed = move_pc(address, text_w);
  8844.     }
  8845.  
  8846.     if (changed)
  8847.     {
  8848.     // Make sure this position is kept visible
  8849.     SetInsertionPosition(text_w, pos);
  8850.     }
  8851.  
  8852.     current_drag_origin     = 0;
  8853.     current_drag_breakpoint = 0;
  8854. }
  8855.  
  8856. // Report glyph state (for debugging)
  8857. void SourceView::log_glyph(Widget glyph, int n)
  8858. {
  8859.     (void) n;            // Use it
  8860.     (void) glyph;
  8861.  
  8862. #if LOG_GLYPHS
  8863.     if (glyph == 0)
  8864.     return;
  8865.  
  8866.     int left = 0;
  8867.     int top  = 0;
  8868.     Position x = 0;
  8869.     Position y = 0;
  8870.     XtPointer user_data;
  8871.     XtVaGetValues(glyph,
  8872.           XmNuserData,           &user_data,
  8873.           XmNleftOffset,         &left,
  8874.           XmNtopOffset,          &top,
  8875.           XmNx,                  &x,
  8876.           XmNy,                  &y,
  8877.           NULL);
  8878.  
  8879.     clog << XtName(glyph);
  8880.     if (n >= 0)
  8881.     clog << "s[" << n << "]";
  8882.     clog << ": ";
  8883.     if (user_data)
  8884.     clog << "mapped";
  8885.     else
  8886.     clog << "unmapped";
  8887.  
  8888.     clog << " at (" << left << ", " << top << " / "
  8889.      << x << ", " << y << ")\n";
  8890. #endif
  8891. }
  8892.  
  8893. void SourceView::log_glyphs()
  8894. {
  8895. #if LOG_GLYPHS
  8896.     for (int k = 0; k < 2; k++)
  8897.     {
  8898.     if (k && !disassemble)
  8899.         continue;
  8900.  
  8901.     if (k == 0)
  8902.         clog << "Source glyphs:\n";
  8903.     else
  8904.         clog << "\nCode glyphs:\n";
  8905.  
  8906.     int i;
  8907.     for (i = 0; i < plain_stops[k].size() - 1; i++)
  8908.         log_glyph(plain_stops[k][i], i);
  8909.     for (i = 0; i < grey_stops[k].size() - 1; i++)
  8910.         log_glyph(grey_stops[k][i], i);
  8911.  
  8912.     for (i = 0; i < plain_conds[k].size() - 1; i++)
  8913.         log_glyph(plain_conds[k][i], i);
  8914.     for (i = 0; i < grey_conds[k].size() - 1; i++)
  8915.         log_glyph(grey_conds[k][i], i);
  8916.  
  8917.     for (i = 0; i < plain_temps[k].size() - 1; i++)
  8918.         log_glyph(plain_temps[k][i], i);
  8919.     for (i = 0; i < grey_temps[k].size() - 1; i++)
  8920.         log_glyph(grey_temps[k][i], i);
  8921.  
  8922.     log_glyph(plain_arrows[k]);
  8923.     log_glyph(grey_arrows[k]);
  8924.     log_glyph(past_arrows[k]);
  8925.     log_glyph(signal_arrows[k]);
  8926.     log_glyph(drag_arrows[k]);
  8927.  
  8928.     log_glyph(drag_stops[k]);
  8929.     log_glyph(drag_conds[k]);
  8930.     log_glyph(drag_temps[k]);
  8931.     }
  8932. #endif
  8933. }
  8934.  
  8935.  
  8936. // Delete glyph (breakpoints)
  8937. void SourceView::deleteGlyphAct(Widget glyph, XEvent *, String *, Cardinal *)
  8938. {
  8939.     IntArray bps;
  8940.     MapRef ref;
  8941.     for (BreakPoint *bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  8942.     {
  8943.     if (glyph == bp->source_glyph() || glyph == bp->code_glyph())
  8944.     {
  8945.         bps += bp->number();
  8946.     }
  8947.     }
  8948.  
  8949.     delete_bps(bps, glyph);
  8950. }
  8951.  
  8952.  
  8953. //----------------------------------------------------------------------------
  8954. // Machine code stuff
  8955. //----------------------------------------------------------------------------
  8956.  
  8957. // Clear the code cache
  8958. void SourceView::clear_code_cache()
  8959. {
  8960.     static CodeCache empty;
  8961.     code_cache = empty;
  8962.     process_disassemble("No code.");
  8963. }
  8964.  
  8965. #if RUNTIME_REGEX
  8966. static regex rxnladdress("\n *" RXADDRESS);
  8967. #endif
  8968.  
  8969. static string first_address(const string& s)
  8970. {
  8971.     int idx = index(s, rxnladdress, "\n");
  8972.     if (idx < 0)
  8973.     return "";
  8974.     idx++;
  8975.  
  8976.     int eol = s.index('\n', idx);
  8977.     if (eol < 0)
  8978.     eol = s.length();
  8979.  
  8980.     string addr = ((string&)s).at(idx, eol - idx);
  8981.     return addr.through(rxaddress);
  8982. }
  8983.  
  8984. static string last_address(const string& s)
  8985. {
  8986.     int idx = index(s, rxnladdress, "\n", -1);
  8987.     if (idx < 0)
  8988.     return "";
  8989.     idx++;
  8990.  
  8991.     int eol = s.index('\n', idx);
  8992.     if (eol < 0)
  8993.     eol = s.length();
  8994.  
  8995.     string addr = ((string&)s).at(idx, eol - idx);
  8996.     return addr.through(rxaddress);
  8997. }
  8998.  
  8999. void SourceView::set_code(const string& code,
  9000.               const string& start,
  9001.               const string& end)
  9002. {
  9003.     XmTextSetString(code_text_w, (String)code);
  9004.     XmTextSetHighlight (code_text_w, 0, code.length(), XmHIGHLIGHT_NORMAL);
  9005.     
  9006.     current_code       = code;
  9007.     current_code_start = start;
  9008.     current_code_end   = end;
  9009.  
  9010.     last_pos_pc             = 0;
  9011.     last_start_highlight_pc = 0;
  9012.     last_end_highlight_pc   = 0;
  9013. }
  9014.  
  9015. // Process output of `disassemble' command
  9016. void SourceView::process_disassemble(const string& disassemble_output)
  9017. {
  9018.     int count             = ((string&)disassemble_output).freq('\n') + 1;
  9019.     string *code_list     = new string[count];
  9020.  
  9021.     split((string &)disassemble_output, code_list, count, '\n');
  9022.  
  9023.     string indented_code;
  9024.     for (int i = 0; i < count; i++)
  9025.     {
  9026.     string& line = code_list[i];
  9027.     untabify_if_needed(line);
  9028.     if (line.length() > 0 && line[0] == '0')
  9029.         line = replicate(' ', indent_amount(code_text_w)) + line;
  9030.     indented_code += line + '\n';
  9031.     }
  9032.  
  9033.     set_code(indented_code,
  9034.          first_address(disassemble_output),
  9035.          last_address(disassemble_output));
  9036.  
  9037.     if (cache_machine_code
  9038.     && current_code_start != ""
  9039.     && current_code_end   != "")
  9040.     code_cache += CodeCacheEntry(current_code_start, 
  9041.                      current_code_end, 
  9042.                      current_code);
  9043. }
  9044.  
  9045. // Search PC in the current code; return beginning of line if found
  9046. XmTextPosition SourceView::find_pc(const string& pc)
  9047. {
  9048.     if (compare_address(pc, current_code_start) < 0
  9049.     || compare_address(pc, current_code_end) > 0)
  9050.     return XmTextPosition(-1);
  9051.  
  9052.     XmTextPosition pos = XmTextPosition(-1);
  9053.  
  9054.     int i = 0;
  9055.     while (i < int(current_code.length()))
  9056.     {
  9057.     int eol = current_code.index('\n', i);
  9058.     if (eol < 0)
  9059.         break;
  9060.  
  9061.     int j = i;
  9062.     while (j < int(current_code.length()) && isspace(current_code[j]))
  9063.         j++;
  9064.  
  9065.     if (j + 2 < int(current_code.length())
  9066.         && (is_address_start(current_code[j])))
  9067.     {
  9068.         // Use first word of line as address.  Much faster than
  9069.         // checking address regexps.
  9070.         string address = current_code.at(j, eol - j);
  9071.         int k = 0;
  9072.         while (k < int(address.length()) && !isspace(address[k]))
  9073.         k++;
  9074.         address = address.before(k);
  9075.         if (compare_address(pc, address) == 0)
  9076.         {
  9077.         pos = i;
  9078.         break;
  9079.         }
  9080.     }
  9081.  
  9082.     i = eol + 1;
  9083.     }
  9084.  
  9085.     return pos;
  9086. }
  9087.  
  9088.  
  9089. // Process `disassemble' output
  9090. void SourceView::refresh_codeOQC(const string& answer, void *client_data)
  9091. {
  9092.     RefreshDisassembleInfo *info = (RefreshDisassembleInfo *)client_data;
  9093.  
  9094.     if (answer == NO_GDB_ANSWER)
  9095.     {
  9096.     info->delay.outcome = "failed";
  9097.     }
  9098.     else
  9099.     {
  9100.     process_disassemble(answer);
  9101.  
  9102.     if (find_pc(info->pc) != XmTextPosition(-1))
  9103.         show_pc(info->pc, info->mode);
  9104.     }
  9105.  
  9106.     delete info;
  9107. }
  9108.  
  9109. void SourceView::normalize_address(string& addr)
  9110. {
  9111.     addr.downcase();
  9112.     if (addr.contains("0", 0))
  9113.     addr = addr.after("0");
  9114.     if (addr.contains("x", 0))
  9115.     addr = addr.after("x");
  9116.     if (addr.contains("h'", 0))
  9117.     addr = addr.after("h'");
  9118.     if (addr.contains("h", -1))
  9119.     addr = addr.before(int(addr.length()) - 1);
  9120.     addr.prepend("0x");
  9121. }
  9122.  
  9123. string SourceView::make_address(long pc)
  9124. {
  9125.     char buffer[BUFSIZ];
  9126.     sprintf(buffer, "0x%lx", (unsigned long) pc);
  9127.     return string(buffer);
  9128. }
  9129.  
  9130. // Return FUNCTION and OFFSET at ADDRESS
  9131. void SourceView::get_func_at(const string& address, string& func, int& offset)
  9132. {
  9133.     // GDB issues /i lines in the format
  9134.     // `ADDR <FUNC[+OFFSET]> INSTRUCTIONS', as in
  9135.     // `0xef7be49c <_IO_file_underflow+128>:\torcc  %o0, %g0, %o2\n'
  9136.     offset = 0;
  9137.     func = gdb_question("x /i " + address);
  9138.     if (func == NO_GDB_ANSWER)
  9139.     return;
  9140.  
  9141.     // Find func
  9142.     func = func.after("<");
  9143.     func = func.before(">");
  9144.  
  9145.     // Find offset
  9146.     int plus = func.index('+');
  9147.     if (plus >= 0)
  9148.     {
  9149.     offset = atoi(func.chars() + plus + 1);
  9150.     func = func.before(plus);
  9151.     }
  9152. }
  9153.  
  9154. // Return TRUE iff the function at PC is larger than MAX_SIZE.
  9155. bool SourceView::function_is_larger_than(string pc, int max_size)
  9156. {
  9157.     if (gdb->type() != GDB)
  9158.     return false;
  9159.  
  9160.     // Get function name at PC
  9161.     normalize_address(pc);
  9162.     string pc_func;
  9163.     int pc_offset;
  9164.     get_func_at(pc, pc_func, pc_offset);
  9165.     if (pc_func == NO_GDB_ANSWER)
  9166.     return true;        // In doubt, treat function as `too large'.
  9167.  
  9168.     if (pc_offset > max_size)
  9169.     {
  9170.     // We're already more than MAX_SIZE bytes away from the
  9171.     // function start: function is too large.
  9172.     return true;
  9173.     }
  9174.  
  9175.     // Get the function name at function start + MAX_SIZE.  If this is
  9176.     // the same name as the name at start, the function is too large.
  9177.     unsigned long pc_l = strtoul(pc.chars(), NULL, 0);
  9178.     unsigned long next_l = pc_l - pc_offset + max_size;
  9179.     if (next_l < pc_l)
  9180.     {
  9181.     // Overflow
  9182.     next_l = ULONG_MAX;
  9183.     }
  9184.     string next = make_address(next_l);
  9185.  
  9186.     string next_func;
  9187.     int next_offset;
  9188.     get_func_at(next, next_func, next_offset);
  9189.  
  9190.     if (next_func == NO_GDB_ANSWER)
  9191.     return true;        // In doubt, treat function as `too large'.
  9192.  
  9193.     if (pc_func == next_func)
  9194.     {
  9195.     // We're still within the same function: function is too large.
  9196.     return true;
  9197.     }
  9198.  
  9199.     return false;
  9200. }
  9201.  
  9202.  
  9203. // Show program counter location PC
  9204. // If MODE is given, highlight PC line.
  9205. // STOPPED indicates that the program just stopped.
  9206. // SIGNALED indicates that the program just received a signal.
  9207. void SourceView::show_pc(const string& pc, XmHighlightMode mode,
  9208.              bool stopped, bool signaled)
  9209. {
  9210.     last_shown_pc = pc;
  9211.     if (mode == XmHIGHLIGHT_SELECTED)
  9212.     last_execution_pc = pc;
  9213.  
  9214.     if (stopped)
  9215.     {
  9216.     at_lowest_frame = true;
  9217.     signal_received = signaled;
  9218.     }
  9219.  
  9220.     if (mode == XmHIGHLIGHT_SELECTED)
  9221.     undo_buffer.add_address(pc, stopped);
  9222.     else
  9223.     undo_buffer.remove_address();
  9224.     undo_buffer.add_state();
  9225.  
  9226.     if (!disassemble)
  9227.     return;
  9228.  
  9229.     // clog << "Showing PC " << pc << "\n";
  9230.  
  9231.     XmTextPosition pos = find_pc(pc);
  9232.  
  9233.     // While PC not found, look for code in cache
  9234.     for (int i = 0; 
  9235.      pos == XmTextPosition(-1) && i < code_cache.size(); 
  9236.      i++)
  9237.     {
  9238.     const CodeCacheEntry& cce = code_cache[i];
  9239.     if (compare_address(pc, cce.start) >= 0 
  9240.         && compare_address(pc, cce.end) <= 0)
  9241.     {
  9242.         set_code(cce.code, cce.start, cce.end);
  9243.         pos = find_pc(pc);
  9244.     }
  9245.     }
  9246.  
  9247.     if (pos == XmTextPosition(-1))
  9248.     {
  9249.     // PC not found in current code: disassemble location
  9250.  
  9251.     string start = pc;
  9252.     string end   = "";
  9253.     if (app_data.max_disassemble > 0 && 
  9254.         function_is_larger_than(pc, app_data.max_disassemble))
  9255.     {
  9256.         // Disassemble only MAX_DISASSEMBLE bytes after PC
  9257.         unsigned long pc_l = strtoul(pc.chars(), NULL, 0);
  9258.         unsigned long next_l = pc_l + app_data.max_disassemble;
  9259.         if (next_l < pc_l)
  9260.         {
  9261.         // Overflow
  9262.         next_l = ULONG_MAX;
  9263.         }
  9264.         end = make_address(next_l);
  9265.     }
  9266.  
  9267.     string msg = "Disassembling location " + start;
  9268.     if (end != "")
  9269.         msg += " to " + end;
  9270.  
  9271.     RefreshDisassembleInfo *info = 
  9272.         new RefreshDisassembleInfo(pc, mode, msg);
  9273.  
  9274.     gdb_command(gdb->disassemble_command(start, end), 0,
  9275.             refresh_codeOQC, (void *)info);
  9276.     return;
  9277.     }
  9278.  
  9279.     if (pos == XmTextPosition(-1))
  9280.     return;
  9281.  
  9282.     SetInsertionPosition(code_text_w, pos + indent_amount(code_text_w));
  9283.  
  9284.     XmTextPosition pos_line_end = 0;
  9285.     if (current_code != "")
  9286.     pos_line_end = current_code.index('\n', pos) + 1;
  9287.  
  9288.     // Clear old selection
  9289.     if (last_start_highlight_pc)
  9290.     {
  9291.     XmTextSetHighlight (code_text_w,
  9292.                 last_start_highlight_pc, 
  9293.                 last_end_highlight_pc,
  9294.                 XmHIGHLIGHT_NORMAL);
  9295.     last_start_highlight_pc = 0;
  9296.     last_end_highlight_pc   = 0;
  9297.     }
  9298.  
  9299.     // Mark current line
  9300.     if (mode == XmHIGHLIGHT_SELECTED)
  9301.     {
  9302.     if (!display_glyphs)
  9303.     {
  9304.         // Set new marker
  9305.         int indent = indent_amount(code_text_w);
  9306.         static const string marker = ">";
  9307.         if (last_pos_pc)
  9308.         {
  9309.         static const string no_marker = " ";
  9310.         XmTextReplace (code_text_w,
  9311.                    last_pos_pc + indent - no_marker.length(),
  9312.                    last_pos_pc + indent,
  9313.                    (String)no_marker);
  9314.         }
  9315.  
  9316.         XmTextReplace (code_text_w,
  9317.                pos + indent - marker.length(),
  9318.                pos + indent,
  9319.                (String)marker);
  9320.     
  9321.         if (pos_line_end)
  9322.         {
  9323.         XmTextSetHighlight (code_text_w,
  9324.                     pos, pos_line_end,
  9325.                     XmHIGHLIGHT_SELECTED);
  9326.  
  9327.         last_start_highlight_pc = pos;
  9328.         last_end_highlight_pc   = pos_line_end;
  9329.         }
  9330.  
  9331.         last_pos_pc = pos;
  9332.     }
  9333.     }
  9334.  
  9335.     if (mode == XmHIGHLIGHT_SELECTED)
  9336.     update_glyphs(code_text_w);
  9337. }
  9338.  
  9339. void SourceView::set_disassemble(bool set)
  9340. {
  9341.     if (disassemble != set)
  9342.     {
  9343.     disassemble = set;
  9344.  
  9345.     if (!disassemble)
  9346.     {
  9347.         unmanage_paned_child(code_form_w);
  9348.     }
  9349.     else
  9350.     {
  9351.         manage_paned_child(code_form_w);
  9352.  
  9353.         if (last_execution_pc != "")
  9354.         show_pc(last_execution_pc, XmHIGHLIGHT_SELECTED);
  9355.         else if (last_shown_pc != "")
  9356.         show_pc(last_shown_pc);
  9357.         else
  9358.         lookup(line_of_cursor());
  9359.     }
  9360.     }
  9361. }
  9362.  
  9363. void SourceView::set_all_registers(bool set)
  9364. {
  9365.     if (all_registers != set)
  9366.     {
  9367.     all_registers = set;
  9368.  
  9369.     if (all_registers_w)
  9370.         XmToggleButtonSetState(all_registers_w, (Boolean)set, False);
  9371.     if (int_registers_w)
  9372.         XmToggleButtonSetState(int_registers_w, !(Boolean)set, False);
  9373.  
  9374.     refresh_registers();
  9375.     }
  9376. }
  9377.  
  9378.  
  9379. // Some DBXes require `{ COMMAND; }', others `{ COMMAND }'.
  9380. string SourceView::command_list(string cmd)
  9381. {
  9382.     if (gdb->has_when_semicolon())
  9383.     return "{ " + cmd + "; }";
  9384.     else
  9385.     return "{ " + cmd + " }";
  9386. }
  9387.  
  9388. // Get the position of breakpoint NUM
  9389. string SourceView::bp_pos(int num)
  9390. {
  9391.     BreakPoint *bp = bp_map.get(num);
  9392.     if (bp == 0)
  9393.     return "";
  9394.     else
  9395.     return bp->pos();
  9396. }
  9397.  
  9398.  
  9399. // True iff we have some selection
  9400. bool SourceView::have_selection()
  9401. {
  9402.     XmTextPosition left, right;
  9403.  
  9404.     return (XmTextGetSelectionPosition(source_text_w, &left, &right)
  9405.         || XmTextGetSelectionPosition(code_text_w, &left, &right)) 
  9406.     && left != right;
  9407. }
  9408.  
  9409.  
  9410.  
  9411. //----------------------------------------------------------------------------
  9412. // Session stuff
  9413. //----------------------------------------------------------------------------
  9414.  
  9415. int SourceView::max_breakpoint_number = 99;
  9416.  
  9417. // Return DDD commands to restore current state (breakpoints, etc.)
  9418. bool SourceView::get_state(ostream& os)
  9419. {
  9420.     IntArray breakpoint_nrs;
  9421.     bool ok = true;
  9422.  
  9423.     // Restore breakpoints
  9424.     MapRef ref;
  9425.     for (BreakPoint *bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
  9426.     breakpoint_nrs += bp->number();
  9427.  
  9428.     if (breakpoint_nrs.size() > 0)
  9429.     {
  9430.     sort(breakpoint_nrs);
  9431.  
  9432.     // If all breakpoint numbers are less than
  9433.     // MAX_BREAKPOINT_NUMBER, insert `dummy' breakpoints that are
  9434.     // immediately deleted after creation, such that the
  9435.     // breakpoint numbers are preserved.  Otherwise, begin
  9436.     // numbering with 1.
  9437.  
  9438.     int max_number = breakpoint_nrs[breakpoint_nrs.size() - 1];
  9439.     bool restore_old_numbers = max_number < max_breakpoint_number;
  9440.  
  9441.     int num = 1;
  9442.     for (int i = 0; i < breakpoint_nrs.size(); i++)
  9443.     {
  9444.         BreakPoint *bp = bp_map.get(breakpoint_nrs[i]);
  9445.         if (restore_old_numbers)
  9446.         {
  9447.         while (num < breakpoint_nrs[i])
  9448.             ok = ok && bp->get_state(os, num++, true);
  9449.         assert(num == breakpoint_nrs[i]);
  9450.         }
  9451.         ok = ok && bp->get_state(os, num++);
  9452.     }
  9453.     }
  9454.  
  9455.     // Restore current cursor position
  9456.     switch (gdb->type())
  9457.     {
  9458.     case GDB:
  9459.     case PYDB:
  9460.     os << "info line " << line_of_cursor() << '\n';
  9461.     break;
  9462.  
  9463.     case DBX:
  9464.     case JDB:
  9465.     case PERL:
  9466.     break;            // FIXME
  9467.  
  9468.     case XDB:
  9469.     os << "v " << line_of_cursor() << '\n';
  9470.     break;
  9471.     }
  9472.  
  9473.     return ok;
  9474. }
  9475.  
  9476. void SourceView::reset_done(const string&, void *)
  9477. {
  9478.     // All breakpoints should be deleted now -- clear all other information
  9479.     clear_file_cache();
  9480.     clear_code_cache();
  9481.     clear_dbx_lookup_cache();
  9482.     current_file_name = "";
  9483.  
  9484.     // Reset execution positions
  9485.     last_execution_file = "";
  9486.     last_execution_line = 0;
  9487.     last_execution_pc   = "";
  9488.     last_shown_pc       = "";
  9489.  
  9490.     // Reset frame info
  9491.     current_frame   = -1;
  9492.     at_lowest_frame = true;
  9493. }
  9494.  
  9495. void SourceView::reset()
  9496. {
  9497.     bool reset_later = false;
  9498.  
  9499.     // Delete all breakpoints
  9500.     if (gdb->has_delete_command())
  9501.     {
  9502.     string del = gdb->delete_command();
  9503.  
  9504.     MapRef ref;
  9505.     int n = 0;
  9506.     for (BreakPoint *bp = bp_map.first(ref); bp != 0; 
  9507.          bp = bp_map.next(ref))
  9508.     {
  9509.         n++;
  9510.         del += " " + itostring(bp->number());
  9511.     }
  9512.  
  9513.     if (n > 0)
  9514.     {
  9515.         Command c(del);
  9516.         c.verbose  = false;
  9517.         c.prompt   = false;
  9518.         c.check    = true;
  9519.         c.priority = COMMAND_PRIORITY_INIT;
  9520.         c.callback = reset_done;
  9521.         gdb_command(c);
  9522.  
  9523.         reset_later = true;
  9524.     }
  9525.     }
  9526.     else if (gdb->has_clear_command())
  9527.     {
  9528.     MapRef ref;
  9529.     for (BreakPoint *bp = bp_map.first(ref); bp != 0; 
  9530.          bp = bp_map.next(ref))
  9531.     {
  9532.         Command c(clear_command(bp->pos()));
  9533.         c.verbose  = false;
  9534.         c.prompt   = false;
  9535.         c.check    = true;
  9536.         c.priority = COMMAND_PRIORITY_INIT;
  9537.  
  9538.         if (bp_map.next(ref) == 0)
  9539.         {
  9540.         // Last command
  9541.         c.callback = reset_done;
  9542.         reset_later = true;
  9543.         }
  9544.         gdb_command(c);
  9545.     }
  9546.     }
  9547.  
  9548.     if (!reset_later)
  9549.     reset_done("", 0);
  9550. }
  9551.