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 GtkWidget* eek_preview_area_new(void)
94 {
95 return NULL;
96 }
98 static void eek_preview_size_request( GtkWidget* widget, GtkRequisition* req )
99 {
100 gint width = 0;
101 gint height = 0;
102 EekPreview* preview = EEK_PREVIEW(widget);
103 gboolean worked = gtk_icon_size_lookup( preview->_size, &width, &height );
104 if ( !worked ) {
105 width = 16;
106 height = 16;
107 }
108 if ( preview->_view == VIEW_TYPE_LIST ) {
109 width *= 3;
110 }
111 req->width = width;
112 req->height = height;
113 }
115 enum {
116 CLICKED_SIGNAL,
117 ALTCLICKED_SIGNAL,
118 LAST_SIGNAL
119 };
122 static guint eek_preview_signals[LAST_SIGNAL] = { 0 };
125 gboolean eek_preview_expose_event( GtkWidget* widget, GdkEventExpose* event )
126 {
127 /* g_message("Exposed!!! %s", GTK_WIDGET_HAS_FOCUS(widget) ? "XXX" : "---" ); */
128 gint insetX = 0;
129 gint insetY = 0;
131 /*
132 gint lower = widget->allocation.width;
133 lower = (widget->allocation.height < lower) ? widget->allocation.height : lower;
134 if ( lower > 16 ) {
135 insetX++;
136 if ( lower > 18 ) {
137 insetX++;
138 if ( lower > 22 ) {
139 insetX++;
140 if ( lower > 24 ) {
141 insetX++;
142 if ( lower > 32 ) {
143 insetX++;
144 }
145 }
146 }
147 }
148 insetY = insetX;
149 }
150 */
152 if ( GTK_WIDGET_DRAWABLE( widget ) ) {
153 GtkStyle* style = gtk_widget_get_style( widget );
155 if ( insetX > 0 || insetY > 0 ) {
156 gtk_paint_flat_box( style,
157 widget->window,
158 (GtkStateType)GTK_WIDGET_STATE(widget),
159 GTK_SHADOW_NONE,
160 NULL,
161 widget,
162 NULL,
163 0, 0,
164 widget->allocation.width, widget->allocation.height);
165 }
167 GdkGC *gc = gdk_gc_new( widget->window );
168 EekPreview* preview = EEK_PREVIEW(widget);
169 GdkColor fg = {0, preview->_r, preview->_g, preview->_b};
170 gdk_colormap_alloc_color( gdk_colormap_get_system(), &fg, FALSE, TRUE );
172 gdk_gc_set_foreground( gc, &fg );
174 gdk_draw_rectangle( widget->window,
175 gc,
176 TRUE,
177 insetX, insetY,
178 widget->allocation.width - (insetX * 2), widget->allocation.height - (insetY * 2) );
180 if ( preview->_linked ) {
181 /* Draw arrow */
182 GdkRectangle possible = {insetX, insetY, (widget->allocation.width - (insetX * 2)), (widget->allocation.height - (insetY * 2)) };
183 GdkRectangle area = {possible.x, possible.y, possible.width / 2, possible.height / 2 };
185 /* Make it square */
186 if ( area.width > area.height )
187 area.width = area.height;
188 if ( area.height > area.width )
189 area.height = area.width;
191 /* Center it horizontally */
192 if ( area.width < possible.width ) {
193 int diff = (possible.width - area.width) / 2;
194 area.x += diff;
195 }
198 if ( preview->_linked & PREVIEW_LINK_IN ) {
199 gtk_paint_arrow( style,
200 widget->window,
201 (GtkStateType)widget->state,
202 GTK_SHADOW_ETCHED_IN,
203 NULL, /* clip area. &area, */
204 widget, /* may be NULL */
205 NULL, /* detail */
206 GTK_ARROW_DOWN,
207 FALSE,
208 area.x, area.y,
209 area.width, area.height
210 );
211 }
213 if ( preview->_linked & PREVIEW_LINK_OUT ) {
214 GdkRectangle otherArea = {area.x, area.y, area.width, area.height};
215 if ( otherArea.height < possible.height ) {
216 otherArea.y = possible.y + (possible.height - otherArea.height);
217 }
219 gtk_paint_arrow( style,
220 widget->window,
221 (GtkStateType)widget->state,
222 GTK_SHADOW_ETCHED_OUT,
223 NULL, /* clip area. &area, */
224 widget, /* may be NULL */
225 NULL, /* detail */
226 GTK_ARROW_UP,
227 FALSE,
228 otherArea.x, otherArea.y,
229 otherArea.width, otherArea.height
230 );
231 }
233 if ( preview->_linked & PREVIEW_LINK_OTHER ) {
234 GdkRectangle otherArea = {insetX, area.y, area.width, area.height};
235 if ( otherArea.height < possible.height ) {
236 otherArea.y = possible.y + (possible.height - otherArea.height) / 2;
237 }
239 gtk_paint_arrow( style,
240 widget->window,
241 (GtkStateType)widget->state,
242 GTK_SHADOW_ETCHED_OUT,
243 NULL, /* clip area. &area, */
244 widget, /* may be NULL */
245 NULL, /* detail */
246 GTK_ARROW_LEFT,
247 FALSE,
248 otherArea.x, otherArea.y,
249 otherArea.width, otherArea.height
250 );
251 }
252 }
254 if ( GTK_WIDGET_HAS_FOCUS(widget) ) {
255 gtk_paint_focus( style,
256 widget->window,
257 GTK_STATE_NORMAL,
258 NULL, /* GdkRectangle *area, */
259 widget,
260 NULL,
261 0 + 1, 0 + 1,
262 widget->allocation.width - 2, widget->allocation.height - 2 );
263 }
264 }
267 return FALSE;
268 }
271 static gboolean eek_preview_enter_cb( GtkWidget* widget, GdkEventCrossing* event )
272 {
273 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
274 EekPreview* preview = EEK_PREVIEW(widget);
275 preview->_within = TRUE;
276 gtk_widget_set_state( widget, preview->_hot ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT );
277 }
278 return FALSE;
279 }
281 static gboolean eek_preview_leave_cb( GtkWidget* widget, GdkEventCrossing* event )
282 {
283 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
284 EekPreview* preview = EEK_PREVIEW(widget);
285 preview->_within = FALSE;
286 gtk_widget_set_state( widget, GTK_STATE_NORMAL );
287 }
288 return FALSE;
289 }
291 /*
292 static gboolean eek_preview_focus_in_event( GtkWidget* widget, GdkEventFocus* event )
293 {
294 g_message("focus IN");
295 gboolean blip = parent_class->focus_in_event ? parent_class->focus_in_event(widget, event) : FALSE;
296 return blip;
297 }
299 static gboolean eek_preview_focus_out_event( GtkWidget* widget, GdkEventFocus* event )
300 {
301 g_message("focus OUT");
302 gboolean blip = parent_class->focus_out_event ? parent_class->focus_out_event(widget, event) : FALSE;
303 return blip;
304 }
305 */
307 static gboolean eek_preview_button_press_cb( GtkWidget* widget, GdkEventButton* event )
308 {
309 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
310 EekPreview* preview = EEK_PREVIEW(widget);
312 if ( preview->_takesFocus && !GTK_WIDGET_HAS_FOCUS(widget) ) {
313 gtk_widget_grab_focus(widget);
314 }
316 if ( event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
317 preview->_hot = TRUE;
318 if ( preview->_within ) {
319 gtk_widget_set_state( widget, GTK_STATE_ACTIVE );
320 }
321 }
322 }
324 return FALSE;
325 }
327 static gboolean eek_preview_button_release_cb( GtkWidget* widget, GdkEventButton* event )
328 {
329 if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) {
330 EekPreview* preview = EEK_PREVIEW(widget);
331 preview->_hot = FALSE;
332 gtk_widget_set_state( widget, GTK_STATE_NORMAL );
333 if ( preview->_within && event->button == PRIME_BUTTON_MAGIC_NUMBER ) {
334 gboolean isAlt = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
336 if ( isAlt ) {
337 g_signal_emit( widget, eek_preview_signals[ALTCLICKED_SIGNAL], 0, 2 );
338 } else {
339 g_signal_emit( widget, eek_preview_signals[CLICKED_SIGNAL], 0 );
340 }
341 }
342 }
343 return FALSE;
344 }
346 gboolean eek_preview_key_press_event( GtkWidget* widget, GdkEventKey* event)
347 {
348 g_message("TICK");
349 return FALSE;
350 }
352 gboolean eek_preview_key_release_event( GtkWidget* widget, GdkEventKey* event)
353 {
354 g_message("tock");
355 return FALSE;
356 }
358 static void eek_preview_get_property( GObject *object,
359 guint property_id,
360 GValue *value,
361 GParamSpec *pspec)
362 {
363 GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
364 switch ( property_id ) {
365 case FOCUS_PROP_ID:
366 {
367 EekPreview* preview = EEK_PREVIEW( object );
368 g_value_set_boolean( value, preview->_takesFocus );
369 }
370 break;
371 default:
372 {
373 if ( gobjClass->get_property ) {
374 gobjClass->get_property( object, property_id, value, pspec );
375 }
376 }
377 }
378 }
380 static void eek_preview_set_property( GObject *object,
381 guint property_id,
382 const GValue *value,
383 GParamSpec *pspec)
384 {
385 GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class);
386 switch ( property_id ) {
387 case FOCUS_PROP_ID:
388 {
389 EekPreview* preview = EEK_PREVIEW( object );
390 gboolean val = g_value_get_boolean( value );
391 if ( val != preview->_takesFocus ) {
392 preview->_takesFocus = val;
393 }
394 }
395 break;
396 default:
397 {
398 if ( gobjClass->set_property ) {
399 gobjClass->set_property( object, property_id, value, pspec );
400 }
401 }
402 }
403 }
406 static gboolean eek_preview_popup_menu( GtkWidget* widget )
407 {
408 /* g_message("Do the popup!"); */
409 gboolean blip = parent_class->popup_menu ? parent_class->popup_menu(widget) : FALSE;
410 return blip;
411 }
414 static void eek_preview_class_init( EekPreviewClass *klass )
415 {
416 GObjectClass* gobjClass = G_OBJECT_CLASS(klass);
417 /*GtkObjectClass* objectClass = (GtkObjectClass*)klass;*/
418 GtkWidgetClass* widgetClass = (GtkWidgetClass*)klass;
420 gobjClass->set_property = eek_preview_set_property;
421 gobjClass->get_property = eek_preview_get_property;
423 /*objectClass->destroy = eek_preview_destroy;*/
425 parent_class = (GtkWidgetClass*)g_type_class_peek_parent( klass );
427 /*widgetClass->map = ;*/
428 /*widgetClass->unmap = ;*/
429 /*widgetClass->realize = ;*/
430 /*widgetClass->unrealize = ;*/
431 widgetClass->size_request = eek_preview_size_request;
432 /*widgetClass->size_allocate = ;*/
433 /*widgetClass->state_changed = ;*/
434 /*widgetClass->style_set = ;*/
435 /*widgetClass->grab_notify = ;*/
437 widgetClass->button_press_event = eek_preview_button_press_cb;
438 widgetClass->button_release_event = eek_preview_button_release_cb;
439 /*widgetClass->delete_event = ;*/
440 /*widgetClass->destroy_event = ;*/
441 widgetClass->expose_event = eek_preview_expose_event;
442 /* widgetClass->key_press_event = eek_preview_key_press_event; */
443 /* widgetClass->key_release_event = eek_preview_key_release_event; */
444 widgetClass->enter_notify_event = eek_preview_enter_cb;
445 widgetClass->leave_notify_event = eek_preview_leave_cb;
446 /*widgetClass->configure_event = ;*/
447 /*widgetClass->focus_in_event = eek_preview_focus_in_event;*/
448 /*widgetClass->focus_out_event = eek_preview_focus_out_event;*/
450 /* selection */
451 /*widgetClass->selection_get = ;*/
452 /*widgetClass->selection_received = ;*/
455 /* drag source: */
456 /*widgetClass->drag_begin = ;*/
457 /*widgetClass->drag_end = ;*/
458 /*widgetClass->drag_data_get = ;*/
459 /*widgetClass->drag_data_delete = ;*/
461 /* drag target: */
462 /*widgetClass->drag_leave = ;*/
463 /*widgetClass->drag_motion = ;*/
464 /*widgetClass->drag_drop = ;*/
465 /*widgetClass->drag_data_received = ;*/
467 /* For keybindings: */
468 widgetClass->popup_menu = eek_preview_popup_menu;
469 /*widgetClass->show_help = ;*/
471 /* Accessibility support: */
472 /*widgetClass->get_accessible = ;*/
473 /*widgetClass->screen_changed = ;*/
474 /*widgetClass->can_activate_accel = ;*/
477 eek_preview_signals[CLICKED_SIGNAL] =
478 g_signal_new( "clicked",
479 G_TYPE_FROM_CLASS( klass ),
480 (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
481 G_STRUCT_OFFSET( EekPreviewClass, clicked ),
482 NULL, NULL,
483 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 );
485 eek_preview_signals[ALTCLICKED_SIGNAL] =
486 g_signal_new( "alt-clicked",
487 G_TYPE_FROM_CLASS( klass ),
488 (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
489 G_STRUCT_OFFSET( EekPreviewClass, clicked ),
490 NULL, NULL,
491 g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
492 1, G_TYPE_INT );
495 g_object_class_install_property( gobjClass,
496 FOCUS_PROP_ID,
497 g_param_spec_boolean(
498 "focus-on-click",
499 NULL,
500 "flag to grab focus when clicked",
501 TRUE,
502 (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT)
503 )
504 );
505 }
507 void eek_preview_set_linked( EekPreview* splat, LinkType link )
508 {
509 link = (LinkType)(link & PREVIEW_LINK_ALL);
510 if ( link != (LinkType)splat->_linked ) {
511 splat->_linked = link;
513 gtk_widget_queue_draw( GTK_WIDGET(splat) );
514 }
515 }
517 LinkType eek_preview_get_linked( EekPreview* splat )
518 {
519 return (LinkType)splat->_linked;
520 }
522 gboolean eek_preview_get_focus_on_click( EekPreview* preview )
523 {
524 return preview->_takesFocus;
525 }
527 void eek_preview_set_focus_on_click( EekPreview* preview, gboolean focus_on_click )
528 {
529 if ( focus_on_click != preview->_takesFocus ) {
530 preview->_takesFocus = focus_on_click;
531 }
532 }
534 void eek_preview_set_details( EekPreview* preview, PreviewStyle prevstyle, ViewType view, GtkIconSize size )
535 {
536 preview->_prevstyle = prevstyle;
537 preview->_view = view;
538 preview->_size = size;
540 gtk_widget_queue_draw(GTK_WIDGET(preview));
541 }
543 static void eek_preview_init( EekPreview *preview )
544 {
545 GtkWidget* widg = GTK_WIDGET(preview);
546 GTK_WIDGET_SET_FLAGS( widg, GTK_CAN_FOCUS );
547 GTK_WIDGET_SET_FLAGS( widg, GTK_RECEIVES_DEFAULT );
549 gtk_widget_set_sensitive( widg, TRUE );
551 gtk_widget_add_events(widg, GDK_BUTTON_PRESS_MASK
552 | GDK_BUTTON_RELEASE_MASK
553 | GDK_KEY_PRESS_MASK
554 | GDK_KEY_RELEASE_MASK
555 | GDK_FOCUS_CHANGE_MASK
556 | GDK_ENTER_NOTIFY_MASK
557 | GDK_LEAVE_NOTIFY_MASK );
559 /* gtk_widget_add_events( widg, GDK_ALL_EVENTS_MASK );*/
561 preview->_r = 0x80;
562 preview->_g = 0x80;
563 preview->_b = 0xcc;
565 preview->_hot = FALSE;
566 preview->_within = FALSE;
567 preview->_takesFocus = FALSE;
569 preview->_prevstyle = PREVIEW_STYLE_ICON;
570 preview->_view = VIEW_TYPE_LIST;
571 preview->_size = GTK_ICON_SIZE_BUTTON;
573 /*
574 GdkColor color = {0};
575 color.red = (255 << 8) | 255;
577 GdkColor whack = {0};
578 whack.green = (255 << 8) | 255;
580 gtk_widget_modify_bg( widg, GTK_STATE_NORMAL, &color );
581 gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &whack );
582 */
584 /* GTK_STATE_ACTIVE, */
585 /* GTK_STATE_PRELIGHT, */
586 /* GTK_STATE_SELECTED, */
587 /* GTK_STATE_INSENSITIVE */
589 if ( 0 ) {
590 GdkColor color = {0,0,0,0};
592 color.red = 0xffff;
593 color.green = 0;
594 color.blue = 0xffff;
595 gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
596 gtk_widget_modify_bg(widg, GTK_STATE_ACTIVE, &color);
598 color.red = 0;
599 color.green = 0xffff;
600 color.blue = 0;
601 gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
602 gtk_widget_modify_bg(widg, GTK_STATE_SELECTED, &color);
604 color.red = 0xffff;
605 color.green = 0;
606 color.blue = 0;
607 gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE );
608 gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &color );
609 }
610 }
613 GtkWidget* eek_preview_new(void)
614 {
615 return GTK_WIDGET( g_object_new( EEK_PREVIEW_TYPE, NULL ) );
616 }