
2001-03-29 Kjartan Maraas <kmaraas@gnome.org> * e-icon-list.c: Replace #include <gtk/gtk.h> * e-msg-composer-attachment-bar.c: Replace #include <gnome.h> * e-msg-composer-attachment-bar.h: Remove #include <gnome.h> * e-msg-composer-attachment.c: Remove #include <gnome.h> * e-msg-composer-attachment.h: Same here. * e-msg-composer-hdrs.c: Replace #include <gnome.h> and <bonobo.h> * e-msg-composer-hdrs.h: Replace #include <gnome.h> * e-msg-composer-file.c: #include <gtk/gtkmain.h>, <gtk/gtksignal.h> * e-msg-composer.c: Replace #include <bonobo.h>, <gnome.h> * e-msg-composer.h: Replace #include <gnome.h> and <bonobo.h> * evolution-composer.c: Replace #include <bonobo.h> * listener.c: Same here. svn path=/trunk/; revision=9023
2676 lines
60 KiB
C
2676 lines
60 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/*
|
|
* Copyright (C) 1998, 1999, 2000 Free Software Foundation
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the Gnome Library.
|
|
*
|
|
* The Gnome Library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* The Gnome Library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with the Gnome Library; see the file COPYING.LIB. If not,
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
/*
|
|
@NOTATION@
|
|
*/
|
|
|
|
/*
|
|
* GnomeIconList widget - scrollable icon list
|
|
*
|
|
* Authors:
|
|
* Federico Mena <federico@ximian.com>
|
|
* Miguel de Icaza <miguel@ximian.com>
|
|
*
|
|
* Rewrote from scratch from the code written by Federico Mena
|
|
* <federico@ximian.com> to be based on a GnomeCanvas, and
|
|
* to support banding selection and allow inline icon renaming.
|
|
*
|
|
* Redone somewhat by Elliot to support gdk-pixbuf, and to use GArray instead of
|
|
* GList for item storage.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <gtk/gtkmain.h>
|
|
#include <gtk/gtkobject.h>
|
|
#include <gtk/gtksignal.h>
|
|
#include <gtk/gtkwidget.h>
|
|
#include <libgnomeui/gnome-icon-item.h>
|
|
#include <libgnomeui/gnome-canvas-rect-ellipse.h>
|
|
#include <gdk-pixbuf/gnome-canvas-pixbuf.h>
|
|
#include "e-icon-list.h"
|
|
|
|
#include "bad-icon.xpm"
|
|
|
|
/* Aliases to minimize screen use in my laptop */
|
|
#define EIL(x) E_ICON_LIST(x)
|
|
#define EIL_CLASS(x) E_ICON_LIST_CLASS(x)
|
|
#define IS_EIL(x) E_IS_ICON_LIST(x)
|
|
|
|
typedef EIconList Eil;
|
|
typedef EIconListClass EilClass;
|
|
|
|
|
|
/* default spacings */
|
|
#define DEFAULT_ROW_SPACING 4
|
|
#define DEFAULT_COL_SPACING 2
|
|
#define DEFAULT_TEXT_SPACING 2
|
|
#define DEFAULT_ICON_BORDER 2
|
|
|
|
/* Autoscroll timeout in milliseconds */
|
|
#define SCROLL_TIMEOUT 30
|
|
|
|
|
|
/* Signals */
|
|
enum {
|
|
SELECT_ICON,
|
|
UNSELECT_ICON,
|
|
TEXT_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
typedef enum {
|
|
SYNC_INSERT,
|
|
SYNC_REMOVE
|
|
} SyncType;
|
|
|
|
enum {
|
|
ARG_0,
|
|
};
|
|
|
|
static guint eil_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
static GnomeCanvasClass *parent_class;
|
|
|
|
|
|
/* Icon structure */
|
|
typedef struct {
|
|
/* Icon image and text items */
|
|
GnomeCanvasPixbuf *image;
|
|
GnomeIconTextItem *text;
|
|
|
|
/* Filename of the icon file. */
|
|
gchar *icon_filename;
|
|
|
|
/* User data and destroy notify function */
|
|
gpointer data;
|
|
GtkDestroyNotify destroy;
|
|
|
|
/* ID for the text item's event signal handler */
|
|
guint text_event_id;
|
|
|
|
/* Whether the icon is selected, and temporary storage for rubberband
|
|
* selections.
|
|
*/
|
|
guint selected : 1;
|
|
guint tmp_selected : 1;
|
|
} Icon;
|
|
|
|
/* A row of icons */
|
|
typedef struct {
|
|
GList *line_icons;
|
|
gint16 y;
|
|
gint16 icon_height, text_height;
|
|
} IconLine;
|
|
|
|
/* Private data of the EIconList structure */
|
|
struct _EIconListPrivate {
|
|
/* List of icons */
|
|
GArray *icon_list;
|
|
|
|
/* List of rows of icons */
|
|
GList *lines;
|
|
|
|
/* Separators used to wrap the text below icons */
|
|
char *separators;
|
|
|
|
Icon *last_selected_icon;
|
|
|
|
/* Rubberband rectangle */
|
|
GnomeCanvasItem *sel_rect;
|
|
|
|
/* Saved event for a pending selection */
|
|
GdkEvent select_pending_event;
|
|
|
|
/* Max of the height of all the icon rows and window height */
|
|
int total_height;
|
|
|
|
/* Selection mode */
|
|
GtkSelectionMode selection_mode;
|
|
|
|
/* A list of integers with the indices of the currently selected icons */
|
|
GList *selection;
|
|
|
|
/* Number of icons in the list */
|
|
int icons;
|
|
|
|
/* Freeze count */
|
|
int frozen;
|
|
|
|
/* Width allocated for icons */
|
|
int icon_width;
|
|
|
|
/* Spacing values */
|
|
int row_spacing;
|
|
int col_spacing;
|
|
int text_spacing;
|
|
int icon_border;
|
|
|
|
/* Index and pointer to last selected icon */
|
|
int last_selected_idx;
|
|
|
|
/* Timeout ID for autoscrolling */
|
|
guint timer_tag;
|
|
|
|
/* Change the adjustment value by this amount when autoscrolling */
|
|
int value_diff;
|
|
|
|
/* Mouse position for autoscrolling */
|
|
int event_last_x;
|
|
int event_last_y;
|
|
|
|
/* Selection start position */
|
|
int sel_start_x;
|
|
int sel_start_y;
|
|
|
|
/* Modifier state when the selection began */
|
|
guint sel_state;
|
|
|
|
/* Whether the icon texts are editable */
|
|
guint is_editable : 1;
|
|
|
|
/* Whether the icon texts need to be copied */
|
|
guint static_text : 1;
|
|
|
|
/* Whether the icons need to be laid out */
|
|
guint dirty : 1;
|
|
|
|
/* Whether the user is performing a rubberband selection */
|
|
guint selecting : 1;
|
|
|
|
/* Whether editing an icon is pending after a button press */
|
|
guint edit_pending : 1;
|
|
|
|
/* Whether selection is pending after a button press */
|
|
guint select_pending : 1;
|
|
|
|
/* Whether the icon that is pending selection was selected to begin with */
|
|
guint select_pending_was_selected : 1;
|
|
};
|
|
|
|
|
|
static inline int
|
|
icon_line_height (Eil *eil, IconLine *il)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
priv = eil->_priv;
|
|
|
|
return il->icon_height + il->text_height + priv->row_spacing + priv->text_spacing;
|
|
}
|
|
|
|
static void
|
|
icon_get_height (Icon *icon, int *icon_height, int *text_height)
|
|
{
|
|
double d_icon_height;
|
|
gtk_object_get(GTK_OBJECT(icon->image), "height", &d_icon_height, NULL);
|
|
*icon_height = d_icon_height;
|
|
*text_height = icon->text->ti->height;
|
|
}
|
|
|
|
static int
|
|
eil_get_items_per_line (Eil *eil)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int items_per_line;
|
|
|
|
priv = eil->_priv;
|
|
|
|
items_per_line = GTK_WIDGET (eil)->allocation.width / (priv->icon_width + priv->col_spacing);
|
|
if (items_per_line == 0)
|
|
items_per_line = 1;
|
|
|
|
return items_per_line;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_get_items_per_line:
|
|
* @eil: An icon list.
|
|
*
|
|
* Returns the number of icons that fit in a line or row.
|
|
*/
|
|
int
|
|
e_icon_list_get_items_per_line (EIconList *eil)
|
|
{
|
|
g_return_val_if_fail (eil != NULL, 1);
|
|
g_return_val_if_fail (IS_EIL (eil), 1);
|
|
|
|
return eil_get_items_per_line (eil);
|
|
}
|
|
|
|
static void
|
|
eil_place_icon (Eil *eil, Icon *icon, int x, int y, int icon_height)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int x_offset, y_offset;
|
|
double d_icon_image_height;
|
|
double d_icon_image_width;
|
|
int icon_image_height;
|
|
int icon_image_width;
|
|
|
|
priv = eil->_priv;
|
|
|
|
gtk_object_get(GTK_OBJECT(icon->image), "height", &d_icon_image_height, NULL);
|
|
icon_image_height = d_icon_image_height;
|
|
g_assert(icon_image_height != 0);
|
|
if (icon_height > icon_image_height)
|
|
y_offset = (icon_height - icon_image_height) / 2;
|
|
else
|
|
y_offset = 0;
|
|
|
|
gtk_object_get(GTK_OBJECT(icon->image), "width", &d_icon_image_width, NULL);
|
|
icon_image_width = d_icon_image_width;
|
|
g_assert(icon_image_width != 0);
|
|
if (priv->icon_width > icon_image_width)
|
|
x_offset = (priv->icon_width - icon_image_width) / 2;
|
|
else
|
|
x_offset = 0;
|
|
|
|
gnome_canvas_item_set (GNOME_CANVAS_ITEM (icon->image),
|
|
"x", (double) (x + x_offset),
|
|
"y", (double) (y + y_offset),
|
|
NULL);
|
|
gnome_icon_text_item_setxy (icon->text,
|
|
x,
|
|
y + icon_height + priv->text_spacing);
|
|
}
|
|
|
|
static void
|
|
eil_layout_line (Eil *eil, IconLine *il)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GList *l;
|
|
int x;
|
|
|
|
priv = eil->_priv;
|
|
|
|
x = 0;
|
|
for (l = il->line_icons; l; l = l->next) {
|
|
Icon *icon = l->data;
|
|
|
|
eil_place_icon (eil, icon, x, il->y, il->icon_height);
|
|
x += priv->icon_width + priv->col_spacing;
|
|
}
|
|
}
|
|
|
|
static void
|
|
eil_add_and_layout_line (Eil *eil, GList *line_icons, int y,
|
|
int icon_height, int text_height)
|
|
{
|
|
EIconListPrivate *priv;
|
|
IconLine *il;
|
|
|
|
priv = eil->_priv;
|
|
|
|
il = g_new (IconLine, 1);
|
|
il->line_icons = line_icons;
|
|
il->y = y;
|
|
il->icon_height = icon_height;
|
|
il->text_height = text_height;
|
|
|
|
eil_layout_line (eil, il);
|
|
priv->lines = g_list_append (priv->lines, il);
|
|
}
|
|
|
|
static void
|
|
eil_relayout_icons_at (Eil *eil, int pos, int y)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int col, row, text_height, icon_height;
|
|
int items_per_line, n;
|
|
GList *line_icons;
|
|
|
|
priv = eil->_priv;
|
|
items_per_line = eil_get_items_per_line (eil);
|
|
|
|
col = row = text_height = icon_height = 0;
|
|
line_icons = NULL;
|
|
|
|
for (n = pos; n < priv->icon_list->len; n++) {
|
|
Icon *icon = g_array_index(priv->icon_list, Icon*, n);
|
|
int ih, th;
|
|
|
|
if (!(n % items_per_line)) {
|
|
if (line_icons) {
|
|
eil_add_and_layout_line (eil, line_icons, y,
|
|
icon_height, text_height);
|
|
line_icons = NULL;
|
|
|
|
y += (icon_height + text_height
|
|
+ priv->row_spacing + priv->text_spacing);
|
|
}
|
|
|
|
icon_height = 0;
|
|
text_height = 0;
|
|
}
|
|
|
|
icon_get_height (icon, &ih, &th);
|
|
|
|
icon_height = MAX (ih, icon_height);
|
|
text_height = MAX (th, text_height);
|
|
|
|
line_icons = g_list_append (line_icons, icon);
|
|
}
|
|
|
|
if (line_icons)
|
|
eil_add_and_layout_line (eil, line_icons, y, icon_height, text_height);
|
|
}
|
|
|
|
static void
|
|
eil_free_line_info (Eil *eil)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GList *l;
|
|
|
|
priv = eil->_priv;
|
|
|
|
for (l = priv->lines; l; l = l->next) {
|
|
IconLine *il = l->data;
|
|
|
|
g_list_free (il->line_icons);
|
|
g_free (il);
|
|
}
|
|
|
|
g_list_free (priv->lines);
|
|
priv->lines = NULL;
|
|
priv->total_height = 0;
|
|
}
|
|
|
|
static void
|
|
eil_free_line_info_from (Eil *eil, int first_line)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GList *l, *ll;
|
|
|
|
priv = eil->_priv;
|
|
ll = g_list_nth (priv->lines, first_line);
|
|
|
|
for (l = ll; l; l = l->next) {
|
|
IconLine *il = l->data;
|
|
|
|
g_list_free (il->line_icons);
|
|
g_free (il);
|
|
}
|
|
|
|
if (priv->lines) {
|
|
if (ll->prev)
|
|
ll->prev->next = NULL;
|
|
else
|
|
priv->lines = NULL;
|
|
}
|
|
|
|
g_list_free (ll);
|
|
}
|
|
|
|
static void
|
|
eil_layout_from_line (Eil *eil, int line)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GList *l;
|
|
int height;
|
|
|
|
priv = eil->_priv;
|
|
|
|
eil_free_line_info_from (eil, line);
|
|
|
|
height = 0;
|
|
for (l = priv->lines; l; l = l->next) {
|
|
IconLine *il = l->data;
|
|
|
|
height += icon_line_height (eil, il);
|
|
}
|
|
|
|
eil_relayout_icons_at (eil, line * eil_get_items_per_line (eil), height);
|
|
}
|
|
|
|
static void
|
|
eil_layout_all_icons (Eil *eil)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
priv = eil->_priv;
|
|
|
|
if (!GTK_WIDGET_REALIZED (eil))
|
|
return;
|
|
|
|
eil_free_line_info (eil);
|
|
eil_relayout_icons_at (eil, 0, 0);
|
|
priv->dirty = FALSE;
|
|
}
|
|
|
|
static void
|
|
eil_scrollbar_adjust (Eil *eil)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GtkAdjustment *adj;
|
|
GList *l;
|
|
double wx, wy, wx1, wy1, wx2, wy2;
|
|
int height, step_increment;
|
|
|
|
priv = eil->_priv;
|
|
|
|
if (!GTK_WIDGET_REALIZED (eil))
|
|
return;
|
|
|
|
height = 0;
|
|
step_increment = 0;
|
|
for (l = priv->lines; l; l = l->next) {
|
|
IconLine *il = l->data;
|
|
|
|
height += icon_line_height (eil, il);
|
|
|
|
if (l == priv->lines)
|
|
step_increment = height;
|
|
}
|
|
|
|
if (!step_increment)
|
|
step_increment = 10;
|
|
|
|
priv->total_height = MAX (height, GTK_WIDGET (eil)->allocation.height);
|
|
|
|
gnome_canvas_c2w (GNOME_CANVAS (eil), 0, 0, &wx1, &wy1);
|
|
gnome_canvas_c2w (GNOME_CANVAS (eil),
|
|
GTK_WIDGET (eil)->allocation.width,
|
|
priv->total_height,
|
|
&wx2, &wy2);
|
|
|
|
gnome_canvas_set_scroll_region (GNOME_CANVAS (eil),
|
|
wx1, wy1, wx2, wy2);
|
|
|
|
wx = wy = 0;
|
|
gnome_canvas_window_to_world (GNOME_CANVAS (eil), 0, 0, &wx, &wy);
|
|
|
|
adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
|
|
|
|
adj->upper = priv->total_height;
|
|
adj->step_increment = step_increment;
|
|
adj->page_increment = GTK_WIDGET (eil)->allocation.height;
|
|
adj->page_size = GTK_WIDGET (eil)->allocation.height;
|
|
|
|
if (wy > adj->upper - adj->page_size)
|
|
wy = adj->upper - adj->page_size;
|
|
|
|
adj->value = wy;
|
|
|
|
gtk_adjustment_changed (adj);
|
|
}
|
|
|
|
/* Emits the select_icon or unselect_icon signals as appropriate */
|
|
static void
|
|
emit_select (Eil *eil, int sel, int i, GdkEvent *event)
|
|
{
|
|
gtk_signal_emit (GTK_OBJECT (eil),
|
|
eil_signals[sel ? SELECT_ICON : UNSELECT_ICON],
|
|
i,
|
|
event);
|
|
}
|
|
|
|
static int
|
|
eil_unselect_all (EIconList *eil, GdkEvent *event, gpointer keep)
|
|
{
|
|
EIconListPrivate *priv;
|
|
Icon *icon;
|
|
int i, idx = 0;
|
|
|
|
g_return_val_if_fail (eil != NULL, 0);
|
|
g_return_val_if_fail (IS_EIL (eil), 0);
|
|
|
|
priv = eil->_priv;
|
|
|
|
for (i = 0; i < priv->icon_list->len; i++) {
|
|
icon = g_array_index(priv->icon_list, Icon*, i);
|
|
|
|
if (icon == keep)
|
|
idx = i;
|
|
else if (icon->selected)
|
|
emit_select (eil, FALSE, i, event);
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_unselect_all:
|
|
* @eil: An icon list.
|
|
*
|
|
* Returns: the number of icons in the icon list
|
|
*/
|
|
int
|
|
e_icon_list_unselect_all (EIconList *eil)
|
|
{
|
|
return eil_unselect_all (eil, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
sync_selection (Eil *eil, int pos, SyncType type)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = eil->_priv->selection; list; list = list->next) {
|
|
if (GPOINTER_TO_INT (list->data) >= pos) {
|
|
int i = GPOINTER_TO_INT (list->data);
|
|
|
|
switch (type) {
|
|
case SYNC_INSERT:
|
|
list->data = GINT_TO_POINTER (i + 1);
|
|
break;
|
|
|
|
case SYNC_REMOVE:
|
|
list->data = GINT_TO_POINTER (i - 1);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
eil_icon_to_index (Eil *eil, Icon *icon)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int n;
|
|
|
|
priv = eil->_priv;
|
|
|
|
for (n = 0; n < priv->icon_list->len; n++)
|
|
if (g_array_index(priv->icon_list, Icon*, n) == icon)
|
|
return n;
|
|
|
|
g_assert_not_reached ();
|
|
return -1; /* Shut up the compiler */
|
|
}
|
|
|
|
/* Event handler for icons when we are in SINGLE or BROWSE mode */
|
|
static gint
|
|
selection_one_icon_event (Eil *eil, Icon *icon, int idx, int on_text, GdkEvent *event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GnomeIconTextItem *text;
|
|
int retval;
|
|
|
|
priv = eil->_priv;
|
|
retval = FALSE;
|
|
|
|
/* We use a separate variable and ref the object because it may be
|
|
* destroyed by one of the signal handlers.
|
|
*/
|
|
text = icon->text;
|
|
gtk_object_ref (GTK_OBJECT (text));
|
|
|
|
switch (event->type) {
|
|
case GDK_BUTTON_PRESS:
|
|
priv->edit_pending = FALSE;
|
|
priv->select_pending = FALSE;
|
|
|
|
/* Ignore wheel mouse clicks for now */
|
|
if (event->button.button > 3)
|
|
break;
|
|
|
|
if (!icon->selected) {
|
|
eil_unselect_all (eil, NULL, NULL);
|
|
emit_select (eil, TRUE, idx, event);
|
|
} else {
|
|
if (priv->selection_mode == GTK_SELECTION_SINGLE
|
|
&& (event->button.state & GDK_CONTROL_MASK))
|
|
emit_select (eil, FALSE, idx, event);
|
|
else if (on_text && priv->is_editable && event->button.button == 1)
|
|
priv->edit_pending = TRUE;
|
|
else
|
|
emit_select (eil, TRUE, idx, event);
|
|
}
|
|
|
|
retval = TRUE;
|
|
break;
|
|
|
|
case GDK_2BUTTON_PRESS:
|
|
case GDK_3BUTTON_PRESS:
|
|
/* Ignore wheel mouse clicks for now */
|
|
if (event->button.button > 3)
|
|
break;
|
|
|
|
emit_select (eil, TRUE, idx, event);
|
|
retval = TRUE;
|
|
break;
|
|
|
|
case GDK_BUTTON_RELEASE:
|
|
if (priv->edit_pending) {
|
|
gnome_icon_text_item_start_editing (text);
|
|
priv->edit_pending = FALSE;
|
|
}
|
|
|
|
retval = TRUE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* If the click was on the text and we actually did something, stop the
|
|
* icon text item's own handler from executing.
|
|
*/
|
|
if (on_text && retval)
|
|
gtk_signal_emit_stop_by_name (GTK_OBJECT (text), "event");
|
|
|
|
gtk_object_unref (GTK_OBJECT (text));
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* Handles range selections when clicking on an icon */
|
|
static void
|
|
select_range (Eil *eil, Icon *icon, int idx, GdkEvent *event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int a, b;
|
|
Icon *i;
|
|
|
|
priv = eil->_priv;
|
|
|
|
if (priv->last_selected_idx == -1) {
|
|
priv->last_selected_idx = idx;
|
|
priv->last_selected_icon = icon;
|
|
}
|
|
|
|
if (idx < priv->last_selected_idx) {
|
|
a = idx;
|
|
b = priv->last_selected_idx;
|
|
} else {
|
|
a = priv->last_selected_idx;
|
|
b = idx;
|
|
}
|
|
|
|
for (; a <= b; a++) {
|
|
i = g_array_index(priv->icon_list, Icon*, a);
|
|
|
|
if (!i->selected)
|
|
emit_select (eil, TRUE, a, NULL);
|
|
}
|
|
|
|
/* Actually notify the client of the event */
|
|
emit_select (eil, TRUE, idx, event);
|
|
}
|
|
|
|
/* Handles icon selection for MULTIPLE or EXTENDED selection modes */
|
|
static void
|
|
do_select_many (Eil *eil, Icon *icon, int idx, GdkEvent *event, int use_event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int range, additive;
|
|
|
|
priv = eil->_priv;
|
|
|
|
range = (event->button.state & GDK_SHIFT_MASK) != 0;
|
|
additive = (event->button.state & GDK_CONTROL_MASK) != 0;
|
|
|
|
if (!additive) {
|
|
if (icon->selected)
|
|
eil_unselect_all (eil, NULL, icon);
|
|
else
|
|
eil_unselect_all (eil, NULL, NULL);
|
|
}
|
|
|
|
if (!range) {
|
|
if (additive)
|
|
emit_select (eil, !icon->selected, idx, use_event ? event : NULL);
|
|
else
|
|
emit_select (eil, TRUE, idx, use_event ? event : NULL);
|
|
|
|
priv->last_selected_idx = idx;
|
|
priv->last_selected_icon = icon;
|
|
} else
|
|
select_range (eil, icon, idx, use_event ? event : NULL);
|
|
}
|
|
|
|
/* Event handler for icons when we are in MULTIPLE or EXTENDED mode */
|
|
static gint
|
|
selection_many_icon_event (Eil *eil, Icon *icon, int idx, int on_text, GdkEvent *event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GnomeIconTextItem *text;
|
|
int retval;
|
|
int additive, range;
|
|
int do_select;
|
|
|
|
priv = eil->_priv;
|
|
retval = FALSE;
|
|
|
|
/* We use a separate variable and ref the object because it may be
|
|
* destroyed by one of the signal handlers.
|
|
*/
|
|
text = icon->text;
|
|
gtk_object_ref (GTK_OBJECT (text));
|
|
|
|
range = (event->button.state & GDK_SHIFT_MASK) != 0;
|
|
additive = (event->button.state & GDK_CONTROL_MASK) != 0;
|
|
|
|
switch (event->type) {
|
|
case GDK_BUTTON_PRESS:
|
|
priv->edit_pending = FALSE;
|
|
priv->select_pending = FALSE;
|
|
|
|
/* Ignore wheel mouse clicks for now */
|
|
if (event->button.button > 3)
|
|
break;
|
|
|
|
do_select = TRUE;
|
|
|
|
if (additive || range) {
|
|
if (additive && !range) {
|
|
priv->select_pending = TRUE;
|
|
priv->select_pending_event = *event;
|
|
priv->select_pending_was_selected = icon->selected;
|
|
|
|
/* We have to emit this so that the client will
|
|
* know about the click.
|
|
*/
|
|
emit_select (eil, TRUE, idx, event);
|
|
do_select = FALSE;
|
|
}
|
|
} else if (icon->selected) {
|
|
priv->select_pending = TRUE;
|
|
priv->select_pending_event = *event;
|
|
priv->select_pending_was_selected = icon->selected;
|
|
|
|
if (on_text && priv->is_editable && event->button.button == 1)
|
|
priv->edit_pending = TRUE;
|
|
|
|
emit_select (eil, TRUE, idx, event);
|
|
do_select = FALSE;
|
|
}
|
|
#if 0
|
|
} else if (icon->selected && on_text && priv->is_editable
|
|
&& event->button.button == 1) {
|
|
priv->edit_pending = TRUE;
|
|
do_select = FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (do_select)
|
|
do_select_many (eil, icon, idx, event, TRUE);
|
|
|
|
retval = TRUE;
|
|
break;
|
|
|
|
case GDK_2BUTTON_PRESS:
|
|
case GDK_3BUTTON_PRESS:
|
|
/* Ignore wheel mouse clicks for now */
|
|
if (event->button.button > 3)
|
|
break;
|
|
|
|
emit_select (eil, TRUE, idx, event);
|
|
retval = TRUE;
|
|
break;
|
|
|
|
case GDK_BUTTON_RELEASE:
|
|
if (priv->select_pending) {
|
|
icon->selected = priv->select_pending_was_selected;
|
|
do_select_many (eil, icon, idx, &priv->select_pending_event, FALSE);
|
|
priv->select_pending = FALSE;
|
|
retval = TRUE;
|
|
}
|
|
|
|
if (priv->edit_pending) {
|
|
gnome_icon_text_item_start_editing (text);
|
|
priv->edit_pending = FALSE;
|
|
retval = TRUE;
|
|
}
|
|
#if 0
|
|
if (priv->select_pending) {
|
|
icon->selected = priv->select_pending_was_selected;
|
|
do_select_many (eil, icon, idx, &priv->select_pending_event);
|
|
priv->select_pending = FALSE;
|
|
retval = TRUE;
|
|
} else if (priv->edit_pending) {
|
|
gnome_icon_text_item_start_editing (text);
|
|
priv->edit_pending = FALSE;
|
|
retval = TRUE;
|
|
}
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* If the click was on the text and we actually did something, stop the
|
|
* icon text item's own handler from executing.
|
|
*/
|
|
if (on_text && retval)
|
|
gtk_signal_emit_stop_by_name (GTK_OBJECT (text), "event");
|
|
|
|
gtk_object_unref (GTK_OBJECT (text));
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* Event handler for icons in the icon list */
|
|
static gint
|
|
icon_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data)
|
|
{
|
|
Icon *icon;
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
int idx;
|
|
int on_text;
|
|
|
|
icon = data;
|
|
eil = EIL (item->canvas);
|
|
priv = eil->_priv;
|
|
idx = eil_icon_to_index (eil, icon);
|
|
on_text = item == GNOME_CANVAS_ITEM (icon->text);
|
|
|
|
switch (priv->selection_mode) {
|
|
case GTK_SELECTION_SINGLE:
|
|
case GTK_SELECTION_BROWSE:
|
|
return selection_one_icon_event (eil, icon, idx, on_text, event);
|
|
|
|
case GTK_SELECTION_MULTIPLE:
|
|
case GTK_SELECTION_EXTENDED:
|
|
return selection_many_icon_event (eil, icon, idx, on_text, event);
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE; /* Shut up the compiler */
|
|
}
|
|
}
|
|
|
|
/* Handler for the editing_started signal of an icon text item. We block the
|
|
* event handler so that it will not be called while the text is being edited.
|
|
*/
|
|
static void
|
|
editing_started (GnomeIconTextItem *iti, gpointer data)
|
|
{
|
|
Icon *icon;
|
|
|
|
icon = data;
|
|
gtk_signal_handler_block (GTK_OBJECT (iti), icon->text_event_id);
|
|
eil_unselect_all (EIL (GNOME_CANVAS_ITEM (iti)->canvas), NULL, icon);
|
|
}
|
|
|
|
/* Handler for the editing_stopped signal of an icon text item. We unblock the
|
|
* event handler so that we can get events from it again.
|
|
*/
|
|
static void
|
|
editing_stopped (GnomeIconTextItem *iti, gpointer data)
|
|
{
|
|
Icon *icon;
|
|
|
|
icon = data;
|
|
gtk_signal_handler_unblock (GTK_OBJECT (iti), icon->text_event_id);
|
|
}
|
|
|
|
static gboolean
|
|
text_changed (GnomeCanvasItem *item, Icon *icon)
|
|
{
|
|
Eil *eil;
|
|
gboolean accept;
|
|
int idx;
|
|
|
|
eil = EIL (item->canvas);
|
|
accept = TRUE;
|
|
|
|
idx = eil_icon_to_index (eil, icon);
|
|
gtk_signal_emit (GTK_OBJECT (eil),
|
|
eil_signals[TEXT_CHANGED],
|
|
idx, gnome_icon_text_item_get_text (icon->text),
|
|
&accept);
|
|
|
|
return accept;
|
|
}
|
|
|
|
static void
|
|
height_changed (GnomeCanvasItem *item, Icon *icon)
|
|
{
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
int n;
|
|
|
|
eil = EIL (item->canvas);
|
|
priv = eil->_priv;
|
|
|
|
if (!GTK_WIDGET_REALIZED (eil))
|
|
return;
|
|
|
|
if (priv->frozen) {
|
|
priv->dirty = TRUE;
|
|
return;
|
|
}
|
|
|
|
n = eil_icon_to_index (eil, icon);
|
|
eil_layout_from_line (eil, n / eil_get_items_per_line (eil));
|
|
eil_scrollbar_adjust (eil);
|
|
}
|
|
|
|
static Icon *
|
|
icon_new_from_pixbuf (EIconList *eil, GdkPixbuf *im,
|
|
const char *icon_filename, const char *text)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GnomeCanvas *canvas;
|
|
GnomeCanvasGroup *group;
|
|
Icon *icon;
|
|
|
|
priv = eil->_priv;
|
|
canvas = GNOME_CANVAS (eil);
|
|
group = GNOME_CANVAS_GROUP (canvas->root);
|
|
|
|
icon = g_new0 (Icon, 1);
|
|
|
|
if (icon_filename)
|
|
icon->icon_filename = g_strdup (icon_filename);
|
|
else
|
|
icon->icon_filename = NULL;
|
|
|
|
if (im == NULL)
|
|
im = gdk_pixbuf_new_from_xpm_data ((const char**) bad_icon_xpm);
|
|
else
|
|
gdk_pixbuf_ref (im);
|
|
|
|
icon->image = GNOME_CANVAS_PIXBUF (gnome_canvas_item_new (
|
|
group,
|
|
gnome_canvas_pixbuf_get_type (),
|
|
"x", 0.0,
|
|
"y", 0.0,
|
|
"width", (double) gdk_pixbuf_get_width (im),
|
|
"height", (double) gdk_pixbuf_get_height (im),
|
|
"pixbuf", im,
|
|
NULL));
|
|
gdk_pixbuf_unref (im);
|
|
|
|
icon->text = GNOME_ICON_TEXT_ITEM (gnome_canvas_item_new (
|
|
group,
|
|
gnome_icon_text_item_get_type (),
|
|
NULL));
|
|
|
|
gnome_canvas_item_set (GNOME_CANVAS_ITEM (icon->text),
|
|
"use_broken_event_handling", FALSE,
|
|
NULL);
|
|
|
|
/* FIXME: should this use a selectable font?? */
|
|
#warning "Ettore: should we allow this font to be selectable??"
|
|
gnome_icon_text_item_configure (icon->text,
|
|
0, 0, priv->icon_width,
|
|
"-adobe-helvetica-medium-r-normal-*-*-120-*-*-p-*-iso8859-1",
|
|
text, priv->is_editable, priv->static_text);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (icon->image), "event",
|
|
GTK_SIGNAL_FUNC (icon_event),
|
|
icon);
|
|
icon->text_event_id = gtk_signal_connect (GTK_OBJECT (icon->text), "event",
|
|
GTK_SIGNAL_FUNC (icon_event),
|
|
icon);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (icon->text), "editing_started",
|
|
GTK_SIGNAL_FUNC (editing_started),
|
|
icon);
|
|
gtk_signal_connect (GTK_OBJECT (icon->text), "editing_stopped",
|
|
GTK_SIGNAL_FUNC (editing_stopped),
|
|
icon);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (icon->text), "text_changed",
|
|
GTK_SIGNAL_FUNC (text_changed),
|
|
icon);
|
|
gtk_signal_connect (GTK_OBJECT (icon->text), "height_changed",
|
|
GTK_SIGNAL_FUNC (height_changed),
|
|
icon);
|
|
|
|
return icon;
|
|
}
|
|
|
|
static Icon *
|
|
icon_new (Eil *eil, const char *icon_filename, const char *text)
|
|
{
|
|
GdkPixbuf *im;
|
|
Icon *retval;
|
|
|
|
if (icon_filename) {
|
|
im = gdk_pixbuf_new_from_file (icon_filename);
|
|
|
|
/* Bad icon image
|
|
Fixme. Need a better graphic. */
|
|
if (im == NULL)
|
|
im = gdk_pixbuf_new_from_xpm_data ((const char**) bad_icon_xpm);
|
|
} else
|
|
im = NULL;
|
|
|
|
retval = icon_new_from_pixbuf (eil, im, icon_filename, text);
|
|
|
|
if(im)
|
|
gdk_pixbuf_unref(im);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
icon_list_append (Eil *eil, Icon *icon)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int pos;
|
|
|
|
priv = eil->_priv;
|
|
|
|
pos = priv->icons++;
|
|
g_array_append_val(priv->icon_list, icon);
|
|
|
|
switch (priv->selection_mode) {
|
|
case GTK_SELECTION_BROWSE:
|
|
e_icon_list_select_icon (eil, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!priv->frozen) {
|
|
/* FIXME: this should only layout the last line */
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
} else
|
|
priv->dirty = TRUE;
|
|
|
|
return priv->icons - 1;
|
|
}
|
|
|
|
static void
|
|
icon_list_insert (Eil *eil, int pos, Icon *icon)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
priv = eil->_priv;
|
|
|
|
if (pos == priv->icons) {
|
|
icon_list_append (eil, icon);
|
|
return;
|
|
}
|
|
|
|
g_array_insert_val(priv->icon_list, pos, icon);
|
|
priv->icons++;
|
|
|
|
switch (priv->selection_mode) {
|
|
case GTK_SELECTION_BROWSE:
|
|
e_icon_list_select_icon (eil, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!priv->frozen) {
|
|
/* FIXME: this should only layout the lines from then one
|
|
* containing the Icon to the end.
|
|
*/
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
} else
|
|
priv->dirty = TRUE;
|
|
|
|
sync_selection (eil, pos, SYNC_INSERT);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_insert_pixbuf:
|
|
* @eil: An icon list.
|
|
* @pos: Position at which the new icon should be inserted.
|
|
* @im: Pixbuf image with the icon image.
|
|
* @filename: Filename of the image file.
|
|
* @text: Text to be used for the icon's caption.
|
|
*
|
|
* Inserts an icon in the specified icon list. The icon is created from the
|
|
* specified Imlib image, and it is inserted at the @pos index.
|
|
*/
|
|
void
|
|
e_icon_list_insert_pixbuf (EIconList *eil, int pos, GdkPixbuf *im,
|
|
const char *icon_filename, const char *text)
|
|
{
|
|
Icon *icon;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
icon = icon_new_from_pixbuf (eil, im, icon_filename, text);
|
|
icon_list_insert (eil, pos, icon);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_insert:
|
|
* @eil: An icon list.
|
|
* @pos: Position at which the new icon should be inserted.
|
|
* @icon_filename: Name of the file that holds the icon's image.
|
|
* @text: Text to be used for the icon's caption.
|
|
*
|
|
* Inserts an icon in the specified icon list. The icon's image is loaded
|
|
* from the specified file, and it is inserted at the @pos index.
|
|
*/
|
|
void
|
|
e_icon_list_insert (EIconList *eil, int pos, const char *icon_filename, const char *text)
|
|
{
|
|
Icon *icon;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
icon = icon_new (eil, icon_filename, text);
|
|
icon_list_insert (eil, pos, icon);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_append_pixbuf:
|
|
* @eil: An icon list.
|
|
* @im: Pixbuf image with the icon image.
|
|
* @filename: Filename of the image file.
|
|
* @text: Text to be used for the icon's caption.
|
|
*
|
|
* Appends an icon to the specified icon list. The icon is created from
|
|
* the specified Imlib image.
|
|
*/
|
|
int
|
|
e_icon_list_append_pixbuf (EIconList *eil, GdkPixbuf *im,
|
|
const char *icon_filename, const char *text)
|
|
{
|
|
Icon *icon;
|
|
|
|
g_return_val_if_fail (eil != NULL, -1);
|
|
g_return_val_if_fail (IS_EIL (eil), -1);
|
|
|
|
icon = icon_new_from_pixbuf (eil, im, icon_filename, text);
|
|
return icon_list_append (eil, icon);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_append:
|
|
* @eil: An icon list.
|
|
* @icon_filename: Name of the file that holds the icon's image.
|
|
* @text: Text to be used for the icon's caption.
|
|
*
|
|
* Appends an icon to the specified icon list. The icon's image is loaded from
|
|
* the specified file, and it is inserted at the @pos index.
|
|
*/
|
|
int
|
|
e_icon_list_append (EIconList *eil, const char *icon_filename,
|
|
const char *text)
|
|
{
|
|
Icon *icon;
|
|
|
|
g_return_val_if_fail (eil != NULL, -1);
|
|
g_return_val_if_fail (IS_EIL (eil), -1);
|
|
|
|
icon = icon_new (eil, icon_filename, text);
|
|
return icon_list_append (eil, icon);
|
|
}
|
|
|
|
static void
|
|
icon_destroy (Icon *icon)
|
|
{
|
|
if (icon->destroy)
|
|
(* icon->destroy) (icon->data);
|
|
|
|
g_free (icon->icon_filename);
|
|
|
|
gtk_object_destroy (GTK_OBJECT (icon->image));
|
|
gtk_object_destroy (GTK_OBJECT (icon->text));
|
|
g_free (icon);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_remove:
|
|
* @eil: An icon list.
|
|
* @pos: Index of the icon that should be removed.
|
|
*
|
|
* Removes the icon at index position @pos. If a destroy handler was specified
|
|
* for that icon, it will be called with the appropriate data.
|
|
*/
|
|
void
|
|
e_icon_list_remove (EIconList *eil, int pos)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int was_selected;
|
|
Icon *icon;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
|
|
|
|
priv = eil->_priv;
|
|
|
|
was_selected = FALSE;
|
|
|
|
icon = g_array_index(priv->icon_list, Icon*, pos);
|
|
|
|
if (icon->selected) {
|
|
was_selected = TRUE;
|
|
|
|
switch (priv->selection_mode) {
|
|
case GTK_SELECTION_SINGLE:
|
|
case GTK_SELECTION_BROWSE:
|
|
case GTK_SELECTION_MULTIPLE:
|
|
case GTK_SELECTION_EXTENDED:
|
|
e_icon_list_unselect_icon (eil, pos);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_array_remove_index(priv->icon_list, pos);
|
|
priv->icons--;
|
|
|
|
sync_selection (eil, pos, SYNC_REMOVE);
|
|
|
|
if (was_selected) {
|
|
switch (priv->selection_mode) {
|
|
case GTK_SELECTION_BROWSE:
|
|
if (pos == priv->icons)
|
|
e_icon_list_select_icon (eil, pos - 1);
|
|
else
|
|
e_icon_list_select_icon (eil, pos);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (priv->icons >= priv->last_selected_idx)
|
|
priv->last_selected_idx = -1;
|
|
|
|
if (priv->last_selected_icon == icon)
|
|
priv->last_selected_icon = NULL;
|
|
|
|
icon_destroy (icon);
|
|
|
|
if (!priv->frozen) {
|
|
/* FIXME: Optimize, only re-layout from pos to end */
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
} else
|
|
priv->dirty = TRUE;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_clear:
|
|
* @eil: An icon list.
|
|
*
|
|
* Clears the contents for the icon list by removing all the icons. If destroy
|
|
* handlers were specified for any of the icons, they will be called with the
|
|
* appropriate data.
|
|
*/
|
|
void
|
|
e_icon_list_clear (EIconList *eil)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int i;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
priv = eil->_priv;
|
|
|
|
for (i = 0; i < priv->icon_list->len; i++)
|
|
icon_destroy (g_array_index (priv->icon_list, Icon*, i));
|
|
|
|
eil_free_line_info (eil);
|
|
|
|
g_list_free (priv->selection);
|
|
priv->selection = NULL;
|
|
g_array_set_size(priv->icon_list, 0);
|
|
priv->icons = 0;
|
|
priv->last_selected_idx = -1;
|
|
priv->last_selected_icon = NULL;
|
|
|
|
if (!priv->frozen) {
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
} else
|
|
priv->dirty = TRUE;
|
|
}
|
|
|
|
static void
|
|
eil_destroy (GtkObject *object)
|
|
{
|
|
Eil *eil;
|
|
|
|
/* remember, destroy can be run multiple times! */
|
|
|
|
eil = EIL (object);
|
|
|
|
g_free (eil->_priv->separators);
|
|
eil->_priv->separators = NULL;
|
|
|
|
eil->_priv->frozen = 1;
|
|
eil->_priv->dirty = TRUE;
|
|
if(eil->_priv->icon_list) {
|
|
e_icon_list_clear (eil);
|
|
g_array_free(eil->_priv->icon_list, TRUE);
|
|
}
|
|
eil->_priv->icon_list = NULL;
|
|
|
|
if (eil->_priv->timer_tag != 0) {
|
|
gtk_timeout_remove (eil->_priv->timer_tag);
|
|
eil->_priv->timer_tag = 0;
|
|
}
|
|
|
|
if (GTK_OBJECT_CLASS (parent_class)->destroy)
|
|
(*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
|
|
}
|
|
|
|
static void
|
|
eil_finalize (GtkObject *object)
|
|
{
|
|
Eil *eil;
|
|
|
|
eil = EIL (object);
|
|
|
|
g_free (eil->_priv);
|
|
eil->_priv = NULL;
|
|
|
|
if (GTK_OBJECT_CLASS (parent_class)->finalize)
|
|
(*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
|
|
}
|
|
|
|
|
|
static void
|
|
select_icon (Eil *eil, int pos, GdkEvent *event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
gint i;
|
|
Icon *icon;
|
|
|
|
priv = eil->_priv;
|
|
|
|
switch (priv->selection_mode) {
|
|
case GTK_SELECTION_SINGLE:
|
|
case GTK_SELECTION_BROWSE:
|
|
i = 0;
|
|
|
|
for (i = 0; i < priv->icon_list->len; i++) {
|
|
icon = g_array_index (priv->icon_list, Icon*, i);
|
|
|
|
if (i != pos && icon->selected)
|
|
emit_select (eil, FALSE, i, event);
|
|
}
|
|
|
|
emit_select (eil, TRUE, pos, event);
|
|
break;
|
|
|
|
case GTK_SELECTION_MULTIPLE:
|
|
case GTK_SELECTION_EXTENDED:
|
|
emit_select (eil, TRUE, pos, event);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_select_icon:
|
|
* @eil: An icon list.
|
|
* @pos: Index of the icon to be selected.
|
|
*
|
|
* Selects the icon at the index specified by @pos.
|
|
*/
|
|
void
|
|
e_icon_list_select_icon (EIconList *eil, int pos)
|
|
{
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
|
|
|
|
select_icon (eil, pos, NULL);
|
|
}
|
|
|
|
static void
|
|
unselect_icon (Eil *eil, int pos, GdkEvent *event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
priv = eil->_priv;
|
|
|
|
switch (priv->selection_mode) {
|
|
case GTK_SELECTION_SINGLE:
|
|
case GTK_SELECTION_BROWSE:
|
|
case GTK_SELECTION_MULTIPLE:
|
|
case GTK_SELECTION_EXTENDED:
|
|
emit_select (eil, FALSE, pos, event);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_unselect_icon:
|
|
* @eil: An icon list.
|
|
* @pos: Index of the icon to be unselected.
|
|
*
|
|
* Unselects the icon at the index specified by @pos.
|
|
*/
|
|
void
|
|
e_icon_list_unselect_icon (EIconList *eil, int pos)
|
|
{
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
|
|
|
|
unselect_icon (eil, pos, NULL);
|
|
}
|
|
|
|
static void
|
|
eil_size_request (GtkWidget *widget, GtkRequisition *requisition)
|
|
{
|
|
requisition->width = 1;
|
|
requisition->height = 1;
|
|
}
|
|
|
|
static void
|
|
eil_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
|
|
{
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
|
|
eil = EIL (widget);
|
|
priv = eil->_priv;
|
|
|
|
if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
|
|
(* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
|
|
|
|
if (priv->frozen)
|
|
return;
|
|
|
|
eil_layout_all_icons (eil);
|
|
}
|
|
|
|
static void
|
|
eil_realize (GtkWidget *widget)
|
|
{
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
GtkStyle *style;
|
|
|
|
eil = EIL (widget);
|
|
priv = eil->_priv;
|
|
|
|
priv->frozen++;
|
|
|
|
if (GTK_WIDGET_CLASS (parent_class)->realize)
|
|
(* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
|
|
|
|
priv->frozen--;
|
|
|
|
/* Change the style to use the base color as the background */
|
|
|
|
style = gtk_style_copy (gtk_widget_get_style (widget));
|
|
style->bg[GTK_STATE_NORMAL] = style->base[GTK_STATE_NORMAL];
|
|
gtk_widget_set_style (widget, style);
|
|
|
|
gdk_window_set_background (GTK_LAYOUT (eil)->bin_window,
|
|
&widget->style->bg[GTK_STATE_NORMAL]);
|
|
|
|
if (priv->frozen)
|
|
return;
|
|
|
|
if (priv->dirty) {
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
}
|
|
}
|
|
|
|
static void
|
|
real_select_icon (Eil *eil, gint num, GdkEvent *event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
Icon *icon;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (num >= 0 && num < eil->_priv->icons);
|
|
|
|
priv = eil->_priv;
|
|
|
|
icon = g_array_index (priv->icon_list, Icon*, num);
|
|
|
|
if (icon->selected)
|
|
return;
|
|
|
|
icon->selected = TRUE;
|
|
gnome_icon_text_item_select (icon->text, TRUE);
|
|
priv->selection = g_list_append (priv->selection, GINT_TO_POINTER (num));
|
|
}
|
|
|
|
static void
|
|
real_unselect_icon (Eil *eil, gint num, GdkEvent *event)
|
|
{
|
|
EIconListPrivate *priv;
|
|
Icon *icon;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (num >= 0 && num < eil->_priv->icons);
|
|
|
|
priv = eil->_priv;
|
|
|
|
icon = g_array_index (priv->icon_list, Icon*, num);
|
|
|
|
if (!icon->selected)
|
|
return;
|
|
|
|
icon->selected = FALSE;
|
|
gnome_icon_text_item_select (icon->text, FALSE);
|
|
priv->selection = g_list_remove (priv->selection, GINT_TO_POINTER (num));
|
|
}
|
|
|
|
/* Saves the selection of the icon list to temporary storage */
|
|
static void
|
|
store_temp_selection (Eil *eil)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int i;
|
|
Icon *icon;
|
|
|
|
priv = eil->_priv;
|
|
|
|
for (i = 0; i < priv->icon_list->len; i++) {
|
|
icon = g_array_index(priv->icon_list, Icon*, i);
|
|
|
|
icon->tmp_selected = icon->selected;
|
|
}
|
|
}
|
|
|
|
#define gray50_width 2
|
|
#define gray50_height 2
|
|
static const char gray50_bits[] = {
|
|
0x02, 0x01, };
|
|
|
|
/* Button press handler for the icon list */
|
|
static gint
|
|
eil_button_press (GtkWidget *widget, GdkEventButton *event)
|
|
{
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
int only_one;
|
|
GdkBitmap *stipple;
|
|
double tx, ty;
|
|
|
|
eil = EIL (widget);
|
|
priv = eil->_priv;
|
|
|
|
/* Invoke the canvas event handler and see if an item picks up the event */
|
|
|
|
if ((* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event))
|
|
return TRUE;
|
|
|
|
if (!(event->type == GDK_BUTTON_PRESS
|
|
&& event->button == 1
|
|
&& priv->selection_mode != GTK_SELECTION_BROWSE))
|
|
return FALSE;
|
|
|
|
only_one = priv->selection_mode == GTK_SELECTION_SINGLE;
|
|
|
|
if (only_one || (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == 0)
|
|
eil_unselect_all (eil, NULL, NULL);
|
|
|
|
if (only_one)
|
|
return TRUE;
|
|
|
|
if (priv->selecting)
|
|
return FALSE;
|
|
|
|
gnome_canvas_window_to_world (GNOME_CANVAS (eil), event->x, event->y, &tx, &ty);
|
|
priv->sel_start_x = tx;
|
|
priv->sel_start_y = ty;
|
|
priv->sel_state = event->state;
|
|
priv->selecting = TRUE;
|
|
|
|
store_temp_selection (eil);
|
|
|
|
stipple = gdk_bitmap_create_from_data (NULL, gray50_bits, gray50_width, gray50_height);
|
|
priv->sel_rect = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (eil)),
|
|
gnome_canvas_rect_get_type (),
|
|
"x1", tx,
|
|
"y1", ty,
|
|
"x2", tx,
|
|
"y2", ty,
|
|
"outline_color", "black",
|
|
"width_pixels", 1,
|
|
"outline_stipple", stipple,
|
|
NULL);
|
|
gdk_bitmap_unref (stipple);
|
|
|
|
gnome_canvas_item_grab (priv->sel_rect, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
|
|
NULL, event->time);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Returns whether the specified icon is at least partially inside the specified
|
|
* rectangle.
|
|
*/
|
|
static int
|
|
icon_is_in_area (Icon *icon, int x1, int y1, int x2, int y2)
|
|
{
|
|
double ix1, iy1, ix2, iy2;
|
|
|
|
if (x1 == x2 && y1 == y2)
|
|
return FALSE;
|
|
|
|
gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (icon->image), &ix1, &iy1, &ix2, &iy2);
|
|
|
|
if (ix1 <= x2 && iy1 <= y2 && ix2 >= x1 && iy2 >= y1)
|
|
return TRUE;
|
|
|
|
gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (icon->text), &ix1, &iy1, &ix2, &iy2);
|
|
|
|
if (ix1 <= x2 && iy1 <= y2 && ix2 >= x1 && iy2 >= y1)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Updates the rubberband selection to the specified point */
|
|
static void
|
|
update_drag_selection (Eil *eil, int x, int y)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int x1, x2, y1, y2;
|
|
int i;
|
|
Icon *icon;
|
|
int additive, invert;
|
|
|
|
priv = eil->_priv;
|
|
|
|
/* Update rubberband */
|
|
|
|
if (priv->sel_start_x < x) {
|
|
x1 = priv->sel_start_x;
|
|
x2 = x;
|
|
} else {
|
|
x1 = x;
|
|
x2 = priv->sel_start_x;
|
|
}
|
|
|
|
if (priv->sel_start_y < y) {
|
|
y1 = priv->sel_start_y;
|
|
y2 = y;
|
|
} else {
|
|
y1 = y;
|
|
y2 = priv->sel_start_y;
|
|
}
|
|
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
|
|
if (y1 < 0)
|
|
y1 = 0;
|
|
|
|
if (x2 >= GTK_WIDGET (eil)->allocation.width)
|
|
x2 = GTK_WIDGET (eil)->allocation.width - 1;
|
|
|
|
if (y2 >= priv->total_height)
|
|
y2 = priv->total_height - 1;
|
|
|
|
gnome_canvas_item_set (priv->sel_rect,
|
|
"x1", (double) x1,
|
|
"y1", (double) y1,
|
|
"x2", (double) x2,
|
|
"y2", (double) y2,
|
|
NULL);
|
|
|
|
/* Select or unselect icons as appropriate */
|
|
|
|
additive = priv->sel_state & GDK_SHIFT_MASK;
|
|
invert = priv->sel_state & GDK_CONTROL_MASK;
|
|
|
|
for (i = 0; i < priv->icon_list->len; i++) {
|
|
icon = g_array_index(priv->icon_list, Icon*, i);
|
|
|
|
if (icon_is_in_area (icon, x1, y1, x2, y2)) {
|
|
if (invert) {
|
|
if (icon->selected == icon->tmp_selected)
|
|
emit_select (eil, !icon->selected, i, NULL);
|
|
} else if (additive) {
|
|
if (!icon->selected)
|
|
emit_select (eil, TRUE, i, NULL);
|
|
} else {
|
|
if (!icon->selected)
|
|
emit_select (eil, TRUE, i, NULL);
|
|
}
|
|
} else if (icon->selected != icon->tmp_selected)
|
|
emit_select (eil, icon->tmp_selected, i, NULL);
|
|
}
|
|
}
|
|
|
|
/* Button release handler for the icon list */
|
|
static gint
|
|
eil_button_release (GtkWidget *widget, GdkEventButton *event)
|
|
{
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
double x, y;
|
|
|
|
eil = EIL (widget);
|
|
priv = eil->_priv;
|
|
|
|
if (!priv->selecting)
|
|
return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
|
|
|
|
if (event->button != 1)
|
|
return FALSE;
|
|
|
|
gnome_canvas_window_to_world (GNOME_CANVAS (eil), event->x, event->y, &x, &y);
|
|
update_drag_selection (eil, x, y);
|
|
gnome_canvas_item_ungrab (priv->sel_rect, event->time);
|
|
|
|
gtk_object_destroy (GTK_OBJECT (priv->sel_rect));
|
|
priv->sel_rect = NULL;
|
|
priv->selecting = FALSE;
|
|
|
|
if (priv->timer_tag != 0) {
|
|
gtk_timeout_remove (priv->timer_tag);
|
|
priv->timer_tag = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Autoscroll timeout handler for the icon list */
|
|
static gint
|
|
scroll_timeout (gpointer data)
|
|
{
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
GtkAdjustment *adj;
|
|
double x, y;
|
|
int value;
|
|
|
|
eil = data;
|
|
priv = eil->_priv;
|
|
|
|
GDK_THREADS_ENTER ();
|
|
|
|
adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
|
|
|
|
value = adj->value + priv->value_diff;
|
|
if (value > adj->upper - adj->page_size)
|
|
value = adj->upper - adj->page_size;
|
|
|
|
gtk_adjustment_set_value (adj, value);
|
|
|
|
gnome_canvas_window_to_world (GNOME_CANVAS (eil),
|
|
priv->event_last_x, priv->event_last_y,
|
|
&x, &y);
|
|
update_drag_selection (eil, x, y);
|
|
|
|
GDK_THREADS_LEAVE();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Motion event handler for the icon list */
|
|
static gint
|
|
eil_motion_notify (GtkWidget *widget, GdkEventMotion *event)
|
|
{
|
|
Eil *eil;
|
|
EIconListPrivate *priv;
|
|
double x, y;
|
|
|
|
eil = EIL (widget);
|
|
priv = eil->_priv;
|
|
|
|
if (!priv->selecting)
|
|
return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
|
|
|
|
gnome_canvas_window_to_world (GNOME_CANVAS (eil), event->x, event->y, &x, &y);
|
|
update_drag_selection (eil, x, y);
|
|
|
|
/* If we are out of bounds, schedule a timeout that will do the scrolling */
|
|
|
|
if (event->y < 0 || event->y > widget->allocation.height) {
|
|
if (priv->timer_tag == 0)
|
|
priv->timer_tag = gtk_timeout_add (SCROLL_TIMEOUT, scroll_timeout, eil);
|
|
|
|
if (event->y < 0)
|
|
priv->value_diff = event->y;
|
|
else
|
|
priv->value_diff = event->y - widget->allocation.height;
|
|
|
|
priv->event_last_x = event->x;
|
|
priv->event_last_y = event->y;
|
|
|
|
/* Make the steppings be relative to the mouse distance from the
|
|
* canvas. Also notice the timeout above is small to give a
|
|
* more smooth movement.
|
|
*/
|
|
priv->value_diff /= 5;
|
|
} else if (priv->timer_tag != 0) {
|
|
gtk_timeout_remove (priv->timer_tag);
|
|
priv->timer_tag = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef gboolean (*xGtkSignal_BOOL__INT_POINTER) (GtkObject * object,
|
|
gint arg1,
|
|
gpointer arg2,
|
|
gpointer user_data);
|
|
static void
|
|
xgtk_marshal_BOOL__INT_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data,
|
|
GtkArg *args)
|
|
{
|
|
xGtkSignal_BOOL__INT_POINTER rfunc;
|
|
gboolean *return_val;
|
|
|
|
return_val = GTK_RETLOC_BOOL (args[2]);
|
|
rfunc = (xGtkSignal_BOOL__INT_POINTER) func;
|
|
*return_val = (*rfunc) (object,
|
|
GTK_VALUE_INT (args[0]),
|
|
GTK_VALUE_POINTER (args[1]),
|
|
func_data);
|
|
}
|
|
|
|
static void
|
|
eil_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
|
|
{
|
|
EIconList *eil;
|
|
|
|
eil = E_ICON_LIST (object);
|
|
|
|
switch (arg_id) {
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
eil_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
|
|
{
|
|
EIconList *eil;
|
|
EIconListPrivate *priv;
|
|
|
|
eil = E_ICON_LIST (object);
|
|
priv = eil->_priv;
|
|
|
|
switch (arg_id) {
|
|
default:
|
|
arg->type = GTK_TYPE_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
eil_class_init (EilClass *eil_class)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
GtkLayoutClass *layout_class;
|
|
GnomeCanvasClass *canvas_class;
|
|
|
|
object_class = (GtkObjectClass *) eil_class;
|
|
widget_class = (GtkWidgetClass *) eil_class;
|
|
layout_class = (GtkLayoutClass *) eil_class;
|
|
canvas_class = (GnomeCanvasClass *) eil_class;
|
|
|
|
parent_class = gtk_type_class (gnome_canvas_get_type ());
|
|
|
|
eil_signals[SELECT_ICON] =
|
|
gtk_signal_new (
|
|
"select_icon",
|
|
GTK_RUN_FIRST,
|
|
object_class->type,
|
|
GTK_SIGNAL_OFFSET (EIconListClass, select_icon),
|
|
gtk_marshal_NONE__INT_POINTER,
|
|
GTK_TYPE_NONE, 2,
|
|
GTK_TYPE_INT,
|
|
GTK_TYPE_GDK_EVENT);
|
|
|
|
eil_signals[UNSELECT_ICON] =
|
|
gtk_signal_new (
|
|
"unselect_icon",
|
|
GTK_RUN_FIRST,
|
|
object_class->type,
|
|
GTK_SIGNAL_OFFSET (EIconListClass, unselect_icon),
|
|
gtk_marshal_NONE__INT_POINTER,
|
|
GTK_TYPE_NONE, 2,
|
|
GTK_TYPE_INT,
|
|
GTK_TYPE_GDK_EVENT);
|
|
|
|
eil_signals[TEXT_CHANGED] =
|
|
gtk_signal_new (
|
|
"text_changed",
|
|
GTK_RUN_LAST,
|
|
object_class->type,
|
|
GTK_SIGNAL_OFFSET (EIconListClass, text_changed),
|
|
xgtk_marshal_BOOL__INT_POINTER,
|
|
GTK_TYPE_BOOL, 2,
|
|
GTK_TYPE_INT,
|
|
GTK_TYPE_POINTER);
|
|
|
|
gtk_object_class_add_signals (object_class, eil_signals, LAST_SIGNAL);
|
|
|
|
object_class->destroy = eil_destroy;
|
|
object_class->finalize = eil_finalize;
|
|
object_class->set_arg = eil_set_arg;
|
|
object_class->get_arg = eil_get_arg;
|
|
|
|
widget_class->size_request = eil_size_request;
|
|
widget_class->size_allocate = eil_size_allocate;
|
|
widget_class->realize = eil_realize;
|
|
widget_class->button_press_event = eil_button_press;
|
|
widget_class->button_release_event = eil_button_release;
|
|
widget_class->motion_notify_event = eil_motion_notify;
|
|
|
|
eil_class->select_icon = real_select_icon;
|
|
eil_class->unselect_icon = real_unselect_icon;
|
|
}
|
|
|
|
static void
|
|
eil_init (Eil *eil)
|
|
{
|
|
eil->_priv = g_new0 (EIconListPrivate, 1);
|
|
|
|
eil->_priv->icon_list = g_array_new(FALSE, FALSE, sizeof(gpointer));
|
|
eil->_priv->row_spacing = DEFAULT_ROW_SPACING;
|
|
eil->_priv->col_spacing = DEFAULT_COL_SPACING;
|
|
eil->_priv->text_spacing = DEFAULT_TEXT_SPACING;
|
|
eil->_priv->icon_border = DEFAULT_ICON_BORDER;
|
|
eil->_priv->separators = g_strdup (" ");
|
|
|
|
eil->_priv->selection_mode = GTK_SELECTION_SINGLE;
|
|
eil->_priv->dirty = TRUE;
|
|
|
|
gnome_canvas_set_scroll_region (GNOME_CANVAS (eil), 0.0, 0.0, 1000000.0, 1000000.0);
|
|
gnome_canvas_scroll_to (GNOME_CANVAS (eil), 0, 0);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_get_type:
|
|
*
|
|
* Registers the &EIconList class if necessary, and returns the type ID
|
|
* associated to it.
|
|
*
|
|
* Returns: The type ID of the &EIconList class.
|
|
*/
|
|
guint
|
|
e_icon_list_get_type (void)
|
|
{
|
|
static guint eil_type = 0;
|
|
|
|
if (!eil_type) {
|
|
GtkTypeInfo eil_info = {
|
|
"EIconList",
|
|
sizeof (EIconList),
|
|
sizeof (EIconListClass),
|
|
(GtkClassInitFunc) eil_class_init,
|
|
(GtkObjectInitFunc) eil_init,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
eil_type = gtk_type_unique (gnome_canvas_get_type (),
|
|
&eil_info);
|
|
}
|
|
|
|
return eil_type;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_icon_width:
|
|
* @eil: An icon list.
|
|
* @w: New width for the icon columns.
|
|
*
|
|
* Sets the amount of horizontal space allocated to the icons, i.e. the column
|
|
* width of the icon list.
|
|
*/
|
|
void
|
|
e_icon_list_set_icon_width (EIconList *eil, int w)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
priv = eil->_priv;
|
|
|
|
priv->icon_width = w;
|
|
|
|
if (priv->frozen) {
|
|
priv->dirty = TRUE;
|
|
return;
|
|
}
|
|
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
}
|
|
|
|
|
|
/**
|
|
* e_icon_list_construct:
|
|
* @eil: An icon list.
|
|
* @icon_width: Width for the icon columns.
|
|
* @flags: A combination of %E_ICON_LIST_IS_EDITABLE and %E_ICON_LIST_STATIC_TEXT.
|
|
*
|
|
* Constructor for the icon list, to be used by derived classes.
|
|
**/
|
|
void
|
|
e_icon_list_construct (EIconList *eil, guint icon_width, int flags)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
priv = eil->_priv;
|
|
|
|
e_icon_list_set_icon_width (eil, icon_width);
|
|
priv->is_editable = (flags & E_ICON_LIST_IS_EDITABLE) != 0;
|
|
priv->static_text = (flags & E_ICON_LIST_STATIC_TEXT) != 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* e_icon_list_new: [constructor]
|
|
* @icon_width: Width for the icon columns.
|
|
* @flags: A combination of %E_ICON_LIST_IS_EDITABLE and %E_ICON_LIST_STATIC_TEXT.
|
|
*
|
|
* Creates a new icon list widget. The icon columns are allocated a width of
|
|
* @icon_width pixels. Icon captions will be word-wrapped to this width as
|
|
* well.
|
|
*
|
|
* If @flags has the %E_ICON_LIST_IS_EDITABLE flag set, then the user will be
|
|
* able to edit the text in the icon captions, and the "text_changed" signal
|
|
* will be emitted when an icon's text is changed.
|
|
*
|
|
* If @flags has the %E_ICON_LIST_STATIC_TEXT flags set, then the text
|
|
* for the icon captions will not be copied inside the icon list; it will only
|
|
* store the pointers to the original text strings specified by the application.
|
|
* This is intended to save memory. If this flag is not set, then the text
|
|
* strings will be copied and managed internally.
|
|
*
|
|
* Returns: a newly-created icon list widget
|
|
*/
|
|
GtkWidget *
|
|
e_icon_list_new (guint icon_width, int flags)
|
|
{
|
|
Eil *eil;
|
|
|
|
gtk_widget_push_colormap (gdk_rgb_get_cmap ());
|
|
eil = EIL (gtk_type_new (e_icon_list_get_type ()));
|
|
gtk_widget_pop_colormap ();
|
|
|
|
e_icon_list_construct (eil, icon_width, flags);
|
|
|
|
return GTK_WIDGET (eil);
|
|
}
|
|
|
|
|
|
/**
|
|
* e_icon_list_freeze:
|
|
* @eil: An icon list.
|
|
*
|
|
* Freezes an icon list so that any changes made to it will not be
|
|
* reflected on the screen until it is thawed with e_icon_list_thaw().
|
|
* It is recommended to freeze the icon list before inserting or deleting
|
|
* many icons, for example, so that the layout process will only be executed
|
|
* once, when the icon list is finally thawed.
|
|
*
|
|
* You can call this function multiple times, but it must be balanced with the
|
|
* same number of calls to e_icon_list_thaw() before the changes will take
|
|
* effect.
|
|
*/
|
|
void
|
|
e_icon_list_freeze (EIconList *eil)
|
|
{
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
eil->_priv->frozen++;
|
|
|
|
/* We hide the root so that the user will not see any changes while the
|
|
* icon list is doing stuff.
|
|
*/
|
|
|
|
if (eil->_priv->frozen == 1)
|
|
gnome_canvas_item_hide (GNOME_CANVAS (eil)->root);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_thaw:
|
|
* @eil: An icon list.
|
|
*
|
|
* Thaws the icon list and performs any pending layout operations. This
|
|
* is to be used in conjunction with e_icon_list_freeze().
|
|
*/
|
|
void
|
|
e_icon_list_thaw (EIconList *eil)
|
|
{
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (eil->_priv->frozen > 0);
|
|
|
|
eil->_priv->frozen--;
|
|
|
|
if (eil->_priv->dirty) {
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
}
|
|
|
|
if (eil->_priv->frozen == 0)
|
|
gnome_canvas_item_show (GNOME_CANVAS (eil)->root);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_selection_mode
|
|
* @eil: An icon list.
|
|
* @mode: New selection mode.
|
|
*
|
|
* Sets the selection mode for an icon list. The %GTK_SELECTION_MULTIPLE and
|
|
* %GTK_SELECTION_EXTENDED modes are considered equivalent.
|
|
*/
|
|
void
|
|
e_icon_list_set_selection_mode (EIconList *eil, GtkSelectionMode mode)
|
|
{
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
eil->_priv->selection_mode = mode;
|
|
eil->_priv->last_selected_idx = -1;
|
|
eil->_priv->last_selected_icon = NULL;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_icon_data_full:
|
|
* @eil: An icon list.
|
|
* @pos: Index of an icon.
|
|
* @data: User data to set on the icon.
|
|
* @destroy: Destroy notification handler for the icon.
|
|
*
|
|
* Associates the @data pointer to the icon at the index specified by @pos. The
|
|
* @destroy argument points to a function that will be called when the icon is
|
|
* destroyed, or NULL if no function is to be called when this happens.
|
|
*/
|
|
void
|
|
e_icon_list_set_icon_data_full (EIconList *eil,
|
|
int pos, gpointer data,
|
|
GtkDestroyNotify destroy)
|
|
{
|
|
Icon *icon;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
|
|
|
|
icon = g_array_index (eil->_priv->icon_list, Icon*, pos);
|
|
icon->data = data;
|
|
icon->destroy = destroy;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_get_icon_data:
|
|
* @eil: An icon list.
|
|
* @pos: Index of an icon.
|
|
* @data: User data to set on the icon.
|
|
*
|
|
* Associates the @data pointer to the icon at the index specified by @pos.
|
|
*/
|
|
void
|
|
e_icon_list_set_icon_data (EIconList *eil, int pos, gpointer data)
|
|
{
|
|
e_icon_list_set_icon_data_full (eil, pos, data, NULL);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_get_icon_data:
|
|
* @eil: An icon list.
|
|
* @pos: Index of an icon.
|
|
*
|
|
* Returns the user data pointer associated to the icon at the index specified
|
|
* by @pos.
|
|
*/
|
|
gpointer
|
|
e_icon_list_get_icon_data (EIconList *eil, int pos)
|
|
{
|
|
Icon *icon;
|
|
|
|
g_return_val_if_fail (eil != NULL, NULL);
|
|
g_return_val_if_fail (IS_EIL (eil), NULL);
|
|
g_return_val_if_fail (pos >= 0 && pos < eil->_priv->icons, NULL);
|
|
|
|
icon = g_array_index (eil->_priv->icon_list, Icon*, pos);
|
|
return icon->data;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_find_icon_from_data:
|
|
* @eil: An icon list.
|
|
* @data: Data pointer associated to an icon.
|
|
*
|
|
* Returns the index of the icon whose user data has been set to @data,
|
|
* or -1 if no icon has this data associated to it.
|
|
*/
|
|
int
|
|
e_icon_list_find_icon_from_data (EIconList *eil, gpointer data)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int n;
|
|
Icon *icon;
|
|
|
|
g_return_val_if_fail (eil != NULL, -1);
|
|
g_return_val_if_fail (IS_EIL (eil), -1);
|
|
|
|
priv = eil->_priv;
|
|
|
|
for (n = 0; n < priv->icon_list->len; n++) {
|
|
icon = g_array_index(priv->icon_list, Icon*, n);
|
|
if (icon->data == data)
|
|
return n;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Sets an integer value in the private structure of the icon list, and updates it */
|
|
static void
|
|
set_value (EIconList *eil, EIconListPrivate *priv, int *dest, int val)
|
|
{
|
|
if (val == *dest)
|
|
return;
|
|
|
|
*dest = val;
|
|
|
|
if (!priv->frozen) {
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
} else
|
|
priv->dirty = TRUE;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_row_spacing:
|
|
* @eil: An icon list.
|
|
* @pixels: Number of pixels for inter-row spacing.
|
|
*
|
|
* Sets the spacing to be used between rows of icons.
|
|
*/
|
|
void
|
|
e_icon_list_set_row_spacing (EIconList *eil, int pixels)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
priv = eil->_priv;
|
|
set_value (eil, priv, &priv->row_spacing, pixels);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_col_spacing:
|
|
* @eil: An icon list.
|
|
* @pixels: Number of pixels for inter-column spacing.
|
|
*
|
|
* Sets the spacing to be used between columns of icons.
|
|
*/
|
|
void
|
|
e_icon_list_set_col_spacing (EIconList *eil, int pixels)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
priv = eil->_priv;
|
|
set_value (eil, priv, &priv->col_spacing, pixels);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_text_spacing:
|
|
* @eil: An icon list.
|
|
* @pixels: Number of pixels between an icon's image and its caption.
|
|
*
|
|
* Sets the spacing to be used between an icon's image and its text caption.
|
|
*/
|
|
void
|
|
e_icon_list_set_text_spacing (EIconList *eil, int pixels)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
priv = eil->_priv;
|
|
set_value (eil, priv, &priv->text_spacing, pixels);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_icon_border:
|
|
* @eil: An icon list.
|
|
* @pixels: Number of border pixels to be used around an icon's image.
|
|
*
|
|
* Sets the width of the border to be displayed around an icon's image. This is
|
|
* currently not implemented.
|
|
*/
|
|
void
|
|
e_icon_list_set_icon_border (EIconList *eil, int pixels)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
|
|
priv = eil->_priv;
|
|
set_value (eil, priv, &priv->icon_border, pixels);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_set_separators:
|
|
* @eil: An icon list.
|
|
* @sep: String with characters to be used as word separators.
|
|
*
|
|
* Sets the characters that can be used as word separators when doing
|
|
* word-wrapping in the icon text captions.
|
|
*/
|
|
void
|
|
e_icon_list_set_separators (EIconList *eil, const char *sep)
|
|
{
|
|
EIconListPrivate *priv;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (sep != NULL);
|
|
|
|
priv = eil->_priv;
|
|
|
|
if (priv->separators)
|
|
g_free (priv->separators);
|
|
|
|
priv->separators = g_strdup (sep);
|
|
|
|
if (priv->frozen) {
|
|
priv->dirty = TRUE;
|
|
return;
|
|
}
|
|
|
|
eil_layout_all_icons (eil);
|
|
eil_scrollbar_adjust (eil);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_moveto:
|
|
* @eil: An icon list.
|
|
* @pos: Index of an icon.
|
|
* @yalign: Vertical alignment of the icon.
|
|
*
|
|
* Makes the icon whose index is @pos be visible on the screen. The icon list
|
|
* gets scrolled so that the icon is visible. An alignment of 0.0 represents
|
|
* the top of the visible part of the icon list, and 1.0 represents the bottom.
|
|
* An icon can be centered on the icon list.
|
|
*/
|
|
void
|
|
e_icon_list_moveto (EIconList *eil, int pos, double yalign)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GtkAdjustment *adj;
|
|
IconLine *il;
|
|
GList *l;
|
|
int i, y, uh, line;
|
|
|
|
g_return_if_fail (eil != NULL);
|
|
g_return_if_fail (IS_EIL (eil));
|
|
g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
|
|
g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
|
|
|
|
priv = eil->_priv;
|
|
|
|
g_return_if_fail (priv->lines != NULL);
|
|
|
|
line = pos / eil_get_items_per_line (eil);
|
|
|
|
y = 0;
|
|
for (i = 0, l = priv->lines; l && i < line; l = l->next, i++) {
|
|
il = l->data;
|
|
y += icon_line_height (eil, il);
|
|
}
|
|
|
|
il = l->data;
|
|
|
|
uh = GTK_WIDGET (eil)->allocation.height - icon_line_height (eil,il);
|
|
adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
|
|
gtk_adjustment_set_value (adj, y - uh * yalign);
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_is_visible:
|
|
* @eil: An icon list.
|
|
* @pos: Index of an icon.
|
|
*
|
|
* Returns whether the icon at the index specified by @pos is visible. This
|
|
* will be %GTK_VISIBILITY_NONE if the icon is not visible at all,
|
|
* %GTK_VISIBILITY_PARTIAL if the icon is at least partially shown, and
|
|
* %GTK_VISIBILITY_FULL if the icon is fully visible.
|
|
*/
|
|
GtkVisibility
|
|
e_icon_list_icon_is_visible (EIconList *eil, int pos)
|
|
{
|
|
EIconListPrivate *priv;
|
|
GtkAdjustment *adj;
|
|
IconLine *il;
|
|
GList *l;
|
|
int line, y1, y2, i;
|
|
|
|
g_return_val_if_fail (eil != NULL, GTK_VISIBILITY_NONE);
|
|
g_return_val_if_fail (IS_EIL (eil), GTK_VISIBILITY_NONE);
|
|
g_return_val_if_fail (pos >= 0 && pos < eil->_priv->icons,
|
|
GTK_VISIBILITY_NONE);
|
|
|
|
priv = eil->_priv;
|
|
|
|
if (priv->lines == NULL)
|
|
return GTK_VISIBILITY_NONE;
|
|
|
|
line = pos / eil_get_items_per_line (eil);
|
|
y1 = 0;
|
|
for (i = 0, l = priv->lines; l && i < line; l = l->next, i++) {
|
|
il = l->data;
|
|
y1 += icon_line_height (eil, il);
|
|
}
|
|
|
|
y2 = y1 + icon_line_height (eil, (IconLine *) l->data);
|
|
|
|
adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
|
|
|
|
if (y2 < adj->value)
|
|
return GTK_VISIBILITY_NONE;
|
|
|
|
if (y1 > adj->value + GTK_WIDGET (eil)->allocation.height)
|
|
return GTK_VISIBILITY_NONE;
|
|
|
|
if (y2 <= adj->value + GTK_WIDGET (eil)->allocation.height &&
|
|
y1 >= adj->value)
|
|
return GTK_VISIBILITY_FULL;
|
|
|
|
return GTK_VISIBILITY_PARTIAL;
|
|
}
|
|
|
|
/**
|
|
* e_icon_list_get_icon_at:
|
|
* @eil: An icon list.
|
|
* @x: X position in the icon list window.
|
|
* @y: Y position in the icon list window.
|
|
*
|
|
* Returns the index of the icon that is under the specified coordinates, which
|
|
* are relative to the icon list's window. If there is no icon in that
|
|
* position, -1 is returned.
|
|
*/
|
|
int
|
|
e_icon_list_get_icon_at (EIconList *eil, int x, int y)
|
|
{
|
|
EIconListPrivate *priv;
|
|
double wx, wy;
|
|
double dx, dy;
|
|
int cx, cy;
|
|
int n;
|
|
GnomeCanvasItem *item;
|
|
double dist;
|
|
|
|
g_return_val_if_fail (eil != NULL, -1);
|
|
g_return_val_if_fail (IS_EIL (eil), -1);
|
|
|
|
priv = eil->_priv;
|
|
|
|
dx = x;
|
|
dy = y;
|
|
|
|
gnome_canvas_window_to_world (GNOME_CANVAS (eil), dx, dy, &wx, &wy);
|
|
gnome_canvas_w2c (GNOME_CANVAS (eil), wx, wy, &cx, &cy);
|
|
|
|
for (n = 0; n < priv->icon_list->len; n++) {
|
|
Icon *icon = g_array_index(priv->icon_list, Icon*, n);
|
|
GnomeCanvasItem *image = GNOME_CANVAS_ITEM (icon->image);
|
|
GnomeCanvasItem *text = GNOME_CANVAS_ITEM (icon->text);
|
|
|
|
if (wx >= image->x1 && wx <= image->x2 && wy >= image->y1 && wy <= image->y2) {
|
|
dist = (* GNOME_CANVAS_ITEM_CLASS (GTK_OBJECT (image)->klass)->point) (
|
|
image,
|
|
wx, wy,
|
|
cx, cy,
|
|
&item);
|
|
|
|
if ((int) (dist * GNOME_CANVAS (eil)->pixels_per_unit + 0.5)
|
|
<= GNOME_CANVAS (eil)->close_enough)
|
|
return n;
|
|
}
|
|
|
|
if (wx >= text->x1 && wx <= text->x2 && wy >= text->y1 && wy <= text->y2) {
|
|
dist = (* GNOME_CANVAS_ITEM_CLASS (GTK_OBJECT (text)->klass)->point) (
|
|
text,
|
|
wx, wy,
|
|
cx, cy,
|
|
&item);
|
|
|
|
if ((int) (dist * GNOME_CANVAS (eil)->pixels_per_unit + 0.5)
|
|
<= GNOME_CANVAS (eil)->close_enough)
|
|
return n;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* e_icon_list_get_num_icons:
|
|
* @eil: An icon list.
|
|
*
|
|
* Returns the number of icons in the icon list.
|
|
*/
|
|
guint
|
|
e_icon_list_get_num_icons (EIconList *eil)
|
|
{
|
|
g_return_val_if_fail (E_IS_ICON_LIST (eil), 0);
|
|
|
|
return eil->_priv->icons;
|
|
}
|
|
|
|
|
|
/**
|
|
* e_icon_list_get_selection:
|
|
* @eil: An icon list.
|
|
*
|
|
* Returns a list of integers with the indices of the currently selected icons.
|
|
*/
|
|
GList *
|
|
e_icon_list_get_selection (EIconList *eil)
|
|
{
|
|
g_return_val_if_fail (E_IS_ICON_LIST (eil), NULL);
|
|
|
|
return eil->_priv->selection;
|
|
}
|
|
|
|
|
|
/**
|
|
* e_icon_list_get_selection:
|
|
* @eil: An icon list.
|
|
* @idx: Index of an @icon.
|
|
*
|
|
* Returns the filename of the icon with index @idx.
|
|
*/
|
|
gchar *
|
|
e_icon_list_get_icon_filename (EIconList *eil, int idx)
|
|
{
|
|
Icon *icon;
|
|
|
|
g_return_val_if_fail (eil != NULL, NULL);
|
|
g_return_val_if_fail (IS_EIL (eil), NULL);
|
|
g_return_val_if_fail (idx >= 0 && idx < eil->_priv->icons, NULL);
|
|
|
|
icon = g_array_index (eil->_priv->icon_list, Icon*, idx);
|
|
return icon->icon_filename;
|
|
}
|
|
|
|
|
|
/**
|
|
* e_icon_list_find_icon_from_filename:
|
|
* @eil: An icon list.
|
|
* @filename: Filename of an icon.
|
|
*
|
|
* Returns the index of the icon whose filename is @filename or -1 if
|
|
* there is no icon with this filename.
|
|
*/
|
|
int
|
|
e_icon_list_find_icon_from_filename (EIconList *eil,
|
|
const gchar *filename)
|
|
{
|
|
EIconListPrivate *priv;
|
|
int n;
|
|
Icon *icon;
|
|
|
|
g_return_val_if_fail (eil != NULL, -1);
|
|
g_return_val_if_fail (IS_EIL (eil), -1);
|
|
g_return_val_if_fail (filename != NULL, -1);
|
|
|
|
priv = eil->_priv;
|
|
|
|
for (n = 0; n < priv->icon_list->len; n++) {
|
|
icon = g_array_index(priv->icon_list, Icon*, n);
|
|
if (!strcmp (icon->icon_filename, filename))
|
|
return n;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|