[PATCH] 542503 - Initial support for monitoring expressions
- From: Dodji Seketeli <dodji seketeli org>
- To: Nemiver Development <nemiver-list gnome org>
- Subject: [PATCH] 542503 - Initial support for monitoring expressions
- Date: Sat, 16 Jun 2012 19:39:56 +0200
Hello,
The patch below implements the "Expression Monitor" feature that I
have been hacking on for a little while now. I believe I have tested
and debugged it enough to find it suitable to be merged into master.
A little screen shot is available at
http://people.gnome.org/~dodji/nemiver/monitor-expression.png. On
that image, the "expression monitor" is on the right hand side of the
debugger. The monitored expression updates itself automatically at
each stop of the program being debugged.
Applied to master.
* src/dbgengine/nmv-i-debugger.h (Variable::id)
(Variable::is_morally_root): New methods.
* src/persp/dbgperspective/ui/exprinspectordialog.ui: Add a new
"add to monitor" button.
* src/persp/dbgperspective/nmv-expr-monitor.{cc,h}: New file.
* src/persp/dbgperspective/menus/exprmonitorpopup.xml: New
description file for the contextual menu.
* src/persp/dbgperspective/Makefile.am: Add
nmv-expr-monitor.{h,cc} and exprmonitorpopup.xml to the build
system.
* po/POTFILES.in: Add src/persp/dbgperspective/nmv-expr-monitor.cc
to the list of translatable source files.
* src/persp/dbgperspective/nmv-expr-inspector-dialog.h: Add
comments.
(ExprInspectorDialog::FunctionalityFlags): New enum.
(ExprInspectorDialog::functionality_mask): New accessors.
* src/persp/dbgperspective/nmv-expr-inspector-dialog.cc
(ExprInspectorDialog::Priv::{add_to_monitor_button,expr_monitor_requested,
fun_mask}): New members.
(ExprInspectorDialog::Priv::Priv): Initialize new fun_mask member.
(ExprInspectorDialog::Priv::on_do_monitor_button_clicked)
(ExprInspectorDialog::Priv::functionality_mask): New functions.
(ExprInspectorDialog::Priv::build_dialog): Initialize the
add_to_monitor button.
(ExprInspectorDialog::Priv::on_var_name_changed_signal): Inspect
button is now controlled by FUNCTIONALITY_EXPR_INSPECTOR flag.
(ExprInspectorDialog::Priv::on_variable_inspected): Add to monitor
button is now controlled by FUNCTIONALITY_EXPR_MONITOR_PICKER
flag.
(ExprInspectorDialog::expr_monitoring_requested)
(ExprInspectorDialog::functionality_mask): New functions.
(ExprInspectorDialog::Priv::on_var_name_changed_signal): Do not
enable/disable the "add to monitor" button based on the presence
of an expression/name in the "variable name" entry field.
(ExprInspectorDialog::Priv::on_variable_inspected): Enable the "add
to monitor" button only when the expression has been inspected.
(ExprInspectorDialog::Priv::on_variable_inspector_cleared): Disable
the "add to monitor" button when the inspector has been cleared.
* src/persp/dbgperspective/menus/menus.xml: Add a menu item to
activate the new variable monitor.
* src/persp/dbgperspective/nmv-dbg-perspective-default-layout.cc
(DBGPerspectiveDefaultLayout::append_view): Style cleanup.
* src/persp/dbgperspective/nmv-variables-utils.h
(unlink_a_variable_row): New function.
* src/persp/dbgperspective/nmv-variables-utils.cc
(update_a_variable_node, ): Add (better) logging.
(find_a_variable): Likewise. Remove dead code.
(unlink_a_variable_row): Add logging. Add const to last
argument.
(update_a_variable): Add logging. Style cleanup.
(set_a_variable): Remove dead code. Style cleanup.
(unlink_a_variable_row): New function.
(visualize_a_variable): Style cleanup.
* src/persp/dbgperspective/nmv-dbg-perspective.h (enum
ViewsIndex::VARS_MONITOR_VIEW_INDEX): New enumerator.
* src/persp/dbgperspective/nmv-dbg-perspective.cc
(DBGPerspective::{on_expr_monitoring_requested,get_vars_monitor_view}):
New functions.
(DBGPerspective::Priv::vars_monitor): New
member.
(DBGPerspective::{on_expr_monitoring_requested,
on_activate_vars_monitor_view, get_vars_monitor_view}): New
functions.
(DBGPerspective::on_going_to_run_target_signal)
(DBGPerspective::clear_status_notebook): Take a boolean saying if
we are re-starting the current program or not. Re-init the
variable monitor. Make the former use the latter.
(DBGPerspective::going_to_run_target_signal): Take a boolean
saying if we are re-starting the current program or not.
(DBGPerspective::detach_from_program): Adjust call to clear to
clear_status_notebook.
(DBGPerspective::restart_local_inferior): Adjust call to
DBGPerspective::going_to_run_target_signal.
(DBGPerspective::execute_program): Remove useless call to
clear_status_notebook. Adjust call to
DBGPerspective::going_to_run_target_signal.
(DBGPerspective::init_actions): Define a new action
to activate the variable monitor view.
(DBGPerspective::add_views_to_layout): Append the variable
monitor view.
(DBGPerspective:::inspect_expression): On the
event of requesting the monitoring of a variable, hook the new
function DBGPerspective::{on_expr_monitoring_requested.
---
po/POTFILES.in | 1 +
src/dbgengine/nmv-i-debugger.h | 32 +-
src/persp/dbgperspective/Makefile.am | 2 +
src/persp/dbgperspective/menus/Makefile.am | 2 +-
.../dbgperspective/menus/exprmonitorpopup.xml | 9 +
src/persp/dbgperspective/menus/menus.xml | 2 +
.../nmv-dbg-perspective-default-layout.cc | 3 +-
src/persp/dbgperspective/nmv-dbg-perspective.cc | 109 ++-
src/persp/dbgperspective/nmv-dbg-perspective.h | 3 +-
.../dbgperspective/nmv-expr-inspector-dialog.cc | 79 ++-
.../dbgperspective/nmv-expr-inspector-dialog.h | 28 +
src/persp/dbgperspective/nmv-expr-monitor.cc | 1306 ++++++++++++++++++++
src/persp/dbgperspective/nmv-expr-monitor.h | 68 +
src/persp/dbgperspective/nmv-variables-utils.cc | 41 +-
src/persp/dbgperspective/nmv-variables-utils.h | 4 +
src/persp/dbgperspective/ui/exprinspectordialog.ui | 17 +-
16 files changed, 1663 insertions(+), 43 deletions(-)
create mode 100644 src/persp/dbgperspective/menus/exprmonitorpopup.xml
create mode 100644 src/persp/dbgperspective/nmv-expr-monitor.cc
create mode 100644 src/persp/dbgperspective/nmv-expr-monitor.h
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1813030..802e250 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -22,6 +22,7 @@ src/persp/dbgperspective/nmv-thread-list.cc
src/persp/dbgperspective/nmv-expr-inspector.cc
src/persp/dbgperspective/nmv-expr-inspector-dialog.cc
src/persp/dbgperspective/nmv-vars-treeview.cc
+src/persp/dbgperspective/nmv-expr-monitor.cc
[type: gettext/glade]src/persp/dbgperspective/ui/callfunctiondialog.ui
[type: gettext/glade]src/persp/dbgperspective/ui/chooseoverloadsdialog.ui
[type: gettext/glade]src/persp/dbgperspective/ui/findtextdialog.ui
diff --git a/src/dbgengine/nmv-i-debugger.h b/src/dbgengine/nmv-i-debugger.h
index 4b48dbd..b9890f5 100644
--- a/src/dbgengine/nmv-i-debugger.h
+++ b/src/dbgengine/nmv-i-debugger.h
@@ -366,7 +366,7 @@ public:
FrameArgsSlot;
typedef sigc::slot<void, const VariableSafePtr> ConstVariableSlot;
- typedef sigc::slot<void, const VariableList> ConstVariableListSlot;
+ typedef sigc::slot<void, const VariableList&> ConstVariableListSlot;
typedef sigc::slot<void, const UString&> ConstUStringSlot;
class Variable : public Object {
@@ -622,6 +622,14 @@ public:
/// \param a_in the new name of backend side counterpart variable object.
void internal_name (const UString &a_in) {m_internal_name = a_in;}
+ /// Return the ID of the variable. The ID is either the
+ /// internal_name of the variable if it has one, or its user
+ /// facing name.
+ const UString& id () const
+ { return internal_name ().empty ()
+ ? name ()
+ : internal_name ();
+ }
IDebugger* debugger () const {return m_debugger;}
void debugger (IDebugger *a_dbg) {m_debugger = a_dbg;}
@@ -656,6 +664,28 @@ public:
return m_parent != 0;
}
+ /// If this variable has a backend counterpart (e.g GDB
+ /// Backend variable object) then, there are cases where a
+ /// variable sub-object can be returned by the backend without
+ /// being linked to its ancestor tree. Such a variable
+ /// appears as being structurally root (it has no parent), but
+ /// is morally a sub-variable. A variable that is
+ /// structurally non-root is also morally non-root.
+ ///
+ /// To know if a variable is morally root, this function
+ /// detects if the fully qualified internal name of the
+ /// variable has dot (".") in it (e.g, "variable.member"). If
+ /// does not, then the function assumes the variable is
+ /// morally root and returns true.
+ bool is_morally_root () const
+ {
+ if (has_parent ())
+ return false;
+ if (internal_name ().empty ())
+ return !has_parent ();
+ return (internal_name ().find (".") == UString::npos);
+ }
+
/// A getter of the parent Variable of the current instance.
const VariableSafePtr parent () const
{
diff --git a/src/persp/dbgperspective/Makefile.am b/src/persp/dbgperspective/Makefile.am
index 8fcfa95..6f2268f 100644
--- a/src/persp/dbgperspective/Makefile.am
+++ b/src/persp/dbgperspective/Makefile.am
@@ -42,6 +42,8 @@ $(h)/nmv-global-vars-inspector-dialog.cc \
$(h)/nmv-global-vars-inspector-dialog.h \
$(h)/nmv-expr-inspector.cc \
$(h)/nmv-expr-inspector.h \
+$(h)/nmv-expr-monitor.cc \
+$(h)/nmv-expr-monitor.h \
$(h)/nmv-breakpoints-view.cc \
$(h)/nmv-breakpoints-view.h \
$(h)/nmv-registers-view.cc \
diff --git a/src/persp/dbgperspective/menus/Makefile.am b/src/persp/dbgperspective/menus/Makefile.am
index a6dc6d8..e3e81e4 100644
--- a/src/persp/dbgperspective/menus/Makefile.am
+++ b/src/persp/dbgperspective/menus/Makefile.am
@@ -8,7 +8,7 @@ callstackpopup.xml \
varinspectorpopup.xml \
localvarsinspectorpopup.todelete.xml \
localvarsinspectorpopup.xml \
-terminalmenu.xml
+terminalmenu.xml exprmonitorpopup.xml
menusdir = @NEMIVER_PLUGINS_DIR@/$(PLUGIN_NAME)/menus
menus_DATA = $(menusfiles)
diff --git a/src/persp/dbgperspective/menus/exprmonitorpopup.xml b/src/persp/dbgperspective/menus/exprmonitorpopup.xml
new file mode 100644
index 0000000..bda9350
--- /dev/null
+++ b/src/persp/dbgperspective/menus/exprmonitorpopup.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<ui>
+ <popup name="ExprMonitorPopup">
+ <menuitem action="AddExpressionMenuItemAction"
+ name="AddExpressionMenuItem"/>
+ <menuitem action="RemoveExpressionsMenuItemAction"
+ name="RemoveExpressionsMenuItem" />
+ </popup>
+</ui>
diff --git a/src/persp/dbgperspective/menus/menus.xml b/src/persp/dbgperspective/menus/menus.xml
index c9a52e2..a790750 100644
--- a/src/persp/dbgperspective/menus/menus.xml
+++ b/src/persp/dbgperspective/menus/menus.xml
@@ -42,6 +42,8 @@
name="ActivateBreakpointsViewMenuItem"/>
<menuitem action="ActivateRegistersViewMenuAction"
name="ActivateRegistersViewMenuItem"/>
+ <menuitem action="ActivateExprMonitorViewMenuAction"
+ name="ActivateExprMonitorViewMenuItem"/>
</menu>
<menu action="DebugMenuAction" name="DebugMenu">
<menuitem action="RunMenuItemAction" name="RunMenuItem"/>
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective-default-layout.cc b/src/persp/dbgperspective/nmv-dbg-perspective-default-layout.cc
index 987a215..cb46de3 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective-default-layout.cc
+++ b/src/persp/dbgperspective/nmv-dbg-perspective-default-layout.cc
@@ -189,8 +189,9 @@ DBGPerspectiveDefaultLayout::append_view (Gtk::Widget &a_widget,
return;
}
- m_priv->views.insert (std::make_pair<int, Gtk::Widget&> (a_index, a_widget));
a_widget.show_all ();
+ m_priv->views.insert (std::make_pair<int, Gtk::Widget&> (a_index,
+ a_widget));
int page_num = m_priv->statuses_notebook->append_page (a_widget, a_title);
m_priv->statuses_notebook->set_current_page (page_num);
}
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.cc b/src/persp/dbgperspective/nmv-dbg-perspective.cc
index d30d922..e226e5d 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.cc
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.cc
@@ -96,6 +96,7 @@
#include "nmv-dbg-perspective-dynamic-layout.h"
#endif // WITH_DYNAMICLAYOUT
#include "nmv-layout-manager.h"
+#include "nmv-expr-monitor.h"
using namespace std;
using namespace nemiver::common;
@@ -113,11 +114,12 @@ const char *STEP_OVER = "nmv-step-over";
const char *STEP_OUT = "nmv-step-out";
// labels for widget tabs in the status notebook
-const char *CONTEXT_VIEW_TITLE = _("Context");
-const char *TARGET_TERMINAL_VIEW_TITLE = _("Target Terminal");
-const char *BREAKPOINTS_VIEW_TITLE = _("Breakpoints");
-const char *REGISTERS_VIEW_TITLE = _("Registers");
-const char *MEMORY_VIEW_TITLE = _("Memory");
+const char *CONTEXT_VIEW_TITLE = _("Context");
+const char *TARGET_TERMINAL_VIEW_TITLE = _("Target Terminal");
+const char *BREAKPOINTS_VIEW_TITLE = _("Breakpoints");
+const char *REGISTERS_VIEW_TITLE = _("Registers");
+const char *MEMORY_VIEW_TITLE = _("Memory");
+const char *EXPR_MONITOR_VIEW_TITLE = _("Expression Monitor");
const char *CAPTION_SESSION_NAME = "captionname";
const char *SESSION_NAME = "sessionname";
@@ -283,6 +285,7 @@ private:
void on_toggle_breakpoint_enabled_action ();
void on_toggle_countpoint_action ();
void on_inspect_expression_action ();
+ void on_expr_monitoring_requested (const IDebugger::VariableSafePtr);
void on_call_function_action ();
void on_find_text_response_signal (int);
void on_breakpoint_delete_action
@@ -299,7 +302,7 @@ private:
void on_debugger_not_started_signal ();
- void on_going_to_run_target_signal ();
+ void on_going_to_run_target_signal (bool);
void on_sv_markers_region_clicked_signal
(int a_line, bool a_dialog_requested,
@@ -421,6 +424,7 @@ private:
#ifdef WITH_MEMORYVIEW
void on_activate_memory_view ();
#endif // WITH_MEMORYVIEW
+ void on_activate_expr_monitor_view ();
void on_activate_global_variables ();
void on_default_config_read ();
@@ -441,7 +445,7 @@ private:
void init_body ();
void init_signals ();
void init_debugger_signals ();
- void clear_status_notebook ();
+ void clear_status_notebook (bool a_restarting);
void clear_session_data ();
void append_source_editor (SourceEditor &a_sv,
const UString &a_path);
@@ -760,6 +764,8 @@ public:
MemoryView& get_memory_view ();
#endif // WITH_MEMORYVIEW
+ ExprMonitor& get_expr_monitor_view ();
+
ThreadList& get_thread_list ();
bool set_where (const IDebugger::Frame &a_frame,
@@ -804,7 +810,7 @@ public:
sigc::signal<void, bool>& debugger_ready_signal ();
sigc::signal<void>& layout_changed_signal ();
sigc::signal<void>& debugger_not_started_signal ();
- sigc::signal<void>& going_to_run_target_signal ();
+ sigc::signal<void, bool>& going_to_run_target_signal ();
sigc::signal<void>& default_config_read_signal ();
};//end class DBGPerspective
@@ -890,7 +896,7 @@ struct DBGPerspective::Priv {
sigc::signal<void, bool> attached_to_target_signal;
sigc::signal<void, bool> debugger_ready_signal;
sigc::signal<void> debugger_not_started_signal;
- sigc::signal<void> going_to_run_target_signal;
+ sigc::signal<void, bool> going_to_run_target_signal;
sigc::signal<void> default_config_read_signal;
map<UString, int> path_2_pagenum_map;
map<UString, int> basename_2_pagenum_map;
@@ -910,6 +916,7 @@ struct DBGPerspective::Priv {
#ifdef WITH_MEMORYVIEW
SafePtr<MemoryView> memory_view;
#endif // WITH_MEMORYVIEW
+ SafePtr<ExprMonitor> expr_monitor;
int current_page_num;
IDebuggerSafePtr debugger;
@@ -1628,6 +1635,22 @@ DBGPerspective::on_inspect_expression_action ()
}
void
+DBGPerspective::on_expr_monitoring_requested
+(const IDebugger::VariableSafePtr a_var)
+{
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (m_priv && m_priv->expr_monitor);
+
+ m_priv->expr_monitor->add_expression (a_var);
+
+ NEMIVER_CATCH;
+}
+
+
+void
DBGPerspective::on_call_function_action ()
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
@@ -1825,21 +1848,15 @@ DBGPerspective::on_debugger_not_started_signal ()
}
void
-DBGPerspective::on_going_to_run_target_signal ()
+DBGPerspective::on_going_to_run_target_signal (bool a_restarting)
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
NEMIVER_TRY;
+ clear_status_notebook (a_restarting);
re_initialize_set_breakpoints ();
clear_session_data ();
- get_local_vars_inspector ().re_init_widget ();
- get_breakpoints_view ().re_init ();
- get_call_stack ().clear ();
-#ifdef WITH_MEMORYVIEW
- get_memory_view ().clear ();
-#endif
- get_registers_view ().clear ();
NEMIVER_CATCH;
}
@@ -2331,7 +2348,7 @@ DBGPerspective::on_debugger_detached_from_target_signal ()
NEMIVER_TRY
- clear_status_notebook ();
+ clear_status_notebook (true);
workbench ().set_title_extension ("");
//****************************
//grey out all the menu
@@ -2480,7 +2497,7 @@ DBGPerspective::on_program_finished_signal ()
//clear threads list and
//call stack
//**********************
- clear_status_notebook ();
+ clear_status_notebook (true);
NEMIVER_CATCH
}
@@ -2896,6 +2913,19 @@ DBGPerspective::on_activate_memory_view ()
#endif //WITH_MEMORYVIEW
void
+DBGPerspective::on_activate_expr_monitor_view ()
+{
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (m_priv);
+ m_priv->layout ().activate_view (EXPR_MONITOR_VIEW_INDEX);
+
+ NEMIVER_CATCH;
+}
+
+void
DBGPerspective::on_activate_global_variables ()
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
@@ -3425,6 +3455,16 @@ DBGPerspective::init_actions ()
false
},
#endif // WITH_MEMORYVIEW
+ {
+ "ActivateExprMonitorViewMenuAction",
+ nil_stock_id,
+ EXPR_MONITOR_VIEW_TITLE,
+ _("Switch to Variables Monitor View"),
+ sigc::mem_fun (*this, &DBGPerspective::on_activate_expr_monitor_view),
+ ActionEntry::DEFAULT,
+ "<alt>6",
+ false
+ },
{
"DebugMenuAction",
nil_stock_id,
@@ -3810,7 +3850,7 @@ DBGPerspective::init_debugger_signals ()
}
void
-DBGPerspective::clear_status_notebook ()
+DBGPerspective::clear_status_notebook (bool a_restarting)
{
get_thread_list ().clear ();
get_call_stack ().clear ();
@@ -3820,6 +3860,7 @@ DBGPerspective::clear_status_notebook ()
#ifdef WITH_MEMORYVIEW
get_memory_view ().clear ();
#endif // WITH_MEMORYVIEW
+ get_expr_monitor_view ().re_init_widget (a_restarting);
}
void
@@ -4968,6 +5009,9 @@ DBGPerspective::add_views_to_layout ()
MEMORY_VIEW_TITLE,
MEMORY_VIEW_INDEX);
#endif // WITH_MEMORYVIEW
+ m_priv->layout ().append_view (get_expr_monitor_view ().widget (),
+ EXPR_MONITOR_VIEW_TITLE,
+ EXPR_MONITOR_VIEW_INDEX);
m_priv->layout ().do_init ();
}
@@ -5872,7 +5916,7 @@ DBGPerspective::restart_local_inferior ()
// restart; in which case, we can't just simply call debugger
// ()->run ().
&& debugger ()->get_target_path () == m_priv->prog_path) {
- going_to_run_target_signal ().emit ();
+ going_to_run_target_signal ().emit (true);
debugger ()->re_run
(sigc::mem_fun
(*this, &DBGPerspective::on_debugger_inferior_re_run_signal));
@@ -6007,8 +6051,6 @@ DBGPerspective::execute_program
clear_session_data ();
}
- clear_status_notebook ();
-
LOG_DD ("load program");
// now really load the inferior program (i.e: the one to be
@@ -6052,7 +6094,7 @@ DBGPerspective::execute_program
}
}
- going_to_run_target_signal ().emit ();
+ going_to_run_target_signal ().emit (a_restarting);
dbg_engine->run ();
m_priv->debugger_has_just_run = true;
@@ -6291,7 +6333,7 @@ DBGPerspective::detach_from_program ()
debugger ()->detach_from_target ();
close_opened_files ();
- clear_status_notebook ();
+ clear_status_notebook (false);
}
void
@@ -7854,6 +7896,9 @@ DBGPerspective::inspect_expression (const UString &a_expression)
ExprInspectorDialog dialog (*debugger (),
*this);
dialog.set_history (m_priv->var_inspector_dialog_history);
+ dialog.expr_monitoring_requested ().connect
+ (sigc::mem_fun (*this,
+ &DBGPerspective::on_expr_monitoring_requested));
if (a_expression != "") {
dialog.inspect_expression (a_expression);
}
@@ -8155,6 +8200,18 @@ DBGPerspective::get_memory_view ()
}
#endif // WITH_MEMORYVIEW
+/// Return the variable monitor view.
+ExprMonitor&
+DBGPerspective::get_expr_monitor_view ()
+{
+ THROW_IF_FAIL (m_priv);
+
+ if (!m_priv->expr_monitor)
+ m_priv->expr_monitor.reset (new ExprMonitor (*debugger (),
+ *this));
+ THROW_IF_FAIL (m_priv->expr_monitor);
+ return *m_priv->expr_monitor;
+}
struct ScrollTextViewToEndClosure {
Gtk::TextView* text_view;
@@ -8207,7 +8264,7 @@ DBGPerspective::debugger_not_started_signal ()
return m_priv->debugger_not_started_signal;
}
-sigc::signal<void>&
+sigc::signal<void, bool>&
DBGPerspective::going_to_run_target_signal ()
{
return m_priv->going_to_run_target_signal;
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.h b/src/persp/dbgperspective/nmv-dbg-perspective.h
index ddb00ff..efa0b3f 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.h
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.h
@@ -47,8 +47,9 @@ enum ViewsIndex
BREAKPOINTS_VIEW_INDEX,
REGISTERS_VIEW_INDEX,
#ifdef WITH_MEMORYVIEW
- MEMORY_VIEW_INDEX
+ MEMORY_VIEW_INDEX,
#endif // WITH_MEMORYVIEW
+ EXPR_MONITOR_VIEW_INDEX
};
class SourceEditor;
diff --git a/src/persp/dbgperspective/nmv-expr-inspector-dialog.cc b/src/persp/dbgperspective/nmv-expr-inspector-dialog.cc
index f2dee7d..dde1c6f 100644
--- a/src/persp/dbgperspective/nmv-expr-inspector-dialog.cc
+++ b/src/persp/dbgperspective/nmv-expr-inspector-dialog.cc
@@ -51,11 +51,15 @@ class ExprInspectorDialog::Priv {
Gtk::ComboBox *var_name_entry;
Glib::RefPtr<Gtk::ListStore> m_variable_history;
Gtk::Button *inspect_button;
+ Gtk::Button *add_to_monitor_button;
SafePtr<ExprInspector> expr_inspector;
Gtk::Dialog &dialog;
Glib::RefPtr<Gtk::Builder> gtkbuilder;
IDebugger &debugger;
IPerspective &perspective;
+ sigc::signal<void, IDebugger::VariableSafePtr> expr_monitoring_requested;
+ // Functionality mask
+ unsigned fun_mask;
Priv ();
public:
@@ -69,7 +73,8 @@ public:
dialog (a_dialog),
gtkbuilder (a_gtkbuilder),
debugger (a_debugger),
- perspective (a_perspective)
+ perspective (a_perspective),
+ fun_mask (FUNCTIONALITY_ALL)
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
build_dialog ();
@@ -97,6 +102,11 @@ public:
"inspectbutton");
inspect_button->set_sensitive (false);
+ add_to_monitor_button =
+ ui_utils::get_widget_from_gtkbuilder<Gtk::Button> (gtkbuilder,
+ "addtomonitorbutton");
+ add_to_monitor_button->set_sensitive (false);
+
Gtk::Box *box =
ui_utils::get_widget_from_gtkbuilder<Gtk::Box> (gtkbuilder,
"inspectorwidgetbox");
@@ -123,6 +133,8 @@ public:
THROW_IF_FAIL (var_name_entry);
inspect_button->signal_clicked ().connect (sigc::mem_fun
(*this, &Priv::do_inspect_expression));
+ add_to_monitor_button->signal_clicked ().connect
+ (sigc::mem_fun (*this, &Priv::on_do_monitor_button_clicked));
var_name_entry->signal_changed ().connect (sigc::mem_fun
(*this, &Priv::on_var_name_changed_signal));
var_name_entry->get_entry()->signal_activate ().connect (sigc::mem_fun
@@ -291,6 +303,23 @@ public:
/*a_allow_dups=*/false);
}
+ /// Set the functionality mask. It's a mask made of the bits
+ /// addressed by VarInspector::FunctionalityFlags.
+ void
+ functionality_mask (int a_mask)
+ {
+ fun_mask = a_mask;
+ }
+
+ /// Return the functionality mask. It's a mask made of the bits
+ /// addressed by VarInspector::FunctionalityFlags.
+ unsigned
+ functionality_mask ()
+ {
+ return fun_mask;
+ }
+
+
//************************
//<signal handlers>
//*************************
@@ -307,11 +336,11 @@ public:
THROW_IF_FAIL (inspect_button);
UString var_name = var_name_entry->get_entry ()->get_text ();
- if (var_name == "") {
+ if (var_name == "")
inspect_button->set_sensitive (false);
- } else {
+ else if (functionality_mask ()
+ & FUNCTIONALITY_EXPR_INSPECTOR)
inspect_button->set_sensitive (true);
- }
// this handler is called when any text is changed in the entry or when
// an item is selected from the combobox. We don't want to inspect any
@@ -329,12 +358,27 @@ public:
void
on_variable_inspected (const IDebugger::VariableSafePtr)
{
+ if ((functionality_mask () & FUNCTIONALITY_EXPR_MONITOR_PICKER))
+ add_to_monitor_button->set_sensitive (true);
}
/// Handler called when the variable inspector is cleared.
void
on_variable_inspector_cleared ()
{
+ add_to_monitor_button->set_sensitive (false);
+ }
+
+ void
+ on_do_monitor_button_clicked ()
+ {
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (expr_inspector->get_expression ());
+
+ expr_monitoring_requested.emit (expr_inspector->get_expression ());
+
+ NEMIVER_CATCH
}
//************************
@@ -438,5 +482,32 @@ ExprInspectorDialog::get_history (std::list<UString> &a_hist) const
m_priv->get_history (a_hist);
}
+sigc::signal<void, IDebugger::VariableSafePtr>&
+ExprInspectorDialog::expr_monitoring_requested ()
+{
+ THROW_IF_FAIL (m_priv);
+ return m_priv->expr_monitoring_requested;
+}
+
+/// Set the functionality mask. It's a mask made of the bits
+/// addressed by ExprInspector::FunctionalityFlags.
+void
+ExprInspectorDialog::functionality_mask (int a_mask)
+{
+ THROW_IF_FAIL (m_priv);
+
+ m_priv->functionality_mask (a_mask);
+}
+
+/// Return the functionality mask. It's a mask made of the bits
+/// addressed by ExprInspector::FunctionalityFlags.
+unsigned
+ExprInspectorDialog::functionality_mask ()
+{
+ THROW_IF_FAIL (m_priv);
+
+ return m_priv->functionality_mask ();
+}
+
NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/persp/dbgperspective/nmv-expr-inspector-dialog.h b/src/persp/dbgperspective/nmv-expr-inspector-dialog.h
index b0242ec..5aaae28 100644
--- a/src/persp/dbgperspective/nmv-expr-inspector-dialog.h
+++ b/src/persp/dbgperspective/nmv-expr-inspector-dialog.h
@@ -44,6 +44,26 @@ class ExprInspectorDialog : public Dialog {
SafePtr<Priv> m_priv;
public:
+
+ /// These flags control the fonctionnalities that are enabled to
+ /// be used with the current instance of VarInspectorDialog.
+ enum FunctionalityFlags {
+ FUNCTIONALITY_NONE = 0,
+ /// When this bit is set, the inspector allows the user to
+ /// inspect expressions. Thus the "inspect" button is made
+ /// clickable.
+ FUNCTIONALITY_EXPR_INSPECTOR = 1,
+ /// When this bit is set, the inspect allows the user to send
+ /// the inspected expression to the expression (or variable)
+ /// monitor.
+ FUNCTIONALITY_EXPR_MONITOR_PICKER = 1 << 1,
+ // This one should be the last one, and should contain all the
+ // flags above.
+ FUNCTIONALITY_ALL =
+ (FUNCTIONALITY_EXPR_INSPECTOR
+ | FUNCTIONALITY_EXPR_MONITOR_PICKER)
+ };
+
ExprInspectorDialog (IDebugger &a_debugger,
IPerspective &a_perspective);
virtual ~ExprInspectorDialog ();
@@ -57,6 +77,14 @@ public:
ExprInspector& inspector () const;
void set_history (const std::list<UString> &);
void get_history (std::list<UString> &) const;
+ void functionality_mask (int functionality_mask);
+ unsigned functionality_mask ();
+
+ // <Signals>
+
+ sigc::signal<void, IDebugger::VariableSafePtr>& expr_monitoring_requested ();
+
+ // </Signals>
};//end class ExprInspectorDialog
NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/persp/dbgperspective/nmv-expr-monitor.cc b/src/persp/dbgperspective/nmv-expr-monitor.cc
new file mode 100644
index 0000000..fa775f1
--- /dev/null
+++ b/src/persp/dbgperspective/nmv-expr-monitor.cc
@@ -0,0 +1,1306 @@
+//Author: Dodji Seketeli
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#include "nmv-expr-monitor.h"
+#include <glib/gi18n.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/treerowreference.h>
+#include "common/nmv-exception.h"
+#include "nmv-vars-treeview.h"
+#include "nmv-variables-utils.h"
+#include "nmv-debugger-utils.h"
+#include "nmv-i-workbench.h"
+#include "nmv-expr-inspector-dialog.h"
+
+using namespace nemiver::common;
+namespace vutils = nemiver::variables_utils2;
+namespace dutils = nemiver::debugger_utils;
+
+using namespace dutils;
+
+NEMIVER_BEGIN_NAMESPACE (nemiver)
+
+struct ExprMonitor::Priv
+{
+ Glib::RefPtr<Gtk::UIManager> ui_manager;
+ IDebugger &debugger;
+ IPerspective &perspective;
+ SafePtr<VarsTreeView> tree_view;
+ Glib::RefPtr<Gtk::TreeStore> tree_store;
+ SafePtr<Gtk::TreeRowReference> in_scope_exprs_row_ref;
+ SafePtr<Gtk::TreeRowReference> out_of_scope_exprs_row_ref;
+ Gtk::TreeModel::iterator cur_selected_row;
+ IDebugger::VariableList monitored_expressions;
+ IDebugger::VariableList changed_in_scope_exprs_at_prev_stop;
+ IDebugger::VariableList changed_oo_scope_exprs_at_prev_stop;
+ // Variables that went out of scope because the inferior got
+ // restarted.
+ IDebugger::VariableList killed_expressions;
+ map<IDebugger::VariableSafePtr, bool> in_scope_exprs;
+ map<IDebugger::VariableSafePtr, bool> revived_exprs;
+ vector<Gtk::TreeModel::Path> selected_paths;
+ Glib::RefPtr<Gtk::ActionGroup> action_group;
+ Gtk::Widget *contextual_menu;
+ IDebugger::Frame saved_frame;
+ IDebugger::StopReason saved_reason;
+ bool saved_has_frame;
+ bool initialized;
+ bool is_new_frame;
+ bool is_up2date;
+
+ Priv (IDebugger &a_debugger,
+ IPerspective &a_perspective)
+ : debugger (a_debugger),
+ perspective (a_perspective),
+ contextual_menu (0),
+ saved_reason (IDebugger::UNDEFINED_REASON),
+ saved_has_frame (false),
+ initialized (false),
+ is_new_frame (true),
+ is_up2date (true)
+ {
+ // The widget is built lazily when somone requests it from
+ // the outside.
+ }
+
+ /// Return the widget to visualize the variables managed by the
+ /// monitor. This function lazily builds the widget and
+ /// initializes the monitor.
+ /// \return the widget to visualize the monitored expressions
+ Gtk::Widget&
+ get_widget ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ if (!initialized)
+ init_widget ();
+ THROW_IF_FAIL (initialized && tree_view);
+ return *tree_view;
+ }
+
+ void
+ init_widget ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ if (initialized)
+ return;
+
+ // Neither in_scope_exprs_row_ref nor out_of_scope_exprs_row_ref
+ // nor tree_view should be non-null before the widget has been
+ // initialized.
+ THROW_IF_FAIL (!in_scope_exprs_row_ref
+ && !out_of_scope_exprs_row_ref
+ && !tree_view);
+
+ tree_view.reset (VarsTreeView::create ());
+ THROW_IF_FAIL (tree_view);
+
+ tree_store = tree_view->get_tree_store ();
+ THROW_IF_FAIL (tree_store);
+
+ // *************************************************************
+ // Create a row for expressions that are in-scope and a row for
+ // expressions that are out-of-scope
+ // *************************************************************
+ Gtk::TreeModel::iterator it = tree_store->append ();
+ (*it)[vutils::get_variable_columns ().name] = _("In scope expressions");
+ in_scope_exprs_row_ref.reset
+ (new Gtk::TreeRowReference (tree_store,
+ tree_store->get_path (it)));
+ it = tree_store->append ();
+ (*it)[vutils::get_variable_columns ().name] = _("Out of scope expressions");
+ out_of_scope_exprs_row_ref.reset
+ (new Gtk::TreeRowReference (tree_store,
+ tree_store->get_path (it)));
+
+ THROW_IF_FAIL (in_scope_exprs_row_ref
+ && out_of_scope_exprs_row_ref);
+
+ // And now finish the initialization.
+ connect_to_debugger_signal ();
+ init_graphical_signals ();
+ init_actions ();
+
+ initialized = true;
+ }
+
+ /// Re-initialize the widget.
+ void
+ re_init_widget (bool a_remember_variables)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ if (a_remember_variables) {
+ kill_monitored_expressions ();
+ } else {
+ killed_expressions.clear ();
+ }
+ monitored_expressions.clear ();
+ clear_in_scope_exprs_rows ();
+ clear_out_of_scope_exprs_rows ();
+ revived_exprs.clear ();
+ }
+
+ /// Clear the rows under the "in scope variables" node.
+ void
+ clear_in_scope_exprs_rows ()
+ {
+ Gtk::TreeModel::iterator row_it;
+ get_in_scope_exprs_row_iterator (row_it);
+ Gtk::TreeModel::Children rows = row_it->children ();
+ for (row_it = rows.begin (); row_it != rows.end ();)
+ row_it = tree_store->erase (*row_it);
+ }
+
+ /// Clear the rows under the "out of scope variables" node.
+ void
+ clear_out_of_scope_exprs_rows ()
+ {
+ Gtk::TreeModel::iterator row_it;
+ get_out_of_scope_exprs_row_iterator (row_it);
+ Gtk::TreeModel::Children rows = row_it->children ();
+ for (row_it = rows.begin (); row_it != rows.end ();)
+ row_it = tree_store->erase (*row_it);
+ }
+
+ /// Connect to the graphical
+ void
+ connect_to_debugger_signal ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ debugger.stopped_signal ().connect
+ (sigc::mem_fun (*this, &Priv::on_stopped_signal));
+ debugger.inferior_re_run_signal ()
+ .connect (sigc::mem_fun
+ (*this,
+ &Priv::on_inferior_re_run_signal));
+ }
+
+ /// Connect slot to signals related to graphical stuff.
+ void
+ init_graphical_signals ()
+ {
+ THROW_IF_FAIL (tree_view);
+
+ tree_view->signal_row_expanded ().connect
+ (sigc::mem_fun (*this, &Priv::on_tree_view_row_expanded_signal));
+
+ tree_view->signal_draw ().connect_notify
+ (sigc::mem_fun (this, &Priv::on_draw_signal));
+
+ // Schedule the button press signal handler to be run before
+ // the default handler.
+ tree_view->signal_button_press_event ().connect_notify
+ (sigc::mem_fun (this, &Priv::on_button_press_signal));
+
+ Glib::RefPtr<Gtk::TreeSelection> selection =
+ tree_view->get_selection ();
+ selection->set_mode (Gtk::SELECTION_MULTIPLE);
+
+ selection->signal_changed ().connect
+ (sigc::mem_fun (*this,
+ &Priv::on_tree_view_selection_changed_signal));
+ }
+
+ /// Initialize actions to be triggered whenever the user clicks on
+ /// menu items that are specific to variable monitoring.
+ void
+ init_actions ()
+ {
+ ui_utils::ActionEntry s_expr_monitor_action_entries [] = {
+ {
+ "RemoveExpressionsMenuItemAction",
+ Gtk::Stock::DELETE,
+ _("Remove"),
+ _("Remove selected expressions from the monitor"),
+ sigc::mem_fun (*this, &Priv::on_remove_expressions_action),
+ ui_utils::ActionEntry::DEFAULT,
+ "",
+ false
+ },
+ {
+ "AddExpressionMenuItemAction",
+ Gtk::Stock::ADD,
+ _("New ..."),
+ _("Add a new expression to the monitor"),
+ sigc::mem_fun (*this, &Priv::on_add_expression_action),
+ ui_utils::ActionEntry::DEFAULT,
+ "",
+ false
+ }
+ };
+ action_group =
+ Gtk::ActionGroup::create ("expr-monitor-action-group");
+ action_group->set_sensitive (true);
+ int num_actions =
+ sizeof (s_expr_monitor_action_entries)
+ /
+ sizeof (ui_utils::ActionEntry);
+ ui_utils::add_action_entries_to_action_group
+ (s_expr_monitor_action_entries,
+ num_actions,
+ action_group);
+ get_ui_manager ()->insert_action_group (action_group);
+ }
+
+ /// Return true iff the variable in parameter is currently being
+ /// monitored.
+ ///
+ /// \param a_expr the variable to check for.
+ bool
+ expression_is_monitored (const IDebugger::Variable &a_expr) const
+ {
+ IDebugger::VariableList::const_iterator it;
+ for (it = monitored_expressions.begin ();
+ it != monitored_expressions.end ();
+ ++it) {
+ if (!a_expr.internal_name ().empty ()
+ && a_expr.internal_name () == (*it)->internal_name ())
+ // Both variables have the same internal name, so they
+ // are equal.
+ return true;
+ else if (!(*it)->needs_unfolding ()
+ && !a_expr.needs_unfolding ()) {
+ // Both variables have been unfolded, so we can
+ // compare them by value.
+ if ((*it)->equals_by_value (a_expr))
+ return true;
+ } else {
+ if (a_expr.name () == a_expr.name ())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Return true iff the variable has been put out of scope because
+ /// the inferior was re-started. Pointer comparison is used for
+ /// the test.
+ bool
+ expression_is_killed (IDebugger::VariableSafePtr a_expr)
+ {
+ for (IDebugger::VariableList::const_iterator i =
+ killed_expressions.begin ();
+ i != killed_expressions.end ();
+ ++i) {
+ if (*i == a_expr)
+ return true;
+ }
+ return false;
+ }
+
+ /// Return true iff a_expr was killed and has been revived.
+ bool
+ expression_is_revived (IDebugger::VariableSafePtr a_expr)
+ {
+ return revived_exprs.find (a_expr) != revived_exprs.end ();
+ }
+
+ /// Monitor a new expression (or varibale). In other words, add a
+ /// new variable to the monitor.
+ ///
+ /// \param a_expr the variable to monitor.
+ void
+ add_expression (const IDebugger::VariableSafePtr a_expr)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("a_expr: " << a_expr->id ());
+
+ if (!a_expr || expression_is_monitored (*a_expr))
+ return;
+
+ monitored_expressions.push_back (a_expr);
+ Gtk::TreeModel::iterator root_node;
+ if (a_expr->in_scope ())
+ get_in_scope_exprs_row_iterator (root_node);
+ else
+ get_out_of_scope_exprs_row_iterator (root_node);
+ THROW_IF_FAIL (root_node);
+ vutils::append_a_variable (a_expr,
+ *tree_view,
+ root_node,
+ /*a_truncate_type=*/true);
+ }
+
+ /// Add an variable for an expression to the monitor.
+ ///
+ /// \param a_expr the expression to consider.
+ ///
+ /// \param a_slot the callback slot to invoke upon creation of the
+ /// variable object representing the expression.
+ void
+ add_expression (const UString &a_expr,
+ const IDebugger::ConstVariableSlot &a_slot)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ debugger.create_variable (a_expr, a_slot);
+ }
+
+ /// Monitor a list of new variables.
+ ///
+ /// \param a_exprs the variables to monitor.
+ void
+ add_expressions (const IDebugger::VariableList &a_exprs)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ IDebugger::VariableList::const_iterator it = a_exprs.begin ();
+ for (; it != a_exprs.end (); ++it)
+ add_expression (*it);
+ }
+
+ /// Remove a variable from the monitor.
+ ///
+ /// \param a_expr the variable to remove from the monitor.
+ void
+ remove_expression (const IDebugger::VariableSafePtr a_expr)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("a_expr: " << a_expr->id ());
+
+ bool found = false;
+ IDebugger::VariableList::iterator it;
+ for (it = monitored_expressions.begin ();
+ it != monitored_expressions.end (); ++it) {
+ if (*it == a_expr
+ || (*it)->internal_name () == a_expr->internal_name ()) {
+ // Remove the graphical representation of the node,
+ // and then remove the node itself from the list of
+ // monitored expressions.
+ Gtk::TreeModel::iterator parent_row;
+ if (a_expr->in_scope ())
+ get_in_scope_exprs_row_iterator (parent_row);
+ else
+ get_out_of_scope_exprs_row_iterator (parent_row);
+ THROW_IF_FAIL (parent_row);
+ THROW_IF_FAIL (vutils::unlink_a_variable_row
+ (a_expr, tree_store, parent_row));
+ monitored_expressions.erase (it);
+ // We removed an element from the array while
+ // iterating on it so the iterator is invalidated. We
+ // must not use it again.
+ found = true;
+ break;
+ }
+ }
+
+ // Remove the expression from the list of killed expressions
+ // too.
+ for (it = killed_expressions.begin ();
+ it != killed_expressions.end ();
+ ++it) {
+ if (*it == a_expr
+ || (*it)->internal_name () == a_expr->internal_name ()) {
+ Gtk::TreeModel::iterator parent_row;
+ if (a_expr->in_scope ())
+ get_in_scope_exprs_row_iterator (parent_row);
+ else
+ get_out_of_scope_exprs_row_iterator (parent_row);
+ THROW_IF_FAIL (parent_row);
+ THROW_IF_FAIL (vutils::unlink_a_variable_row
+ (a_expr, tree_store, parent_row));
+ found = true;
+ killed_expressions.erase (it);
+ // it is now invalidated, we must not use it
+ // again.
+ break;
+ }
+ }
+
+ // Make sure the expression is not stored in the other lists
+ // either.
+ for (it = changed_in_scope_exprs_at_prev_stop.begin ();
+ it != changed_in_scope_exprs_at_prev_stop.end ();
+ ++it) {
+ if (*it == a_expr
+ || (*it)->internal_name () == a_expr->internal_name ()) {
+ changed_in_scope_exprs_at_prev_stop.erase (it);
+ break;
+ }
+ }
+
+ for (it = changed_oo_scope_exprs_at_prev_stop.begin ();
+ it != changed_oo_scope_exprs_at_prev_stop.end ();
+ ++it) {
+ if (*it == a_expr
+ || (*it)->internal_name () == a_expr->internal_name ()) {
+ changed_oo_scope_exprs_at_prev_stop.erase (it);
+ break;
+ }
+ }
+
+ for (map<IDebugger::VariableSafePtr, bool>::iterator i =
+ revived_exprs.begin ();
+ i != revived_exprs.end ();
+ ++i) {
+ if (i->first->internal_name () == a_expr->internal_name ()) {
+ revived_exprs.erase (i);
+ break;
+ }
+ }
+
+ if (found) {
+ LOG_DD ("variable found and erased");
+ } else {
+ LOG_DD ("variable was *NOT* found");
+ }
+ }
+
+ /// Remove a set of variables from the monitor.
+ void
+ remove_expressions (const IDebugger::VariableList &a_exprs)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ IDebugger::VariableList::const_iterator it = a_exprs.begin ();
+ for (; it != a_exprs.end (); ++it)
+ remove_expression (*it);
+ }
+
+ /// Copy all the expressions being currently monitored, into the
+ /// list of killed expressions. This happens when all the
+ /// monitored expressions go out of scope in a way that is not
+ /// tracked by the debugging engine anymore; e.g, when the user
+ /// restarts the infererior, or when the inferior exits. In that
+ /// case, these expressions must be automatically re-created when
+ /// the inferior starts again; the killed expressions then become
+ /// "revived" expressions.
+ void
+ kill_monitored_expressions ()
+ {
+ for (IDebugger::VariableList::const_iterator i =
+ monitored_expressions.begin ();
+ i != monitored_expressions.end ();
+ ++i) {
+ // Sometimes the debugging backend gets this wrong.
+ (*i)->in_scope (false);
+ killed_expressions.push_back (*i);
+ }
+ }
+
+ /// Re-monitor a killed expression.
+ /// A killed expression is one that went out of scope because the
+ /// inferior was re-started.
+ void
+ re_monitor_killed_variable (const IDebugger::VariableSafePtr a_expr)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ THROW_IF_FAIL (!a_expr->name ().empty ());
+ THROW_IF_FAIL (expression_is_killed (a_expr));
+
+ // Update the graphical stuff according to the in-scope-ness
+ // propert of a_expr.
+ Gtk::TreeModel::iterator var_it, parent_it;
+ update_expr_in_scope_or_not (a_expr, var_it, parent_it);
+
+ if (!a_expr->in_scope ())
+ add_expression
+ (a_expr->name (),
+ sigc::bind (sigc::mem_fun
+ (*this, &Priv::on_killed_var_recreated),
+ a_expr));
+ }
+
+ /// Walk the killed expressions and try to re-monitor them.
+ /// killed expressions are those that went out of scope because
+ /// the inferior was re-started.
+ void
+ re_monitor_killed_variables ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ for (IDebugger::VariableList::const_iterator it =
+ killed_expressions.begin ();
+ it != killed_expressions.end ();
+ ++it)
+ re_monitor_killed_variable (*it);
+ }
+
+ /// Update the graphical representation of the in-scope-ness of a
+ /// revived variable. A re-vived variable is a variable that has
+ /// been killed, and that has been re-created. Look at the
+ /// comments of kill_monitored_expressions to understand what a
+ /// killed expression is.
+ void
+ update_revived_exprs_oo_scope_or_not ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ IDebugger::VariableList to_delete;
+
+ for (map<IDebugger::VariableSafePtr, bool>::const_iterator i =
+ revived_exprs.begin ();
+ i != revived_exprs.end ();
+ ++i) {
+ if (!i->first->in_scope ())
+ debugger.create_variable
+ (i->first->name (),
+ sigc::bind
+ (sigc::mem_fun
+ (*this, &Priv::on_tentatively_create_revived_expr),
+ i->first));
+ else
+ to_delete.push_back (i->first);
+
+ }
+
+ for (IDebugger::VariableList::iterator i = to_delete.begin ();
+ i != to_delete.end ();
+ ++i)
+ revived_exprs.erase (*i);
+ }
+
+ /// Return an iterator on the monitored expressions that are in
+ /// scope.
+ bool
+ get_in_scope_exprs_row_iterator (Gtk::TreeModel::iterator &a_it)
+ {
+ if (!in_scope_exprs_row_ref)
+ return false;
+ a_it = tree_store->get_iter (in_scope_exprs_row_ref->get_path ());
+ return true;
+ }
+
+ /// Return an iterator on the monitored expressions that are out of
+ /// scope.
+ bool
+ get_out_of_scope_exprs_row_iterator (Gtk::TreeModel::iterator &a_it)
+ {
+ if (!out_of_scope_exprs_row_ref)
+ return false;
+ a_it = tree_store->get_iter (out_of_scope_exprs_row_ref->get_path ());
+ return true;
+ }
+
+ /// Ensure that a given expression is only under a given graphical
+ /// node.
+ ///
+ /// This is a sub-routine for update_expr_in_scope_or_not.
+ /// \param a_expr the variable to consider
+ ///
+ /// \param a_first the node under which we want a_expr to be.
+ ///
+ /// \param a_second the node under which we *don't* want a_expr to
+ /// be.
+ ///
+ /// \param a_expr_it a graphical iterator pointing to where a_expr
+ /// was put, finally.
+ void
+ ensure_expr_under_first_but_not_under_second
+ (const IDebugger::VariableSafePtr a_expr,
+ Gtk::TreeModel::iterator &a_first,
+ Gtk::TreeModel::iterator &a_second,
+ Gtk::TreeModel::iterator &a_expr_it)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ // If is under a_second then remove it from there.
+ vutils::unlink_a_variable_row (a_expr, tree_store, a_second);
+
+ // If a_expr is not in under a_first, add it now.
+ Gtk::TreeModel::iterator var_it;
+ if (!vutils::find_a_variable (a_expr, a_first, a_expr_it)) {
+ LOG_DD ("Adding variable "
+ << a_expr->id ()
+ << " under the first iterator");
+ vutils::append_a_variable (a_expr, *tree_view,
+ a_first, a_expr_it,
+ /*a_truncate_type=*/true);
+ }
+ }
+
+ /// Update the graphical state of a_expr so that, it appears under
+ /// the right graphical node depending on whether it is in scope
+ /// or not.
+ void
+ update_expr_in_scope_or_not (const IDebugger::VariableSafePtr &a_expr,
+ Gtk::TreeModel::iterator &a_expr_it,
+ Gtk::TreeModel::iterator &a_parent_it)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("a_expr: " << a_expr->id ());
+
+ Gtk::TreeModel::iterator in_scope_exprs_it, out_of_scope_exprs_it;
+
+ THROW_IF_FAIL (a_expr->is_morally_root ());
+
+ get_in_scope_exprs_row_iterator (in_scope_exprs_it);
+ get_out_of_scope_exprs_row_iterator (out_of_scope_exprs_it);
+
+ if (a_expr->in_scope ()) {
+ LOG_DD ("variable " << a_expr->id () << " is in scope");
+ in_scope_exprs[a_expr] = true;
+ ensure_expr_under_first_but_not_under_second (a_expr,
+ in_scope_exprs_it,
+ out_of_scope_exprs_it,
+ a_expr_it);
+ a_parent_it = in_scope_exprs_it;
+ } else {
+ LOG_DD ("variable " << a_expr->id () << " is not in scope");
+ in_scope_exprs.erase (a_expr);
+ ensure_expr_under_first_but_not_under_second (a_expr,
+ out_of_scope_exprs_it,
+ in_scope_exprs_it,
+ a_expr_it);
+ a_parent_it = out_of_scope_exprs_it;
+ }
+
+ }
+
+ /// Clear the internal state we maintain to know what variables
+ /// changed at the previous step.
+ void
+ clear_exprs_changed_at_prev_step ()
+ {
+ changed_in_scope_exprs_at_prev_stop.clear ();
+ changed_oo_scope_exprs_at_prev_stop.clear ();
+ }
+
+ /// Graphically update the variables that changed at the previous
+ /// step.
+ void
+ update_exprs_changed_at_prev_step ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ Gtk::TreeModel::iterator in_scope_exprs_it, oo_scope_exprs_it;
+
+ get_in_scope_exprs_row_iterator (in_scope_exprs_it);
+ get_out_of_scope_exprs_row_iterator (oo_scope_exprs_it);
+
+ IDebugger::VariableList::const_iterator it;
+ for (it = changed_in_scope_exprs_at_prev_stop.begin ();
+ it != changed_in_scope_exprs_at_prev_stop.end ();
+ ++it) {
+ if (!expression_is_killed (*it))
+ vutils::update_a_variable (*it, *tree_view,
+ in_scope_exprs_it,
+ /*a_truncate_type=*/false,
+ /*a_handle_highlight=*/true,
+ /*a_is_new_frame=*/true,
+ /*a_update_members=*/true);
+ }
+
+ for (it = changed_oo_scope_exprs_at_prev_stop.begin ();
+ it != changed_oo_scope_exprs_at_prev_stop.end ();
+ ++it) {
+ if (!expression_is_killed (*it))
+ vutils::update_a_variable (*it, *tree_view,
+ oo_scope_exprs_it,
+ /*a_truncate_type=*/false,
+ /*a_handle_highlight=*/true,
+ /*a_is_new_frame=*/true,
+ /*a_update_members=*/true);
+ }
+ clear_exprs_changed_at_prev_step ();
+ }
+
+ /// Return true iff we need to graphically update the current
+ /// view. Basically, the widget is not visible, this function
+ /// returns false.
+ bool
+ should_process_now () const
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ THROW_IF_FAIL (tree_view);
+ bool is_visible = tree_view->get_is_drawable ();
+ LOG_DD ("is visible: " << is_visible);
+ return is_visible;
+ }
+
+ /// This does what needs to do whenever the widget becomes visible
+ /// and we need to update its rendering after the inferior has
+ /// stopped.
+ ///
+ /// \param a_reason the reason why the inferior has stopped.
+ ///
+ /// \param a_has_frame true if after the stop, we have a frame
+ /// handy.
+ ///
+ /// \param a_frame the frame we have, if a_has_frame is non-null.
+ void
+ finish_handling_debugger_stopped_event (IDebugger::StopReason a_reason,
+ bool a_has_frame,
+ const IDebugger::Frame &a_frame)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (tree_store);
+
+ LOG_DD ("stopped, reason: " << a_reason);
+ if (a_reason == IDebugger::EXITED_SIGNALLED
+ || a_reason == IDebugger::EXITED_NORMALLY
+ || a_reason == IDebugger::EXITED
+ || !a_has_frame) {
+ kill_monitored_expressions ();
+ return;
+ }
+
+ is_new_frame = (saved_frame != a_frame);
+ saved_frame = a_frame;
+
+ // Clear the highlighting from variables that where
+ // highlighted during previous step.
+ update_exprs_changed_at_prev_step ();
+
+ // Walk the monitored expressions and list those that have
+ // changed.
+ IDebugger::VariableList::const_iterator it;
+ for (it = monitored_expressions.begin ();
+ it != monitored_expressions.end ();
+ ++it) {
+ debugger.list_changed_variables (*it,
+ sigc::bind
+ (sigc::mem_fun
+ (*this, &Priv::on_vars_changed),
+ *it));
+ }
+
+ // Walk the killed expressions and try to re-monitor them.
+ // killed expressions are those that went out of scope because
+ // the inferior was re-started.
+ re_monitor_killed_variables ();
+
+ update_revived_exprs_oo_scope_or_not ();
+
+ NEMIVER_CATCH;
+ }
+
+ /// Return the UI manager associated with this variable monitor.
+ /// this is for e.g, the contextual menu.
+ Glib::RefPtr<Gtk::UIManager>
+ get_ui_manager ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ if (!ui_manager)
+ ui_manager = Gtk::UIManager::create ();
+ return ui_manager;
+ }
+
+ /// Return the contextual menu for the variable menu. Build it,
+ /// if it is not already built.
+ ///
+ /// \return a widget representing the contextual menu.
+ Gtk::Widget*
+ get_contextual_menu ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ if (!contextual_menu) {
+ string absolute_path;
+ perspective.build_absolute_resource_path
+ (Glib::build_filename ("menus", "exprmonitorpopup.xml"),
+ absolute_path);
+ get_ui_manager ()->add_ui_from_file (absolute_path);
+ get_ui_manager ()->ensure_update ();
+ contextual_menu =
+ get_ui_manager ()->get_widget ("/ExprMonitorPopup");
+ THROW_IF_FAIL (contextual_menu);
+ }
+ return contextual_menu;
+ }
+
+ /// Return true iff a given path pointing at a row of the variable
+ /// monitor is for a row that contains an instance of
+ /// IDebugger::Variable.
+ ///
+ /// \param a_path the path to check for.
+ bool
+ path_points_to_variable (const Gtk::TreeModel::Path &a_path) const
+ {
+ Gtk::TreeModel::iterator it = tree_store->get_iter (a_path);
+ if (it->get_value (vutils::get_variable_columns ().variable))
+ return true;
+ return false;
+ }
+
+ /// Return true iff a row containing an instance of
+ /// IDebugger::Variable has been selected (clicked) by the user.
+ bool
+ expression_is_selected () const
+ {
+ std::vector<Gtk::TreeModel::Path> selected_paths =
+ tree_view->get_selection ()->get_selected_rows ();
+ std::vector<Gtk::TreeModel::Path>::const_iterator it;
+ for (it = selected_paths.begin ();
+ it != selected_paths.end ();
+ ++it)
+ if (path_points_to_variable (*it))
+ return true;
+ return false;
+ }
+
+ /// Update the sensitivity of the items of the contextual menu,
+ /// depending on the set of rows that are currently selected.
+ void
+ update_contextual_menu_sensitivity ()
+ {
+ Glib::RefPtr<Gtk::Action> remove_expression_action =
+ get_ui_manager ()->get_action
+ ("/ExprMonitorPopup/RemoveExpressionsMenuItem");
+ THROW_IF_FAIL (remove_expression_action);
+
+ Glib::RefPtr<Gtk::TreeSelection> selection = tree_view->get_selection ();
+ THROW_IF_FAIL (selection);
+
+ remove_expression_action->set_sensitive
+ (expression_is_selected ());
+ }
+
+ /// Pop up the contextual menu of the variable monitor.
+ void
+ popup_contextual_menu (GdkEventButton *a_event)
+ {
+ Gtk::Menu *menu = dynamic_cast<Gtk::Menu*> (get_contextual_menu ());
+ THROW_IF_FAIL (menu);
+
+ update_contextual_menu_sensitivity ();
+
+ menu->popup (a_event->button, a_event->time);
+ }
+
+ // *********************
+ // <signal handlers>
+ // ********************
+
+ /// Invoked whenever the inferior stops.
+ void
+ on_stopped_signal (IDebugger::StopReason a_reason,
+ bool a_has_frame,
+ const IDebugger::Frame &a_frame,
+ int /*a_thread_id*/,
+ int /*a_bp_num*/,
+ const UString &/*a_cookie*/)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ if (IDebugger::is_exited (a_reason)
+ || !a_has_frame)
+ return;
+
+ saved_frame = a_frame;
+ saved_reason = a_reason;
+ saved_has_frame = a_has_frame;
+
+ if (should_process_now ()) {
+ finish_handling_debugger_stopped_event (a_reason,
+ a_has_frame,
+ a_frame);
+ } else {
+ is_up2date = false;
+ }
+ NEMIVER_CATCH;
+ }
+
+ /// Callback invoked whenever the inferior is re-run.
+ void
+ on_inferior_re_run_signal ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+ }
+
+ /// Callback invoked whenever a killed expression (made out of scope because
+ /// the inferior was restarted), is re-created again, for the
+ /// purpose of being monitored again.
+ ///
+ /// So this function monitors the variable again, and removes it
+ /// from the previously killed expressions.
+ void
+ on_killed_var_recreated (const IDebugger::VariableSafePtr a_new_var,
+ const IDebugger::VariableSafePtr a_killed_var)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (a_new_var);
+ THROW_IF_FAIL (a_killed_var);
+
+ remove_expression (a_killed_var);
+ add_expression (a_new_var);
+ // Mark a_new_var as a revived one.
+ revived_exprs[a_new_var] = true;
+
+ NEMIVER_CATCH;
+ }
+
+ void
+ on_tentatively_create_revived_expr
+ (const IDebugger::VariableSafePtr a_new_expr,
+ const IDebugger::VariableSafePtr a_initial_revived_expr)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ if (a_new_expr->in_scope ()
+ && !a_initial_revived_expr->in_scope ()) {
+ remove_expression (a_initial_revived_expr);
+ add_expression (a_new_expr);
+ }
+ // else the expression a_new_expr is just deleted.
+
+ NEMIVER_CATCH;
+ }
+
+ /// Invoked whenever a variable changed.
+ ///
+ /// \a_param the sub variables that actually changed.
+ ///
+ /// \param the variable which sub-variables changed.
+ void
+ on_vars_changed (const IDebugger::VariableList &a_sub_vars,
+ const IDebugger::VariableSafePtr a_var_root)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("a_var_root: "<< a_var_root->id ());
+
+ NEMIVER_TRY;
+
+ // Is this variable in scope or not? Update the graphical
+ // stuff according to that property.
+ Gtk::TreeModel::iterator var_it, parent_it;
+ update_expr_in_scope_or_not (a_var_root, var_it, parent_it);
+ THROW_IF_FAIL (var_it);
+
+ // Walk children of a_var_root and update their graphical
+ // representation.
+ if (!expression_is_killed (a_var_root)) {
+ IDebugger::VariableList::const_iterator v = a_sub_vars.begin ();
+ for (; v != a_sub_vars.end (); ++v) {
+ LOG_DD ("Going to update variable " << (*v)->id () << ":" << **v);
+ vutils::update_a_variable (*v, *tree_view,
+ parent_it,
+ /*a_truncate_type=*/false,
+ /*a_handle_highlight=*/true,
+ /*a_is_new_frame=*/is_new_frame,
+ /*a_update_members=*/false);
+ }
+ }
+
+ // Now, update changed_in_scope_exprs_at_prev_stop and
+ // changed_oo_scope_exprs_at_prev_stop variables.
+ Gtk::TreeModel::iterator in_scope_exprs_row_it, oo_scope_exprs_row_it;
+ get_in_scope_exprs_row_iterator (in_scope_exprs_row_it);
+ get_out_of_scope_exprs_row_iterator (oo_scope_exprs_row_it);
+ if (parent_it == in_scope_exprs_row_it) {
+ changed_in_scope_exprs_at_prev_stop.push_back (a_var_root);
+ } else {
+ THROW_IF_FAIL (parent_it == oo_scope_exprs_row_it);
+ changed_oo_scope_exprs_at_prev_stop.push_back (a_var_root);
+ }
+ NEMIVER_CATCH;
+ }
+
+ /// Invoked whenever a graphical node has been (graphically)
+ /// expanded.
+ ///
+ /// \param a_it an iterator on the row that got expanded.
+ ///
+ /// \param a_path a path to the row that got expanded.
+ void
+ on_tree_view_row_expanded_signal (const Gtk::TreeModel::iterator &a_it,
+ const Gtk::TreeModel::Path &a_path)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ if (!(*a_it)[vutils::get_variable_columns ().needs_unfolding]) {
+ return;
+ }
+ LOG_DD ("A variable needs unfolding");
+
+ IDebugger::VariableSafePtr var =
+ (*a_it)[vutils::get_variable_columns ().variable];
+ debugger.unfold_variable
+ (var,
+ sigc::bind (sigc::mem_fun (*this,
+ &Priv::on_variable_unfolded_signal),
+ a_path));
+
+ NEMIVER_CATCH;
+ }
+
+ /// Invoked when a variable is unfolded.
+ ///
+ /// Usually the variable is unfolded the first time its graphical
+ /// node is expanded.
+ ///
+ /// \param a_expr the variable we are looking at.
+ ///
+ /// \param a_expr_node the graphical node for a_expr.
+ void
+ on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_expr,
+ const Gtk::TreeModel::Path a_expr_node)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ Gtk::TreeModel::iterator var_it = tree_store->get_iter (a_expr_node);
+ vutils::update_unfolded_variable (a_expr,
+ *tree_view,
+ var_it,
+ false /* do not truncate type */);
+ tree_view->expand_row (a_expr_node, false);
+ NEMIVER_CATCH;
+ }
+
+ /// Invoked whenever the current view (widget) is drawn on
+ /// screen. That is, when it becomes visible.
+ void
+ on_draw_signal (const Cairo::RefPtr<Cairo::Context> &)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+ NEMIVER_TRY;
+ if (!is_up2date) {
+ finish_handling_debugger_stopped_event (saved_reason,
+ saved_has_frame,
+ saved_frame);
+ is_up2date = true;
+ }
+ NEMIVER_CATCH;
+ }
+
+ /// Callback function called whenever the user presses button from
+ /// either the keyboard or the mousse.
+ void
+ on_button_press_signal (GdkEventButton *a_event)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ // Right-clicking should pop up a context menu
+ if (a_event->type == GDK_BUTTON_PRESS
+ && a_event->button == 3)
+ popup_contextual_menu (a_event);
+
+ NEMIVER_CATCH;
+ }
+
+ /// Callback called whenever the user clicks on a menu item to
+ /// remove the set of currently selected variables.
+ void
+ on_remove_expressions_action ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ Glib::RefPtr<Gtk::TreeSelection> selection = tree_view->get_selection ();
+ THROW_IF_FAIL (selection);
+
+ std::vector<Gtk::TreeModel::Path> selected_rows =
+ selection->get_selected_rows ();
+
+ std::list<IDebugger::VariableSafePtr> delete_list;
+ for (std::vector<Gtk::TreeModel::Path>::const_iterator it =
+ selected_rows.begin ();
+ it != selected_rows.end ();
+ ++it) {
+ Gtk::TreeModel::iterator i = tree_store->get_iter (*it);
+ IDebugger::VariableSafePtr cur_var =
+ (*i)[vutils::get_variable_columns ().variable];
+ THROW_IF_FAIL (cur_var);
+ delete_list.push_back (cur_var->root ());
+ }
+ for (std::list<IDebugger::VariableSafePtr>::const_iterator it =
+ delete_list.begin ();
+ it != delete_list.end ();
+ ++it) {
+ remove_expression (*it);
+ }
+
+ NEMIVER_CATCH;
+ }
+
+ /// Callback function invoked whenever the user clicks on a menu
+ /// item to monitor a new expression.
+ void
+ on_add_expression_action ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ ExprInspectorDialog dialog (debugger, perspective);
+ dialog.expr_monitoring_requested ().connect
+ (sigc::mem_fun (*this,
+ &ExprMonitor::Priv::on_expr_monitoring_requested));
+ dialog.inspector ().expr_inspected_signal ().connect
+ (sigc::bind (sigc::mem_fun (*this,
+ &ExprMonitor::Priv::on_expr_inspected),
+ &dialog));
+ dialog.run ();
+ }
+
+ /// Callback function invoked whenever an expression has been been
+ /// inspected. This is usuall triggered when the user clicks on
+ /// the the "inspect" button in the ExprInspectorDialog dialog.
+ ///
+ /// \param a_expr the expression that got recently inspected
+ ///
+ /// \param a_dialog the dialog this variable monitor belongs to.
+ void
+ on_expr_inspected (const IDebugger::VariableSafePtr a_expr,
+ ExprInspectorDialog *a_dialog)
+ {
+ if (expression_is_monitored (*a_expr))
+ {
+ a_dialog->functionality_mask
+ (a_dialog->functionality_mask ()
+ & ~ExprInspectorDialog::FUNCTIONALITY_EXPR_MONITOR_PICKER);
+ }
+ else
+ {
+ a_dialog->functionality_mask
+ (a_dialog->functionality_mask ()
+ | ExprInspectorDialog::FUNCTIONALITY_EXPR_MONITOR_PICKER);
+ }
+ }
+
+ /// Callback function invoked whenever the user clicks on the
+ /// "monitor variable" button in the dialog launched by
+ /// on_add_expression_action above.
+ void
+ on_expr_monitoring_requested (const IDebugger::VariableSafePtr a_expr)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ add_expression (a_expr);
+ }
+
+ /// Callback function invoked whenever the user selects or
+ /// unselects rows of the variable monitor.
+ void
+ on_tree_view_selection_changed_signal ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (tree_view);
+ THROW_IF_FAIL (tree_store);
+
+ Glib::RefPtr<Gtk::TreeSelection> selection = tree_view->get_selection ();
+ THROW_IF_FAIL (selection);
+
+ selected_paths =
+ selection->get_selected_rows ();
+
+ NEMIVER_CATCH;
+ }
+
+ // *********************
+ // </signal handlers>
+ // ********************
+
+}; // end struct ExprMonitor
+
+ExprMonitor::ExprMonitor (IDebugger &a_dbg,
+ IPerspective &a_perspective)
+{
+ m_priv.reset (new Priv (a_dbg, a_perspective));
+}
+
+ExprMonitor::~ExprMonitor ()
+{
+}
+
+/// Return the widget for this type.
+Gtk::Widget&
+ExprMonitor::widget ()
+{
+ THROW_IF_FAIL (m_priv);
+ return m_priv->get_widget ();
+}
+
+/// Monitor a new variable. IOW, add a new variable to the monitor.
+///
+/// \param a_expr the new variable to monitor.
+void
+ExprMonitor::add_expression (const IDebugger::VariableSafePtr a_expr)
+{
+ m_priv->add_expression (a_expr);
+}
+
+/// Monitor a list of new variables.
+///
+/// \param a_exprs the variables to monitor.
+void
+ExprMonitor::add_expressions (const IDebugger::VariableList &a_exprs)
+{
+ m_priv->add_expressions (a_exprs);
+}
+
+/// Return true iff the given variable is being monitored by this
+/// monitored.
+///
+/// \param a_expr the variable to check for.
+bool
+ExprMonitor::expression_is_monitored (const IDebugger::Variable &a_expr) const
+{
+ return m_priv->expression_is_monitored (a_expr);
+}
+
+/// Remove a variable from the monitor.
+///
+/// \param a_expr the variable to remove from the monitor.
+void
+ExprMonitor::remove_expression (const IDebugger::VariableSafePtr a_expr)
+{
+ m_priv->remove_expression (a_expr);
+}
+
+/// Remove a list of variables from the monitor.
+///
+/// \param a_exprs the list of variables to remove.
+void
+ExprMonitor::remove_expressions (const std::list<IDebugger::VariableSafePtr> &a_exprs)
+{
+ m_priv->remove_expressions (a_exprs);
+}
+
+/// Clear the widget.
+void
+ExprMonitor::re_init_widget (bool a_remember_variables)
+{
+ m_priv->re_init_widget (a_remember_variables);
+}
+
+NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/persp/dbgperspective/nmv-expr-monitor.h b/src/persp/dbgperspective/nmv-expr-monitor.h
new file mode 100644
index 0000000..3595ffc
--- /dev/null
+++ b/src/persp/dbgperspective/nmv-expr-monitor.h
@@ -0,0 +1,68 @@
+//Author: Dodji Seketeli
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NMV_EXPR_MONITOR_H__
+#define __NMV_EXPR_MONITOR_H__
+
+#include "common/nmv-safe-ptr-utils.h"
+#include "common/nmv-object.h"
+#include "nmv-i-perspective.h"
+#include "nmv-i-debugger.h"
+
+NEMIVER_BEGIN_NAMESPACE (nemiver)
+
+/// \brief A widget that can monitor the state of a given set of
+/// variables.
+///
+/// Each time the debugger stops, this widget updates the state of the
+/// variables that have been added to it. Whenever a variable has
+/// gone out of scope [when it hasn't been created by address] the
+/// widget is supposed to clearly show it
+class NEMIVER_API ExprMonitor : public nemiver::common::Object {
+ // Non copyable
+ ExprMonitor (const ExprMonitor&);
+ ExprMonitor& operator= (const ExprMonitor&);
+
+ struct Priv;
+ SafePtr<Priv> m_priv;
+
+ protected:
+ ExprMonitor ();
+
+ public:
+ ExprMonitor (IDebugger &a_dbg,
+ IPerspective &a_perspective);
+ virtual ~ExprMonitor ();
+ Gtk::Widget& widget ();
+ void add_expression (const IDebugger::VariableSafePtr a_expr);
+ void add_expressions (const IDebugger::VariableList &a_exprs);
+ bool expression_is_monitored (const IDebugger::Variable &a_expr) const;
+ void remove_expression (const IDebugger::VariableSafePtr a_expr);
+ void remove_expressions (const IDebugger::VariableList &a_exprs);
+ void re_init_widget (bool a_remember_variables);
+};// end ExprMonitor
+
+NEMIVER_END_NAMESPACE (nemiver)
+
+#endif // __NMV_EXPR_MONITOR_H__
diff --git a/src/persp/dbgperspective/nmv-variables-utils.cc b/src/persp/dbgperspective/nmv-variables-utils.cc
index dfb5117..f330497 100644
--- a/src/persp/dbgperspective/nmv-variables-utils.cc
+++ b/src/persp/dbgperspective/nmv-variables-utils.cc
@@ -155,7 +155,7 @@ update_a_variable_node (const IDebugger::VariableSafePtr a_var,
LOG_FUNCTION_SCOPE_NORMAL_DD;
if (a_var) {
LOG_DD ("going to really update variable '"
- << a_var->internal_name ()
+ << a_var->id ()
<< "'");
} else {
LOG_DD ("eek, got null variable");
@@ -258,6 +258,7 @@ find_a_variable (const IDebugger::VariableSafePtr a_var,
Gtk::TreeModel::iterator &a_out_row_it)
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
+ LOG_DD ("a_var: " << a_var->id ());
LOG_DD ("looking for variable: " << a_var->internal_name ());
if (!a_var) {
@@ -266,11 +267,9 @@ find_a_variable (const IDebugger::VariableSafePtr a_var,
}
Gtk::TreeModel::iterator row_it;
- IDebugger::VariableSafePtr var;
for (row_it = a_parent_row_it->children ().begin ();
row_it != a_parent_row_it->children ().end ();
++row_it) {
- var = row_it->get_value (get_variable_columns ().variable);
if (variables_match (a_var, row_it)) {
a_out_row_it = row_it;
LOG_DD ("found variable at row: " << get_row_name (row_it));
@@ -467,6 +466,8 @@ update_a_variable (const IDebugger::VariableSafePtr a_var,
bool a_update_members)
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
+ LOG_DD ("a_var: " << a_var->id ());
+
THROW_IF_FAIL (a_parent_row_it);
Gtk::TreeModel::iterator row_it;
@@ -641,7 +642,7 @@ append_a_variable (const IDebugger::VariableSafePtr a_var,
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
- Glib::RefPtr<Gtk::TreeStore> tree_store =
+ Glib::RefPtr<Gtk::TreeStore> tree_store =
Glib::RefPtr<Gtk::TreeStore>::cast_dynamic (a_tree_view.get_model ());
THROW_IF_FAIL (tree_store);
@@ -698,10 +699,6 @@ set_a_variable (const IDebugger::VariableSafePtr a_var,
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
- Glib::RefPtr<const Gtk::TreeStore> tree_store =
- Glib::RefPtr<const Gtk::TreeStore>::cast_dynamic (a_tree_view.get_model ());
- THROW_IF_FAIL (tree_store);
-
if (!a_var) {
return false;
}
@@ -727,6 +724,34 @@ set_a_variable (const IDebugger::VariableSafePtr a_var,
return true;
}
+/// Unlike the graphical node representing a variable a_var.
+///
+/// \param a_var the variable which graphical node to unlink.
+///
+/// \param a_store the tree store of the tree view to act upon.
+///
+/// \param a_parent_row_it the parent graphical row under which we
+/// have to look to find the graphical node to unlink.
+///
+/// \return true upon successful unlinking, false otherwise.
+bool
+unlink_a_variable_row (const IDebugger::VariableSafePtr &a_var,
+ const Glib::RefPtr<Gtk::TreeStore> &a_store,
+ const Gtk::TreeModel::iterator &a_parent_row_it)
+{
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ Gtk::TreeModel::iterator var_to_unlink_it;
+ if (!find_a_variable (a_var, a_parent_row_it, var_to_unlink_it)) {
+ LOG_DD ("var " << a_var->id () << " was not found");
+ return false;
+ }
+
+ a_store->erase (var_to_unlink_it);
+ LOG_DD ("var " << a_var->id () << " was found and unlinked");
+ return true;
+}
+
/// Unlink the graphical nodes representing the member variables of
/// the variable pointed to by the given row iterator.
///
diff --git a/src/persp/dbgperspective/nmv-variables-utils.h b/src/persp/dbgperspective/nmv-variables-utils.h
index 29861a6..c90bb1c 100644
--- a/src/persp/dbgperspective/nmv-variables-utils.h
+++ b/src/persp/dbgperspective/nmv-variables-utils.h
@@ -128,6 +128,10 @@ bool set_a_variable (const IDebugger::VariableSafePtr a_var,
Gtk::TreeModel::iterator a_row_it,
bool a_truncate_type);
+bool unlink_a_variable_row (const IDebugger::VariableSafePtr &a_var,
+ const Glib::RefPtr<Gtk::TreeStore> &a_store,
+ const Gtk::TreeModel::iterator &a_parent_row_it);
+
bool unlink_member_variable_rows (const Gtk::TreeModel::iterator &a_row_it,
const Glib::RefPtr<Gtk::TreeStore> &a_store);
diff --git a/src/persp/dbgperspective/ui/exprinspectordialog.ui b/src/persp/dbgperspective/ui/exprinspectordialog.ui
index 158db4b..d4ceed7 100644
--- a/src/persp/dbgperspective/ui/exprinspectordialog.ui
+++ b/src/persp/dbgperspective/ui/exprinspectordialog.ui
@@ -19,6 +19,20 @@
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
+ <object class="GtkButton" id="addtomonitorbutton">
+ <property name="label" translatable="yes">Add to monitor</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkButton" id="okbutton">
<property name="label">gtk-close</property>
<property name="visible">True</property>
@@ -31,7 +45,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">0</property>
+ <property name="position">1</property>
</packing>
</child>
</object>
@@ -133,6 +147,7 @@
</object>
</child>
<action-widgets>
+ <action-widget response="-3">addtomonitorbutton</action-widget>
<action-widget response="-7">okbutton</action-widget>
</action-widgets>
</object>
--
1.7.6.5
--
Dodji
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]