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
/
DataDisp.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-12-04
|
159KB
|
6,627 lines
// $Id: DataDisp.C,v 1.344.4.2 1998/12/04 15:48:26 zeller Exp $
// Data Display
// Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
// Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
// and Andreas Zeller <zeller@ips.cs.tu-bs.de>.
//
// This file is part of DDD.
//
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page,
// `http://www.cs.tu-bs.de/softech/ddd/',
// or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
char DataDisp_rcsid[] =
"$Id: DataDisp.C,v 1.344.4.2 1998/12/04 15:48:26 zeller Exp $";
// An interactive debugger is an outstanding example of what's NOT
// needed--it encourages trial-and-error hacking rather than
// systematic design, and also hides marginal people barely qualified
// for precision programming.
// -- HARLAN MILLS
//
// The debugger isn't a substitute for good thinking. But, in some
// cases, thinking isn't a substitute for a good debugger either. The
// most effective combination is good thinking and a good debugger.
//
// -- STEVE McCONNELL, Code Complete
#ifdef __GNUG__
#pragma implementation
#endif
#ifndef LOG_DISPLAYS
#define LOG_DISPLAYS 0
#endif
#ifndef LOG_COMPARE
#define LOG_COMPARE 0
#endif
//-----------------------------------------------------------------------------
// Data Display Implementation
//-----------------------------------------------------------------------------
#include "DataDisp.h"
// Misc includes
#include "AliasGE.h"
#include "AppData.h" // Constructors
#include "ArgField.h"
#include "ComboBox.h"
#include "Command.h"
#include "CompositeB.h"
#include "DestroyCB.h"
#include "DispGraph.h"
#include "DispNode.h"
#include "DispBox.h"
#include "GraphEdit.h"
#include "Graph.h"
#include "HistoryD.h"
#include "IntIntAA.h"
#include "LessTifH.h"
#include "MString.h"
#include "MakeMenu.h"
#include "Map.h"
#include "PannedGE.h"
#include "PosBuffer.h"
#include "ProgressM.h"
#include "ScrolledGE.h"
#include "SmartC.h"
#include "StringBox.h" // StringBox::fontTable
#include "StringMap.h"
#include "TagBox.h"
#include "TimeOut.h"
#include "UndoBuffer.h"
#include "VSEFlags.h"
#include "VSLLib.h"
#include "VoidArray.h"
#include "assert.h"
#include "bool.h"
#include "buttons.h"
#include "charsets.h"
#include "cmdtty.h"
#include "comm-manag.h"
#include "converters.h"
#include "cook.h"
#include "ddd.h"
#include "deref.h"
#include "disp-read.h"
#include "history.h"
#include "logo.h"
#include "mydialogs.h"
#include "post.h"
#include "regexps.h"
#include "settings.h"
#include "status.h"
#include "string-fun.h"
#include "toolbar.h"
#include "value-read.h"
#include "verify.h"
#include "version.h"
#include "windows.h"
#include "wm.h"
// Motif includes
#include <Xm/List.h>
#include <Xm/MessageB.h>
#include <Xm/ToggleB.h>
#include <Xm/RowColumn.h> // XmMenuPosition()
#include <Xm/SelectioB.h> // XmCreatePromptDialog()
#include <Xm/TextF.h> // XmTextFieldGetString()
#include <Xm/Label.h>
#include <X11/StringDefs.h>
// System includes
#include <iostream.h>
#include <fstream.h> // ofstream
#include <iomanip.h>
#include <ctype.h>
//-----------------------------------------------------------------------
// Xt Stuff
//-----------------------------------------------------------------------
XtActionsRec DataDisp::actions [] = {
{"graph-select", DataDisp::graph_selectAct},
{"graph-select-or-move", DataDisp::graph_select_or_moveAct},
{"graph-extend", DataDisp::graph_extendAct},
{"graph-extend-or-move", DataDisp::graph_extend_or_moveAct},
{"graph-toggle", DataDisp::graph_toggleAct},
{"graph-toggle-or-move", DataDisp::graph_toggle_or_moveAct},
{"graph-popup-menu", DataDisp::graph_popupAct},
{"graph-dereference", DataDisp::graph_dereferenceAct},
{"graph-detail", DataDisp::graph_detailAct},
{"graph-rotate", DataDisp::graph_rotateAct},
{"graph-dependent", DataDisp::graph_dependentAct}
};
// Popup Menu
struct GraphItms { enum Itms {SelectAll, Refresh, NewArg, New}; };
MMDesc DataDisp::graph_popup[] =
{
{"selectAll", MMPush,
{DataDisp::selectAllCB, 0}, 0, 0, 0, 0},
{"refresh", MMPush,
{DataDisp::refreshCB, 0}, 0, 0, 0, 0},
{"new_arg", MMPush | MMUnmanaged,
{DataDisp::popup_new_argCB, 0}, 0, 0, 0, 0},
{"new", MMPush,
{DataDisp::popup_newCB, 0}, 0, 0, 0, 0},
MMEnd
};
// Number of shortcut items
const int DataDisp::shortcut_items = 20;
#define SHORTCUT_MENU \
{"s1", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(1) }, 0, 0, 0, 0 }, \
{"s2", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(2) }, 0, 0, 0, 0 }, \
{"s3", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(3) }, 0, 0, 0, 0 }, \
{"s4", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(4) }, 0, 0, 0, 0 }, \
{"s5", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(5) }, 0, 0, 0, 0 }, \
{"s6", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(6) }, 0, 0, 0, 0 }, \
{"s7", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(7) }, 0, 0, 0, 0 }, \
{"s8", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(8) }, 0, 0, 0, 0 }, \
{"s9", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(9) }, 0, 0, 0, 0 }, \
{"s10", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(10) }, 0, 0, 0, 0 }, \
{"s11", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(11) }, 0, 0, 0, 0 }, \
{"s12", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(12) }, 0, 0, 0, 0 }, \
{"s13", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(13) }, 0, 0, 0, 0 }, \
{"s14", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(14) }, 0, 0, 0, 0 }, \
{"s15", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(15) }, 0, 0, 0, 0 }, \
{"s16", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(16) }, 0, 0, 0, 0 }, \
{"s17", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(17) }, 0, 0, 0, 0 }, \
{"s18", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(18) }, 0, 0, 0, 0 }, \
{"s19", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(19) }, 0, 0, 0, 0 }, \
{"s20", MMPush | MMUnmanaged, \
{ DataDisp::shortcutCB, XtPointer(20) }, 0, 0, 0, 0 }, \
{"other", MMPush, { DataDisp::dependentCB, 0 }, 0, 0, 0, 0}, \
MMSep, \
{"edit", MMPush, { dddEditShortcutsCB, 0 }, 0, 0, 0, 0}
// The menu used in the `New Display' button.
struct ShortcutItms { enum Itms {S1, S2, S3, S4, S5,
S6, S7, S8, S9, S10,
S11, S12, S13, S14, S15,
S16, S17, S18, S19, S20,
Other, Sep1, Edit,
Sep2, New2, Dereference2 }; };
MMDesc DataDisp::shortcut_menu[] =
{
SHORTCUT_MENU,
MMSep,
{"new2", MMPush, {DataDisp::displayArgCB, XtPointer(false)}, 0, 0, 0, 0 },
{"dereference2", MMPush, {DataDisp::dereferenceArgCB, 0}, 0, 0, 0, 0 },
MMEnd
};
// A stand-alone popup menu.
MMDesc DataDisp::shortcut_popup1[] = { SHORTCUT_MENU, MMEnd };
// The sub-menu in the `New Display' item.
MMDesc DataDisp::shortcut_popup2[] = { SHORTCUT_MENU, MMEnd };
struct RotateItms { enum Itms {RotateAll}; };
MMDesc DataDisp::rotate_menu[] =
{
{"rotateAll", MMPush | MMInsensitive,
{DataDisp::rotateCB, XtPointer(true)}, 0, 0, 0, 0},
MMEnd
};
struct NodeItms { enum Itms {Dereference, New, Sep1, Detail, Rotate, Set,
Sep2, Delete }; };
MMDesc DataDisp::node_popup[] =
{
{"dereference", MMPush, {DataDisp::dereferenceCB, 0}, 0, 0, 0, 0},
{"new", MMMenu, MMNoCB, DataDisp::shortcut_popup2, 0, 0, 0},
MMSep,
{"detail", MMPush,
{DataDisp::toggleDetailCB, XtPointer(-1)}, 0, 0, 0, 0},
{"rotate", MMPush,
{DataDisp::rotateCB, XtPointer(false) }, 0, 0, 0, 0},
{"set", MMPush, {DataDisp::setCB, 0}, 0, 0, 0, 0},
MMSep,
{"delete", MMPush,
{DataDisp::deleteCB, XtPointer(true)}, 0, 0, 0, 0},
MMEnd
};
struct DeleteItms { enum Itms {Cluster}; };
MMDesc DataDisp::delete_menu[] =
{
{"cluster", MMPush, {DataDisp::toggleClusterSelectedCB, 0},
0, 0, 0, 0},
MMEnd
};
struct PlotItms { enum Itms { History }; };
MMDesc DataDisp::plot_menu[] =
{
{"history", MMPush, {DataDisp::plotHistoryCB, 0},
0, 0, 0, 0},
MMEnd
};
struct CmdItms { enum Itms {New, Dereference, Plot,
Detail, Rotate, Set, Delete }; };
MMDesc DataDisp::graph_cmd_area[] =
{
{"new", MMPush,
{DataDisp::displayArgCB, XtPointer(true)},
DataDisp::shortcut_menu, 0, 0, 0 },
{"dereference", MMPush | MMInsensitive | MMUnmanaged,
{DataDisp::dereferenceArgCB, 0}, 0, 0, 0, 0},
{"plot", MMPush | MMInsensitive,
{DataDisp::plotArgCB, 0},
DataDisp::plot_menu, 0, 0, 0},
{"detail", MMPush | MMInsensitive,
{DataDisp::toggleDetailCB, XtPointer(-1)},
DataDisp::detail_menu, 0, 0, 0 },
{"rotate", MMPush | MMInsensitive,
{DataDisp::rotateCB, XtPointer(false)}, DataDisp::rotate_menu, 0, 0, 0 },
{"set", MMPush | MMInsensitive,
{DataDisp::setCB, 0}, 0, 0, 0, 0 },
{"delete", MMPush | MMInsensitive,
{DataDisp::deleteArgCB, XtPointer(true)},
DataDisp::delete_menu, 0, 0, 0 },
MMEnd
};
struct DetailItms { enum Itms { ShowMore, ShowJust,
ShowDetail, HideDetail }; };
MMDesc DataDisp::detail_menu[] =
{
{"show_more", MMPush,
{DataDisp::showMoreDetailCB, XtPointer(1) }, 0, 0, 0, 0},
{"show_just", MMPush,
{DataDisp::showDetailCB, XtPointer(1) }, 0, 0, 0, 0},
{"show_detail", MMPush,
{DataDisp::showDetailCB, XtPointer(-1) }, 0, 0, 0, 0},
{"hide_detail", MMPush,
{DataDisp::hideDetailCB, XtPointer(-1) }, 0, 0, 0, 0},
MMEnd
};
struct DisplayItms { enum Itms {New, Dereference,
ShowDetail, HideDetail, Set, Delete}; };
MMDesc DataDisp::display_area[] =
{
{"new", MMPush, {DataDisp::dependentCB, 0 }, 0, 0, 0, 0},
{"dereference", MMPush, {DataDisp::dereferenceCB, 0 }, 0, 0, 0, 0},
{"show_detail", MMPush,
{DataDisp::showDetailCB, XtPointer(-1) }, 0, 0, 0, 0},
{"hide_detail", MMPush,
{DataDisp::hideDetailCB, XtPointer(-1) }, 0, 0, 0, 0},
{"set", MMPush, {DataDisp::setCB, 0}, 0, 0, 0, 0},
{"delete", MMPush | MMHelp,
{DataDisp::deleteCB, XtPointer(true) }, 0, 0, 0, 0},
MMEnd
};
DispGraph *DataDisp::disp_graph = 0;
Widget DataDisp::graph_edit = 0;
Widget DataDisp::graph_form_w = 0;
Widget DataDisp::last_origin = 0;
ArgField *DataDisp::graph_arg = 0;
Widget DataDisp::graph_cmd_w = 0;
Widget DataDisp::graph_selection_w = 0;
Widget DataDisp::edit_displays_dialog_w = 0;
Widget DataDisp::display_list_w = 0;
Widget DataDisp::graph_popup_w = 0;
Widget DataDisp::node_popup_w = 0;
Widget DataDisp::shortcut_popup_w = 0;
bool DataDisp::detect_aliases = false;
bool DataDisp::cluster_displays = false;
bool DataDisp::arg_needs_update = false;
int DataDisp::next_ddd_display_number = 1;
int DataDisp::next_gdb_display_number = 1;
XtIntervalId DataDisp::refresh_args_timer = 0;
XtIntervalId DataDisp::refresh_addr_timer = 0;
XtIntervalId DataDisp::refresh_graph_edit_timer = 0;
// Array of shortcut expressions and their labels
StringArray DataDisp::shortcut_exprs;
StringArray DataDisp::shortcut_labels;
//----------------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------------
// Return A <= B
inline bool default_le(int a, int b) { return a <= b; }
// If A and B are both negative, reverse order. This way, we'll get
// -1, -2, -3, -4, ..., 0, 1, 2, 3, 4 when sorting.
inline bool absolute_le(int a, int b)
{
if (a < 0 && b < 0)
return b <= a;
else
return a <= b;
}
// Sort A
static void sort(IntArray& a, bool (*le)(int, int) = default_le)
{
// Shell sort -- simple and fast
int h = 1;
do {
h = h * 3 + 1;
} while (h <= a.size());
do {
h /= 3;
for (int i = h; i < a.size(); i++)
{
int v = a[i];
int j;
for (j = i; j >= h && !le(a[j - h], v); j -= h)
a[j] = a[j - h];
if (i != j)
a[j] = v;
}
} while (h != 1);
}
//----------------------------------------------------------------------------
// Origin
//-----------------------------------------------------------------------------
void DataDisp::ClearOriginCB(Widget w, XtPointer, XtPointer)
{
if (last_origin == w)
{
last_origin = 0;
}
}
void DataDisp::set_last_origin(Widget w)
{
if (last_origin != 0)
{
XtRemoveCallback(last_origin, XtNdestroyCallback, ClearOriginCB, 0);
}
last_origin = find_shell(w);
if (last_origin != 0)
{
XtAddCallback(last_origin, XtNdestroyCallback, ClearOriginCB, 0);
}
}
//----------------------------------------------------------------------------
// DispNode functions
//-----------------------------------------------------------------------------
bool DataDisp::selected(DispNode *dn)
{
// Don't treat a cluster as selected if only a member is selected
if (is_cluster(dn) && dn->selected() && dn->selected_value() != 0)
return false;
else
return dn->selected();
}
bool DataDisp::needs_refresh(DispNode *cluster)
{
if (!is_cluster(cluster))
return true;
if (cluster->last_refresh() == 0)
return true;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->clustered() == cluster->disp_nr() &&
dn->last_refresh() > cluster->last_refresh())
return true;
}
return false;
}
//----------------------------------------------------------------------------
// Counters
//-----------------------------------------------------------------------------
// Count the number of data displays
int DataDisp::count_data_displays()
{
int count = 0;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->is_user_command() && !dn->deferred())
count++;
}
return count;
}
// Get all display numbers
void DataDisp::get_all_display_numbers(IntArray& numbers)
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->deferred())
numbers += dn->disp_nr();
}
}
// Get all clusters
void DataDisp::get_all_clusters(IntArray& numbers)
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (is_cluster(dn))
numbers += dn->disp_nr();
}
}
//-----------------------------------------------------------------------------
// Button Callbacks
//-----------------------------------------------------------------------------
void DataDisp::dereferenceCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
set_last_origin(w);
DispNode *disp_node_arg = selected_node();
DispValue *disp_value_arg = selected_value();
if (disp_node_arg == 0 || disp_value_arg == 0)
{
newCB(w, client_data, call_data);
return;
}
string display_expression = disp_value_arg->dereferenced_name();
disp_value_arg->dereference();
disp_node_arg->refresh();
string depends_on;
if (gdb->recording())
depends_on = disp_node_arg->name();
else
depends_on = itostring(disp_node_arg->disp_nr());
new_display(display_expression, 0, depends_on, false, false, w);
}
// Replace node by its dereferenced variant
void DataDisp::dereferenceInPlaceCB(Widget w, XtPointer, XtPointer)
{
DispNode *disp_node_arg = selected_node();
DispValue *disp_value_arg = selected_value();
if (disp_node_arg == 0 || disp_value_arg == 0)
return;
string display_expression = disp_value_arg->dereferenced_name();
static BoxPoint p;
p = disp_node_arg->pos();
new_display(display_expression, &p, "", false, false, w);
IntArray nrs;
nrs += disp_node_arg->disp_nr();
delete_display(nrs, w);
}
void DataDisp::dereferenceArgCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
if (selected_value() != 0)
{
dereferenceCB(w, client_data, call_data);
return;
}
new_display(deref(source_arg->get_string()), 0, "", false, false, w);
}
void DataDisp::toggleDetailCB(Widget dialog,
XtPointer client_data,
XtPointer call_data)
{
if (gdb->recording())
{
showDetailCB(dialog, client_data, call_data);
return;
}
int depth = (int)(long)client_data;
set_last_origin(dialog);
IntArray disable_nrs;
IntArray enable_nrs;
bool changed = false;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (is_cluster(dn))
continue;
if (selected(dn))
{
DispValue *dv = dn->selected_value();
if (dv == 0)
dv = dn->value();
if (dv == 0 || dn->disabled() || dv->collapsedAll() > 0)
{
if (dv != 0)
{
// Expand this value
dv->collapseAll();
dv->expandAll(depth);
}
if (dn->disabled())
{
// Enable display
enable_nrs += dn->disp_nr();
}
else
{
dn->refresh();
changed = true;
}
}
else
{
// Collapse this value
dv->collapse();
if (dv == dn->value() && dn->enabled())
{
// Disable display
disable_nrs += dn->disp_nr();
}
else
{
dn->refresh();
changed = true;
}
}
}
}
if (enable_nrs.size() > 0)
enable_display(enable_nrs, dialog);
else if (disable_nrs.size() > 0)
disable_display(disable_nrs, dialog);
if (changed)
refresh_graph_edit();
}
void DataDisp::showDetailCB (Widget dialog, XtPointer client_data, XtPointer)
{
int depth = (int)(long)client_data;
show(dialog, depth, 0);
}
void DataDisp::showMoreDetailCB(Widget dialog, XtPointer client_data,
XtPointer)
{
int more = (int)(long)client_data;
show(dialog, 0, more);
}
void DataDisp::show(Widget dialog, int depth, int more)
{
set_last_origin(dialog);
if (gdb->recording())
{
gdb_command("graph enable display " + source_arg->get_string());
return;
}
IntArray disp_nrs;
bool changed = false;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (selected(dn))
{
if (dn->disabled())
{
// Enable display
disp_nrs += dn->disp_nr();
}
DispValue *dv = dn->selected_value();
if (dv == 0)
dv = dn->value();
if (dv == 0)
continue;
if (more != 0)
depth = dv->heightExpanded() + more;
if (depth > 0 || dv->collapsedAll() > 0)
{
dv->collapseAll();
dv->expandAll(depth);
dn->refresh();
// Mark as enabled right now; this way, the
// `enable display' command won't re-expand it.
dn->enable();
changed = true;
}
}
}
enable_display(disp_nrs, dialog);
if (changed)
refresh_graph_edit();
}
void DataDisp::hideDetailCB (Widget dialog, XtPointer, XtPointer)
{
set_last_origin(dialog);
if (gdb->recording())
{
gdb_command("graph disable display " + source_arg->get_string());
return;
}
IntArray disp_nrs;
bool changed = false;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (selected(dn))
{
DispValue *dv = dn->selected_value();
if (dv == 0)
dv = dn->value();
if ((dv == 0 || dv == dn->value()) && dn->enabled())
{
// Disable display
disp_nrs += dn->disp_nr();
}
if (dv != 0 && dv->expanded())
{
dv->collapse();
dn->refresh();
changed = true;
}
}
}
disable_display(disp_nrs, dialog);
if (changed)
refresh_graph_edit();
}
void DataDisp::toggle_rotate(DispValue *dv, bool all)
{
if (dv == 0)
return;
if (dv->horizontal_aligned())
dv->align_vertical();
else
dv->align_horizontal();
if (all)
for (int i = 0; i < dv->nchildren(); i++)
toggle_rotate(dv->child(i), all);
}
void DataDisp::rotateCB(Widget w, XtPointer client_data, XtPointer)
{
bool rotate_all = bool(client_data);
set_last_origin(w);
DispNode *disp_node_arg = selected_node();
DispValue *disp_value_arg = selected_value();
if (disp_node_arg == 0 || disp_value_arg == 0)
return;
toggle_rotate(disp_value_arg, rotate_all);
if (disp_value_arg->type() == Simple)
{
// We have rotated a scalar value in a plot. Replot.
if (disp_node_arg->clustered())
{
DispNode *cluster = disp_graph->get(disp_node_arg->clustered());
if (cluster != 0 && cluster->value() != 0)
cluster->value()->replot();
}
else if (disp_node_arg->value() != 0)
{
disp_node_arg->value()->replot();
}
}
disp_node_arg->refresh();
refresh_graph_edit();
}
void DataDisp::toggleDisableCB (Widget dialog, XtPointer, XtPointer)
{
set_last_origin(dialog);
IntArray disp_nrs;
bool do_enable = true;
bool do_disable = true;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (selected(dn))
{
disp_nrs += dn->disp_nr();
if (dn->enabled())
do_enable = false;
if (dn->disabled())
do_disable = false;
}
}
if (do_enable)
enable_display(disp_nrs, dialog);
else if (do_disable)
disable_display(disp_nrs, dialog);
}
void DataDisp::select_with_all_descendants(GraphNode *node)
{
bool selected = node->selected();
DispNode *dn = ptr_cast(DispNode, node);
if (dn != 0)
dn->select(0);
if (!selected)
{
node->selected() = true;
for (GraphEdge *edge = node->firstFrom();
edge != 0; edge = node->nextFrom(edge))
select_with_all_descendants(edge->to());
}
}
void DataDisp::select_with_all_ancestors(GraphNode *node)
{
bool selected = node->selected();
DispNode *dn = ptr_cast(DispNode, node);
if (dn != 0)
dn->select(0);
if (!selected)
{
node->selected() = true;
for (GraphEdge *edge = node->firstTo();
edge != 0; edge = node->nextTo(edge))
select_with_all_ancestors(edge->from());
}
}
// Upon deletion, select the ancestor and all siblings
void DataDisp::deleteCB (Widget dialog, XtPointer client_data, XtPointer)
{
set_last_origin(dialog);
IntArray disp_nrs;
VarArray<GraphNode *> ancestors;
VarArray<GraphNode *> descendants;
bool delete_from_display_part = bool((int)(long)client_data);
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
DispValue *dv = dn->selected_value();
if (selected(dn) && (delete_from_display_part || dv == 0))
{
disp_nrs += dn->disp_nr();
// Select all ancestors
GraphEdge *edge;
for (edge = dn->firstTo(); edge != 0; edge = dn->nextTo(edge))
{
GraphNode *ancestor = edge->from();
while (ancestor->isHint())
ancestor = ancestor->firstTo()->from();
ancestors += ancestor;
}
// Select all descendants
for (edge = dn->firstFrom(); edge != 0; edge = dn->nextFrom(edge))
{
GraphNode *descendant = edge->to();
while (descendant->isHint())
descendant = descendant->firstFrom()->to();
descendants += descendant;
}
}
}
int i;
for (i = 0; i < ancestors.size(); i++)
select_with_all_descendants(ancestors[i]);
for (i = 0; i < descendants.size(); i++)
select_with_all_ancestors(descendants[i]);
delete_display(disp_nrs, dialog);
}
void DataDisp::refreshCB(Widget w, XtPointer, XtPointer)
{
// Unmerge all displays
MapRef ref;
for (int k = disp_graph->first_nr(ref);
k != 0;
k = disp_graph->next_nr(ref))
{
unmerge_display(k);
}
// Refresh them
refresh_display(w);
}
void DataDisp::selectAllCB(Widget w, XtPointer, XtPointer)
{
// StatusDelay d("Selecting all displays");
set_last_origin(w);
XtCallActionProc(graph_edit,
"select-all", (XEvent *)0, (String *)0, 0);
refresh_graph_edit();
}
void DataDisp::unselectAllCB(Widget w, XtPointer, XtPointer)
{
// StatusDelay d("Unselecting all displays");
set_last_origin(w);
XtCallActionProc(graph_edit,
"unselect-all", (XEvent *)0, (String *)0, 0);
refresh_graph_edit();
}
void DataDisp::enableCB(Widget w, XtPointer, XtPointer)
{
set_last_origin(w);
IntArray disp_nrs;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (selected(dn) && dn->disabled())
{
disp_nrs += dn->disp_nr();
}
}
enable_display(disp_nrs, w);
}
void DataDisp::disableCB(Widget w, XtPointer, XtPointer)
{
set_last_origin(w);
IntArray disp_nrs;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (selected(dn) && dn->enabled())
{
disp_nrs += dn->disp_nr();
}
}
disable_display(disp_nrs, w);
}
void DataDisp::shortcutCB(Widget w, XtPointer client_data, XtPointer)
{
int number = ((int)(long)client_data) - 1;
assert (number >= 0);
assert (number < shortcut_exprs.size());
set_last_origin(w);
string expr = shortcut_exprs[number];
string depends_on = "";
DispNode *disp_node_arg = selected_node();
DispValue *disp_value_arg = selected_value();
if (disp_node_arg != 0
&& disp_value_arg != 0
&& !disp_node_arg->hidden())
{
if (gdb->recording())
depends_on = disp_node_arg->name();
else
depends_on = itostring(disp_node_arg->disp_nr());
}
string arg = source_arg->get_string();
// Avoid multiple /format specifications
if (arg.contains('/', 0) && expr.contains('/', 0))
arg = arg.after(rxwhite);
expr.gsub("()", arg);
new_display(expr, 0, depends_on, false, false, w);
}
// Set shortcut menu to expressions EXPRS
void DataDisp::set_shortcut_menu(const StringArray& exprs,
const StringArray& labels)
{
shortcut_labels = labels;
shortcut_exprs = exprs;
while (shortcut_labels.size() < exprs.size())
shortcut_labels += "";
#if 0
if (exprs.size() > shortcut_items)
{
post_warning("Shortcut menu capacity exceeded.",
"too_many_shortcuts_warning", last_origin);
}
#endif
for (int i = 0; i < shortcut_items; i++)
{
Widget popup1_item = shortcut_popup1[i].widget;
Widget popup2_item = shortcut_popup2[i].widget;
Widget menu_item = shortcut_menu [i].widget;
if (i < exprs.size())
{
string& expr = shortcut_exprs[i];
string& label = shortcut_labels[i];
if (label == "")
label = "Display " + expr;
set_label(popup1_item, label);
set_label(popup2_item, label);
set_label(menu_item, label);
XtManageChild(popup1_item);
XtManageChild(popup2_item);
XtManageChild(menu_item);
}
else
{
// Unmanage widgets
XtUnmanageChild(popup1_item);
XtUnmanageChild(popup2_item);
XtUnmanageChild(menu_item);
}
}
refresh_args();
}
// Add one expr to shortcut menus
void DataDisp::add_shortcut_expr(const string& expr)
{
// Insert as first item in SHORTCUT_EXPRS
shortcut_exprs += string("");
shortcut_labels += string("");
for (int i = shortcut_exprs.size() - 1; i > 0; i--)
{
shortcut_exprs[i] = shortcut_exprs[i - 1];
shortcut_labels[i] = shortcut_labels[i - 1];
}
shortcut_exprs[0] = expr;
shortcut_labels[0] = "";
set_shortcut_menu(shortcut_exprs, shortcut_labels);
refresh_button_editor();
refresh_args();
}
MString DataDisp::shortcut_help(Widget w)
{
for (int i = 0; i < shortcut_items; i++)
{
if (w == shortcut_menu [i].widget ||
w == shortcut_popup1[i].widget ||
w == shortcut_popup2[i].widget)
{
MString ret = rm("Display ");
string expr = shortcut_exprs[i];
while (expr.contains("()"))
{
ret += tt(expr.before("()"));
ret += bf("()");
expr = expr.after("()");
}
ret += tt(expr);
return ret;
}
}
return MString(0, true); // Not found
}
//-----------------------------------------------------------------------------
// Count displays
//-----------------------------------------------------------------------------
struct DataDispCount {
int all; // Total # of displays
int visible; // # of non-hidden displays
int selected; // # of selected displays
int selected_expanded; // # of selected and expanded displays
int selected_collapsed; // # of selected and collapsed displays
int selected_clustered; // # of selected clustered data displays
int selected_unclustered; // # of selected unclustered data displays
int selected_titles; // # of selected titles (no display parts)
DataDispCount(DispGraph *disp_graph);
};
DataDispCount::DataDispCount(DispGraph *disp_graph)
: all(0), visible(0), selected(0),
selected_expanded(0),
selected_collapsed(0),
selected_clustered(0),
selected_unclustered(0),
selected_titles(0)
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
all++;
if (!dn->hidden())
visible++;
if (DataDisp::selected(dn))
{
selected++;
if (dn->deferred())
{
selected_titles++;
}
else
{
DispValue *dv = dn->selected_value();
if (dv == 0 || dv == dn->value())
selected_titles++;
if (!dn->is_user_command() && !dn->clustered())
selected_unclustered++;
if (!dn->is_user_command() && dn->clustered())
selected_clustered++;
if (dn->disabled())
{
selected_collapsed++;
}
else
{
if (dv == 0)
dv = dn->value();
if (dv != 0)
{
selected_expanded += int(dv->expanded());
selected_collapsed += dv->collapsedAll();
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Double click callback
//-----------------------------------------------------------------------------
void DataDisp::DoubleClickCB(Widget w, XtPointer, XtPointer call_data)
{
GraphEditPreSelectionInfo *info = (GraphEditPreSelectionInfo *)call_data;
if (!info->double_click)
return; // Single click
if (info->node == 0)
return; // Double-click on background
DispNode *disp_node_arg = ptr_cast(DispNode, info->node);
if (disp_node_arg == 0)
disp_node_arg = selected_node();
if (disp_node_arg == 0)
return;
XEvent *ev = info->event;
bool control = (ev != 0 &&
(ev->type == ButtonPress || ev->type == ButtonRelease) &&
(ev->xbutton.state & ControlMask) != 0);
// Do the right thing
if (disp_node_arg->disabled())
{
showMoreDetailCB(w, XtPointer(1), 0); // Show 1 level more
}
else
{
DispValue *disp_value_arg = disp_node_arg->selected_value();
if (disp_value_arg == 0)
return; // No selected value within node
DataDispCount count(disp_graph);
if (disp_value_arg->type() == Pointer && !disp_value_arg->collapsed())
{
// Dereference
if (control)
dereferenceInPlaceCB(w, XtPointer(true), 0);
else
dereferenceCB(w, 0, 0);
}
else if (count.selected_collapsed > 0)
{
// Show 1 level more
showMoreDetailCB(w, XtPointer(1), 0);
}
else
{
// Hide all
hideDetailCB(w, XtPointer(-1), 0);
}
}
// Don't do the default action
info->doit = False;
}
//-----------------------------------------------------------------------------
// Popup menu callbacks
//-----------------------------------------------------------------------------
void DataDisp::popup_new_argCB (Widget display_dialog,
XtPointer client_data,
XtPointer)
{
set_last_origin(display_dialog);
BoxPoint *p = (BoxPoint *) client_data;
new_display(source_arg->get_string(), p, "", false, false, display_dialog);
}
void DataDisp::popup_newCB (Widget display_dialog,
XtPointer client_data,
XtPointer)
{
set_last_origin(display_dialog);
BoxPoint *p = (BoxPoint *) client_data;
new_displayCD(display_dialog, *p);
}
//-----------------------------------------------------------------------------
// Entering new Data Displays
//-----------------------------------------------------------------------------
class NewDisplayInfo {
public:
string display_expression;
string scope;
StringArray display_expressions;
BoxPoint point;
BoxPoint *point_ptr;
string depends_on;
Widget origin;
Widget shortcut;
Widget text;
bool verbose;
bool prompt;
bool constant;
DeferMode deferred;
bool clustered;
bool plotted;
NewDisplayInfo()
: display_expression(),
scope(),
display_expressions(),
point(),
point_ptr(0),
depends_on(),
origin(0),
shortcut(0),
text(0),
verbose(false),
prompt(false),
constant(false),
deferred(DeferNever),
clustered(false),
plotted(false)
{}
~NewDisplayInfo()
{}
private:
NewDisplayInfo(const NewDisplayInfo&)
: display_expression(),
scope(),
display_expressions(),
point(),
point_ptr(0),
depends_on(),
origin(0),
shortcut(0),
text(0),
verbose(false),
prompt(false),
constant(false),
deferred(DeferNever),
clustered(false),
plotted(false)
{
assert(0);
}
NewDisplayInfo& operator = (const NewDisplayInfo&)
{
assert(0); return *this;
}
};
void DataDisp::new_displayDCB (Widget dialog, XtPointer client_data, XtPointer)
{
set_last_origin(dialog);
NewDisplayInfo *info = (NewDisplayInfo *)client_data;
char *inp = XmTextFieldGetString(info->text);
string expr(inp);
XtFree(inp);
strip_leading_space(expr);
strip_trailing_space(expr);
if (expr != "")
{
new_display(expr, info->point_ptr, info->depends_on, info->clustered,
info->plotted, info->origin);
if (info->shortcut != 0 && XmToggleButtonGetState(info->shortcut))
{
// Add expression to shortcut menu
expr.gsub("()", "( )");
if (expr != info->display_expression)
expr.gsub(info->display_expression, string("()"));
add_shortcut_expr(expr);
}
}
}
Widget DataDisp::create_display_dialog(Widget parent, String name,
NewDisplayInfo& info)
{
Arg args[10];
int arg = 0;
Widget dialog = verify(XmCreatePromptDialog(find_shell(parent),
name, args, arg));
Delay::register_shell(dialog);
if (lesstif_version <= 79)
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL));
XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
XtAddCallback(dialog, XmNokCallback, new_displayDCB, XtPointer(&info));
arg = 0;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
XtSetArg(args[arg], XmNborderWidth, 0); arg++;
XtSetArg(args[arg], XmNadjustMargin, False); arg++;
Widget box = verify(XmCreateRowColumn(dialog, "box", args, arg));
XtManageChild(box);
arg = 0;
XtSetArg(args[arg], XmNalignment, XmALIGNMENT_BEGINNING); arg++;
Widget label = verify(XmCreateLabel(box, "label", args, arg));
XtManageChild(label);
arg = 0;
info.text = verify(CreateComboBox(box, "text", args, arg));
XtManageChild(info.text);
tie_combo_box_to_history(info.text, display_history_filter);
arg = 0;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
XtSetArg(args[arg], XmNborderWidth, 0); arg++;
XtSetArg(args[arg], XmNadjustMargin, False); arg++;
XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
Widget box2 = verify(XmCreateRowColumn(box, "box2", args, arg));
XtManageChild(box2);
arg = 0;
XtSetArg(args[arg], XmNalignment, XmALIGNMENT_BEGINNING); arg++;
info.shortcut = verify(XmCreateToggleButton(box2, "shortcut", args, arg));
XtManageChild(info.shortcut);
Widget display = verify(XmCreateLabel(box2, "display", args, arg));
XtManageChild(display);
Widget menu = verify(XmCreateLabel(box2, "menu", args, arg));
XtManageChild(menu);
return dialog;
}
// Enter a new Display at BOX_POINT
void DataDisp::new_displayCD (Widget w, BoxPoint box_point)
{
static NewDisplayInfo info;
if (info.point_ptr == 0)
info.point_ptr = new BoxPoint;
info.origin = w;
static Widget new_display_dialog =
create_display_dialog(w, "new_display_dialog", info);
XmToggleButtonSetState(info.shortcut, False, False);
*(info.point_ptr) = box_point;
info.display_expression = source_arg->get_string();
XmTextSetString(info.text, info.display_expression);
manage_and_raise(new_display_dialog);
}
// Create a new display
void DataDisp::newCB(Widget w, XtPointer, XtPointer)
{
set_last_origin(w);
new_displayCD(w);
}
// Create a new dependent display
void DataDisp::dependentCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
set_last_origin(w);
DispNode *disp_node_arg = selected_node();
DispValue *disp_value_arg = selected_value();
if (disp_node_arg == 0
|| disp_value_arg == 0
|| disp_node_arg->hidden())
{
newCB(w, client_data, call_data);
return;
}
static NewDisplayInfo info;
if (gdb->recording())
info.depends_on = disp_node_arg->name();
else
info.depends_on = itostring(disp_node_arg->disp_nr());
info.origin = w;
static Widget dependent_display_dialog =
create_display_dialog(w, "dependent_display_dialog", info);
XmToggleButtonSetState(info.shortcut, True, False);
info.display_expression = disp_value_arg->full_name();
XmTextSetString(info.text, info.display_expression);
manage_and_raise(dependent_display_dialog);
}
void DataDisp::displayArgCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
bool check_pointer = bool((int)(long)client_data);
if (check_pointer)
{
DispValue *disp_value_arg = selected_value();
if (disp_value_arg != 0 && disp_value_arg->type() == Pointer)
{
// Dereference selected pointer
dereferenceCB(w, client_data, call_data);
return;
}
}
// Create new display
string arg = source_arg->get_string();
string depends_on = "";
DispNode *disp_node_arg = selected_node();
if (disp_node_arg != 0)
{
if (gdb->recording())
depends_on = disp_node_arg->name();
else
depends_on = itostring(disp_node_arg->disp_nr());
}
new_display(arg, 0, depends_on, false, false, w);
}
void DataDisp::plotArgCB(Widget w, XtPointer, XtPointer)
{
DispValue *disp_value_arg = selected_value();
if (disp_value_arg != 0)
{
// Plot selected value
disp_value_arg->plot();
return;
}
// Create new display and plot it
string arg = source_arg->get_string();
new_display(arg, 0, "", false, true, w);
}
void DataDisp::plotHistoryCB(Widget w, XtPointer, XtPointer)
{
// Create new display and plot its history
string arg = "`graph history " + source_arg->get_string() + "`";
new_display(arg, 0, "", false, true, w);
}
void DataDisp::deleteArgCB(Widget dialog, XtPointer client_data,
XtPointer call_data)
{
DataDispCount count(disp_graph);
if (count.selected_titles > 0)
{
// Delete selected displays
deleteCB(dialog, client_data, call_data);
}
else
{
// Delete argument
delete_display(source_arg->get_string());
}
}
//-----------------------------------------------------------------------------
// Redraw graph and update display list
//-----------------------------------------------------------------------------
struct GraphEditState {
Boolean autoLayout;
Boolean snapToGrid;
};
void DataDisp::refresh_graph_edit(bool silent)
{
// Save current graph editor state
static GraphEditState state;
XtVaGetValues(graph_edit,
XtNautoLayout, &state.autoLayout,
XtNsnapToGrid, &state.snapToGrid,
NULL);
if (refresh_graph_edit_timer == 0)
{
refresh_graph_edit_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(graph_edit),
0, RefreshGraphEditCB, XtPointer(&state));
}
refresh_builtin_user_displays();
refresh_args();
refresh_display_list(silent);
}
void DataDisp::RefreshGraphEditCB(XtPointer client_data, XtIntervalId *id)
{
(void) id; // Use it
assert(*id == refresh_graph_edit_timer);
refresh_graph_edit_timer = 0;
static GraphEditState state;
XtVaGetValues(graph_edit,
XtNautoLayout, &state.autoLayout,
XtNsnapToGrid, &state.snapToGrid,
NULL);
const GraphEditState& old_state = *((GraphEditState *) client_data);
static Graph *dummy = new Graph;
XtVaSetValues(graph_edit,
XtNautoLayout, old_state.autoLayout,
XtNsnapToGrid, old_state.snapToGrid,
XtNgraph, dummy,
NULL);
XtVaSetValues(graph_edit,
XtNgraph, (Graph *)disp_graph,
NULL);
XtVaSetValues(graph_edit,
XtNautoLayout, state.autoLayout,
XtNsnapToGrid, state.snapToGrid,
NULL);
}
// ***************************************************************************
//
inline int DataDisp::getDispNrAtPoint (BoxPoint point)
{
GraphNode* gn = graphEditGetNodeAtPoint (graph_edit, point);
if (gn == 0)
return 0;
BoxGraphNode* bgn = ptr_cast (BoxGraphNode, gn);
if (bgn == 0)
return 0;
return disp_graph->get_nr (bgn);
}
//-----------------------------------------------------------------------------
// Make buttons sensitive or insensitive
//-----------------------------------------------------------------------------
void DataDisp::no_displaysHP (void*, void* , void* call_data)
{
bool empty = bool(call_data);
set_sensitive (graph_popup[GraphItms::Refresh].widget,
(!empty && gdb->isReadyWithPrompt()));
}
//-----------------------------------------------------------------------------
// Unselect nodes when selection is lost
//-----------------------------------------------------------------------------
bool DataDisp::lose_selection = true;
void DataDisp::SelectionLostCB(Widget, XtPointer, XtPointer)
{
if (!lose_selection)
return;
// Selection lost - clear all highlights
bool changed = false;
for (GraphNode *gn = disp_graph->firstNode();
gn != 0; gn = disp_graph->nextNode(gn))
{
if (gn->selected())
{
gn->selected() = false;
changed = true;
graphEditRedrawNode(graph_edit, gn);
}
}
if (changed)
{
refresh_args();
refresh_display_list();
}
}
//-----------------------------------------------------------------------------
// Action procs
//----------------------------------------------------------------------------
void DataDisp::graph_dereferenceAct (Widget w, XEvent*, String*, Cardinal*)
{
dereferenceCB(w, 0, 0);
}
void DataDisp::graph_detailAct (Widget w, XEvent *,
String *params, Cardinal *num_params)
{
int depth = -1;
if (params != 0 && num_params != 0 && *num_params >= 1)
depth = atoi(params[0]);
toggleDetailCB(w, XtPointer(depth), 0);
}
void DataDisp::graph_rotateAct (Widget w, XEvent*, String*, Cardinal*)
{
rotateCB(w, XtPointer(false), 0);
}
void DataDisp::graph_dependentAct (Widget w, XEvent*, String*, Cardinal*)
{
dependentCB(w, 0, 0);
}
Time DataDisp::last_select_time = 0;
// The GraphEdit actions with some data display magic prepended
void DataDisp::call_selection_proc(Widget w,
String name,
XEvent* event,
String* args,
Cardinal num_args,
SelectionMode mode)
{
// Let multi-clicks pass right through
Time t = time(event);
if (Time(t - last_select_time) > Time(XtGetMultiClickTime(XtDisplay(w))))
set_args(point(event), mode);
last_select_time = t;
XtCallActionProc(w, name, event, args, num_args);
}
void DataDisp::graph_selectAct (Widget, XEvent* event, String* args,
Cardinal* num_args)
{
call_selection_proc(graph_edit, "select", event, args, *num_args,
SetSelection);
}
void DataDisp::graph_select_or_moveAct (Widget, XEvent* event, String* args,
Cardinal* num_args)
{
call_selection_proc(graph_edit, "select-or-move", event, args, *num_args,
SetSelection);
}
void DataDisp::graph_extendAct (Widget, XEvent* event, String* args,
Cardinal* num_args)
{
call_selection_proc(graph_edit, "extend", event, args, *num_args,
ExtendSelection);
}
void DataDisp::graph_extend_or_moveAct (Widget, XEvent* event, String* args,
Cardinal* num_args)
{
call_selection_proc(graph_edit, "extend-or-move", event, args, *num_args,
ExtendSelection);
}
void DataDisp::graph_toggleAct (Widget, XEvent* event, String* args,
Cardinal* num_args)
{
call_selection_proc(graph_edit, "toggle", event, args, *num_args,
ToggleSelection);
}
void DataDisp::graph_toggle_or_moveAct (Widget, XEvent* event, String* args,
Cardinal* num_args)
{
call_selection_proc(graph_edit, "toggle-or-move", event, args, *num_args,
ToggleSelection);
}
void DataDisp::graph_popupAct (Widget, XEvent* event, String *args,
Cardinal *num_args)
{
static BoxPoint* p = 0;
if (p == 0)
{
p = new BoxPoint;
MMaddCallbacks(graph_popup, XtPointer(p));
MMaddCallbacks(node_popup, XtPointer(p));
MMaddCallbacks(shortcut_popup1, XtPointer(p));
MMaddHelpCallback(graph_popup, ImmediateHelpCB);
MMaddHelpCallback(node_popup, ImmediateHelpCB);
MMaddHelpCallback(shortcut_popup1, ImmediateHelpCB);
}
*p = point(event);
set_args(*p, SetSelection);
string arg = "";
if (num_args != 0 && *num_args > 0)
arg = downcase(args[0]);
Widget popup = 0;
if (arg == "graph" || selected_node() == 0)
popup = graph_popup_w;
else if (arg == "shortcut"
|| (arg == "" && event->xbutton.state & ShiftMask))
popup = shortcut_popup_w;
else if (arg == "node" || arg == "")
popup = node_popup_w;
else
cerr << "graph-popup: bad argument " << quote(arg) << "\n";
if (popup != 0)
{
XmMenuPosition(popup, &event->xbutton);
XtManageChild(popup);
}
}
void DataDisp::set_args(BoxPoint p, SelectionMode mode)
{
DispNode* disp_node = 0;
DispValue* disp_value = 0;
bool was_selected = false;
int disp_nr = getDispNrAtPoint(p);
if (disp_nr)
{
disp_node = disp_graph->get (disp_nr);
disp_value = (DispValue *)disp_node->box()->data(p);
was_selected = selected(disp_node) && disp_value == 0;
switch (mode)
{
case ExtendSelection:
case ToggleSelection:
if (disp_node == selected_node()
&& disp_node->selected_value() != 0
&& disp_value != disp_node->selected_value())
{
// Add another value in this node. We can't do this,
// so toggle the entire node.
disp_node->selected() = false;
disp_node->select(0);
graphEditRedrawNode(graph_edit, disp_node);
break;
}
// FALL THROUGH
case SetSelection:
if (disp_value != disp_node->selected_value())
{
disp_node->select(disp_value);
graphEditRedrawNode(graph_edit, disp_node);
}
break;
}
}
if (mode == SetSelection)
{
// Clear other highlights and selections
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn != disp_node)
{
bool redraw = false;
if (!was_selected)
{
if (!redraw)
redraw = dn->selected();
dn->selected() = false;
}
if (!redraw)
redraw = (dn->highlight() != 0);
dn->select(0);
if (redraw)
graphEditRedrawNode(graph_edit, dn);
}
}
}
refresh_args(true);
refresh_display_list();
}
DispNode *DataDisp::selected_node()
{
DispNode *selection = 0;
// Check for selected nodes
MapRef ref;
for (DispNode *dn = disp_graph->first(ref);
dn != 0; dn = disp_graph->next(ref))
{
if (!selected(dn))
continue;
if (selection == 0)
{
// First node found
selection = dn;
}
else if (selection == dn)
{
// Already found
}
else if (is_cluster(dn))
{
if (selection->clustered() != dn->disp_nr())
return 0; // Node is unclustered or clustered elsewhere
// Select cluster
selection = dn;
}
else if (is_cluster(selection))
{
if (dn->clustered() != selection->disp_nr())
return 0; // Node is unclustered or clustered elsewhere
// Cluster remains selected
}
else
{
return 0; // Differing nodes
}
}
return selection;
}
DispValue *DataDisp::selected_value()
{
DispNode *dn = selected_node();
if (dn == 0)
return 0;
if (is_cluster(dn))
return dn->value();
DispValue *dv = dn->selected_value();
if (dv != 0)
return dv;
// We treat the selected node just like the selected top value
return dn->value();
}
// Select DV (and nothing else)
void DataDisp::select(DispValue *dv)
{
bool changed = false;
MapRef ref;
for (DispNode *dn = disp_graph->first(ref);
dn != 0; dn = disp_graph->next(ref))
{
if (dn->clustered())
continue;
// Check whether DV occurs in DN
dn->select(dv);
if (dn->highlight() != 0)
{
if (!dn->selected())
{
// Found it
dn->selected() = true;
changed = true;
}
}
else
{
if (dn->selected())
{
// Did not find it
dn->selected() = false;
changed = true;
}
dn->select(0);
}
}
if (changed)
{
refresh_args();
refresh_graph_edit();
}
}
void DataDisp::refresh_args(bool update_arg)
{
if (update_arg)
arg_needs_update = true;
if (refresh_args_timer == 0)
{
refresh_args_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(graph_edit),
0, RefreshArgsCB, XtPointer(graph_edit));
}
// Synchronize node selection with cluster: if cluster is
// selected, select all contained nodes, too.
MapRef ref;
for (DispNode *dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->clustered())
continue;
DispNode *cluster = disp_graph->get(dn->clustered());
if (cluster == 0)
continue;
Box *old_highlight = dn->highlight();
bool old_selected = dn->selected();
dn->selected() = false;
if (cluster->selected())
{
// Cluster is selected -- select display, too
if (cluster->selected_value() == 0 ||
cluster->selected_value() == cluster->value())
{
// Entire cluster is selected
dn->selected() = true;
dn->select(0);
}
else
{
// Part of cluster is selected
dn->select(cluster->selected_value());
if (dn->highlight() != 0)
{
// Found selected value in display
dn->selected() = true;
if (dn->selected_value() == dn->value())
{
// Top value is selected
dn->select(0);
}
}
}
}
if (dn->selected() != old_selected || dn->highlight() != old_highlight)
{
graphEditRedrawNode(graph_edit, dn);
}
}
}
void DataDisp::RefreshArgsCB(XtPointer, XtIntervalId *timer_id)
{
(void) timer_id; // Use it
assert(*timer_id == refresh_args_timer);
refresh_args_timer = 0;
DataDispCount count(disp_graph);
if (count.selected > 1)
{
// Clear all local highlights
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
bool redraw = (dn->highlight() != 0);
dn->select(0);
if (redraw)
graphEditRedrawNode(graph_edit, dn);
}
}
DispNode *disp_node_arg = selected_node();
DispValue *disp_value_arg = selected_value();
// New ()
set_sensitive(graph_popup[GraphItms::NewArg].widget, !source_arg->empty());
// Refresh (), Select All ()
set_sensitive(graph_popup[GraphItms::Refresh].widget, count.all > 0);
set_sensitive(graph_popup[GraphItms::SelectAll].widget, count.visible > 0);
bool dereference_ok = false;
bool rotate_ok = false;
bool rotate_plot_ok = false;
if (disp_node_arg != 0 && disp_value_arg != 0)
{
// We have selected a single node
switch (disp_value_arg->type())
{
case Simple:
rotate_plot_ok = disp_value_arg->has_plot_alignment();
break;
case Text:
case Reference:
break;
case Pointer:
dereference_ok = true;
break;
case Array:
rotate_ok = disp_value_arg->expanded();
break;
case Sequence:
case List:
case Struct:
break;
case UnknownType:
assert(0);
abort();
}
}
// Earlier state
bool undoing = undo_buffer.showing_earlier_state();
// Argument
bool arg_ok = false;
bool plot_ok = false;
string arg;
if (disp_node_arg != 0)
{
if (disp_value_arg != 0)
{
arg = disp_value_arg->full_name();
arg_ok = true;
plot_ok = disp_value_arg->can_plot();
}
}
else
{
// No node selected
arg = source_arg->get_string();
arg_ok = (arg != "") && !is_file_pos(arg);
plot_ok = arg_ok && !undoing;
}
// New
#if 0
if (count.selected_titles > 0)
{
set_label(graph_cmd_area[CmdItms::New].widget,
"Undisplay ()", UNDISPLAY_ICON);
}
else
#endif
if (dereference_ok)
{
string label("Display " + deref(arg, "()"));
set_label(graph_cmd_area[CmdItms::New].widget, label, DISPREF_ICON);
}
else
{
set_label(graph_cmd_area[CmdItms::New].widget,
"Display ()", DISPLAY_ICON);
}
bool recording = gdb->recording() && emptyCommandQueue();
bool record_ok = recording && arg_ok;
set_sensitive(shortcut_menu[ShortcutItms::New2].widget,
arg_ok && !undoing);
set_sensitive(graph_cmd_area[CmdItms::New].widget, arg_ok && !undoing);
set_sensitive(display_area[DisplayItms::New].widget, !undoing);
// Dereference
set_sensitive(node_popup[NodeItms::Dereference].widget,
dereference_ok && !undoing);
set_sensitive(shortcut_menu[ShortcutItms::Dereference2].widget,
(record_ok || dereference_ok ||
(count.selected == 0 && arg_ok)) && !undoing);
set_sensitive(graph_cmd_area[CmdItms::Dereference].widget,
(dereference_ok || (count.selected == 0 && arg_ok)) &&
!undoing);
set_sensitive(display_area[DisplayItms::Dereference].widget,
dereference_ok && !undoing);
// Plot
bool arg_is_displayed = (display_number(source_arg->get_string()) != 0);
bool can_delete_arg = (count.selected == 0 && arg_is_displayed ||
record_ok ||
count.selected_titles > 0);
set_sensitive(graph_cmd_area[CmdItms::Plot].widget, plot_ok);
set_sensitive(plot_menu[PlotItms::History].widget, can_delete_arg);
// Rotate
set_sensitive(node_popup[NodeItms::Rotate].widget,
rotate_ok || rotate_plot_ok);
set_sensitive(graph_cmd_area[CmdItms::Rotate].widget,
rotate_ok || rotate_plot_ok);
set_sensitive(rotate_menu[RotateItms::RotateAll].widget, rotate_ok);
// Show/Hide Detail
if (recording)
{
// Recording
set_label(node_popup[NodeItms::Detail].widget, "Show All");
set_label(graph_cmd_area[CmdItms::Detail].widget,
"Show ()", SHOW_ICON);
set_sensitive(node_popup[NodeItms::Detail].widget, record_ok);
set_sensitive(graph_cmd_area[CmdItms::Detail].widget, record_ok);
}
else if (count.selected_expanded > 0 && count.selected_collapsed == 0)
{
// Only expanded displays selected
set_label(node_popup[NodeItms::Detail].widget, "Hide All");
set_label(graph_cmd_area[CmdItms::Detail].widget,
"Hide ()", HIDE_ICON);
set_sensitive(node_popup[NodeItms::Detail].widget, true);
set_sensitive(graph_cmd_area[CmdItms::Detail].widget, true);
}
else if (count.selected_collapsed > 0)
{
// Some collapsed displays selected
set_label(node_popup[NodeItms::Detail].widget, "Show All");
set_label(graph_cmd_area[CmdItms::Detail].widget,
"Show ()", SHOW_ICON);
set_sensitive(node_popup[NodeItms::Detail].widget, true);
set_sensitive(graph_cmd_area[CmdItms::Detail].widget, true);
}
else
{
// Expanded as well as collapsed displays selected
set_sensitive(node_popup[NodeItms::Detail].widget, false);
set_sensitive(graph_cmd_area[CmdItms::Detail].widget, false);
}
set_sensitive(display_area[DisplayItms::ShowDetail].widget,
record_ok || count.selected_collapsed > 0);
set_sensitive(display_area[DisplayItms::HideDetail].widget,
record_ok || count.selected_expanded > 0);
set_sensitive(detail_menu[DetailItms::ShowMore].widget,
record_ok || count.selected_collapsed > 0);
set_sensitive(detail_menu[DetailItms::ShowJust].widget,
record_ok || count.selected > 0);
set_sensitive(detail_menu[DetailItms::ShowDetail].widget,
record_ok || count.selected_collapsed > 0);
set_sensitive(detail_menu[DetailItms::HideDetail].widget,
record_ok || count.selected_expanded > 0);
// Delete
set_sensitive(graph_cmd_area[CmdItms::Delete].widget, can_delete_arg);
set_sensitive(display_area[DisplayItms::Delete].widget,
count.selected > 0);
// Set
bool can_set = gdb->has_assign_command() && !undoing;
bool set_node_ok =
disp_node_arg != 0 &&
(!disp_node_arg->is_user_command() ||
disp_value_arg != 0 && disp_value_arg != disp_node_arg->value());
bool set_arg_ok = (disp_node_arg == 0 && arg_ok && !is_user_command(arg));
set_sensitive(graph_cmd_area[CmdItms::Set].widget,
can_set && (set_arg_ok || set_node_ok));
set_sensitive(display_area[DisplayItms::Set].widget,
can_set && (set_arg_ok || set_node_ok));
set_sensitive(node_popup[NodeItms::Set].widget,
can_set && set_node_ok);
// Cluster
if (count.selected_unclustered > 0 || count.selected_clustered == 0)
{
set_label(delete_menu[DeleteItms::Cluster].widget, "Cluster ()");
set_sensitive(delete_menu[DeleteItms::Cluster].widget,
count.selected_unclustered > 0);
}
else
{
set_label(delete_menu[DeleteItms::Cluster].widget, "Uncluster ()");
set_sensitive(delete_menu[DeleteItms::Cluster].widget, true);
}
// Shortcut menu
for (int i = 0; i < shortcut_items && i < shortcut_exprs.size(); i++)
{
const string& expr = shortcut_exprs[i];
bool sens = false;
if (!expr.contains("()"))
sens = true; // Argument not needed
else if (arg_ok)
sens = true; // We have an argument
else if (count.selected == 0)
sens = false; // Nothing selected
else if (disp_value_arg != 0)
sens = true; // Exactly one value selected
else if (disp_node_arg != 0)
sens = true; // Exactly one expression selected
if (undoing)
sens = false;
set_sensitive(shortcut_popup1[i].widget, sens);
set_sensitive(shortcut_popup2[i].widget, sens);
set_sensitive(shortcut_menu [i].widget, sens);
}
// Argument field
if (arg_needs_update)
{
if (count.selected > 0)
{
string arg;
if (disp_value_arg)
{
arg = disp_value_arg->full_name();
source_arg->set_string(arg);
}
else if (disp_node_arg)
{
arg = disp_node_arg->name();
source_arg->set_string(arg);
}
}
arg_needs_update = false;
}
// Set selection.
// If the entire graph is selected, include position info, too.
bool include_position = (count.selected >= count.visible);
ostrstream os;
get_selection(os, include_position);
string cmd(os);
// Setting the string causes the selection to be lost. By setting
// LOSE_SELECTION, we make sure the associated callbacks return
// immediately.
lose_selection = false;
XmTextSetString(graph_selection_w, (char *)cmd);
lose_selection = true;
Time tm = XtLastTimestampProcessed(XtDisplay(graph_selection_w));
if (cmd == "")
{
// Nothing selected - clear selection explicitly
XmTextClearSelection(graph_selection_w, tm);
}
else
{
// Own the selection
XmTextSetSelection(graph_selection_w,
0, XmTextGetLastPosition(graph_selection_w), tm);
}
}
// The maximum display number when saving states
int DataDisp::max_display_number = 99;
// Get scopes in SCOPES
bool DataDisp::get_scopes(StringArray& scopes)
{
// Fetch current backtrace and store scopes in SCOPES
string backtrace = gdb_question(gdb->where_command(), -1, true);
while (backtrace != "")
{
string scope = get_scope(backtrace);
if (scope != "")
scopes += scope;
backtrace = backtrace.after('\n');
}
return scopes.size() > 0;
}
// Write commands to restore frame #TARGET_FRAME in OS
void DataDisp::write_frame_command(ostream& os, int& current_frame,
int target_frame)
{
if (target_frame != current_frame)
{
os << "graph ";
if (gdb->has_frame_command())
{
// Issue `frame' command
os << gdb->frame_command(target_frame) << "\n";
}
else
{
// Use `up' and `down' commands
int offset = current_frame - target_frame;
if (offset == -1)
os << "up";
else if (offset < 0)
os << "up " << -offset;
else if (offset == 1)
os << "down";
else if (offset > 0)
os << "down " << offset;
}
current_frame = target_frame;
}
}
// Write commands to restore scope of DN in OS
void DataDisp::write_restore_scope_command(ostream& os,
int& current_frame,
const StringArray& scopes,
DispNode *dn,
bool& ok)
{
if (dn->deferred())
{
// No need to restore frame for a deferred node
return;
}
if (dn->is_user_command())
{
// No need to restore frame for user displays
return;
}
int target_frame = -1;
if (dn->scope() == "")
{
// No scope - maybe a global?
target_frame = scopes.size() - 1; // Return to main frame
}
else
{
// Search scope in backtrace
for (int i = 0; i < scopes.size(); i++)
if (scopes[i] == dn->scope())
{
target_frame = i;
break;
}
}
if (target_frame < 0)
{
// Not in current backtrace - deferring display
MString msg;
msg += rm("Deferring display ");
msg += rm(itostring(dn->disp_nr()) + ": ");
msg += tt(dn->name());
if (dn->scope() != "")
{
msg += rm(" because ");
msg += tt(dn->scope());
msg += rm(" is not in current backtrace");
}
set_status_mstring(msg);
// OK remains set here - this is no fatal error
(void) ok;
return;
}
write_frame_command(os, current_frame, target_frame);
}
void DataDisp::get_node_state(ostream& os, DispNode *dn, bool include_position)
{
os << "graph ";
if (dn->plotted())
os << "plot ";
else
os << "display ";
os << dn->name();
// Write cluster
if (dn->clustered())
os << " clustered";
// Write position
if (include_position)
{
BoxPoint pos = dn->pos();
if (pos.isValid())
{
if (bump_displays && is_cluster(dn))
{
// When this cluster will be restored, it will be
// empty first, but later additions will bump it
// to a new position. Compensate for this.
static DispNode empty_cluster(-1, dn->name(),
dn->scope(), "No displays.",
false);
BoxPoint offset =
(dn->box()->size() - empty_cluster.box()->size()) / 2;
pos = graphEditFinalPosition(graph_edit, pos - offset);
}
os << " at " << pos;
}
}
// Write dependency
string depends_on = "";
if (dn->deferred())
{
depends_on = dn->depends_on();
}
else
{
for (GraphEdge *edge = dn->firstTo();
edge != 0; edge = dn->nextTo(edge))
{
DispNode *ancestor = ptr_cast(DispNode, edge->from());
if (ancestor != 0)
{
depends_on = ancestor->name();
break;
}
}
}
if (depends_on != "")
os << " dependent on " << depends_on;
// Write scope
if (dn->scope() != "")
os << " now or when in " << dn->scope();
os << '\n';
}
bool DataDisp::get_state(ostream& os,
bool restore_state,
bool include_position,
const StringArray& scopes,
int target_frame)
{
// Sort displays by number, such that old displays appear before
// newer ones.
// Note: this fails with the negative numbers of user-defined
// displays; a topological sort would make more sense here. (FIXME)
IntArray nrs;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (restore_state || selected(dn))
nrs += dn->disp_nr();
}
sort(nrs, absolute_le);
bool ok = true;
if (restore_state && scopes.size() == 0 && need_core_to_restore())
{
set_status("Cannot get current backtrace");
ok = false;
}
// When restoring, we'll be at the lowest frame (#0).
int current_frame = 0;
for (int i = 0; i < nrs.size(); i++)
{
DispNode *dn = disp_graph->get(nrs[i]);
if (dn == 0)
continue;
if (restore_state && scopes.size() > 0)
write_restore_scope_command(os, current_frame, scopes, dn, ok);
get_node_state(os, dn, include_position);
}
// That's it: return to target frame...
write_frame_command(os, current_frame, target_frame);
// ... and refresh the graph.
if (restore_state && nrs.size() > 0)
os << refresh_display_cmd() << "\n";
return ok;
}
// Return true if a core dump is needed to restore displays
bool DataDisp::need_core_to_restore()
{
MapRef ref;
for (DispNode *dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->deferred() && dn->scope() != "")
return true;
}
return false;
}
// Reset all
void DataDisp::reset_done(const string& answer, void *)
{
// Nothing special yet
(void) answer;
}
void DataDisp::reset()
{
// Clear all data displays
IntArray display_nrs;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
display_nrs += dn->disp_nr();
}
if (display_nrs.size() > 0)
{
Command c(delete_display_cmd(display_nrs));
c.verbose = false;
c.prompt = false;
c.check = true;
c.priority = COMMAND_PRIORITY_INIT;
c.callback = reset_done;
gdb_command(c);
}
}
// Return number of display aliased by NODE
int DataDisp::alias_display_nr(GraphNode *node)
{
if (!node->isHint())
return 0;
AliasGraphEdge *edge = ptr_cast(AliasGraphEdge, node->firstTo());
if (edge == 0)
return 0;
return edge->disp_nr();
}
// Update graph editor selection after a change in the display editor
void DataDisp::UpdateGraphEditorSelectionCB(Widget, XtPointer, XtPointer)
{
IntArray display_nrs;
getItemNumbers(display_list_w, display_nrs);
// Update graph editor selection
MapRef ref;
DispNode *dn;
for (dn = disp_graph->first(ref); dn != 0; dn = disp_graph->next(ref))
{
int display_nr = dn->disp_nr();
bool select = false;
for (int i = 0; i < display_nrs.size(); i++)
{
if (display_nr == display_nrs[i])
{
select = true;
break;
}
}
if (dn->clustered())
{
// Synchronize nodes with cluster
DispNode *cluster = disp_graph->get(dn->clustered());
if (cluster != 0 && cluster->selected() &&
cluster->selected_value() == 0)
{
// All cluster nodes are selected
select = true;
dn->select();
}
}
if (select != dn->selected())
{
dn->selected() = select;
graphEditRedrawNode(graph_edit, dn);
}
if (dn->hidden())
{
// Synchronize hint nodes with this alias node
for (GraphNode *node = disp_graph->firstNode();
node != 0;
node = disp_graph->nextNode(node))
{
if (alias_display_nr(node) == display_nr)
{
if (node->selected() != dn->selected())
{
node->selected() = dn->selected();
graphEditRedrawNode(graph_edit, node);
}
}
}
}
}
// Update cluster selection
for (dn = disp_graph->first(ref); dn != 0; dn = disp_graph->next(ref))
{
// Clear all local cluster selections
if (is_cluster(dn) && dn->selected_value() != 0)
{
dn->select();
graphEditRedrawNode(graph_edit, dn);
}
}
// Reset local cluster selections
for (dn = disp_graph->first(ref); dn != 0; dn = disp_graph->next(ref))
{
if (!dn->selected())
continue;
if (!dn->clustered())
continue;
DispNode *cluster = disp_graph->get(dn->clustered());
if (cluster == 0)
continue; // No such cluster
if (cluster->selected() && cluster->selected_value() == 0)
continue; // Entire cluster selected
DispValue *dv = cluster->value();
if (dv == 0)
continue;
// Find the display numbers in this cluster
IntArray cluster_children;
MapRef ref;
for (DispNode *dn2 = disp_graph->first(ref);
dn2 != 0; dn2 = disp_graph->next(ref))
{
if (dn2->clustered() == cluster->disp_nr())
cluster_children += dn2->disp_nr();
}
sort(cluster_children);
// The number of children of the main cluster List should be
// the same as the number of displays we just found.
assert(dv->nchildren() == cluster_children.size());
for (int i = 0; i < cluster_children.size(); i++)
{
if (cluster_children[i] == dn->disp_nr())
{
// Got it
cluster->selected() = true;
if (cluster->selected_value() == 0)
{
// Select this child
cluster->select(dv->child(i));
}
else
{
// Multiple nodes selected -- select them all
cluster->select(0);
}
break;
}
}
graphEditRedrawNode(graph_edit, cluster);
}
refresh_args(true);
refresh_display_list();
}
// Update display editor selection after a change in the graph editor
void DataDisp::UpdateDisplayEditorSelectionCB(Widget, XtPointer, XtPointer)
{
// Synchronize alias nodes with hint nodes
for (GraphNode *node = disp_graph->firstNode();
node != 0;
node = disp_graph->nextNode(node))
{
int nr = alias_display_nr(node);
if (nr < 0)
continue;
DispNode *dn = disp_graph->get(nr);
if (dn == 0)
continue;
if (node->selected() != dn->selected())
{
dn->selected() = node->selected();
graphEditRedrawNode(graph_edit, dn);
}
}
refresh_args(true);
refresh_display_list();
}
//-----------------------------------------------------------------------
// Sorting nodes for layout
//-----------------------------------------------------------------------
void DataDisp::CompareNodesCB(Widget, XtPointer, XtPointer call_data)
{
GraphEditCompareNodesInfo *info = (GraphEditCompareNodesInfo *)call_data;
BoxGraphNode *node1 = ptr_cast(BoxGraphNode, info->node1);
BoxGraphNode *node2 = ptr_cast(BoxGraphNode, info->node2);
DispNode *disp1 = 0;
DispNode *disp2 = 0;
if (node1 != 0 && node2 != 0)
{
int nr1 = disp_graph->get_nr(node1);
int nr2 = disp_graph->get_nr(node2);
disp1 = disp_graph->get(nr1);
disp2 = disp_graph->get(nr2);
}
if (disp1 != 0 && disp2 != 0)
{
info->result = smart_compare(disp1->name(), disp2->name());
}
else
{
if (disp1 != 0)
{
// Known nodes are ``larger'' than unknown nodes
info->result = 1;
}
else if (disp2 != 0)
{
// Known nodes are ``larger'' than unknown nodes
info->result = -1;
}
else
{
// Comparing the pointer values will keep pointers constant
info->result = long(info->node1) - long(info->node2);
}
}
#if LOG_COMPARE
if (disp1 && disp2)
{
clog << "Comparing " << disp1->name()
<< " and " << disp2->name() << " yields " << info->result << "\n";
}
else
{
clog << "Cannot compare: unknown nodes\n";
}
#endif
}
//-----------------------------------------------------------------------
// Handle GDB input / output
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Create new Display(s)
//-----------------------------------------------------------------------------
#if RUNTIME_REGEX
static regex rxmore_than_one ("\\[-?[0-9]+\\.\\.-?[0-9]+\\]");
#endif
void DataDisp::again_new_displaySQ (XtPointer client_data, XtIntervalId *)
{
NewDisplayInfo *info = (NewDisplayInfo *)client_data;
new_displaySQ(info->display_expression, info->scope, info->point_ptr,
info->depends_on, info->deferred, info->clustered,
info->plotted, info->origin, info->verbose, info->prompt);
delete info;
}
int DataDisp::display_number(const string& name, bool verbose)
{
int nr = disp_graph->get_by_name(name);
if (nr == 0)
{
if (verbose)
post_gdb_message("No display named " + quote(name) + ".\n");
return 0;
}
DispNode *dn = disp_graph->get(nr);
if (dn == 0)
{
if (verbose)
post_gdb_message("No display number " + itostring(nr) + ".\n");
return 0;
}
return nr;
}
void DataDisp::get_display_numbers(const string& name, IntArray& numbers)
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref); dn != 0;
dn = disp_graph->next(ref))
{
if (dn->name() == name)
numbers += dn->disp_nr();
}
}
void DataDisp::new_displaySQ (string display_expression,
string scope, BoxPoint *p,
string depends_on,
DeferMode deferred,
bool clustered, bool plotted,
Widget origin, bool verbose, bool do_prompt)
{
// Check arguments
if (deferred != DeferAlways && depends_on != "")
{
int depend_nr = display_number(depends_on, verbose);
if (depend_nr == 0)
return;
}
NewDisplayInfo *info = new NewDisplayInfo;
info->display_expression = display_expression;
info->scope = scope;
info->verbose = verbose;
info->prompt = do_prompt;
info->deferred = deferred;
info->clustered = clustered;
info->plotted = plotted;
if (p != 0)
{
info->point = *p;
info->point_ptr = &info->point;
}
else
{
info->point = BoxPoint();
info->point_ptr = 0;
}
info->depends_on = depends_on;
info->origin = origin;
static Delay *reading_delay = 0;
if (!DispBox::vsllib_initialized)
{
// We don't have the VSL library yet. Try again later.
if (VSLLib::background != 0)
{
reading_delay = new StatusDelay("Reading VSL library");
// Disable background processing
VSLLib::background = 0;
}
// As soon as the VSL library will be completely read, we
// shall enter the main DDD event loop and get called again.
XtAppAddTimeOut(XtWidgetToApplicationContext(graph_edit),
100, again_new_displaySQ, info);
return;
}
delete reading_delay;
reading_delay = 0;
if (origin)
set_last_origin(origin);
if (display_expression == "")
return;
if (deferred == DeferAlways)
{
// Create deferred display now
DispNode *dn = new_deferred_node(display_expression, scope,
info->point, depends_on,
info->clustered, info->plotted);
// Insert deferred node into graph
disp_graph->insert(dn->disp_nr(), dn);
if (do_prompt)
prompt();
delete info;
refresh_display_list();
}
else if (is_user_command(display_expression))
{
// User-defined display
string cmd = user_command(display_expression);
if (is_builtin_user_command(cmd))
{
info->constant = true;
string answer = builtin_user_command(cmd);
new_user_displayOQC(answer, info);
}
else
{
gdb_command(cmd, last_origin, new_user_displayOQC, info);
}
}
else
{
// Data display
if (display_expression.contains (rxmore_than_one))
{
new_data_displaysSQA (display_expression, info);
return;
}
if (gdb->display_prints_values())
{
gdb_command(gdb->display_command(display_expression),
last_origin, new_data_displayOQC, info);
}
else
{
gdb_command(gdb->display_command(display_expression),
last_origin, OQCProc(0), (void *)0);
gdb_command(gdb->print_command(display_expression, true),
last_origin, new_data_displayOQC, info);
}
}
}
// Get display number and name from ANSWER; store them in NR and NAME
void DataDisp::read_number_and_name(string& answer, string& nr, string& name)
{
nr = "";
name = "";
if (gdb->has_numbered_displays())
{
nr = read_disp_nr_str(answer, gdb);
if (nr != "")
name = read_disp_name(answer, gdb);
}
else
{
name = read_disp_name(answer, gdb);
if (gdb->has_display_command())
{
// Fetch number from `display' output
string ans = gdb_question(gdb->display_command(), -1);
int index = ans.index(name + "\n", -1);
if (index > 0)
{
while (index > 0 && ans[index - 1] != '\n')
index--;
ans = ans.from(index);
int n = get_nr(ans);
nr = itostring(n);
}
if (nr == "")
{
// Could not determine number
post_warning("Could not determine number of display "
+ quote(name),
"no_display_number_warning", last_origin);
}
}
if (nr == "")
{
// Assign a default number
nr = itostring(next_ddd_display_number++);
}
}
}
string DataDisp::new_display_cmd(const string& display_expression,
BoxPoint *p,
const string& depends_on,
bool clustered, bool plotted)
{
string cmd = "graph ";
if (plotted)
cmd += "plot ";
else
cmd += "display ";
cmd += display_expression;
if (clustered)
cmd += " clustered ";
if (p != 0 && *p != BoxPoint())
cmd += " at (" + itostring((*p)[X]) + ", " + itostring((*p)[Y]) + ")";
if (depends_on != "")
cmd += " dependent on " + depends_on;
return cmd;
}
//-----------------------------------------------------------------------------
// Built-in user commands
//-----------------------------------------------------------------------------
#define HOOK_PREFIX "<?"
#define HOOK_POSTFIX ">"
bool DataDisp::is_builtin_user_command(const string& cmd)
{
if (cmd == CLUSTER_COMMAND)
return true;
return false;
}
string DataDisp::builtin_user_command(const string& cmd, DispNode *node)
{
if (cmd == CLUSTER_COMMAND)
{
MapRef ref;
IntArray clustered_displays;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->clustered())
continue;
if (dn->is_user_command())
continue;
if (dn->deferred())
continue;
if (!dn->active())
continue;
if (node != 0 && dn->clustered() != node->disp_nr())
continue;
if (node == 0 && dn->clustered() != -next_ddd_display_number)
continue;
clustered_displays += dn->disp_nr();
}
sort(clustered_displays);
ostrstream os;
if (clustered_displays.size() == 0)
{
os << "No displays.\n";
}
else
{
for (int i = 0; i < clustered_displays.size(); i++)
{
DispNode *dn = disp_graph->get(clustered_displays[i]);
os << dn->name() << " = " HOOK_PREFIX
<< dn->disp_nr() << HOOK_POSTFIX "\n";
}
}
return string(os);
}
return NO_GDB_ANSWER;
}
// This function is called to update displays in clusters
DispValue *DataDisp::update_hook(string& value)
{
if (!value.contains(HOOK_PREFIX, 0))
return 0;
value = value.after(HOOK_PREFIX);
int nr = atoi(value.chars());
value = value.after(HOOK_POSTFIX);
DispNode *dn = disp_graph->get(nr);
if (dn == 0 || dn->value() == 0)
return 0; // Ignore
// Share the clustered DispValue with the original display
return dn->value()->link();
}
void DataDisp::refresh_builtin_user_displays()
{
DispValue::value_hook = update_hook;
ProgressMeter *s = 0;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->is_user_command())
continue;
const string& cmd = dn->user_command();
string answer = NO_GDB_ANSWER;
if (is_internal_command(cmd))
{
if (s == 0)
s = new ProgressMeter("Updating histories");
answer = internal_command(cmd);
}
else if (is_builtin_user_command(cmd))
{
if (is_cluster(dn) && !needs_refresh(dn))
continue;
if (s == 0)
s = new ProgressMeter("Updating clusters");
answer = builtin_user_command(cmd, dn);
}
else
{
continue;
}
if (answer != NO_GDB_ANSWER && dn->update(answer))
{
// Clear old local selection
dn->select(0);
}
dn->refresh();
graphEditRedrawNode(graph_edit, dn);
}
delete s;
DispValue::value_hook = 0;
}
//-----------------------------------------------------------------------------
// Open and close data window
//-----------------------------------------------------------------------------
void DataDisp::open_data_window()
{
// Make sure graph is visible
gdbOpenDataWindowCB(graph_edit, 0, 0);
}
void DataDisp::close_data_window()
{
if (app_data.separate_data_window)
{
// Don't close a separate data window.
}
else
{
gdbCloseDataWindowCB(graph_edit, 0, 0);
}
}
//-----------------------------------------------------------------------------
// Create new data and user nodes
//-----------------------------------------------------------------------------
DispNode *DataDisp::new_data_node(const string& given_name,
const string& scope,
const string& answer,
bool plotted)
{
string value = answer;
string nr_s;
string display_name;
read_number_and_name(value, nr_s, display_name);
gdb->munch_value(value, display_name);
int nr = get_nr(nr_s);
if (nr == 0 || display_name == "")
{
post_gdb_message(answer, true, last_origin);
return 0;
}
strip_space(value);
// Naming a data display after the GDB display name causes trouble
// when displaying functions: `display tree_test' creates a
// display named `tree_test(void)', and while `print tree_test'
// works fine, `print tree_test(void)' fails. We may use quotes,
// as in `print 'tree_test(void)'', but it is too hard to
// determine where quotes are needed, and where not - just
// consider `print tree_test(42)'. Hence, if a function call
// occurs in an expression, we use the name specified by the user,
// not the name supplied by GDB.
// Upon some occasions, GDB gives names like
// `{<text variable, no debug info>} 0x2270 <main>'. In such cases,
// also use the user-given name instead.
// If the user quoted some part of a name, as in
// `tree->date.'_vptr.'[0]', also prefer the user-given name,
// since the quotes will be removed in the GDB output.
#if RUNTIME_REGEX
static regex rxfunction_call("[a-zA-Z0-9_$][(]");
#endif
string title = display_name;
if (title.contains(rxfunction_call) ||
title.contains('{') ||
title.contains('}') ||
given_name.contains('\''))
title = given_name;
bool disabling_occurred = false;
if (is_disabling(value, gdb))
{
string error_msg = get_disp_value_str(value, gdb);
post_gdb_message(error_msg, true, last_origin);
value = "";
disabling_occurred = true;
}
ProgressMeter s("Creating display");
s.total = value.length();
s.current = value.length();
DispNode *dn = new DispNode(nr, title, scope, value, plotted);
if (plotted && (dn->value() == 0 || dn->value()->can_plot() == 0))
{
post_gdb_message("Nothing to plot.", true, last_origin);
if (gdb->has_display_command())
{
gdb_command("undisplay " + itostring(dn->disp_nr()),
last_origin, OQCProc(0));
}
delete dn;
return 0;
}
if (disabling_occurred)
{
dn->disable();
dn->make_active();
}
open_data_window();
undo_buffer.add_display(title, value);
undo_buffer.add_command(delete_display_cmd(title), true);
return dn;
}
DispNode *DataDisp::new_user_node(const string& name,
const string& /* scope */,
const string& answer,
bool plotted)
{
// Assign a default number
int nr = -(next_ddd_display_number++);
ProgressMeter s("Creating status display");
s.total = answer.length();
s.current = answer.length();
// Set cluster update hook
if (name == "`" CLUSTER_COMMAND "`")
DispValue::value_hook = update_hook;
// User displays work regardless of scope
static const string scope = "";
DispNode *dn = new DispNode(nr, name, scope, answer, plotted);
DispValue::value_hook = 0;
if (plotted && (dn->value() == 0 || dn->value()->can_plot() == 0))
{
post_gdb_message("Nothing to plot.", true, last_origin);
delete dn;
return 0;
}
open_data_window();
if (!is_cluster(dn))
{
undo_buffer.add_display(name, answer);
undo_buffer.add_command(delete_display_cmd(name), true);
}
return dn;
}
DispNode *DataDisp::new_deferred_node(const string& expr, const string& scope,
const BoxPoint& pos,
const string& depends_on,
bool clustered, bool plotted)
{
// Assign a default number
int nr = -(next_ddd_display_number++);
// A `dummy' answer (never shown)
string answer = "<deferred>";
MString msg = rm("Creating deferred display " + itostring(nr) + ": ") +
tt(expr) + rm(" (scope ") + tt(scope) + rm(")");
set_status_mstring(msg);
DispNode *dn = new DispNode(nr, expr, scope, answer, plotted);
dn->deferred() = true;
if (clustered)
dn->cluster(-1);
dn->make_inactive();
dn->depends_on() = depends_on;
dn->plotted() = plotted;
dn->moveTo(pos);
undo_buffer.add_display(expr, answer);
undo_buffer.add_command(delete_display_cmd(expr), true);
return dn;
}
// Create new data display from ANSWER
void DataDisp::new_data_displayOQC (const string& answer, void* data)
{
NewDisplayInfo *info = (NewDisplayInfo *)data;
if (answer == NO_GDB_ANSWER)
{
delete info; // Command was canceled
return;
}
if (answer == "")
{
if (gdb->has_display_command())
{
// No display output (GDB bug). Refresh displays explicitly.
gdb_command(gdb->display_command(), last_origin,
new_data_display_extraOQC, data);
}
else
{
delete info;
}
return;
}
if (!contains_display(answer, gdb) || !is_valid(answer, gdb))
{
if (info->deferred == DeferIfNeeded)
{
// Create deferred display now
DispNode *dn = new_deferred_node(info->display_expression,
info->scope,
info->point, info->depends_on,
info->clustered, info->plotted);
// Insert deferred node into graph
disp_graph->insert(dn->disp_nr(), dn);
if (info->prompt)
prompt();
refresh_display_list();
}
else
{
if (info->verbose)
post_gdb_message(answer, info->prompt, last_origin);
}
delete info;
return;
}
// Unselect all nodes
for (GraphNode *gn = disp_graph->firstNode();
gn != 0; gn = disp_graph->nextNode(gn))
{
gn->selected() = false;
}
// Create new DispNode
string ans = answer;
DispNode *dn = new_data_node(info->display_expression,
info->scope, ans, info->plotted);
if (dn == 0)
{
// Display could not be created
if (info->deferred == DeferIfNeeded)
{
// Create deferred display now
dn = new_deferred_node(info->display_expression, info->scope,
info->point, info->depends_on,
info->clustered, info->plotted);
// Insert deferred node into graph
disp_graph->insert(dn->disp_nr(), dn);
if (info->prompt)
prompt();
refresh_display_list();
}
delete info;
return;
}
// Insert node into graph
int depend_nr = disp_graph->get_by_name(info->depends_on);
insert_data_node(dn, depend_nr, info->clustered, info->plotted);
// Determine position
BoxPoint box_point = info->point;
if (box_point == BoxPoint())
box_point = disp_graph->default_pos(dn, graph_edit, depend_nr);
dn->moveTo(box_point);
select_node(dn, depend_nr);
refresh_addr(dn);
refresh_graph_edit();
if (info->prompt)
prompt();
delete info;
}
// Create new user display from ANSWER
void DataDisp::new_user_displayOQC (const string& answer, void* data)
{
NewDisplayInfo *info = (NewDisplayInfo *)data;
if (answer == NO_GDB_ANSWER)
{
delete info; // Command was canceled
return;
}
// Unselect all nodes
for (GraphNode *gn = disp_graph->firstNode();
gn != 0; gn = disp_graph->nextNode(gn))
{
gn->selected() = false;
}
// Create new user node and issue `disabling' messages
string ans = answer;
DispNode *dn = new_user_node(info->display_expression, info->scope,
ans, info->plotted);
if (dn != 0)
{
dn->constant() = info->constant;
// Insert into graph
int depend_nr = disp_graph->get_by_name(info->depends_on);
disp_graph->insert(dn->disp_nr(), dn, depend_nr);
// Plot new node
if (dn->plotted() && dn->value() != 0)
dn->value()->plot();
// Determine new position
BoxPoint box_point = info->point;
if (box_point == BoxPoint())
box_point = disp_graph->default_pos(dn, graph_edit, depend_nr);
dn->moveTo(box_point);
select_node(dn, depend_nr);
refresh_addr(dn);
refresh_graph_edit();
update_infos();
if (info->prompt)
prompt();
}
delete info;
}
// Create new display value from `display' output
void DataDisp::new_data_display_extraOQC (const string& answer, void* data)
{
NewDisplayInfo *info = (NewDisplayInfo *)data;
if (answer == NO_GDB_ANSWER)
{
delete info; // Command was canceled
return;
}
// The new display is the first one
string ans = answer;
string display = read_next_display(ans, gdb);
if (display != "")
new_data_displayOQC(display, data);
}
//-----------------------------------------------------------------------------
// Create multiple displays, using the [FROM..TO] syntax
//-----------------------------------------------------------------------------
void DataDisp::new_data_displaysSQA (string display_expression,
void *data)
{
NewDisplayInfo *info = (NewDisplayInfo *)data;
assert (display_expression.contains (rxmore_than_one));
// Create individual display expressions and process entire array
string prefix = display_expression.before(rxmore_than_one);
string postfix = display_expression.after(rxmore_than_one);
string range = display_expression.from(rxmore_than_one);
range.del("[");
int start = ::get_nr(range);
range = range.after("..");
int stop = ::get_nr(range);
if (start > stop)
{
post_error("Invalid range in " + quote(display_expression),
"invalid_range_error");
delete info;
return;
}
assert (stop >= start);
StringArray display_cmds;
StringArray print_cmds;
for (int i = start; i < stop + 1; i++)
{
string expr = prefix + "[" + itostring (i) + "]" + postfix;
info->display_expressions += expr;
display_cmds += gdb->display_command(expr);
print_cmds += gdb->print_command(expr);
}
VoidArray dummy;
while (dummy.size() < display_cmds.size())
dummy += (void *)0;
bool ok = true;
if (gdb->display_prints_values())
{
ok = gdb->send_qu_array (display_cmds, dummy, display_cmds.size(),
new_data_displaysOQAC, info);
}
else
{
for (int i = 0; i < display_cmds.size(); i++)
gdb_question(display_cmds[i]);
ok = gdb->send_qu_array (print_cmds, dummy, print_cmds.size(),
new_data_displaysOQAC, info);
}
if (!ok)
post_gdb_busy(last_origin);
}
void DataDisp::new_data_displaysOQAC (const StringArray& answers,
const VoidArray& /* qu_datas */,
void* data)
{
int count = answers.size();
// Unselect all nodes
for (GraphNode *gn = disp_graph->firstNode();
gn != 0; gn = disp_graph->nextNode(gn))
{
gn->selected() = false;
}
NewDisplayInfo *info = (NewDisplayInfo *)data;
// Create and select new nodes
int depend_nr = disp_graph->get_by_name(info->depends_on);
for (int i = 0; i < count; i++)
{
string answer = answers[i];
if (!contains_display(answer, gdb))
{
// Looks like an error message
if (info->verbose)
post_gdb_message(answer, info->prompt, last_origin);
}
else
{
// Create new display and remember disabling message
string var = info->display_expressions[i];
gdb->munch_value(answer, var);
DispNode *dn =
new_data_node(var, info->scope, answer, info->plotted);
if (dn == 0)
continue;
// Insert into graph
insert_data_node(dn, depend_nr, info->clustered, info->plotted);
// Set position
BoxPoint box_point = info->point;
if (box_point == BoxPoint())
box_point = disp_graph->default_pos(dn, graph_edit, depend_nr);
dn->moveTo(box_point);
dn->selected() = true;
}
}
refresh_addr();
refresh_graph_edit();
if (info->prompt)
prompt();
delete info;
}
// Insert DN into graph, possibly clustering it
void DataDisp::insert_data_node(DispNode *dn, int depend_nr,
bool clustered, bool plotted)
{
// Insert into graph
disp_graph->insert(dn->disp_nr(), dn, depend_nr);
if (plotted)
{
dn->plotted() = true;
if (dn->value() != 0)
dn->value()->plot();
}
// Check for clusters
if (!clustered && !cluster_displays)
return;
if (dn->is_user_command())
return;
if (depend_nr != 0)
return;
// Insert into current cluster
dn->cluster(current_cluster());
}
// Create a new cluster and return its number
int DataDisp::new_cluster()
{
gdb_command("graph display `" CLUSTER_COMMAND "`", last_origin, 0);
return -next_ddd_display_number;
}
// Return a cluster number; create a new cluster if necessary
int DataDisp::current_cluster()
{
// Use last cluster or create a new one
IntArray all_clusters;
get_all_clusters(all_clusters);
sort(all_clusters);
if (all_clusters.size() > 0)
return all_clusters[0];
else
return new_cluster();
}
//-----------------------------------------------------------------------------
// Refresh graph
//-----------------------------------------------------------------------------
class RefreshInfo {
public:
bool verbose;
bool prompt;
IntArray display_nrs;
RefreshInfo()
: verbose(false), prompt(false), display_nrs()
{}
~RefreshInfo()
{}
private:
RefreshInfo(const RefreshInfo&)
: verbose(false), prompt(false), display_nrs()
{
assert(0);
}
RefreshInfo& operator = (const RefreshInfo&)
{
assert(0); return *this;
}
};
int DataDisp::add_refresh_data_commands(StringArray& cmds)
{
int initial_size = cmds.size();
if (gdb->display_prints_values())
cmds += gdb->display_command();
else
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->is_user_command() && !dn->deferred())
{
string cmd = gdb->print_command(dn->name());
while (cmd != "")
{
string line = cmd;
if (line.contains('\n'))
line = line.before('\n');
cmd = cmd.after('\n');
cmds += line;
}
}
}
}
return cmds.size() - initial_size;
}
int DataDisp::add_refresh_user_commands(StringArray& cmds)
{
int initial_size = cmds.size();
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->is_user_command() && dn->enabled() &&
!dn->deferred() && !dn->constant())
{
const string& cmd = dn->user_command();
if (!is_internal_command(cmd))
cmds += cmd;
}
}
return cmds.size() - initial_size;
}
string DataDisp::refresh_display_cmd()
{
return "graph refresh";
}
#define PROCESS_INFO_DISPLAY 0
#define PROCESS_DATA 1
#define PROCESS_USER 2
#define PROCESS_ADDR 3
void DataDisp::refresh_displaySQ(Widget origin, bool verbose, bool do_prompt)
{
if (origin)
set_last_origin(origin);
// Some sanitizing actions...
make_sane();
// Now for the refreshments. Process all displays.
StringArray cmds;
VoidArray dummy;
if (gdb->has_info_display_command())
cmds += gdb->info_display_command();
while (dummy.size() < cmds.size())
dummy += (void *)PROCESS_INFO_DISPLAY;
add_refresh_data_commands(cmds);
while (dummy.size() < cmds.size())
dummy += (void *)PROCESS_DATA;
add_refresh_user_commands(cmds);
while (dummy.size() < cmds.size())
dummy += (void *)PROCESS_USER;
add_refresh_addr_commands(cmds);
while (dummy.size() < cmds.size())
dummy += (void *)PROCESS_ADDR;
static RefreshInfo info;
info.verbose = verbose;
info.prompt = do_prompt;
bool ok = gdb->send_qu_array(cmds, dummy, cmds.size(),
refresh_displayOQAC, (void *)&info);
if (!ok || cmds.size() == 0)
{
// Simply redraw display
refresh_graph_edit();
if (do_prompt)
prompt();
}
}
void DataDisp::refresh_displayOQAC (const StringArray& answers,
const VoidArray& qu_datas,
void* data)
{
int count = answers.size();
string data_answers;
int data_answers_seen = 0;
StringArray user_answers;
StringArray addr_answers;
RefreshInfo *info = (RefreshInfo *)data;
for (int i = 0; i < count; i++)
{
switch ((int)(long)qu_datas[i])
{
case PROCESS_INFO_DISPLAY:
// Process 'info display' output (delete displays if necessary)
process_info_display(answers[i]);
break;
case PROCESS_DATA:
data_answers += answers[i];
data_answers_seen++;
break;
case PROCESS_USER:
user_answers += answers[i];
break;
case PROCESS_ADDR:
addr_answers += answers[i];
break;
default:
assert(0);
break;
}
}
// Process `display', user command, and addr command output
if (data_answers_seen > 0)
{
bool disabling_occurred = false;
process_displays(data_answers, disabling_occurred);
// If we had a `disabling' message, refresh displays once more
if (disabling_occurred)
{
refresh_displaySQ(0, info->verbose, info->prompt);
info->prompt = false; // No more prompts
}
}
if (user_answers.size() > 0)
process_user(user_answers);
if (addr_answers.size() > 0)
{
force_check_aliases = true;
process_addr(addr_answers);
}
if (info->prompt)
prompt();
}
//-----------------------------------------------------------------------------
// Disabling Displays
//-----------------------------------------------------------------------------
// Convert A to a space-separated string
string DataDisp::numbers(IntArray& a)
{
sort(a);
string ret;
for (int i = 0; i < a.size(); i++)
{
if (i > 0)
ret += " ";
ret += itostring(a[i]);
}
return ret;
}
// Sort and verify the display numbers in DISPLAY_NRS
bool DataDisp::sort_and_check(IntArray& display_nrs)
{
bool ok = true;
sort(display_nrs);
for (int i = 0; i < display_nrs.size(); i++)
{
DispNode *dn = disp_graph->get(display_nrs[i]);
if (dn == 0)
{
post_gdb_message("No display number "
+ itostring(display_nrs[i]) + ".\n");
display_nrs[i] = 0;
ok = false;
}
}
return ok;
}
// For all nodes in DISPLAY_NRS, add their aliases
void DataDisp::add_aliases(IntArray& display_nrs)
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->hidden())
{
bool have_alias = false;
bool need_alias = false;
for (int i = 0; i < display_nrs.size(); i++)
{
if (display_nrs[i] == dn->disp_nr())
have_alias = true;
if (display_nrs[i] == dn->alias_of)
need_alias = true;
}
if (need_alias && !have_alias)
display_nrs += dn->disp_nr();
}
}
}
string DataDisp::disable_display_cmd(IntArray& display_nrs)
{
add_aliases(display_nrs);
if (display_nrs.size() > 0)
return "graph disable display " + numbers(display_nrs);
else
return "";
}
void DataDisp::disable_displaySQ(IntArray& display_nrs, bool verbose,
bool do_prompt)
{
bool ok = sort_and_check(display_nrs);
if (!ok)
do_prompt = false;
int disabled_data_displays = 0;
int i;
string cmd = "disable display";
for (i = 0; i < display_nrs.size(); i++)
{
if (gdb->has_disable_display_command() && display_nrs[i] > 0)
{
cmd += " " + itostring(display_nrs[i]);
disabled_data_displays++;
}
}
if (disabled_data_displays > 0)
{
static RefreshInfo info;
info.verbose = verbose;
info.prompt = do_prompt;
gdb_command(cmd, last_origin, disable_displayOQC, (void *)&info);
}
int disabled_user_displays = 0;
for (i = 0; i < display_nrs.size(); i++)
{
DispNode *dn = disp_graph->get(display_nrs[i]);
if (dn != 0 && dn->enabled())
{
dn->disable();
disabled_user_displays++;
}
}
if (disabled_data_displays == 0)
{
if (disabled_user_displays > 0)
refresh_graph_edit();
if (do_prompt)
prompt();
}
}
void DataDisp::disable_displayOQC (const string& answer, void *data)
{
if (answer == NO_GDB_ANSWER)
return; // Command was canceled
RefreshInfo *info = (RefreshInfo *)data;
if (info->verbose)
post_gdb_message(answer, info->prompt);
if (info->prompt)
prompt();
refresh_graph_edit();
}
//-----------------------------------------------------------------------------
// Enable Displays
//-----------------------------------------------------------------------------
string DataDisp::enable_display_cmd(IntArray& display_nrs)
{
add_aliases(display_nrs);
if (display_nrs.size() > 0)
return "graph enable display " + numbers(display_nrs);
else
return "";
}
void DataDisp::enable_displaySQ(IntArray& display_nrs, bool verbose,
bool do_prompt)
{
bool ok = sort_and_check(display_nrs);
if (!ok)
do_prompt = false;
int enabled_data_displays = 0;
int i;
string cmd = "enable display";
for (i = 0; i < display_nrs.size(); i++)
{
if (gdb->has_enable_display_command() && display_nrs[i] > 0)
{
cmd += " " + itostring(display_nrs[i]);
enabled_data_displays++;
}
}
// Have GDB enable data displays
if (enabled_data_displays > 0)
{
static RefreshInfo info;
info.verbose = verbose;
info.prompt = do_prompt;
gdb_command(cmd, last_origin, enable_displayOQC, (void *)&info);
}
// Handle user displays
int enabled_user_displays = 0;
for (i = 0; i < display_nrs.size(); i++)
{
DispNode *dn = disp_graph->get(display_nrs[i]);
if (dn != 0 && dn->is_user_command() &&
dn->disabled() && !dn->deferred())
{
dn->enable();
if (dn->value() != 0)
dn->value()->expandAll();
enabled_user_displays++;
}
}
if (enabled_data_displays == 0)
{
refresh_graph_edit();
if (do_prompt)
prompt();
}
}
void DataDisp::enable_displayOQC (const string& answer, void *data)
{
if (answer == NO_GDB_ANSWER)
return; // Command was canceled
RefreshInfo *info = (RefreshInfo *)data;
if (info->verbose)
post_gdb_message(answer, false);
refresh_displaySQ(0, info->verbose, info->prompt);
}
//-----------------------------------------------------------------------------
// Delete Displays
//-----------------------------------------------------------------------------
string DataDisp::delete_display_cmd(IntArray& display_nrs)
{
if (app_data.delete_alias_displays)
add_aliases(display_nrs);
if (display_nrs.size() > 0)
return delete_display_cmd(numbers(display_nrs));
else
return "";
}
string DataDisp::delete_display_cmd(const string& name)
{
return "graph undisplay " + name;
}
// Return true iff DISPLAY_NRS contains all data displays
bool DataDisp::all_data_displays(IntArray& display_nrs)
{
// Fetch given data displays
IntArray data_display_nrs;
int i;
for (i = 0; i < display_nrs.size(); i++)
if (display_nrs[i] > 0)
data_display_nrs += display_nrs[i];
if (data_display_nrs.size() < 2)
return false;
// Fetch existing data displays
IntArray all_data_display_nrs;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->deferred() && !dn->is_user_command())
all_data_display_nrs += dn->disp_nr();
}
// Compare
if (data_display_nrs.size() != all_data_display_nrs.size())
return false;
sort(all_data_display_nrs);
sort(data_display_nrs);
for (i = 0; i < data_display_nrs.size(); i++)
if (data_display_nrs[i] != all_data_display_nrs[i])
return false;
return true;
}
void DataDisp::delete_displaySQ(IntArray& display_nrs, bool verbose,
bool do_prompt)
{
bool ok = sort_and_check(display_nrs);
if (!ok)
do_prompt = false;
string cmd = "undisplay";
int deleted_data_displays = 0;
if (gdb->type() == GDB && verbose && all_data_displays(display_nrs))
{
// We want to delete all data displays. Use GDB `undisplay'
// command without args; this will ask for confirmation.
deleted_data_displays = display_nrs.size();
}
else
{
// Build command
for (int i = 0; i < display_nrs.size(); i++)
{
if (display_nrs[i] > 0)
{
if (deleted_data_displays++ > 0 && gdb->wants_display_comma())
cmd += ",";
cmd += " " + itostring(display_nrs[i]);
}
}
}
if (deleted_data_displays > 0 && gdb->has_display_command())
{
static RefreshInfo info;
info.verbose = verbose;
info.prompt = do_prompt;
info.display_nrs = display_nrs;
Command c(cmd, last_origin, delete_displayOQC, (void *)&info);
if (gdb->has_redisplaying_undisplay())
c.verbose = false;
else
c.verbose = verbose;
gdb_command(c);
}
else
{
deletion_done(display_nrs, do_prompt);
}
}
void DataDisp::delete_displayOQC (const string& answer, void *data)
{
if (answer == NO_GDB_ANSWER)
return; // Command was canceled
RefreshInfo *info = (RefreshInfo *)data;
if (gdb->type() == GDB && answer.contains("(y or n)"))
{
// The `undisplay' command required confirmation. Since GDBAgent
// does not tell us the outcome, we must check the user reply.
string reply = lastUserReply();
if (!reply.contains('y', 0))
{
// Deletion was canceled
static IntArray empty;
info->display_nrs = empty;
}
}
if (gdb->has_redisplaying_undisplay())
{
string ans = answer;
// Upon `undisplay', Sun DBX redisplays remaining displays
// with values. Process them.
if (answer != "" && !answer.contains("no such expression"))
{
bool disabling_occurred;
process_displays(ans, disabling_occurred);
}
// Show remaining output
post_gdb_message(ans);
}
deletion_done(info->display_nrs, info->prompt);
}
void DataDisp::deletion_done (IntArray& display_nrs, bool do_prompt)
{
bool unclustered = false;
// Build undo command
ostrstream undo_commands;
int i;
for (i = 0; i < display_nrs.size(); i++)
{
int nr = display_nrs[i];
DispNode *node = disp_graph->get(nr);
if (node == 0)
continue; // Already deleted or bad number
if (is_cluster(node))
continue; // Saving clusters is a bad idea
// Save current state
get_node_state(undo_commands, node, true);
}
string u = string(undo_commands);
if (u != "")
undo_buffer.add_command(u, true);
// Delete nodes
for (i = 0; i < display_nrs.size(); i++)
{
int nr = display_nrs[i];
// Delete node from graph
DispNode *node = disp_graph->get(nr);
if (node == 0)
continue; // Already deleted or bad number
if (node->clustered())
{
// Deleting a clustered node:
// force its cluster to be redisplayed
DispNode *cluster = disp_graph->get(node->clustered());
if (cluster != 0)
cluster->set_last_refresh();
}
if (is_cluster(node))
{
// Deleting a cluster: uncluster all contained nodes
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0; dn = disp_graph->next(ref))
{
if (dn->clustered() == nr)
{
disp_graph->uncluster(dn);
dn->selected() = true;
unclustered = true;
}
}
}
// Delete the node itself
disp_graph->del(nr);
}
if (display_nrs.size() > 0)
{
// Refresh arguments
if (unclustered)
refresh_args();
// Refresh editor
refresh_graph_edit();
// Refresh addresses now
force_check_aliases = true;
refresh_addr();
}
if (disp_graph->firstVisibleNode() == 0)
{
// Deleted last visible display
if (app_data.auto_close_data_window)
{
close_data_window();
}
}
if (do_prompt)
prompt();
update_infos();
}
//-----------------------------------------------------------------------------
// Handle output of 'info display'
//-----------------------------------------------------------------------------
void DataDisp::process_info_display(string& info_display_answer,
bool defer_deleted)
{
int disp_nr;
StringMap info_disp_string_map;
string *strptr;
int max_disp_nr = 0;
string next_disp_info =
read_first_disp_info (info_display_answer, gdb);
while (next_disp_info != "")
{
disp_nr = get_positive_nr (next_disp_info);
if (disp_nr >= 0)
{
max_disp_nr = max(max_disp_nr, disp_nr);
if (disp_graph->contains(disp_nr))
{
// This is a DDD display.
strptr = new string(get_info_disp_str(next_disp_info, gdb));
info_disp_string_map.insert (disp_nr, strptr);
}
}
next_disp_info =
read_next_disp_info(info_display_answer, gdb);
}
next_gdb_display_number = max(next_gdb_display_number, max_disp_nr + 1);
// Process DDD displays
// Part 1. Update existing display values.
IntArray deleted_displays;
bool changed = false;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->is_user_command() && !dn->deferred())
{
string *disp_info = info_disp_string_map.get(dn->disp_nr());
if (disp_info == 0)
{
// The DDD display is not contained in the GDB
// `display' output. This happens if the debuggee has
// changed; in this case, GDB deletes all displays.
// If DEFER_DELETED is set, we simply defer the
// existing displays such that they can be restored
// later.
deleted_displays += dn->disp_nr();
}
else
{
// Update values
if (disp_is_disabled(*disp_info, gdb))
{
if (dn->enabled())
{
dn->disable();
changed = true;
}
}
else
{
if (dn->disabled())
{
changed = true;
}
}
delete disp_info;
info_disp_string_map.del(dn->disp_nr());
}
}
}
assert(info_disp_string_map.length() == 0);
// Part 2. Defer deleted displays.
sort(deleted_displays);
// Give an appropriate message
if (defer_deleted && deleted_displays.size() >= 1)
{
MString msg = rm("Deferring display");
if (deleted_displays.size() >= 2)
msg += rm("s");
msg += rm(" ");
for (int i = 0; i < deleted_displays.size(); i++)
{
if (i > 0)
{
if (deleted_displays.size() == 2)
msg += rm(" and ");
else if (i == deleted_displays.size() - 1)
msg += rm(", and ");
else
msg += rm(", ");
}
msg += rm(itostring(deleted_displays[i]));
}
msg += rm(" because ");
if (deleted_displays.size() >= 2)
msg += rm("they have");
else
msg += rm("it has");
msg += rm(" been deleted by " + gdb->title());
set_status_mstring(msg);
}
if (defer_deleted)
{
// Create new deferred displays
for (int i = 0; i < deleted_displays.size(); i++)
{
DispNode *dn = disp_graph->get(deleted_displays[i]);
// Fetch old position and dependent info
BoxPoint pos = dn->pos();
string depends_on = "";
for (GraphEdge *edge = dn->firstTo();
edge != 0; edge = dn->nextTo(edge))
{
BoxGraphNode *ancestor = ptr_cast(BoxGraphNode, edge->from());
if (ancestor != 0)
{
int depnr = disp_graph->get_nr(ancestor);
DispNode *depnode = disp_graph->get(depnr);
if (depnode != 0)
{
depends_on = depnode->name();
break;
}
}
}
// Create new deferred node
new_displaySQ(dn->name(), dn->scope(), &pos,
depends_on, DeferIfNeeded,
dn->clustered(), dn->plotted(),
0, false, false);
}
}
// Delete remaining (= undeferred) displays
for (int i = 0; i < deleted_displays.size(); i++)
{
disp_graph->del(deleted_displays[i]);
changed = true;
}
if (deleted_displays.size() >= 1)
{
force_check_aliases = true;
refresh_addr();
}
if (changed)
refresh_graph_edit();
refresh_display_list();
}
//-----------------------------------------------------------------------------
// Process `display' output
//-----------------------------------------------------------------------------
string DataDisp::process_displays(string& displays,
bool& disabling_occurred)
{
string not_my_displays;
disabling_occurred = false;
strip_space(displays);
if (displays.length() == 0)
{
bool have_displays = false;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
!have_displays && dn != 0;
dn = disp_graph->next(ref))
{
have_displays = (!dn->is_user_command() && dn->active());
}
if (!have_displays)
return ""; // No data and no displays
}
ProgressMeter s("Updating displays");
// Store graph displays in DISP_STRING_MAP; return all other
// (text) displays as well as error messages
int disp_nr = 0;
StringMap disp_string_map;
#if LOG_DISPLAYS
clog << "Updating displays " << quote(displays) << "...\n";
#endif
string next_display = read_next_display (displays, gdb);
while (next_display != "")
{
#if LOG_DISPLAYS
clog << "Updating display " << quote(next_display);
#endif
if (gdb->has_numbered_displays())
{
disp_nr = get_positive_nr (next_display);
}
else
{
disp_nr = 0;
string disp_name = next_display;
disp_name = read_disp_name(disp_name, gdb);
if (disp_name != "")
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->name() == disp_name)
{
disp_nr = dn->disp_nr();
break;
}
}
}
}
#if LOG_DISPLAYS
clog << " (number " << disp_nr << ")\n";
#endif
if (is_disabling (next_display, gdb))
{
// A display was disabled: record this and try again
disabling_occurred = true;
DispNode *dn = disp_graph->get(disp_nr);
if (disp_nr >= 0 && dn != 0)
{
string error_msg = get_disp_value_str(next_display, gdb);
post_gdb_message(error_msg);
dn->make_active();
dn->disable();
}
else
{
not_my_displays = next_display; // Memorize this one only
}
// Clear DISP_STRING_MAP and try again
disp_string_map.delete_all_contents();
return not_my_displays;
}
if (!is_valid(next_display, gdb))
{
// Display is not active
}
else if (disp_nr >= 0 && disp_graph->contains(disp_nr))
{
string *strptr = new string(get_disp_value_str(next_display, gdb));
disp_string_map.insert(disp_nr, strptr);
s.total += strptr->length();
}
else
{
not_my_displays += next_display + '\n';
}
next_display = read_next_display (displays, gdb);
}
// Process own displays
bool changed = false;
bool activated = false;
// Change `active' status. This must be done before updating
// values, since inactive nodes must not be bumped after a resize.
MapRef ref;
int k;
for (k = disp_graph->first_nr(ref); k != 0; k = disp_graph->next_nr(ref))
{
DispNode *dn = disp_graph->get(k);
if (dn->is_user_command() || dn->deferred())
continue;
if (disp_string_map.contains(k))
{
if (disp_graph->make_active(dn))
{
// Now active
changed = activated = true;
}
}
else
{
// Node is no more part of `display' output
if (disp_graph->make_inactive(dn))
{
// Now inactive
changed = true;
}
}
}
// Update values
for (k = disp_graph->first_nr(ref); k != 0; k = disp_graph->next_nr(ref))
{
DispNode* dn = disp_graph->get(k);
if (dn->is_user_command() || dn->deferred())
continue;
if (!disp_string_map.contains(k))
{
undo_buffer.remove_display(dn->name());
continue;
}
// Update existing node
string *strptr = disp_string_map.get(k);
s.current = strptr->length();
undo_buffer.add_display(dn->name(), *strptr);
if (dn->update(*strptr))
{
// New value
changed = true;
}
if (*strptr != "" && !(strptr->matches(rxwhite)))
{
// After the `display' output, more info followed
// (e.g. the returned value when `finish'ing)
not_my_displays += strptr->after(rxwhite);
}
s.base += s.current;
delete strptr;
disp_string_map.del(k);
}
assert (disp_string_map.length() == 0);
if (activated)
{
force_check_aliases = true;
refresh_addr();
}
if (changed)
refresh_graph_edit();
return not_my_displays;
}
//-----------------------------------------------------------------------------
// Undo stuff
//-----------------------------------------------------------------------------
void DataDisp::update_displays(const StringArray& displays,
const StringArray& values,
const StringArray& addrs)
{
assert(displays.size() == values.size());
assert(displays.size() == addrs.size());
if (displays.size() == 0)
{
bool have_displays = false;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
!have_displays && dn != 0;
dn = disp_graph->next(ref))
{
have_displays = dn->active();
}
if (!have_displays)
return; // No data and no displays
}
bool changed = false;
bool addr_changed = false;
// Check `active' status
MapRef ref;
for (DispNode *dn = disp_graph->first(ref);
dn != 0; dn = disp_graph->next(ref))
{
if (is_cluster(dn))
continue;
if (dn->deferred())
continue;
bool found = false;
for (int i = 0; !found && i < displays.size(); i++)
found = (displays[i] == dn->name());
if (found)
{
if (!dn->active())
{
disp_graph->make_active(dn);
changed = true;
}
}
else
{
if (dn->active())
{
disp_graph->make_inactive(dn);
changed = true;
}
}
}
ProgressMeter s("Restoring displays");
int i;
for (i = 0; i < values.size(); i++)
s.total += values[i].length();
// Update values
for (i = 0; i < displays.size(); i++)
{
const string& name = displays[i];
const string& value = values[i];
const string& addr = addrs[i];
MapRef ref;
for (DispNode *dn = disp_graph->first(ref);
dn != 0; dn = disp_graph->next(ref))
{
if (dn->name() != name)
continue;
if (dn->deferred())
continue;
s.current = value.length();
string v = value;
if (dn->update(v))
changed = true;
if (dn->addr() != addr)
{
dn->set_addr(addr);
addr_changed = true;
}
s.base += s.current;
}
}
bool suppressed = false;
if (addr_changed)
suppressed = check_aliases();
if (changed)
refresh_graph_edit();
if (addr_changed)
refresh_display_list(suppressed);
}
// Restore sane state after undoing / redoing
void DataDisp::make_sane()
{
// Activate all user displays. Undo may leave them deactivated.
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0; dn = disp_graph->next(ref))
{
if (dn->is_user_command())
dn->make_active();
}
}
//-----------------------------------------------------------------------------
// Handle output of user commands
//-----------------------------------------------------------------------------
void DataDisp::process_user (StringArray& answers)
{
if (answers.size() == 0)
return;
ProgressMeter s("Updating status displays");
int i;
for (i = 0; i < answers.size(); i++)
s.total += answers[i].length();
i = 0;
bool changed = false;
MapRef ref;
for (int k = disp_graph->first_nr(ref);
k != 0 && i < answers.size();
k = disp_graph->next_nr(ref))
{
DispNode* dn = disp_graph->get(k);
if (dn->is_user_command() && dn->enabled() &&
!dn->deferred() && !dn->constant())
{
string answer = answers[i++];
undo_buffer.add_display(dn->name(), answer);
s.current = answer.length();
if (dn->update(answer))
changed = true;
s.base += s.current;
}
}
if (changed)
refresh_graph_edit();
}
//-----------------------------------------------------------------------------
// Handle change of current scope
//-----------------------------------------------------------------------------
bool DataDisp::need_scope()
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->deferred())
return true;
}
return false;
}
void DataDisp::process_scope(const string& scope)
{
// Fetch deferred displays that are in current scope
IntArray deferred_displays;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->deferred() && dn->scope() == scope)
deferred_displays += dn->disp_nr();
}
if (deferred_displays.size() > 0)
{
// Enable these deferred displays.
sort(deferred_displays, absolute_le);
MString msg = rm("Enabling deferred display");
if (deferred_displays.size() >= 2)
msg += rm("s");
msg += rm(" ");
int i;
for (i = 0; i < deferred_displays.size(); i++)
{
if (i > 0)
{
if (deferred_displays.size() == 2)
msg += rm(" and ");
else if (i == deferred_displays.size() - 1)
msg += rm(", and ");
else
msg += rm(", ");
}
msg += rm(itostring(deferred_displays[i]));
}
set_status_mstring(msg);
for (i = 0; i < deferred_displays.size(); i++)
{
DispNode *dn = disp_graph->get(deferred_displays[i]);
assert(dn != 0 && dn->deferred());
BoxPoint pos = dn->pos();
Command c(new_display_cmd(dn->name(), &pos, dn->depends_on(),
dn->clustered(), dn->plotted()));
c.verbose = false;
c.prompt = false;
gdb_command(c);
}
for (i = 0; i < deferred_displays.size(); i++)
{
disp_graph->del(deferred_displays[i]);
}
}
}
//----------------------------------------------------------------------------
// Display Editor
//----------------------------------------------------------------------------
// Sort LABELS and SELECTED
static void sort(string *labels, bool *selected, int size)
{
// Shell sort -- simple and fast
int h = 1;
do {
h = h * 3 + 1;
} while (h <= size);
do {
h /= 3;
for (int i = h; i < size; i++)
{
string v = labels[i];
bool b = selected[i];
int j;
for (j = i;
j >= h && get_positive_nr(labels[j - h]) > get_positive_nr(v);
j -= h)
{
labels[j] = labels[j - h];
selected[j] = selected[j - h];
}
if (i != j)
{
labels[j] = v;
selected[j] = b;
}
}
} while (h != 1);
}
static string fmt(string s, unsigned size)
{
if (s.length() > size)
s = s.before(int(size));
else if (s.length() < size)
s += replicate(' ', size - s.length());
assert(s.length() == size);
return s;
}
static int max_width(const StringArray& s)
{
int w = 0;
for (int i = 0; i < s.size(); i++)
w = max(w, s[i].length());
return w;
}
// Create labels for the list
void DataDisp::refresh_display_list(bool silent)
{
if (display_list_w == 0)
return;
int number_of_displays = disp_graph->count_all();
StringArray nums;
StringArray states;
StringArray exprs;
StringArray scopes;
StringArray addrs;
if (number_of_displays > 0)
{
// Add titles
nums += "Num";
states += "State";
exprs += "Expression";
scopes += "Scope";
addrs += "Address";
}
else
{
nums += "";
states += "";
exprs += "";
scopes += "";
addrs += "";
}
MapRef ref;
int k;
for (k = disp_graph->first_nr(ref); k != 0; k = disp_graph->next_nr(ref))
{
DispNode* dn = disp_graph->get(k);
nums += itostring(dn->disp_nr()) + ":";
if (dn->deferred())
states += "deferred";
else if (!dn->active())
states += "not active";
else if (dn->clustered())
states += "clustered";
else if (dn->hidden() && dn->alias_of != 0)
states += "alias of " + itostring(dn->alias_of);
else if (dn->enabled())
states += "enabled";
else
states += "disabled";
exprs += dn->name();
scopes += dn->scope();
addrs += dn->addr();
}
int nums_width = max_width(nums);
int exprs_width = max_width(exprs) + 1;
int states_width = max_width(states) + 1;
int scopes_width = max_width(scopes) + 1;
int addrs_width = max_width(addrs);
string *label_list = new string[number_of_displays + 1];
bool *selected_list = new bool[number_of_displays + 1];
// Set titles
int display_count = 0;
string line;
if (number_of_displays > 0)
{
line = fmt(nums[display_count], nums_width)
+ " " + fmt(exprs[display_count], exprs_width)
+ " " + fmt(states[display_count], states_width)
+ " " + fmt(scopes[display_count], scopes_width);
if (detect_aliases)
line += " " + fmt(addrs[display_count], addrs_width);
}
else
{
line = "No displays. ";
}
label_list [display_count] = line;
selected_list[display_count] = false;
display_count++;
int selected_displays = 0; // Number of selected displays
int index_selected = -1; // Index of single selected display
// Set contents
for (k = disp_graph->first_nr(ref); k != 0; k = disp_graph->next_nr(ref))
{
DispNode* dn = disp_graph->get(k);
line = fmt(nums[display_count], nums_width)
+ " " + fmt(exprs[display_count], exprs_width)
+ " " + fmt(states[display_count], states_width)
+ " " + fmt(scopes[display_count], scopes_width);
if (detect_aliases)
line += " " + fmt(addrs[display_count], addrs_width);
bool select = selected(dn);
label_list [display_count] = line;
selected_list[display_count] = select;
if (select)
{
selected_displays++;
index_selected = display_count;
}
display_count++;
}
sort(label_list + 1, selected_list + 1, display_count - 1);
setLabelList(display_list_w, label_list, selected_list, display_count,
number_of_displays > 0, false);
if (!silent)
{
// Setup status line
MString msg;
if (selected_displays == 1)
{
// Show info about single selected display
DispNode *dn = selected_node();
DispValue *dv = 0;
if (dn != 0)
{
if (dn->disabled())
dv = dn->value();
else
dv = dn->selected_value();
}
if (dv != 0)
{
DataDispCount count(disp_graph);
// Value within display selected
msg = rm("In display " + nums[index_selected] + " ");
string title = dv->full_name();
// shorten(title, DispBox::max_display_title_length);
msg += tt(title);
msg += rm(" (double-click to ");
if (dv->type() == Pointer && !dv->collapsed())
msg += rm("dereference");
else if (count.selected_collapsed > 0)
msg += rm("show more");
else
msg += rm("hide");
msg += rm(")");
}
else
{
// Display selected
msg = rm("Display " + nums[index_selected] + " ");
string title = exprs[index_selected];
// shorten(title, DispBox::max_display_title_length);
msg += tt(title);
msg += rm(" (" + states[index_selected]);
if (scopes[index_selected] != "")
{
msg += rm(", scope ");
msg += tt(scopes[index_selected]);
}
if (detect_aliases && addrs[index_selected] != "")
{
msg += rm(", address ");
msg += tt(addrs[index_selected]);
}
msg += rm(")");
}
set_status_mstring(msg);
}
else if (selected_displays > 1)
{
// Show info about multiple selected displays
msg = rm("Displays ");
IntArray displays;
for (k = disp_graph->first_nr(ref); k != 0;
k = disp_graph->next_nr(ref))
{
DispNode* dn = disp_graph->get(k);
if (selected(dn))
displays += dn->disp_nr();
}
sort(displays);
assert(displays.size() == selected_displays);
for (k = 0; k < displays.size(); k++)
{
if (k > 0)
{
if (displays.size() == 2)
msg += rm(" and ");
else if (k == displays.size() - 1)
msg += rm(", and ");
else
msg += rm(", ");
}
msg += rm(itostring(displays[k]));
}
msg += rm(" (" + itostring(displays.size())
+ " displays)");
set_status_mstring(msg);
}
}
delete[] label_list;
delete[] selected_list;
}
void DataDisp::EditDisplaysCB(Widget, XtPointer, XtPointer)
{
manage_and_raise(edit_displays_dialog_w);
}
//----------------------------------------------------------------------------
// Value Editor
//----------------------------------------------------------------------------
struct SetInfo {
string name; // The variable to be set
Widget text; // The widget containing the value
Widget dialog; // The prompt dialog used
bool running; // True if a command has been submitted
};
void DataDisp::DeleteSetInfoCB(Widget, XtPointer client_data, XtPointer)
{
SetInfo *info = (SetInfo *)client_data;
info->dialog = 0;
info->text = 0;
if (info->running)
{
// The command is still running - don't delete info now
}
else
{
delete info;
}
}
void DataDisp::setCB(Widget w, XtPointer, XtPointer)
{
if (!gdb->has_assign_command())
return;
string name;
DispValue *disp_value = selected_value();
if (disp_value != 0)
name = disp_value->full_name();
else
name = source_arg->get_string();
bool can_set = (name != "") && !is_file_pos(name);
if (!can_set)
return;
string value = gdbValue(name);
if (value == NO_GDB_ANSWER)
{
value = ""; // GDB is busy - don't show old value
}
else if (!is_valid(value, gdb))
{
post_gdb_message(value);
value = ""; // Variable cannot be accessed
}
value = assignment_value(value);
// Make sure the old value is saved in the history
add_to_history(gdb->assign_command(name, value));
SetInfo *info = new SetInfo;
info->name = name;
info->running = false;
Arg args[10];
int arg = 0;
XtSetArg(args[arg], XmNdeleteResponse, XmDESTROY); arg++;
XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
info->dialog =
verify(XmCreatePromptDialog(find_shell(w), "set_dialog", args, arg));
Delay::register_shell(info->dialog);
XtAddCallback(info->dialog, XmNdestroyCallback,
DeleteSetInfoCB, XtPointer(info));
if (lesstif_version <= 79)
XtUnmanageChild(XmSelectionBoxGetChild(info->dialog,
XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(info->dialog,
XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(info->dialog,
XmDIALOG_SELECTION_LABEL));
arg = 0;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
XtSetArg(args[arg], XmNborderWidth, 0); arg++;
XtSetArg(args[arg], XmNadjustMargin, False); arg++;
Widget box = verify(XmCreateRowColumn(info->dialog, "box", args, arg));
XtManageChild(box);
arg = 0;
MString prompt = bf("Set value of ") + tt(name);
XtSetArg(args[arg], XmNalignment, XmALIGNMENT_BEGINNING); arg++;
XtSetArg(args[arg], XmNlabelString, prompt.xmstring()); arg++;
Widget label = verify(XmCreateLabel(box, "label", args, arg));
XtManageChild(label);
arg = 0;
XtSetArg(args[arg], XmNvalue, value.chars()); arg++;
info->text = verify(CreateComboBox(box, "text", args, arg));
XtManageChild(info->text);
tie_combo_box_to_history(info->text, set_history_filter);
XtAddCallback(info->dialog, XmNokCallback, setDCB, XtPointer(info));
XtAddCallback(info->dialog, XmNapplyCallback, setDCB, XtPointer(info));
XtAddCallback(info->dialog, XmNhelpCallback, ImmediateHelpCB, 0);
XtAddCallback(info->dialog, XmNcancelCallback,
DestroyThisCB, XtPointer(info->dialog));
Widget apply = XmSelectionBoxGetChild(info->dialog, XmDIALOG_APPLY_BUTTON);
XtManageChild(apply);
manage_and_raise(info->dialog);
}
void DataDisp::SetDone(const string& complete_answer, void *qu_data)
{
SetInfo *info = (SetInfo *)qu_data;
info->running = false;
if (info->dialog == 0)
{
// Dialog has been destroyed while the command was in the queue
delete info;
return;
}
if (complete_answer == NO_GDB_ANSWER)
return; // Command was canceled - keep dialog open
if (!is_valid(complete_answer, gdb))
return; // Bad value - keep dialog open
// All done - pop down dialog
XtDestroyWidget(info->dialog);
}
void DataDisp::setDCB(Widget, XtPointer client_data, XtPointer call_data)
{
SetInfo *info = (SetInfo *)client_data;
if (info->running)
return; // Already running with a value
XmSelectionBoxCallbackStruct *cbs =
(XmSelectionBoxCallbackStruct *)call_data;
String value_s = XmTextFieldGetString(info->text);
string value(value_s);
XtFree(value_s);
Command c(gdb->assign_command(info->name, value), last_origin);
if (cbs->reason != XmCR_APPLY)
{
// We've pressed OK => destroy widget as soon as command completes.
info->running = true;
c.callback = SetDone;
c.data = XtPointer(info);
}
gdb_command(c);
}
//----------------------------------------------------------------------------
// Helpers for user displays
//-----------------------------------------------------------------------------
bool DataDisp::have_user_display(const string& name)
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->user_command() == name)
return true;
}
return false;
}
void DataDisp::new_user_display(const string& name)
{
// Check for duplicates
if (have_user_display(name))
return;
gdb_command("graph display `" + name + "`", last_origin);
}
void DataDisp::delete_user_display(const string& name)
{
IntArray killme;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->user_command() == name)
{
killme += dn->disp_nr();
}
}
delete_display(killme);
refresh_graph_edit();
}
//----------------------------------------------------------------------------
// Language changed - re-label buttons
//----------------------------------------------------------------------------
void DataDisp::language_changedHP(Agent *source, void *, void *)
{
GDBAgent *gdb = ptr_cast(GDBAgent, source);
(void) gdb; // Use it
assert(gdb != 0);
string arg = source_arg->get_string();
if (selected_value() != 0)
arg = selected_value()->full_name();
string label("Display " + deref(arg, "()"));
set_label(shortcut_menu[ShortcutItms::Dereference2].widget, label);
set_label(node_popup[NodeItms::Dereference].widget, label);
set_label(display_area[DisplayItms::Dereference].widget, label);
set_label(graph_cmd_area[CmdItms::Dereference].widget, label);
}
//----------------------------------------------------------------------------
// Titles
//----------------------------------------------------------------------------
// Refresh titles after change in APP_DATA
void DataDisp::refresh_titles()
{
bool changed = disp_graph->refresh_titles();
if (changed)
refresh_graph_edit();
}
//----------------------------------------------------------------------------
// Display Clustering
//----------------------------------------------------------------------------
// Set whether aliases are to be detected
void DataDisp::set_cluster_displays(bool value)
{
if (value == cluster_displays)
return;
cluster_displays = value;
if (cluster_displays)
{
// Cluster all independent data displays
int target_cluster = 0;
MapRef ref;
for (DispNode *dn = disp_graph->first(ref);
dn != 0; dn = disp_graph->next(ref))
{
if (dn->is_user_command())
continue; // No data display
if (dn->firstTo() != 0 && dn->firstTo()->from() != dn)
continue; // Dependent display
if (dn->clustered())
continue; // Already clustered
if (target_cluster == 0)
target_cluster = current_cluster();
disp_graph->cluster(dn, target_cluster);
}
if (target_cluster != 0)
refresh_graph_edit();
}
else
{
// Uncluster all
IntArray all_clusters;
get_all_clusters(all_clusters);
IntArray killme;
for (int i = 0; i < all_clusters.size(); i++)
{
DispNode *cluster = disp_graph->get(all_clusters[i]);
if (cluster != 0)
{
// Delete cluster
killme += all_clusters[i];
}
}
if (killme.size() > 0)
{
delete_display(killme);
refresh_graph_edit();
}
}
}
void DataDisp::toggleClusterSelectedCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
DataDispCount count(disp_graph);
if (count.selected_unclustered > 0)
{
clusterSelectedCB(w, client_data, call_data);
}
else
{
unclusterSelectedCB(w, client_data, call_data);
}
}
// Uncluster selected nodes (and clusters)
void DataDisp::unclusterSelectedCB(Widget, XtPointer, XtPointer)
{
// Uncluster selected nodes
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (selected(dn) && dn->clustered())
{
// Force cluster to be redisplayed
DispNode *cluster = disp_graph->get(dn->clustered());
if (cluster != 0)
cluster->set_last_refresh();
// Uncluster display
disp_graph->uncluster(dn);
}
}
// Delete selected clusters
IntArray all_clusters;
get_all_clusters(all_clusters);
IntArray killme;
for (int i = 0; i < all_clusters.size(); i++)
{
DispNode *cluster = disp_graph->get(all_clusters[i]);
if (cluster != 0 && selected(cluster))
{
// Delete cluster
killme += all_clusters[i];
}
}
delete_display(killme);
refresh_args();
refresh_graph_edit();
}
// Cluster selected nodes into a new cluster
void DataDisp::clusterSelectedCB(Widget, XtPointer, XtPointer)
{
int target_cluster = 0;
IntArray all_clusters;
get_all_clusters(all_clusters);
// If we have a selected cluster, choose this one as a target
for (int i = 0; i < all_clusters.size(); i++)
{
DispNode *cluster = disp_graph->get(all_clusters[i]);
if (cluster != 0 && selected(cluster))
{
target_cluster = all_clusters[i];
break;
}
}
if (target_cluster == 0)
{
// No target cluster selected - make a new one
target_cluster = new_cluster();
}
// Cluster all selected displays into the current one
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (!dn->is_user_command() && selected(dn))
disp_graph->cluster(dn, target_cluster);
}
refresh_graph_edit();
}
//----------------------------------------------------------------------------
// Alias Detection
//----------------------------------------------------------------------------
// True iff aliases are to be checked regardless of address changes
bool DataDisp::force_check_aliases = false;
// Set whether aliases are to be detected
void DataDisp::set_detect_aliases(bool value)
{
if (value == detect_aliases)
return;
detect_aliases = value;
if (detect_aliases)
{
// Re-check for aliases
force_check_aliases = true;
refresh_addr();
}
else
{
bool changed = false;
MapRef ref;
for (int k = disp_graph->first_nr(ref);
k != 0;
k = disp_graph->next_nr(ref))
{
// Unmerge all displays
changed = unmerge_display(k) || changed;
}
if (changed)
refresh_graph_edit();
}
}
// Add address-printing commands to CMDS
int DataDisp::add_refresh_addr_commands(StringArray& cmds, DispNode *dn)
{
if (!detect_aliases)
return 0;
int initial_size = cmds.size();
if (dn != 0)
{
if (dn->active() && !dn->is_user_command())
{
string addr = gdb->address_expr(dn->name());
if (addr != "")
cmds += gdb->print_command(addr);
}
}
else
{
MapRef ref;
for (dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
add_refresh_addr_commands(cmds, dn);
}
}
return cmds.size() - initial_size;
}
// Refresh all addresses
void DataDisp::refresh_addr(DispNode *dn)
{
if (refresh_addr_timer != 0)
{
XtRemoveTimeOut(refresh_addr_timer);
refresh_addr_timer = 0;
dn = 0;
}
RefreshAddrCB(XtPointer(dn), (XtIntervalId *)0);
}
void DataDisp::RefreshAddrCB(XtPointer client_data, XtIntervalId *id)
{
if (id != 0)
{
assert (*id == refresh_addr_timer);
refresh_addr_timer = 0;
}
DispNode *dn = (DispNode *)client_data;
bool ok = false;
bool sent = false;
if (gdb->isReadyWithPrompt())
{
StringArray cmds;
VoidArray dummy;
add_refresh_addr_commands(cmds, dn);
if (cmds.size() > 0)
{
while (dummy.size() < cmds.size())
dummy += (void *)PROCESS_ADDR;
static RefreshInfo info;
info.verbose = false;
info.prompt = false;
ok = gdb->send_qu_array(cmds, dummy, cmds.size(),
refresh_displayOQAC, (void *)&info);
sent = cmds.size() > 0;
}
else
{
// No refreshing commands - rely on addresses as read
bool suppressed = check_aliases();
force_check_aliases = false;
refresh_display_list(suppressed);
ok = true;
}
}
if (!ok)
{
// Commands not sent - try again in 50 ms
refresh_addr_timer =
XtAppAddTimeOut(XtWidgetToApplicationContext(graph_edit),
50, RefreshAddrCB, client_data);
}
if (sent)
{
// At least one command sent - disable redisplay until we have
// processed all addresses
graphEditEnableRedisplay(graph_edit, False);
}
}
// Handle output of addr commands
void DataDisp::process_addr (StringArray& answers)
{
int i = 0;
bool changed = false;
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0 && i < answers.size();
dn = disp_graph->next(ref))
{
if (dn->active() && !dn->is_user_command())
{
string addr = answers[i++];
if (addr.contains('(', 0) || addr.contains('{', 0))
{
// Skip type prefix
read_token(addr);
}
addr = addr.from(rxaddress);
addr = addr.through(rxaddress);
undo_buffer.add_display_address(dn->name(), addr);
if (dn->addr() != addr)
{
dn->set_addr(addr);
changed = true;
}
}
}
bool suppressed = false;
if (changed || force_check_aliases)
{
suppressed = check_aliases();
force_check_aliases = false;
}
// Re-enable redisplay
graphEditEnableRedisplay(graph_edit, True);
if (changed)
refresh_display_list(suppressed);
}
// Check for aliases after change; return true iff displays were suppressed
bool DataDisp::check_aliases()
{
if (!detect_aliases)
return false;
// Group displays into equivalence classes depending on their
// address and their structure.
// EQUIVALENCES is an assoc table classed according to addresses.
// Each entry is a list of tables. Each table contains
// structurally equivalent display numbers.
// If the `typedAliases' resource is `off', all displays
// go into one category, regardless of structure.
StringIntArrayArrayAssoc equivalences;
MapRef ref;
for (int k = disp_graph->first_nr(ref);
k != 0;
k = disp_graph->next_nr(ref))
{
DispNode *dn = disp_graph->get(k);
if (dn->value() == 0)
continue;
if (dn != 0 && dn->alias_ok())
{
IntArrayArray& list = equivalences[dn->addr()];
// Search for structurally equivalent entries in DISPLAY_TABLE.
bool added = false;
for (int i = 0; !added && i < list.size(); i++)
{
IntArray& displays = list[i];
assert (displays.size() > 0);
DispNode *d1 = disp_graph->get(displays[0]);
if (d1->value() == 0)
continue;
if (!app_data.typed_aliases ||
dn->value()->structurally_equal(d1->value()))
{
displays += k;
added = true;
}
}
if (!added)
{
IntArray new_displays;
new_displays += k;
list += new_displays;
}
}
}
// Merge displays with identical address.
bool changed = false;
bool suppressed = false;
for (StringIntArrayArrayAssocIter iter(equivalences); iter.ok(); iter++)
{
string addr = iter.key();
IntArrayArray& list = iter.value();
assert(list.size() > 0);
for (int i = 0; i < list.size(); i++)
{
IntArray& displays = list[i];
assert(displays.size() > 0);
if (addr == "" || displays.size() == 1)
{
// No address or just one display -- unmerge them
for (int k = 0; k < displays.size(); k++)
changed = unmerge_display(displays[k]) || changed;
}
else
{
// Multiple displays at one location
merge_displays(displays, changed, suppressed);
}
}
}
if (changed)
refresh_graph_edit(suppressed);
return suppressed;
}
// Return last change, or INT_MAX if hidden
int DataDisp::last_change_of_disp_nr(int disp_nr)
{
DispNode *dn = disp_graph->get(disp_nr);
assert(dn != 0);
if (dn->hidden())
return INT_MAX;
return dn->last_change();
}
// Sort DISP_NRS according to the last change
void DataDisp::sort_last_change(IntArray& disp_nrs)
{
// Shell sort -- simple and fast
int h = 1;
do {
h = h * 3 + 1;
} while (h <= disp_nrs.size());
do {
h /= 3;
for (int i = h; i < disp_nrs.size(); i++)
{
int v = disp_nrs[i];
int j;
for (j = i; j >= h && last_change_of_disp_nr(disp_nrs[j - h]) >
last_change_of_disp_nr(v); j -= h)
disp_nrs[j] = disp_nrs[j - h];
if (i != j)
disp_nrs[j] = v;
}
} while (h != 1);
}
// Merge displays in DISPLAYS. Set CHANGED iff changed. Set
// SUPPRESSED if displays were suppressed.
void DataDisp::merge_displays(IntArray displays,
bool& changed, bool& suppressed)
{
assert(displays.size() > 0);
// Hide all aliases except the node which has changed least recently.
sort_last_change(displays);
int i;
#if 0
for (i = 0; i < displays.size(); i++)
{
clog << "Last change of display " << displays[i]
<< ": " << last_change_of_disp_nr(displays[i]) << "\n";
}
#endif
DispNode *d0 = disp_graph->get(displays[0]);
if (d0->active() && d0->hidden())
{
// All aliases are hidden. Make sure we see at least the
// least recently changed one.
changed = unmerge_display(displays[0]) || changed;
}
IntArray suppressed_displays;
for (i = 1; i < displays.size(); i++)
{
int disp_nr = displays[i];
DispNode *dn = disp_graph->get(disp_nr);
if (!dn->active())
continue; // Out of scope
bool hidden = dn->hidden();
if (!hidden && dn->firstTo() == 0)
{
// There is no edge pointing at this node. Don't merge it
// because it would simply disappear otherwise.
changed = unmerge_display(disp_nr) || changed;
}
else
{
bool c = disp_graph->alias(graph_edit, displays[0], disp_nr);
if (c)
{
if (!hidden)
suppressed_displays += disp_nr;
changed = true;
}
}
}
if (suppressed_displays.size() > 0)
{
suppressed = true;
sort(suppressed_displays);
// Some displays have been suppressed. Generate appropriate message.
MString msg = rm("Suppressing ");
if (suppressed_displays.size() == 1)
{
DispNode *node = disp_graph->get(suppressed_displays[0]);
msg += rm("display " + itostring(node->disp_nr()) + ": ");
msg += tt(node->name());
}
else if (suppressed_displays.size() == 2)
{
msg += rm("displays "
+ itostring(suppressed_displays[0])
+ " and "
+ itostring(suppressed_displays[1]));
}
else
{
msg += rm("displays ");
for (i = 1; i < suppressed_displays.size(); i++)
{
if (i == suppressed_displays.size() - 1)
msg += rm(", and ");
else if (i > 1)
msg += rm(", ");
msg += rm(itostring(suppressed_displays[i]));
}
}
if (suppressed_displays.size() == 1)
msg += rm(" because it is an alias");
else
msg += rm(" because they are aliases");
DispNode *of = disp_graph->get(displays[0]);
msg += rm(" of display " + itostring(of->disp_nr()) + ": ");
msg += tt(of->name());
set_status_mstring(msg);
}
}
bool DataDisp::unmerge_display(int disp_nr)
{
return disp_graph->unalias(disp_nr);
}
void DataDisp::PreLayoutCB(Widget w, XtPointer, XtPointer)
{
if (detect_aliases)
{
// Don't redisplay while or after layouting
graphEditEnableRedisplay(w, False);
}
}
// Re-enable aliases after layouting
void DataDisp::PostLayoutCB(Widget w, XtPointer, XtPointer)
{
if (detect_aliases)
{
// Unmerge and re-merge all displays
MapRef ref;
for (int k = disp_graph->first_nr(ref);
k != 0;
k = disp_graph->next_nr(ref))
{
unmerge_display(k);
}
check_aliases();
// Okay - we can redisplay now
graphEditEnableRedisplay(w, True);
refresh_graph_edit();
}
}
// True iff we have some selection
bool DataDisp::have_selection()
{
MapRef ref;
for (DispNode* dn = disp_graph->first(ref);
dn != 0;
dn = disp_graph->next(ref))
{
if (dn->selected())
return true;
}
return false;
}
// Select node, copying selection state from NR
void DataDisp::select_node(DispNode *dn, int nr)
{
dn->selected() = true;
if (nr == 0)
return;
DispNode *src = disp_graph->get(nr);
if (src == 0)
return;
dn->copy_selection_state(*src);
refresh_args(true);
}
//----------------------------------------------------------------------------
// Bumper
//----------------------------------------------------------------------------
bool DataDisp::bump_displays = true;
static bool Yes(RegionGraphNode *, const BoxSize&)
{
return true;
}
// This one is called whenever NODE is to be resized to NEWSIZE
bool DataDisp::bump(RegionGraphNode *node, const BoxSize& newSize)
{
if (!bump_displays)
return true; // Okay
if (node->pos() == BoxPoint())
return true; // No valid position yet
DispNode *dn = ptr_cast(DispNode, node);
if (dn != 0 && (!dn->active() || dn->clustered()))
return true; // Clustered or inactive
const GraphGC& gc = graphEditGetGraphGC(graph_edit);
BoxRegion oldRegion = node->region(gc);
// Do the resize, but don't get called recursively
RegionGraphNode::ResizeCB = Yes;
node->resize(newSize);
RegionGraphNode::ResizeCB = bump;
// Let origin remain constant
node->moveTo(node->originToPos(oldRegion.origin(), gc));
// Move all nodes that are right or below NODE such that their
// distance to NODE remains constant.
// DELTA is the difference between old and new size
BoxSize delta = node->space(gc) - oldRegion.space();
// NODE_ORIGIN is the (old) upper left corner of NODE
// BoxPoint node_origin = oldRegion.origin();
// BUMPER is the (old) lower right corner of NODE
BoxPoint node_bumper = oldRegion.origin() + oldRegion.space();
for (GraphNode *r = disp_graph->firstNode();
r != 0; r = disp_graph->nextNode(r))
{
if (r == node)
continue;
// If R is inactive or clustered, don't bump it
DispNode *rn = ptr_cast(DispNode, r);
if (rn != 0 && (!rn->active() || rn->clustered()))
continue;
// If ORIGIN (the upper left corner of R) is right of BUMPER,
// move R DELTA units to the right. If it is below BUMPER,
// move R DELTA units down.
BoxPoint r_origin = r->origin(gc);
// BoxPoint r_bumper = r->origin(gc) + r->space(gc);
BoxPoint r_pos = r->pos();
if (r_origin[X] > node_bumper[X])
r_pos[X] += delta[X];
if (r_origin[Y] > node_bumper[Y])
r_pos[Y] += delta[Y];
r->moveTo(r_pos);
}
// All is done - don't use default behavior.
return false;
}
//----------------------------------------------------------------------------
// Constructor
//----------------------------------------------------------------------------
DataDisp::DataDisp(Widget parent, Widget& data_buttons_w)
{
XtAppContext app_context = XtWidgetToApplicationContext(parent);
registerOwnConverters();
// Init globals
StringBox::fontTable = new FontTable (XtDisplay(parent));
DispBox::vsllib_name = app_data.vsl_library;
DispBox::vsllib_path = app_data.vsl_path;
DispBox::vsllib_base_defs = app_data.vsl_base_defs;
DispBox::vsllib_defs = app_data.vsl_defs;
// Create graph
disp_graph = new DispGraph();
disp_graph->addHandler(DispGraph_Empty, no_displaysHP);
// Create graph toolbar
unsigned char label_type = XmSTRING;
if (app_data.button_captions || app_data.button_images)
label_type = XmPIXMAP;
Widget arg_label = 0;
if (graph_cmd_w == 0 && !app_data.toolbars_at_bottom)
{
graph_cmd_w = create_toolbar(parent, "graph",
graph_cmd_area, 0, arg_label, graph_arg,
label_type);
}
// Add buttons
if (data_buttons_w == 0 && !app_data.toolbars_at_bottom)
data_buttons_w =
make_buttons(parent, "data_buttons", app_data.data_buttons);
// Create graph editor
Arg args[10];
int arg = 0;
XtSetArg (args[arg], XtNgraph, (Graph *)disp_graph); arg++;
if (app_data.panned_graph_editor)
{
graph_edit = createPannedGraphEdit(parent, "graph_edit", args, arg);
graph_form_w = pannerOfGraphEdit(graph_edit);
}
else
{
graph_edit = createScrolledGraphEdit(parent, "graph_edit", args, arg);
graph_form_w = scrollerOfGraphEdit(graph_edit);
}
set_last_origin(graph_edit);
// Add actions
XtAppAddActions (app_context, actions, XtNumber (actions));
XtManageChild (graph_edit);
// Create buttons
registerOwnConverters();
if (graph_cmd_w == 0)
{
graph_cmd_w = create_toolbar(parent, "graph",
graph_cmd_area, 0, arg_label, graph_arg,
label_type);
}
if (arg_label != 0)
{
XtAddCallback(arg_label, XmNactivateCallback,
SelectionLostCB, XtPointer(0));
XtAddCallback(arg_label, XmNactivateCallback,
ClearTextFieldCB, graph_arg->text());
}
// Create (unmanaged) selection widget
graph_selection_w =
verify(XmCreateText(graph_cmd_w, "graph_selection", NULL, 0));
XtAddCallback(graph_selection_w, XmNlosePrimaryCallback,
SelectionLostCB, XtPointer(0));
}
void DataDisp::create_shells()
{
Arg args[10];
Cardinal arg = 0;
// Create menus
graph_popup_w =
MMcreatePopupMenu(graph_edit, "graph_popup", graph_popup);
InstallButtonTips(graph_popup_w);
node_popup_w =
MMcreatePopupMenu(graph_edit, "node_popup", node_popup);
InstallButtonTips(node_popup_w);
shortcut_popup_w =
MMcreatePopupMenu(graph_edit, "shortcut_popup", shortcut_popup1);
InstallButtonTips(shortcut_popup_w);
disp_graph->callHandlers();
// Create display editor
arg = 0;
XtSetArg(args[arg], XmNvisibleItemCount, 0); arg++;
edit_displays_dialog_w =
verify(createTopLevelSelectionDialog(find_shell(graph_edit),
"edit_displays_dialog",
args, arg));
Delay::register_shell(edit_displays_dialog_w);
XtUnmanageChild(XmSelectionBoxGetChild(edit_displays_dialog_w,
XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(edit_displays_dialog_w,
XmDIALOG_CANCEL_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(edit_displays_dialog_w,
XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(edit_displays_dialog_w,
XmDIALOG_SELECTION_LABEL));
XtUnmanageChild(XmSelectionBoxGetChild(edit_displays_dialog_w,
XmDIALOG_LIST_LABEL));
display_list_w =
XmSelectionBoxGetChild(edit_displays_dialog_w, XmDIALOG_LIST);
if (app_data.flat_dialog_buttons)
{
for (MMDesc *item = display_area; item != 0 && item->name != 0; item++)
{
if ((item->type & MMTypeMask) == MMPush)
item->type = (MMFlatPush | (item->type & ~MMTypeMask));
}
}
Widget buttons = verify(MMcreateWorkArea(edit_displays_dialog_w,
"buttons", display_area));
XtVaSetValues(buttons,
XmNmarginWidth, 0,
XmNmarginHeight, 0,
XmNborderWidth, 0,
XmNshadowThickness, 0,
XmNspacing, 0,
NULL);
MMaddCallbacks (display_area);
MMaddHelpCallback(display_area, ImmediateHelpCB);
register_menu_shell(display_area);
// Add widget callbacks
XtAddCallback(graph_edit, XtNpreSelectionCallback,
DoubleClickCB, XtPointer(this));
XtAddCallback(graph_edit, XtNselectionChangedCallback,
UpdateDisplayEditorSelectionCB, XtPointer(this));
XtAddCallback(graph_edit, XtNcompareNodesCallback,
CompareNodesCB, XtPointer(this));
XtAddCallback(graph_edit, XtNpreLayoutCallback,
PreLayoutCB, XtPointer(this));
XtAddCallback(graph_edit, XtNpostLayoutCallback,
PostLayoutCB, XtPointer(this));
if (display_list_w != 0)
{
XtAddCallback(display_list_w,
XmNsingleSelectionCallback,
UpdateGraphEditorSelectionCB,
0);
XtAddCallback(display_list_w,
XmNmultipleSelectionCallback,
UpdateGraphEditorSelectionCB,
0);
XtAddCallback(display_list_w,
XmNextendedSelectionCallback,
UpdateGraphEditorSelectionCB,
0);
XtAddCallback(display_list_w,
XmNbrowseSelectionCallback,
UpdateGraphEditorSelectionCB,
0);
}
if (edit_displays_dialog_w != 0)
{
XtAddCallback(edit_displays_dialog_w,
XmNokCallback,
UnmanageThisCB,
edit_displays_dialog_w);
XtAddCallback(edit_displays_dialog_w,
XmNhelpCallback,
ImmediateHelpCB,
0);
}
// Add graph callbacks
RegionGraphNode::ResizeCB = bump;
// Reset argument field and display editor buttons
set_args();
}