Add a new right-click popup menu for the statusbar profiles for easy functions
authorstig <stig@f5534014-38df-0310-8fa8-9805f1628bb7>
Fri, 1 Oct 2010 16:29:37 +0000 (16:29 +0000)
committerstig <stig@f5534014-38df-0310-8fa8-9805f1628bb7>
Fri, 1 Oct 2010 16:29:37 +0000 (16:29 +0000)
to create new, copy, delete and rename configuration profiles.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@34312 f5534014-38df-0310-8fa8-9805f1628bb7

epan/filesystem.c
epan/filesystem.h
gtk/keys.h
gtk/main_statusbar.c
gtk/menus.c
gtk/menus.h
gtk/profile_dlg.c
gtk/profile_dlg.h

index b5e149029f75fd72d223335cbb48833f970db380..283ea30dd75542247b096d8ab382656d3557edf6 100644 (file)
@@ -965,6 +965,12 @@ get_profile_name(void)
        }
 }
 
+gboolean
+is_default_profile(void)
+{
+       return (!persconfprofile || strcmp(persconfprofile, DEFAULT_PROFILE) == 0) ? TRUE : FALSE;
+}
+
 void
 profile_store_persconffiles(gboolean store)
 {
index ce5cc386bcb1f86e6df738fcd7c5fb9cbbcadef9..28d3ee926a2b08893d61a968064278435dbb3457 100644 (file)
@@ -103,6 +103,11 @@ extern void set_profile_name(const gchar *profilename);
  */
 extern const char *get_profile_name(void);
 
+/*
+ * Check if current profile is default profile.
+ */
+extern gboolean is_default_profile(void);
+
 /*
  * Get the directory used to store configuration profile directories.
  */
index b8b2fba17e7405dc261a7c23a579b95fc041ff52..bdbbfee2c6d993c72b542ea2ad23470162f56fa0 100644 (file)
@@ -53,6 +53,7 @@
 #define PM_PACKET_LIST_KEY                             "popup_menu_packet_list"
 #define PM_TREE_VIEW_KEY                               "popup_menu_tree_view"
 #define PM_BYTES_VIEW_KEY                              "popup_menu_bytes_view"
+#define PM_STATUSBAR_PROFILES_KEY                      "popup_menu_statusbar_profiles"
 
 #define E_TB_MAIN_KEY                                  "toolbar_main"
 #define E_TB_FILTER_KEY                                        "toolbar_filter"
index 4a11f454deaf3f747bfb2d7a10286d0b6540cf94..5a55c4e726967fd65575584ca9eb977c43e3fb21 100644 (file)
@@ -56,6 +56,8 @@
 #include "gtk/profile_dlg.h"
 #include "gtk/main_welcome.h"
 #include "gtk/expert_indicators.h"
+#include "gtk/keys.h"
+#include "gtk/menus.h"
 
 /*
  * The order below defines the priority of info bar contexts.
@@ -411,6 +413,8 @@ profile_bar_new(void)
     profile_bar = gtk_statusbar_new();
     gtk_container_add(GTK_CONTAINER(profile_bar_event), profile_bar);
     g_signal_connect(profile_bar_event, "button_press_event", G_CALLBACK(profile_show_popup_cb), NULL);
+    g_signal_connect(profile_bar_event, "button_press_event", G_CALLBACK(popup_menu_handler),
+                    g_object_get_data(G_OBJECT(popup_menu_object), PM_STATUSBAR_PROFILES_KEY));
     profile_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(profile_bar), "profile");
     gtk_tooltips_set_tip (tooltips, profile_bar_event,
                           "Click to change configuration profile", NULL);
@@ -478,8 +482,9 @@ profile_bar_update(void)
         }
 
         profile_str = g_strdup_printf (" Profile: %s", get_profile_name ());
-
         gtk_statusbar_push(GTK_STATUSBAR(profile_bar), profile_ctx, profile_str);
+
+        set_menus_for_profiles(is_default_profile());
     }
 }
 
index c8779be5a03eec4c20129facd913158356c21753..07e8e967a8a52d57610bf0c6c666b81ebdab77eb 100644 (file)
@@ -44,7 +44,6 @@
 #include <epan/plugins.h>
 #include <epan/epan_dissect.h>
 #include <epan/column.h>
-#include "gtk/dissector_tables_dlg.h"
 
 #include "../print.h"
 #include "../ui_util.h"
@@ -96,6 +95,7 @@
 #include "gtk/gui_utils.h"
 #include "gtk/manual_addr_resolv.h"
 #include "gtk/proto_help.h"
+#include "gtk/dissector_tables_dlg.h"
 
 #define MENUS_USE_UIMANAGER 1
 
@@ -2279,6 +2279,7 @@ static GtkUIManager *ui_manager_packet_list_heading = NULL;
 static GtkUIManager *ui_manager_packet_list_menu = NULL;
 static GtkUIManager *ui_manager_tree_view_menu = NULL;
 static GtkUIManager *ui_manager_bytes_menu = NULL;
+static GtkUIManager *ui_manager_statusbar_profiles_menu = NULL;
 #else
 static GtkItemFactory *packet_list_heading_factory = NULL;
 static GtkItemFactory *packet_list_menu_factory = NULL;
@@ -3458,6 +3459,32 @@ static const GtkRadioActionEntry bytes_menu_radio_action_entries [] =
        { "/HexView",   NULL,           "Hex View",     NULL,   NULL,     BYTES_HEX },
        { "/BitsView",  NULL,           "Bits View",    NULL,   NULL,     BYTES_BITS },
 };
+
+static const char *ui_statusbar_profiles_menu_popup =
+"<ui>\n"
+"  <popup name='ProfilesMenuPopup' action='PopupAction'>\n"
+"     <menuitem name='Profiles' action='/Profiles'/>\n"
+"     <separator/>\n"
+"     <menuitem name='New' action='/New'/>\n"
+"     <menuitem name='Copy' action='/Copy'/>\n"
+"     <menuitem name='Delete' action='/Delete'/>\n"
+"     <menuitem name='Rename' action='/Rename'/>\n"
+"     <separator/>\n"
+"     <menu name='Change' action='/Change'>\n"
+"        <menuitem name='Default' action='/Change/Default'/>\n"
+"     </menu>\n"
+"  </popup>\n"
+"</ui>\n";
+static const GtkActionEntry statusbar_profiles_menu_action_entries [] =
+{
+       { "/Profiles",  NULL,   "Configuration Profiles...",    NULL,   NULL,     G_CALLBACK(profile_dialog_cb) },
+       { "/New",       GTK_STOCK_NEW,  "New...",       NULL,   NULL,     G_CALLBACK(profile_new_cb) },
+       { "/Copy",      GTK_STOCK_COPY, "Copy...",      NULL,   NULL,     G_CALLBACK(profile_copy_cb) },
+       { "/Delete",    GTK_STOCK_DELETE,       "Delete",       NULL,   NULL,     G_CALLBACK(profile_delete_cb) },
+       { "/Rename",    GTK_STOCK_EDIT, "Rename...",    NULL,   NULL,     G_CALLBACK(profile_rename_cb) },
+       { "/Change",    NULL,           "Change",       NULL,   NULL,   NULL },
+       { "/Change/Default",    NULL,   "Default",      NULL,   NULL,     NULL },
+};
 #endif
 
 GtkWidget *
@@ -3617,7 +3644,8 @@ static void
 menus_init(void) {
 #ifdef MENUS_USE_UIMANAGER
     GtkActionGroup *packet_list_heading_action_group, *packet_list_action_group,
-                    *packet_list_details_action_group, *packet_list_byte_menu_action_group;
+        *packet_list_details_action_group, *packet_list_byte_menu_action_group,
+        *statusbar_profiles_action_group;
     GError *error = NULL;
 #endif
 
@@ -3748,7 +3776,7 @@ menus_init(void) {
      * for text widgets.
      */
 #ifdef MENUS_USE_UIMANAGER
-    packet_list_byte_menu_action_group = gtk_action_group_new ("PacketListDetailsMenuPopUpActionGroup");
+    packet_list_byte_menu_action_group = gtk_action_group_new ("PacketListByteMenuPopUpActionGroup");
 
 
     gtk_action_group_add_radio_actions  (packet_list_byte_menu_action_group,            /* the action group */
@@ -3830,6 +3858,35 @@ menus_init(void) {
     main_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", grp);
     gtk_item_factory_create_items_ac(main_menu_factory, nmenu_items, menu_items, NULL, 2);
 #endif /* MAIN_MENU_USE_UIMANAGER */
+
+#ifdef MENUS_USE_UIMANAGER
+    statusbar_profiles_action_group = gtk_action_group_new ("StatusBarProfilesPopUpMenuActionGroup");
+
+    gtk_action_group_add_actions (statusbar_profiles_action_group,            /* the action group */
+        statusbar_profiles_menu_action_entries,                        /* an array of action descriptions */
+        G_N_ELEMENTS(statusbar_profiles_menu_action_entries),        /* the number of entries */
+        popup_menu_object);                                                    /* data to pass to the action callbacks */
+
+    ui_manager_statusbar_profiles_menu = gtk_ui_manager_new ();
+    gtk_ui_manager_insert_action_group (ui_manager_statusbar_profiles_menu,
+        statusbar_profiles_action_group,
+        0); /* the position at which the group will be inserted.  */
+
+    gtk_ui_manager_add_ui_from_string (ui_manager_statusbar_profiles_menu,ui_statusbar_profiles_menu_popup, -1, &error);
+    if (error != NULL)
+    {
+        fprintf (stderr, "Warning: building Statusbar Profiles Pop-Up failed: %s\n",
+                error->message);
+        g_error_free (error);
+        error = NULL;
+    }
+
+    g_object_set_data(G_OBJECT(popup_menu_object), PM_STATUSBAR_PROFILES_KEY,
+                   gtk_ui_manager_get_widget(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup"));
+
+    popup_menu_list = g_slist_append((GSList *)popup_menu_list, ui_manager_statusbar_profiles_menu);
+#endif /* MENUS_USE_UIMANAGER */
+
     menu_dissector_filter();
     merge_all_tap_menus(tap_menu_tree_root);
 
@@ -6866,6 +6923,23 @@ void set_menus_for_file_set(gboolean file_set, gboolean previous_file, gboolean
 #endif /* MAIN_MENU_USE_UIMANAGER */
 }
 
+GtkWidget *menus_get_profiles_menu (void)
+{
+#ifdef MENUS_USE_UIMANAGER
+    return gtk_ui_manager_get_widget(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Change");
+#else
+    return NULL;
+#endif /* MENUS_USE_UIMANAGER */
+}
+
+void set_menus_for_profiles(gboolean default_profile)
+{
+#ifdef MENUS_USE_UIMANAGER
+    set_menu_sensitivity(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Delete", !default_profile);
+    set_menu_sensitivity(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Rename", !default_profile);
+#endif /* MENUS_USE_UIMANAGER */
+}
+
 /*
  * Editor modelines
  *
index fb41e077959adc047cb8dd7f0ef85419ab64ed07..41b3e8fcc7460c70a3eb5e4c5830fe95cac58db6 100644 (file)
@@ -120,6 +120,9 @@ void menus_set_column_align_default (gboolean right_justify);
 /* Update the packet list heading menu to indicate if column can be resolved. */
 void menus_set_column_resolved (gboolean resolved, gboolean can_resolve);
 
+/* Fetch the statusbar profiles submenu */
+extern GtkWidget *menus_get_profiles_menu (void);
+
 /* Enable or disable menu items based on whether a tree row is selected
    and and on whether a "Match Selected" can be done. */
 void set_menus_for_selected_tree_row(capture_file *cf);
@@ -142,6 +145,9 @@ void set_menus_for_captured_packets(gboolean);
 /* Enable or disable menu items based on whether a packet is selected. */
 void set_menus_for_selected_packet(capture_file *cf);
 
+/* Enable or disable menu items based on configuration profile */
+void set_menus_for_profiles(gboolean default_profile);
+
 /*#define MAIN_MENU_USE_UIMANAGER 1 */
 
 #ifdef MAIN_MENU_USE_UIMANAGER
index 43c310538f00acc2117b7c23eb9ceec1a73df9e9..3da1dce8ceaf881c4f5bb5675440fde1a22b718e 100644 (file)
@@ -39,6 +39,7 @@
 #include <wsutil/file_util.h>
 
 #include "gtk/main.h"
+#include "gtk/menus.h"
 #include "gtk/profile_dlg.h"
 #include "gtk/dlg_utils.h"
 #include "gtk/gui_utils.h"
@@ -60,6 +61,10 @@ static GList *edited_profiles = NULL;
 #define PROF_STAT_CHANGED  4
 #define PROF_STAT_COPY     5
 
+#define PROF_OPERATION_NEW  1
+#define PROF_OPERATION_COPY 2
+#define PROF_OPERATION_EDIT 3
+
 typedef struct {
   char *name;           /* profile name */
   char *reference;      /* profile reference */
@@ -242,7 +247,7 @@ fill_list(GtkWidget *main_w)
 }
 
 static gboolean
-profile_is_invalid_name(gchar *name)
+profile_is_invalid_name(const gchar *name)
 {
   gchar  *message = NULL;
 
@@ -918,6 +923,11 @@ profile_show_popup_cb (GtkWidget *w _U_, GdkEvent *event, gpointer user_data _U_
 
   menu = gtk_menu_new ();
 
+  if (bevent->button != 1) {
+    GtkWidget *top_menu = menus_get_profiles_menu ();
+    gtk_menu_item_set_submenu (GTK_MENU_ITEM(top_menu), menu);
+  }
+
   /* Add a menu item for the Default profile */
   menu_item = gtk_check_menu_item_new_with_label (DEFAULT_PROFILE);
   if (strcmp (profile_name, DEFAULT_PROFILE)==0) {
@@ -948,12 +958,226 @@ profile_show_popup_cb (GtkWidget *w _U_, GdkEvent *event, gpointer user_data _U_
     }
   }
 
+  if (bevent->button != 1) {
+    /* Second-click is handled in popup_menu_handler() */
+    return FALSE;
+  }
+
   gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
                  bevent->button, bevent->time);
 
   return TRUE;
 }
 
+static void
+profile_name_edit_ok (GtkWidget *w _U_, gpointer parent_w)
+{
+  gint operation = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(w), "operation"));
+  GtkWidget *entry = g_object_get_data (G_OBJECT(w), "entry");
+  const gchar *new_name =  gtk_entry_get_text(GTK_ENTRY(entry));
+  const gchar *profile_name = get_profile_name();
+  char        *pf_dir_path, *pf_dir_path2, *pf_filename;
+
+  if (strlen(new_name) == 0 || profile_is_invalid_name(new_name)) {
+    return;
+  }
+
+  if (operation == PROF_OPERATION_EDIT && strcmp(new_name, profile_name) == 0) {
+    /* Rename without a change, do nothing */
+    window_destroy(GTK_WIDGET(parent_w));
+    return;
+  }
+
+  if (profile_exists (new_name)) {
+    simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+                 "The profile already exists:\n%s.", new_name);
+    return;
+  }
+
+  switch (operation) {
+  case PROF_OPERATION_NEW:
+    if (create_persconffile_profile(new_name, &pf_dir_path) == -1) {
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "Can't create directory\n\"%s\":\n%s.",
+                   pf_dir_path, strerror(errno));
+      
+      g_free(pf_dir_path);
+    } else {
+      change_configuration_profile (new_name);
+    }
+    break;
+  case PROF_OPERATION_COPY:
+    if (create_persconffile_profile(new_name, &pf_dir_path) == -1) {
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "Can't create directory\n\"%s\":\n%s.",
+                   pf_dir_path, strerror(errno));
+      
+      g_free(pf_dir_path);
+    } else {
+      if (copy_persconffile_profile(new_name, profile_name, &pf_filename,
+                                   &pf_dir_path, &pf_dir_path2) == -1) {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
+                     pf_filename, pf_dir_path2, pf_dir_path, strerror(errno));
+       
+       g_free(pf_filename);
+       g_free(pf_dir_path);
+       g_free(pf_dir_path2);
+      } else {
+       change_configuration_profile (new_name);
+      }
+    }
+    break;
+  case PROF_OPERATION_EDIT:
+    if (rename_persconffile_profile(profile_name, new_name,
+                                   &pf_dir_path, &pf_dir_path2) == -1) {
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
+                   pf_dir_path, pf_dir_path2, strerror(errno));
+      
+      g_free(pf_dir_path);
+      g_free(pf_dir_path2);
+    } else {
+      change_configuration_profile (new_name);
+    }
+    break;
+  default:
+      g_assert_not_reached();
+  }
+
+  window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+profile_name_edit_cancel (GtkWidget *w _U_, gpointer parent_w)
+{
+  window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+profile_name_edit_dlg (gint operation)
+{
+  GtkWidget   *win, *main_tb, *main_vb, *bbox, *cancel_bt, *ok_bt;
+  GtkWidget   *entry, *label;
+  gchar       *window_title, *new_name;
+  const gchar *profile_name;
+  GtkTooltips *tooltips;
+
+  tooltips = gtk_tooltips_new();
+  profile_name = get_profile_name();
+
+  switch (operation) {
+  case PROF_OPERATION_NEW:
+    window_title = g_strdup ("Create New Profile");
+    break;
+  case PROF_OPERATION_COPY:
+    window_title = g_strdup_printf ("Copy: %s", profile_name);
+    break;
+  case PROF_OPERATION_EDIT:
+    window_title = g_strdup_printf ("Rename: %s", profile_name);
+    break;
+  default:
+    g_assert_not_reached();
+  }
+
+  win = dlg_window_new(window_title);
+  g_free (window_title);
+
+  gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
+  gtk_window_resize(GTK_WINDOW(win), 400, 100);
+
+  main_vb = gtk_vbox_new(FALSE, 5);
+  gtk_container_add(GTK_CONTAINER(win), main_vb);
+  gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+
+  main_tb = gtk_table_new(2, 2, FALSE);
+  gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+  gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
+
+  label = gtk_label_new(ep_strdup_printf("Profile name:"));
+  gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 1, 2);
+  gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+
+  entry = gtk_entry_new();
+  gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, 1, 2);
+  switch (operation) {
+  case PROF_OPERATION_NEW:
+    gtk_entry_set_text(GTK_ENTRY(entry), "New profile");
+    break;
+  case PROF_OPERATION_COPY:
+    new_name = g_strdup_printf ("%s (copy)", profile_name);
+    gtk_entry_set_text(GTK_ENTRY(entry), new_name);
+    g_free (new_name);
+    break;
+  case PROF_OPERATION_EDIT:
+    gtk_entry_set_text(GTK_ENTRY(entry), profile_name);
+    break;
+  default:
+    g_assert_not_reached();
+    break;
+  }
+#ifdef _WIN32
+  gtk_tooltips_set_tip (tooltips, entry, "A profile name cannot start or end with a period (.), and cannot contain any of the following characters:\n   \\ / : * ? \" < > |", NULL);
+#else
+  gtk_tooltips_set_tip (tooltips, entry, "A profile name cannot contain the '/' character", NULL);
+#endif
+
+  bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
+  gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+  ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+  g_object_set_data (G_OBJECT(ok_bt), "entry", entry);
+  g_object_set_data (G_OBJECT(ok_bt), "operation", GINT_TO_POINTER(operation));
+  g_signal_connect(ok_bt, "clicked", G_CALLBACK(profile_name_edit_ok), win);
+
+  dlg_set_activate(entry, ok_bt);
+
+  cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+  g_signal_connect(cancel_bt, "clicked", G_CALLBACK(profile_name_edit_cancel), win);
+  window_set_cancel_button(win, cancel_bt, NULL);
+
+  gtk_widget_grab_default(ok_bt);
+  gtk_widget_show_all(win);
+}
+
+void
+profile_new_cb (GtkWidget *w _U_, gpointer data _U_)
+{
+  profile_name_edit_dlg (PROF_OPERATION_NEW);
+}
+
+void
+profile_copy_cb (GtkWidget *w _U_, gpointer data _U_)
+{
+  profile_name_edit_dlg (PROF_OPERATION_COPY);
+}
+
+void
+profile_delete_cb (GtkWidget *w _U_, gpointer data _U_)
+{
+  const gchar *name = get_profile_name();
+  char        *pf_dir_path;
+
+  if (profile_exists(name) && strcmp (name, DEFAULT_PROFILE) != 0) {
+    if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "Can't delete profile directory\n\"%s\":\n%s.",
+                   pf_dir_path, strerror(errno));
+      
+      g_free(pf_dir_path);
+    }
+
+    /* Change to the default profile */
+    change_configuration_profile (NULL);
+  }
+}
+
+void
+profile_rename_cb (GtkWidget *w _U_, gpointer data _U_)
+{
+  profile_name_edit_dlg (PROF_OPERATION_EDIT);
+}
+
 /* Create a profile dialog for editing display profiles; this is to be used
    as a callback for menu items, toolbars, etc.. */
 void
index 5ebe9e6a5286c1ef773626c7c3b80848bdf426bf..57e0d02e843d0a039cc8976c663845e6d859afbe 100644 (file)
  */
 gboolean profile_show_popup_cb(GtkWidget *w _U_, GdkEvent *event, gpointer user_data _U_);
 
+/** User requested to create a new profile.
+ *
+ * @param w parent widget (unused)
+ * @param data pointer to user_data (unused)
+ */
+void profile_new_cb (GtkWidget *w _U_, gpointer data _U_);
+
+/** User requested to copy the current profile.
+ *
+ * @param w parent widget (unused)
+ * @param data pointer to user_data (unused)
+ */
+void profile_copy_cb (GtkWidget *w _U_, gpointer data _U_);
+
+/** User requested to delete the current profile.
+ *
+ * @param w parent widget (unused)
+ * @param data pointer to user_data (unused)
+ */
+void profile_delete_cb (GtkWidget *w _U_, gpointer data _U_);
+
+/** User requested to rename the current profile.
+ *
+ * @param w parent widget (unused)
+ * @param data pointer to user_data (unused)
+ */
+void profile_rename_cb (GtkWidget *w _U_, gpointer data _U_);
+
 /** User requested the "Configuration Profiles" dialog box by menu or toolbar.
  *
  * @param widget parent widget