1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 */
4 /* ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
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
15 * License.
16 *
17 * The Original Code is Eek Preview Stuffs.
18 *
19 * The Initial Developer of the Original Code is
20 * Jon A. Cruz.
21 * Portions created by the Initial Developer are Copyright (C) 2005
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
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.
37 *
38 * ***** END LICENSE BLOCK ***** */
40 #include <gtk/gtk.h>
41 #include "eek-preview.h"
43 #define PRIME_BUTTON_MAGIC_NUMBER 1
45 #define FOCUS_PROP_ID 1
49 static void eek_preview_class_init( EekPreviewClass *klass );
50 static void eek_preview_init( EekPreview *preview );
52 static GtkWidgetClass* parent_class = 0;
54 void eek_preview_set_color( EekPreview* preview, int r, int g, int b )
55 {
56 if ( (preview->_r = r)
57 || (preview->_g = g)
58 || (preview->_b = b) ) {
59 preview->_r = r;
60 preview->_g = g;
61 preview->_b = b;
63 gtk_widget_queue_draw(GTK_WIDGET(preview));
64 }
65 }
68 GType eek_preview_get_type(void)
69 {
70 static GType preview_type = 0;
72 if (!preview_type) {
73 static const GTypeInfo preview_info = {
74 sizeof( EekPreviewClass ),
75 NULL, /* base_init */
76 NULL, /* base_finalize */
77 (GClassInitFunc)eek_preview_class_init,
78 NULL, /* class_finalize */
79 NULL, /* class_data */
80 sizeof( EekPreview ),
81 0, /* n_preallocs */
82 (GInstanceInitFunc)eek_preview_init,
83 NULL /* value_table */
84 };
87 preview_type = g_type_register_static( GTK_TYPE_DRAWING_AREA, "EekPreview", &preview_info, (GTypeFlags)0 );
88 }
90 return preview_type;
91 }
93 static guint trackCount = 0;
94 static guint* trackSizes = 0;
95 static GtkIconSize* trackKeys = 0;
97 void eek_preview_set_size_mappings( guint count, GtkIconSize const* sizes )
98 {
99 gint width = 0;
100 gint height = 0;
101 gint smallest = 512;
102 gint largest = 0;
103 guint i = 0;
104 guint delta = 0;
106 for ( i = 0; i < count; ++i ) {
107 gboolean worked = gtk_icon_size_lookup( sizes[i], &width, &height );
108 if ( worked ) {
109 if ( width < smallest ) {
110 smallest = width;
111 }
112 if ( width > largest ) {
113 largest = width;
114 }
115 }
116 }
118 smallest = (smallest * 3) / 4;
120 delta = largest - smallest;
122 if ( trackSizes ) {
123 g_free(trackSizes);
124 trackSizes = 0;
125 }
126 if ( trackKeys ) {
127 g_free(trackKeys);
128 trackKeys = 0;
129 }
131 trackCount = count;
132 trackSizes = g_new(guint, count);
133 trackKeys = g_new(GtkIconSize, count);
134 for ( i = 0; i < count; ++i ) {
135 guint val = smallest + ( (i * delta) / (count-1) );
136 trackKeys[i] = sizes[i];
137 trackSizes[i] = val;
138 }
139 }
141 GtkWidget* eek_preview_area_new(void)
142 {
143 return NULL;
144 }
146 static void eek_preview_size_request( GtkWidget* widget, GtkRequisition* req )
147 {
148 gint width = 0;
149 gint height = 0;
150 EekPreview* preview = EEK_PREVIEW(widget);
151 gboolean tracked = TRUE;
152 guint i = 0;
154 for ( i = 0; i < trackCount; ++i ) {
155 tracked = (trackKeys[i] == preview->_size);
156 if ( tracked ) {
157 width = trackSizes[i];
158 height = width;
159 break;
160 }
161 }
163 if ( !tracked ) {
164 gboolean worked = gtk_icon_size_lookup( preview->_size, &width, &height );
165 if ( !worked ) {
166 width = 16;
167 height = 16;
168 g_warning("Size not found [%d]", preview->_size);
169 }
170 }
171 if ( preview->_view == VIEW_TYPE_LIST ) {
172 width *= 3;
173 }
174 req->width = width;
175 req->height = height;
176 }
178 enum {
179 CLICKED_SIGNAL,
180 ALTCLICKED_SIGNAL,
181 LAST_SIGNAL
182 };
185 static guint eek_preview_signals[LAST_SIGNAL] = { 0 };
188 gboolean eek_preview_expose_event( GtkWidget* widget, GdkEventExpose* event )
189 {
190 /* g_message("Exposed!!! %s", GTK_WIDGET_HAS_FOCUS(widget) ? "XXX" : "---" ); */
191 gint insetX = 0;
192 gint insetY = 0;
194 (void)event;
195 /*
196 gint lower = widget->allocation.width;
197 lower = (widget->allocation.height < lower) ? widget->allocation.height : lower;
198 if ( lower > 16 ) {
199 insetX++;
200 if ( lower > 18 ) {
201 insetX++;
202 if ( lower > 22 ) {
203 insetX++;
204 if ( lower > 24 ) {
205 insetX++;
206 if ( lower > 32 ) {
207 insetX++;
208 }
209 }
210 }
211 }
212 insetY = insetX;
213 }
214 */
216 if ( GTK_WIDGET_DRAWABLE( widget ) ) {
217 GtkStyle* style = gtk_widget_get_style( widget );
219 if ( insetX > 0 || insetY > 0 ) {
220 gtk_paint_flat_box( style,
221 widget->window,
222 (GtkStateType)GTK_WIDGET_STATE(widget),
223 GTK_SHADOW_NONE,
224 NULL,
225 widget,
226 NULL,
227 0, 0,
228 widget->allocation.width, widget->allocation.height);
229 }
231 GdkGC *gc = gdk_gc_new( widget->window );
232 EekPreview* preview = EEK_PREVIEW(widget);
233 GdkColor fg = {0, preview->_r, preview->_g, preview->_b};
234 gdk_colormap_alloc_color( gdk_colormap_get_system(), &fg, FALSE, TRUE );
236 gdk_gc_set_foreground( gc, &fg );
238 gdk_draw_rectangle( widget->window,
239 gc,
240 TRUE,
241 insetX, insetY,
242 widget->allocation.width - (insetX * 2), widget->allocation.height - (insetY * 2) );
244 if ( preview->_linked ) {
245 /* Draw arrow */
246 GdkRectangle possible = {insetX, insetY, (widget->allocation.width - (insetX * 2)), (widget->allocation.height - (insetY * 2)) };
247 GdkRectangle area = {possible.x, possible.y, possible.width / 2, possible.height / 2 };
249 /* Make it square */
250 if ( area.width > area.height )
251 area.width = area.height;
252 if ( area.height > area.width )
253 area.height = area.width;
255 /* Center it horizontally */
256 if ( area.width < possible.width ) {
257 int diff = (possible.width - area.width) / 2;
258 area.x += diff;
259 }
262 if ( preview->_linked & PREVIEW_LINK_IN ) {
263 gtk_paint_arrow( style,
264 widget->window,
265 (GtkStateType)widget->state,
266 GTK_SHADOW_ETCHED_IN,
267 NULL, /* clip area. &area, */
268 widget, /* may be NULL */
269 NULL, /* detail */
270 GTK_ARROW_DOWN,
271 FALSE,
272 area.x, area.y,
273 area.width, area.height
274 );
275 }
277 if ( preview->_linked & PREVIEW_LINK_OUT ) {
278 GdkRectangle otherArea = {area.x, area.y, area.width, area.height};
279 if ( otherArea.height < possible.height ) {
280 otherArea.y = possible.y + (possible.height - otherArea.height);
281 }
283 gtk_paint_arrow( style,
284 widget->window,
285 (GtkStateType)widget->state,
286 GTK_SHADOW_ETCHED_OUT,
287 NULL, /* clip area. &area, */
288 widget, /* may be NULL */
289 NULL, /* detail */
290 GTK_ARROW_UP,
291 FALSE,
292 otherArea.x, otherArea.y,
293 otherArea.width, otherArea.height
294 );
295 }
297 if ( preview->_linked & PREVIEW_LINK_OTHER ) {
298 GdkRectangle otherArea = {insetX, area.y, area.width, area.height};
299 if ( otherArea.height < possible.height ) {
300 otherArea.y = possible.y + (possible.height - otherArea.height) / 2;
301 }
303 gtk_paint_arrow( style,
304 widget->window,
305 (GtkStateType)widget->state,
306 GTK_SHADOW_ETCHED_OUT,
307 NULL, /* clip area. &area, */
308 widget, /* may be NULL */
309 NULL, /* detail */
310 GTK_ARROW_LEFT,
311 FALSE,
312 otherArea.x, otherArea.y,
313 otherArea.width, otherArea.height
314 );
315 }
316 }
318 if ( GTK_WIDGET_HAS_FOCUS(widget) ) {
319 gtk_paint_focus( style,
320 widget->window,
321 GTK_STATE_NORMAL,
322 NULL, /* GdkRectangle *area, */
323 widget,
324 NULL,
325 0 + 1, 0 + 1,
326 widget->allocation.width - 2, widget->allocation.height - 2 );
327 }
328 }
331 return FALSE;
332 }
335 static gboolean eek_preview_enter_cb( GtkWidget* widget, GdkEventCrossing* event )
336 {
337 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
338 EekPreview* preview = EEK_PREVIEW(widget);
339 preview->_within = TRUE;
340 gtk_widget_set_state( widget, preview->_hot ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT );
341 }
342 return FALSE;
343 }
345 static gboolean eek_preview_leave_cb( GtkWidget* widget, GdkEventCrossing* event )
346 {
347 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
348 EekPreview* preview = EEK_PREVIEW(widget);
349 preview->_within = FALSE;
350 gtk_widget_set_state( widget, GTK_STATE_NORMAL );
351 }
352 return FALSE;
353 }
355 /*
356 static gboolean eek_preview_focus_in_event( GtkWidget* widget, GdkEventFocus* event )
357 {
358 g_message("focus IN");
359 gboolean blip = parent_class->focus_in_event ? parent_class->focus_in_event(widget, event) : FALSE;
360 return blip;
361 }
363 static gboolean eek_preview_focus_out_event( GtkWidget* widget, GdkEventFocus* event )
364 {
365 g_message("focus OUT");
366 gboolean blip = parent_class->focus_out_event ? parent_class->focus_out_event(widget, event) : FALSE;
367 return blip;
368 }
369 */
371 static gboolean eek_preview_button_press_cb( GtkWidget* widget, GdkEventButton* event )
372 {
373 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
374 EekPreview* preview = EEK_PREVIEW(widget);
376 if ( preview->_takesFocus && !GTK_WIDGET_HAS_FOCUS(widget) ) {
377 gtk_widget_grab_focus(widget);
378 }
380 if ( event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
381 preview->_hot = TRUE;
382 if ( preview->_within ) {
383 gtk_widget_set_state( widget, GTK_STATE_ACTIVE );
384 }
385 }
386 }
388 return FALSE;
389 }
391 static gboolean eek_preview_button_release_cb( GtkWidget* widget, GdkEventButton* event )
392 {
393 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
394 EekPreview* preview = EEK_PREVIEW(widget);
395 preview->_hot = FALSE;
396 gtk_widget_set_state( widget, GTK_STATE_NORMAL );
397 if ( preview->_within && event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
398 gboolean isAlt = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
400 if ( isAlt ) {
401 g_signal_emit( widget, eek_preview_signals[ALTCLICKED_SIGNAL], 0, 2 );
402 } else {
403 g_signal_emit( widget, eek_preview_signals[CLICKED_SIGNAL], 0 );
404 }
405 }
406 }
407 return FALSE;
408 }
410 gboolean eek_preview_key_press_event( GtkWidget* widget, GdkEventKey* event)
411 {
412 (void)widget;
413 (void)event;
414 g_message("TICK");
415 return FALSE;
416 }
418 gboolean eek_preview_key_release_event( GtkWidget* widget, GdkEventKey* event)
419 {
420 (void)widget;
421 (void)event;
422 g_message("tock");
423 return FALSE;
424 }
426 static void eek_preview_get_property( GObject *object,
427 guint property_id,
428 GValue *value,
429 GParamSpec *pspec)
430 {
431 GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
432 switch ( property_id ) {
433 case FOCUS_PROP_ID:
434 {
435 EekPreview* preview = EEK_PREVIEW( object );
436 g_value_set_boolean( value, preview->_takesFocus );
437 }
438 break;
439 default:
440 {
441 if ( gobjClass->get_property ) {
442 gobjClass->get_property( object, property_id, value, pspec );
443 }
444 }
445 }
446 }
448 static void eek_preview_set_property( GObject *object,
449 guint property_id,
450 const GValue *value,
451 GParamSpec *pspec)
452 {
453 GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
454 switch ( property_id ) {
455 case FOCUS_PROP_ID:
456 {
457 EekPreview* preview = EEK_PREVIEW( object );
458 gboolean val = g_value_get_boolean( value );
459 if ( val != preview->_takesFocus ) {
460 preview->_takesFocus = val;
461 }
462 }
463 break;
464 default:
465 {
466 if ( gobjClass->set_property ) {
467 gobjClass->set_property( object, property_id, value, pspec );
468 }
469 }
470 }
471 }
474 static gboolean eek_preview_popup_menu( GtkWidget* widget )
475 {
476 /* g_message("Do the popup!"); */
477 gboolean blip = parent_class->popup_menu ? parent_class->popup_menu(widget) : FALSE;
478 return blip;
479 }
482 static void eek_preview_class_init( EekPreviewClass *klass )
483 {
484 GObjectClass* gobjClass = G_OBJECT_CLASS(klass);
485 /*GtkObjectClass* objectClass = (GtkObjectClass*)klass;*/
486 GtkWidgetClass* widgetClass = (GtkWidgetClass*)klass;
488 gobjClass->set_property = eek_preview_set_property;
489 gobjClass->get_property = eek_preview_get_property;
491 /*objectClass->destroy = eek_preview_destroy;*/
493 parent_class = (GtkWidgetClass*)g_type_class_peek_parent( klass );
495 /*widgetClass->map = ;*/
496 /*widgetClass->unmap = ;*/
497 /*widgetClass->realize = ;*/
498 /*widgetClass->unrealize = ;*/
499 widgetClass->size_request = eek_preview_size_request;
500 /*widgetClass->size_allocate = ;*/
501 /*widgetClass->state_changed = ;*/
502 /*widgetClass->style_set = ;*/
503 /*widgetClass->grab_notify = ;*/
505 widgetClass->button_press_event = eek_preview_button_press_cb;
506 widgetClass->button_release_event = eek_preview_button_release_cb;
507 /*widgetClass->delete_event = ;*/
508 /*widgetClass->destroy_event = ;*/
509 widgetClass->expose_event = eek_preview_expose_event;
510 /* widgetClass->key_press_event = eek_preview_key_press_event; */
511 /* widgetClass->key_release_event = eek_preview_key_release_event; */
512 widgetClass->enter_notify_event = eek_preview_enter_cb;
513 widgetClass->leave_notify_event = eek_preview_leave_cb;
514 /*widgetClass->configure_event = ;*/
515 /*widgetClass->focus_in_event = eek_preview_focus_in_event;*/
516 /*widgetClass->focus_out_event = eek_preview_focus_out_event;*/
518 /* selection */
519 /*widgetClass->selection_get = ;*/
520 /*widgetClass->selection_received = ;*/
523 /* drag source: */
524 /*widgetClass->drag_begin = ;*/
525 /*widgetClass->drag_end = ;*/
526 /*widgetClass->drag_data_get = ;*/
527 /*widgetClass->drag_data_delete = ;*/
529 /* drag target: */
530 /*widgetClass->drag_leave = ;*/
531 /*widgetClass->drag_motion = ;*/
532 /*widgetClass->drag_drop = ;*/
533 /*widgetClass->drag_data_received = ;*/
535 /* For keybindings: */
536 widgetClass->popup_menu = eek_preview_popup_menu;
537 /*widgetClass->show_help = ;*/
539 /* Accessibility support: */
540 /*widgetClass->get_accessible = ;*/
541 /*widgetClass->screen_changed = ;*/
542 /*widgetClass->can_activate_accel = ;*/
545 eek_preview_signals[CLICKED_SIGNAL] =
546 g_signal_new( "clicked",
547 G_TYPE_FROM_CLASS( klass ),
548 (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
549 G_STRUCT_OFFSET( EekPreviewClass, clicked ),
550 NULL, NULL,
551 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 );
553 eek_preview_signals[ALTCLICKED_SIGNAL] =
554 g_signal_new( "alt-clicked",
555 G_TYPE_FROM_CLASS( klass ),
556 (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
557 G_STRUCT_OFFSET( EekPreviewClass, clicked ),
558 NULL, NULL,
559 g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
560 1, G_TYPE_INT );
563 g_object_class_install_property( gobjClass,
564 FOCUS_PROP_ID,
565 g_param_spec_boolean(
566 "focus-on-click",
567 NULL,
568 "flag to grab focus when clicked",
569 TRUE,
570 (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT)
571 )
572 );
573 }
575 void eek_preview_set_linked( EekPreview* splat, LinkType link )
576 {
577 link = (LinkType)(link & PREVIEW_LINK_ALL);
578 if ( link != (LinkType)splat->_linked ) {
579 splat->_linked = link;
581 gtk_widget_queue_draw( GTK_WIDGET(splat) );
582 }
583 }
585 LinkType eek_preview_get_linked( EekPreview* splat )
586 {
587 return (LinkType)splat->_linked;
588 }
590 gboolean eek_preview_get_focus_on_click( EekPreview* preview )
591 {
592 return preview->_takesFocus;
593 }
595 void eek_preview_set_focus_on_click( EekPreview* preview, gboolean focus_on_click )
596 {
597 if ( focus_on_click != preview->_takesFocus ) {
598 preview->_takesFocus = focus_on_click;
599 }
600 }
602 void eek_preview_set_details( EekPreview* preview, PreviewStyle prevstyle, ViewType view, GtkIconSize size )
603 {
604 preview->_prevstyle = prevstyle;
605 preview->_view = view;
606 preview->_size = size;
608 gtk_widget_queue_draw(GTK_WIDGET(preview));
609 }
611 static void eek_preview_init( EekPreview *preview )
612 {
613 GtkWidget* widg = GTK_WIDGET(preview);
614 GTK_WIDGET_SET_FLAGS( widg, GTK_CAN_FOCUS );
615 GTK_WIDGET_SET_FLAGS( widg, GTK_RECEIVES_DEFAULT );
617 gtk_widget_set_sensitive( widg, TRUE );
619 gtk_widget_add_events(widg, GDK_BUTTON_PRESS_MASK
620 | GDK_BUTTON_RELEASE_MASK
621 | GDK_KEY_PRESS_MASK
622 | GDK_KEY_RELEASE_MASK
623 | GDK_FOCUS_CHANGE_MASK
624 | GDK_ENTER_NOTIFY_MASK
625 | GDK_LEAVE_NOTIFY_MASK );
627 /* gtk_widget_add_events( widg, GDK_ALL_EVENTS_MASK );*/
629 preview->_r = 0x80;
630 preview->_g = 0x80;
631 preview->_b = 0xcc;
633 preview->_hot = FALSE;
634 preview->_within = FALSE;
635 preview->_takesFocus = FALSE;
637 preview->_prevstyle = PREVIEW_STYLE_ICON;
638 preview->_view = VIEW_TYPE_LIST;
639 preview->_size = GTK_ICON_SIZE_BUTTON;
641 /*
642 GdkColor color = {0};
643 color.red = (255 << 8) | 255;
645 GdkColor whack = {0};
646 whack.green = (255 << 8) | 255;
648 gtk_widget_modify_bg( widg, GTK_STATE_NORMAL, &color );
649 gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &whack );
650 */
652 /* GTK_STATE_ACTIVE, */
653 /* GTK_STATE_PRELIGHT, */
654 /* GTK_STATE_SELECTED, */
655 /* GTK_STATE_INSENSITIVE */
657 if ( 0 ) {
658 GdkColor color = {0,0,0,0};
660 color.red = 0xffff;
661 color.green = 0;
662 color.blue = 0xffff;
663 gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
664 gtk_widget_modify_bg(widg, GTK_STATE_ACTIVE, &color);
666 color.red = 0;
667 color.green = 0xffff;
668 color.blue = 0;
669 gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
670 gtk_widget_modify_bg(widg, GTK_STATE_SELECTED, &color);
672 color.red = 0xffff;
673 color.green = 0;
674 color.blue = 0;
675 gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
676 gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &color );
677 }
678 }
681 GtkWidget* eek_preview_new(void)
682 {
683 return GTK_WIDGET( g_object_new( EEK_PREVIEW_TYPE, NULL ) );
684 }