Add new locale for Dutch.
[lxde/gpicview.git] / src / main-win.c
CommitLineData
1d48a247
HJYP
1/***************************************************************************
2 * Copyright (C) 2007 by PCMan (Hong Jen Yee) *
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>
1d48a247 35
bb0f721e
HJYP
36#include "pref.h"
37
9c52ae58 38#include "image-view.h"
1d48a247 39#include "working-area.h"
cf15ca10 40#include "ptk-menu.h"
7c3b160c 41#include "jpeg-tran.h"
1d48a247
HJYP
42
43// For drag & drop
cf15ca10 44static GtkTargetEntry drop_targets[] =
1d48a247
HJYP
45{
46 {"text/uri-list", 0, 0},
cf15ca10 47 {"text/plain", 0, 1}
1d48a247
HJYP
48};
49
cf15ca10
HJYP
50static void main_win_init( MainWin*mw );
51static void main_win_finalize( GObject* obj );
52
53static void create_nav_bar( MainWin* mw, GtkWidget* box);
54GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle);
55// GtkWidget* add_menu_item( GtkMenuShell* menu, const char* label, const char* icon, GCallback cb, gboolean toggle=FALSE );
56static void rotate_image( MainWin* mw, GdkPixbufRotation angle );
57static void show_popup_menu( MainWin* mw, GdkEventButton* evt );
58
59/* signal handlers */
60static gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt );
61static void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation );
62static void on_zoom_fit( GtkToggleButton* btn, MainWin* mw );
63static void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw );
64static void on_full_screen( GtkWidget* btn, MainWin* mw );
65static void on_next( GtkWidget* btn, MainWin* mw );
66static void on_orig_size( GtkToggleButton* btn, MainWin* mw );
67static void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw );
68static void on_prev( GtkWidget* btn, MainWin* mw );
69static void on_rotate_clockwise( GtkWidget* btn, MainWin* mw );
70static void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw );
71static void on_save_as( GtkWidget* btn, MainWin* mw );
72static void on_save( GtkWidget* btn, MainWin* mw );
73static void on_open( GtkWidget* btn, MainWin* mw );
74static void on_zoom_in( GtkWidget* btn, MainWin* mw );
75static void on_zoom_out( GtkWidget* btn, MainWin* mw );
76static void on_preference( GtkWidget* btn, MainWin* mw );
77static void on_quit( GtkWidget* btn, MainWin* mw );
78static gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
79static gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
80static gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw );
81static gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw );
82static gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key);
83static void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
84 int x, int y, GtkSelectionData* data, guint info,
85 guint time, MainWin* mw );
86static void on_delete( GtkWidget* btn, MainWin* mw );
87static void on_about( GtkWidget* menu, MainWin* mw );
1d48a247 88
1d48a247 89
cf15ca10 90// Begin of GObject-related stuff
1d48a247 91
cf15ca10 92G_DEFINE_TYPE( MainWin, main_win, GTK_TYPE_WINDOW )
1d48a247 93
cf15ca10
HJYP
94void main_win_class_init( MainWinClass* klass )
95{
1d48a247
HJYP
96 GObjectClass * obj_class;
97 GtkWidgetClass *widget_class;
98
99 obj_class = ( GObjectClass * ) klass;
100// obj_class->set_property = _set_property;
101// obj_class->get_property = _get_property;
cf15ca10 102 obj_class->finalize = main_win_finalize;
1d48a247
HJYP
103
104 widget_class = GTK_WIDGET_CLASS ( klass );
105 widget_class->delete_event = on_delete_event;
106 widget_class->size_allocate = on_size_allocate;
107 widget_class->key_press_event = on_key_press_event;
108}
109
cf15ca10
HJYP
110void main_win_finalize( GObject* obj )
111{
112 MainWin *mw = (MainWin*)obj;
113
114 main_win_close(mw);
115
116 if( G_LIKELY(mw->img_list) )
117 image_list_free( mw->img_list );
118 gdk_cursor_unref( mw->hand_cursor );
119
120 g_object_unref( mw->tooltips );
121
122 // FIXME: Put this here is weird
123 gtk_main_quit();
124}
125
126GtkWidget* main_win_new()
1d48a247 127{
cf15ca10 128 return (GtkWidget*)g_object_new ( MAIN_WIN_TYPE, NULL );
1d48a247
HJYP
129}
130
131// End of GObject-related stuff
132
cf15ca10 133void main_win_init( MainWin*mw )
1d48a247 134{
cf15ca10
HJYP
135 gtk_window_set_title( (GtkWindow*)mw, _("Image Viewer"));
136 gtk_window_set_icon_from_file( (GtkWindow*)mw, PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL );
137 gtk_window_set_default_size( (GtkWindow*)mw, 640, 480 );
1d48a247
HJYP
138
139 GtkWidget* box = gtk_vbox_new( FALSE, 0 );
cf15ca10 140 gtk_container_add( (GtkContainer*)mw, box);
1d48a247
HJYP
141
142 // image area
cf15ca10
HJYP
143 mw->evt_box = gtk_event_box_new();
144 GTK_WIDGET_SET_FLAGS( mw->evt_box, GTK_CAN_FOCUS );
145 gtk_widget_add_events( mw->evt_box,
1d48a247
HJYP
146 GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
147 GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK );
cf15ca10
HJYP
148 g_signal_connect( mw->evt_box, "button-press-event", G_CALLBACK(on_button_press), mw );
149 g_signal_connect( mw->evt_box, "button-release-event", G_CALLBACK(on_button_release), mw );
150 g_signal_connect( mw->evt_box, "motion-notify-event", G_CALLBACK(on_mouse_move), mw );
151 g_signal_connect( mw->evt_box, "scroll-event", G_CALLBACK(on_scroll_event), mw );
5bfec971
HJYP
152 // Set bg color to white
153 GdkColor white = {0, 65535, 65535, 65535};
cf15ca10 154 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &white );
1d48a247 155
cf15ca10
HJYP
156 mw->img_view = image_view_new();
157 gtk_container_add( (GtkContainer*)mw->evt_box, (GtkWidget*)mw->img_view);
1d48a247 158
2c34ffe0
HJYP
159 const char scroll_style[]=
160 "style \"gpicview-scroll\" {"
161 "GtkScrolledWindow::scrollbar-spacing=0"
162 "}"
163 "class \"GtkScrolledWindow\" style \"gpicview-scroll\"";
164 gtk_rc_parse_string( scroll_style );
cf15ca10
HJYP
165 mw->scroll = gtk_scrolled_window_new( NULL, NULL );
166 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)mw->scroll, GTK_SHADOW_NONE );
167 gtk_scrolled_window_set_policy((GtkScrolledWindow*)mw->scroll,
1d48a247
HJYP
168 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
169 GtkAdjustment *hadj, *vadj;
cf15ca10 170 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
171 hadj->page_increment = 10;
172 gtk_adjustment_changed(hadj);
cf15ca10 173 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247
HJYP
174 vadj->page_increment = 10;
175 gtk_adjustment_changed(vadj);
9c52ae58 176
cf15ca10
HJYP
177 image_view_set_adjustments( mw->img_view, hadj, vadj ); // dirty hack :-(
178 gtk_scrolled_window_add_with_viewport( (GtkScrolledWindow*)mw->scroll, mw->evt_box );
179 GtkWidget* viewport = gtk_bin_get_child( (GtkBin*)mw->scroll );
2c34ffe0
HJYP
180 gtk_viewport_set_shadow_type( (GtkViewport*)viewport, GTK_SHADOW_NONE );
181 gtk_container_set_border_width( (GtkContainer*)viewport, 0 );
1d48a247 182
cf15ca10 183 gtk_box_pack_start( (GtkBox*)box, mw->scroll, TRUE, TRUE, 0 );
1d48a247
HJYP
184
185 // build toolbar
cf15ca10 186 mw->tooltips = gtk_tooltips_new();
9c52ae58 187#if GTK_CHECK_VERSION(2, 10, 0)
cf15ca10 188 g_object_ref_sink(mw->tooltips);
1d48a247 189#else
cf15ca10 190 gtk_object_sink((GtkObject*)mw->tooltips);
1d48a247 191#endif
1d48a247 192
cf15ca10 193 create_nav_bar( mw, box );
1d48a247
HJYP
194 gtk_widget_show_all( box );
195
cf15ca10 196 mw->hand_cursor = gdk_cursor_new_for_display( gtk_widget_get_display((GtkWidget*)mw), GDK_FLEUR );
1d48a247 197
5bfec971 198// zoom_mode = ZOOM_NONE;
cf15ca10 199 mw->zoom_mode = ZOOM_FIT;
1d48a247
HJYP
200
201 // Set up drag & drop
cf15ca10
HJYP
202 gtk_drag_dest_set( (GtkWidget*)mw, GTK_DEST_DEFAULT_ALL,
203 drop_targets,
204 G_N_ELEMENTS(drop_targets),
205 GDK_ACTION_COPY | GDK_ACTION_ASK );
206 g_signal_connect( mw, "drag-data-received", G_CALLBACK(on_drag_data_received), mw );
9c52ae58 207
cf15ca10 208 mw->img_list = image_list_new();
7c3b160c 209
7c3b160c
HJYP
210 // rotation angle is zero on startup
211 mw->rotation_angle = 0;
1d48a247
HJYP
212}
213
cf15ca10 214void create_nav_bar( MainWin* mw, GtkWidget* box )
1d48a247 215{
cf15ca10 216 mw->nav_bar = gtk_hbox_new( FALSE, 0 );
1d48a247 217
cf15ca10
HJYP
218 add_nav_btn( mw, GTK_STOCK_GO_BACK, _("Previous"), G_CALLBACK(on_prev), FALSE );
219 add_nav_btn( mw, GTK_STOCK_GO_FORWARD, _("Next"), G_CALLBACK(on_next), FALSE );
1d48a247 220
cf15ca10 221 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 222
cf15ca10
HJYP
223 add_nav_btn( mw, GTK_STOCK_ZOOM_OUT, _("Zoom Out"), G_CALLBACK(on_zoom_out), FALSE );
224 add_nav_btn( mw, GTK_STOCK_ZOOM_IN, _("Zoom In"), G_CALLBACK(on_zoom_in), FALSE );
5bfec971
HJYP
225
226// percent = gtk_entry_new(); // show scale (in percentage)
cf15ca10 227// g_signal_connect( percent, "activate", G_CALLBACK(on_percentage), mw );
5bfec971
HJYP
228// gtk_widget_set_size_request( percent, 45, -1 );
229// gtk_box_pack_start( (GtkBox*)nav_bar, percent, FALSE, FALSE, 2 );
230
cf15ca10
HJYP
231 mw->btn_fit = add_nav_btn( mw, GTK_STOCK_ZOOM_FIT, _("Fit Image To Window Size"),
232 G_CALLBACK(on_zoom_fit), TRUE );
233 mw->btn_orig = add_nav_btn( mw, GTK_STOCK_ZOOM_100, _("Original Size"),
234 G_CALLBACK(on_orig_size), TRUE );
235 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
5bfec971 236
1d48a247
HJYP
237#ifndef GTK_STOCK_FULLSCREEN
238#define GTK_STOCK_FULLSCREEN "gtk-fullscreen"
239#endif
cf15ca10 240 add_nav_btn( mw, GTK_STOCK_FULLSCREEN, _(" Full Screen"), G_CALLBACK(on_full_screen), FALSE ); // gtk+ 2.8+
1d48a247 241
cf15ca10 242 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 243
cf15ca10
HJYP
244 add_nav_btn( mw, "gtk-counterclockwise", _("Rotate Counterclockwise"),
245 G_CALLBACK(on_rotate_counterclockwise), FALSE );
246 add_nav_btn( mw, "gtk-clockwise", _("Rotate Clockwise"), G_CALLBACK(on_rotate_clockwise), FALSE );
1d48a247 247
cf15ca10 248 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
1d48a247 249
cf15ca10
HJYP
250 add_nav_btn( mw, GTK_STOCK_OPEN, _("Open File"), G_CALLBACK(on_open), FALSE );
251 add_nav_btn( mw, GTK_STOCK_SAVE, _("Save File"), G_CALLBACK(on_save), FALSE );
252 add_nav_btn( mw, GTK_STOCK_SAVE_AS, _("Save File As"), G_CALLBACK(on_save_as), FALSE );
253 add_nav_btn( mw, GTK_STOCK_DELETE, _("Delete File"), G_CALLBACK(on_delete), FALSE );
1d48a247 254
cf15ca10 255 gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
bb0f721e 256 add_nav_btn( mw, GTK_STOCK_PREFERENCES, _("Preferences"), G_CALLBACK(on_preference), FALSE );
1d48a247
HJYP
257
258 GtkWidget* align = gtk_alignment_new( 0.5, 0, 0, 0 );
cf15ca10 259 gtk_container_add( (GtkContainer*)align, mw->nav_bar );
1d48a247
HJYP
260 gtk_box_pack_start( (GtkBox*)box, align, FALSE, TRUE, 2 );
261}
262
cf15ca10 263gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt )
1d48a247
HJYP
264{
265 gtk_widget_destroy( widget );
266 return TRUE;
267}
268
cf15ca10 269gboolean main_win_open( MainWin* mw, const char* file_path, ZoomMode zoom )
1d48a247 270{
1d48a247 271 GError* err = NULL;
cf15ca10
HJYP
272
273 main_win_close( mw );
274 mw->pix = gdk_pixbuf_new_from_file( file_path, &err );
275 if( ! mw->pix )
1d48a247 276 {
cf15ca10
HJYP
277 main_win_show_error( mw, err->message );
278 return FALSE;
1d48a247 279 }
d8ce3af1 280
cf15ca10 281 mw->zoom_mode = zoom;
da8bd946 282
5bfec971 283 // select most suitable viewing mode
d8ce3af1 284 if( zoom == ZOOM_NONE )
1d48a247 285 {
cf15ca10
HJYP
286 int w = gdk_pixbuf_get_width( mw->pix );
287 int h = gdk_pixbuf_get_height( mw->pix );
1d48a247 288
1d48a247 289 GdkRectangle area;
cf15ca10 290 get_working_area( gtk_widget_get_screen((GtkWidget*)mw), &area );
c325c989
HJYP
291 // g_debug("determine best zoom mode: orig size: w=%d, h=%d", w, h);
292 // FIXME: actually this is a little buggy :-(
da8bd946 293 if( w < area.width && h < area.height && (w >= 640 || h >= 480) )
1d48a247 294 {
cf15ca10
HJYP
295 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER );
296 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, w, h );
5bfec971 297 GtkRequisition req;
cf15ca10 298 gtk_widget_size_request ( (GtkWidget*)mw, &req );
9c52ae58
HJYP
299 if( req.width < 640 ) req.width = 640;
300 if( req.height < 480 ) req.height = 480;
cf15ca10
HJYP
301 gtk_window_resize( (GtkWindow*)mw, req.width, req.height );
302 gtk_widget_set_size_request( (GtkWidget*)mw->img_view, -1, -1 );
303 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
304 mw->zoom_mode = ZOOM_ORIG;
1d48a247
HJYP
305 }
306 else
cf15ca10 307 mw->zoom_mode = ZOOM_FIT;
5bfec971 308 }
5bfec971 309
cf15ca10 310 if( mw->zoom_mode == ZOOM_FIT )
d8ce3af1 311 {
cf15ca10 312 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
c325c989 313 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
d8ce3af1 314 }
cf15ca10 315 else if( mw->zoom_mode == ZOOM_SCALE ) // scale
d8ce3af1 316 {
cf15ca10
HJYP
317 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
318 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
319 main_win_scale_image( mw, mw->scale, GDK_INTERP_BILINEAR );
d8ce3af1 320 }
cf15ca10 321 else if( mw->zoom_mode == ZOOM_ORIG ) // original size
5bfec971 322 {
cf15ca10
HJYP
323 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
324 image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
325 main_win_center_image( mw );
1d48a247
HJYP
326 }
327
cf15ca10 328 image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
9c52ae58 329
1d48a247
HJYP
330// while (gtk_events_pending ())
331// gtk_main_iteration ();
332
333 // build file list
334 char* dir_path = g_path_get_dirname( file_path );
cf15ca10
HJYP
335 image_list_open_dir( mw->img_list, dir_path, NULL );
336 image_list_sort_by_name( mw->img_list, GTK_SORT_ASCENDING );
1d48a247 337 g_free( dir_path );
76e88fcf 338
1d48a247 339 char* base_name = g_path_get_basename( file_path );
cf15ca10 340 image_list_set_current( mw->img_list, base_name );
76e88fcf
HJYP
341
342 char* disp_name = g_filename_display_name( base_name );
1d48a247
HJYP
343 g_free( base_name );
344
cf15ca10 345 gtk_window_set_title( (GtkWindow*)mw, disp_name );
76e88fcf 346 g_free( disp_name );
1d48a247 347
cf15ca10 348 return TRUE;
1d48a247
HJYP
349}
350
cf15ca10 351void main_win_close( MainWin* mw )
1d48a247 352{
cf15ca10 353 if( mw->pix )
9c52ae58 354 {
cf15ca10
HJYP
355 g_object_unref( mw->pix );
356 mw->pix = NULL;
1d48a247
HJYP
357 }
358}
359
cf15ca10 360void main_win_show_error( MainWin* mw, const char* message )
1d48a247 361{
cf15ca10 362 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
363 GTK_DIALOG_MODAL,
364 GTK_MESSAGE_ERROR,
365 GTK_BUTTONS_OK,
366 message );
367 gtk_dialog_run( (GtkDialog*)dlg );
368 gtk_widget_destroy( dlg );
369}
370
cf15ca10 371void on_size_allocate( GtkWidget* widget, GtkAllocation *allocation )
1d48a247 372{
cf15ca10 373 GTK_WIDGET_CLASS(main_win_parent_class)->size_allocate( widget, allocation );
52c867e9 374 if( GTK_WIDGET_REALIZED (widget) )
1d48a247 375 {
cf15ca10 376 MainWin* mw = (MainWin*)widget;
9c52ae58 377
cf15ca10 378 if( mw->zoom_mode == ZOOM_FIT )
52c867e9
HJYP
379 {
380 while(gtk_events_pending ())
381 gtk_main_iteration(); // makes it more fluid
1d48a247 382
c325c989 383 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
52c867e9 384 }
1d48a247
HJYP
385 }
386}
387
cf15ca10 388void main_win_fit_size( MainWin* mw, int width, int height, gboolean can_strech, GdkInterpType type )
1d48a247 389{
cf15ca10 390 if( ! mw->pix )
1d48a247
HJYP
391 return;
392
cf15ca10
HJYP
393 int orig_w = gdk_pixbuf_get_width( mw->pix );
394 int orig_h = gdk_pixbuf_get_height( mw->pix );
1d48a247 395
da8bd946
HJYP
396 if( can_strech || (orig_w > width || orig_h > height) )
397 {
cf15ca10
HJYP
398 gdouble xscale = ((gdouble)width) / orig_w;
399 gdouble yscale = ((gdouble)height)/ orig_h;
400 gdouble final_scale = xscale < yscale ? xscale : yscale;
1d48a247 401
cf15ca10 402 main_win_scale_image( mw, final_scale, type );
da8bd946
HJYP
403 }
404 else // use original size if the image is smaller than the window
405 {
cf15ca10
HJYP
406 mw->scale = 1.0;
407 image_view_set_scale( (ImageView*)mw->img_view, 1.0, type );
da8bd946 408 }
1d48a247
HJYP
409}
410
cf15ca10 411void main_win_fit_window_size( MainWin* mw, gboolean can_strech, GdkInterpType type )
1d48a247 412{
cf15ca10 413 mw->zoom_mode = ZOOM_FIT;
94fcf707 414
cf15ca10 415 if( mw->pix == NULL )
da8bd946 416 return;
94fcf707 417
cf15ca10 418 main_win_fit_size( mw, mw->scroll->allocation.width, mw->scroll->allocation.height, can_strech, type );
1d48a247
HJYP
419}
420
cf15ca10 421GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle )
1d48a247
HJYP
422{
423 GtkWidget* img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
424 GtkWidget* btn;
425 if( G_UNLIKELY(toggle) )
426 {
427 btn = gtk_toggle_button_new();
cf15ca10 428 g_signal_connect( btn, "toggled", cb, mw );
1d48a247
HJYP
429 }
430 else
431 {
432 btn = gtk_button_new();
cf15ca10 433 g_signal_connect( btn, "clicked", cb, mw );
1d48a247
HJYP
434 }
435 gtk_button_set_relief( (GtkButton*)btn, GTK_RELIEF_NONE );
436 gtk_button_set_focus_on_click( (GtkButton*)btn, FALSE );
437 gtk_container_add( (GtkContainer*)btn, img );
cf15ca10
HJYP
438 gtk_tooltips_set_tip( mw->tooltips, btn, tip, NULL );
439 gtk_box_pack_start( (GtkBox*)mw->nav_bar, btn, FALSE, FALSE, 0 );
1d48a247
HJYP
440 return btn;
441}
442
cf15ca10 443void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw )
1d48a247 444{
cf15ca10 445 gtk_button_clicked( (GtkButton*)mw->btn_fit );
1d48a247
HJYP
446}
447
cf15ca10 448void on_zoom_fit( GtkToggleButton* btn, MainWin* mw )
1d48a247
HJYP
449{
450 if( ! btn->active )
451 {
cf15ca10 452 if( mw->zoom_mode == ZOOM_FIT )
1d48a247
HJYP
453 gtk_toggle_button_set_active( btn, TRUE );
454 return;
455 }
cf15ca10 456 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
1d48a247 457
c325c989 458 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
459}
460
cf15ca10 461void on_full_screen( GtkWidget* btn, MainWin* mw )
1d48a247 462{
cf15ca10 463 if( ! mw->full_screen )
1d48a247 464 {
5bfec971 465 static GdkColor black = {0};
cf15ca10
HJYP
466 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &black );
467 gtk_widget_hide( gtk_widget_get_parent(mw->nav_bar) );
468 gtk_window_fullscreen( (GtkWindow*)mw );
1d48a247
HJYP
469 }
470 else
471 {
cf15ca10 472// gtk_widget_reset_rc_styles( mw->evt_box );
5bfec971 473 static GdkColor white = {0, 65535, 65535, 65535};
cf15ca10
HJYP
474 gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &white );
475 gtk_widget_show( gtk_widget_get_parent(mw->nav_bar) );
476 gtk_window_unfullscreen( (GtkWindow*)mw );
1d48a247 477 }
cf15ca10 478 mw->full_screen = ! mw->full_screen;
1d48a247
HJYP
479}
480
cf15ca10 481void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw )
1d48a247 482{
cf15ca10 483 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
484}
485
cf15ca10 486void on_orig_size( GtkToggleButton* btn, MainWin* mw )
1d48a247 487{
cf15ca10 488 // this callback could be called from activate signal of menu item.
1d48a247
HJYP
489 if( GTK_IS_MENU_ITEM(btn) )
490 {
cf15ca10 491 gtk_button_clicked( (GtkButton*)mw->btn_orig );
1d48a247
HJYP
492 return;
493 }
494
495 if( ! btn->active )
496 {
cf15ca10 497 if( mw->zoom_mode == ZOOM_ORIG )
1d48a247
HJYP
498 gtk_toggle_button_set_active( btn, TRUE );
499 return;
500 }
cf15ca10
HJYP
501 mw->zoom_mode = ZOOM_ORIG;
502 mw->scale = 1.0;
503// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 504// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 505
cf15ca10 506 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
1d48a247 507
cf15ca10 508 if( ! mw->pix )
1d48a247
HJYP
509 return;
510
cf15ca10 511 image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
1d48a247 512
9c52ae58
HJYP
513 while (gtk_events_pending ())
514 gtk_main_iteration ();
515
cf15ca10 516 main_win_center_image( mw ); // FIXME: mw doesn't work well. Why?
1d48a247
HJYP
517}
518
7c3b160c 519void on_next( GtkWidget* btn, MainWin* mw )
1d48a247 520{
cf15ca10
HJYP
521 const char* name;
522 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
523 return;
524
cf15ca10 525 name = image_list_get_prev( mw->img_list );
1d48a247 526
cf15ca10 527 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
528 {
529 // FIXME: need to ask user first?
cf15ca10 530 name = image_list_get_last( mw->img_list );
1d48a247
HJYP
531 }
532
533 if( name )
534 {
cf15ca10
HJYP
535 char* file_path = image_list_get_current_file_path( mw->img_list );
536 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
537 g_free( file_path );
538 }
539}
540
7c3b160c 541void on_prev( GtkWidget* btn, MainWin* mw )
1d48a247 542{
cf15ca10 543 if( image_list_is_empty( mw->img_list ) )
1d48a247
HJYP
544 return;
545
cf15ca10 546 const char* name = image_list_get_next( mw->img_list );
1d48a247 547
cf15ca10 548 if( ! name && image_list_has_multiple_files( mw->img_list ) )
1d48a247
HJYP
549 {
550 // FIXME: need to ask user first?
cf15ca10 551 name = image_list_get_first( mw->img_list );
1d48a247
HJYP
552 }
553
554 if( name )
555 {
cf15ca10
HJYP
556 char* file_path = image_list_get_current_file_path( mw->img_list );
557 main_win_open( mw, file_path, ZOOM_FIT );
1d48a247
HJYP
558 g_free( file_path );
559 }
560}
561
cf15ca10 562void on_rotate_clockwise( GtkWidget* btn, MainWin* mw )
1d48a247 563{
cf15ca10 564 rotate_image( mw, GDK_PIXBUF_ROTATE_CLOCKWISE );
7c3b160c 565 mw->rotation_angle += 90;
bb0f721e
HJYP
566 if(pref.auto_save_rotated){
567 pref.ask_before_save = FALSE;
7c3b160c 568 on_save(btn,mw);
bb0f721e 569 pref.ask_before_save = TRUE;
7c3b160c 570 }
1d48a247
HJYP
571}
572
cf15ca10 573void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw )
1d48a247 574{
cf15ca10 575 rotate_image( mw, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE );
7c3b160c 576 mw->rotation_angle += 270;
bb0f721e
HJYP
577 if(pref.auto_save_rotated){
578 pref.ask_before_save = FALSE;
7c3b160c 579 on_save(btn,mw);
bb0f721e 580 pref.ask_before_save = TRUE;
7c3b160c 581 }
1d48a247
HJYP
582}
583
584static void on_update_preview( GtkFileChooser *chooser, GtkImage* img )
585{
586 char* file = gtk_file_chooser_get_preview_filename( chooser );
587 GdkPixbuf* pix = NULL;
588 if( file )
589 {
590 pix = gdk_pixbuf_new_from_file_at_scale( file, 128, 128, TRUE, NULL );
591 g_free( file );
592 }
1d48a247 593 if( pix )
9c52ae58
HJYP
594 {
595 gtk_image_set_from_pixbuf( img, pix );
596 g_object_unref( pix );
597 }
1d48a247
HJYP
598}
599
cf15ca10 600void on_save_as( GtkWidget* btn, MainWin* mw )
1d48a247 601{
cf15ca10 602 if( ! mw->pix )
1d48a247
HJYP
603 return;
604
cf15ca10 605 GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)mw,
1d48a247
HJYP
606 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
607 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL );
608
cf15ca10 609 gtk_file_chooser_set_current_folder( dlg, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
610
611 GtkWidget* img = gtk_image_new();
612 gtk_widget_set_size_request( img, 128, 128 );
613 gtk_file_chooser_set_preview_widget( dlg, img );
614 g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
615
616 GtkFileFilter *filter;
617
618 /*
619 /// TODO: determine file type from file name
620 filter = gtk_file_filter_new();
621 gtk_file_filter_set_name( filter, _("Determined by File Name") );
622 gtk_file_filter_add_pixbuf_formats( filter );
623 gtk_file_chooser_add_filter( dlg, filter );
624 */
625
626 GSList* modules = gdk_pixbuf_get_formats();
627 GSList* module;
628 for( module = modules; module; module = module->next )
629 {
630 GdkPixbufFormat* format = (GdkPixbufFormat*)module->data;
631 if( ! gdk_pixbuf_format_is_writable( format ) )
632 continue;
633
634 filter = gtk_file_filter_new();
635
636 char* desc = gdk_pixbuf_format_get_description( format );
637 char* name = gdk_pixbuf_format_get_name( format );
638 char* tmp = g_strjoin( ": ", name, desc, NULL );
639 g_free( desc );
640 g_free( name );
641 gtk_file_filter_set_name( filter, tmp );
642 g_free( tmp );
643
644 char** mimes = gdk_pixbuf_format_get_mime_types( format ), **mime;
645 for( mime = mimes; *mime ; ++mime )
646 gtk_file_filter_add_mime_type( filter, *mime );
647 g_strfreev( mimes );
648 gtk_file_chooser_add_filter( dlg, filter );
649 }
650
651 if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
652 {
653 filter = gtk_file_chooser_get_filter( dlg );
654 const char* filter_name = gtk_file_filter_get_name( filter );
655 char* p = strstr( filter_name, ": " );
656 char* type = NULL;
657 if( ! p ) // auto detection
658 {
659 /// TODO: auto file type
660 }
661 else
662 {
663 type = g_strndup( filter_name, (p - filter_name) );
664 }
665 char* file = gtk_file_chooser_get_filename( dlg );
666 // g_debug("type = %s", type);
cf15ca10 667 main_win_save( mw, file, type, TRUE );
1d48a247
HJYP
668 g_free( file );
669 g_free( type );
670 }
671 gtk_widget_destroy( (GtkWidget*)dlg );
672}
673
7c3b160c
HJYP
674#ifdef HAVE_LIBJPEG
675int rotate_and_save_jpeg_lossless(char * filename,int angle){
676 JXFORM_CODE code = JXFORM_NONE;
677
678 angle = angle % 360;
679
680 if(angle == 90)
681 code = JXFORM_ROT_90;
682 else if(angle == 180)
683 code = JXFORM_ROT_180;
684 else if(angle == 270)
685 code = JXFORM_ROT_270;
686
687 //rotate the image and save it to /tmp/rot.jpg
688 int error = jpegtran (filename, "/tmp/rot.jpg" , code);
689 if(error)
690 return error;
691
692 //now copy /tmp/rot.jpg back to the original file
693 char command[strlen(filename)+50]; //this should not generate buffer owerflow
694 // MS: didn't know, how to make it better, maybe an own copy routine
695 sprintf(command,"cp /tmp/rot.jpg \"%s\"",filename);
696 system(command);
697
698 return 0;
699}
700#endif
701
cf15ca10 702void on_save( GtkWidget* btn, MainWin* mw )
1d48a247 703{
cf15ca10 704 if( ! mw->pix )
1d48a247
HJYP
705 return;
706
cf15ca10
HJYP
707 char* file_name = g_build_filename( image_list_get_dir( mw->img_list ),
708 image_list_get_current( mw->img_list ), NULL );
1d48a247
HJYP
709 GdkPixbufFormat* info;
710 info = gdk_pixbuf_get_file_info( file_name, NULL, NULL );
711 char* type = gdk_pixbuf_format_get_name( info );
7c3b160c
HJYP
712#ifdef HAVE_LIBJPEG
713 if(strcmp(type,"jpeg")==0){
714 if(rotate_and_save_jpeg_lossless(file_name,mw->rotation_angle)!=0)
715 main_win_show_error(mw, "Save failed! Check permissions.");
716 } else
717#endif
bb0f721e 718 main_win_save( mw, file_name, type, pref.ask_before_save );
7c3b160c 719 mw->rotation_angle = 0;
1d48a247
HJYP
720 g_free( file_name );
721 g_free( type );
722}
723
cf15ca10 724void on_open( GtkWidget* btn, MainWin* mw )
1d48a247 725{
cf15ca10 726 GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)mw,
1d48a247
HJYP
727 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
728 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
729
cf15ca10
HJYP
730 if( image_list_get_dir( mw->img_list ) )
731 gtk_file_chooser_set_current_folder( dlg, image_list_get_dir( mw->img_list ) );
1d48a247
HJYP
732
733 GtkWidget* img = gtk_image_new();
734 gtk_widget_set_size_request( img, 128, 128 );
735 gtk_file_chooser_set_preview_widget( dlg, img );
736 g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
737
738 GtkFileFilter *filter = gtk_file_filter_new();
739 gtk_file_filter_set_name( filter, _("All Supported Images") );
740 gtk_file_filter_add_pixbuf_formats( filter );
741 gtk_file_chooser_add_filter( dlg, filter );
742
743 filter = gtk_file_filter_new();
744 gtk_file_filter_set_name( filter, _("All Files") );
745 gtk_file_filter_add_pattern( filter, "*" );
746 gtk_file_chooser_add_filter( dlg, filter );
747
748 char* file = NULL;
749 if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
750 file = gtk_file_chooser_get_filename( dlg );
751 gtk_widget_destroy( (GtkWidget*)dlg );
752
753 if( file )
754 {
cf15ca10 755 main_win_open( mw, file, ZOOM_NONE );
1d48a247
HJYP
756 g_free( file );
757 }
758}
759
cf15ca10 760void on_zoom_in( GtkWidget* btn, MainWin* mw )
1d48a247 761{
cf15ca10
HJYP
762 mw->zoom_mode = ZOOM_SCALE;
763 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
764 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
765// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 766// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 767
cf15ca10
HJYP
768 double scale = mw->scale;
769 if( mw->pix && scale < 20.0 )
1d48a247 770 {
cf15ca10 771// busy(TRUE);
9c52ae58
HJYP
772 scale *= 1.05;
773 if( scale > 20.0 )
774 scale = 20.0;
cf15ca10
HJYP
775 if( mw->scale != scale )
776 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 777// adjust_adjustment_on_zoom(oldscale);
cf15ca10 778// busy(FALSE);
1d48a247
HJYP
779 }
780}
781
cf15ca10 782void on_zoom_out( GtkWidget* btn, MainWin* mw )
1d48a247 783{
cf15ca10
HJYP
784 mw->zoom_mode = ZOOM_SCALE;
785 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, FALSE );
786 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, FALSE );
787// gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll,
94fcf707 788// GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1d48a247 789
cf15ca10
HJYP
790 double scale = mw->scale;
791 if( mw->pix && scale > 0.02 )
1d48a247 792 {
cf15ca10 793// busy(TRUE);
da8bd946 794
9c52ae58
HJYP
795 scale /= 1.05;
796 if( scale < 0.02 )
797 scale = 0.02;
cf15ca10
HJYP
798 if( mw->scale != scale )
799 main_win_scale_image( mw, scale, GDK_INTERP_BILINEAR );
1d48a247 800// adjust_adjustment_on_zoom(oldscale);
cf15ca10 801// busy(FALSE);
1d48a247
HJYP
802 }
803}
804
cf15ca10 805void on_preference( GtkWidget* btn, MainWin* mw )
1d48a247 806{
bb0f721e 807 edit_preferences( (GtkWindow*)mw );
1d48a247
HJYP
808}
809
cf15ca10 810void on_quit( GtkWidget* btn, MainWin* mw )
1d48a247 811{
cf15ca10 812 gtk_widget_destroy( (GtkWidget*)mw );
1d48a247
HJYP
813}
814
cf15ca10 815gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 816{
9c52ae58
HJYP
817 if( ! GTK_WIDGET_HAS_FOCUS( widget ) )
818 gtk_widget_grab_focus( widget );
5bfec971 819
1d48a247
HJYP
820 if( evt->type == GDK_BUTTON_PRESS)
821 {
822 if( evt->button == 1 ) // left button
823 {
cf15ca10 824 if( ! mw->pix )
1d48a247 825 return FALSE;
cf15ca10
HJYP
826 mw->dragging = TRUE;
827 gtk_widget_get_pointer( (GtkWidget*)mw, &mw->drag_old_x ,&mw->drag_old_y );
828 gdk_window_set_cursor( widget->window, mw->hand_cursor );
1d48a247
HJYP
829 }
830 else if( evt->button == 3 ) // right button
831 {
cf15ca10 832 show_popup_menu( mw, evt );
1d48a247
HJYP
833 }
834 }
835 else if( evt->type == GDK_2BUTTON_PRESS && evt->button == 1 ) // double clicked
836 {
cf15ca10 837 on_full_screen( NULL, mw );
1d48a247 838 }
1d48a247
HJYP
839 return FALSE;
840}
841
cf15ca10 842gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw )
1d48a247 843{
cf15ca10 844 if( ! mw->dragging )
1d48a247 845 return FALSE;
9c52ae58 846
1d48a247 847 int cur_x, cur_y;
cf15ca10 848 gtk_widget_get_pointer( (GtkWidget*)mw, &cur_x ,&cur_y );
1d48a247 849
cf15ca10
HJYP
850 int dx = (mw->drag_old_x - cur_x);
851 int dy = (mw->drag_old_y - cur_y);
1d48a247
HJYP
852
853 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
854 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
855 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 856
9c52ae58 857 GtkRequisition req;
cf15ca10 858 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247
HJYP
859
860 if( ABS(dx) > 4 )
861 {
cf15ca10 862 mw->drag_old_x = cur_x;
9c52ae58 863 if( req.width > hadj->page_size )
1d48a247
HJYP
864 {
865 gdouble x = gtk_adjustment_get_value (hadj) + dx;
866 if( x < hadj->lower )
867 x = hadj->lower;
868 else if( (x + hadj->page_size) > hadj->upper )
869 x = hadj->upper - hadj->page_size;
870
871 if( x != hadj->value )
872 gtk_adjustment_set_value (hadj, x );
873 }
874 }
875
876 if( ABS(dy) > 4 )
877 {
9c52ae58 878 if( req.height > vadj->page_size )
1d48a247 879 {
cf15ca10 880 mw->drag_old_y = cur_y;
1d48a247
HJYP
881 gdouble y = gtk_adjustment_get_value (vadj) + dy;
882 if( y < vadj->lower )
883 y = vadj->lower;
884 else if( (y + vadj->page_size) > vadj->upper )
885 y = vadj->upper - vadj->page_size;
886
887 if( y != vadj->value )
888 gtk_adjustment_set_value (vadj, y );
889 }
890 }
891 return FALSE;
892}
893
cf15ca10 894gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1d48a247 895{
cf15ca10 896 mw->dragging = FALSE;
1d48a247
HJYP
897 gdk_window_set_cursor( widget->window, NULL );
898 return FALSE;
899}
900
cf15ca10 901gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw )
5bfec971
HJYP
902{
903 switch( evt->direction )
904 {
905 case GDK_SCROLL_UP:
cf15ca10 906 on_zoom_out( NULL, mw );
5bfec971
HJYP
907 break;
908 case GDK_SCROLL_DOWN:
cf15ca10 909 on_zoom_in( NULL, mw );
5bfec971
HJYP
910 break;
911 case GDK_SCROLL_LEFT:
cf15ca10 912 on_prev( NULL, mw );
5bfec971
HJYP
913 break;
914 case GDK_SCROLL_RIGHT:
cf15ca10 915 on_next( NULL, mw );
5bfec971
HJYP
916 break;
917 }
da8bd946 918 return TRUE;
5bfec971
HJYP
919}
920
cf15ca10 921gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key)
1d48a247 922{
cf15ca10 923 MainWin* mw = (MainWin*)widget;
1d48a247
HJYP
924 switch( key->keyval )
925 {
7c3b160c
HJYP
926 case GDK_Right:
927 case GDK_KP_Right:
928 case GDK_rightarrow:
1d48a247
HJYP
929 case GDK_Return:
930 case GDK_space:
931 case GDK_Next:
5bfec971
HJYP
932 case GDK_KP_Down:
933 case GDK_Down:
934 case GDK_downarrow:
cf15ca10 935 on_next( NULL, mw );
1d48a247 936 break;
7c3b160c
HJYP
937 case GDK_Left:
938 case GDK_KP_Left:
939 case GDK_leftarrow:
1d48a247
HJYP
940 case GDK_Prior:
941 case GDK_BackSpace:
5bfec971
HJYP
942 case GDK_KP_Up:
943 case GDK_Up:
944 case GDK_uparrow:
cf15ca10 945 on_prev( NULL, mw );
1d48a247
HJYP
946 break;
947 case GDK_KP_Add:
948 case GDK_plus:
641adad3 949 case GDK_equal:
cf15ca10 950 on_zoom_in( NULL, mw );
1d48a247
HJYP
951 break;
952 case GDK_KP_Subtract:
953 case GDK_minus:
cf15ca10 954 on_zoom_out( NULL, mw );
1d48a247 955 break;
1d48a247 956 case GDK_s:
641adad3 957// case GDK_S:
cf15ca10 958 on_save( NULL, mw );
1d48a247
HJYP
959 break;
960 case GDK_l:
5bfec971 961// case GDK_L:
cf15ca10 962 on_rotate_counterclockwise( NULL, mw );
1d48a247
HJYP
963 break;
964 case GDK_r:
5bfec971 965// case GDK_R:
cf15ca10 966 on_rotate_clockwise( NULL, mw );
1d48a247
HJYP
967 break;
968 case GDK_f:
5bfec971 969// case GDK_F:
cf15ca10
HJYP
970 if( mw->zoom_mode != ZOOM_FIT )
971 gtk_button_clicked((GtkButton*)mw->btn_fit );
1d48a247
HJYP
972 break;
973 case GDK_g:
5bfec971 974// case GDK_G:
cf15ca10
HJYP
975 if( mw->zoom_mode != ZOOM_ORIG )
976 gtk_button_clicked((GtkButton*)mw->btn_orig );
1d48a247
HJYP
977 break;
978 case GDK_o:
5bfec971 979// case GDK_O:
cf15ca10 980 on_open( NULL, mw );
5bfec971
HJYP
981 break;
982 case GDK_Delete:
983 case GDK_d:
984// case GDK_D:
cf15ca10 985 on_delete( NULL, mw );
1d48a247
HJYP
986 break;
987 case GDK_Escape:
cf15ca10
HJYP
988 if( mw->full_screen )
989 on_full_screen( NULL, mw );
1d48a247 990 else
cf15ca10 991 on_quit( NULL, mw );
1d48a247
HJYP
992 break;
993 case GDK_F11:
cf15ca10 994 on_full_screen( NULL, mw );
1d48a247
HJYP
995 break;
996
997 default:
cf15ca10 998 GTK_WIDGET_CLASS(main_win_parent_class)->key_press_event( widget, key );
1d48a247
HJYP
999 }
1000 return FALSE;
1001}
1002
cf15ca10 1003void main_win_center_image( MainWin* mw )
1d48a247
HJYP
1004{
1005 GtkAdjustment *hadj, *vadj;
cf15ca10
HJYP
1006 hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1007 vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1d48a247 1008
9c52ae58 1009 GtkRequisition req;
cf15ca10 1010 gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1d48a247 1011
9c52ae58
HJYP
1012 if( req.width > hadj->page_size )
1013 gtk_adjustment_set_value(hadj, ( hadj->upper - hadj->page_size ) / 2 );
1d48a247 1014
9c52ae58
HJYP
1015 if( req.height > vadj->page_size )
1016 gtk_adjustment_set_value(vadj, ( vadj->upper - vadj->page_size ) / 2 );
1d48a247
HJYP
1017}
1018
cf15ca10 1019void rotate_image( MainWin* mw, GdkPixbufRotation angle )
1d48a247 1020{
cf15ca10 1021 if( ! mw->pix )
1d48a247
HJYP
1022 return;
1023
9c52ae58 1024 GdkPixbuf* rpix = NULL;
cf15ca10
HJYP
1025 rpix = gdk_pixbuf_rotate_simple( mw->pix, angle );
1026 g_object_unref( mw->pix );
1027 mw->pix = rpix;
1028 image_view_set_pixbuf( mw->img_view, mw->pix );
1d48a247 1029
cf15ca10 1030 if( mw->zoom_mode == ZOOM_FIT )
c325c989 1031 main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1d48a247
HJYP
1032}
1033
cf15ca10 1034gboolean main_win_scale_image( MainWin* mw, double new_scale, GdkInterpType type )
1d48a247
HJYP
1035{
1036 if( G_UNLIKELY( new_scale == 1.0 ) )
1037 {
cf15ca10
HJYP
1038 gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
1039 return TRUE;
1d48a247 1040 }
cf15ca10
HJYP
1041 mw->scale = new_scale;
1042 image_view_set_scale( mw->img_view, new_scale, type );
2c34ffe0 1043
cf15ca10 1044 return TRUE;
1d48a247
HJYP
1045}
1046
cf15ca10 1047gboolean main_win_save( MainWin* mw, const char* file_path, const char* type, gboolean confirm )
1d48a247 1048{
cf15ca10
HJYP
1049 if( ! mw->pix )
1050 return FALSE;
1d48a247
HJYP
1051
1052 if( confirm ) // check existing file
1053 {
1054 if( g_file_test( file_path, G_FILE_TEST_EXISTS ) )
1055 {
cf15ca10 1056 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
1057 GTK_DIALOG_MODAL,
1058 GTK_MESSAGE_QUESTION,
1059 GTK_BUTTONS_YES_NO,
1060 _("The file name you selected already exist.\nDo you want to overwrite existing file?\n(Warning: The quality of original image might be lost)") );
1061 if( gtk_dialog_run( (GtkDialog*)dlg ) != GTK_RESPONSE_YES )
1062 {
1063 gtk_widget_destroy( dlg );
cf15ca10 1064 return FALSE;
1d48a247
HJYP
1065 }
1066 gtk_widget_destroy( dlg );
1067 }
1068 }
1069
1070 GError* err = NULL;
cf15ca10 1071 if( ! gdk_pixbuf_save( mw->pix, file_path, type, &err, NULL ) )
1d48a247 1072 {
cf15ca10
HJYP
1073 main_win_show_error( mw, err->message );
1074 return FALSE;
1d48a247
HJYP
1075 }
1076
cf15ca10 1077 return TRUE;
1d48a247
HJYP
1078}
1079
cf15ca10 1080void on_delete( GtkWidget* btn, MainWin* mw )
1d48a247 1081{
cf15ca10 1082 char* file_path = image_list_get_current_file_path( mw->img_list );
1d48a247
HJYP
1083 if( file_path )
1084 {
cf15ca10 1085 GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1d48a247
HJYP
1086 GTK_DIALOG_MODAL,
1087 GTK_MESSAGE_QUESTION,
1088 GTK_BUTTONS_YES_NO,
1089 _("Are you sure you want to delete current file?\n\nWarning: Once deleted, the file cannot be recovered.") );
1090 int resp = gtk_dialog_run( (GtkDialog*)dlg );
1091 gtk_widget_destroy( dlg );
1092
1093 if( resp == GTK_RESPONSE_YES )
1094 {
1095 g_unlink( file_path );
1096 if( errno )
cf15ca10 1097 main_win_show_error( mw, g_strerror(errno) );
1d48a247
HJYP
1098 g_free( file_path );
1099 }
1100 }
1101}
1102
cf15ca10 1103void show_popup_menu( MainWin* mw, GdkEventButton* evt )
1d48a247 1104{
5bfec971
HJYP
1105 static PtkMenuItemEntry menu_def[] =
1106 {
1107 PTK_IMG_MENU_ITEM( N_( "Previous" ), GTK_STOCK_GO_BACK, on_prev, GDK_leftarrow, 0 ),
1108 PTK_IMG_MENU_ITEM( N_( "Next" ), GTK_STOCK_GO_FORWARD, on_next, GDK_rightarrow, 0 ),
1109 PTK_SEPARATOR_MENU_ITEM,
1110 PTK_IMG_MENU_ITEM( N_( "Zoom Out" ), GTK_STOCK_ZOOM_OUT, on_zoom_out, GDK_minus, 0 ),
1111 PTK_IMG_MENU_ITEM( N_( "Zoom In" ), GTK_STOCK_ZOOM_IN, on_zoom_in, GDK_plus, 0 ),
1112 PTK_IMG_MENU_ITEM( N_( "Fit Image To Window Size" ), GTK_STOCK_ZOOM_FIT, on_zoom_fit_menu, GDK_F, 0 ),
1113 PTK_IMG_MENU_ITEM( N_( "Original Size" ), GTK_STOCK_ZOOM_100, on_orig_size_menu, GDK_G, 0 ),
1114 PTK_SEPARATOR_MENU_ITEM,
0c8ac162 1115 PTK_IMG_MENU_ITEM( N_( "Full Screen" ), GTK_STOCK_FULLSCREEN, on_full_screen, GDK_F11, 0 ),
5bfec971
HJYP
1116 PTK_SEPARATOR_MENU_ITEM,
1117 PTK_IMG_MENU_ITEM( N_( "Rotate Counterclockwise" ), "gtk-counterclockwise", on_rotate_counterclockwise, GDK_L, 0 ),
1118 PTK_IMG_MENU_ITEM( N_( "Rotate Clockwise" ), "gtk-clockwise", on_rotate_clockwise, GDK_R, 0 ),
1119 PTK_SEPARATOR_MENU_ITEM,
1120 PTK_IMG_MENU_ITEM( N_("Open File"), GTK_STOCK_OPEN, G_CALLBACK(on_open), GDK_O, 0 ),
1121 PTK_IMG_MENU_ITEM( N_("Save File"), GTK_STOCK_SAVE, G_CALLBACK(on_save), GDK_S, 0 ),
1122 PTK_IMG_MENU_ITEM( N_("Save As"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
1123 PTK_IMG_MENU_ITEM( N_("Delete File"), GTK_STOCK_DELETE, G_CALLBACK(on_delete), GDK_Delete, 0 ),
1124 PTK_SEPARATOR_MENU_ITEM,
1125 PTK_STOCK_MENU_ITEM( GTK_STOCK_ABOUT, on_about ),
1126 PTK_MENU_END
1127 };
1128
cf15ca10 1129 // mw accel group is useless. It's only used to display accels in popup menu
5bfec971 1130 GtkAccelGroup* accel_group = gtk_accel_group_new();
cf15ca10 1131 GtkMenuShell* popup = (GtkMenuShell*)ptk_menu_new_from_data( menu_def, mw, accel_group );
1d48a247
HJYP
1132
1133 gtk_widget_show_all( (GtkWidget*)popup );
1134 g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1135 gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
1136}
1137
7c3b160c
HJYP
1138/* callback used to open default browser when URLs got clicked */
1139static void open_url( GtkAboutDialog *dlg, const gchar *url, gpointer data)
1d48a247 1140{
7c3b160c
HJYP
1141 /* FIXME: is there any better way to do this? */
1142 char* programs[] = { "xdg-open", "gnome-open" /* Sorry, KDE users. :-P */, "exo-open" };
1143 int i;
1144 for( i = 0; i < G_N_ELEMENTS(programs); ++i)
1d48a247 1145 {
7c3b160c
HJYP
1146 char* open_cmd = NULL;
1147 if( (open_cmd = g_find_program_in_path( programs[i] )) )
1d48a247 1148 {
7c3b160c
HJYP
1149 char* cmd = g_strdup_printf( "%s \'%s\'", open_cmd, url );
1150 g_spawn_command_line_async( cmd, NULL );
1151 g_free( cmd );
1152 g_free( open_cmd );
1153 break;
1d48a247 1154 }
1d48a247 1155 }
1d48a247
HJYP
1156}
1157
cf15ca10 1158void on_about( GtkWidget* menu, MainWin* mw )
1d48a247
HJYP
1159{
1160 GtkWidget * about_dlg;
1161 const gchar *authors[] =
1162 {
1163 "洪任諭 Hong Jen Yee <pcman.tw@gmail.com>",
7c3b160c
HJYP
1164 "Martin Siggel <martinsiggel@googlemail.com>",
1165 _(" * Refer to source code of EOG image viewer and GThumb"),
1d48a247
HJYP
1166 _(" * Some icons are taken from gimmage"),
1167 NULL
1168 };
cf15ca10 1169 /* TRANSLATORS: Replace mw string with your names, one name per line. */
1d48a247
HJYP
1170 gchar *translators = _( "translator-credits" );
1171
7c3b160c
HJYP
1172 gtk_about_dialog_set_url_hook( open_url, mw, NULL);
1173
1d48a247 1174 about_dlg = gtk_about_dialog_new ();
7c3b160c
HJYP
1175
1176 gtk_container_set_border_width ( ( GtkContainer*)about_dlg , 2 );
1177 gtk_about_dialog_set_version ( (GtkAboutDialog*)about_dlg, VERSION );
1178 gtk_about_dialog_set_name ( (GtkAboutDialog*)about_dlg, _( "GPicView" ) );
1179 gtk_about_dialog_set_logo( (GtkAboutDialog*)about_dlg, gdk_pixbuf_new_from_file( PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL ) );
1180 gtk_about_dialog_set_copyright ( (GtkAboutDialog*)about_dlg, _( "Copyright (C) 2007" ) );
1181 gtk_about_dialog_set_comments ( (GtkAboutDialog*)about_dlg, _( "Lightweight image viewer from LXDE project" ) );
1182 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." );
1183 gtk_about_dialog_set_website ( (GtkAboutDialog*)about_dlg, "http://lxde.sourceforge.net/gpicview/" );
1184 gtk_about_dialog_set_authors ( (GtkAboutDialog*)about_dlg, authors );
1185 gtk_about_dialog_set_translator_credits ( (GtkAboutDialog*)about_dlg, translators );
1186 gtk_window_set_transient_for( (GtkWindow*) about_dlg, GTK_WINDOW( mw ) );
1187
1188 gtk_dialog_run( ( GtkDialog*)about_dlg );
1d48a247
HJYP
1189 gtk_widget_destroy( about_dlg );
1190}
1191
cf15ca10
HJYP
1192void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
1193 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw )
1d48a247
HJYP
1194{
1195 if( ! data || data->length <= 0)
1196 return;
1197
1d48a247
HJYP
1198 char* file = NULL;
1199 if( info == 0 ) // text/uri-list
1200 {
1201 char** uris = gtk_selection_data_get_uris( data );
1202 if( uris )
1203 {
1204 file = g_filename_from_uri(*uris, NULL, NULL);
1205 g_strfreev( uris );
1206 }
1207 }
1208 else if( info == 1 ) // text/plain
1209 {
1210 file = (char*)gtk_selection_data_get_text( data );
1211 }
1212 if( file )
1213 {
cf15ca10 1214 main_win_open( mw, file, ZOOM_FIT );
1d48a247
HJYP
1215 g_free( file );
1216 }
1217}