CVE-2008-3904
[lxde/gpicview.git] / src / main-win.c
CommitLineData
1d48a247 1/***************************************************************************
d9f34bf9 2 * Copyright (C) 2007, 2008 by PCMan (Hong Jen Yee) *
1d48a247
HJYP
3 * pcman.tw@gmail.com *
4 * *
cf15ca10 5 * mw program is free software; you can redistribute it and/or modify *
1d48a247
HJYP
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
cf15ca10 10 * mw program is distributed in the hope that it will be useful, *
1d48a247
HJYP
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
cf15ca10 16 * along with mw program; if not, write to the *
1d48a247
HJYP
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
cf15ca10 25#include "main-win.h"
bb0f721e 26
1d48a247
HJYP
27#include <glib/gi18n.h>
28#include <glib/gstdio.h>
29#include <gdk/gdkkeysyms.h>
30
0ed1aa41 31#include <unistd.h>
1d48a247
HJYP
32#include <string.h>
33#include <errno.h>
da8bd946 34#include <math.h>
d2d3c393 35#include <stdlib.h>
1d48a247 36
bb0f721e
HJYP
37#include "pref.h"
38
9c52ae58 39#include "image-view.h"
0c0c8179 40#include "image-list.h"
1d48a247 41#include "working-area.h"
cf15ca10 42#include "ptk-menu.h"
7c3b160c 43#include "jpeg-tran.h"
1d48a247
HJYP
44
45// For drag & drop
cf15ca10 46static GtkTargetEntry drop_targets[] =
1d48a247
HJYP
47{
48 {"text/uri-list", 0, 0},
cf15ca10 49 {"text/plain", 0, 1}
1d48a247
HJYP
50};
51
cf15ca10
HJYP
52static void main_win_init( MainWin*mw );
53static void main_win_finalize( GObject* obj );
54
55static void create_nav_bar( MainWin* mw, GtkWidget* box);
56GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle);
57// GtkWidget* add_menu_item( GtkMenuShell* menu, const char* label, const char* icon, GCallback cb, gboolean toggle=FALSE );
da046c6e 58static void rotate_image( MainWin* mw, int angle );
cf15ca10
HJYP
59static void show_popup_menu( MainWin* mw, GdkEventButton* evt );
60
61/* signal handlers */
62static gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt );
63static void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation );
86ebaeb3 64static gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state );
cf15ca10
HJYP
65static void on_zoom_fit( GtkToggleButton* btn, MainWin* mw );
66static void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw );
67static void on_full_screen( GtkWidget* btn, MainWin* mw );
68static void on_next( GtkWidget* btn, MainWin* mw );
69static void on_orig_size( GtkToggleButton* btn, MainWin* mw );
70static void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw );
71static void on_prev( GtkWidget* btn, MainWin* mw );
72static void on_rotate_clockwise( GtkWidget* btn, MainWin* mw );
73static void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw );
74static void on_save_as( GtkWidget* btn, MainWin* mw );
75static void on_save( GtkWidget* btn, MainWin* mw );
76static void on_open( GtkWidget* btn, MainWin* mw );
77static void on_zoom_in( GtkWidget* btn, MainWin* mw );
78static void on_zoom_out( GtkWidget* btn, MainWin* mw );
79static void on_preference( GtkWidget* btn, MainWin* mw );
80static void on_quit( GtkWidget* btn, MainWin* mw );
81static gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
82static gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
83static gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw );
84static gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw );
85static gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key);
86static void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
87 int x, int y, GtkSelectionData* data, guint info,
88 guint time, MainWin* mw );
89static void on_delete( GtkWidget* btn, MainWin* mw );
90static void on_about( GtkWidget* menu, MainWin* mw );
1d48a247 91
da046c6e 92static GdkPixbuf* RotateByEXIF(const char* FileName, GdkPixbuf* pix);
93static void updateTitle(const char *filename, MainWin *mw );
94
95void on_flip_vertical( GtkWidget* btn, MainWin* mw );
96void on_flip_horizontal( GtkWidget* btn, MainWin* mw );
97static int trans_angle_to_id(int i);
98static int get_new_angle( int orig_angle, int rotate_angle );
1d48a247 99
cf15ca10 100// Begin of GObject-related stuff
1d48a247 101
cf15ca10 102G_DEFINE_TYPE( MainWin, main_win, GTK_TYPE_WINDOW )
1d48a247 103
cf15ca10
HJYP
104void main_win_class_init( MainWinClass* klass )
105{
1d48a247
HJYP
106 GObjectClass * obj_class;
107 GtkWidgetClass *widget_class;
108
109 obj_class = ( GObjectClass * ) klass;
110// obj_class->set_property = _set_property;
111// obj_class->get_property = _get_property;
cf15ca10 112 obj_class->finalize = main_win_finalize;
1d48a247
HJYP
113
114 widget_class = GTK_WIDGET_CLASS ( klass );
115 widget_class->delete_event = on_delete_event;
116 widget_class->size_allocate = on_size_allocate;
117 widget_class->key_press_event = on_key_press_event;
e216a767 118 widget_class->window_state_event = on_win_state_event;
1d48a247
HJYP
119}
120
cf15ca10
HJYP
121void main_win_finalize( GObject* obj )
122{
123 MainWin *mw = (MainWin*)obj;
124
125 main_win_close(mw);
126
127 if( G_LIKELY(mw->img_list) )
128 image_list_free( mw->img_list );
129 gdk_cursor_unref( mw->hand_cursor );
130
131 g_object_unref( mw->tooltips );
132
133 // FIXME: Put this here is weird
134 gtk_main_quit();
135}
136
137GtkWidget* main_win_new()
1d48a247 138{
cf15ca10 139 return (GtkWidget*)g_object_new ( MAIN_WIN_TYPE, NULL );
1d48a247
HJYP
140}
141
142// End of GObject-related stuff
143
cf15ca10 144void main_win_init( MainWin*mw )
1d48a247 145{
cf15ca10
HJYP
146 gtk_window_set_title( (GtkWindow*)mw, _("Image Viewer"));
147 gtk_window_set_icon_from_file( (GtkWindow*)mw, PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL );
148 gtk_window_set_default_size( (GtkWindow*)mw, 640, 480 );
1d48a247
HJYP
149
150 GtkWidget* box = gtk_vbox_new( FALSE, 0 );
cf15ca10 151 gtk_container_add( (GtkContainer*)mw, box);
1d48a247
HJYP
152
153 // image area
cf15ca10
HJYP
154 mw->evt_box = gtk_event_box_new();
155 GTK_WIDGET_SET_FLAGS( mw->evt_box, GTK_CAN_FOCUS );
156 gtk_widget_add_events( mw->evt_box,
1d48a247
HJYP
157 GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
158 GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK );
cf15ca10
HJYP
159 g_signal_connect( mw->evt_box, "button-press-event", G_CALLBACK(on_button_press), mw );
160 g_signal_connect( mw->evt_box, "button-release-event", G_CALLBACK(on_button_release), mw );
161 g_signal_connect( mw->evt_box, "motion-notify-event", G_CALLBACK(on_mouse_move), mw );
162 g_signal_connect( mw->evt_box, "scroll-event", G_CALLBACK(on_scroll_event), mw );
5bfec971
HJYP
163 // Set bg color to white
164 GdkColor white = {0, 65535, 65535, 65535};
cf15ca10 165 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &white );
1d48a247 166
cf15ca10
HJYP
167 mw->img_view = image_view_new();
168 gtk_container_add( (GtkContainer*)mw->evt_box, (GtkWidget*)mw->img_view);
1d48a247 169
2c34ffe0
HJYP
170 const char scroll_style[]=
171 "style \"gpicview-scroll\" {"
172 "GtkScrolledWindow::scrollbar-spacing=0"
173 "}"
174 "class \"GtkScrolledWindow\" style \"gpicview-scroll\"";
175 gtk_rc_parse_string( scroll_style );
cf15ca10
HJYP
176 mw->scroll = gtk_scrolled_window_new( NULL, NULL );
177 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)mw->scroll, GTK_SHADOW_NONE );
178 gtk_scrolled_window_set_policy((GtkScrolledWindow*)mw->scroll,
1d48a247
HJYP
179 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
180 GtkAdjustment *hadj, *vadj;
cf15ca10 181 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
182 hadj->page_increment = 10;
183 gtk_adjustment_changed(hadj);
cf15ca10 184 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
185 vadj->page_increment = 10;
186 gtk_adjustment_changed(vadj);
9c52ae58 187
d2d3c393 188 image_view_set_adjustments( IMAGE_VIEW(mw->img_view), hadj, vadj ); // dirty hack :-(
cf15ca10
HJYP
189 gtk_scrolled_window_add_with_viewport( (GtkScrolledWindow*)mw->scroll, mw->evt_box );
190 GtkWidget* viewport = gtk_bin_get_child( (GtkBin*)mw->scroll );
2c34ffe0
HJYP
191 gtk_viewport_set_shadow_type( (GtkViewport*)viewport, GTK_SHADOW_NONE );
192 gtk_container_set_border_width( (GtkContainer*)viewport, 0 );
1d48a247 193
cf15ca10 194 gtk_box_pack_start( (GtkBox*)box, mw->scroll, TRUE, TRUE, 0 );
1d48a247
HJYP
195
196 // build toolbar
cf15ca10 197 mw->tooltips = gtk_tooltips_new();
d9f34bf9 198#if GTK_CHECK_VERSION(2, 8, 0)
cf15ca10 199 g_object_ref_sink(mw->tooltips);
1d48a247 200#else
cf15ca10 201 gtk_object_sink((GtkObject*)mw->tooltips);
1d48a247 202#endif
1d48a247 203
cf15ca10 204 create_nav_bar( mw, box );
1d48a247
HJYP
205 gtk_widget_show_all( box );
206
cf15ca10 207 mw->hand_cursor = gdk_cursor_new_for_display( gtk_widget_get_display((GtkWidget*)mw), GDK_FLEUR );
1d48a247 208
5bfec971 209// zoom_mode = ZOOM_NONE;
cf15ca10 210 mw->zoom_mode = ZOOM_FIT;
1d48a247
HJYP
211
212 // Set up drag & drop
cf15ca10
HJYP
213 gtk_drag_dest_set( (GtkWidget*)mw, GTK_DEST_DEFAULT_ALL,
214 drop_targets,
215 G_N_ELEMENTS(drop_targets),
216 GDK_ACTION_COPY | GDK_ACTION_ASK );
217 g_signal_connect( mw, "drag-data-received", G_CALLBACK(on_drag_data_received), mw );
9c52ae58 218
cf15ca10 219 mw->img_list = image_list_new();
7c3b160c 220
7c3b160c
HJYP
221 // rotation angle is zero on startup
222 mw->rotation_angle = 0;
1d48a247
HJYP
223}
224
cf15ca10 225void create_nav_bar( MainWin* mw, GtkWidget* box )
1d48a247 226{
cf15ca10 227 mw->nav_bar = gtk_hbox_new( FALSE, 0 );
1d48a247 228
cf15ca10
HJYP
229 add_nav_btn( mw, GTK_STOCK_GO_BACK, _("Previous"), G_CALLBACK(on_prev), FALSE );
230 add_nav_btn( mw, GTK_STOCK_GO_FORWARD, _("Next"), G_CALLBACK(on_next), FALSE );
1d48a247 231
cf15ca10 232 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 233
cf15ca10
HJYP
234 add_nav_btn( mw, GTK_STOCK_ZOOM_OUT, _("Zoom Out"), G_CALLBACK(on_zoom_out), FALSE );
235 add_nav_btn( mw, GTK_STOCK_ZOOM_IN, _("Zoom In"), G_CALLBACK(on_zoom_in), FALSE );
5bfec971
HJYP
236
237// percent = gtk_entry_new(); // show scale (in percentage)
cf15ca10 238// g_signal_connect( percent, "activate", G_CALLBACK(on_percentage), mw );
5bfec971
HJYP
239// gtk_widget_set_size_request( percent, 45, -1 );
240// gtk_box_pack_start( (GtkBox*)nav_bar, percent, FALSE, FALSE, 2 );
241
cf15ca10
HJYP
242 mw->btn_fit = add_nav_btn( mw, GTK_STOCK_ZOOM_FIT, _("Fit Image To Window Size"),
243 G_CALLBACK(on_zoom_fit), TRUE );
244 mw->btn_orig = add_nav_btn( mw, GTK_STOCK_ZOOM_100, _("Original Size"),
245 G_CALLBACK(on_orig_size), TRUE );
246 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
5bfec971 247
1d48a247
HJYP
248#ifndef GTK_STOCK_FULLSCREEN
249#define GTK_STOCK_FULLSCREEN "gtk-fullscreen"
250#endif
cf15ca10 251 add_nav_btn( mw, GTK_STOCK_FULLSCREEN, _(" Full Screen"), G_CALLBACK(on_full_screen), FALSE ); // gtk+ 2.8+
1d48a247 252
cf15ca10 253 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 254
cf15ca10
HJYP
255 add_nav_btn( mw, "gtk-counterclockwise", _("Rotate Counterclockwise"),
256 G_CALLBACK(on_rotate_counterclockwise), FALSE );
257 add_nav_btn( mw, "gtk-clockwise", _("Rotate Clockwise"), G_CALLBACK(on_rotate_clockwise), FALSE );
1d48a247 258
cf15ca10 259 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 260
cf15ca10
HJYP
261 add_nav_btn( mw, GTK_STOCK_OPEN, _("Open File"), G_CALLBACK(on_open), FALSE );
262 add_nav_btn( mw, GTK_STOCK_SAVE, _("Save File"), G_CALLBACK(on_save), FALSE );
263 add_nav_btn( mw, GTK_STOCK_SAVE_AS, _("Save File As"), G_CALLBACK(on_save_as), FALSE );
264 add_nav_btn( mw, GTK_STOCK_DELETE, _("Delete File"), G_CALLBACK(on_delete), FALSE );
1d48a247 265
cf15ca10 266 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
bb0f721e 267 add_nav_btn( mw, GTK_STOCK_PREFERENCES, _("Preferences"), G_CALLBACK(on_preference), FALSE );
1d48a247
HJYP
268
269 GtkWidget* align = gtk_alignment_new( 0.5, 0, 0, 0 );
cf15ca10 270 gtk_container_add( (GtkContainer*)align, mw->nav_bar );
1d48a247
HJYP
271 gtk_box_pack_start( (GtkBox*)box, align, FALSE, TRUE, 2 );
272}
273
cf15ca10 274gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt )
1d48a247
HJYP
275{
276 gtk_widget_destroy( widget );
277 return TRUE;
278}
279
da046c6e 280static GdkPixbuf* RotateByEXIF(const char* FileName, GdkPixbuf* pix)
281{
282 GdkPixbuf* tmppixbuf = pix;
283#if GTK_CHECK_VERSION( 2, 12, 0 )
284 // apply orientation provided by EXIF (Use gtk+ 2.12 specific API)
285 tmppixbuf = gdk_pixbuf_apply_embedded_orientation(pix);
286 g_object_unref( pix );
287#else
288 // use jhead functions
289 ResetJpgfile();
290
291 // Start with an empty image information structure.
292 memset(&ImageInfo, 0, sizeof(ImageInfo));
293
294 if (!ReadJpegFile( FileName, READ_METADATA)) return;
295
296 // Do Rotate
297 switch(ImageInfo.Orientation)
298 {
299 case 0: // Undefined
300 case 1: // Normal
301 break;
302 case 2: // flip horizontal: left right reversed mirror
303 tmppixbuf = gdk_pixbuf_flip(pix, TRUE);
304 g_object_unref( pix );
305 break;
306 case 3: // rotate 180
307 tmppixbuf = gdk_pixbuf_rotate_simple(pix, 180);
308 g_object_unref( pix );
309 break;
310 case 4: // flip vertical: upside down mirror
311 tmppixbuf = gdk_pixbuf_flip(pix, FALSE);
312 g_object_unref( pix );
313 break;
314 case 5: // transpose: Flipped about top-left <--> bottom-right axis.
315 tmppixbuf = gtk_pixbuf_flip(pix, FALSE);
316 g_object_unref( pix );
317 pix = tmppixbuf;
318 tmppixbuf = gtk_pixbuf_rotate_simple(pix, 270);
319 g_object_unref( pix );
320 break;
321 case 6: // rotate 90: rotate 90 cw to right it.
322 tmppixbuf = gdk_pixbuf_rotate_simple(pix, 270);
323 g_object_unref( pix );
324 break;
325 case 7: // transverse: flipped about top-right <--> bottom-left axis
326 tmppixbuf = gtk_pixbuf_flip(pix, FALSE);
327 g_object_unref( pix );
328 pix = tmppixbuf;
329 tmppixbuf = gtk_pixbuf_rotate_simple(pix, 90);
330 g_object_unref( pix );
331 break;
332 case 8: // rotate 270: rotate 270 to right it.
333 tmppixbuf = gdk_pixbuf_rotate_simple(pix, 90);
334 g_object_unref( pix );
335 break;
336 default:
337 break;
338 }
339
340 DiscardData();
341#endif
342
343 return tmppixbuf;
344}
345
346static void updateTitle(const char *filename, MainWin *mw )
347{
348 static char fname[50];
349 static int wid, hei;
350
351 char buf[100];
352
353 if(filename != NULL)
354 {
355 strncpy(fname, filename, 49);
356 fname[49] = '\0';
357
358 wid = gdk_pixbuf_get_width( mw->pix );
359 hei = gdk_pixbuf_get_height( mw->pix );
360 }
361
362 snprintf(buf, 100, "%s (%dx%d) %d%%", fname, wid, hei, (int)(mw->scale * 100));
363 gtk_window_set_title( (GtkWindow*)mw, buf );
364
365 return;
366}
367
cf15ca10 368gboolean main_win_open( MainWin* mw, const char* file_path, ZoomMode zoom )
1d48a247 369{
1d48a247 370 GError* err = NULL;
da046c6e 371 GdkPixbufFormat* info;
372 info = gdk_pixbuf_get_file_info( file_path, NULL, NULL );
373 char* type = gdk_pixbuf_format_get_name( info );
374
cf15ca10
HJYP
375 main_win_close( mw );
376 mw->pix = gdk_pixbuf_new_from_file( file_path, &err );
c215db47 377
cf15ca10 378 if( ! mw->pix )
1d48a247 379 {
cf15ca10
HJYP
380 main_win_show_error( mw, err->message );
381 return FALSE;
1d48a247 382 }
da046c6e 383 else if(!strcmp(type,"jpeg"))
c215db47 384 {
da046c6e 385 // Only jpeg should rotate by EXIF
386 mw->pix = RotateByEXIF( file_path, mw->pix);
c215db47 387 }
da046c6e 388
cf15ca10 389 mw->zoom_mode = zoom;
da8bd946 390
5bfec971 391 // select most suitable viewing mode
d8ce3af1 392 if( zoom == ZOOM_NONE )
1d48a247 393 {
cf15ca10
HJYP
394 int w = gdk_pixbuf_get_width( mw->pix );
395 int h = gdk_pixbuf_get_height( mw->pix );
1d48a247 396
1d48a247 397 GdkRectangle area;
cf15ca10 398 get_working_area( gtk_widget_get_screen((GtkWidget*)mw), &area );
c325c989
HJYP
399 // g_debug("determine best zoom mode: orig size: w=%d, h=%d", w, h);
400 // FIXME: actually this is a little buggy :-(
da8bd946 401 if( w < area.width && h < area.height && (w >= 640 || h >= 480) )
1d48a247 402 {
cf15ca10
HJYP
403 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER );
404 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, w, h );
5bfec971 405 GtkRequisition req;
cf15ca10 406 gtk_widget_size_request ( (GtkWidget*)mw, &req );
9c52ae58
HJYP
407 if( req.width < 640 ) req.width = 640;
408 if( req.height < 480 ) req.height = 480;
cf15ca10
HJYP
409 gtk_window_resize( (GtkWindow*)mw, req.width, req.height );
410 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, -1, -1 );
411 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
412 mw->zoom_mode = ZOOM_ORIG;
da046c6e 413 mw->scale = 1.0;
1d48a247
HJYP
414 }
415 else
cf15ca10 416 mw->zoom_mode = ZOOM_FIT;
5bfec971 417 }
5bfec971 418
cf15ca10 419 if( mw->zoom_mode == ZOOM_FIT )
d8ce3af1 420 {
cf15ca10 421 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
c325c989 422 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
d8ce3af1 423 }
cf15ca10 424 else if( mw->zoom_mode == ZOOM_SCALE ) // scale
d8ce3af1 425 {
cf15ca10
HJYP
426 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
427 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
428 main_win_scale_image( mw, mw->scale, GDK_INTERP_BILINEAR );
d8ce3af1 429 }
cf15ca10 430 else if( mw->zoom_mode == ZOOM_ORIG ) // original size
5bfec971 431 {
cf15ca10 432 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
da046c6e 433 image_view_set_scale( (ImageView*)mw->img_view, mw->scale, GDK_INTERP_BILINEAR );
cf15ca10 434 main_win_center_image( mw );
1d48a247
HJYP
435 }
436
cf15ca10 437 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
9c52ae58 438
1d48a247
HJYP
439// while (gtk_events_pending ())
440// gtk_main_iteration ();
441
442 // build file list
443 char* dir_path = g_path_get_dirname( file_path );
cf15ca10 444 image_list_open_dir( mw->img_list, dir_path, NULL );
f214531d 445 //image_list_sort_by_name( mw->img_list, GTK_SORT_ASCENDING );
446 image_list_sort_by_name( mw->img_list, GTK_SORT_DESCENDING );
1d48a247 447 g_free( dir_path );
76e88fcf 448
1d48a247 449 char* base_name = g_path_get_basename( file_path );
cf15ca10 450 image_list_set_current( mw->img_list, base_name );
76e88fcf
HJYP
451
452 char* disp_name = g_filename_display_name( base_name );
1d48a247
HJYP
453 g_free( base_name );
454
da046c6e 455 updateTitle( disp_name, mw );
76e88fcf 456 g_free( disp_name );
1d48a247 457
cf15ca10 458 return TRUE;
1d48a247
HJYP
459}
460
cf15ca10 461void main_win_close( MainWin* mw )
1d48a247 462{
cf15ca10 463 if( mw->pix )
9c52ae58 464 {
cf15ca10
HJYP
465 g_object_unref( mw->pix );
466 mw->pix = NULL;
1d48a247
HJYP
467 }
468}
469
cf15ca10 470void main_win_show_error( MainWin* mw, const char* message )
1d48a247 471{
cf15ca10 472 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
473 GTK_DIALOG_MODAL,
474 GTK_MESSAGE_ERROR,
475 GTK_BUTTONS_OK,
476 message );
477 gtk_dialog_run( (GtkDialog*)dlg );
478 gtk_widget_destroy( dlg );
479}
480
cf15ca10 481void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation )
1d48a247 482{
cf15ca10 483 GTK_WIDGET_CLASS(main_win_parent_class)->size_allocate( widget, allocation );
52c867e9 484 if( GTK_WIDGET_REALIZED (widget) )
1d48a247 485 {
cf15ca10 486 MainWin* mw = (MainWin*)widget;
9c52ae58 487
cf15ca10 488 if( mw->zoom_mode == ZOOM_FIT )
52c867e9
HJYP
489 {
490 while(gtk_events_pending ())
491 gtk_main_iteration(); // makes it more fluid
1d48a247 492
c325c989 493 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
52c867e9 494 }
1d48a247
HJYP
495 }
496}
497
86ebaeb3 498gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state )
e216a767
HJYP
499{
500 MainWin* mw = (MainWin*)widget;
501 if( state->new_window_state == GDK_WINDOW_STATE_FULLSCREEN )
502 {
503 static GdkColor black = {0};
504 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &black );
505 gtk_widget_hide( gtk_widget_get_parent(mw->nav_bar) );
506 mw->full_screen = TRUE;
507 }
508 else
509 {
510// gtk_widget_reset_rc_styles( mw->evt_box );
511 static GdkColor white = {0, 65535, 65535, 65535};
512 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &white );
513 gtk_widget_show( gtk_widget_get_parent(mw->nav_bar) );
514 mw->full_screen = FALSE;
515 }
86ebaeb3 516 return TRUE;
e216a767
HJYP
517}
518
cf15ca10 519void main_win_fit_size( MainWin* mw, int width, int height, gboolean can_strech, GdkInterpType type )
1d48a247 520{
cf15ca10 521 if( ! mw->pix )
1d48a247
HJYP
522 return;
523
cf15ca10
HJYP
524 int orig_w = gdk_pixbuf_get_width( mw->pix );
525 int orig_h = gdk_pixbuf_get_height( mw->pix );
1d48a247 526
da8bd946
HJYP
527 if( can_strech || (orig_w > width || orig_h > height) )
528 {
cf15ca10
HJYP
529 gdouble xscale = ((gdouble)width) / orig_w;
530 gdouble yscale = ((gdouble)height)/ orig_h;
531 gdouble final_scale = xscale < yscale ? xscale : yscale;
1d48a247 532
cf15ca10 533 main_win_scale_image( mw, final_scale, type );
da8bd946
HJYP
534 }
535 else // use original size if the image is smaller than the window
536 {
cf15ca10
HJYP
537 mw->scale = 1.0;
538 image_view_set_scale( (ImageView*)mw->img_view, 1.0, type );
da046c6e 539
540 updateTitle(NULL, mw);
da8bd946 541 }
1d48a247
HJYP
542}
543
cf15ca10 544void main_win_fit_window_size( MainWin* mw, gboolean can_strech, GdkInterpType type )
1d48a247 545{
cf15ca10 546 mw->zoom_mode = ZOOM_FIT;
94fcf707 547
cf15ca10 548 if( mw->pix == NULL )
da8bd946 549 return;
94fcf707 550
cf15ca10 551 main_win_fit_size( mw, mw->scroll->allocation.width, mw->scroll->allocation.height, can_strech, type );
1d48a247
HJYP
552}
553
cf15ca10 554GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle )
1d48a247
HJYP
555{
556 GtkWidget* img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
557 GtkWidget* btn;
558 if( G_UNLIKELY(toggle) )
559 {
560 btn = gtk_toggle_button_new();
cf15ca10 561 g_signal_connect( btn, "toggled", cb, mw );
1d48a247
HJYP
562 }
563 else
564 {
565 btn = gtk_button_new();
cf15ca10 566 g_signal_connect( btn, "clicked", cb, mw );
1d48a247
HJYP
567 }
568 gtk_button_set_relief( (GtkButton*)btn, GTK_RELIEF_NONE );
569 gtk_button_set_focus_on_click( (GtkButton*)btn, FALSE );
570 gtk_container_add( (GtkContainer*)btn, img );
cf15ca10
HJYP
571 gtk_tooltips_set_tip( mw->tooltips, btn, tip, NULL );
572 gtk_box_pack_start( (GtkBox*)mw->nav_bar, btn, FALSE, FALSE, 0 );
1d48a247
HJYP
573 return btn;
574}
575
cf15ca10 576void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw )
1d48a247 577{
cf15ca10 578 gtk_button_clicked( (GtkButton*)mw->btn_fit );
1d48a247
HJYP
579}
580
cf15ca10 581void on_zoom_fit( GtkToggleButton* btn, MainWin* mw )
1d48a247
HJYP
582{
583 if( ! btn->active )
584 {
cf15ca10 585 if( mw->zoom_mode == ZOOM_FIT )
1d48a247
HJYP
586 gtk_toggle_button_set_active( btn, TRUE );
587 return;
588 }
cf15ca10 589 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1d48a247 590
c325c989 591 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
592}
593
cf15ca10 594void on_full_screen( GtkWidget* btn, MainWin* mw )
1d48a247 595{
cf15ca10 596 if( ! mw->full_screen )
cf15ca10 597 gtk_window_fullscreen( (GtkWindow*)mw );
1d48a247 598 else
cf15ca10 599 gtk_window_unfullscreen( (GtkWindow*)mw );
1d48a247
HJYP
600}
601
cf15ca10 602void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw )
1d48a247 603{
cf15ca10 604 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
605}
606
cf15ca10 607void on_orig_size( GtkToggleButton* btn, MainWin* mw )
1d48a247 608{
cf15ca10 609 // this callback could be called from activate signal of menu item.
1d48a247
HJYP
610 if( GTK_IS_MENU_ITEM(btn) )
611 {
cf15ca10 612 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
613 return;
614 }
615
616 if( ! btn->active )
617 {
cf15ca10 618 if( mw->zoom_mode == ZOOM_ORIG )
1d48a247
HJYP
619 gtk_toggle_button_set_active( btn, TRUE );
620 return;
621 }
cf15ca10
HJYP
622 mw->zoom_mode = ZOOM_ORIG;
623 mw->scale = 1.0;
624// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 625// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 626
da046c6e 627 // update scale
628 updateTitle(NULL, mw);
629
cf15ca10 630 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1d48a247 631
cf15ca10 632 if( ! mw->pix )
1d48a247
HJYP
633 return;
634
cf15ca10 635 image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
1d48a247 636
9c52ae58
HJYP
637 while (gtk_events_pending ())
638 gtk_main_iteration ();
639
cf15ca10 640 main_win_center_image( mw ); // FIXME: mw doesn't work well. Why?
1d48a247
HJYP
641}
642
f34b3b31 643void on_prev( GtkWidget* btn, MainWin* mw )
1d48a247 644{
cf15ca10
HJYP
645 const char* name;
646 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
647 return;
648
cf15ca10 649 name = image_list_get_prev( mw->img_list );
1d48a247 650
cf15ca10 651 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
652 {
653 // FIXME: need to ask user first?
cf15ca10 654 name = image_list_get_last( mw->img_list );
1d48a247
HJYP
655 }
656
657 if( name )
658 {
cf15ca10
HJYP
659 char* file_path = image_list_get_current_file_path( mw->img_list );
660 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
661 g_free( file_path );
662 }
663}
664
f34b3b31 665void on_next( GtkWidget* btn, MainWin* mw )
1d48a247 666{
cf15ca10 667 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
668 return;
669
cf15ca10 670 const char* name = image_list_get_next( mw->img_list );
1d48a247 671
cf15ca10 672 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
673 {
674 // FIXME: need to ask user first?
cf15ca10 675 name = image_list_get_first( mw->img_list );
1d48a247
HJYP
676 }
677
678 if( name )
679 {
cf15ca10
HJYP
680 char* file_path = image_list_get_current_file_path( mw->img_list );
681 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
682 g_free( file_path );
683 }
684}
685
da046c6e 686//////////////////// rotate & flip
687
688static int trans_angle_to_id(int i)
689{
690 if(i == 0) return 1;
691 else if(i == 90) return 6;
692 else if(i == 180) return 3;
693 else if(i == 270) return 8;
694 else if(i == -45) return 7;
695 else if(i == -90) return 2;
696 else if(i == -135) return 5;
697 else if(i == -180) return 4;
698}
699
700static int get_new_angle( int orig_angle, int rotate_angle )
701{
702 // defined in exif.c
703 extern int ExifRotateFlipMapping[9][9];
704 static int angle_trans_back[] = {0, 0, -90, 180, -180, -135, 90, -45, 270};
705
706 orig_angle = trans_angle_to_id(orig_angle);
707 rotate_angle = trans_angle_to_id(rotate_angle);
708
709 return angle_trans_back[ ExifRotateFlipMapping[orig_angle][rotate_angle] ];
710}
711
cf15ca10 712void on_rotate_clockwise( GtkWidget* btn, MainWin* mw )
1d48a247 713{
cf15ca10 714 rotate_image( mw, GDK_PIXBUF_ROTATE_CLOCKWISE );
da046c6e 715 mw->rotation_angle = get_new_angle(mw->rotation_angle, 90);
bb0f721e
HJYP
716 if(pref.auto_save_rotated){
717 pref.ask_before_save = FALSE;
7c3b160c 718 on_save(btn,mw);
bb0f721e 719 pref.ask_before_save = TRUE;
7c3b160c 720 }
1d48a247
HJYP
721}
722
cf15ca10 723void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw )
1d48a247 724{
cf15ca10 725 rotate_image( mw, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE );
da046c6e 726 mw->rotation_angle = get_new_angle(mw->rotation_angle, 270);
727 if(pref.auto_save_rotated){
728 pref.ask_before_save = FALSE;
729 on_save(btn,mw);
730 pref.ask_before_save = TRUE;
731 }
732}
733
734void on_flip_vertical( GtkWidget* btn, MainWin* mw )
735{
736 rotate_image( mw, -180 );
737 mw->rotation_angle = get_new_angle(mw->rotation_angle, -180);
738 if(pref.auto_save_rotated){
739 pref.ask_before_save = FALSE;
740 on_save(btn,mw);
741 pref.ask_before_save = TRUE;
742 }
743}
744
745void on_flip_horizontal( GtkWidget* btn, MainWin* mw )
746{
747 rotate_image( mw, -90 );
748 mw->rotation_angle = get_new_angle(mw->rotation_angle, -90);
bb0f721e
HJYP
749 if(pref.auto_save_rotated){
750 pref.ask_before_save = FALSE;
7c3b160c 751 on_save(btn,mw);
bb0f721e 752 pref.ask_before_save = TRUE;
7c3b160c 753 }
1d48a247
HJYP
754}
755
da046c6e 756///////////////////// end of rotate & flip
757
1d48a247
HJYP
758static void on_update_preview( GtkFileChooser *chooser, GtkImage* img )
759{
760 char* file = gtk_file_chooser_get_preview_filename( chooser );
761 GdkPixbuf* pix = NULL;
762 if( file )
763 {
764 pix = gdk_pixbuf_new_from_file_at_scale( file, 128, 128, TRUE, NULL );
765 g_free( file );
766 }
1d48a247 767 if( pix )
9c52ae58
HJYP
768 {
769 gtk_image_set_from_pixbuf( img, pix );
770 g_object_unref( pix );
771 }
1d48a247
HJYP
772}
773
cf15ca10 774void on_save_as( GtkWidget* btn, MainWin* mw )
1d48a247 775{
cf15ca10 776 if( ! mw->pix )
1d48a247
HJYP
777 return;
778
cf15ca10 779 GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)mw,
1d48a247
HJYP
780 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
781 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL );
782
cf15ca10 783 gtk_file_chooser_set_current_folder( dlg, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
784
785 GtkWidget* img = gtk_image_new();
786 gtk_widget_set_size_request( img, 128, 128 );
787 gtk_file_chooser_set_preview_widget( dlg, img );
788 g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
789
790 GtkFileFilter *filter;
791
792 /*
793 /// TODO: determine file type from file name
794 filter = gtk_file_filter_new();
795 gtk_file_filter_set_name( filter, _("Determined by File Name") );
796 gtk_file_filter_add_pixbuf_formats( filter );
797 gtk_file_chooser_add_filter( dlg, filter );
798 */
799
800 GSList* modules = gdk_pixbuf_get_formats();
801 GSList* module;
802 for( module = modules; module; module = module->next )
803 {
804 GdkPixbufFormat* format = (GdkPixbufFormat*)module->data;
805 if( ! gdk_pixbuf_format_is_writable( format ) )
806 continue;
807
808 filter = gtk_file_filter_new();
809
810 char* desc = gdk_pixbuf_format_get_description( format );
811 char* name = gdk_pixbuf_format_get_name( format );
812 char* tmp = g_strjoin( ": ", name, desc, NULL );
813 g_free( desc );
814 g_free( name );
815 gtk_file_filter_set_name( filter, tmp );
816 g_free( tmp );
817
818 char** mimes = gdk_pixbuf_format_get_mime_types( format ), **mime;
819 for( mime = mimes; *mime ; ++mime )
820 gtk_file_filter_add_mime_type( filter, *mime );
821 g_strfreev( mimes );
822 gtk_file_chooser_add_filter( dlg, filter );
823 }
824
825 if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
826 {
827 filter = gtk_file_chooser_get_filter( dlg );
828 const char* filter_name = gtk_file_filter_get_name( filter );
829 char* p = strstr( filter_name, ": " );
830 char* type = NULL;
831 if( ! p ) // auto detection
832 {
833 /// TODO: auto file type
834 }
835 else
836 {
837 type = g_strndup( filter_name, (p - filter_name) );
838 }
839 char* file = gtk_file_chooser_get_filename( dlg );
840 // g_debug("type = %s", type);
cf15ca10 841 main_win_save( mw, file, type, TRUE );
1d48a247
HJYP
842 g_free( file );
843 g_free( type );
844 }
845 gtk_widget_destroy( (GtkWidget*)dlg );
846}
847
7c3b160c
HJYP
848#ifdef HAVE_LIBJPEG
849int rotate_and_save_jpeg_lossless(char * filename,int angle){
da046c6e 850
c9bcabc8
YCLP
851 char tmpfilename[] = {"/tmp/rot.jpg.XXXXXX"};
852 int tmpfilefd;
853 FILE *cp_in;
854 FILE *cp_out;
855 char cp_buf[512];
856
da046c6e 857 if(angle < 0)
858 return -1;
859
7c3b160c
HJYP
860 JXFORM_CODE code = JXFORM_NONE;
861
862 angle = angle % 360;
863
864 if(angle == 90)
865 code = JXFORM_ROT_90;
866 else if(angle == 180)
867 code = JXFORM_ROT_180;
868 else if(angle == 270)
869 code = JXFORM_ROT_270;
870
c9bcabc8
YCLP
871 /* create tmp file */
872 tmpfilefd = mkstemp(tmpfilename);
873 if (tmpfilefd == -1) {
874 return -1;
875 }
876 close(tmpfilefd);
877
7c3b160c 878 //rotate the image and save it to /tmp/rot.jpg
c9bcabc8 879 int error = jpegtran (filename, tmpfilename , code);
7c3b160c
HJYP
880 if(error)
881 return error;
882
883 //now copy /tmp/rot.jpg back to the original file
c9bcabc8
YCLP
884 cp_in = fopen(tmpfilename,"rb");
885 cp_out = fopen(filename,"wb");
886 while (!feof(cp_in)) {
887 int n;
888 n = fread(cp_buf,1,sizeof(cp_buf),cp_in);
889 if (n<=0) {
890 break;
891 }
892 fwrite (cp_buf,1,n,cp_out);
893 }
894 fclose(cp_in);
895 fclose(cp_out);
7c3b160c 896
c9bcabc8
YCLP
897 /* remove tmp file */
898 unlink(tmpfilename);
7c3b160c
HJYP
899 return 0;
900}
901#endif
902
cf15ca10 903void on_save( GtkWidget* btn, MainWin* mw )
1d48a247 904{
cf15ca10 905 if( ! mw->pix )
1d48a247
HJYP
906 return;
907
cf15ca10
HJYP
908 char* file_name = g_build_filename( image_list_get_dir( mw->img_list ),
909 image_list_get_current( mw->img_list ), NULL );
1d48a247
HJYP
910 GdkPixbufFormat* info;
911 info = gdk_pixbuf_get_file_info( file_name, NULL, NULL );
912 char* type = gdk_pixbuf_format_get_name( info );
da046c6e 913
914 if(strcmp(type,"jpeg")==0)
915 {
916 if(!pref.rotate_exif_only || ExifRotate(file_name, mw->rotation_angle) == FALSE)
917 {
918 // hialan notes:
919 // ExifRotate retrun FALSE when
920 // 1. Can not read file
921 // 2. Exif do not have TAG_ORIENTATION tag
922 // 3. Format unknown
923 // And then we apply rotate_and_save_jpeg_lossless() ,
924 // the result would not effected by EXIF Orientation...
7c3b160c 925#ifdef HAVE_LIBJPEG
da046c6e 926 if(rotate_and_save_jpeg_lossless(file_name,mw->rotation_angle)!=0)
927 {
928 main_win_show_error(mw, "Save failed! Check permissions.");
929 }
930#else
931 main_win_save( mw, file_name, type, pref.ask_before_save );
7c3b160c 932#endif
da046c6e 933 }
934 } else
bb0f721e 935 main_win_save( mw, file_name, type, pref.ask_before_save );
7c3b160c 936 mw->rotation_angle = 0;
1d48a247
HJYP
937 g_free( file_name );
938 g_free( type );
939}
940
cf15ca10 941void on_open( GtkWidget* btn, MainWin* mw )
1d48a247 942{
cf15ca10 943 GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)mw,
1d48a247
HJYP
944 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
945 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
946
cf15ca10
HJYP
947 if( image_list_get_dir( mw->img_list ) )
948 gtk_file_chooser_set_current_folder( dlg, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
949
950 GtkWidget* img = gtk_image_new();
951 gtk_widget_set_size_request( img, 128, 128 );
952 gtk_file_chooser_set_preview_widget( dlg, img );
953 g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
954
955 GtkFileFilter *filter = gtk_file_filter_new();
956 gtk_file_filter_set_name( filter, _("All Supported Images") );
957 gtk_file_filter_add_pixbuf_formats( filter );
958 gtk_file_chooser_add_filter( dlg, filter );
959
960 filter = gtk_file_filter_new();
961 gtk_file_filter_set_name( filter, _("All Files") );
962 gtk_file_filter_add_pattern( filter, "*" );
963 gtk_file_chooser_add_filter( dlg, filter );
964
965 char* file = NULL;
966 if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
967 file = gtk_file_chooser_get_filename( dlg );
968 gtk_widget_destroy( (GtkWidget*)dlg );
969
970 if( file )
971 {
cf15ca10 972 main_win_open( mw, file, ZOOM_NONE );
1d48a247
HJYP
973 g_free( file );
974 }
975}
976
cf15ca10 977void on_zoom_in( GtkWidget* btn, MainWin* mw )
1d48a247 978{
cf15ca10
HJYP
979 mw->zoom_mode = ZOOM_SCALE;
980 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
981 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
982// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 983// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 984
cf15ca10
HJYP
985 double scale = mw->scale;
986 if( mw->pix && scale < 20.0 )
1d48a247 987 {
cf15ca10 988// busy(TRUE);
9c52ae58
HJYP
989 scale *= 1.05;
990 if( scale > 20.0 )
991 scale = 20.0;
cf15ca10
HJYP
992 if( mw->scale != scale )
993 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 994// adjust_adjustment_on_zoom(oldscale);
cf15ca10 995// busy(FALSE);
1d48a247
HJYP
996 }
997}
998
cf15ca10 999void on_zoom_out( GtkWidget* btn, MainWin* mw )
1d48a247 1000{
cf15ca10
HJYP
1001 mw->zoom_mode = ZOOM_SCALE;
1002 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1003 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1004// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 1005// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 1006
cf15ca10
HJYP
1007 double scale = mw->scale;
1008 if( mw->pix && scale > 0.02 )
1d48a247 1009 {
cf15ca10 1010// busy(TRUE);
da8bd946 1011
9c52ae58
HJYP
1012 scale /= 1.05;
1013 if( scale < 0.02 )
1014 scale = 0.02;
cf15ca10
HJYP
1015 if( mw->scale != scale )
1016 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 1017// adjust_adjustment_on_zoom(oldscale);
cf15ca10 1018// busy(FALSE);
1d48a247
HJYP
1019 }
1020}
1021
cf15ca10 1022void on_preference( GtkWidget* btn, MainWin* mw )
1d48a247 1023{
bb0f721e 1024 edit_preferences( (GtkWindow*)mw );
1d48a247
HJYP
1025}
1026
cf15ca10 1027void on_quit( GtkWidget* btn, MainWin* mw )
1d48a247 1028{
cf15ca10 1029 gtk_widget_destroy( (GtkWidget*)mw );
1d48a247
HJYP
1030}
1031
cf15ca10 1032gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 1033{
9c52ae58
HJYP
1034 if( ! GTK_WIDGET_HAS_FOCUS( widget ) )
1035 gtk_widget_grab_focus( widget );
5bfec971 1036
1d48a247
HJYP
1037 if( evt->type == GDK_BUTTON_PRESS)
1038 {
1039 if( evt->button == 1 ) // left button
1040 {
cf15ca10 1041 if( ! mw->pix )
1d48a247 1042 return FALSE;
cf15ca10
HJYP
1043 mw->dragging = TRUE;
1044 gtk_widget_get_pointer( (GtkWidget*)mw, &mw->drag_old_x ,&mw->drag_old_y );
1045 gdk_window_set_cursor( widget->window, mw->hand_cursor );
1d48a247
HJYP
1046 }
1047 else if( evt->button == 3 ) // right button
1048 {
cf15ca10 1049 show_popup_menu( mw, evt );
1d48a247
HJYP
1050 }
1051 }
1052 else if( evt->type == GDK_2BUTTON_PRESS && evt->button == 1 ) // double clicked
1053 {
cf15ca10 1054 on_full_screen( NULL, mw );
1d48a247 1055 }
1d48a247
HJYP
1056 return FALSE;
1057}
1058
cf15ca10 1059gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw )
1d48a247 1060{
cf15ca10 1061 if( ! mw->dragging )
1d48a247 1062 return FALSE;
9c52ae58 1063
1d48a247 1064 int cur_x, cur_y;
cf15ca10 1065 gtk_widget_get_pointer( (GtkWidget*)mw, &cur_x ,&cur_y );
1d48a247 1066
cf15ca10
HJYP
1067 int dx = (mw->drag_old_x - cur_x);
1068 int dy = (mw->drag_old_y - cur_y);
1d48a247
HJYP
1069
1070 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
1071 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1072 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 1073
9c52ae58 1074 GtkRequisition req;
cf15ca10 1075 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247
HJYP
1076
1077 if( ABS(dx) > 4 )
1078 {
cf15ca10 1079 mw->drag_old_x = cur_x;
9c52ae58 1080 if( req.width > hadj->page_size )
1d48a247
HJYP
1081 {
1082 gdouble x = gtk_adjustment_get_value (hadj) + dx;
1083 if( x < hadj->lower )
1084 x = hadj->lower;
1085 else if( (x + hadj->page_size) > hadj->upper )
1086 x = hadj->upper - hadj->page_size;
1087
1088 if( x != hadj->value )
1089 gtk_adjustment_set_value (hadj, x );
1090 }
1091 }
1092
1093 if( ABS(dy) > 4 )
1094 {
9c52ae58 1095 if( req.height > vadj->page_size )
1d48a247 1096 {
cf15ca10 1097 mw->drag_old_y = cur_y;
1d48a247
HJYP
1098 gdouble y = gtk_adjustment_get_value (vadj) + dy;
1099 if( y < vadj->lower )
1100 y = vadj->lower;
1101 else if( (y + vadj->page_size) > vadj->upper )
1102 y = vadj->upper - vadj->page_size;
1103
1104 if( y != vadj->value )
1105 gtk_adjustment_set_value (vadj, y );
1106 }
1107 }
1108 return FALSE;
1109}
1110
cf15ca10 1111gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 1112{
cf15ca10 1113 mw->dragging = FALSE;
1d48a247
HJYP
1114 gdk_window_set_cursor( widget->window, NULL );
1115 return FALSE;
1116}
1117
cf15ca10 1118gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw )
5bfec971 1119{
58e1b106 1120 guint modifiers = gtk_accelerator_get_default_mod_mask();
5bfec971
HJYP
1121 switch( evt->direction )
1122 {
1123 case GDK_SCROLL_UP:
58e1b106
JH
1124 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1125 on_zoom_in( NULL, mw );
1126 else
1127 on_prev( NULL, mw );
5bfec971
HJYP
1128 break;
1129 case GDK_SCROLL_DOWN:
58e1b106
JH
1130 if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1131 on_zoom_out( NULL, mw );
1132 else
1133 on_next( NULL, mw );
5bfec971
HJYP
1134 break;
1135 case GDK_SCROLL_LEFT:
cf15ca10 1136 on_prev( NULL, mw );
5bfec971
HJYP
1137 break;
1138 case GDK_SCROLL_RIGHT:
cf15ca10 1139 on_next( NULL, mw );
5bfec971
HJYP
1140 break;
1141 }
da8bd946 1142 return TRUE;
5bfec971
HJYP
1143}
1144
cf15ca10 1145gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key)
1d48a247 1146{
cf15ca10 1147 MainWin* mw = (MainWin*)widget;
1d48a247
HJYP
1148 switch( key->keyval )
1149 {
7c3b160c
HJYP
1150 case GDK_Right:
1151 case GDK_KP_Right:
1152 case GDK_rightarrow:
1d48a247
HJYP
1153 case GDK_Return:
1154 case GDK_space:
1155 case GDK_Next:
5bfec971
HJYP
1156 case GDK_KP_Down:
1157 case GDK_Down:
1158 case GDK_downarrow:
cf15ca10 1159 on_next( NULL, mw );
1d48a247 1160 break;
7c3b160c
HJYP
1161 case GDK_Left:
1162 case GDK_KP_Left:
1163 case GDK_leftarrow:
1d48a247
HJYP
1164 case GDK_Prior:
1165 case GDK_BackSpace:
5bfec971
HJYP
1166 case GDK_KP_Up:
1167 case GDK_Up:
1168 case GDK_uparrow:
cf15ca10 1169 on_prev( NULL, mw );
1d48a247
HJYP
1170 break;
1171 case GDK_KP_Add:
1172 case GDK_plus:
641adad3 1173 case GDK_equal:
cf15ca10 1174 on_zoom_in( NULL, mw );
1d48a247
HJYP
1175 break;
1176 case GDK_KP_Subtract:
1177 case GDK_minus:
cf15ca10 1178 on_zoom_out( NULL, mw );
1d48a247 1179 break;
1d48a247 1180 case GDK_s:
641adad3 1181// case GDK_S:
cf15ca10 1182 on_save( NULL, mw );
1d48a247
HJYP
1183 break;
1184 case GDK_l:
5bfec971 1185// case GDK_L:
cf15ca10 1186 on_rotate_counterclockwise( NULL, mw );
1d48a247
HJYP
1187 break;
1188 case GDK_r:
5bfec971 1189// case GDK_R:
cf15ca10 1190 on_rotate_clockwise( NULL, mw );
1d48a247
HJYP
1191 break;
1192 case GDK_f:
5bfec971 1193// case GDK_F:
cf15ca10
HJYP
1194 if( mw->zoom_mode != ZOOM_FIT )
1195 gtk_button_clicked((GtkButton*)mw->btn_fit );
1d48a247
HJYP
1196 break;
1197 case GDK_g:
5bfec971 1198// case GDK_G:
cf15ca10
HJYP
1199 if( mw->zoom_mode != ZOOM_ORIG )
1200 gtk_button_clicked((GtkButton*)mw->btn_orig );
1d48a247
HJYP
1201 break;
1202 case GDK_o:
5bfec971 1203// case GDK_O:
cf15ca10 1204 on_open( NULL, mw );
5bfec971
HJYP
1205 break;
1206 case GDK_Delete:
1207 case GDK_d:
1208// case GDK_D:
cf15ca10 1209 on_delete( NULL, mw );
1d48a247
HJYP
1210 break;
1211 case GDK_Escape:
cf15ca10
HJYP
1212 if( mw->full_screen )
1213 on_full_screen( NULL, mw );
1d48a247 1214 else
cf15ca10 1215 on_quit( NULL, mw );
1d48a247
HJYP
1216 break;
1217 case GDK_F11:
cf15ca10 1218 on_full_screen( NULL, mw );
1d48a247
HJYP
1219 break;
1220
1221 default:
cf15ca10 1222 GTK_WIDGET_CLASS(main_win_parent_class)->key_press_event( widget, key );
1d48a247
HJYP
1223 }
1224 return FALSE;
1225}
1226
cf15ca10 1227void main_win_center_image( MainWin* mw )
1d48a247
HJYP
1228{
1229 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
1230 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1231 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 1232
9c52ae58 1233 GtkRequisition req;
cf15ca10 1234 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247 1235
9c52ae58
HJYP
1236 if( req.width > hadj->page_size )
1237 gtk_adjustment_set_value(hadj, ( hadj->upper - hadj->page_size ) / 2 );
1d48a247 1238
9c52ae58
HJYP
1239 if( req.height > vadj->page_size )
1240 gtk_adjustment_set_value(vadj, ( vadj->upper - vadj->page_size ) / 2 );
1d48a247
HJYP
1241}
1242
da046c6e 1243void rotate_image( MainWin* mw, int angle )
1d48a247 1244{
da046c6e 1245 GdkPixbuf* rpix = NULL;
1246
cf15ca10 1247 if( ! mw->pix )
1d48a247
HJYP
1248 return;
1249
da046c6e 1250 if(angle > 0)
1251 {
1252 rpix = gdk_pixbuf_rotate_simple( mw->pix, angle );
1253 }
1254 else
1255 {
1256 if(angle == -90)
1257 rpix = gdk_pixbuf_flip( mw->pix, TRUE );
1258 else if(angle == -180)
1259 rpix = gdk_pixbuf_flip( mw->pix, FALSE );
1260 }
cf15ca10 1261 g_object_unref( mw->pix );
da046c6e 1262
cf15ca10 1263 mw->pix = rpix;
0c0c8179 1264 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
1d48a247 1265
cf15ca10 1266 if( mw->zoom_mode == ZOOM_FIT )
c325c989 1267 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
1268}
1269
cf15ca10 1270gboolean main_win_scale_image( MainWin* mw, double new_scale, GdkInterpType type )
1d48a247
HJYP
1271{
1272 if( G_UNLIKELY( new_scale == 1.0 ) )
1273 {
cf15ca10 1274 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
da046c6e 1275 mw->scale = 1.0;
cf15ca10 1276 return TRUE;
1d48a247 1277 }
cf15ca10 1278 mw->scale = new_scale;
0c0c8179 1279 image_view_set_scale( (ImageView*)mw->img_view, new_scale, type );
da046c6e 1280
1281 updateTitle( NULL, mw );
1282
cf15ca10 1283 return TRUE;
1d48a247
HJYP
1284}
1285
cf15ca10 1286gboolean main_win_save( MainWin* mw, const char* file_path, const char* type, gboolean confirm )
1d48a247 1287{
cf15ca10
HJYP
1288 if( ! mw->pix )
1289 return FALSE;
1d48a247
HJYP
1290
1291 if( confirm ) // check existing file
1292 {
1293 if( g_file_test( file_path, G_FILE_TEST_EXISTS ) )
1294 {
cf15ca10 1295 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
1296 GTK_DIALOG_MODAL,
1297 GTK_MESSAGE_QUESTION,
1298 GTK_BUTTONS_YES_NO,
1299 _("The file name you selected already exist.\nDo you want to overwrite existing file?\n(Warning: The quality of original image might be lost)") );
1300 if( gtk_dialog_run( (GtkDialog*)dlg ) != GTK_RESPONSE_YES )
1301 {
1302 gtk_widget_destroy( dlg );
cf15ca10 1303 return FALSE;
1d48a247
HJYP
1304 }
1305 gtk_widget_destroy( dlg );
1306 }
1307 }
1308
1309 GError* err = NULL;
cf15ca10 1310 if( ! gdk_pixbuf_save( mw->pix, file_path, type, &err, NULL ) )
1d48a247 1311 {
cf15ca10
HJYP
1312 main_win_show_error( mw, err->message );
1313 return FALSE;
1d48a247
HJYP
1314 }
1315
cf15ca10 1316 return TRUE;
1d48a247
HJYP
1317}
1318
cf15ca10 1319void on_delete( GtkWidget* btn, MainWin* mw )
1d48a247 1320{
cf15ca10 1321 char* file_path = image_list_get_current_file_path( mw->img_list );
1d48a247
HJYP
1322 if( file_path )
1323 {
cf15ca10 1324 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
1325 GTK_DIALOG_MODAL,
1326 GTK_MESSAGE_QUESTION,
1327 GTK_BUTTONS_YES_NO,
1328 _("Are you sure you want to delete current file?\n\nWarning: Once deleted, the file cannot be recovered.") );
1329 int resp = gtk_dialog_run( (GtkDialog*)dlg );
1330 gtk_widget_destroy( dlg );
1331
1332 if( resp == GTK_RESPONSE_YES )
1333 {
1334 g_unlink( file_path );
1335 if( errno )
cf15ca10 1336 main_win_show_error( mw, g_strerror(errno) );
1d48a247
HJYP
1337 g_free( file_path );
1338 }
1339 }
1340}
1341
cf15ca10 1342void show_popup_menu( MainWin* mw, GdkEventButton* evt )
1d48a247 1343{
5bfec971
HJYP
1344 static PtkMenuItemEntry menu_def[] =
1345 {
1346 PTK_IMG_MENU_ITEM( N_( "Previous" ), GTK_STOCK_GO_BACK, on_prev, GDK_leftarrow, 0 ),
1347 PTK_IMG_MENU_ITEM( N_( "Next" ), GTK_STOCK_GO_FORWARD, on_next, GDK_rightarrow, 0 ),
1348 PTK_SEPARATOR_MENU_ITEM,
1349 PTK_IMG_MENU_ITEM( N_( "Zoom Out" ), GTK_STOCK_ZOOM_OUT, on_zoom_out, GDK_minus, 0 ),
1350 PTK_IMG_MENU_ITEM( N_( "Zoom In" ), GTK_STOCK_ZOOM_IN, on_zoom_in, GDK_plus, 0 ),
1351 PTK_IMG_MENU_ITEM( N_( "Fit Image To Window Size" ), GTK_STOCK_ZOOM_FIT, on_zoom_fit_menu, GDK_F, 0 ),
1352 PTK_IMG_MENU_ITEM( N_( "Original Size" ), GTK_STOCK_ZOOM_100, on_orig_size_menu, GDK_G, 0 ),
1353 PTK_SEPARATOR_MENU_ITEM,
0c8ac162 1354 PTK_IMG_MENU_ITEM( N_( "Full Screen" ), GTK_STOCK_FULLSCREEN, on_full_screen, GDK_F11, 0 ),
5bfec971
HJYP
1355 PTK_SEPARATOR_MENU_ITEM,
1356 PTK_IMG_MENU_ITEM( N_( "Rotate Counterclockwise" ), "gtk-counterclockwise", on_rotate_counterclockwise, GDK_L, 0 ),
1357 PTK_IMG_MENU_ITEM( N_( "Rotate Clockwise" ), "gtk-clockwise", on_rotate_clockwise, GDK_R, 0 ),
da046c6e 1358// PTK_IMG_MENU_ITEM( N_( "Flip Vertical" ), "gtk-clockwise", on_flip_vertical, GDK_V, 0 ),
1359// PTK_IMG_MENU_ITEM( N_( "Flip Horizontal" ), "gtk-clockwise", on_flip_horizontal, GDK_H, 0 ),
5bfec971
HJYP
1360 PTK_SEPARATOR_MENU_ITEM,
1361 PTK_IMG_MENU_ITEM( N_("Open File"), GTK_STOCK_OPEN, G_CALLBACK(on_open), GDK_O, 0 ),
1362 PTK_IMG_MENU_ITEM( N_("Save File"), GTK_STOCK_SAVE, G_CALLBACK(on_save), GDK_S, 0 ),
1363 PTK_IMG_MENU_ITEM( N_("Save As"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
da046c6e 1364// PTK_IMG_MENU_ITEM( N_("Save As Other Size"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
5bfec971
HJYP
1365 PTK_IMG_MENU_ITEM( N_("Delete File"), GTK_STOCK_DELETE, G_CALLBACK(on_delete), GDK_Delete, 0 ),
1366 PTK_SEPARATOR_MENU_ITEM,
1367 PTK_STOCK_MENU_ITEM( GTK_STOCK_ABOUT, on_about ),
1368 PTK_MENU_END
1369 };
1370
cf15ca10 1371 // mw accel group is useless. It's only used to display accels in popup menu
5bfec971 1372 GtkAccelGroup* accel_group = gtk_accel_group_new();
cf15ca10 1373 GtkMenuShell* popup = (GtkMenuShell*)ptk_menu_new_from_data( menu_def, mw, accel_group );
1d48a247
HJYP
1374
1375 gtk_widget_show_all( (GtkWidget*)popup );
1376 g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1377 gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
1378}
1379
7c3b160c
HJYP
1380/* callback used to open default browser when URLs got clicked */
1381static void open_url( GtkAboutDialog *dlg, const gchar *url, gpointer data)
1d48a247 1382{
7c3b160c
HJYP
1383 /* FIXME: is there any better way to do this? */
1384 char* programs[] = { "xdg-open", "gnome-open" /* Sorry, KDE users. :-P */, "exo-open" };
1385 int i;
1386 for( i = 0; i < G_N_ELEMENTS(programs); ++i)
1d48a247 1387 {
7c3b160c
HJYP
1388 char* open_cmd = NULL;
1389 if( (open_cmd = g_find_program_in_path( programs[i] )) )
1d48a247 1390 {
ff032ffe
JH
1391 gchar* argv [3];
1392 argv [0] = programs[i];
1393 argv [1] = url;
1394 argv [2] = NULL;
1395 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
7c3b160c
HJYP
1396 g_free( open_cmd );
1397 break;
1d48a247 1398 }
1d48a247 1399 }
1d48a247
HJYP
1400}
1401
cf15ca10 1402void on_about( GtkWidget* menu, MainWin* mw )
1d48a247
HJYP
1403{
1404 GtkWidget * about_dlg;
1405 const gchar *authors[] =
1406 {
1407 "洪任諭 Hong Jen Yee <pcman.tw@gmail.com>",
7c3b160c 1408 "Martin Siggel <martinsiggel@googlemail.com>",
da046c6e 1409 "Hialan Liu <hialan.liu@gmail.com>",
7c3b160c 1410 _(" * Refer to source code of EOG image viewer and GThumb"),
1d48a247
HJYP
1411 NULL
1412 };
cf15ca10 1413 /* TRANSLATORS: Replace mw string with your names, one name per line. */
1d48a247
HJYP
1414 gchar *translators = _( "translator-credits" );
1415
7c3b160c
HJYP
1416 gtk_about_dialog_set_url_hook( open_url, mw, NULL);
1417
1d48a247 1418 about_dlg = gtk_about_dialog_new ();
7c3b160c
HJYP
1419
1420 gtk_container_set_border_width ( ( GtkContainer*)about_dlg , 2 );
1421 gtk_about_dialog_set_version ( (GtkAboutDialog*)about_dlg, VERSION );
1422 gtk_about_dialog_set_name ( (GtkAboutDialog*)about_dlg, _( "GPicView" ) );
1423 gtk_about_dialog_set_logo( (GtkAboutDialog*)about_dlg, gdk_pixbuf_new_from_file( PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL ) );
1424 gtk_about_dialog_set_copyright ( (GtkAboutDialog*)about_dlg, _( "Copyright (C) 2007" ) );
1425 gtk_about_dialog_set_comments ( (GtkAboutDialog*)about_dlg, _( "Lightweight image viewer from LXDE project" ) );
1426 gtk_about_dialog_set_license ( (GtkAboutDialog*)about_dlg, "GPicView\n\nCopyright (C) 2007 Hong Jen Yee (PCMan)\n\nmw program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nmw program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with mw program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." );
c5a539c7 1427 gtk_about_dialog_set_website ( (GtkAboutDialog*)about_dlg, "http://lxde.org/gpicview/" );
7c3b160c
HJYP
1428 gtk_about_dialog_set_authors ( (GtkAboutDialog*)about_dlg, authors );
1429 gtk_about_dialog_set_translator_credits ( (GtkAboutDialog*)about_dlg, translators );
1430 gtk_window_set_transient_for( (GtkWindow*) about_dlg, GTK_WINDOW( mw ) );
1431
1432 gtk_dialog_run( ( GtkDialog*)about_dlg );
1d48a247
HJYP
1433 gtk_widget_destroy( about_dlg );
1434}
1435
cf15ca10
HJYP
1436void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
1437 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw )
1d48a247
HJYP
1438{
1439 if( ! data || data->length <= 0)
1440 return;
1441
1d48a247
HJYP
1442 char* file = NULL;
1443 if( info == 0 ) // text/uri-list
1444 {
1445 char** uris = gtk_selection_data_get_uris( data );
1446 if( uris )
1447 {
1448 file = g_filename_from_uri(*uris, NULL, NULL);
1449 g_strfreev( uris );
1450 }
1451 }
1452 else if( info == 1 ) // text/plain
1453 {
1454 file = (char*)gtk_selection_data_get_text( data );
1455 }
1456 if( file )
1457 {
cf15ca10 1458 main_win_open( mw, file, ZOOM_FIT );
1d48a247
HJYP
1459 g_free( file );
1460 }
1461}