1016 lines
29 KiB
C
1016 lines
29 KiB
C
/*
|
|
* @file libsexy/sexy-icon-entry.c Entry widget
|
|
*
|
|
* @Copyright (C) 2004-2006 Christian Hammond.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include <string.h>
|
|
#include <gtk/gtk.h>
|
|
#include "sexy-icon-entry.h"
|
|
|
|
#define ICON_MARGIN 2
|
|
#define MAX_ICONS 2
|
|
|
|
#define IS_VALID_ICON_ENTRY_POSITION( pos ) \
|
|
( ( pos ) == SEXY_ICON_ENTRY_PRIMARY \
|
|
|| ( pos ) == SEXY_ICON_ENTRY_SECONDARY )
|
|
|
|
typedef struct
|
|
{
|
|
GtkImage * icon;
|
|
gboolean highlight;
|
|
gboolean hovered;
|
|
GdkWindow * window;
|
|
} SexyIconInfo;
|
|
|
|
struct _SexyIconEntryPriv
|
|
{
|
|
SexyIconInfo icons[MAX_ICONS];
|
|
|
|
gulong icon_released_id;
|
|
};
|
|
|
|
enum
|
|
{
|
|
ICON_PRESSED,
|
|
ICON_RELEASED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static void sexy_icon_entry_class_init( SexyIconEntryClass *klass );
|
|
|
|
static void sexy_icon_entry_editable_init(
|
|
GtkEditableClass *iface );
|
|
|
|
static void sexy_icon_entry_init( SexyIconEntry *entry );
|
|
|
|
static void sexy_icon_entry_finalize( GObject *obj );
|
|
|
|
static void sexy_icon_entry_destroy( GtkObject *obj );
|
|
|
|
static void sexy_icon_entry_map( GtkWidget *widget );
|
|
|
|
static void sexy_icon_entry_unmap( GtkWidget *widget );
|
|
|
|
static void sexy_icon_entry_realize( GtkWidget *widget );
|
|
|
|
static void sexy_icon_entry_unrealize( GtkWidget *widget );
|
|
|
|
static void sexy_icon_entry_size_request(
|
|
GtkWidget *widget,
|
|
GtkRequisition *
|
|
requisition );
|
|
|
|
static void sexy_icon_entry_size_allocate(
|
|
GtkWidget *widget,
|
|
GtkAllocation *
|
|
allocation );
|
|
|
|
static gint sexy_icon_entry_expose( GtkWidget * widget,
|
|
GdkEventExpose *event );
|
|
|
|
static gint sexy_icon_entry_enter_notify(
|
|
GtkWidget * widget,
|
|
GdkEventCrossing *event );
|
|
|
|
static gint sexy_icon_entry_leave_notify(
|
|
GtkWidget * widget,
|
|
GdkEventCrossing *event );
|
|
|
|
static gint sexy_icon_entry_button_press( GtkWidget * widget,
|
|
GdkEventButton *event );
|
|
|
|
static gint sexy_icon_entry_button_release(
|
|
GtkWidget * widget,
|
|
GdkEventButton *event );
|
|
|
|
static GtkEntryClass *parent_class = NULL;
|
|
static guint signals[LAST_SIGNAL] = {0};
|
|
|
|
G_DEFINE_TYPE_EXTENDED( SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
|
|
0,
|
|
G_IMPLEMENT_INTERFACE( GTK_TYPE_EDITABLE,
|
|
sexy_icon_entry_editable_init ) );
|
|
|
|
static void
|
|
sexy_icon_entry_class_init( SexyIconEntryClass *klass )
|
|
{
|
|
GObjectClass * gobject_class;
|
|
GtkObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
|
|
parent_class = g_type_class_peek_parent( klass );
|
|
|
|
gobject_class = G_OBJECT_CLASS( klass );
|
|
object_class = GTK_OBJECT_CLASS( klass );
|
|
widget_class = GTK_WIDGET_CLASS( klass );
|
|
|
|
gobject_class->finalize = sexy_icon_entry_finalize;
|
|
|
|
object_class->destroy = sexy_icon_entry_destroy;
|
|
|
|
widget_class->map = sexy_icon_entry_map;
|
|
widget_class->unmap = sexy_icon_entry_unmap;
|
|
widget_class->realize = sexy_icon_entry_realize;
|
|
widget_class->unrealize = sexy_icon_entry_unrealize;
|
|
widget_class->size_request = sexy_icon_entry_size_request;
|
|
widget_class->size_allocate = sexy_icon_entry_size_allocate;
|
|
widget_class->expose_event = sexy_icon_entry_expose;
|
|
widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
|
|
widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
|
|
widget_class->button_press_event = sexy_icon_entry_button_press;
|
|
widget_class->button_release_event = sexy_icon_entry_button_release;
|
|
|
|
/**
|
|
* SexyIconEntry::icon-pressed:
|
|
* @entry: The entry on which the signal is emitted.
|
|
* @icon_pos: The position of the clicked icon.
|
|
* @button: The mouse button clicked.
|
|
*
|
|
* The ::icon-pressed signal is emitted when an icon is clicked.
|
|
*/
|
|
signals[ICON_PRESSED] =
|
|
g_signal_new( "icon_pressed",
|
|
G_TYPE_FROM_CLASS( gobject_class ),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_STRUCT_OFFSET( SexyIconEntryClass, icon_pressed ),
|
|
NULL, NULL,
|
|
gtk_marshal_VOID__INT_INT,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_INT,
|
|
G_TYPE_INT );
|
|
|
|
/**
|
|
* SexyIconEntry::icon-released:
|
|
* @entry: The entry on which the signal is emitted.
|
|
* @icon_pos: The position of the clicked icon.
|
|
* @button: The mouse button clicked.
|
|
*
|
|
* The ::icon-released signal is emitted on the button release from a
|
|
* mouse click.
|
|
*/
|
|
signals[ICON_RELEASED] =
|
|
g_signal_new( "icon_released",
|
|
G_TYPE_FROM_CLASS( gobject_class ),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_STRUCT_OFFSET( SexyIconEntryClass, icon_released ),
|
|
NULL, NULL,
|
|
gtk_marshal_VOID__INT_INT,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_INT,
|
|
G_TYPE_INT );
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_editable_init( GtkEditableClass *iface G_GNUC_UNUSED )
|
|
{}
|
|
|
|
static void
|
|
sexy_icon_entry_init( SexyIconEntry *entry )
|
|
{
|
|
entry->priv = g_new0( SexyIconEntryPriv, 1 );
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_finalize( GObject *obj )
|
|
{
|
|
SexyIconEntry *entry;
|
|
|
|
g_return_if_fail( obj != NULL );
|
|
g_return_if_fail( SEXY_IS_ICON_ENTRY( obj ) );
|
|
|
|
entry = SEXY_ICON_ENTRY( obj );
|
|
|
|
g_free( entry->priv );
|
|
|
|
if( G_OBJECT_CLASS( parent_class )->finalize )
|
|
G_OBJECT_CLASS( parent_class )->finalize( obj );
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_destroy( GtkObject *obj )
|
|
{
|
|
SexyIconEntry *entry;
|
|
|
|
entry = SEXY_ICON_ENTRY( obj );
|
|
|
|
sexy_icon_entry_set_icon( entry, SEXY_ICON_ENTRY_PRIMARY, NULL );
|
|
sexy_icon_entry_set_icon( entry, SEXY_ICON_ENTRY_SECONDARY, NULL );
|
|
|
|
if( GTK_OBJECT_CLASS( parent_class )->destroy )
|
|
GTK_OBJECT_CLASS( parent_class )->destroy( obj );
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_map( GtkWidget *widget )
|
|
{
|
|
if( GTK_WIDGET_REALIZED( widget ) && !GTK_WIDGET_MAPPED( widget ) )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
int i;
|
|
|
|
GTK_WIDGET_CLASS( parent_class )->map( widget );
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
if( entry->priv->icons[i].icon != NULL )
|
|
gdk_window_show( entry->priv->icons[i].window );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_unmap( GtkWidget *widget )
|
|
{
|
|
if( GTK_WIDGET_MAPPED( widget ) )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
int i;
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
if( entry->priv->icons[i].icon != NULL )
|
|
gdk_window_hide( entry->priv->icons[i].window );
|
|
}
|
|
|
|
GTK_WIDGET_CLASS( parent_class )->unmap( widget );
|
|
}
|
|
}
|
|
|
|
static gint
|
|
get_icon_width( SexyIconEntry * entry,
|
|
SexyIconEntryPosition icon_pos )
|
|
{
|
|
GtkRequisition requisition;
|
|
gint menu_icon_width;
|
|
gint width;
|
|
SexyIconInfo * icon_info = &entry->priv->icons[icon_pos];
|
|
|
|
if( icon_info->icon == NULL )
|
|
return 0;
|
|
|
|
gtk_widget_size_request( GTK_WIDGET( icon_info->icon ), &requisition );
|
|
gtk_icon_size_lookup( GTK_ICON_SIZE_MENU, &menu_icon_width, NULL );
|
|
|
|
width = MAX( requisition.width, menu_icon_width );
|
|
|
|
return width;
|
|
}
|
|
|
|
static void
|
|
get_borders( SexyIconEntry *entry,
|
|
gint * xborder,
|
|
gint * yborder )
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET( entry );
|
|
gint focus_width;
|
|
gboolean interior_focus;
|
|
|
|
gtk_widget_style_get( widget,
|
|
"interior-focus", &interior_focus,
|
|
"focus-line-width", &focus_width,
|
|
NULL );
|
|
|
|
if( gtk_entry_get_has_frame( GTK_ENTRY( entry ) ) )
|
|
{
|
|
*xborder = widget->style->xthickness;
|
|
*yborder = widget->style->ythickness;
|
|
}
|
|
else
|
|
{
|
|
*xborder = 0;
|
|
*yborder = 0;
|
|
}
|
|
|
|
if( !interior_focus )
|
|
{
|
|
*xborder += focus_width;
|
|
*yborder += focus_width;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_text_area_size( SexyIconEntry *entry,
|
|
GtkAllocation *alloc )
|
|
{
|
|
GtkWidget * widget = GTK_WIDGET( entry );
|
|
GtkRequisition requisition;
|
|
gint xborder, yborder;
|
|
|
|
gtk_widget_get_child_requisition( widget, &requisition );
|
|
get_borders( entry, &xborder, &yborder );
|
|
|
|
alloc->x = xborder;
|
|
alloc->y = yborder;
|
|
alloc->width = widget->allocation.width - xborder * 2;
|
|
alloc->height = requisition.height - yborder * 2;
|
|
}
|
|
|
|
static void
|
|
get_icon_allocation( SexyIconEntry * icon_entry,
|
|
gboolean left,
|
|
GtkAllocation *widget_alloc G_GNUC_UNUSED,
|
|
GtkAllocation * text_area_alloc,
|
|
GtkAllocation * allocation,
|
|
SexyIconEntryPosition * icon_pos )
|
|
{
|
|
gboolean rtl;
|
|
|
|
rtl = ( gtk_widget_get_direction( GTK_WIDGET( icon_entry ) ) ==
|
|
GTK_TEXT_DIR_RTL );
|
|
|
|
if( left )
|
|
*icon_pos =
|
|
( rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY );
|
|
else
|
|
*icon_pos =
|
|
( rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY );
|
|
|
|
allocation->y = text_area_alloc->y;
|
|
allocation->width = get_icon_width( icon_entry, *icon_pos );
|
|
allocation->height = text_area_alloc->height;
|
|
|
|
if( left )
|
|
allocation->x = text_area_alloc->x + ICON_MARGIN;
|
|
else
|
|
{
|
|
allocation->x = text_area_alloc->x + text_area_alloc->width -
|
|
allocation->width - ICON_MARGIN;
|
|
}
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_realize( GtkWidget *widget )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
GdkWindowAttr attributes;
|
|
gint attributes_mask;
|
|
int i;
|
|
|
|
GTK_WIDGET_CLASS( parent_class )->realize( widget );
|
|
|
|
attributes.x = 0;
|
|
attributes.y = 0;
|
|
attributes.width = 1;
|
|
attributes.height = 1;
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
attributes.visual = gtk_widget_get_visual( widget );
|
|
attributes.colormap = gtk_widget_get_colormap( widget );
|
|
attributes.event_mask = gtk_widget_get_events( widget );
|
|
attributes.event_mask |=
|
|
( GDK_EXPOSURE_MASK
|
|
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
|
|
| GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
|
|
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
SexyIconInfo *icon_info;
|
|
|
|
icon_info = &entry->priv->icons[i];
|
|
icon_info->window = gdk_window_new( widget->window, &attributes,
|
|
attributes_mask );
|
|
gdk_window_set_user_data( icon_info->window, widget );
|
|
|
|
gdk_window_set_background( icon_info->window,
|
|
&widget->style->base[GTK_WIDGET_STATE(
|
|
widget )] );
|
|
}
|
|
|
|
gtk_widget_queue_resize( widget );
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_unrealize( GtkWidget *widget )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
int i;
|
|
|
|
GTK_WIDGET_CLASS( parent_class )->unrealize( widget );
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
SexyIconInfo *icon_info = &entry->priv->icons[i];
|
|
|
|
gdk_window_destroy( icon_info->window );
|
|
icon_info->window = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_size_request( GtkWidget * widget,
|
|
GtkRequisition *requisition )
|
|
{
|
|
SexyIconEntry *entry;
|
|
gint icon_widths = 0;
|
|
int i;
|
|
|
|
entry = SEXY_ICON_ENTRY( widget );
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
int icon_width = get_icon_width( entry, i );
|
|
|
|
if( icon_width > 0 )
|
|
icon_widths += icon_width + ICON_MARGIN;
|
|
}
|
|
|
|
GTK_WIDGET_CLASS( parent_class )->size_request( widget, requisition );
|
|
|
|
if( icon_widths > requisition->width )
|
|
requisition->width += icon_widths;
|
|
}
|
|
|
|
static void
|
|
place_windows( SexyIconEntry *icon_entry,
|
|
GtkAllocation *widget_alloc )
|
|
{
|
|
SexyIconEntryPosition left_icon_pos;
|
|
SexyIconEntryPosition right_icon_pos;
|
|
GtkAllocation left_icon_alloc;
|
|
GtkAllocation right_icon_alloc;
|
|
GtkAllocation text_area_alloc;
|
|
|
|
get_text_area_size( icon_entry, &text_area_alloc );
|
|
get_icon_allocation( icon_entry, TRUE, widget_alloc, &text_area_alloc,
|
|
&left_icon_alloc, &left_icon_pos );
|
|
get_icon_allocation( icon_entry, FALSE, widget_alloc, &text_area_alloc,
|
|
&right_icon_alloc, &right_icon_pos );
|
|
|
|
if( left_icon_alloc.width > 0 )
|
|
{
|
|
text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
|
|
ICON_MARGIN;
|
|
}
|
|
|
|
if( right_icon_alloc.width > 0 )
|
|
text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
|
|
|
|
text_area_alloc.width -= text_area_alloc.x;
|
|
|
|
gdk_window_move_resize( icon_entry->priv->icons[left_icon_pos].window,
|
|
left_icon_alloc.x, left_icon_alloc.y,
|
|
left_icon_alloc.width, left_icon_alloc.height );
|
|
|
|
gdk_window_move_resize( icon_entry->priv->icons[right_icon_pos].window,
|
|
right_icon_alloc.x, right_icon_alloc.y,
|
|
right_icon_alloc.width, right_icon_alloc.height );
|
|
|
|
gdk_window_move_resize( GTK_ENTRY( icon_entry )->text_area,
|
|
text_area_alloc.x, text_area_alloc.y,
|
|
text_area_alloc.width, text_area_alloc.height );
|
|
}
|
|
|
|
static void
|
|
sexy_icon_entry_size_allocate( GtkWidget * widget,
|
|
GtkAllocation *allocation )
|
|
{
|
|
g_return_if_fail( SEXY_IS_ICON_ENTRY( widget ) );
|
|
g_return_if_fail( allocation != NULL );
|
|
|
|
widget->allocation = *allocation;
|
|
|
|
GTK_WIDGET_CLASS( parent_class )->size_allocate( widget, allocation );
|
|
|
|
if( GTK_WIDGET_REALIZED( widget ) )
|
|
place_windows( SEXY_ICON_ENTRY( widget ), allocation );
|
|
}
|
|
|
|
static GdkPixbuf *
|
|
get_pixbuf_from_icon( SexyIconEntry * entry,
|
|
SexyIconEntryPosition icon_pos )
|
|
{
|
|
GdkPixbuf * pixbuf = NULL;
|
|
gchar * stock_id;
|
|
SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
|
|
GtkIconSize size;
|
|
|
|
switch( gtk_image_get_storage_type( GTK_IMAGE( icon_info->icon ) ) )
|
|
{
|
|
case GTK_IMAGE_PIXBUF:
|
|
pixbuf = gtk_image_get_pixbuf( GTK_IMAGE( icon_info->icon ) );
|
|
g_object_ref( pixbuf );
|
|
break;
|
|
|
|
case GTK_IMAGE_STOCK:
|
|
gtk_image_get_stock( GTK_IMAGE(
|
|
icon_info->icon ), &stock_id, &size );
|
|
pixbuf = gtk_widget_render_icon( GTK_WIDGET( entry ),
|
|
stock_id, size, NULL );
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return pixbuf;
|
|
}
|
|
|
|
/* Kudos to the gnome-panel guys. */
|
|
static void
|
|
colorshift_pixbuf( GdkPixbuf *dest,
|
|
GdkPixbuf *src,
|
|
int shift )
|
|
{
|
|
gint i, j;
|
|
gint width, height, has_alpha, src_rowstride, dest_rowstride;
|
|
guchar *target_pixels;
|
|
guchar *original_pixels;
|
|
guchar *pix_src;
|
|
guchar *pix_dest;
|
|
int val;
|
|
guchar r, g, b;
|
|
|
|
has_alpha = gdk_pixbuf_get_has_alpha( src );
|
|
width = gdk_pixbuf_get_width( src );
|
|
height = gdk_pixbuf_get_height( src );
|
|
src_rowstride = gdk_pixbuf_get_rowstride( src );
|
|
dest_rowstride = gdk_pixbuf_get_rowstride( dest );
|
|
original_pixels = gdk_pixbuf_get_pixels( src );
|
|
target_pixels = gdk_pixbuf_get_pixels( dest );
|
|
|
|
for( i = 0; i < height; i++ )
|
|
{
|
|
pix_dest = target_pixels + i * dest_rowstride;
|
|
pix_src = original_pixels + i * src_rowstride;
|
|
|
|
for( j = 0; j < width; j++ )
|
|
{
|
|
r = *( pix_src++ );
|
|
g = *( pix_src++ );
|
|
b = *( pix_src++ );
|
|
|
|
val = r + shift;
|
|
*( pix_dest++ ) = CLAMP( val, 0, 255 );
|
|
|
|
val = g + shift;
|
|
*( pix_dest++ ) = CLAMP( val, 0, 255 );
|
|
|
|
val = b + shift;
|
|
*( pix_dest++ ) = CLAMP( val, 0, 255 );
|
|
|
|
if( has_alpha )
|
|
*( pix_dest++ ) = *( pix_src++ );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_icon( GtkWidget * widget,
|
|
SexyIconEntryPosition icon_pos )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
SexyIconInfo * icon_info = &entry->priv->icons[icon_pos];
|
|
GdkPixbuf * pixbuf;
|
|
gint x, y, width, height;
|
|
|
|
if( icon_info->icon == NULL || !GTK_WIDGET_REALIZED( widget ) )
|
|
return;
|
|
|
|
if( ( pixbuf = get_pixbuf_from_icon( entry, icon_pos ) ) == NULL )
|
|
return;
|
|
|
|
gdk_drawable_get_size( icon_info->window, &width, &height );
|
|
|
|
if( width == 1 || height == 1 )
|
|
{
|
|
/*
|
|
* size_allocate hasn't been called yet. These are the default values.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
if( gdk_pixbuf_get_height( pixbuf ) > height )
|
|
{
|
|
GdkPixbuf *temp_pixbuf;
|
|
int scale;
|
|
|
|
scale = height - ( 2 * ICON_MARGIN );
|
|
|
|
temp_pixbuf = gdk_pixbuf_scale_simple( pixbuf, scale, scale,
|
|
GDK_INTERP_BILINEAR );
|
|
|
|
g_object_unref( pixbuf );
|
|
|
|
pixbuf = temp_pixbuf;
|
|
}
|
|
|
|
x = ( width - gdk_pixbuf_get_width( pixbuf ) ) / 2;
|
|
y = ( height - gdk_pixbuf_get_height( pixbuf ) ) / 2;
|
|
|
|
if( icon_info->hovered )
|
|
{
|
|
GdkPixbuf *temp_pixbuf;
|
|
|
|
temp_pixbuf = gdk_pixbuf_copy( pixbuf );
|
|
|
|
colorshift_pixbuf( temp_pixbuf, pixbuf, 30 );
|
|
|
|
g_object_unref( pixbuf );
|
|
|
|
pixbuf = temp_pixbuf;
|
|
}
|
|
|
|
gdk_draw_pixbuf( icon_info->window, widget->style->black_gc, pixbuf,
|
|
0, 0, x, y, -1, -1,
|
|
GDK_RGB_DITHER_NORMAL, 0, 0 );
|
|
|
|
g_object_unref( pixbuf );
|
|
}
|
|
|
|
static gint
|
|
sexy_icon_entry_expose( GtkWidget * widget,
|
|
GdkEventExpose *event )
|
|
{
|
|
SexyIconEntry *entry;
|
|
|
|
g_return_val_if_fail( SEXY_IS_ICON_ENTRY( widget ), FALSE );
|
|
g_return_val_if_fail( event != NULL, FALSE );
|
|
|
|
entry = SEXY_ICON_ENTRY( widget );
|
|
|
|
if( GTK_WIDGET_DRAWABLE( widget ) )
|
|
{
|
|
gboolean found = FALSE;
|
|
int i;
|
|
|
|
for( i = 0; i < MAX_ICONS && !found; i++ )
|
|
{
|
|
SexyIconInfo *icon_info = &entry->priv->icons[i];
|
|
|
|
if( event->window == icon_info->window )
|
|
{
|
|
gint width;
|
|
GtkAllocation text_area_alloc;
|
|
|
|
get_text_area_size( entry, &text_area_alloc );
|
|
gdk_drawable_get_size( icon_info->window, &width, NULL );
|
|
|
|
gtk_paint_flat_box( widget->style, icon_info->window,
|
|
GTK_WIDGET_STATE(
|
|
widget ), GTK_SHADOW_NONE,
|
|
NULL, widget, "entry_bg",
|
|
0, 0, width, text_area_alloc.height );
|
|
|
|
draw_icon( widget, i );
|
|
|
|
found = TRUE;
|
|
}
|
|
}
|
|
|
|
if( !found )
|
|
GTK_WIDGET_CLASS( parent_class )->expose_event( widget, event );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
update_icon( GObject *obj G_GNUC_UNUSED,
|
|
GParamSpec * param,
|
|
SexyIconEntry *entry )
|
|
{
|
|
if( param != NULL )
|
|
{
|
|
const char *name = g_param_spec_get_name( param );
|
|
|
|
if( strcmp( name, "pixbuf" ) && strcmp( name, "stock" )
|
|
&& strcmp( name, "image" ) && strcmp( name, "pixmap" )
|
|
&& strcmp( name, "icon_set" ) && strcmp( name, "pixbuf_animation" ) )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
gtk_widget_queue_resize( GTK_WIDGET( entry ) );
|
|
}
|
|
|
|
static gint
|
|
sexy_icon_entry_enter_notify( GtkWidget * widget,
|
|
GdkEventCrossing *event )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
int i;
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
if( event->window == entry->priv->icons[i].window )
|
|
{
|
|
if( sexy_icon_entry_get_icon_highlight( entry, i ) )
|
|
{
|
|
entry->priv->icons[i].hovered = TRUE;
|
|
|
|
update_icon( NULL, NULL, entry );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
sexy_icon_entry_leave_notify( GtkWidget * widget,
|
|
GdkEventCrossing *event )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
int i;
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
if( event->window == entry->priv->icons[i].window )
|
|
{
|
|
if( sexy_icon_entry_get_icon_highlight( entry, i ) )
|
|
{
|
|
entry->priv->icons[i].hovered = FALSE;
|
|
|
|
update_icon( NULL, NULL, entry );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
sexy_icon_entry_button_press( GtkWidget * widget,
|
|
GdkEventButton *event )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
int i;
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
if( event->window == entry->priv->icons[i].window )
|
|
{
|
|
if( event->button == 1
|
|
&& sexy_icon_entry_get_icon_highlight( entry, i ) )
|
|
{
|
|
entry->priv->icons[i].hovered = FALSE;
|
|
|
|
update_icon( NULL, NULL, entry );
|
|
}
|
|
|
|
g_signal_emit( entry, signals[ICON_PRESSED], 0, i,
|
|
event->button );
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if( GTK_WIDGET_CLASS( parent_class )->button_press_event )
|
|
return GTK_WIDGET_CLASS( parent_class )->button_press_event( widget,
|
|
event );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
sexy_icon_entry_button_release( GtkWidget * widget,
|
|
GdkEventButton *event )
|
|
{
|
|
SexyIconEntry *entry = SEXY_ICON_ENTRY( widget );
|
|
int i;
|
|
|
|
for( i = 0; i < MAX_ICONS; i++ )
|
|
{
|
|
GdkWindow *icon_window = entry->priv->icons[i].window;
|
|
|
|
if( event->window == icon_window )
|
|
{
|
|
int width, height;
|
|
gdk_drawable_get_size( icon_window, &width, &height );
|
|
|
|
if( event->button == 1
|
|
&& sexy_icon_entry_get_icon_highlight( entry, i )
|
|
&& event->x >= 0 && event->y >= 0
|
|
&& event->x <= width && event->y <= height )
|
|
{
|
|
entry->priv->icons[i].hovered = TRUE;
|
|
|
|
update_icon( NULL, NULL, entry );
|
|
}
|
|
|
|
g_signal_emit( entry, signals[ICON_RELEASED], 0, i,
|
|
event->button );
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if( GTK_WIDGET_CLASS( parent_class )->button_release_event )
|
|
return GTK_WIDGET_CLASS( parent_class )->button_release_event(
|
|
widget,
|
|
event );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* sexy_icon_entry_new
|
|
*
|
|
* Creates a new SexyIconEntry widget.
|
|
*
|
|
* Returns a new #SexyIconEntry.
|
|
*/
|
|
GtkWidget *
|
|
sexy_icon_entry_new( void )
|
|
{
|
|
return GTK_WIDGET( g_object_new( SEXY_TYPE_ICON_ENTRY, NULL ) );
|
|
}
|
|
|
|
/**
|
|
* sexy_icon_entry_set_icon
|
|
* @entry: A #SexyIconEntry.
|
|
* @position: Icon position.
|
|
* @icon: A #GtkImage to set as the icon.
|
|
*
|
|
* Sets the icon shown in the entry
|
|
*/
|
|
void
|
|
sexy_icon_entry_set_icon( SexyIconEntry * entry,
|
|
SexyIconEntryPosition icon_pos,
|
|
GtkImage * icon )
|
|
{
|
|
SexyIconInfo *icon_info;
|
|
|
|
g_return_if_fail( entry != NULL );
|
|
g_return_if_fail( SEXY_IS_ICON_ENTRY( entry ) );
|
|
g_return_if_fail( IS_VALID_ICON_ENTRY_POSITION( icon_pos ) );
|
|
g_return_if_fail( icon == NULL || GTK_IS_IMAGE( icon ) );
|
|
|
|
icon_info = &entry->priv->icons[icon_pos];
|
|
|
|
if( icon == icon_info->icon )
|
|
return;
|
|
|
|
if( icon_pos == SEXY_ICON_ENTRY_SECONDARY
|
|
&& entry->priv->icon_released_id != 0 )
|
|
{
|
|
g_signal_handler_disconnect( entry, entry->priv->icon_released_id );
|
|
entry->priv->icon_released_id = 0;
|
|
}
|
|
|
|
if( icon == NULL )
|
|
{
|
|
if( icon_info->icon != NULL )
|
|
{
|
|
gtk_widget_destroy( GTK_WIDGET( icon_info->icon ) );
|
|
icon_info->icon = NULL;
|
|
|
|
/*
|
|
* Explicitly check, as the pointer may become invalidated
|
|
* during destruction.
|
|
*/
|
|
if( icon_info->window != NULL
|
|
&& GDK_IS_WINDOW( icon_info->window ) )
|
|
gdk_window_hide( icon_info->window );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( icon_info->window != NULL && icon_info->icon == NULL )
|
|
gdk_window_show( icon_info->window );
|
|
|
|
g_signal_connect( G_OBJECT( icon ), "notify",
|
|
G_CALLBACK( update_icon ), entry );
|
|
|
|
icon_info->icon = icon;
|
|
g_object_ref( icon );
|
|
}
|
|
|
|
update_icon( NULL, NULL, entry );
|
|
}
|
|
|
|
/**
|
|
* sexy_icon_entry_set_icon_highlight
|
|
* @entry: A #SexyIconEntry;
|
|
* @position: Icon position.
|
|
* @highlight: TRUE if the icon should highlight on mouse-over
|
|
*
|
|
* Determines whether the icon will highlight on mouse-over.
|
|
*/
|
|
void
|
|
sexy_icon_entry_set_icon_highlight( SexyIconEntry * entry,
|
|
SexyIconEntryPosition icon_pos,
|
|
gboolean highlight )
|
|
{
|
|
SexyIconInfo *icon_info;
|
|
|
|
g_return_if_fail( entry != NULL );
|
|
g_return_if_fail( SEXY_IS_ICON_ENTRY( entry ) );
|
|
g_return_if_fail( IS_VALID_ICON_ENTRY_POSITION( icon_pos ) );
|
|
|
|
icon_info = &entry->priv->icons[icon_pos];
|
|
|
|
if( icon_info->highlight == highlight )
|
|
return;
|
|
|
|
icon_info->highlight = highlight;
|
|
}
|
|
|
|
/**
|
|
* sexy_icon_entry_get_icon
|
|
* @entry: A #SexyIconEntry.
|
|
* @position: Icon position.
|
|
*
|
|
* Retrieves the image used for the icon
|
|
*
|
|
* Returns: A #GtkImage.
|
|
*/
|
|
GtkImage *
|
|
sexy_icon_entry_get_icon( const SexyIconEntry * entry,
|
|
SexyIconEntryPosition icon_pos )
|
|
{
|
|
g_return_val_if_fail( entry != NULL, NULL );
|
|
g_return_val_if_fail( SEXY_IS_ICON_ENTRY( entry ), NULL );
|
|
g_return_val_if_fail( IS_VALID_ICON_ENTRY_POSITION( icon_pos ), NULL );
|
|
|
|
return entry->priv->icons[icon_pos].icon;
|
|
}
|
|
|
|
/**
|
|
* sexy_icon_entry_get_icon_highlight
|
|
* @entry: A #SexyIconEntry.
|
|
* @position: Icon position.
|
|
*
|
|
* Retrieves whether entry will highlight the icon on mouseover.
|
|
*
|
|
* Returns: TRUE if icon highlights.
|
|
*/
|
|
gboolean
|
|
sexy_icon_entry_get_icon_highlight( const SexyIconEntry * entry,
|
|
SexyIconEntryPosition icon_pos )
|
|
{
|
|
g_return_val_if_fail( entry != NULL, FALSE );
|
|
g_return_val_if_fail( SEXY_IS_ICON_ENTRY( entry ), FALSE );
|
|
g_return_val_if_fail( IS_VALID_ICON_ENTRY_POSITION( icon_pos ), FALSE );
|
|
|
|
return entry->priv->icons[icon_pos].highlight;
|
|
}
|
|
|
|
static void
|
|
clear_button_clicked_cb( SexyIconEntry * icon_entry,
|
|
SexyIconEntryPosition icon_pos,
|
|
int button )
|
|
{
|
|
if( icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1 )
|
|
return;
|
|
|
|
gtk_entry_set_text( GTK_ENTRY( icon_entry ), "" );
|
|
}
|
|
|
|
/**
|
|
* sexy_icon_entry_add_clear_button
|
|
* @icon_entry: A #SexyIconEntry.
|
|
*
|
|
* A convenience function to add a clear button to the end of the entry.
|
|
* This is useful for search boxes.
|
|
*/
|
|
void
|
|
sexy_icon_entry_add_clear_button( SexyIconEntry *icon_entry )
|
|
{
|
|
GtkWidget *icon;
|
|
|
|
g_return_if_fail( icon_entry != NULL );
|
|
g_return_if_fail( SEXY_IS_ICON_ENTRY( icon_entry ) );
|
|
|
|
icon = gtk_image_new_from_stock( GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU );
|
|
gtk_widget_show( icon );
|
|
sexy_icon_entry_set_icon( SEXY_ICON_ENTRY( icon_entry ),
|
|
SEXY_ICON_ENTRY_SECONDARY,
|
|
GTK_IMAGE( icon ) );
|
|
sexy_icon_entry_set_icon_highlight( SEXY_ICON_ENTRY( icon_entry ),
|
|
SEXY_ICON_ENTRY_SECONDARY, TRUE );
|
|
|
|
if( icon_entry->priv->icon_released_id != 0 )
|
|
{
|
|
g_signal_handler_disconnect( icon_entry,
|
|
icon_entry->priv->icon_released_id );
|
|
}
|
|
|
|
icon_entry->priv->icon_released_id =
|
|
g_signal_connect( G_OBJECT( icon_entry ), "icon_released",
|
|
G_CALLBACK( clear_button_clicked_cb ), NULL );
|
|
}
|
|
|