Logo Search packages:      
Sourcecode: galeon version File versions

gul-gui.c

/*
 *  Copyright (C) 2002 Marco Pesenti Gritti
 *
 *  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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Galeon includes */
#include "gul-gui.h"
#include "gul-string.h"
#include "hig-alert.h"
#include "eel-gconf-extensions.h"

/* system includes */
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <glib/gi18n.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtkmain.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtktearoffmenuitem.h>
#include <gtk/gtkseparatormenuitem.h>
#include <gtk/gtkiconfactory.h>
#include <gtk/gtkimagemenuitem.h>
#include <gtk/gtkcheckmenuitem.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkicontheme.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkalignment.h>
#include <libgnomeui/gnome-icon-lookup.h>
#include <libgnome/gnome-help.h>

static void
make_selection_list (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
                     GList **list);



void
gul_gui_sanitise_popup_position (GtkMenu *menu,
                         GtkWidget *widget,
                         gint *x,
                         gint *y)
{
      GdkScreen *screen = gtk_widget_get_screen (widget);
      gint monitor_num;
      GdkRectangle monitor;
      GtkRequisition req;

      g_return_if_fail (widget != NULL);

      gtk_widget_size_request (GTK_WIDGET (menu), &req);

      monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
      gtk_menu_set_monitor (menu, monitor_num);
      gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);

      *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
      *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
}

void
gul_gui_menu_position_tree_selection (GtkMenu   *menu,
                              gint  *x,
                              gint  *y,
                              gboolean    *push_in,
                              gpointer    user_data)
{
      GtkTreeSelection *selection;
      GList *selected_rows;
      GtkTreeModel *model;
      GtkTreeView *tree_view = GTK_TREE_VIEW (user_data);
      GtkWidget *widget = GTK_WIDGET (user_data);
      GtkRequisition req;
      GdkRectangle visible;

      gtk_widget_size_request (GTK_WIDGET (menu), &req);
      gdk_window_get_origin (widget->window, x, y);

      *x += (widget->allocation.width - req.width) / 2;

      /* Add on height for the treeview title */
      gtk_tree_view_get_visible_rect (tree_view, &visible);
      *y += widget->allocation.height - visible.height;

      selection = gtk_tree_view_get_selection (tree_view);
      selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
      if (selected_rows)
      {
            GdkRectangle cell_rect;

            gtk_tree_view_get_cell_area (tree_view, selected_rows->data,
                                   NULL, &cell_rect);

            *y += CLAMP (cell_rect.y + cell_rect.height, 0, visible.height);
            
            g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
            g_list_free (selected_rows);
      }

      gul_gui_sanitise_popup_position (menu, widget, x, y);
}

/**
 * gul_gui_menu_position_under_widget:
 */
void
gul_gui_menu_position_under_widget (GtkMenu   *menu,
                             gint      *x,
                             gint      *y,
                             gboolean  *push_in,
                             gpointer     user_data)
{
      GtkWidget *w = GTK_WIDGET (user_data);
      GtkRequisition requisition;

      gdk_window_get_origin (w->window, x, y);
      gtk_widget_size_request (GTK_WIDGET (menu), &requisition);

      if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
      {
            *x += w->allocation.x + w->allocation.width - requisition.width;
      }
      else
      {
            *x += w->allocation.x;
      }
      
      *y += w->allocation.y + w->allocation.height;

      gul_gui_sanitise_popup_position (menu, w, x, y);
}

/**
 * gul_gui_gtk_radio_button_get: get the active member of a radiobutton
 * group from one of the buttons in the group. This should be in GTK+!
 */
gint
gul_gui_gtk_radio_button_get (GtkRadioButton *radio_button)
{
      GtkToggleButton *toggle_button;
      gint i, length;
        GSList *list;
      
      /* get group list */
        list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_button));
        length = g_slist_length (list);

      /* iterate over list to find active button */
      for (i = 0; list != NULL; i++, list = g_slist_next (list))
      {
            /* get button and text */
            toggle_button = GTK_TOGGLE_BUTTON (list->data);
            if (gtk_toggle_button_get_active (toggle_button))
            {
                  break;
            }
      }

      /* check we didn't run off end */
      g_assert (list != NULL);

      /* return index (reverse order!) */
      return (length - 1) - i;
}

/**
 * gul_gui_gtk_radio_button_set: set the active member of a radiobutton
 * group from one of the buttons in the group. This should be in GTK+!
 */
void
gul_gui_gtk_radio_button_set (GtkRadioButton *radio_button, gint index)
{
      GtkToggleButton *button;
      GSList *list;
      gint length;

      /* get the list */
        list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_button));

      /* check out the length */
        length = g_slist_length (list);

        /* new buttons are *preppended* to the list, so button added as first 
         * has last position in the list */
        index = (length - 1) - index;

      /* find the right button */
        button = GTK_TOGGLE_BUTTON (g_slist_nth_data (list, index));

      /* set it... this will de-activate the others in the group */
      if (gtk_toggle_button_get_active (button) == FALSE)
      {
            gtk_toggle_button_set_active (button, TRUE);
      }
}

/**
 * treeview_get_selection: get a list of selected iters
 */
GList *
gul_gui_treeview_get_selection_refs (GtkTreeView *treeview)
{
        GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
        GList *list = NULL;

        gtk_tree_selection_selected_foreach (selection,
                                             (GtkTreeSelectionForeachFunc) make_selection_list,
                                             &list);

        return list;
}

/**
 * gul_gui_treeview_free_selection_refs: free the list returned from
 * gul_gui_treeview_get_selection_refs
 */
void
gul_gui_treeview_free_selection_refs (GList *l)
{
      g_list_foreach (l, (GFunc)gtk_tree_row_reference_free, NULL);
      g_list_free (l);
}

/**
 * make_selection_list: utl func to retreive the selection of a treeview
 * supporting multiple selection
 */
static void
make_selection_list (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
                     GList **list)
{
      GtkTreeRowReference *node;

      node = gtk_tree_row_reference_new (model, path);
        *list = g_list_append (*list, node);
}

GtkWidget * 
gul_gui_append_new_menuitem (GtkWidget  *menu,
                       const char *mnemonic,
                       GCallback   callback,
                       gpointer    data)
{
      GtkWidget *menu_item;
      
      menu_item = gtk_menu_item_new_with_mnemonic (mnemonic);
      gtk_widget_show (menu_item);
      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                         menu_item);
      
      if (callback)
      {
            g_signal_connect (G_OBJECT (menu_item),
                          "activate",
                          callback, data);
      }

      return menu_item;
}

GtkWidget * 
gul_gui_append_new_menuitem_stock (GtkWidget  *menu,
                           const char *stock_id,
                           GCallback   callback,
                           gpointer    data)
{
      GtkWidget *menu_item;
      
      menu_item = gtk_image_menu_item_new_from_stock (stock_id, NULL);
      gtk_widget_show (menu_item);
      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                         menu_item);
      
      if (callback)
      {
            g_signal_connect (G_OBJECT (menu_item),
                          "activate",
                          callback, data);
      }
      
      return menu_item;
}

GtkWidget *
gul_gui_append_new_menuitem_stock_icon (GtkWidget *menu, 
                              const char *stock_id,
                              const char *mnemonic,
                              GCallback callback,
                              gpointer data)
{
      GtkWidget *menu_item;
      GtkWidget *image;

      menu_item = gtk_image_menu_item_new_with_mnemonic (mnemonic);
      image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
      gtk_widget_show (image);
      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), image);

      gtk_widget_show (menu_item);
      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                         menu_item);
      
      if (callback)
      {
            g_signal_connect (G_OBJECT (menu_item),
                          "activate",
                          callback, data);
      }
      
      return menu_item;
}

GtkWidget * 
gul_gui_append_new_check_menuitem (GtkWidget  *menu,
                           const char *mnemonic,
                           gboolean value,
                           GCallback callback,
                           gpointer data)
{
      GtkWidget *menu_item;
      
      menu_item = gtk_check_menu_item_new_with_mnemonic (mnemonic);
      gtk_widget_show (menu_item);
      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                         menu_item);
      
      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), value);
      
      if (callback)
      {
            g_signal_connect (G_OBJECT (menu_item),
                          "activate",
                          callback, data);
      }
      
      return menu_item;
}

GtkWidget *
gul_gui_append_separator (GtkWidget *menu)
{
      GtkWidget *menu_item;
      
      menu_item = gtk_menu_item_new ();
      gtk_widget_show (menu_item);
      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                         menu_item);

      return menu_item;
}

gboolean 
gul_gui_confirm_overwrite_file (GtkWidget *parent, const char *filename)
{
      char *tmp, *basename, *fullname, *utf8File;
      GtkWidget *dialog;
      gboolean res;
      
      g_return_val_if_fail (filename != NULL, FALSE);

      /* See if the file exists */
      if (!g_file_test (filename, G_FILE_TEST_EXISTS))
      {
            char *path = g_path_get_dirname (filename);
            char *utf8Path;

            /* Check that files can be created in the specified directory */
            if (access (path, W_OK) == 0)
            {
                  g_free (path);
                  return TRUE;
            }

            utf8Path = g_filename_to_utf8 (path, -1, NULL, NULL, NULL);

            dialog = hig_alert_new (parent ? GTK_WINDOW(parent) : NULL,
                              GTK_DIALOG_MODAL,
                              HIG_ALERT_ERROR,
                              _("Directory not writable"),
                              NULL,
                              GTK_STOCK_OK, GTK_RESPONSE_OK,
                              NULL);
            
            hig_alert_set_secondary_printf (HIG_ALERT(dialog),
                                    _("You do not have permission to "
                                      "create files in %s."),
                                    utf8Path);

            g_free (path);
            g_free (utf8Path);


            gtk_dialog_run (GTK_DIALOG (dialog));

            gtk_widget_destroy (dialog);        
            return FALSE;
      }

      utf8File = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
      tmp = g_path_get_basename (utf8File);
      basename = g_markup_escape_text (tmp, -1);
      g_free (tmp);
      tmp = g_markup_escape_text (utf8File, -1);
      g_free (utf8File);
      fullname = g_strdup_printf ("<tt>\"%s\"</tt>", tmp);
      g_free (tmp);

      /* See if we have perms to overwrite it, or whether to ask about
       * overwriting it */
      if (access (filename, W_OK) == -1)
      {
            dialog = hig_alert_new (parent ? GTK_WINDOW(parent) : NULL,
                              GTK_DIALOG_MODAL,
                              HIG_ALERT_ERROR,
                              NULL, NULL,
                              GTK_STOCK_OK, GTK_RESPONSE_OK,
                              NULL);
            
            hig_alert_set_primary_printf (HIG_ALERT(dialog), 
                                    _("File \"%s\" is not writable"), basename);
            hig_alert_set_secondary_printf (HIG_ALERT(dialog),
                                    _("You do not have permission to overwrite %s."),
                                    fullname);

            g_free (basename);
            g_free (fullname);

            gtk_dialog_run (GTK_DIALOG (dialog));

            gtk_widget_destroy (dialog);        
            return FALSE;
      }


      dialog = hig_alert_new (parent ? GTK_WINDOW(parent) : NULL,
                          GTK_DIALOG_MODAL,
                        HIG_ALERT_CONFIRMATION,
                        NULL, NULL,
                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                          _("_Replace"),    GTK_RESPONSE_YES,
                        NULL);

      hig_alert_set_primary_printf (HIG_ALERT(dialog), 
                                _("Replace \"%s\"?"), basename);
      hig_alert_set_secondary_printf (HIG_ALERT(dialog),
                                  _("The file %s already exists.\n"
                                "Do you want to replace it?"),
                              fullname);
      g_free (basename);
      g_free (fullname);

      gtk_dialog_set_default_response (GTK_DIALOG (dialog),
                                   GTK_RESPONSE_CANCEL);

      res = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES);
      gtk_widget_destroy (dialog);        

      return res;
}

/** gul_gui_widget_hide_now:
 *
 * Same as gtk_widget_hide() but also flushes pending events so that the widget
 * really gets hidden, provides quicker response when closing windows.
 * Conveniently returns %FALSE so that it can be used as a signal handler for
 * "delete-event"
 *
 * Return value: %FALSE
 */
gboolean
gul_gui_widget_hide_now (GtkWidget *widget)
{
      gtk_widget_hide (widget);
      while (gtk_events_pending ()) gtk_main_iteration ();
      return FALSE;
}

/** gul_gui_window_present:
 *  @window: A #GtkWindow
 *  @user_time: Time of the event that caused the window to 
 *              want to raise
 *
 *  Like gtk_window_present, but doesn't have 
 *  http://bugzilla.gnome.org/show_bug.cgi?id=166379
 **/
void
gul_gui_window_present (GtkWindow *window, guint32 user_time)
{
      GtkWidget *widget;
  
      g_return_if_fail (GTK_IS_WINDOW (window));

      widget = GTK_WIDGET (window);
      
      if (GTK_WIDGET_VISIBLE (window))
      {
            g_assert (widget->window != NULL);
            
            if (user_time == 0)
            {
                  user_time = gtk_get_current_event_time ();
            }

            gdk_window_show (widget->window);
            
            /* note that gdk_window_focus() will also move the window to
             * the current desktop, for WM spec compliant window managers.
             */
            gdk_window_focus (widget->window, user_time);
      }
      else
      {
            gtk_widget_show (widget);
      }
}


/**
 * Check if the menushell already has a taroff menuitem.
 */
static gboolean 
gul_gui_menu_shell_has_tearoff (GtkMenuShell *ms)
{
      gboolean ret = FALSE;
      GList *children;
      GList *li;

      children = gtk_container_get_children (GTK_CONTAINER (ms));
      for (li = children; li; li = li->next)
      {
            if (GTK_IS_TEAROFF_MENU_ITEM (li->data))
            {
                  ret = TRUE;
                  break;
            }
      }
      g_list_free (children);

      return ret;
}

/**
 * Adds a teaoroff menuitem if there isn't one already and the gconf option is set
 */
void
gul_gui_setup_tearoff (GtkMenuShell *ms)
{
      gboolean has_tearoff = eel_gconf_get_boolean ("/desktop/gnome/interface/menus_have_tearoff");

      if (has_tearoff && !gul_gui_menu_shell_has_tearoff (ms))
      {
            GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
            gtk_widget_show (tearoff);
            gtk_menu_shell_prepend (ms, tearoff);
      }
}

/**
 * Adds a separated menuitem if there isn't one at the end of the menushell
 */
void
gul_gui_add_separator_conditional (GtkMenuShell *ms)
{
      GList *children;
      GList *li;
      gboolean add = TRUE;

      children = gtk_container_get_children (GTK_CONTAINER (ms));
      for (li = children; li; li = li->next)
      {
            if (!li->next && GTK_IS_SEPARATOR_MENU_ITEM (li->data))
            {
                  add = FALSE;
                  break;
            }
      }
      g_list_free (children);

      if (add)
      {
            GtkWidget *sep = gtk_separator_menu_item_new ();
            gtk_widget_show (sep);
            gtk_menu_shell_append (ms, sep);
      }
}

/**
 * Removes a separator menuitem if there is one at the end of the menushell
 * Needed because the menushell for the context menu persists when not visible.
 * If the context bookmarks becomes null in the interim, the separator will
 * still be left at the end of the context menu. Ugly
 */
void
gul_gui_remove_separator_conditional(GtkMenuShell *ms)
{
      GList *children;
      GList *li;
      GtkWidget *sep = NULL;

      children = gtk_container_get_children(GTK_CONTAINER(ms));
      for (li = children; li; li = li->next)
      {
            if (!li->next && GTK_IS_SEPARATOR_MENU_ITEM(li->data))
            {
                  sep = GTK_WIDGET(li->data);
                  break;
            }
      }
      g_list_free(children);

      if (sep)
      {
            gtk_widget_destroy(sep);
      }
}

void
gul_gui_remove_all_children (GtkContainer *c)
{
      GList *children = gtk_container_get_children (c);
      GList *li;
      for (li = children; li; li = li->next)
      {
            gtk_container_remove (c, li->data);
      }
      g_list_free (children);
}

/**
 * gul_gui_image_set_from_mime_type:
 * @image: a #GtkImage
 * @mime_type: a MIME type
 * @icon_size: a stock icon size
 *
 * Set the @image to display the icon for MIME type @mime_type.  Sample MIME
 * types are "text/plain", "application/ogg".  Sample stock sizes are
 * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_DIALOG.
 */
void
gul_gui_image_set_from_mime_type (GtkWidget  *image,
                              const char *mime_type,
                          GtkIconSize icon_size)
{
      GtkIconTheme *theme;
      char           *icon;
      GtkIconSource  *source;
      GtkIconSet     *icon_set;

      g_return_if_fail (GTK_IS_IMAGE(image));
      g_return_if_fail (mime_type != NULL);

      theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (image));

      icon = gnome_icon_lookup (theme, NULL, NULL, NULL, NULL,
                          mime_type,
                          GNOME_ICON_LOOKUP_FLAGS_NONE, NULL);

      if (!g_path_is_absolute (icon))
      {
            int   width, height;
            GtkIconInfo *icon_info;

            if (!gtk_icon_size_lookup_for_settings 
                  (gtk_widget_get_settings (GTK_WIDGET(image)),
                   icon_size, &width, &height))
            {
                  width = height = -1;
            }

            icon_info = gtk_icon_theme_lookup_icon (theme, icon, height,0);
            g_free (icon);

            g_return_if_fail (icon_info != NULL);

            icon = g_strdup (gtk_icon_info_get_filename (icon_info));
            gtk_icon_info_free (icon_info);
      }

      /* Now that we have the icon filename, wrap it into an GtkIconSet so
       * that we really get the desired size; the icon size in the file may
       * still be arbitrary, in case of SVG themes, for example.
       */

      source = gtk_icon_source_new ();
      gtk_icon_source_set_filename (source, icon);
      g_free (icon);

      icon_set = gtk_icon_set_new ();
      gtk_icon_set_add_source (icon_set, source);
      gtk_icon_source_free (source);

      gtk_image_set_from_icon_set (GTK_IMAGE(image), icon_set, icon_size);

      gtk_icon_set_unref (icon_set);
}

/**
 * gul_image_button_new:
 * @label_text: the text of the button, with an underscore in front of the
 *              mnemonic character
 * @image_stock_id: the name of the stock image
 *
 * Creates a new #GtkButton containing an image and text similar to a stock
 * button.  Not using the stock item label helps to avoid mnemonic conflicts
 * (e.g. _Clear and _Close)
 *
 * Returns: a new #GtkButton
 */
GtkWidget *
gul_gui_image_button_new (const char *label_text, const char *image_stock_id)
{
      GtkWidget *button;
      GtkWidget *label;
      GtkWidget *image;
      GtkWidget *hbox;
      GtkWidget *align;

      button = gtk_button_new ();

      label = gtk_label_new_with_mnemonic (label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button)); 

      image = gtk_image_new_from_stock (image_stock_id, GTK_ICON_SIZE_BUTTON);

      hbox = gtk_hbox_new (FALSE, 2);
      align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);

      gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
      
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);

      gtk_widget_show_all (align);

      return button;
}

/**
 * Open the help browser
 */
void
gul_gui_help (GtkWindow *parent, const char *file, const char *link_id)
{
      GError *error;
      GtkWidget *dialog;

      error = NULL;
      gnome_help_display (file, link_id, &error);

      if (error) 
      {
            dialog = hig_alert_new (parent,
                              (GtkDialogFlags)0,
                              HIG_ALERT_ERROR,
                              _("Cannot display help."),
                              NULL,
                              GTK_STOCK_OK,
                              GTK_RESPONSE_OK,
                              NULL);
            hig_alert_set_secondary_printf (HIG_ALERT (dialog),
                                    error->message);
            g_signal_connect (G_OBJECT (dialog), "response",
                          G_CALLBACK (gtk_widget_destroy),
                          NULL);
            gtk_widget_show (dialog);
            g_error_free (error);
      }
}

Generated by  Doxygen 1.6.0   Back to index