[nemiver/monitor-variable: 6/6] Contextual menu to add/remove expression to the monitor



commit 8f4571137512a3eafade920df2f92ab371d31029
Author: Dodji Seketeli <dodji seketeli org>
Date:   Mon May 28 20:13:40 2012 +0200

    Contextual menu to add/remove expression to the monitor
    
    	* src/persp/dbgperspective/menus/Makefile.am: Add
    	varsmonitorpopup.xml to the build system.
    	* src/persp/dbgperspective/menus/varsmonitorpopup.xml: New
    	description file for the contextual menu.
    	* src/persp/dbgperspective/nmv-vars-monitor.{cc,h}: Lots of new
    	stuff.

 src/persp/dbgperspective/menus/Makefile.am         |    2 +-
 .../dbgperspective/menus/varsmonitorpopup.xml      |    9 +
 src/persp/dbgperspective/nmv-vars-monitor.cc       |  348 +++++++++++++++++++-
 src/persp/dbgperspective/nmv-vars-monitor.h        |    1 +
 4 files changed, 358 insertions(+), 2 deletions(-)
---
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/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-vars-monitor.cc b/src/persp/dbgperspective/nmv-vars-monitor.cc
index d06e524..9018e06 100644
--- a/src/persp/dbgperspective/nmv-vars-monitor.cc
+++ b/src/persp/dbgperspective/nmv-vars-monitor.cc
@@ -30,6 +30,8 @@
 #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;
@@ -41,6 +43,7 @@ NEMIVER_BEGIN_NAMESPACE (nemiver)
 
 struct VarsMonitor::Priv
 {
+    Glib::RefPtr<Gtk::UIManager> ui_manager;
     IDebugger &debugger;
     IPerspective &perspective;
     SafePtr<VarsTreeView> tree_view;
@@ -52,6 +55,9 @@ struct VarsMonitor::Priv
     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;
@@ -63,6 +69,7 @@ struct VarsMonitor::Priv
           IPerspective &a_perspective)
         : debugger (a_debugger),
           perspective (a_perspective),
+          contextual_menu (0),
           saved_reason (IDebugger::UNDEFINED_REASON),
           saved_has_frame (false),
           initialized (false),
@@ -134,12 +141,14 @@ struct VarsMonitor::Priv
         initialized = true;
     }
 
+    /// Re-initialize the widget.
     void
     re_init_widget ()
     {
         LOG_FUNCTION_SCOPE_NORMAL_DD;
     }
 
+    /// Connect to the graphical
     void
     connect_to_debugger_signal ()
     {
@@ -149,6 +158,7 @@ struct VarsMonitor::Priv
             (sigc::mem_fun (*this, &Priv::on_stopped_signal));
     }
 
+    /// Connect slot to signals related to graphical stuff.
     void
     init_graphical_signals ()
     {
@@ -159,13 +169,96 @@ struct VarsMonitor::Priv
 
         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)
     {
@@ -173,7 +266,7 @@ struct VarsMonitor::Priv
 
         LOG_DD ("a_var: " << a_var->id ());
 
-        if (!a_var)
+        if (!a_var || variable_monitored (*a_var))
             return;
 
         monitored_variables.push_back (a_var);
@@ -189,6 +282,9 @@ struct VarsMonitor::Priv
                                    /*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)
     {
@@ -199,6 +295,9 @@ struct VarsMonitor::Priv
             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)
     {
@@ -409,6 +508,100 @@ struct VarsMonitor::Priv
         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>
     // ********************
@@ -541,6 +734,134 @@ struct VarsMonitor::Priv
         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>
     // ********************
@@ -557,6 +878,7 @@ VarsMonitor::~VarsMonitor ()
 {
 }
 
+/// Return the widget for this type.
 Gtk::Widget&
 VarsMonitor::widget ()
 {
@@ -564,33 +886,57 @@ VarsMonitor::widget ()
     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
index 44f1962..143b516 100644
--- a/src/persp/dbgperspective/nmv-vars-monitor.h
+++ b/src/persp/dbgperspective/nmv-vars-monitor.h
@@ -57,6 +57,7 @@ class NEMIVER_API VarsMonitor : public nemiver::common::Object {
     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 ();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]