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