[nemiver/monitor-variable: 1/5] Initial import of variable monitor work



commit d6c62b2136bcddc184f9f34a62b4e891962a07ae
Author: Dodji Seketeli <dodji seketeli org>
Date:   Sun Oct 23 18:53:45 2011 +0200

    Initial import of variable monitor work
    
    This is Work In Progress.  On the basics work.  One can add a variable to
    the monitor from the expression inspector widget, and the monitored
    variable gets updated at each stop.  It's very rough as no
    check is performed to make sure you can't add the same expression to the
    monitor twice, etc.
    
    Only a tiny part of the commit has been ChangeLoged:
    
    	* src/dbgengine/nmv-i-debugger.h (Variable::id): New method.
    	* src/persp/dbgperspective/nmv-variables-utils.cc
    	(update_a_variable_node, ): Add logging.
    	(find_a_variable): Likewise.  Remove dead code.
    	(unlink_a_variable_row): Add logging.  Add const to last
    	argument.

 src/dbgengine/nmv-i-debugger.h                     |   49 ++-
 src/persp/dbgperspective/Makefile.am               |    2 +
 src/persp/dbgperspective/menus/menus.xml           |    2 +
 .../nmv-dbg-perspective-default-layout.cc          |    3 +-
 src/persp/dbgperspective/nmv-dbg-perspective.cc    |   74 +++-
 src/persp/dbgperspective/nmv-dbg-perspective.h     |    3 +-
 .../dbgperspective/nmv-var-inspector-dialog.cc     |   31 +-
 .../dbgperspective/nmv-var-inspector-dialog.h      |    4 +
 src/persp/dbgperspective/nmv-variables-utils.cc    |   49 ++-
 src/persp/dbgperspective/nmv-variables-utils.h     |    4 +
 src/persp/dbgperspective/nmv-vars-monitor.cc       |  590 ++++++++++++++++++++
 src/persp/dbgperspective/nmv-vars-monitor.h        |   67 +++
 src/persp/dbgperspective/ui/varinspectordialog.ui  |   17 +-
 13 files changed, 875 insertions(+), 20 deletions(-)
---
diff --git a/src/dbgengine/nmv-i-debugger.h b/src/dbgengine/nmv-i-debugger.h
index 29480d9..b0a32fa 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 {
@@ -621,6 +621,7 @@ public:
         /// (e.g. variable object in GDB).
         /// \param a_in the new name of backend side counterpart variable object.
         void internal_name (const UString &a_in) {m_internal_name = a_in;}
+        const UString& id () const { return internal_name ().empty () ? name () : internal_name ();}
 
 
         IDebugger* debugger () const {return m_debugger;}
@@ -656,6 +657,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
         {
@@ -1183,6 +1206,12 @@ public:
     virtual sigc::signal<void, const VariableSafePtr, const UString&>&
                                  variable_expression_evaluated_signal () const = 0;
 
+    /// This is a callback slot called upon completion of the
+    /// IDebugger::list_changed_variables entry point.
+    ///
+    /// The parameters of are the slots are: the list of variables
+    /// that changed, and the cookie passed to
+    /// IDebugger::list_changed_variables.
     virtual sigc::signal<void, const VariableList&, const UString&>&
                                 changed_variables_signal () const  = 0;
 
@@ -1565,8 +1594,26 @@ public:
              const ConstVariableSlot &a_slot,
              const UString &a_cookie = "")= 0;
 
+    /// List the sub-variables of a_root (included a_root) which value
+    /// changed since the last time this function was called.
+    ///
+    /// \param a_root the variable to consider
+    /// \a_cookie the cookie to be passed to the callback function
+    /// IDebugger::changed_variables_signal
     virtual void list_changed_variables (VariableSafePtr a_root,
                                          const UString &a_cookie = "") = 0;
+
+    /// List the sub-variables of a_root (included a_root) which value
+    /// changed since the last time this function was called.
+    ///
+    /// \param a_root the variable to consider
+    /// 
+    /// \param a_slot the slot to be invoked upon completion of this
+    /// function.  That slot is going to be passed the list of
+    /// sub-variables that have changed.
+    /// 
+    /// \a_cookie the cookie to be passed to the callback function
+    /// IDebugger::changed_variables_signal
     virtual void list_changed_variables
             (VariableSafePtr a_root,
              const ConstVariableListSlot &a_slot,
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/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/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 2560498..e817ed4 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 ();
 
 }
@@ -7845,6 +7895,9 @@ DBGPerspective::inspect_variable (const UString &a_variable_name)
                                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);
     }
@@ -8146,6 +8199,17 @@ DBGPerspective::get_memory_view ()
 }
 #endif // WITH_MEMORYVIEW
 
+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 e7994e3..6959f1d 100644
--- a/src/persp/dbgperspective/nmv-var-inspector-dialog.cc
+++ b/src/persp/dbgperspective/nmv-var-inspector-dialog.cc
@@ -51,11 +51,13 @@ 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;
     IDebuggerSafePtr debugger;
     IPerspective &perspective;
+    sigc::signal<void, IDebugger::VariableSafePtr> expr_monitoring_requested;
 
     Priv ();
 public:
@@ -94,6 +96,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");
@@ -116,6 +123,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
@@ -266,8 +275,10 @@ public:
         UString var_name = var_name_entry->get_entry ()->get_text ();
         if (var_name == "") {
             inspect_button->set_sensitive (false);
+            add_to_monitor_button->set_sensitive (false);
         } else {
             inspect_button->set_sensitive (true);
+            add_to_monitor_button->set_sensitive (true);
         }
 
         // this handler is called when any text is changed in the entry or when
@@ -281,6 +292,18 @@ public:
         NEMIVER_CATCH
     }
 
+    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
+    }
+
     //************************
     //</signal handlers>
     //*************************
@@ -352,5 +375,11 @@ 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;
+}
 
+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 d02928f..383bca9 100644
--- a/src/persp/dbgperspective/nmv-var-inspector-dialog.h
+++ b/src/persp/dbgperspective/nmv-var-inspector-dialog.h
@@ -53,6 +53,10 @@ public:
     const IDebugger::VariableSafePtr variable () const;
     void set_history (const std::list<UString> &);
     void get_history (std::list<UString> &) const;
+    
+    // Signals
+
+    sigc::signal<void, IDebugger::VariableSafePtr>& expr_monitoring_requested ();
 };//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 0038a8d..c747bc9 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");
@@ -253,6 +253,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) {
@@ -261,11 +262,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));
@@ -462,6 +461,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;
@@ -636,7 +637,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);
 
@@ -693,10 +694,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;
     }
@@ -716,12 +713,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.
 ///
@@ -777,7 +804,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..06eb469
--- /dev/null
+++ b/src/persp/dbgperspective/nmv-vars-monitor.cc
@@ -0,0 +1,590 @@
+//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"
+
+using namespace nemiver::common;
+namespace vutils = nemiver::variables_utils2;
+
+NEMIVER_BEGIN_NAMESPACE (nemiver)
+
+struct VarsMonitor::Priv
+{
+    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;
+    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),
+          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;
+    }
+
+    void
+    re_init_widget ()
+    {
+        LOG_FUNCTION_SCOPE_NORMAL_DD;
+    }
+
+    void
+    connect_to_debugger_signal ()
+    {
+        LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+        debugger.stopped_signal ().connect
+            (sigc::mem_fun (*this, &Priv::on_stopped_signal));
+    }
+
+    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));
+    }
+
+    void
+    init_actions ()
+    {
+    }
+
+    void
+    add_variable (const IDebugger::VariableSafePtr a_var)
+    {
+        LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+        LOG_DD ("a_var: " << a_var->id ());
+
+        if (!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);
+    }
+
+    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);
+    }
+
+    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;
+            }
+        }
+    }
+
+    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);
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    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);
+        }
+    }
+
+    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;
+        }
+    }
+
+    void
+    clear_vars_changed_at_prev_step ()
+    {
+        changed_in_scope_vars_at_prev_stop.clear ();
+        changed_oo_scope_vars_at_prev_stop.clear ();
+    }
+
+    void
+    update_vars_changed_at_prev_step ()
+    {
+
+        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 ();
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    // *********************
+    // <signal handlers>
+    // ********************
+
+    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;
+    }
+
+    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) {
+            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;
+    }
+
+  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;
+  }
+
+    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;
+    }
+
+    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;
+    }
+
+    // *********************
+    // </signal handlers>
+    // ********************
+
+}; // end struct VarsMonitor
+
+VarsMonitor::VarsMonitor (IDebugger &a_dbg,
+                          IPerspective &a_perspective)
+{
+    m_priv.reset (new Priv (a_dbg, a_perspective));
+}
+
+VarsMonitor::~VarsMonitor ()
+{
+}
+
+Gtk::Widget&
+VarsMonitor::widget ()
+{
+    THROW_IF_FAIL (m_priv);
+    return m_priv->get_widget ();
+}
+
+void
+VarsMonitor::add_variable (const IDebugger::VariableSafePtr a_var)
+{
+    m_priv->add_variable (a_var);
+}
+
+void
+VarsMonitor::add_variables (const IDebugger::VariableList &a_vars)
+{
+    m_priv->add_variables (a_vars);
+}
+
+void
+VarsMonitor::remove_variable (const IDebugger::VariableSafePtr a_var)
+{
+    m_priv->remove_variable (a_var);
+}
+
+void
+VarsMonitor::remove_variables (const std::list<IDebugger::VariableSafePtr> &a_vars)
+{
+    m_priv->remove_variables (a_vars);
+}
+
+void
+VarsMonitor::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..44f1962
--- /dev/null
+++ b/src/persp/dbgperspective/nmv-vars-monitor.h
@@ -0,0 +1,67 @@
+//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);
+    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]