[nemiver/monitor-variable] Add support for Expression Monitor
- From: Dodji Seketeli <dodji src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [nemiver/monitor-variable] Add support for Expression Monitor
- Date: Sat, 2 Jun 2012 17:22:22 +0000 (UTC)
commit 22eea38d7d0bbe2ca27b56a707cff77d249acb51
Author: Dodji Seketeli <dodji seketeli org>
Date: Sat Jun 2 18:51:20 2012 +0200
Add support for Expression Monitor
* src/dbgengine/nmv-i-debugger.h (Variable::id)
(Variable::is_morally_root): New methods.
* src/persp/dbgperspective/ui/varinspectordialog.ui: Add a new
"add to monitor" button.
* src/persp/dbgperspective/nmv-vars-monitor.{cc,h}: New file.
* src/persp/dbgperspective/menus/varsmonitorpopup.xml: New
description file for the contextual menu.
* src/persp/dbgperspective/Makefile.am: Add
nmv-vars-monitor.{h,cc} and varsmonitorpopup.xml to the build
system.
* po/POTFILES.in: Add src/persp/dbgperspective/nmv-vars-monitor.cc
to the list of translatable source files.
* src/persp/dbgperspective/nmv-var-inspector-dialog.h: Add
comments.
(VarInspectorDialog::FunctionalityFlags): New enum.
(VarInspectorDialog::functionality_mask): New accessors.
* src/persp/dbgperspective/nmv-var-inspector-dialog.cc
(VarInspectorDialog::Priv::{add_to_monitor_button,expr_monitor_requested,
fun_mask}): New members.
(VarInspectorDialog::Priv::Priv): Initialize new fun_mask member.
(VarInspectorDialog::Priv::on_do_monitor_button_clicked)
(VarInspectorDialog::Priv::functionality_mask): New functions.
(VarInspectorDialog::Priv::build_dialog): Initialize the
add_to_monitor button.
(VarInspectorDialog::Priv::on_var_name_changed_signal): Inspect
button is now controlled by FUNCTIONALITY_EXPR_INSPECTOR flag.
(VarInspectorDialog::Priv::on_variable_inspected): Add to monitor
button is now controlled by FUNCTIONALITY_EXPR_MONITOR_PICKER
flag.
(VarInspectorDialog::expr_monitoring_requested)
(VarInspectorDialog::functionality_mask): New functions.
(VarInspectorDialog::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.
(VarInspectorDialog::Priv::on_variable_inspected): Enable the "add
to monitor" button only when the expression has been inspected.
(VarInspectorDialog::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): Re-init the variable
monitor. (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_variable): 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 | 33 +-
src/persp/dbgperspective/Makefile.am | 2 +
src/persp/dbgperspective/menus/Makefile.am | 2 +-
src/persp/dbgperspective/menus/menus.xml | 2 +
.../dbgperspective/menus/varsmonitorpopup.xml | 9 +
.../nmv-dbg-perspective-default-layout.cc | 3 +-
src/persp/dbgperspective/nmv-dbg-perspective.cc | 75 ++-
src/persp/dbgperspective/nmv-dbg-perspective.h | 3 +-
.../dbgperspective/nmv-var-inspector-dialog.cc | 79 ++-
.../dbgperspective/nmv-var-inspector-dialog.h | 28 +
src/persp/dbgperspective/nmv-variables-utils.cc | 49 +-
src/persp/dbgperspective/nmv-variables-utils.h | 4 +
src/persp/dbgperspective/nmv-vars-monitor.cc | 1002 ++++++++++++++++++++
src/persp/dbgperspective/nmv-vars-monitor.h | 68 ++
src/persp/dbgperspective/ui/varinspectordialog.ui | 17 +-
16 files changed, 1352 insertions(+), 25 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 48bb0c6..10fe1a0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -23,6 +23,7 @@ src/persp/dbgperspective/nmv-var-inspector.cc
src/persp/dbgperspective/nmv-var-inspector.cc
src/persp/dbgperspective/nmv-var-inspector-dialog.cc
src/persp/dbgperspective/nmv-vars-treeview.cc
+src/persp/dbgperspective/nmv-vars-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 9bf6f05..9a76347 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,15 @@ 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 +665,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 7852432..e79cca7 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-var-inspector.cc \
$(h)/nmv-var-inspector.h \
+$(h)/nmv-vars-monitor.cc \
+$(h)/nmv-vars-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..3580dfd 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 varsmonitorpopup.xml
menusdir = @NEMIVER_PLUGINS_DIR@/$(PLUGIN_NAME)/menus
menus_DATA = $(menusfiles)
diff --git a/src/persp/dbgperspective/menus/menus.xml b/src/persp/dbgperspective/menus/menus.xml
index 9273add..d8fa0f9 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="ActivateVarsMonitorViewMenuAction"
+ name="ActivateVarsMonitorViewMenuItem"/>
</menu>
<menu action="DebugMenuAction" name="DebugMenu">
<menuitem action="RunMenuItemAction" name="RunMenuItem"/>
diff --git a/src/persp/dbgperspective/menus/varsmonitorpopup.xml b/src/persp/dbgperspective/menus/varsmonitorpopup.xml
new file mode 100644
index 0000000..09168c1
--- /dev/null
+++ b/src/persp/dbgperspective/menus/varsmonitorpopup.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<ui>
+ <popup name="VarsMonitorPopup">
+ <menuitem action="AddVariableMenuItemAction"
+ name="AddVariableMenuItem"/>
+ <menuitem action="RemoveVariablesMenuItemAction"
+ name="RemoveVariablesMenuItem" />
+ </popup>
+</ui>
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 7c905af..47e2bab 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-vars-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 *VARS_MONITOR_VIEW_TITLE = _("Variables 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_variable_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
@@ -419,6 +422,7 @@ private:
#ifdef WITH_MEMORYVIEW
void on_activate_memory_view ();
#endif // WITH_MEMORYVIEW
+ void on_activate_vars_monitor_view ();
void on_activate_global_variables ();
void on_default_config_read ();
@@ -758,6 +762,8 @@ public:
MemoryView& get_memory_view ();
#endif // WITH_MEMORYVIEW
+ VarsMonitor& get_vars_monitor_view ();
+
ThreadList& get_thread_list ();
bool set_where (const IDebugger::Frame &a_frame,
@@ -908,6 +914,7 @@ struct DBGPerspective::Priv {
#ifdef WITH_MEMORYVIEW
SafePtr<MemoryView> memory_view;
#endif // WITH_MEMORYVIEW
+ SafePtr<VarsMonitor> vars_monitor;
int current_page_num;
IDebuggerSafePtr debugger;
@@ -1626,6 +1633,21 @@ DBGPerspective::on_inspect_variable_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->vars_monitor);
+
+ m_priv->vars_monitor->add_variable (a_var);
+
+ NEMIVER_CATCH;
+}
+
+void
DBGPerspective::on_call_function_action ()
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
@@ -1838,6 +1860,7 @@ DBGPerspective::on_going_to_run_target_signal ()
get_memory_view ().clear ();
#endif
get_registers_view ().clear ();
+ get_vars_monitor_view ().re_init_widget ();;
NEMIVER_CATCH;
}
@@ -2881,6 +2904,19 @@ DBGPerspective::on_activate_memory_view ()
#endif //WITH_MEMORYVIEW
void
+DBGPerspective::on_activate_vars_monitor_view ()
+{
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (m_priv);
+ m_priv->layout ().activate_view (VARS_MONITOR_VIEW_INDEX);
+
+ NEMIVER_CATCH;
+}
+
+void
DBGPerspective::on_activate_global_variables ()
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
@@ -3411,6 +3447,16 @@ DBGPerspective::init_actions ()
},
#endif // WITH_MEMORYVIEW
{
+ "ActivateVarsMonitorViewMenuAction",
+ nil_stock_id,
+ VARS_MONITOR_VIEW_TITLE,
+ _("Switch to Variables Monitor View"),
+ sigc::mem_fun (*this, &DBGPerspective::on_activate_vars_monitor_view),
+ ActionEntry::DEFAULT,
+ "<alt>6",
+ false
+ },
+ {
"DebugMenuAction",
nil_stock_id,
_("_Debug"),
@@ -3805,6 +3851,7 @@ DBGPerspective::clear_status_notebook ()
#ifdef WITH_MEMORYVIEW
get_memory_view ().clear ();
#endif // WITH_MEMORYVIEW
+ get_vars_monitor_view ().re_init_widget ();
}
void
@@ -4953,6 +5000,9 @@ DBGPerspective::add_views_to_layout ()
MEMORY_VIEW_TITLE,
MEMORY_VIEW_INDEX);
#endif // WITH_MEMORYVIEW
+ m_priv->layout ().append_view (get_vars_monitor_view ().widget (),
+ VARS_MONITOR_VIEW_TITLE,
+ VARS_MONITOR_VIEW_INDEX);
m_priv->layout ().do_init ();
}
@@ -7844,6 +7894,9 @@ DBGPerspective::inspect_variable (const UString &a_variable_name)
VarInspectorDialog 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_variable_name != "") {
dialog.inspect_variable (a_variable_name);
}
@@ -8145,6 +8198,18 @@ DBGPerspective::get_memory_view ()
}
#endif // WITH_MEMORYVIEW
+/// Return the variable monitor view.
+VarsMonitor&
+DBGPerspective::get_vars_monitor_view ()
+{
+ THROW_IF_FAIL (m_priv);
+
+ if (!m_priv->vars_monitor)
+ m_priv->vars_monitor.reset (new VarsMonitor (*debugger (),
+ *this));
+ THROW_IF_FAIL (m_priv->vars_monitor);
+ return *m_priv->vars_monitor;
+}
struct ScrollTextViewToEndClosure {
Gtk::TextView* text_view;
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.h b/src/persp/dbgperspective/nmv-dbg-perspective.h
index ddb00ff..1eced95 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
+ VARS_MONITOR_VIEW_INDEX
};
class SourceEditor;
diff --git a/src/persp/dbgperspective/nmv-var-inspector-dialog.cc b/src/persp/dbgperspective/nmv-var-inspector-dialog.cc
index 1c5f0e7..9ccb59c 100644
--- a/src/persp/dbgperspective/nmv-var-inspector-dialog.cc
+++ b/src/persp/dbgperspective/nmv-var-inspector-dialog.cc
@@ -51,11 +51,15 @@ class VarInspectorDialog::Priv {
Gtk::ComboBox *var_name_entry;
Glib::RefPtr<Gtk::ListStore> m_variable_history;
Gtk::Button *inspect_button;
+ Gtk::Button *add_to_monitor_button;
SafePtr<VarInspector> var_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_variable));
+ 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,22 @@ 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 +335,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 +357,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 (var_inspector->get_variable ());
+
+ expr_monitoring_requested.emit (var_inspector->get_variable ());
+
+ NEMIVER_CATCH
}
//************************
@@ -438,5 +481,31 @@ VarInspectorDialog::get_history (std::list<UString> &a_hist) const
m_priv->get_history (a_hist);
}
-NEMIVER_END_NAMESPACE (nemiver)
+sigc::signal<void, IDebugger::VariableSafePtr>&
+VarInspectorDialog::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 VarInspector::FunctionalityFlags.
+void
+VarInspectorDialog::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 VarInspector::FunctionalityFlags.
+unsigned
+VarInspectorDialog::functionality_mask ()
+{
+ THROW_IF_FAIL (m_priv);
+
+ return m_priv->functionality_mask ();
+}
+
+NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/persp/dbgperspective/nmv-var-inspector-dialog.h b/src/persp/dbgperspective/nmv-var-inspector-dialog.h
index f397423..d5178c4 100644
--- a/src/persp/dbgperspective/nmv-var-inspector-dialog.h
+++ b/src/persp/dbgperspective/nmv-var-inspector-dialog.h
@@ -44,6 +44,26 @@ class VarInspectorDialog : 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)
+ };
+
VarInspectorDialog (IDebugger &a_debugger,
IPerspective &a_perspective);
virtual ~VarInspectorDialog ();
@@ -57,6 +77,14 @@ public:
VarInspector& 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 VarInspectorDialog
NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/persp/dbgperspective/nmv-variables-utils.cc b/src/persp/dbgperspective/nmv-variables-utils.cc
index dfb5117..4804485 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;
}
@@ -721,12 +718,42 @@ set_a_variable (const IDebugger::VariableSafePtr a_var,
for (it = a_var->members ().begin ();
it != a_var->members ().end ();
++it) {
- append_a_variable (*it, a_tree_view, a_row_it, a_truncate_type);
+ append_a_variable (*it, a_tree_view,
+ a_row_it,
+ a_truncate_type);
}
}
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.
///
@@ -782,7 +809,9 @@ visualize_a_variable (const IDebugger::VariableSafePtr a_var,
if (!unlink_member_variable_rows (a_row_it, a_store))
return false;
- return set_a_variable (a_var, a_tree_view, a_row_it,
+ return set_a_variable (a_var,
+ a_tree_view,
+ a_row_it,
/*a_truncate_type=*/true);
}
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/nmv-vars-monitor.cc b/src/persp/dbgperspective/nmv-vars-monitor.cc
new file mode 100644
index 0000000..169c8f8
--- /dev/null
+++ b/src/persp/dbgperspective/nmv-vars-monitor.cc
@@ -0,0 +1,1002 @@
+//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-vars-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-var-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 VarsMonitor::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_vars_row_ref;
+ SafePtr<Gtk::TreeRowReference> out_of_scope_vars_row_ref;
+ Gtk::TreeModel::iterator cur_selected_row;
+ IDebugger::VariableList monitored_variables;
+ IDebugger::VariableList changed_in_scope_vars_at_prev_stop;
+ IDebugger::VariableList changed_oo_scope_vars_at_prev_stop;
+ map<IDebugger::VariableSafePtr, bool> in_scope_vars;
+ 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 variables
+ Gtk::Widget&
+ get_widget ()
+ {
+ 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_vars_row_ref nor out_of_scope_vars_row_ref
+ // nor tree_view should be non-null before the widget has been
+ // initialized.
+ THROW_IF_FAIL (!in_scope_vars_row_ref
+ && !out_of_scope_vars_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 variables that are in-scope and a row for
+ // variables that are out-of-scope
+ // *************************************************************
+ Gtk::TreeModel::iterator it = tree_store->append ();
+ (*it)[vutils::get_variable_columns ().name] = _("In scope variables");
+ in_scope_vars_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 variables");
+ out_of_scope_vars_row_ref.reset
+ (new Gtk::TreeRowReference (tree_store,
+ tree_store->get_path (it)));
+
+ THROW_IF_FAIL (in_scope_vars_row_ref
+ && out_of_scope_vars_row_ref);
+
+ // And now finish the initialization.
+ connect_to_debugger_signal ();
+ init_graphical_signals ();
+ init_actions ();
+ re_init_widget ();
+
+ initialized = true;
+ }
+
+ /// Re-initialize the widget.
+ void
+ re_init_widget ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+ }
+
+ /// 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));
+ }
+
+ /// 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_vars_monitor_action_entries [] = {
+ {
+ "RemoveVariablesMenuItemAction",
+ Gtk::Stock::DELETE,
+ _("Remove"),
+ _("Remove selected variables from the monitor"),
+ sigc::mem_fun (*this, &Priv::on_remove_variables_action),
+ ui_utils::ActionEntry::DEFAULT,
+ "",
+ false
+ },
+ {
+ "AddVariableMenuItemAction",
+ Gtk::Stock::ADD,
+ _("New ..."),
+ _("Add a new variable to the monitor"),
+ sigc::mem_fun (*this, &Priv::on_add_variable_action),
+ ui_utils::ActionEntry::DEFAULT,
+ "",
+ false
+ }
+ };
+ action_group =
+ Gtk::ActionGroup::create ("vars-monitor-action-group");
+ action_group->set_sensitive (true);
+ int num_actions =
+ sizeof (s_vars_monitor_action_entries)
+ /
+ sizeof (ui_utils::ActionEntry);
+ ui_utils::add_action_entries_to_action_group
+ (s_vars_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_var the variable to check for.
+ bool
+ variable_monitored (const IDebugger::Variable &a_var) const
+ {
+ IDebugger::VariableList::const_iterator it;
+ for (it = monitored_variables.begin ();
+ it != monitored_variables.end ();
+ ++it) {
+ if (!a_var.internal_name ().empty ()
+ && a_var.internal_name () == (*it)->internal_name ())
+ // Both variables have the same internal name, so they
+ // are equal.
+ return true;
+ else if (!(*it)->needs_unfolding ()
+ && !a_var.needs_unfolding ()) {
+ // Both variables have been unfolded, so we can
+ // compare them by value.
+ if ((*it)->equals_by_value (a_var))
+ return true;
+ } else {
+ if (a_var.name () == a_var.name ())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Monitor a new expression (or varibale). In other words, add a
+ /// new variable to the monitor.
+ ///
+ /// \param a_var the variable to monitor.
+ void
+ add_variable (const IDebugger::VariableSafePtr a_var)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("a_var: " << a_var->id ());
+
+ if (!a_var || variable_monitored (*a_var))
+ return;
+
+ monitored_variables.push_back (a_var);
+ Gtk::TreeModel::iterator root_node;
+ if (a_var->in_scope ())
+ get_in_scope_vars_row_iterator (root_node);
+ else
+ get_out_of_scope_vars_row_iterator (root_node);
+ THROW_IF_FAIL (root_node);
+ vutils::append_a_variable (a_var,
+ *tree_view,
+ root_node,
+ /*a_truncate_type=*/true);
+ }
+
+ /// Monitor a list of new variables.
+ ///
+ /// \param a_vars the variables to monitor.
+ void
+ add_variables (const IDebugger::VariableList &a_vars)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ IDebugger::VariableList::const_iterator it = a_vars.begin ();
+ for (; it != a_vars.end (); ++it)
+ add_variable (*it);
+ }
+
+ /// Remove a variable from the monitor.
+ ///
+ /// \param a_var the variable to remove from the monitor.
+ void
+ remove_variable (const IDebugger::VariableSafePtr a_var)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("a_var: " << a_var->id ());
+
+ IDebugger::VariableList::iterator it = monitored_variables.begin ();
+ for (; it != monitored_variables.end (); ++it) {
+ if ((*it)->internal_name () == a_var->internal_name ()
+ || (*it)->equals_by_value (*a_var)) {
+ // Remove the graphical representation of the node,
+ // and then remove the node itself from the list of
+ // monitored variables.
+ Gtk::TreeModel::iterator parent_row;
+ if (a_var->in_scope ())
+ get_in_scope_vars_row_iterator (parent_row);
+ else
+ get_out_of_scope_vars_row_iterator (parent_row);
+ THROW_IF_FAIL (parent_row);
+ vutils::unlink_a_variable_row (a_var, tree_store, parent_row);
+ monitored_variables.erase (it);
+ // We removed an element from the array while
+ // iterating on it so the iterator is invalidated. We
+ // must not use it again.
+ break;
+ }
+ }
+ }
+
+ /// Remove a set of variables from the monitor.
+ void
+ remove_variables (const IDebugger::VariableList &a_vars)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ IDebugger::VariableList::const_iterator it = a_vars.begin ();
+ for (; it != a_vars.end (); ++it)
+ remove_variable (*it);
+ }
+
+ /// Return an iterator on the monitored variables that are in
+ /// scope.
+ bool
+ get_in_scope_vars_row_iterator (Gtk::TreeModel::iterator &a_it)
+ {
+ if (!in_scope_vars_row_ref)
+ return false;
+ a_it = tree_store->get_iter (in_scope_vars_row_ref->get_path ());
+ return true;
+ }
+
+ /// Return an iterator on the monitored variables that are out of
+ /// scope.
+ bool
+ get_out_of_scope_vars_row_iterator (Gtk::TreeModel::iterator &a_it)
+ {
+ if (!out_of_scope_vars_row_ref)
+ return false;
+ a_it = tree_store->get_iter (out_of_scope_vars_row_ref->get_path ());
+ return true;
+ }
+
+ /// Ensure that a given variable is only under a given graphical
+ /// node.
+ ///
+ /// This is a sub-routine for update_var_in_scope_or_not.
+ /// \param a_var the variable to consider
+ ///
+ /// \param a_first the node under which we want a_var to be.
+ ///
+ /// \param a_second the node under which we *don't* want a_var to
+ /// be.
+ ///
+ /// \param a_var_it a graphical iterator pointing to where a_var
+ /// was put, finally.
+ void
+ ensure_var_under_first_but_not_under_second
+ (const IDebugger::VariableSafePtr &a_var,
+ Gtk::TreeModel::iterator &a_first,
+ Gtk::TreeModel::iterator &a_second,
+ Gtk::TreeModel::iterator &a_var_it)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ // If is under a_second then remove it from there.
+ vutils::unlink_a_variable_row (a_var, tree_store, a_second);
+
+ // If a_var is not in under a_first, add it now.
+ Gtk::TreeModel::iterator var_it;
+ if (!vutils::find_a_variable (a_var, a_first, a_var_it)) {
+ LOG_DD ("Adding variable "
+ << a_var->id ()
+ << " under the first iterator");
+ vutils::append_a_variable (a_var, *tree_view,
+ a_first, a_var_it,
+ /*a_truncate_type=*/true);
+ }
+ }
+
+ /// Update the graphical state of a_var so that, it appears under
+ /// the right graphical node depending on whether it is in scope
+ /// or not.
+ void
+ update_var_in_scope_or_not (const IDebugger::VariableSafePtr &a_var,
+ Gtk::TreeModel::iterator &a_var_it,
+ Gtk::TreeModel::iterator &a_parent_it)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("a_var: " << a_var->id ());
+
+ Gtk::TreeModel::iterator in_scope_vars_it, out_of_scope_vars_it;
+
+ THROW_IF_FAIL (a_var->is_morally_root ());
+
+ get_in_scope_vars_row_iterator (in_scope_vars_it);
+ get_out_of_scope_vars_row_iterator (out_of_scope_vars_it);
+
+ if (a_var->in_scope ()) {
+ LOG_DD ("variable " << a_var->id () << " is in scope");
+ in_scope_vars[a_var] = true;
+ ensure_var_under_first_but_not_under_second (a_var,
+ in_scope_vars_it,
+ out_of_scope_vars_it,
+ a_var_it);
+ a_parent_it = in_scope_vars_it;
+ } else {
+ LOG_DD ("variable " << a_var->id () << " is not in scope");
+ in_scope_vars.erase (a_var);
+ ensure_var_under_first_but_not_under_second (a_var,
+ out_of_scope_vars_it,
+ in_scope_vars_it,
+ a_var_it);
+ a_parent_it = out_of_scope_vars_it;
+ }
+ }
+
+ /// Clear the internal state we maintain to know what variables
+ /// changed at the previous step.
+ void
+ clear_vars_changed_at_prev_step ()
+ {
+ changed_in_scope_vars_at_prev_stop.clear ();
+ changed_oo_scope_vars_at_prev_stop.clear ();
+ }
+
+ /// Graphically update the variables that changed at the previous
+ /// step.
+ void
+ update_vars_changed_at_prev_step ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ Gtk::TreeModel::iterator in_scope_vars_it, oo_scope_vars_it;
+
+ get_in_scope_vars_row_iterator (in_scope_vars_it);
+ get_out_of_scope_vars_row_iterator (oo_scope_vars_it);
+
+ IDebugger::VariableList::const_iterator it;
+ for (it = changed_in_scope_vars_at_prev_stop.begin ();
+ it != changed_in_scope_vars_at_prev_stop.end ();
+ ++it)
+ vutils::update_a_variable (*it, *tree_view,
+ in_scope_vars_it,
+ /*a_truncate_type=*/false,
+ /*a_handle_highlight=*/true,
+ /*a_is_new_frame=*/true,
+ /*a_update_members=*/true);
+
+ for (it = changed_oo_scope_vars_at_prev_stop.begin ();
+ it != changed_oo_scope_vars_at_prev_stop.end ();
+ ++it)
+ vutils::update_a_variable (*it, *tree_view,
+ oo_scope_vars_it,
+ /*a_truncate_type=*/false,
+ /*a_handle_highlight=*/true,
+ /*a_is_new_frame=*/true,
+ /*a_update_members=*/true);
+ clear_vars_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) {
+ return;
+ }
+
+ is_new_frame = (saved_frame != a_frame);
+ saved_frame = a_frame;
+
+ // Clear the highlighting from variables that where
+ // highlighted during previous step.
+ update_vars_changed_at_prev_step ();
+
+ // Walk the monitored variables and list those that have
+ // changed.
+ IDebugger::VariableList::const_iterator it;
+ for (it = monitored_variables.begin ();
+ it != monitored_variables.end ();
+ ++it) {
+ debugger.list_changed_variables (*it,
+ sigc::bind
+ (sigc::mem_fun
+ (*this, &Priv::on_vars_changed),
+ *it));
+ }
+
+ 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", "varsmonitorpopup.xml"),
+ absolute_path);
+ get_ui_manager ()->add_ui_from_file (absolute_path);
+ get_ui_manager ()->ensure_update ();
+ contextual_menu =
+ get_ui_manager ()->get_widget ("/VarsMonitorPopup");
+ 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
+ variable_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_variable_action =
+ get_ui_manager ()->get_action
+ ("/VarsMonitorPopup/RemoveVariablesMenuItem");
+ THROW_IF_FAIL (remove_variable_action);
+
+ Glib::RefPtr<Gtk::TreeSelection> selection = tree_view->get_selection ();
+ THROW_IF_FAIL (selection);
+
+ remove_variable_action->set_sensitive
+ (variable_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;
+ }
+
+ /// 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_var_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.
+ 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_vars_at_prev_stop and
+ // changed_oo_scope_vars_at_prev_stop variables.
+ Gtk::TreeModel::iterator in_scope_vars_row_it, oo_scope_vars_row_it;
+ get_in_scope_vars_row_iterator (in_scope_vars_row_it);
+ get_out_of_scope_vars_row_iterator (oo_scope_vars_row_it);
+ if (parent_it == in_scope_vars_row_it) {
+ changed_in_scope_vars_at_prev_stop.push_back (a_var_root);
+ } else {
+ THROW_IF_FAIL (parent_it == oo_scope_vars_row_it);
+ changed_oo_scope_vars_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_var the variable we are looking at.
+ ///
+ /// \param a_var_node the graphical node for a_var.
+ void
+ on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
+ const Gtk::TreeModel::Path a_var_node)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ NEMIVER_TRY;
+
+ Gtk::TreeModel::iterator var_it = tree_store->get_iter (a_var_node);
+ vutils::update_unfolded_variable (a_var,
+ *tree_view,
+ var_it,
+ false /* do not truncate type */);
+ tree_view->expand_row (a_var_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_variables_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_variable (*it);
+ }
+
+ NEMIVER_CATCH;
+ }
+
+ /// Callback function invoked whenever the user clicks on a menu
+ /// item to monitor a new expression.
+ void
+ on_add_variable_action ()
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ VarInspectorDialog dialog (debugger, perspective);
+ dialog.expr_monitoring_requested ().connect
+ (sigc::mem_fun (*this,
+ &VarsMonitor::Priv::on_expr_monitoring_requested));
+ dialog.inspector ().var_inspected_signal ().connect
+ (sigc::bind (sigc::mem_fun (*this,
+ &VarsMonitor::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 VarInspectorDialog dialog.
+ ///
+ /// \param a_var the expression that got recently inspected
+ ///
+ /// \param a_dialog the dialog this variable monitor belongs to.
+ void
+ on_expr_inspected (const IDebugger::VariableSafePtr a_var,
+ VarInspectorDialog *a_dialog)
+ {
+ if (variable_monitored (*a_var))
+ {
+ a_dialog->functionality_mask
+ (a_dialog->functionality_mask ()
+ & ~VarInspectorDialog::FUNCTIONALITY_EXPR_MONITOR_PICKER);
+ }
+ else
+ {
+ a_dialog->functionality_mask
+ (a_dialog->functionality_mask ()
+ | VarInspectorDialog::FUNCTIONALITY_EXPR_MONITOR_PICKER);
+ }
+ }
+
+ /// Callback function invoked whenever the user clicks on the
+ /// "monitor variable" button in the dialog launched by
+ /// on_add_variable_action above.
+ void
+ on_expr_monitoring_requested (const IDebugger::VariableSafePtr a_var)
+ {
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ add_variable (a_var);
+ }
+
+ /// 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 VarsMonitor
+
+VarsMonitor::VarsMonitor (IDebugger &a_dbg,
+ IPerspective &a_perspective)
+{
+ m_priv.reset (new Priv (a_dbg, a_perspective));
+}
+
+VarsMonitor::~VarsMonitor ()
+{
+}
+
+/// Return the widget for this type.
+Gtk::Widget&
+VarsMonitor::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_var the new variable to monitor.
+void
+VarsMonitor::add_variable (const IDebugger::VariableSafePtr a_var)
+{
+ m_priv->add_variable (a_var);
+}
+
+/// Monitor a list of new variables.
+///
+/// \param a_vars the variables to monitor.
+void
+VarsMonitor::add_variables (const IDebugger::VariableList &a_vars)
+{
+ m_priv->add_variables (a_vars);
+}
+
+/// Return true iff the given variable is being monitored by this
+/// monitored.
+///
+/// \param a_var the variable to check for.
+bool
+VarsMonitor::variable_monitored (const IDebugger::Variable &a_var) const
+{
+ return m_priv->variable_monitored (a_var);
+}
+
+/// Remove a variable from the monitor.
+///
+/// \param a_var the variable to remove from the monitor.
+void
+VarsMonitor::remove_variable (const IDebugger::VariableSafePtr a_var)
+{
+ m_priv->remove_variable (a_var);
+}
+
+/// Remove a list of variables from the monitor.
+///
+/// \param a_vars the list of variables to remove.
+void
+VarsMonitor::remove_variables (const std::list<IDebugger::VariableSafePtr> &a_vars)
+{
+ m_priv->remove_variables (a_vars);
+}
+
+/// Clear the widget.
+void
+VarsMonitor::re_init_widget ()
+{
+ m_priv->re_init_widget ();
+}
+
+NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/persp/dbgperspective/nmv-vars-monitor.h b/src/persp/dbgperspective/nmv-vars-monitor.h
new file mode 100644
index 0000000..143b516
--- /dev/null
+++ b/src/persp/dbgperspective/nmv-vars-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_VARS_MONITOR_H__
+#define __NMV_VARS_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 VarsMonitor : public nemiver::common::Object {
+ // Non copyable
+ VarsMonitor (const VarsMonitor&);
+ VarsMonitor& operator= (const VarsMonitor&);
+
+ struct Priv;
+ SafePtr<Priv> m_priv;
+
+ protected:
+ VarsMonitor ();
+
+ public:
+ VarsMonitor (IDebugger &a_dbg,
+ IPerspective &a_perspective);
+ virtual ~VarsMonitor ();
+ Gtk::Widget& widget ();
+ void add_variable (const IDebugger::VariableSafePtr a_var);
+ void add_variables (const IDebugger::VariableList &a_vars);
+ bool variable_monitored (const IDebugger::Variable &a_var) const;
+ void remove_variable (const IDebugger::VariableSafePtr a_var);
+ void remove_variables (const IDebugger::VariableList &a_vars);
+ void re_init_widget ();
+};// end VarsMonitor
+
+NEMIVER_END_NAMESPACE (nemiver)
+
+#endif // __NMV_VARS_MONITOR_H__
diff --git a/src/persp/dbgperspective/ui/varinspectordialog.ui b/src/persp/dbgperspective/ui/varinspectordialog.ui
index 5b6bf75..4fab8c4 100644
--- a/src/persp/dbgperspective/ui/varinspectordialog.ui
+++ b/src/persp/dbgperspective/ui/varinspectordialog.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>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]