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