[nemiver/profiler: 1/16] Multiple perspectives support



commit 04519a235a4a80858f465dd7033dc29bccf30427
Author: Fabien Parent <parent f gmail com>
Date:   Mon May 21 21:42:34 2012 +0200

    Multiple perspectives support
    
        * src/main.c
        (init_option_context): Add the option context of every loaded
        perspectives
        (parse_command_line): Move the code that process the debugger --remote and
        --load-core options to the dbgperspective
        Retrieve the perspective from the --tool option and make the perspective
        process its options
        (process_non_gui_options): Move the option to activate the debugger log
        to the dbgperspective
        (load_debugger_perspective): Removed
        (process_gui_options): Moved to dbgperspective
        (main): Load the perspectives, then start parsing and processing the
        command line
        * src/persp/dbgperspective/nmv-dbg-perspective.cc
        (DBGPerspective::Priv::Priv): Create dbgperspective's option group
        (DBGPerspective::add_stock_icon): Add missing pointer check
        (DBGPerspective::add_perspective_menu_entries): Save the memory merge id
        (DBGPerspective::edit_workbench_menu): Returns the merge ids
        (DBGPerspective::option_group): New API
        (DBGPerspective::name): New API
        (DBGPerspective::process_options): Moved from main
        (DBGPerspective::process_gui_options): Moved from main
        * src/persp/dbgperspective/nmv-dbg-perspective.h
        (DBGPerspective::edit_workbench_menu): Returns the merge ids
        (DBGPerspective::name): New API
        * src/persp/nmv-i-perspective.h
        (IPerspective::name): New API
        (IPerspective::process_options): Moved from main
        (IPerspective::process_gui_options): Moved from main
        (IPerspective::option_group): New API
        (IPerspective::edit_workbench_menu): Returns the merge ids
        (IPerspective::open_file): Removed
        (IPerspective::close_current_file): Removed
        (IPerspective::close_file): Removed
        * src/workbench/nmv-i-workbench.h
        (IWorkbench::perspectives): New API
        (IWorkbench::get_perspective): Returns SafePtr instead of raw pointer
        (IWorkbench::select_perspective): New API
        (IWorkbench::load_perspectives): New API
        * src/workbench/nmv-workbench.cc
        (Workbench::on_perspective_changed): New API
        (Workbench::load_perspectives): New API
        (Workbench::do_init): Don't load the perspectives, just init them
        (Workbench::perspectives): New API
        (Workbench::get_perspective): Returns a SafePtr instead of a raw pointer
        (Workbench::init_toolbar): Initialize the perspective selector combobox
        (Workbench::add_perspective_to_perspective_selector): New API
        (Workbench::add_perspective_body): Remove the previous merge ids before
        editing the workbench menu

 src/main.cc                                     |  501 +++--------------------
 src/persp/dbgperspective/nmv-dbg-perspective.cc |  450 ++++++++++++++++++++-
 src/persp/dbgperspective/nmv-dbg-perspective.h  |    4 +-
 src/persp/nmv-i-perspective.h                   |   31 +-
 src/workbench/nmv-i-workbench.h                 |    9 +-
 src/workbench/nmv-workbench.cc                  |  202 +++++++---
 ui/workbench.ui                                 |   71 +++-
 7 files changed, 720 insertions(+), 548 deletions(-)
---
diff --git a/src/main.cc b/src/main.cc
index 2ce9320..e55481e 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -36,7 +36,7 @@
 #include "nmv-ui-utils.h"
 #include "nmv-proc-mgr.h"
 #include "nmv-env.h"
-#include "nmv-dbg-perspective.h"
+#include "nmv-i-perspective.h"
 #include "nmv-i-conf-mgr.h"
 
 using namespace std;
@@ -45,88 +45,25 @@ using nemiver::common::DynamicModuleManager;
 using nemiver::common::Initializer;
 using nemiver::IWorkbench;
 using nemiver::IWorkbenchSafePtr;
-using nemiver::IDBGPerspective;
+using nemiver::IPerspective;
+using nemiver::IPerspectiveSafePtr;
 using nemiver::common::UString;
 using nemiver::common::GCharSafePtr;
-using nemiver::ISessMgr;
-
-static const UString DBGPERSPECTIVE_PLUGIN_NAME = "dbgperspective";
-static gchar *gv_env_vars = 0;
-static gchar *gv_process_to_attach_to = 0;
-static bool gv_list_sessions = false;
-static bool gv_purge_sessions = false;
-static int gv_execute_session = 0;
-static bool gv_last_session = false;
+
 static gchar *gv_log_domains=0;
-static bool gv_log_debugger_output = false;
 static bool gv_show_version = false;
-static bool gv_use_launch_terminal = false;
-static gchar *gv_remote = 0;
-static gchar *gv_solib_prefix = 0;
-static gchar *gv_gdb_binary_filepath = 0;
-static gchar *gv_core_path = 0;
-static bool gv_just_load = false;
+static gchar *gv_tool = (gchar*) "dbgperspective";
 
 static GOptionEntry entries[] =
 {
     {
-        "env",
-        0,
-        0,
-        G_OPTION_ARG_STRING,
-        &gv_env_vars,
-        _("Set the environment of the program to debug"),
-        "<\"var0=val0 var1=val1 var2=val2 ...\">"
-    },
-    {
-        "attach",
+        "tool",
         0,
         0,
         G_OPTION_ARG_STRING,
-        &gv_process_to_attach_to,
-        _("Attach to a process"),
-        "<pid|process name>"
-    },
-    {
-        "load-core",
-        0,
-        0,
-        G_OPTION_ARG_STRING,
-        &gv_core_path,
-        _("Load a core file"),
-        "</path/to/core/file>"
-    },
-    { "list-sessions",
-      0,
-      0,
-      G_OPTION_ARG_NONE,
-      &gv_list_sessions,
-      _("List the saved debugging sessions"),
-      0
-    },
-    { "purge-sessions",
-      0,
-      0,
-      G_OPTION_ARG_NONE,
-      &gv_purge_sessions,
-      _("Erase the saved debugging sessions"),
-      0
-    },
-    { "session",
-      0,
-      0,
-      G_OPTION_ARG_INT,
-      &gv_execute_session,
-      _("Debug the program that was of session number N"),
-      "<N>"
-    },
-    { "last",
-      0,
-      0,
-      G_OPTION_ARG_NONE,
-      &gv_last_session,
-      _("Execute the last session"),
-      0
+        &gv_tool,
+        _("Use the nemiver tool named <name>"),
+        "<name>"
     },
     { "log-domains",
       0,
@@ -136,59 +73,6 @@ static GOptionEntry entries[] =
       _("Enable logging domains DOMAINS"),
       "<DOMAINS>"
     },
-    { "log-debugger-output",
-      0,
-      0,
-      G_OPTION_ARG_NONE,
-      &gv_log_debugger_output,
-      _("Log the debugger output"),
-      0
-    },
-    { "use-launch-terminal",
-      0,
-      0,
-      G_OPTION_ARG_NONE,
-      &gv_use_launch_terminal,
-      _("Use this terminal as the debugee's terminal"),
-      0
-    },
-    {
-        "remote",
-        0,
-        0,
-        G_OPTION_ARG_STRING,
-        &gv_remote,
-        _("Connect to remote target specified by HOST:PORT"),
-        "<HOST:PORT|serial-line-path>"
-    },
-    {
-        "solib-prefix",
-        0,
-        0,
-        G_OPTION_ARG_STRING,
-        &gv_solib_prefix,
-        _("Where to look for shared libraries loaded by the inferior. "
-          "Use in conjunction with --remote"),
-        "</path/to/prefix>"
-    },
-    {
-        "gdb-binary",
-        0,
-        0,
-        G_OPTION_ARG_STRING,
-        &gv_gdb_binary_filepath,
-        _("Set the path of the GDB binary to use to debug the inferior"),
-        "</path/to/gdb>"
-    },
-    {
-        "just-load",
-        0,
-        0,
-        G_OPTION_ARG_NONE,
-        &gv_just_load,
-        _("Do not set a breakpoint in 'main' and do not run the inferior either"),
-        0
-    },
     { 
         "version",
         0,
@@ -275,6 +159,18 @@ init_option_context ()
     THROW_IF_FAIL (gtk_option_group);
     g_option_context_add_group (context.get (),
                                 gtk_option_group.release ());
+
+    std::list<IPerspectiveSafePtr>::iterator perspective;
+    std::list<IPerspectiveSafePtr> perspectives = s_workbench->perspectives ();
+    for (perspective = perspectives.begin ();
+         perspective != perspectives.end ();
+         ++perspective) {
+        if (*perspective && (*perspective)->option_group ()) {
+            g_option_context_add_group
+                (context.get (), (*perspective)->option_group ());
+        }
+    }
+
     return context.release ();
 }
 
@@ -345,51 +241,21 @@ parse_command_line (int& a_argc,
         return false;
     }
 
-    if (a_argv != inf_argv) {
-        memmove (a_argv, inf_argv, inf_argc * sizeof (char*));
-        a_argc = inf_argc;
+    IPerspectiveSafePtr perspective = s_workbench->get_perspective (gv_tool);
+    if (!perspective) {
+        std::cerr << "Invalid tool name: " << gv_tool << std::endl;
+        return false;
     }
 
-    //***************************************************************
-    // Here goes some sanity checking on the command line parsed so far.
-    //****************************************************************
-
-    // If the user wants to debug a binary running on a remote target,
-    // make sure she provides us with a local copy of the binary too.
-    if (gv_remote) {
-        if (a_argc < 1 || a_argv[0][0] == '-') {
-            cerr << _("Please provide a local copy of the binary "
-                      "you intend to debug remotely.\n"
-                      "Like this:\n")
-                 << "nemiver --remote=" << gv_remote
-                 << " <binary-copy>\n\n";
-            cerr << _("Otherwise, find below the full set of Nemiver options.\n");
-            GCharSafePtr help_message;
-            help_message.reset (g_option_context_get_help (context.get (),
-                                                           true, 0));
-            cerr << help_message.get () << std::endl;
-            return false;
-        }
+    if (!perspective->process_options  (context.get (), inf_argc, inf_argv)) {
+        return false;
     }
 
-    // If the user wants to analyse a core dump, make sure she
-    // provides us with a path to the binary that generated the core
-    // dump too.
-    if (gv_core_path) {
-        if (a_argc < 1 || a_argv[0][0] == '-') {
-            cerr << _("Please provide the path to the binary "
-                      "that generated the core file too.\n"
-                      "Like this:\n")
-                 << "nemiver --load-core=\""
-                 << gv_core_path << "\" </path/to/binary>\n\n";
-            cerr << _("Otherwise, find below the full set of Nemiver options.\n");
-            GCharSafePtr help_message;
-            help_message.reset (g_option_context_get_help (context.get (),
-                                                           true, 0));
-            cerr << help_message.get () << std::endl;
-            return false;
-        }
+    if (a_argv != inf_argv) {
+        memmove (a_argv, inf_argv, inf_argc * sizeof (char*));
+        a_argc = inf_argc;
     }
+
     return true;
 }
 
@@ -398,10 +264,6 @@ parse_command_line (int& a_argc,
 static bool
 process_non_gui_options ()
 {
-    if (gv_log_debugger_output) {
-        LOG_STREAM.enable_domain ("gdbmi-output-domain");
-    }
-
     if (gv_show_version) {
         cout << PACKAGE_VERSION << endl;
         return false;
@@ -416,258 +278,7 @@ process_non_gui_options ()
             LOG_STREAM.enable_domain (*iter);
         }
     }
-    return true;
-}
-
-/// Load the debugger perspective.
-static IDBGPerspective*
-load_debugger_perspective ()
-{
-    return dynamic_cast<IDBGPerspective*> (s_workbench->get_perspective
-                                           (DBGPERSPECTIVE_PLUGIN_NAME));
-}
-
-/// Return true if Nemiver should keep going after the GUI option(s)
-/// have been processed.
-static bool
-process_gui_options (int& a_argc, char** a_argv)
-{
-    if (gv_purge_sessions) {
-        IDBGPerspective *debug_persp = load_debugger_perspective ();
-        if (debug_persp) {
-            debug_persp->session_manager ().delete_sessions ();
-        }
-        return false;
-    }
-
-    if (gv_process_to_attach_to) {
-        using nemiver::common::IProcMgrSafePtr;
-        using nemiver::common::IProcMgr;
-        IDBGPerspective *debug_persp = load_debugger_perspective ();
-        if (!debug_persp) {
-            cerr << "Could not get the debugging perspective" << endl;
-            return false;
-        }
-        int pid = atoi (gv_process_to_attach_to);
-        if (!pid) {
-            IProcMgrSafePtr proc_mgr = IProcMgr::create ();
-            if (!proc_mgr) {
-                cerr << "Could not create proc mgr" << endl;
-                return false;
-            }
-            IProcMgr::Process process;
-            if (!proc_mgr->get_process_from_name (gv_process_to_attach_to,
-                                                  process, true)) {
-                cerr << "Could not find any process named '"
-                << gv_process_to_attach_to
-                << "'"
-                << endl;
-                return false;
-            }
-            pid = process.pid ();
-        }
-        if (!pid) {
-            cerr << "Could not find any process '"
-                 << gv_process_to_attach_to
-                 << "'"
-                 << endl;
-            return false;
-        } else {
-            debug_persp->uses_launch_terminal (gv_use_launch_terminal);
-            debug_persp->attach_to_program (pid);
-        }
-    }
 
-    if (gv_list_sessions) {        
-        IDBGPerspective *debug_persp = load_debugger_perspective ();
-        if (debug_persp) {
-            debug_persp->session_manager ().load_sessions ();
-            list<ISessMgr::Session>::iterator session_iter;
-            list<ISessMgr::Session>& sessions =
-                            debug_persp->session_manager ().sessions ();
-            for (session_iter = sessions.begin ();
-                 session_iter != sessions.end ();
-                 ++session_iter) {
-                cout << session_iter->session_id ()
-                     << " "
-                     << session_iter->properties ()["sessionname"]
-                     << "\n";
-            }
-            return false;
-        } else {
-            cerr << "Could not find the debugger perpective plugin";
-            return false;
-        }
-    }
-
-    if (gv_execute_session) {
-        IDBGPerspective *debug_persp = load_debugger_perspective ();
-        if (debug_persp) {
-            debug_persp->session_manager ().load_sessions ();
-            list<ISessMgr::Session>::iterator session_iter;
-            list<ISessMgr::Session>& sessions =
-                            debug_persp->session_manager ().sessions ();
-            bool found_session=false;
-            debug_persp->uses_launch_terminal (gv_use_launch_terminal);
-            for (session_iter = sessions.begin ();
-                 session_iter != sessions.end ();
-                 ++session_iter) {
-                if (session_iter->session_id () == gv_execute_session) {
-                    debug_persp->execute_session (*session_iter);
-                    found_session = true;
-                    break;
-                }
-            }
-
-            if (!found_session) {
-                cerr << "Could not find session of number "
-                     << gv_execute_session
-                     << "\n";
-                return false;
-            }
-            return true;
-        } else {
-            cerr << "Could not find the debugger perpective plugin";
-            return false;
-        }
-    }
-
-    //execute the last session if one exists
-    if (gv_last_session) {
-        IDBGPerspective *debug_persp = load_debugger_perspective ();
-        if (debug_persp) {
-            debug_persp->session_manager ().load_sessions ();
-            list<ISessMgr::Session>& sessions =
-                            debug_persp->session_manager ().sessions ();
-            if (!sessions.empty ()) {
-                debug_persp->uses_launch_terminal (gv_use_launch_terminal);
-                list<ISessMgr::Session>::iterator session_iter,
-                    latest_session_iter;
-                glong time_val = 0;
-                for (session_iter = sessions.begin ();
-                        session_iter != sessions.end ();
-                        ++session_iter) {
-                    std::map<UString, UString>::const_iterator map_iter =
-                                session_iter->properties ().find ("lastruntime");
-                    if (map_iter != session_iter->properties ().end ()) {
-                        glong new_time = atoi (map_iter->second.c_str ());
-                        if (new_time > time_val) {
-                            time_val = new_time;
-                            latest_session_iter = session_iter;
-                        }
-                    }
-                }
-                debug_persp->execute_session (*latest_session_iter);
-            } else {
-                cerr << "Could not find any sessions"
-                     << "\n";
-                return false;
-            }
-            return true;
-        } else {
-            cerr << "Could not find the debugger perpective plugin";
-            return false;
-        }
-    }
-
-    // Load and analyse a core file
-    if (gv_core_path) {
-        IDBGPerspective *debug_persp = load_debugger_perspective ();
-        if (debug_persp == 0) {
-            cerr << "Could not find the debugger perpective plugin";
-            return false;
-        }
-        UString prog_path = a_argv[0];
-        THROW_IF_FAIL (!prog_path.empty ());
-        debug_persp->load_core_file (prog_path, gv_core_path);
-        return true;
-    }
-
-    vector<UString> prog_args;
-    UString prog_path;
-    // Here, a_argc is the argument count of the inferior program.
-    // It's zero if there is there is no inferior program.
-    // Otherwise it equals the number of arguments to the inferior program + 1
-    if (a_argc > 0)
-        prog_path = a_argv[0];
-    for (int i = 1; i < a_argc; ++i) {
-        prog_args.push_back (Glib::locale_to_utf8 (a_argv[i]));
-    }
-    IDBGPerspective *debug_persp =
-        dynamic_cast<IDBGPerspective*> (s_workbench->get_perspective
-                                            (DBGPERSPECTIVE_PLUGIN_NAME));
-    if (debug_persp) {
-        if (gv_gdb_binary_filepath) {
-            char *debugger_full_path = realpath (gv_gdb_binary_filepath, 0);
-            if (debugger_full_path) {
-                nemiver::IDebuggerSafePtr debugger = debug_persp->debugger ();
-                if (!debugger) {
-                    cerr << "Could not get the debugger instance" << endl;
-                    return false;
-                }
-                debugger->set_non_persistent_debugger_path (debugger_full_path);
-                free (debugger_full_path);
-            } else {
-                LOG_ERROR ("Could not resolve the full path of the debugger");
-            }
-        }
-
-        map<UString, UString> env;
-        if (gv_env_vars) {
-            vector<UString> env_vars =
-                UString (Glib::locale_to_utf8 (gv_env_vars)).split (" ");
-            for (vector<UString>::const_iterator it = env_vars.begin ();
-                 it != env_vars.end ();
-                 ++it) {
-                vector<UString> env_var = it->split ("=");
-                if (env_var.size () != 2) {
-                    continue;
-                }
-                UString name = env_var[0];
-                name.chomp ();
-                UString value = env_var[1];
-                value.chomp ();
-                LOG_DD ("got env var: " << name << "=" << value);
-                env[name] = value;
-            }
-        }
-	if (gv_remote) {
-            // The user asked to connect to a remote target.
-            std::string host;
-            unsigned port = 0;
-            std::string solib_prefix;
-
-            if (gv_solib_prefix)
-                solib_prefix = gv_solib_prefix;
-
-            if (nemiver::str_utils::parse_host_and_port (gv_remote,
-                                                         host, port)) {
-                // So it looks like gv_remote has the form
-                // "host:port", so let's try to connect to that.
-                debug_persp->connect_to_remote_target (host, port,
-                                                       prog_path,
-                                                       solib_prefix);
-            } else {
-                // We think gv_remote contains a serial line address.
-                // Let's try to connect via the serial line then.
-                debug_persp->connect_to_remote_target (gv_remote,
-                                                       prog_path,
-                                                       solib_prefix);
-            }
-	} else if (!prog_path.empty ()) {
-            // The user wants to debug a local program
-            debug_persp->uses_launch_terminal (gv_use_launch_terminal);
-            debug_persp->execute_program (prog_path,
-                                          prog_args,
-                                          env,
-                                          /*a_cwd=*/".",
-                                          /*a_clone_opened_files=*/false,
-                                          /*a_break_in_main_run=*/!gv_just_load);
-        }
-    } else {
-        cerr << "Could not find the debugger perspective plugin\n";
-        return false;
-    }
     return true;
 }
 
@@ -683,13 +294,6 @@ main (int a_argc, char *a_argv[])
     Initializer::do_init ();
     Gtk::Main gtk_kit (a_argc, a_argv);
 
-    if (parse_command_line (a_argc, a_argv) == false)
-        return -1;
-
-    if (process_non_gui_options () != true) {
-        return -1;
-    }
-
     //********************************************
     //load and init the workbench dynamic module
     //********************************************
@@ -700,43 +304,34 @@ main (int a_argc, char *a_argv[])
     THROW_IF_FAIL (s_workbench);
     LOG_D ("workbench refcount: " <<  (int) s_workbench->get_refcount (),
            "refcount-domain");
+    s_workbench->load_perspectives ();
+
+    if (!parse_command_line (a_argc, a_argv))
+        return -1;
+
+    if (!process_non_gui_options ()) {
+        return -1;
+    }
 
     s_workbench->do_init (gtk_kit);
     LOG_D ("workbench refcount: " <<  (int) s_workbench->get_refcount (),
            "refcount-domain");
 
-    if (process_gui_options (a_argc, a_argv) != true) {
+    IPerspectiveSafePtr perspective = s_workbench->get_perspective (gv_tool);
+    if (!perspective) {
+        std::cerr << "Invalid tool name: " << gv_tool << std::endl;
+        return -1;
+    }
+
+    if (!perspective->process_gui_options  (a_argc, a_argv)) {
         return -1;
     }
 
+    s_workbench->select_perspective (perspective);
+
     //intercept ctrl-c/SIGINT
     signal (SIGINT, sigint_handler);
 
-    if (gv_use_launch_terminal) {
-        // So the user wants the inferior to use the terminal Nemiver was
-        // launched from, for input/output.
-        //
-        // Letting the inferior use the terminal from which Nemiver was
-        // launched from implies calling the "set inferior-tty" GDB command,
-        // when we are using the GDB backend.
-        // That command is implemented using the TIOCSCTTY ioctl. It sets the
-        // terminal as the controlling terminal of the inferior process. But that
-        // ioctl requires (among other things) that the terminal shall _not_ be
-        // the controlling terminal of any other process in another process
-        // session already. Otherwise, GDB spits the error:
-        // "GDB: Failed to set controlling terminal: operation not
-        // permitted"
-        // The problem is, Nemiver itself uses that terminal as a
-        // controlling terminal. So Nemiver itself must relinquish its
-        // controlling terminal to avoid letting the ioctl fail.
-        // We do so by calling the setsid function below.
-        // The problem is that setsid will fail with EPERM if Nemiver is
-        // started as a process group leader already.
-        // Trying ioctl (tty_fd, TIOCNOTTY, 0) does not seem to properly
-        // disconnect Nemiver from its terminal either. Sigh.
-        setsid ();
-    }
-
     gtk_kit.run (s_workbench->get_root_window ());
 
     NEMIVER_CATCH_NOX
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.cc b/src/persp/dbgperspective/nmv-dbg-perspective.cc
index b7ad484..cd503b5 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.cc
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.cc
@@ -147,6 +147,138 @@ const Gtk::StockID STOCK_STEP_OUT (STEP_OUT);
 
 const char *PROG_ARG_SEPARATOR = "#DUMMY-SEP007#";
 
+static gchar *gv_env_vars = 0;
+static gchar *gv_process_to_attach_to = 0;
+static bool gv_list_sessions = false;
+static bool gv_purge_sessions = false;
+static int gv_execute_session = 0;
+static bool gv_last_session = false;
+static bool gv_log_debugger_output = false;
+static bool gv_use_launch_terminal = false;
+static gchar *gv_remote = 0;
+static gchar *gv_solib_prefix = 0;
+static gchar *gv_gdb_binary_filepath = 0;
+static gchar *gv_core_path = 0;
+static bool gv_just_load = false;
+
+static const GOptionEntry entries[] =
+{
+    {
+        "env",
+        0,
+        0,
+        G_OPTION_ARG_STRING,
+        &gv_env_vars,
+        _("Set the environment of the program to debug"),
+        "<\"var0=val0 var1=val1 var2=val2 ...\">"
+    },
+    {
+        "attach",
+        0,
+        0,
+        G_OPTION_ARG_STRING,
+        &gv_process_to_attach_to,
+        _("Attach to a process"),
+        "<pid|process name>"
+    },
+    {
+        "load-core",
+        0,
+        0,
+        G_OPTION_ARG_STRING,
+        &gv_core_path,
+        _("Load a core file"),
+        "</path/to/core/file>"
+    },
+    { "list-sessions",
+      0,
+      0,
+      G_OPTION_ARG_NONE,
+      &gv_list_sessions,
+      _("List the saved debugging sessions"),
+      0
+    },
+    { "purge-sessions",
+      0,
+      0,
+      G_OPTION_ARG_NONE,
+      &gv_purge_sessions,
+      _("Erase the saved debugging sessions"),
+      0
+    },
+    { "session",
+      0,
+      0,
+      G_OPTION_ARG_INT,
+      &gv_execute_session,
+      _("Debug the program that was of session number N"),
+      "<N>"
+    },
+    { "last",
+      0,
+      0,
+      G_OPTION_ARG_NONE,
+      &gv_last_session,
+      _("Execute the last session"),
+      0
+    },
+    { "log-debugger-output",
+      0,
+      0,
+      G_OPTION_ARG_NONE,
+      &gv_log_debugger_output,
+      _("Log the debugger output"),
+      0
+    },
+    { "use-launch-terminal",
+      0,
+      0,
+      G_OPTION_ARG_NONE,
+      &gv_use_launch_terminal,
+      _("Use this terminal as the debugee's terminal"),
+      0
+    },
+    {
+        "remote",
+        0,
+        0,
+        G_OPTION_ARG_STRING,
+        &gv_remote,
+        _("Connect to remote target specified by HOST:PORT"),
+        "<HOST:PORT|serial-line-path>"
+    },
+    {
+        "solib-prefix",
+        0,
+        0,
+        G_OPTION_ARG_STRING,
+        &gv_solib_prefix,
+        _("Where to look for shared libraries loaded by the inferior. "
+          "Use in conjunction with --remote"),
+        "</path/to/prefix>"
+    },
+    {
+        "gdb-binary",
+        0,
+        0,
+        G_OPTION_ARG_STRING,
+        &gv_gdb_binary_filepath,
+        _("Set the path of the GDB binary to use to debug the inferior"),
+        "</path/to/gdb>"
+    },
+    {
+        "just-load",
+        0,
+        0,
+        G_OPTION_ARG_NONE,
+        &gv_just_load,
+        _("Do not set a breakpoint in 'main' "
+          "and do not run the inferior either"),
+        0
+    },
+    {0, 0, 0, (GOptionArg) 0, 0, 0, 0}
+};
+
 class DBGPerspective;
 
 static void
@@ -488,6 +620,14 @@ public:
 
     virtual ~DBGPerspective ();
 
+    GOptionGroup* option_group () const;
+
+    bool process_options (GOptionContext *a_context, int a_argc, char **a_argv);
+
+    bool process_gui_options (int a_argc, char **a_argv);
+
+    const UString& name () const;
+
     void do_init (IWorkbench *a_workbench);
 
     const UString& get_perspective_identifier ();
@@ -500,7 +640,7 @@ public:
 
     IWorkbench& get_workbench ();
 
-    void edit_workbench_menu ();
+    std::list<Gtk::UIManager::ui_merge_id> edit_workbench_menu ();
 
     SourceEditor* create_source_editor (Glib::RefPtr<Gsv::Buffer> &a_source_buf,
                                         bool a_asm_view,
@@ -871,7 +1011,8 @@ struct DBGPerspective::Priv {
     SafePtr<Gtk::ScrolledWindow> thread_list_scrolled_win;
     SafePtr<Gtk::HPaned> call_stack_paned;
     SafePtr<Gtk::HPaned> context_paned;
-   
+    GOptionGroup *option_group;
+
     Glib::RefPtr<Gtk::ActionGroup> default_action_group;
     Glib::RefPtr<Gtk::ActionGroup> target_not_started_action_group;
     Glib::RefPtr<Gtk::ActionGroup> inferior_loaded_action_group;
@@ -881,6 +1022,7 @@ struct DBGPerspective::Priv {
     Glib::RefPtr<Gtk::ActionGroup> debugger_busy_action_group;
     Glib::RefPtr<Gtk::UIManager> ui_manager;
     Glib::RefPtr<Gtk::IconFactory> icon_factory;
+    Gtk::UIManager::ui_merge_id memoryview_merge_id;
     Gtk::UIManager::ui_merge_id menubar_merge_id;
     Gtk::UIManager::ui_merge_id toolbar_merge_id;
     Gtk::UIManager::ui_merge_id contextual_menu_merge_id;
@@ -970,6 +1112,8 @@ struct DBGPerspective::Priv {
         reused_session (false),
         debugger_has_just_run (false),
         debugger_engine_alive (false),
+        option_group (0),
+        memoryview_merge_id (0),
         menubar_merge_id (0),
         toolbar_merge_id (0),
         contextual_menu_merge_id(0),
@@ -993,6 +1137,9 @@ struct DBGPerspective::Priv {
         var_popup_tip_x (0),
         var_popup_tip_y (0)
     {
+        option_group = g_option_group_new
+            ("debugger", _("Debugger"), _("Show debugger options"), 0, 0);
+        g_option_group_add_entries (option_group, entries);
     }
 
     Layout&
@@ -2992,6 +3139,8 @@ DBGPerspective::add_stock_icon (const UString &a_stock_id,
 void
 DBGPerspective::add_perspective_menu_entries ()
 {
+    THROW_IF_FAIL (m_priv);
+
     string relative_path = Glib::build_filename ("menus",
                                                  "menus.xml");
     string absolute_path;
@@ -3014,7 +3163,8 @@ DBGPerspective::add_perspective_menu_entries ()
     relative_path = Glib::build_filename ("menus", "memoryview-menu.xml");
     THROW_IF_FAIL (build_absolute_resource_path
                     (Glib::filename_to_utf8 (relative_path), absolute_path));
-    workbench ().get_ui_manager ()->add_ui_from_file
+    m_priv->memoryview_merge_id =
+        workbench ().get_ui_manager ()->add_ui_from_file
                                 (Glib::filename_to_utf8 (absolute_path));
 #endif // WITH_MEMORYVIEW
 }
@@ -5231,12 +5381,16 @@ DBGPerspective::get_workbench ()
     return workbench ();
 }
 
-void
+std::list<Gtk::UIManager::ui_merge_id>
 DBGPerspective::edit_workbench_menu ()
 {
     CHECK_P_INIT;
 
     add_perspective_menu_entries ();
+    std::list<Gtk::UIManager::ui_merge_id> merge_ids;
+    merge_ids.push_back (m_priv->menubar_merge_id);
+    merge_ids.push_back (m_priv->memoryview_merge_id);
+    return merge_ids;
 }
 
 SourceEditor*
@@ -5337,6 +5491,20 @@ DBGPerspective::open_file ()
     bring_source_as_current (*(paths.begin()));
 }
 
+GOptionGroup*
+DBGPerspective::option_group () const
+{
+    THROW_IF_FAIL (m_priv);
+    return m_priv->option_group;
+}
+
+const UString&
+DBGPerspective::name () const
+{
+    static const UString s_name (_("Debugger"));
+    return s_name;
+}
+
 bool
 DBGPerspective::open_file (const UString &a_path, int current_line)
 {
@@ -8028,6 +8196,280 @@ DBGPerspective::debugger ()
     return m_priv->debugger;
 }
 
+bool
+DBGPerspective::process_options (GOptionContext *a_context,
+                                 int a_argc,
+                                 char **a_argv)
+{
+    // If the user wants to debug a binary running on a remote target,
+    // make sure she provides us with a local copy of the binary too.
+    if (gv_remote) {
+        if (a_argc < 1 || a_argv[0][0] == '-') {
+            cerr << _("Please provide a local copy of the binary "
+                      "you intend to debug remotely.\n"
+                      "Like this:\n")
+                 << "nemiver --remote=" << gv_remote
+                 << " <binary-copy>\n\n";
+            cerr << _("Otherwise, find below the full set of Nemiver options."
+                      "\n");
+            GCharSafePtr help_message;
+            help_message.reset (g_option_context_get_help
+                (a_context, true, m_priv->option_group));
+            cerr << help_message.get () << std::endl;
+            return false;
+        }
+    }
+
+    // If the user wants to analyse a core dump, make sure she
+    // provides us with a path to the binary that generated the core
+    // dump too.
+    if (gv_core_path) {
+        if (a_argc < 1 || a_argv[0][0] == '-') {
+            cerr << _("Please provide the path to the binary "
+                      "that generated the core file too.\n"
+                      "Like this:\n")
+                 << "nemiver --load-core=\""
+                 << gv_core_path << "\" </path/to/binary>\n\n";
+            cerr << _("Otherwise, find below the full set of Nemiver options."
+                      "\n");
+            GCharSafePtr help_message;
+            help_message.reset (g_option_context_get_help
+                (a_context, true, m_priv->option_group));
+            cerr << help_message.get () << std::endl;
+            return false;
+        }
+    }
+
+    if (gv_log_debugger_output) {
+        LOG_STREAM.enable_domain ("gdbmi-output-domain");
+    }
+
+    if (gv_purge_sessions) {
+        session_manager ().delete_sessions ();
+    }
+
+    if (gv_list_sessions) {
+        session_manager ().load_sessions ();
+        list<ISessMgr::Session>::iterator session_iter;
+        list<ISessMgr::Session>& sessions = session_manager ().sessions ();
+        for (session_iter = sessions.begin ();
+             session_iter != sessions.end ();
+             ++session_iter) {
+            cout << session_iter->session_id ()
+                 << " "
+                 << session_iter->properties ()["sessionname"]
+                 << "\n";
+        }
+        return false;
+    }
+
+    return true;
+}
+
+bool
+DBGPerspective::process_gui_options (int a_argc, char **a_argv)
+{
+    if (gv_process_to_attach_to) {
+        using nemiver::common::IProcMgrSafePtr;
+        using nemiver::common::IProcMgr;
+        int pid = atoi (gv_process_to_attach_to);
+        if (!pid) {
+            IProcMgrSafePtr proc_mgr = IProcMgr::create ();
+            if (!proc_mgr) {
+                cerr << "Could not create proc mgr" << std::endl;
+                return false;
+            }
+            IProcMgr::Process process;
+            if (!proc_mgr->get_process_from_name (gv_process_to_attach_to,
+                                                  process, true)) {
+                cerr << "Could not find any process named '"
+                << gv_process_to_attach_to
+                << "'"
+                << std::endl;
+                return false;
+            }
+            pid = process.pid ();
+        }
+        if (!pid) {
+            cerr << "Could not find any process '"
+                 << gv_process_to_attach_to
+                 << "'"
+                 << std::endl;
+            return false;
+        } else {
+            uses_launch_terminal (gv_use_launch_terminal);
+            attach_to_program (pid);
+        }
+    }
+
+    if (gv_execute_session) {
+        session_manager ().load_sessions ();
+        list<ISessMgr::Session>::iterator session_iter;
+        list<ISessMgr::Session>& sessions = session_manager ().sessions ();
+        bool found_session = false;
+        uses_launch_terminal (gv_use_launch_terminal);
+        for (session_iter = sessions.begin ();
+             session_iter != sessions.end ();
+             ++session_iter) {
+            if (session_iter->session_id () == gv_execute_session) {
+                execute_session (*session_iter);
+                found_session = true;
+                break;
+            }
+        }
+
+        if (!found_session) {
+            cerr << "Could not find session of number "
+                 << gv_execute_session
+                 << "\n";
+            return false;
+        }
+        return true;
+    }
+
+    //execute the last session if one exists
+    if (gv_last_session) {
+        session_manager ().load_sessions ();
+        list<ISessMgr::Session>& sessions = session_manager ().sessions ();
+        if (!sessions.empty ()) {
+            uses_launch_terminal (gv_use_launch_terminal);
+            list<ISessMgr::Session>::iterator session_iter,
+                latest_session_iter;
+            glong time_val = 0;
+            for (session_iter = sessions.begin ();
+                    session_iter != sessions.end ();
+                    ++session_iter) {
+                std::map<UString, UString>::const_iterator map_iter =
+                    session_iter->properties ().find ("lastruntime");
+                if (map_iter != session_iter->properties ().end ()) {
+                    glong new_time = atoi (map_iter->second.c_str ());
+                    if (new_time > time_val) {
+                        time_val = new_time;
+                        latest_session_iter = session_iter;
+                    }
+                }
+            }
+            execute_session (*latest_session_iter);
+        } else {
+            cerr << "Could not find any sessions"
+                 << "\n";
+            return false;
+        }
+        return true;
+    }
+
+    // Load and analyse a core file
+    if (gv_core_path) {
+        UString prog_path = a_argv[0];
+        THROW_IF_FAIL (!prog_path.empty ());
+        load_core_file (prog_path, gv_core_path);
+        return true;
+    }
+
+    vector<UString> prog_args;
+    UString prog_path;
+    // Here, a_argc is the argument count of the inferior program.
+    // It's zero if there is there is no inferior program.
+    // Otherwise it equals the number of arguments to the inferior program + 1
+    if (a_argc > 0)
+        prog_path = a_argv[0];
+    for (int i = 1; i < a_argc; ++i) {
+        prog_args.push_back (Glib::locale_to_utf8 (a_argv[i]));
+    }
+
+    if (gv_gdb_binary_filepath) {
+        char *debugger_full_path = realpath (gv_gdb_binary_filepath, 0);
+        if (debugger_full_path) {
+            if (!debugger ()) {
+                cerr << "Could not get the debugger instance" << std::endl;
+                return false;
+            }
+            debugger ()->set_non_persistent_debugger_path (debugger_full_path);
+            free (debugger_full_path);
+        } else {
+            LOG_ERROR ("Could not resolve the full path of the debugger");
+        }
+    }
+
+    std::map<UString, UString> env;
+    if (gv_env_vars) {
+        std::vector<UString> env_vars =
+            UString (Glib::locale_to_utf8 (gv_env_vars)).split (" ");
+        for (std::vector<UString>::const_iterator it = env_vars.begin ();
+             it != env_vars.end ();
+             ++it) {
+            std::vector<UString> env_var = it->split ("=");
+            if (env_var.size () != 2) {
+                continue;
+            }
+            UString name = env_var[0];
+            name.chomp ();
+            UString value = env_var[1];
+            value.chomp ();
+            LOG_DD ("got env var: " << name << "=" << value);
+            env[name] = value;
+        }
+    }
+
+    if (gv_remote) {
+        // The user asked to connect to a remote target.
+        std::string host;
+        unsigned port = 0;
+        std::string solib_prefix;
+
+        if (gv_solib_prefix) {
+            solib_prefix = gv_solib_prefix;
+        }
+
+        if (nemiver::str_utils::parse_host_and_port (gv_remote, host,
+                                                     port)) {
+            // So it looks like gv_remote has the form
+            // "host:port", so let's try to connect to that.
+            connect_to_remote_target (host, port, prog_path, solib_prefix);
+        } else {
+            // We think gv_remote contains a serial line address.
+            // Let's try to connect via the serial line then.
+            connect_to_remote_target (gv_remote, prog_path, solib_prefix);
+        }
+    } else if (!prog_path.empty ()) {
+        // The user wants to debug a local program
+        uses_launch_terminal (gv_use_launch_terminal);
+        execute_program (prog_path,
+                         prog_args,
+                         env,
+                         /*a_cwd=*/".",
+                         /*a_clone_opened_files=*/false,
+                         /*a_break_in_main_run=*/!gv_just_load);
+    }
+    
+    if (gv_use_launch_terminal) {
+        // So the user wants the inferior to use the terminal Nemiver was
+        // launched from, for input/output.
+        //
+        // Letting the inferior use the terminal from which Nemiver was
+        // launched from implies calling the "set inferior-tty" GDB command,
+        // when we are using the GDB backend.
+        // That command is implemented using the TIOCSCTTY ioctl. It sets the
+        // terminal as the controlling terminal of the inferior process. But that
+        // ioctl requires (among other things) that the terminal shall _not_ be
+        // the controlling terminal of any other process in another process
+        // session already. Otherwise, GDB spits the error:
+        // "GDB: Failed to set controlling terminal: operation not
+        // permitted"
+        // The problem is, Nemiver itself uses that terminal as a
+        // controlling terminal. So Nemiver itself must relinquish its
+        // controlling terminal to avoid letting the ioctl fail.
+        // We do so by calling the setsid function below.
+        // The problem is that setsid will fail with EPERM if Nemiver is
+        // started as a process group leader already.
+        // Trying ioctl (tty_fd, TIOCNOTTY, 0) does not seem to properly
+        // disconnect Nemiver from its terminal either. Sigh.
+        setsid ();
+    }
+
+    return true;
+}
+
 IConfMgr&
 DBGPerspective::get_conf_mgr ()
 {
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.h b/src/persp/dbgperspective/nmv-dbg-perspective.h
index 2cc4c47..433655e 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.h
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.h
@@ -79,7 +79,7 @@ public:
 
     virtual IWorkbench& get_workbench () = 0;
 
-    virtual void edit_workbench_menu () = 0;
+    virtual std::list<Gtk::UIManager::ui_merge_id> edit_workbench_menu () = 0;
 
     virtual void open_file () = 0;
 
@@ -186,6 +186,8 @@ public:
 
     virtual sigc::signal<void>& layout_changed_signal () = 0;
 
+    virtual const UString& name () const = 0;
+
     virtual bool agree_to_shutdown () = 0;
 };//end class IDBGPerspective
 
diff --git a/src/persp/nmv-i-perspective.h b/src/persp/nmv-i-perspective.h
index c0440f1..444ae45 100644
--- a/src/persp/nmv-i-perspective.h
+++ b/src/persp/nmv-i-perspective.h
@@ -65,6 +65,16 @@ protected:
 
 public:
 
+    virtual bool process_gui_options (int a_argc, char **a_argv) = 0;
+
+    virtual bool process_options (GOptionContext *a_context,
+                                  int a_argc,
+                                  char **a_argv) = 0;
+
+    virtual GOptionGroup* option_group () const = 0;
+
+    virtual const UString& name () const = 0;
+
     /// initialize the perspective within the context of
     /// of the workbench that loads it.
     /// \param a_workbench, the workbench that loaded the
@@ -92,26 +102,7 @@ public:
     /// This method is only called once, during the
     /// perspective's initialisation time,
     /// by the workbench.
-    virtual void edit_workbench_menu () = 0;
-
-    /// \brief open a source file from a url
-    /// \param a_uri the uri of the file to open
-    /// \param a_cur_line the line to flag as being the current exceution line
-    /// if set to -1, this parameter is ignored.
-    virtual bool open_file (const UString &a_uri, int a_cur_line=-1) = 0;
-
-    /// \brief open a source file
-    ///
-    /// Let the user choose the set of files to open
-    /// via a file chooser dialog and open them.
-    virtual void open_file () = 0;
-
-    /// \brief close the currently selected file
-    virtual void close_current_file () = 0;
-
-    /// \brief closes a file
-    /// \param a_uri the uri that identifies the file to close
-    virtual void close_file (const UString &a_uri) = 0;
+    virtual std::list<Gtk::UIManager::ui_merge_id> edit_workbench_menu () = 0;
 
     /// \brief load a menu file
     /// \param a_filename the file name of the menu file.
diff --git a/src/workbench/nmv-i-workbench.h b/src/workbench/nmv-i-workbench.h
index 0ae7531..02a5ac8 100644
--- a/src/workbench/nmv-i-workbench.h
+++ b/src/workbench/nmv-i-workbench.h
@@ -69,6 +69,7 @@ using nemiver::common::UString;
 
 class IWorkbench;
 typedef SafePtr<IWorkbench, ObjectRef, ObjectUnref> IWorkbenchSafePtr;
+typedef SafePtr<IPerspective, ObjectRef, ObjectUnref> IPerspectiveSafePtr;
 
 /// \brief the interface of the Workbench.
 /// The workbench is what you see graphically when you use
@@ -136,8 +137,14 @@ public:
     /// \return the Gtk::UIManager of the workbench
     virtual Glib::RefPtr<Gtk::UIManager>& get_ui_manager () = 0;
 
+    virtual std::list<IPerspectiveSafePtr> perspectives () const = 0;
+
     /// \return the perspective that which name matches a_name
-    virtual IPerspective* get_perspective (const UString &a_name) = 0;
+    virtual IPerspectiveSafePtr get_perspective (const UString &a_name) = 0;
+
+    virtual void select_perspective (IPerspectiveSafePtr &a_perspective) = 0;
+
+    virtual void load_perspectives () = 0;
 
     /// set the configuration manager used by this interface
     virtual void do_init (IConfMgrSafePtr &) = 0;
diff --git a/src/workbench/nmv-workbench.cc b/src/workbench/nmv-workbench.cc
index e82c13d..1437424 100644
--- a/src/workbench/nmv-workbench.cc
+++ b/src/workbench/nmv-workbench.cc
@@ -86,6 +86,7 @@ private:
     void on_contents_menu_item_action ();
     void on_shutting_down_signal ();
     void on_perspective_layout_changed_signal (IPerspectiveSafePtr);
+    void on_perspective_changed ();
     //************************
     //</slots (signal callbacks)>
     //************************
@@ -100,10 +101,11 @@ private:
                                    list<Gtk::Widget*> &a_tbs);
     void add_perspective_body (IPerspectiveSafePtr &a_perspective,
                                Gtk::Widget *a_body);
+    void add_perspective_to_perspective_selector
+        (IPerspectiveSafePtr &a_perspective);
     bool remove_perspective_body (IPerspectiveSafePtr &a_perspective);
     void remove_all_perspective_bodies ();
     void disconnect_all_perspective_signals ();
-    void select_perspective (IPerspectiveSafePtr &a_perspective);
 
     void save_window_geometry ();
 
@@ -118,8 +120,10 @@ private:
 public:
     Workbench (DynamicModule *a_dynmod);
     virtual ~Workbench ();
+    void load_perspectives ();
     void do_init (Gtk::Main &a_main);
     void do_init (IConfMgrSafePtr &);
+    void select_perspective (IPerspectiveSafePtr &a_perspective);
     void shut_down ();
     Glib::RefPtr<Gtk::ActionGroup> get_default_action_group ();
     Glib::RefPtr<Gtk::ActionGroup> get_debugger_ready_action_group ();
@@ -128,7 +132,8 @@ public:
     Gtk::Window& get_root_window ();
     void set_title_extension (const UString &a_str);
     Glib::RefPtr<Gtk::UIManager>& get_ui_manager () ;
-    IPerspective* get_perspective (const UString &a_name);
+    IPerspectiveSafePtr get_perspective (const UString &a_name);
+    std::list<IPerspectiveSafePtr> perspectives () const;
     void set_configuration_manager (IConfMgrSafePtr &);
     IConfMgrSafePtr get_configuration_manager () ;
     Glib::RefPtr<Glib::MainContext> get_main_context () ;
@@ -145,6 +150,7 @@ struct Workbench::Priv {
     Gtk::Widget *menubar;
     Gtk::Notebook *toolbar_container;
     Gtk::Notebook *bodies_container;
+    Gtk::ComboBoxText *persp_selector_combobox;
     PluginManagerSafePtr plugin_manager;
     list<IPerspectiveSafePtr> perspectives;
     map<IPerspective*, int> toolbars_index_map;
@@ -153,6 +159,7 @@ struct Workbench::Priv {
     IConfMgrSafePtr conf_mgr;
     sigc::signal<void> shutting_down_signal;
     UString base_title;
+    std::list<Gtk::UIManager::ui_merge_id> perspective_menubar_merge_ids;
 
     Priv () :
         initialized (false),
@@ -193,6 +200,28 @@ Workbench::query_for_shutdown ()
 //*********************
 //signal slots methods
 //*********************
+void
+Workbench::on_perspective_changed ()
+{
+    NEMIVER_TRY;
+
+    THROW_IF_FAIL (m_priv);
+    THROW_IF_FAIL (m_priv->persp_selector_combobox);
+
+    UString name = m_priv->persp_selector_combobox->get_active_text ();
+    std::list<IPerspectiveSafePtr>::iterator iter;
+    for (iter = m_priv->perspectives.begin ();
+         iter != m_priv->perspectives.end ();
+         ++iter) {
+        if ((*iter)->name () == name) {
+            select_perspective (*iter);
+            return;
+        }
+    }
+
+    NEMIVER_CATCH;
+}
+
 bool
 Workbench::on_delete_event (GdkEventAny* a_event)
 {
@@ -346,15 +375,10 @@ Workbench::do_init (IConfMgrSafePtr &a_conf_mgr)
     set_configuration_manager (a_conf_mgr);
 }
 
-/// Initialize the workbench by doing all the graphical plumbling
-/// needed to setup the perspectives held by this workbench.  Calling
-/// this function is mandatory prior to using the workbench.
-///
-/// \param a_main the Gtk main object the workbench is going to use.
 void
-Workbench::do_init (Gtk::Main &a_main)
+Workbench::load_perspectives ()
 {
-    LOG_FUNCTION_SCOPE_NORMAL_DD;
+    THROW_IF_FAIL (m_priv);
 
     DynamicModule::Loader *loader =
         get_dynamic_module ().get_module_loader ();
@@ -364,6 +388,57 @@ Workbench::do_init (Gtk::Main &a_main)
         loader->get_dynamic_module_manager ();
     THROW_IF_FAIL (dynmod_manager);
 
+    m_priv->plugin_manager =
+        PluginManagerSafePtr (new PluginManager (*dynmod_manager));
+    THROW_IF_FAIL (m_priv->plugin_manager);
+
+    NEMIVER_TRY;
+
+    std::map<UString, PluginSafePtr>::const_iterator plugin_iter;
+    Plugin::EntryPointSafePtr entry_point;
+    IPerspectiveSafePtr perspective;
+
+    m_priv->plugin_manager->load_plugins ();
+
+    //**************************************************************
+    //store the list of perspectives we may have loaded as plugins,
+    //and init each of them.
+    //**************************************************************
+    for (plugin_iter = m_priv->plugin_manager->plugins_map ().begin ();
+         plugin_iter != m_priv->plugin_manager->plugins_map ().end ();
+         ++plugin_iter) {
+         LOG_D ("plugin '"
+                << plugin_iter->second->descriptor ()->name ()
+                << "' refcount: "
+                << (int) plugin_iter->second->get_refcount (),
+                "refcount-domain");
+        if (plugin_iter->second && plugin_iter->second->entry_point_ptr ()) {
+            entry_point = plugin_iter->second->entry_point_ptr ();
+            perspective = entry_point.do_dynamic_cast<IPerspective> ();
+            if (perspective) {
+                m_priv->perspectives.push_front (perspective);
+                LOG_D ("perspective '"
+                       << perspective->get_perspective_identifier ()
+                       << "' refcount: "
+                       << (int) perspective->get_refcount (),
+                       "refcount-domain");
+            }
+        }
+    }
+
+    NEMIVER_CATCH;
+}
+
+/// Initialize the workbench by doing all the graphical plumbling
+/// needed to setup the perspectives held by this workbench.  Calling
+/// this function is mandatory prior to using the workbench.
+///
+/// \param a_main the Gtk main object the workbench is going to use.
+void
+Workbench::do_init (Gtk::Main &a_main)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
     m_priv->main = &a_main;
 
     // set the icon that will be used by all workbench windows that don't
@@ -394,15 +469,8 @@ Workbench::do_init (Gtk::Main &a_main)
 
     m_priv->initialized = true;
 
-    m_priv->plugin_manager =
-        PluginManagerSafePtr (new PluginManager (*dynmod_manager));
-
     NEMIVER_TRY
-        m_priv->plugin_manager->load_plugins ();
-
-        map<UString, PluginSafePtr>::const_iterator plugin_iter;
         IPerspectiveSafePtr perspective;
-        Plugin::EntryPointSafePtr entry_point;
         list<Gtk::Widget*> toolbars;
 
         //**************************************************************
@@ -410,41 +478,31 @@ Workbench::do_init (Gtk::Main &a_main)
         //and init each of them.
         //**************************************************************
         IWorkbench *workbench = dynamic_cast<IWorkbench*> (this);
-        for (plugin_iter = m_priv->plugin_manager->plugins_map ().begin ();
-             plugin_iter != m_priv->plugin_manager->plugins_map ().end ();
-             ++plugin_iter) {
-             LOG_D ("plugin '"
-                    << plugin_iter->second->descriptor ()->name ()
-                    << "' refcount: "
-                    << (int) plugin_iter->second->get_refcount (),
-                    "refcount-domain");
-            if (plugin_iter->second && plugin_iter->second->entry_point_ptr ()) {
-                entry_point = plugin_iter->second->entry_point_ptr ();
-                perspective = entry_point.do_dynamic_cast<IPerspective> ();
-                if (perspective) {
-                    m_priv->perspectives.push_front (perspective);
-                    perspective->do_init (workbench);
-                    perspective->layout_changed_signal ().connect
-                        (sigc::bind<IPerspectiveSafePtr> (sigc::mem_fun
-                            (*this,
-                            &Workbench::on_perspective_layout_changed_signal),
-                        perspective));
-                    perspective->edit_workbench_menu ();
-                    toolbars.clear ();
-                    perspective->get_toolbars (toolbars);
-                    add_perspective_toolbars (perspective, toolbars);
-                    add_perspective_body (perspective, perspective->get_body ());
-                    LOG_D ("perspective '"
-                           << perspective->get_perspective_identifier ()
-                           << "' refcount: "
-                           << (int) perspective->get_refcount (),
-                           "refcount-domain");
-                }
+        std::list<IPerspectiveSafePtr>::iterator iter;
+        for (iter = m_priv->perspectives.begin ();
+             iter != m_priv->perspectives.end ();
+             ++iter) {
+            perspective = *iter;
+            if (perspective) {
+                perspective->do_init (workbench);
+                perspective->layout_changed_signal ().connect
+                    (sigc::bind<IPerspectiveSafePtr> (sigc::mem_fun
+                        (*this,
+                        &Workbench::on_perspective_layout_changed_signal),
+                    perspective));
+                toolbars.clear ();
+                perspective->get_toolbars (toolbars);
+                add_perspective_to_perspective_selector (perspective);
+                add_perspective_toolbars (perspective, toolbars);
+                add_perspective_body (perspective, perspective->get_body ());
             }
         }
 
         if (!m_priv->perspectives.empty ()) {
-            select_perspective (*m_priv->perspectives.begin ());
+            perspective = *m_priv->perspectives.begin ();
+            THROW_IF_FAIL (perspective);
+            m_priv->persp_selector_combobox->set_active_text
+                (perspective->name ());
         }
     NEMIVER_CATCH
 }
@@ -514,7 +572,14 @@ Workbench::get_ui_manager ()
     return m_priv->ui_manager;
 }
 
-IPerspective*
+std::list<IPerspectiveSafePtr>
+Workbench::perspectives () const
+{
+    THROW_IF_FAIL (m_priv);
+    return m_priv->perspectives;
+}
+
+IPerspectiveSafePtr
 Workbench::get_perspective (const UString &a_name)
 {
     list<IPerspectiveSafePtr>::const_iterator iter;
@@ -522,11 +587,11 @@ Workbench::get_perspective (const UString &a_name)
          iter != m_priv->perspectives.end ();
          ++iter) {
         if ((*iter)->descriptor ()->name () == a_name) {
-            return iter->get ();
+            return *iter;
         }
     }
-    LOG_ERROR ("could not find perspective: '" << a_name << "'");
-    return 0;
+
+    return IPerspectiveSafePtr ();
 }
 
 /// Set the configuration manager
@@ -772,6 +837,13 @@ Workbench::init_toolbar ()
     m_priv->toolbar_container =
         ui_utils::get_widget_from_gtkbuilder<Gtk::Notebook> (m_priv->builder,
                                                         "toolbarcontainer");
+
+    m_priv->persp_selector_combobox =
+        ui_utils::get_widget_from_gtkbuilder<Gtk::ComboBoxText>
+            (m_priv->builder, "perspectiveselector");
+    m_priv->persp_selector_combobox->set_entry_text_column (0);
+    m_priv->persp_selector_combobox->signal_changed ().connect (sigc::mem_fun
+        (*this, &Workbench::on_perspective_changed));
 }
 
 void
@@ -806,6 +878,18 @@ Workbench::add_perspective_toolbars (IPerspectiveSafePtr &a_perspective,
 }
 
 void
+Workbench::add_perspective_to_perspective_selector
+    (IPerspectiveSafePtr &a_perspective)
+{
+    THROW_IF_FAIL (m_priv);
+    THROW_IF_FAIL (a_perspective);
+    THROW_IF_FAIL (m_priv->persp_selector_combobox);
+
+    m_priv->persp_selector_combobox->append
+        (a_perspective->name ());
+}
+
+void
 Workbench::add_perspective_body (IPerspectiveSafePtr &a_perspective,
                                  Gtk::Widget *a_body)
 {
@@ -923,6 +1007,7 @@ Workbench::select_perspective (IPerspectiveSafePtr &a_perspective)
     THROW_IF_FAIL (m_priv);
     THROW_IF_FAIL (m_priv->toolbar_container);
     THROW_IF_FAIL (m_priv->bodies_container);
+    THROW_IF_FAIL (a_perspective);
 
     map<IPerspective*, int>::const_iterator iter, nil;
     int toolbar_index=0, body_index=0;
@@ -939,9 +1024,24 @@ Workbench::select_perspective (IPerspectiveSafePtr &a_perspective)
         body_index = iter->second;
     }
 
+    std::list<Gtk::UIManager::ui_merge_id>::iterator merge_id;
+    for (merge_id = m_priv->perspective_menubar_merge_ids.begin ();
+         merge_id != m_priv->perspective_menubar_merge_ids.end ();
+         ++merge_id) {
+        m_priv->ui_manager->remove_ui (*merge_id);
+    }
+    m_priv->perspective_menubar_merge_ids =
+        a_perspective->edit_workbench_menu ();
+
     m_priv->toolbar_container->set_current_page (toolbar_index);
 
     m_priv->bodies_container->set_current_page (body_index);
+
+    UString name = m_priv->persp_selector_combobox->get_active_text ();
+    if (a_perspective->name () != name) {
+        m_priv->persp_selector_combobox->set_active_text
+            (a_perspective->name ());
+    }
 }
 
 class WorkbenchModule : public DynamicModule {
diff --git a/ui/workbench.ui b/ui/workbench.ui
index 22d555b..aafc74b 100644
--- a/ui/workbench.ui
+++ b/ui/workbench.ui
@@ -1,9 +1,9 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <!-- interface-requires gtk+ 2.6 -->
-  <!-- interface-naming-policy toplevel-contextual -->
+  <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkWindow" id="workbench">
     <property name="visible">True</property>
+    <property name="can_focus">False</property>
     <property name="title" translatable="yes">Nemiver</property>
     <property name="default_width">800</property>
     <property name="default_height">600</property>
@@ -11,11 +11,11 @@
     <child>
       <object class="GtkVBox" id="vbox1">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <child>
           <object class="GtkVBox" id="menucontainer">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="can_focus">False</property>
             <child>
               <placeholder/>
             </child>
@@ -29,53 +29,82 @@
         <child>
           <object class="GtkVBox" id="toolbarvbox">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="can_focus">False</property>
             <child>
-              <object class="GtkNotebook" id="toolbarcontainer">
+              <object class="GtkBox" id="toolbarhbox">
                 <property name="visible">True</property>
-                <property name="show_tabs">False</property>
-                <property name="show_border">False</property>
+                <property name="can_focus">False</property>
                 <child>
-                  <object class="GtkVBox" id="emptytoolbar">
+                  <object class="GtkNotebook" id="toolbarcontainer">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
+                    <property name="can_focus">False</property>
+                    <property name="show_tabs">False</property>
+                    <property name="show_border">False</property>
                     <child>
-                      <placeholder/>
+                      <object class="GtkVBox" id="emptytoolbar">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="tab">
+                      <object class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">label1</property>
+                      </object>
+                      <packing>
+                        <property name="tab_fill">False</property>
+                      </packing>
                     </child>
                   </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
                 </child>
-                <child type="tab">
-                  <object class="GtkLabel" id="label1">
+                <child>
+                  <object class="GtkComboBoxText" id="perspectiveselector">
                     <property name="visible">True</property>
-                    <property name="label" translatable="yes">label1</property>
+                    <property name="can_focus">False</property>
+                    <property name="id_column">0</property>
                   </object>
                   <packing>
-                    <property name="tab_fill">False</property>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
                   </packing>
                 </child>
               </object>
               <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
                 <property name="position">0</property>
               </packing>
             </child>
           </object>
           <packing>
             <property name="expand">False</property>
+            <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
         <child>
           <object class="GtkVBox" id="bodybox">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="can_focus">False</property>
             <child>
               <object class="GtkNotebook" id="bodynotebook">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="show_tabs">False</property>
                 <child>
                   <object class="GtkVBox" id="emptybody">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
+                    <property name="can_focus">False</property>
                     <child>
                       <placeholder/>
                     </child>
@@ -84,6 +113,7 @@
                 <child type="tab">
                   <object class="GtkLabel" id="label2">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="label" translatable="yes">label2</property>
                   </object>
                   <packing>
@@ -92,11 +122,15 @@
                 </child>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">0</property>
               </packing>
             </child>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">2</property>
           </packing>
         </child>
@@ -104,3 +138,4 @@
     </child>
   </object>
 </interface>
+



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