2
* @brief EEK preview stuff
4
/* ***** BEGIN LICENSE BLOCK *****
5
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
7
* The contents of this file are subject to the Mozilla Public License Version
8
* 1.1 (the "License"); you may not use this file except in compliance with
9
* the License. You may obtain a copy of the License at
10
* http://www.mozilla.org/MPL/
12
* Software distributed under the License is distributed on an "AS IS" basis,
13
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14
* for the specific language governing rights and limitations under the
17
* The Original Code is Eek Preview Stuffs.
19
* The Initial Developer of the Original Code is
21
* Portions created by the Initial Developer are Copyright (C) 2005
22
* the Initial Developer. All Rights Reserved.
26
* Alternatively, the contents of this file may be used under the terms of
27
* either the GNU General Public License Version 2 or later (the "GPL"), or
28
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
* in which case the provisions of the GPL or the LGPL are applicable instead
30
* of those above. If you wish to allow use of your version of this file only
31
* under the terms of either the GPL or the LGPL, and not to allow others to
32
* use your version of this file under the terms of the MPL, indicate your
33
* decision by deleting the provisions above and replace them with the notice
34
* and other provisions required by the GPL or the LGPL. If you do not delete
35
* the provisions above, a recipient may use your version of this file under
36
* the terms of any one of the MPL, the GPL or the LGPL.
38
* ***** END LICENSE BLOCK ***** */
44
#include "eek-preview.h"
46
#define PRIME_BUTTON_MAGIC_NUMBER 1
48
#define FOCUS_PROP_ID 1
50
/* Keep in sycn with last value in eek-preview.h */
51
#define PREVIEW_SIZE_LAST PREVIEW_SIZE_HUGE
52
#define PREVIEW_SIZE_NEXTFREE (PREVIEW_SIZE_HUGE + 1)
54
#define PREVIEW_MAX_RATIO 500
56
static void eek_preview_class_init( EekPreviewClass *klass );
57
static void eek_preview_init( EekPreview *preview );
59
static GtkWidgetClass* parent_class = 0;
61
void eek_preview_set_color( EekPreview* preview, int r, int g, int b )
67
gtk_widget_queue_draw(GTK_WIDGET(preview));
71
void eek_preview_set_pixbuf( EekPreview* preview, GdkPixbuf* pixbuf )
73
preview->_previewPixbuf = pixbuf;
75
gtk_widget_queue_draw(GTK_WIDGET(preview));
77
if (preview->_scaled) {
78
g_object_unref(preview->_scaled);
81
preview->_scaledW = gdk_pixbuf_get_width(preview->_previewPixbuf);
82
preview->_scaledH = gdk_pixbuf_get_height(preview->_previewPixbuf);
86
GType eek_preview_get_type(void)
88
static GType preview_type = 0;
91
static const GTypeInfo preview_info = {
92
sizeof( EekPreviewClass ),
94
NULL, /* base_finalize */
95
(GClassInitFunc)eek_preview_class_init,
96
NULL, /* class_finalize */
97
NULL, /* class_data */
100
(GInstanceInitFunc)eek_preview_init,
101
NULL /* value_table */
105
preview_type = g_type_register_static( GTK_TYPE_DRAWING_AREA, "EekPreview", &preview_info, (GTypeFlags)0 );
111
static gboolean setupDone = FALSE;
112
static GtkRequisition sizeThings[PREVIEW_SIZE_NEXTFREE];
114
void eek_preview_set_size_mappings( guint count, GtkIconSize const* sizes )
123
for ( i = 0; i < count; ++i ) {
124
gboolean worked = gtk_icon_size_lookup( sizes[i], &width, &height );
126
if ( width < smallest ) {
129
if ( width > largest ) {
135
smallest = (smallest * 3) / 4;
137
delta = largest - smallest;
139
for ( i = 0; i < G_N_ELEMENTS(sizeThings); ++i ) {
140
guint val = smallest + ( (i * delta) / (G_N_ELEMENTS(sizeThings) - 1) );
141
sizeThings[i].width = val;
142
sizeThings[i].height = val;
148
static void eek_preview_size_request( GtkWidget* widget, GtkRequisition* req )
152
EekPreview* preview = EEK_PREVIEW(widget);
155
GtkIconSize sizes[] = {
157
GTK_ICON_SIZE_SMALL_TOOLBAR,
158
GTK_ICON_SIZE_LARGE_TOOLBAR,
159
GTK_ICON_SIZE_BUTTON,
162
eek_preview_set_size_mappings( G_N_ELEMENTS(sizes), sizes );
165
width = sizeThings[preview->_size].width;
166
height = sizeThings[preview->_size].height;
168
if ( preview->_view == VIEW_TYPE_LIST ) {
172
if ( preview->_ratio != 100 ) {
173
width = (width * preview->_ratio) / 100;
180
req->height = height;
190
static guint eek_preview_signals[LAST_SIGNAL] = { 0 };
193
gboolean eek_preview_expose_event( GtkWidget* widget, GdkEventExpose* event )
195
/* g_message("Exposed!!! %s", GTK_WIDGET_HAS_FOCUS(widget) ? "XXX" : "---" ); */
201
gint lower = widget->allocation.width;
202
lower = (widget->allocation.height < lower) ? widget->allocation.height : lower;
221
if ( GTK_WIDGET_DRAWABLE( widget ) ) {
222
GtkStyle* style = gtk_widget_get_style( widget );
224
if ( insetX > 0 || insetY > 0 ) {
225
gtk_paint_flat_box( style,
227
(GtkStateType)GTK_WIDGET_STATE(widget),
233
widget->allocation.width, widget->allocation.height);
236
GdkGC *gc = gdk_gc_new( widget->window );
237
EekPreview* preview = EEK_PREVIEW(widget);
238
GdkColor fg = {0, preview->_r, preview->_g, preview->_b};
240
gdk_colormap_alloc_color( gdk_colormap_get_system(), &fg, FALSE, TRUE );
242
gdk_gc_set_foreground( gc, &fg );
244
gdk_draw_rectangle( widget->window,
248
widget->allocation.width - (insetX * 2), widget->allocation.height - (insetY * 2) );
250
if ( preview->_previewPixbuf ) {
251
GtkDrawingArea* da = &(preview->drawing);
252
GdkDrawable* drawable = (GdkDrawable*) (((GtkWidget*)da)->window);
255
gdk_drawable_get_size(drawable, &w, &h);
256
if ((w != preview->_scaledW) || (h != preview->_scaledH)) {
257
if (preview->_scaled) {
258
g_object_unref(preview->_scaled);
260
preview->_scaled = gdk_pixbuf_scale_simple(preview->_previewPixbuf, w, h, GDK_INTERP_BILINEAR);
261
preview->_scaledW = w;
262
preview->_scaledH = h;
265
GdkPixbuf* pix = (preview->_scaled) ? preview->_scaled : preview->_previewPixbuf;
266
gdk_draw_pixbuf( drawable, 0, pix, 0, 0, 0, 0, w, h, GDK_RGB_DITHER_NONE, 0, 0 );
270
if ( preview->_linked ) {
272
GdkRectangle possible = {insetX, insetY, (widget->allocation.width - (insetX * 2)), (widget->allocation.height - (insetY * 2)) };
273
GdkRectangle area = {possible.x, possible.y, possible.width / 2, possible.height / 2 };
276
if ( area.width > area.height )
277
area.width = area.height;
278
if ( area.height > area.width )
279
area.height = area.width;
281
/* Center it horizontally */
282
if ( area.width < possible.width ) {
283
int diff = (possible.width - area.width) / 2;
288
if ( preview->_linked & PREVIEW_LINK_IN ) {
289
gtk_paint_arrow( style,
291
(GtkStateType)widget->state,
292
GTK_SHADOW_ETCHED_IN,
293
NULL, /* clip area. &area, */
294
widget, /* may be NULL */
299
area.width, area.height
303
if ( preview->_linked & PREVIEW_LINK_OUT ) {
304
GdkRectangle otherArea = {area.x, area.y, area.width, area.height};
305
if ( otherArea.height < possible.height ) {
306
otherArea.y = possible.y + (possible.height - otherArea.height);
309
gtk_paint_arrow( style,
311
(GtkStateType)widget->state,
312
GTK_SHADOW_ETCHED_OUT,
313
NULL, /* clip area. &area, */
314
widget, /* may be NULL */
318
otherArea.x, otherArea.y,
319
otherArea.width, otherArea.height
323
if ( preview->_linked & PREVIEW_LINK_OTHER ) {
324
GdkRectangle otherArea = {insetX, area.y, area.width, area.height};
325
if ( otherArea.height < possible.height ) {
326
otherArea.y = possible.y + (possible.height - otherArea.height) / 2;
329
gtk_paint_arrow( style,
331
(GtkStateType)widget->state,
332
GTK_SHADOW_ETCHED_OUT,
333
NULL, /* clip area. &area, */
334
widget, /* may be NULL */
338
otherArea.x, otherArea.y,
339
otherArea.width, otherArea.height
344
if ( preview->_linked & PREVIEW_FILL ) {
345
GdkRectangle otherArea = {possible.x + ((possible.width / 4) - (area.width / 2)),
347
area.width, area.height};
348
if ( otherArea.height < possible.height ) {
349
otherArea.y = possible.y + (possible.height - otherArea.height) / 2;
351
gtk_paint_check( style,
353
(GtkStateType)widget->state,
354
GTK_SHADOW_ETCHED_OUT,
358
otherArea.x, otherArea.y,
359
otherArea.width, otherArea.height );
362
if ( preview->_linked & PREVIEW_STROKE ) {
363
GdkRectangle otherArea = {possible.x + (((possible.width * 3) / 4) - (area.width / 2)),
365
area.width, area.height};
366
if ( otherArea.height < possible.height ) {
367
otherArea.y = possible.y + (possible.height - otherArea.height) / 2;
369
gtk_paint_diamond( style,
371
(GtkStateType)widget->state,
372
GTK_SHADOW_ETCHED_OUT,
376
otherArea.x, otherArea.y,
377
otherArea.width, otherArea.height );
382
if ( GTK_WIDGET_HAS_FOCUS(widget) ) {
383
gtk_paint_focus( style,
386
NULL, /* GdkRectangle *area, */
390
widget->allocation.width - 2, widget->allocation.height - 2 );
399
static gboolean eek_preview_enter_cb( GtkWidget* widget, GdkEventCrossing* event )
401
if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
402
EekPreview* preview = EEK_PREVIEW(widget);
403
preview->_within = TRUE;
404
gtk_widget_set_state( widget, preview->_hot ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT );
409
static gboolean eek_preview_leave_cb( GtkWidget* widget, GdkEventCrossing* event )
411
if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
412
EekPreview* preview = EEK_PREVIEW(widget);
413
preview->_within = FALSE;
414
gtk_widget_set_state( widget, GTK_STATE_NORMAL );
420
static gboolean eek_preview_focus_in_event( GtkWidget* widget, GdkEventFocus* event )
422
g_message("focus IN");
423
gboolean blip = parent_class->focus_in_event ? parent_class->focus_in_event(widget, event) : FALSE;
427
static gboolean eek_preview_focus_out_event( GtkWidget* widget, GdkEventFocus* event )
429
g_message("focus OUT");
430
gboolean blip = parent_class->focus_out_event ? parent_class->focus_out_event(widget, event) : FALSE;
435
static gboolean eek_preview_button_press_cb( GtkWidget* widget, GdkEventButton* event )
437
if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
438
EekPreview* preview = EEK_PREVIEW(widget);
440
if ( preview->_takesFocus && !GTK_WIDGET_HAS_FOCUS(widget) ) {
441
gtk_widget_grab_focus(widget);
444
if ( event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
445
preview->_hot = TRUE;
446
if ( preview->_within ) {
447
gtk_widget_set_state( widget, GTK_STATE_ACTIVE );
455
static gboolean eek_preview_button_release_cb( GtkWidget* widget, GdkEventButton* event )
457
if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
458
EekPreview* preview = EEK_PREVIEW(widget);
459
preview->_hot = FALSE;
460
gtk_widget_set_state( widget, GTK_STATE_NORMAL );
461
if ( preview->_within && event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
462
gboolean isAlt = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
465
g_signal_emit( widget, eek_preview_signals[ALTCLICKED_SIGNAL], 0, 2 );
467
g_signal_emit( widget, eek_preview_signals[CLICKED_SIGNAL], 0 );
474
gboolean eek_preview_key_press_event( GtkWidget* widget, GdkEventKey* event)
482
gboolean eek_preview_key_release_event( GtkWidget* widget, GdkEventKey* event)
490
static void eek_preview_get_property( GObject *object,
495
GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
496
switch ( property_id ) {
499
EekPreview* preview = EEK_PREVIEW( object );
500
g_value_set_boolean( value, preview->_takesFocus );
505
if ( gobjClass->get_property ) {
506
gobjClass->get_property( object, property_id, value, pspec );
512
static void eek_preview_set_property( GObject *object,
517
GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
518
switch ( property_id ) {
521
EekPreview* preview = EEK_PREVIEW( object );
522
gboolean val = g_value_get_boolean( value );
523
if ( val != preview->_takesFocus ) {
524
preview->_takesFocus = val;
530
if ( gobjClass->set_property ) {
531
gobjClass->set_property( object, property_id, value, pspec );
538
static gboolean eek_preview_popup_menu( GtkWidget* widget )
540
/* g_message("Do the popup!"); */
541
gboolean blip = parent_class->popup_menu ? parent_class->popup_menu(widget) : FALSE;
546
static void eek_preview_class_init( EekPreviewClass *klass )
548
GObjectClass* gobjClass = G_OBJECT_CLASS(klass);
549
/*GtkObjectClass* objectClass = (GtkObjectClass*)klass;*/
550
GtkWidgetClass* widgetClass = (GtkWidgetClass*)klass;
552
gobjClass->set_property = eek_preview_set_property;
553
gobjClass->get_property = eek_preview_get_property;
555
/*objectClass->destroy = eek_preview_destroy;*/
557
parent_class = (GtkWidgetClass*)g_type_class_peek_parent( klass );
559
/*widgetClass->map = ;*/
560
/*widgetClass->unmap = ;*/
561
/*widgetClass->realize = ;*/
562
/*widgetClass->unrealize = ;*/
563
widgetClass->size_request = eek_preview_size_request;
564
/*widgetClass->size_allocate = ;*/
565
/*widgetClass->state_changed = ;*/
566
/*widgetClass->style_set = ;*/
567
/*widgetClass->grab_notify = ;*/
569
widgetClass->button_press_event = eek_preview_button_press_cb;
570
widgetClass->button_release_event = eek_preview_button_release_cb;
571
/*widgetClass->delete_event = ;*/
572
/*widgetClass->destroy_event = ;*/
573
widgetClass->expose_event = eek_preview_expose_event;
574
/* widgetClass->key_press_event = eek_preview_key_press_event; */
575
/* widgetClass->key_release_event = eek_preview_key_release_event; */
576
widgetClass->enter_notify_event = eek_preview_enter_cb;
577
widgetClass->leave_notify_event = eek_preview_leave_cb;
578
/*widgetClass->configure_event = ;*/
579
/*widgetClass->focus_in_event = eek_preview_focus_in_event;*/
580
/*widgetClass->focus_out_event = eek_preview_focus_out_event;*/
583
/*widgetClass->selection_get = ;*/
584
/*widgetClass->selection_received = ;*/
588
/*widgetClass->drag_begin = ;*/
589
/*widgetClass->drag_end = ;*/
590
/*widgetClass->drag_data_get = ;*/
591
/*widgetClass->drag_data_delete = ;*/
594
/*widgetClass->drag_leave = ;*/
595
/*widgetClass->drag_motion = ;*/
596
/*widgetClass->drag_drop = ;*/
597
/*widgetClass->drag_data_received = ;*/
599
/* For keybindings: */
600
widgetClass->popup_menu = eek_preview_popup_menu;
601
/*widgetClass->show_help = ;*/
603
/* Accessibility support: */
604
/*widgetClass->get_accessible = ;*/
605
/*widgetClass->screen_changed = ;*/
606
/*widgetClass->can_activate_accel = ;*/
609
eek_preview_signals[CLICKED_SIGNAL] =
610
g_signal_new( "clicked",
611
G_TYPE_FROM_CLASS( klass ),
612
(GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
613
G_STRUCT_OFFSET( EekPreviewClass, clicked ),
615
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 );
617
eek_preview_signals[ALTCLICKED_SIGNAL] =
618
g_signal_new( "alt-clicked",
619
G_TYPE_FROM_CLASS( klass ),
620
(GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
621
G_STRUCT_OFFSET( EekPreviewClass, clicked ),
623
g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
627
g_object_class_install_property( gobjClass,
629
g_param_spec_boolean(
632
"flag to grab focus when clicked",
634
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT)
639
void eek_preview_set_linked( EekPreview* splat, LinkType link )
641
link = (LinkType)(link & PREVIEW_LINK_ALL);
642
if ( link != (LinkType)splat->_linked ) {
643
splat->_linked = link;
645
gtk_widget_queue_draw( GTK_WIDGET(splat) );
649
LinkType eek_preview_get_linked( EekPreview* splat )
651
return (LinkType)splat->_linked;
654
gboolean eek_preview_get_focus_on_click( EekPreview* preview )
656
return preview->_takesFocus;
659
void eek_preview_set_focus_on_click( EekPreview* preview, gboolean focus_on_click )
661
if ( focus_on_click != preview->_takesFocus ) {
662
preview->_takesFocus = focus_on_click;
666
void eek_preview_set_details( EekPreview* preview, PreviewStyle prevstyle, ViewType view, PreviewSize size, guint ratio )
668
preview->_prevstyle = prevstyle;
669
preview->_view = view;
671
if ( size > PREVIEW_SIZE_LAST ) {
672
size = PREVIEW_SIZE_LAST;
674
preview->_size = size;
676
if ( ratio > PREVIEW_MAX_RATIO ) {
677
ratio = PREVIEW_MAX_RATIO;
679
preview->_ratio = ratio;
681
gtk_widget_queue_draw(GTK_WIDGET(preview));
684
static void eek_preview_init( EekPreview *preview )
686
GtkWidget* widg = GTK_WIDGET(preview);
687
GTK_WIDGET_SET_FLAGS( widg, GTK_CAN_FOCUS );
688
GTK_WIDGET_SET_FLAGS( widg, GTK_RECEIVES_DEFAULT );
690
gtk_widget_set_sensitive( widg, TRUE );
692
gtk_widget_add_events(widg, GDK_BUTTON_PRESS_MASK
693
| GDK_BUTTON_RELEASE_MASK
695
| GDK_KEY_RELEASE_MASK
696
| GDK_FOCUS_CHANGE_MASK
697
| GDK_ENTER_NOTIFY_MASK
698
| GDK_LEAVE_NOTIFY_MASK );
700
/* gtk_widget_add_events( widg, GDK_ALL_EVENTS_MASK );*/
705
preview->_scaledW = 0;
706
preview->_scaledH = 0;
708
preview->_hot = FALSE;
709
preview->_within = FALSE;
710
preview->_takesFocus = FALSE;
712
preview->_prevstyle = PREVIEW_STYLE_ICON;
713
preview->_view = VIEW_TYPE_LIST;
714
preview->_size = PREVIEW_SIZE_SMALL;
715
preview->_ratio = 100;
717
preview->_previewPixbuf = 0;
718
preview->_scaled = 0;
721
GdkColor color = {0};
722
color.red = (255 << 8) | 255;
724
GdkColor whack = {0};
725
whack.green = (255 << 8) | 255;
727
gtk_widget_modify_bg( widg, GTK_STATE_NORMAL, &color );
728
gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &whack );
731
/* GTK_STATE_ACTIVE, */
732
/* GTK_STATE_PRELIGHT, */
733
/* GTK_STATE_SELECTED, */
734
/* GTK_STATE_INSENSITIVE */
737
GdkColor color = {0,0,0,0};
742
gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
743
gtk_widget_modify_bg(widg, GTK_STATE_ACTIVE, &color);
746
color.green = 0xffff;
748
gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
749
gtk_widget_modify_bg(widg, GTK_STATE_SELECTED, &color);
754
gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
755
gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &color );
760
GtkWidget* eek_preview_new(void)
762
return GTK_WIDGET( g_object_new( EEK_PREVIEW_TYPE, NULL ) );
768
c-file-style:"stroustrup"
769
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
774
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :