All pastes #186775 Raw Edit

Anonymous

public c v1 · immutable
#186775 ·published 2006-09-30 13:10 UTC
rendered paste body
/* Ekiga -- A VoIP and Video-Conferencing application * Copyright (C) 2000-2006 Damien Sandras * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * * Ekiga is licensed under the GPL license and as a special exception, * you have permission to link or otherwise combine this program with the * programs OPAL, OpenH323 and PWLIB, and distribute the combination, * without applying the requirements of the GNU GPL to the OPAL, OpenH323 * and PWLIB programs, as long as you do follow the requirements of the * GNU GPL for all the rest of the software thus combined. *//* *                         gmgroupseditor.c  -  description *                         -------------------------------- *   begin                : Wed Sep 27 2006 *   copyright            : (C) 2006 by Jan Schampera *   description          : A GTK widget that allows editing of contact group definitions *                          like Ekiga uses it *   license              : GPL * *//*!\file gmgroupseditor.c * \brief The GmGroupsEditor widget * \author Jan Schampera <jan.schampera@unix.net> * \date 2006 * \ingroup GmGroupsEditor */#include "gmgroupseditor.h"#include "gmmarshallers.h"#include <gtk/gtktreeview.h>#include <gtk/gtkliststore.h>#include <gtk/gtkvbox.h>#include <gtk/gtkhbox.h>#include <gtk/gtkhseparator.h>#include <gtk/gtklabel.h>#include <gtk/gtk.h>#include <gtk/gtkmenu.h>#include <string.h>#ifndef DISABLE_GNOME/* for I18N */#include <gnome.h>#endif#define GM_GROUPS_EDITOR_SPACING 2#define GM_GROUPS_EDITOR_MSG_TMOUT 2000#ifndef _#ifdef DISABLE_GNOME#include <libintl.h>#define _(x) gettext(x)#ifdef gettext_noop#define N_(String) gettext_noop (String)#else#define N_(String) (String)#endif /* gettext_noop */#endif /* DISABLE_GNOME */#endif /* _ */enum {  SIG_GROUP_DELETE_REQUEST,  SIG_GROUP_RENAME_REQUEST,  SIG__LAST};enum {  COL_SELECTED,  COL_GROUPNAME,  COL_LAST};/*!\struct _GmGroupsEditorPrivate * \brief the GmGroupsEditor private data storage, inaccessible from outside * \see GmGroupsEditorPrivate * \see GmGroupsEditor * \see _GmGroupsEditor */struct _GmGroupsEditorPrivate {  GSList *selected_groups;  /*!< stringlist of the currently selected groups */  GSList *all_groups;  /*!< stringlist of all the groups available */  gchar *special_group;  /*!< name of the "special group" */  gchar *special_label;  /*!< label for the selection toggle of the "special group" */  GtkWidget *message_label;  /*!< the messaging label */  GtkWidget *main_vbox;  /*!< the main container */  GtkWidget *check_button;  /*!< the check button for the "special group" */  GtkWidget *newg_entry;  /*!< the entry widget to add a new group */  GtkWidget *newg_button;  /*!< the button to add a new group */  GtkWidget *tree_view;  /*!< the tree view that shows the group list */  GtkListStore *list_store;  /*!< the group list */};static guint gm_groups_editor_signals [SIG__LAST] = { 0 };/*!< the array that holds the signal numbers *//*!\fn gm_groups_editor_class_init (GmGroupsEditorClass *) * \brief class init function * * \param klass the GmGroupsEditorClass to init */static voidgm_groups_editor_class_init (GmGroupsEditorClass *);/*!\fn gm_groups_editor_init (GmGroupsEditor *) * \brief instance init function * * \param groups_editor the GmGroupsEditor to init */static voidgm_groups_editor_init (GmGroupsEditor *);/*!\fn gm_groups_editor_fill_selectlist (GmGroupsEditor *) * \brief initially fill the selection list * * Initially fills the selection list and toggles groups that are also * found in the editor's list for selected groups. * \param groups_editor the GmGroupsEditor to perform that */static voidgm_groups_editor_fill_selectlist (GmGroupsEditor *);/*!\fn gm_groups_editor_flash_message (GmGroupsEditor *, gchar *, guint); * \brief flash a message on the editor's message label * * Flashes a message on the editor's message label. The timeout is given in * ms. After the timeout, the message will be cleared. * \param groups_editor the GmGroupsEditor to perform that * \param message the message to flash * \param timeout timeout to clear the label */static voidgm_groups_editor_flash_message (GmGroupsEditor *,				gchar *,				guint);/*!\fn gm_groups_editor_clear_message_cb (gpointer) * \brief timeout callback to clear the editor's message label * * This is called by a timeout caller from GLib. It will clear the message. * The call to it is installed by #gm_groups_editor_flash_message. * \param data a #GmGroupsEditor as gpointer * \return always FALSE to indicate that the call should not be scheduled again * \see gm_groups_editor_flash_message */static gbooleangm_groups_editor_clear_message_cb (gpointer);/*!\fn gm_groups_editor_list_button_event_cb (GtkWidget *, GdkEventButton *, gpointer) * \brief button event handler for the tree view * * This button event handler gets called on a button event over the tree view * that shows the groups. It will show a context menu. * \param tree_view the tree view that generated the event * \param event the #GdkEventButton structure * \param data a #GmGroupsEditor as gpointer * \see gm_groups_editor_show_list_contextmenu * \return TRUE if the event was handled, FALSE if not. */static gbooleangm_groups_editor_list_button_event_cb (GtkWidget *,				       GdkEventButton *,				       gpointer);/*!\fn gm_groups_editor_list_delete_group (GmGroupsEditor *, const gchar *) * \brief delete a group from the list * * This function will delete a group from the list. It will not do something * related to the real existing group list in the contacts API! * \param groups_editor the GmGroupsEditor to perform that * \param groupname the name of the group to delete * \see gm_groups_editor_list_rename_group */static voidgm_groups_editor_list_delete_group (GmGroupsEditor *,				    const gchar *);/*!\fn gm_groups_editor_list_rename_group (GmGroupsEditor *, const gchar *, const gchar *) * \brief rename a group in the list * * This function will rename a group in the list. It will not do something * related to the real existing group list in the contacts API! * \param groups_editor the GmGroupsEditor to perform that * \param from the name of the group to rename * \param to the new name of the group */static voidgm_groups_editor_list_rename_group (GmGroupsEditor *,				    const gchar *,				    const gchar *);/*!\fn gm_groups_editor_show_list_contextmenu (GmGroupsEditor *) * \brief show a list context menu * * This will show a context menu with the options to delete and rename a * group. The actual tree selection is used to get the group name. * \param groups_editor the GmGroupsEditor to perform that * \see gm_groups_editor_list_button_event_cb */static voidgm_groups_editor_show_list_contextmenu (GmGroupsEditor *);/*!\fn gm_groups_editor_list_menu_delete_cb (GtkMenuItem *, gpointer) * \brief menu callback to delete a group * * This will perform the whole delete-process: It displays a confirmation * dialog and sends the request signal. * \param menu_item the menu item the callback was connected to * \param data a #GmGroupsEditor as gpointer * \see gm_groups_editor_show_list_contextmenu */static voidgm_groups_editor_list_menu_delete_cb (GtkMenuItem *,                                      gpointer);/*!\fn gm_groups_editor_list_menu_rename_cb (GtkMenuItem *, gpointer) * \brief menu callback to rename a group * * This will perform the whole rename-process: It displays a dialog to * rename and sends the request signal. It will do some slight sanity checks * on the new group name. * \param menu_item the menu item the callback was connected to * \param data a #GmGroupsEditor as gpointer * \see gm_groups_editor_show_list_contextmenu */static voidgm_groups_editor_list_menu_rename_cb (GtkMenuItem *,                                      gpointer);/*!\fn gm_groups_editor_destroy (GtkObject *object) * \brief cleanup function for the widget's destruction * * \param object the #GmGroupsEditor as #GtkObject */static voidgm_groups_editor_destroy (GtkObject *);/*!\fn stringlist_contains (const GSList *, const gchar *) * \brief checks if a stringlist contains a specific string * * \param stringlist a #GSList* of #gchar* * \param string the string to check for * \return TRUE, if \p stringlist contains \p string, FALSE if not * \see stringlist_deep_copy * \see stringlist_remove * \see stringlist_add * \see stringlist_dump */static gbooleanstringlist_contains (const GSList *,		     const gchar *);/*!\fn stringlist_deep_copy (const GSList *) * \brief copy a stringlist with all its data * * \param stringlist a #GSList* of #gchar* * \return the (new) generated stringlist * \see stringlist_contains * \see stringlist_remove * \see stringlist_add * \see stringlist_dump */static GSList *stringlist_deep_copy (const GSList *);/*!\fn stringlist_remove (const GSList *, const gchar *) * \brief remove a string from a given stringlist * * \param stringlist a #GSList* of #gchar* * \param string the string to remove * \return the new list start * \see stringlist_contains * \see stringlist_deep_copy * \see stringlist_add * \see stringlist_dump */static GSList *stringlist_remove (const GSList *,		   const gchar *);/*!\fn stringlist_add (const GSList *, const gchar *) * \brief add a string to a given stringlist * * \param stringlist a #GSList* of #gchar* * \param string the string to add * \return the new list start * \see stringlist_contains * \see stringlist_deep_copy * \see stringlist_remove * \see stringlist_dump */static GSList *stringlist_add (const GSList *,		const gchar *);/*!\fn stringlist_dump (const GSList *, gchar) * \brief dump the values of a stringlist * * Dumps the several strings in \p stringlist into one string, separated * by \p separator. If \p stringlist is NULL, the returned string is an * empty string (""), not NULL. * \param stringlist a #GSList* of #gchar* * \param separator the separator character to use, must not be NUL * \return the generated string * \see stringlist_contains * \see stringlist_deep_copy * \see stringlist_remove * \see stringlist_add */static gchar *stringlist_dump (const GSList *,		 gchar);/*!\fn grouplist_toggled_cb (GtkCellRendererToggle *, gchar *, gpointer) * \brief called when a group the the list was toggled * * Adds and removes (depending on the toggle status) the toggled group * to/from the list of selected groups. * \param renderer the #GtkCellRendererToggle which produced that signal * \param path the string-path marking the position in the list * \param data a #GmGroupsEditor as gpointer */static voidgrouplist_toggled_cb (GtkCellRendererToggle *,		      gchar *,		      gpointer);/*!\fn special_group_toggled_cb (GtkToggleButton *, gpointer) * \brief called, when the check button for the "special group" was toggled * * Adds and removes (depending on the toggle status) the "special group" * to/from the list of selected groups. * \param toggle_button the check-button (as toggle-button) that sent the signal * \param data a #GmGroupsEditor as gpointer */static voidspecial_group_toggled_cb (GtkToggleButton *,                          gpointer);/*!\fn add_group_clicked_cb (GtkButton *, gpointer) * \brief called when the button to add a new group was clicked * * Will add - after some sanity checks - a new group to the list. * \param button the button that sent the signal * \param data a #GmGroupsEditor as gpointer * \see add_group_entry_activated_cb */static voidadd_group_clicked_cb (GtkButton *,		      gpointer);/*!\fn add_group_entry_activated_cb (GtkEntry *, gpointer) * \brief called when the entry for new groups was activated (RETURN pressed) * * Makes the button to add new groups send a "clicked" signal (will add a new group) * \param entry entry that sent the signal * \param data a #GmGroupsEditor as gpointer * \see add_group_clicked_cb */static voidadd_group_entry_activated_cb (GtkEntry *,			      gpointer);/* Implementation */static voidgrouplist_toggled_cb (GtkCellRendererToggle *renderer,                      gchar *path,                      gpointer data){  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  GtkListStore *store = NULL;  GtkTreeIter iter;  gboolean toggled = FALSE;  gchar *group = NULL;  g_return_if_fail (data != NULL);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  store = priv->list_store;  if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store),					    &iter,					    path))    return;  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,		      COL_SELECTED, &toggled,		      COL_GROUPNAME, &group,		      -1);  toggled = !toggled;  gtk_list_store_set (store, &iter,		      COL_SELECTED, toggled,		      -1);  if (toggled)    priv->selected_groups =      stringlist_add (priv->selected_groups, group);  else    priv->selected_groups =      stringlist_remove (priv->selected_groups, group);  g_free (group);}static voidspecial_group_toggled_cb (GtkToggleButton *toggle_button,			  gpointer data){  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  gchar *special_group = NULL;  g_return_if_fail (data != NULL);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  special_group = g_strdup (priv->special_group);  if (gtk_toggle_button_get_active (toggle_button))    priv->selected_groups =      stringlist_add (priv->selected_groups, special_group);  else    priv->selected_groups =      stringlist_remove (priv->selected_groups, special_group);  g_free (special_group);}static voidadd_group_clicked_cb (GtkButton *button,		      gpointer data){  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  GtkTreeIter iter;  gchar *new_group = NULL;  g_return_if_fail (data != NULL);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  if (stringlist_contains      (priv->all_groups,       gtk_entry_get_text (GTK_ENTRY (priv->newg_entry)))) {    gtk_entry_set_text (GTK_ENTRY (priv->newg_entry), "");    return;  }  new_group = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->newg_entry)));  if (new_group &&      !strcmp ((const char *) new_group,	       "")) {    g_free (new_group);    return;  }  priv->all_groups =    stringlist_add (priv->all_groups, new_group);  priv->selected_groups =    stringlist_add (priv->selected_groups, new_group);  gtk_list_store_append (priv->list_store, &iter);  gtk_list_store_set (priv->list_store, &iter,		      COL_SELECTED, TRUE,		      COL_GROUPNAME, new_group,		      -1);  gtk_entry_set_text (GTK_ENTRY (priv->newg_entry), "");  g_free (new_group);}static voidadd_group_entry_activated_cb (GtkEntry *entry,                              gpointer data){  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  g_return_if_fail (data != NULL);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  gtk_button_clicked (GTK_BUTTON (priv->newg_button));}/* STRINGLIST utility functions, quick hack (TM) by Jan * FIXME improvements?! */static gbooleanstringlist_contains (const GSList *stringlist,		     const gchar *string){  GSList *stringlist_iter = NULL;  g_return_val_if_fail (string != NULL, FALSE);  if (!stringlist)    return FALSE;  stringlist_iter = (GSList *) stringlist;  while (stringlist_iter)    {      if (stringlist_iter->data &&	  !strcmp ((const char *) stringlist_iter->data,		   (const char *) string))	return TRUE;      stringlist_iter = g_slist_next (stringlist_iter);    }  return FALSE;}static GSList *stringlist_deep_copy (const GSList *stringlist){  GSList *stringlist_iter = NULL;  GSList *new_list = NULL;  if (!stringlist)    return NULL;  stringlist_iter = (GSList *) stringlist;  while (stringlist_iter)    {      new_list =	g_slist_append (new_list,			(gpointer) g_strdup			((const gchar *) stringlist_iter->data));      stringlist_iter = g_slist_next (stringlist_iter);    }  return new_list;}static GSList *stringlist_remove (const GSList *list,                   const gchar *string){  GSList *new_list = NULL;  GSList *list_iter = NULL;  gboolean leave = FALSE;  gboolean last_element = FALSE;  if (!list)    return NULL;  if (!string)    return (GSList *) list;  if (!stringlist_contains (list, string))    return (GSList *) list;  new_list = (GSList *) list;  while (!leave) {    list_iter = new_list;    last_element = (g_slist_length (list_iter) == 1);    while (list_iter) {      if (list_iter->data &&	  !strcmp ((const char *) list_iter->data,		   string)) {	g_free (list_iter->data);	new_list = g_slist_delete_link (new_list,					list_iter);	if (last_element) new_list = NULL;	break;      }      list_iter = g_slist_next (list_iter);    }    if (!stringlist_contains (list, string))      leave = TRUE;  }  return new_list;}static GSList *stringlist_add (const GSList *list,                const gchar *string){  if (!string)    return (GSList *) list;  return    g_slist_append ((GSList *) list,		    (gpointer) g_strdup (string));}static gchar *stringlist_dump (const GSList *list,                 gchar separator){  GSList *list_iter = NULL;  gchar *tmpstr[2] = { NULL, NULL };  g_return_val_if_fail (separator != '\0', NULL);  if (!list)    return g_strdup ("");  list_iter = (GSList *) list;  while (list_iter) {    if (list_iter->data) {      if (tmpstr[0]) {	tmpstr[1] = tmpstr[0];	tmpstr[0] = g_strdup_printf ("%s%c%s",				     tmpstr[1],				     separator,				     (const gchar *) list_iter->data);	g_free (tmpstr[1]);      }      else	tmpstr[0] = g_strdup (list_iter->data);    }    list_iter = g_slist_next (list_iter);  }  return tmpstr[0];}GTypegm_groups_editor_get_type (void){  static GType gm_groups_editor_type = 0;  if (!gm_groups_editor_type)    {      static const GTypeInfo gm_groups_editor_info =	{	  sizeof (GmGroupsEditorClass),	  NULL,	  NULL,	  (GClassInitFunc) gm_groups_editor_class_init,	  NULL,	  NULL,	  sizeof (GmGroupsEditor),	  0,	  (GInstanceInitFunc) gm_groups_editor_init,	};      gm_groups_editor_type =	g_type_register_static (GTK_TYPE_EXPANDER,				"GmGroupsEditor",				&gm_groups_editor_info, (GTypeFlags) 0);    }  return gm_groups_editor_type;}static voidgm_groups_editor_class_init (GmGroupsEditorClass *klass){  GtkObjectClass *object_class = NULL;  object_class = GTK_OBJECT_CLASS (klass);  object_class->destroy = gm_groups_editor_destroy;  gm_groups_editor_signals [SIG_GROUP_DELETE_REQUEST] =    g_signal_new ("group-delete-request",		  G_OBJECT_CLASS_TYPE (object_class),		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,		  G_STRUCT_OFFSET (GmGroupsEditorClass, group_delete_request),		  NULL, NULL,		  gm_marshal_BOOLEAN__STRING,		  G_TYPE_BOOLEAN, 1,		  G_TYPE_STRING);  gm_groups_editor_signals [SIG_GROUP_RENAME_REQUEST] =    g_signal_new ("group-rename-request",		  G_OBJECT_CLASS_TYPE (object_class),		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,		  G_STRUCT_OFFSET (GmGroupsEditorClass, group_rename_request),		  NULL, NULL,		  gm_marshal_BOOLEAN__STRING_STRING,		  G_TYPE_BOOLEAN, 2,		  G_TYPE_STRING,		  G_TYPE_STRING);}static voidgm_groups_editor_init (GmGroupsEditor *groups_editor){  GmGroupsEditorPrivate *priv = NULL;  GtkCellRenderer *renderer = NULL;  GtkWidget *hbox = NULL;  GtkWidget *hsep = NULL;  GtkWidget *scrollwindow = NULL;  priv = g_new (GmGroupsEditorPrivate, 1);  groups_editor->priv = priv;  priv->selected_groups = NULL;  priv->all_groups = NULL;  priv->special_group = NULL;  priv->special_label = NULL;  priv->message_label = NULL;  priv->main_vbox = NULL;  priv->check_button = NULL;  priv->newg_entry = NULL;  priv->newg_button = NULL;  priv->tree_view = NULL;  priv->list_store = NULL;  /* the main vbox */  priv->main_vbox = gtk_vbox_new (FALSE,				  GM_GROUPS_EDITOR_SPACING);  /* the separator at the bottom */  hsep = gtk_hseparator_new ();  gtk_box_pack_end (GTK_BOX (priv->main_vbox), hsep,		    FALSE, FALSE, GM_GROUPS_EDITOR_SPACING);  /* the messaging label below the list */  priv->message_label = gtk_label_new ("");  gtk_misc_set_alignment (GTK_MISC (priv->message_label), 0, 0.5);  gtk_box_pack_end (GTK_BOX (priv->main_vbox), priv->message_label,		    FALSE, FALSE, GM_GROUPS_EDITOR_SPACING);  /* the selection list */  priv->tree_view = gtk_tree_view_new ();  scrollwindow = gtk_scrolled_window_new (NULL, NULL);  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwindow),				  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);  gtk_container_add (GTK_CONTAINER (scrollwindow),		     priv->tree_view);  gtk_widget_set_size_request (scrollwindow, -1, 200);  renderer = gtk_cell_renderer_toggle_new ();  gtk_tree_view_insert_column_with_attributes    (GTK_TREE_VIEW (priv->tree_view),     -1, "select", renderer,     "active", COL_SELECTED,     NULL);  g_signal_connect (G_OBJECT (renderer),                    "toggled",                    (GCallback) grouplist_toggled_cb,                    (gpointer) groups_editor);  renderer = gtk_cell_renderer_text_new ();  gtk_tree_view_insert_column_with_attributes    (GTK_TREE_VIEW (priv->tree_view),     -1, _("Group"), renderer,     "text", COL_GROUPNAME,     NULL);  priv->list_store = gtk_list_store_new (COL_LAST,                                         G_TYPE_BOOLEAN,                                         G_TYPE_STRING);  gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view),                           GTK_TREE_MODEL (priv->list_store));  gtk_box_pack_end (GTK_BOX (priv->main_vbox),		    scrollwindow,		    TRUE, TRUE,		    GM_GROUPS_EDITOR_SPACING);  g_signal_connect(G_OBJECT (priv->tree_view),			     "button-press-event",			     (GCallback) gm_groups_editor_list_button_event_cb,			     (gpointer) groups_editor);  /* the "add group" stuff */  hbox = gtk_hbox_new (FALSE, GM_GROUPS_EDITOR_SPACING);  priv->newg_entry = gtk_entry_new ();  gtk_entry_set_width_chars (GTK_ENTRY (priv->newg_entry), 5);  priv->newg_button = gtk_button_new_from_stock (GTK_STOCK_ADD);  gtk_box_pack_start (GTK_BOX (hbox), priv->newg_entry,		      TRUE, TRUE, GM_GROUPS_EDITOR_SPACING);  gtk_box_pack_start (GTK_BOX (hbox), priv->newg_button,		      FALSE, FALSE, GM_GROUPS_EDITOR_SPACING);  g_signal_connect (G_OBJECT (priv->newg_entry),		    "activate",		    (GCallback) add_group_entry_activated_cb,		    (gpointer) groups_editor);  g_signal_connect (G_OBJECT (priv->newg_button),		    "clicked",		    (GCallback) add_group_clicked_cb,		    (gpointer) groups_editor);  gtk_box_pack_end (GTK_BOX (priv->main_vbox),		    hbox,		    FALSE, FALSE,		    GM_GROUPS_EDITOR_SPACING);  /* put all together into expander */  gtk_container_add (GTK_CONTAINER (groups_editor),		     priv->main_vbox);  gtk_widget_show_all (GTK_WIDGET (priv->main_vbox));}GtkWidget *gm_groups_editor_new (const gchar *label,		      const GSList *selected,		      const GSList *allgroups,		      const gchar *specialgroup,		      const gchar *speciallabel){  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  groups_editor = g_object_new (gm_groups_editor_get_type (), NULL);  gtk_expander_set_label (GTK_EXPANDER (groups_editor),			  label);  gtk_container_set_resize_mode (GTK_CONTAINER (groups_editor),				 GTK_RESIZE_PARENT);  priv = groups_editor->priv;  priv->selected_groups = stringlist_deep_copy (selected);  priv->all_groups = stringlist_deep_copy (allgroups);  priv->special_group = g_strdup (specialgroup);  priv->special_label = g_strdup (speciallabel);  /* if the "special" group is present, filter it from the main list   * before filling the selection list FIXME?*/  if (priv->special_group)    priv->all_groups =      stringlist_remove (priv->all_groups, priv->special_group);  gm_groups_editor_fill_selectlist (groups_editor);  /* add it again ...*/  if (priv->special_group)    priv->all_groups =      stringlist_add (priv->all_groups, priv->special_group);  if (priv->special_group) {    priv->check_button =      gtk_check_button_new_with_label (priv->special_label?				       priv->special_label:				       priv->special_group);    gtk_box_pack_end (GTK_BOX (priv->main_vbox),		      priv->check_button,		      FALSE, FALSE,		      GM_GROUPS_EDITOR_SPACING);    /* activate the CheckButton, if the special group already was     * in the "selected" list */    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check_button),				  stringlist_contains (priv->selected_groups,						       (priv->special_group)));    g_signal_connect (G_OBJECT (priv->check_button),		      "toggled",		      (GCallback) special_group_toggled_cb,		      (gpointer) groups_editor);    gtk_widget_show (GTK_WIDGET (priv->check_button));  }  return GTK_WIDGET (groups_editor);}static voidgm_groups_editor_fill_selectlist (GmGroupsEditor *groups_editor){  GmGroupsEditorPrivate *priv = NULL;  GtkTreeIter iter;  GSList *a_list_iter = NULL;  gchar *check_group = NULL;  g_return_if_fail (groups_editor != NULL);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  g_return_if_fail (priv->tree_view != NULL);  g_return_if_fail (priv->list_store != NULL);  if (!priv->selected_groups &&      !priv->all_groups)    return;  /* fill all groups into the list, deselected */  a_list_iter = priv->all_groups;  while (a_list_iter) {      if (a_list_iter->data) {	gtk_list_store_append (priv->list_store, &iter);	gtk_list_store_set (priv->list_store, &iter,			    COL_SELECTED, FALSE,			    COL_GROUPNAME,			    g_strdup ((const gchar *) a_list_iter->data),			    -1);      }      a_list_iter = g_slist_next (a_list_iter);  }  /* walk again through the list store and select all groups   * that are in priv->selected_groups */  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store), &iter)) {    do {      gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,			  COL_GROUPNAME, &check_group,			  -1);      if (stringlist_contains (priv->selected_groups,			       check_group))	gtk_list_store_set (priv->list_store, &iter,			    COL_SELECTED, TRUE, -1);      g_free (check_group);    } while (gtk_tree_model_iter_next	     (GTK_TREE_MODEL (priv->list_store), &iter));  }}GSList *gm_groups_editor_get_stringlist (GmGroupsEditor *groups_editor){  GmGroupsEditorPrivate *priv = NULL;  g_return_val_if_fail (groups_editor != NULL, NULL);  g_return_val_if_fail (GM_IS_GROUPS_EDITOR (groups_editor), NULL);  priv = groups_editor->priv;  return stringlist_deep_copy (priv->selected_groups);}gchar *gm_groups_editor_get_commalist (GmGroupsEditor *groups_editor){  GmGroupsEditorPrivate *priv = NULL;  g_return_val_if_fail (groups_editor != NULL, NULL);  g_return_val_if_fail (GM_IS_GROUPS_EDITOR (groups_editor), NULL);  priv = groups_editor->priv;  return stringlist_dump (priv->selected_groups, ',');}static voidgm_groups_editor_flash_message (GmGroupsEditor *groups_editor,				gchar *message,				guint timeout){  GmGroupsEditorPrivate *priv = NULL;  g_return_if_fail (groups_editor != NULL);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  gtk_label_set_text (GTK_LABEL (priv->message_label), message);  g_timeout_add (timeout,		 (GSourceFunc) gm_groups_editor_clear_message_cb,		 (gpointer) groups_editor);}static gbooleangm_groups_editor_clear_message_cb (gpointer data){  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  g_return_val_if_fail (data != NULL, FALSE);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_val_if_fail (GM_IS_GROUPS_EDITOR (groups_editor), FALSE);  priv = groups_editor->priv;  gtk_label_set_text (GTK_LABEL (priv->message_label), "");  return FALSE;}static gbooleangm_groups_editor_list_button_event_cb (GtkWidget *tree_view,                                       GdkEventButton *event,                                       gpointer data){  GmGroupsEditor *groups_editor = NULL;  GtkTreePath *path = NULL;  GtkTreeSelection *selection = NULL;  g_return_val_if_fail (data != NULL, FALSE);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_val_if_fail (GM_IS_GROUPS_EDITOR (groups_editor), FALSE);  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3) {    /* select the row where the button was pressed */    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));    if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tree_view),				      (gint) event->x,				      (gint) event->y,				      &path, NULL, NULL, NULL)) {      gtk_tree_selection_unselect_all (selection);      gtk_tree_selection_select_path (selection, path);      gtk_tree_path_free (path);      /* show nice (*vomit*) context menu */      gm_groups_editor_show_list_contextmenu (groups_editor);      return TRUE;    }  }  return FALSE;}static voidgm_groups_editor_list_delete_group (GmGroupsEditor *groups_editor,                                    const gchar *groupname){  GtkTreeIter iter;  GmGroupsEditorPrivate *priv = NULL;  gchar *check_group = NULL;  g_return_if_fail (groups_editor != NULL);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  g_return_if_fail (groupname != NULL);  priv = groups_editor->priv;  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store), &iter)) {    do {      gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,                          COL_GROUPNAME, &check_group,                          -1);      if (check_group &&	  !strcmp ((const char *) check_group,		   (const char *) groupname)) {	g_free (check_group);	(void) gtk_list_store_remove (priv->list_store, &iter);	break;      }      g_free (check_group);    } while (gtk_tree_model_iter_next             (GTK_TREE_MODEL (priv->list_store), &iter));  }  stringlist_remove (priv->selected_groups, groupname);  stringlist_remove (priv->all_groups, groupname);}static voidgm_groups_editor_list_rename_group (GmGroupsEditor *groups_editor,                                    const gchar *from,                                    const gchar *to){  GtkTreeIter iter;  GmGroupsEditorPrivate *priv = NULL;  gchar *check_group = NULL;  gboolean selected = FALSE;  g_return_if_fail (groups_editor != NULL);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  g_return_if_fail (from != NULL || to != NULL);  priv = groups_editor->priv;  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store), &iter)) {    do {      gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,			  COL_GROUPNAME, &check_group,			  COL_SELECTED, &selected,			  -1);      if (check_group &&	  !strcmp ((const char *) check_group,		   (const char *) from)) {	g_free (check_group);	g_debug ("BEGIN setting liststore data");	gtk_list_store_set (priv->list_store, &iter,			    COL_GROUPNAME, g_strdup (to),			    -1);	g_debug ("END setting liststore data");	break;      }      g_free (check_group);    } while (gtk_tree_model_iter_next	     (GTK_TREE_MODEL (priv->list_store), &iter));  }  g_debug ("BEGIN removing from selected groups");  stringlist_remove (priv->selected_groups, from);  g_debug ("END removing from selected groups");  g_debug ("BEGIN removing from all groups");  stringlist_remove (priv->all_groups, from);  g_debug ("END removing from all groups");  if (selected) {    g_debug ("BEGIN adding to selected groups");    stringlist_add (priv->selected_groups, to);    g_debug ("END adding to selected groups");  }  g_debug ("BEGIN adding to all groups");  stringlist_add (priv->all_groups, to);  g_debug ("END adding to all groups");}static voidgm_groups_editor_show_list_contextmenu (GmGroupsEditor *groups_editor){  GtkWidget *popup_menu = NULL;  GtkWidget *menu_item = NULL;  g_return_if_fail (groups_editor != NULL);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  /* build the menu and connect the callbacks */  popup_menu = gtk_menu_new ();  menu_item = gtk_menu_item_new_with_label (_("Rename"));  gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu),			 menu_item);  g_signal_connect (G_OBJECT (menu_item),		    "activate",		    (GCallback) gm_groups_editor_list_menu_rename_cb,		    (gpointer) groups_editor);  menu_item = gtk_menu_item_new_with_label (_("Delete"));  gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu),			 menu_item);  g_signal_connect (G_OBJECT (menu_item),		    "activate",		    (GCallback) gm_groups_editor_list_menu_delete_cb,		    (gpointer) groups_editor);  gtk_widget_show_all (popup_menu);  gtk_menu_popup (GTK_MENU (popup_menu),		  NULL, NULL, NULL, NULL,		  0, gtk_get_current_event_time ());  g_signal_connect (G_OBJECT (popup_menu),		    "hide",                   GTK_SIGNAL_FUNC (g_object_unref),		   (gpointer) popup_menu);  g_object_ref_sink ((gpointer) popup_menu);}static voidgm_groups_editor_list_menu_delete_cb (GtkMenuItem *menu_item,				      gpointer data){  GtkTreeSelection *selection;  GtkTreeModel     *model;  GtkTreeIter       iter;  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  GtkWidget *toplevel = NULL;  GtkWidget *dialog = NULL;  gchar *groupname = NULL;  gboolean confirmed = FALSE;  gboolean was_deleted = FALSE;  g_return_if_fail (data != NULL);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (groups_editor));  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));  if (gtk_tree_selection_get_selected(selection, &model, &iter))  {    gtk_tree_model_get (model, &iter,			COL_GROUPNAME, &groupname,			-1);  }  if (!groupname)    return;  dialog =    gtk_message_dialog_new (GTK_WINDOW (toplevel),                            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,                            GTK_MESSAGE_QUESTION,                            GTK_BUTTONS_YES_NO,                            _("Delete group %s?"),                            groupname);  confirmed =    (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES);  gtk_widget_destroy (dialog);  if (!confirmed) {    g_free (groupname);    return;  }  g_signal_emit ((gpointer) groups_editor,                 gm_groups_editor_signals [SIG_GROUP_DELETE_REQUEST],                 0,                 groupname,                 &was_deleted);  if (was_deleted) {    gm_groups_editor_list_delete_group (groups_editor,					groupname);    gm_groups_editor_flash_message (groups_editor,				    _("Group deleted."),				    GM_GROUPS_EDITOR_MSG_TMOUT);  }  else    gm_groups_editor_flash_message (groups_editor,                                    _("Could not delete group!"),                                    GM_GROUPS_EDITOR_MSG_TMOUT);  g_free (groupname);}static voidgm_groups_editor_list_menu_rename_cb (GtkMenuItem *menu_item,                                      gpointer data){  GtkTreeSelection *selection;  GtkTreeModel     *model;  GtkTreeIter       iter;  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  GtkWidget *toplevel = NULL;  GtkWidget *dialog = NULL;  GtkWidget *name_entry = NULL;  gchar *groupname = NULL;  gchar *new_groupname = NULL;  gboolean confirmed = FALSE;  gboolean was_renamed = FALSE;  g_return_if_fail (data != NULL);  groups_editor = GM_GROUPS_EDITOR (data);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (groups_editor));  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));  if (gtk_tree_selection_get_selected(selection, &model, &iter))  {    gtk_tree_model_get (model, &iter,                        COL_GROUPNAME, &groupname,                        -1);  }  if (!groupname)    return;  dialog =    gtk_dialog_new_with_buttons (_("Rename group"),				 GTK_WINDOW (toplevel),				 GTK_DIALOG_MODAL |				 GTK_DIALOG_DESTROY_WITH_PARENT,				 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,				 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,				 NULL);  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);  name_entry = gtk_entry_new ();  gtk_entry_set_text (GTK_ENTRY (name_entry), groupname);  gtk_editable_select_region (GTK_EDITABLE (name_entry), 0, -1);  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), name_entry,		      TRUE, TRUE, GM_GROUPS_EDITOR_SPACING);  gtk_entry_set_activates_default (GTK_ENTRY (name_entry), TRUE);  gtk_widget_show (name_entry);  confirmed =    (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT);  new_groupname =    g_strdup (gtk_entry_get_text (GTK_ENTRY (name_entry)));  gtk_widget_destroy (dialog);  if (!confirmed)    {      g_free (groupname);      g_free (new_groupname);      return;    }  /* little sanity check */  if (groupname &&      new_groupname &&      (!strcmp ((const char *) groupname,		(const char *) new_groupname) ||       !strcmp ((const char *) new_groupname,		""))) {    g_free (groupname);    g_free (new_groupname);    return;  }  g_signal_emit ((gpointer) groups_editor,                 gm_groups_editor_signals [SIG_GROUP_RENAME_REQUEST],                 0,                 groupname,		 new_groupname,                 &was_renamed);  if (was_renamed) {    gm_groups_editor_list_rename_group (groups_editor,					groupname,					new_groupname);    gm_groups_editor_flash_message (groups_editor,                                    _("Group renamed."),                                    GM_GROUPS_EDITOR_MSG_TMOUT);  }  else    gm_groups_editor_flash_message (groups_editor,                                    _("Could not rename group!"),                                    GM_GROUPS_EDITOR_MSG_TMOUT);  g_free (groupname);  g_free (new_groupname);}static voidgm_groups_editor_destroy (GtkObject *object){  GmGroupsEditor *groups_editor = NULL;  GmGroupsEditorPrivate *priv = NULL;  g_return_if_fail (object != NULL);  groups_editor = GM_GROUPS_EDITOR (object);  g_return_if_fail (GM_IS_GROUPS_EDITOR (groups_editor));  priv = groups_editor->priv;  /* free all used data... */  /* FIXME crash crash crash .... GNARG*///  g_free (priv->special_group);//  g_free (priv->special_label);/*  if (priv->selected_groups) {    g_slist_foreach (priv->selected_groups, (GFunc) g_free, NULL);    g_slist_free (priv->selected_groups);  }  if (priv->all_groups) {    g_slist_foreach (priv->all_groups, (GFunc) g_free, NULL);    g_slist_free (priv->all_groups);  }  if (priv->list_store)    gtk_list_store_clear (priv->list_store);    */}