# +-----------------------------------------------------------------------+ # | pLoader - a Perl photo uploader for Piwigo | # +-----------------------------------------------------------------------+ # | Copyright(C) 2008 Piwigo Team http://piwigo.org | # +-----------------------------------------------------------------------+ # | This program is free software; you can redistribute it and/or modify | # | it under the terms of the GNU General Public License as published by | # | the Free Software Foundation | # | | # | This program is distributed in the hope that it will be useful, but | # | WITHOUT ANY WARRANTY; without even the implied warranty of | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | # | General Public License for more details. | # | | # | You should have received a copy of the GNU General Public License | # | along with this program; if not, write to the Free Software | # | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | # | USA. | # +-----------------------------------------------------------------------+ package Uploader::GUI::wxApp; use strict; use base qw/Wx::App Class::Accessor::Fast/; use Wx qw/ wxBITMAP_TYPE_GIF wxBITMAP_TYPE_ICO wxBITMAP_TYPE_BMP wxBITMAP_TYPE_PNG wxBITMAP_TYPE_JPEG wxIMAGE_QUALITY_NORMAL wxIMAGE_QUALITY_HIGH wxSPLASH_CENTRE_ON_SCREEN wxSPLASH_TIMEOUT wxDefaultPosition wxDefaultSize wxSIMPLE_BORDER wxFRAME_TOOL_WINDOW wxFRAME_NO_TASKBAR wxSTAY_ON_TOP wxWHITE wxICON_EXCLAMATION wxOK wxLANGUAGE_CHINESE_SIMPLIFIED wxLANGUAGE_CZECH wxLANGUAGE_DANISH wxLANGUAGE_DUTCH wxLANGUAGE_ENGLISH wxLANGUAGE_FRENCH wxLANGUAGE_GERMAN wxLANGUAGE_HUNGARIAN wxLANGUAGE_ITALIAN wxLANGUAGE_JAPANESE wxLANGUAGE_POLISH wxLANGUAGE_PORTUGUESE wxLANGUAGE_PORTUGUESE_BRAZILIAN wxLANGUAGE_RUSSIAN wxLANGUAGE_SLOVAK wxLANGUAGE_SPANISH /; use Uploader::GUI::wxFrameAUI; use File::Slurp ; use Data::Dumper; use Storable; use File::HomeDir; use File::Spec; use Uploader::PWG; use Uploader::ImageList; use Uploader::GUI::wxLoginDlg; use Wx::Locale qw/:default/; use utf8; $|=1; __PACKAGE__->mk_accessors( qw/ pwg site_url site_username site_password http_username http_password rejects imagelist storable_file wx_thumb_dir resized_dir userdata_dir resized_dir thumb_dir conf_file layout_file locale current_language languages available_languages version imagelist_version use_offline login_dlg branding frame perspective imageviewerIndex frameLayout chunk_size layout_clean colors eng_colors positions eng_positions default_photo_names eng_default_photo_names upload_hd eng_upload_hd / ); sub FilterEvent { my( $self, $event ) = @_; Wx::LogMessage "EventType %s", $event->GetEventType(); return -1; } sub OnInit { my( $self ) = @_; $self->version( '2.0.5C' ); # to check if we can use stored cache $self->imagelist_version( '9' ); $self->languages( [ ["中文 (%s)", wxLANGUAGE_CHINESE_SIMPLIFIED, 'Chinese simplified'], ["Česky (%s)", wxLANGUAGE_CZECH, 'Czech'], ["Dansk (%s)", wxLANGUAGE_DANISH, 'Danish'], ["Deutsch (%s)", wxLANGUAGE_GERMAN, 'German'], ["English (%s)", wxLANGUAGE_ENGLISH, 'English'], ["Español (%s)", wxLANGUAGE_SPANISH, 'Spanish'], ["Français (%s)", wxLANGUAGE_FRENCH, 'French'], ["Italiano (%s)", wxLANGUAGE_ITALIAN, 'Italian'], ["日本語 (にほんご) (%s)", wxLANGUAGE_JAPANESE, 'Japanese'], ["Magyar (%s)", wxLANGUAGE_HUNGARIAN, 'Hungarian'], ["Nederlands (%s)", wxLANGUAGE_DUTCH, 'Dutch'], ["Polski (%s)", wxLANGUAGE_POLISH, 'Polish'], ["Português Brasileiro (%s)", wxLANGUAGE_PORTUGUESE_BRAZILIAN, 'Portuguese Brazil'], ["Português Portugal (%s)", wxLANGUAGE_PORTUGUESE, 'Portuguese Portugal'], ["Русский (%s)", wxLANGUAGE_RUSSIAN, 'Russian'], ["Slovenčina (%s)", wxLANGUAGE_SLOVAK, 'Slovak'], ] ); # some languages may be unavailable due to system configuration. $self->_filter_available_languages; Wx::InitAllImageHandlers(); my $applicationName = "pLoader" ; $self->SetAppName( $applicationName ); $self->SetVendorName( "Piwigo Team" ); $self->{IMGTYPE} = { 'jpg' => wxBITMAP_TYPE_JPEG, 'gif' => wxBITMAP_TYPE_GIF, 'png' => wxBITMAP_TYPE_PNG, }; $self->_init_userdir; my $conf = retrieve $self->conf_file if -e $self->conf_file; if(defined $conf ){ $self->SetKeyValues($conf); } else { $self->_readParams( 'pLoader.ini' ) unless defined $conf ; } # when language is switched, need a new layout unlink $self->layout_file if $self->layout_clean; $self->layout_clean(0); my $layout = retrieve $self->layout_file if -e $self->layout_file; if(defined $layout ){ $self->SetKeyValues($layout); } $self->site_url( $self->{site_url} ); $self->site_username( $self->{site_username} ); $self->site_password( $self->{site_password} ); $self->http_username( $self->{http_username} ); $self->http_password( $self->{http_password} ); $self->current_language( $self->{current_language}||Wx::Locale::GetSystemLanguage() ); $self->chunk_size( $self->{chunk_size}||500_000 ); $self->init_locale; $self->_init_localized_properties; my $not_exit = $self->Login(); # user pressed OK if($not_exit){ $self->StoreConnectionProperties; if( !$self->use_offline ){ while( $not_exit and !$self->_is_connected ){ $not_exit = $self->Login(); last if $self->use_offline; } } $self->_init_imagelist; $self->_init_thumbimagelist; $self->_init_frame; } $not_exit; } # some properties are displayed in choice list, with translated strings # the translation has to be reverted to store a language independant value sub _init_localized_properties { my ( $self ) = @_; $self->colors( ['Black', 'White'] ); # We need to translate back to english when we store properties $self->eng_colors( { map { gettext($_) => $_ } @{$self->colors} } ); $self->positions( [ 'Top', 'Left', 'Right', 'Bottom', 'Top left', 'Top right', 'Bottom left', 'Bottom right', 'Center', ] ); $self->eng_positions( { map { gettext($_) => $_ } @{$self->positions} } ); $self->upload_hd( [ 'No', 'Yes, use HD resized of the original photo', 'Yes, use a copy of the original photo', ] ); $self->eng_upload_hd( { map { gettext($_) => $_ } @{$self->upload_hd} } ); $self->default_photo_names( [ 'None', 'File name', 'File path and name', 'Prefix', 'Prefix + rank number', 'Rank number + prefix', 'Prefix + create date chrono', 'Create date chrono + prefix', ] ); $self->eng_default_photo_names( { map { gettext($_) => $_ } @{$self->default_photo_names} } ); } sub _filter_available_languages { my ( $self ) = @_; # check if the locale can be set and the translation catalog available $self->available_languages( [ grep {$_} map{ # a locale may be unavailable due to system limitations ( ex: chinese, japanese when language pack are not installed ) if(Wx::Locale::IsAvailable($_->[1])){ my $locale = Wx::Locale->new($_->[1]); $locale->AddCatalogLookupPathPrefix('../locale'); $_ if $locale->AddCatalog('pLoader'); } } @{$self->languages} ] ); } sub _is_connected { my ( $self ) = @_; my $is_connected; if($self->pwg->login_result->{stat} eq 'ok'){ $is_connected = 1; } else{ Wx::MessageBox( sprintf( "%s\n\n%s %s %s", $self->pwg->login_result->{message}, gettext("Connection to"), $self->site_url, gettext("failed"), ), gettext("Piwigo login error"), wxOK | wxICON_EXCLAMATION, ); } $is_connected; } my $locale; sub init_locale { my ( $self, $language ) = @_; $self->current_language( $language ) if defined $language; undef $locale; $locale = Wx::Locale->new( $self->current_language ); $locale->AddCatalogLookupPathPrefix( '../locale'); if(!$locale->AddCatalog( 'pLoader.mo' )){ Wx::LogMessage gettext("Cannot find translation catalog files for %s. Use default language"), $locale->GetCanonicalName(); } $self->locale($locale); } sub StoreConnectionProperties { my ( $self ) = @_; eval { store( { map{ $_ => $self->{$_}, } qw/ site_url site_username site_password http_username http_password current_language chunk_size layout_clean / }, $self->conf_file ); }; } sub StoreLayoutProperties { my ( $self ) = @_; eval { store( { map{ $_ => $self->{$_}, } qw/ perspective imageviewerIndex frameLayout / }, $self->layout_file ); }; } sub _init_imagelist { my ( $self ) = @_; my $stored_imagelist; my $use_new_imagelist; if( -e $self->storable_file ){ eval { $stored_imagelist = retrieve $self->storable_file; }; if($@){ Wx::LogMessage( gettext("An error has occured. Can not read %s\n%s"), $self->storable_file, $@ ); $use_new_imagelist = 1 ; } # should have a valid imagelist else{ $use_new_imagelist = 1 unless $self->imagelist_version eq $stored_imagelist->{imagelist_version}; if($use_new_imagelist){ Wx::LogMessage(gettext("pLoader has to reset image cache.")); } } } else{ $use_new_imagelist = 1 ; } if($use_new_imagelist){ $stored_imagelist = $self->_default_imagelist_params ; } $self->imagelist( Uploader::ImageList->new( $stored_imagelist ) ); $self->imagelist->RescaleCallback( sub { $self->RescaleImage(@_) } ); $self->imagelist->ResizeCallback( sub { $self->ResizeImage(@_) } ); $self->imagelist->YieldCallback( sub { Wx::Yield } ); } sub _default_imagelist_params { my ( $self ) = @_ ; my $params = { new_files => [], hd_filter => 'Lanczos', hd_blur => 0.9, hd_quality => 95, hd_w => 1600, hd_h => 1200, hd_interlace => 'Line', thumb_size => 120, site_thumb_dir => $self->thumb_dir, wx_thumb_size => 100, wx_thumb_dir => $self->wx_thumb_dir, resize_w => 800, resize_h => 600, site_resized_dir => $self->resized_dir, type => 'jpg', filter => 'Lanczos', blur => 0.9, quality => 95, wx_quality => 90, th_quality => 90, auto_rotate => 1, upload_hd => 'No', remove_uploaded_from_selection => 1, interlace => 'Line', create_resized => 1, prefix => 'TN', count => 0, storable_file => $self->storable_file, userdata_dir => $self->userdata_dir, default_photo_name => 'File name', default_name_prefix => gettext('Photo '), upload_rejects => [], image_sums => {}, sums => [], version => $self->version, imagelist_version => $self->imagelist_version, RescaleCallback => sub { $self->RescaleImage(@_) }, ResizeCallback => sub { $self->ResizeImage(@_) }, watermark_text => gettext("my watermark"), watermark_text_size => 12, watermark_position => 'Center', watermark_y => 10, watermark_x => 10, watermark_color => 'White', }; return $params; } sub Login { my ( $self ) = @_; $self->login_dlg( Uploader::GUI::wxLoginDlg->new( { title => gettext("Piwigo login"), site_url => sub { $self->site_url(@_) }, site_username => sub { $self->site_username(@_) }, site_password => sub { $self->site_password(@_) }, use_offline => sub { $self->use_offline(@_) }, } ) ) unless $self->login_dlg; my $icon = Wx::Icon->new(); $icon->LoadFile('../res/favicon.ico', wxBITMAP_TYPE_ICO); $self->login_dlg->SetIcon($icon); my $rval = $self->login_dlg->ShowModal(); $self->login_dlg->Show(0); $self->_init_branding; if ($self->site_url !~ /^http:/){ $self->site_url( sprintf( "http://%s", $self->site_url ) ); } $self->pwg( # get these parameters from dialog or from file Uploader::PWG->new( { site_url => $self->site_url, site_username => $self->site_username, site_password => $self->site_password, http_username => $self->http_username, http_password => $self->http_password, branding => $self->branding, chunk_size => $self->chunk_size, use_offline => $self->use_offline, version => $self->version, } ) ); $rval; } sub _init_userdir { my ( $self ) = @_; my $applicationName = $self->GetAppName ; my $userdatadir = File::Spec->canonpath( File::Spec->catfile( File::HomeDir->my_data(), "\.$applicationName" ) ); if(! -d $userdatadir){ if(! mkdir $userdatadir){ Wx::MessageBox( sprintf( "%s directory creation failed", $userdatadir, ), "pLoader working directory creation error", wxOK | wxICON_EXCLAMATION, ); $userdatadir = File::Spec->canonpath( File::Spec->catfile( File::Spec->tmpdir(), "\.$applicationName" ) ); mkdir $userdatadir; } } $self->userdata_dir($userdatadir); $self->conf_file( File::Spec->catfile( $self->userdata_dir, ".$applicationName.conf" ) ); $self->layout_file( File::Spec->catfile( $self->userdata_dir, ".$applicationName.layout" ) ); $self->storable_file( File::Spec->catfile($self->userdata_dir, 'pLoader.dat') ); my $thumbdir = File::Spec->catfile($self->userdata_dir, 'thumbnails'); mkdir $thumbdir unless -d $thumbdir ; $self->thumb_dir($thumbdir); my $wxthumbdir = File::Spec->catfile($self->userdata_dir, 'wxthumbnails'); mkdir $wxthumbdir unless -d $wxthumbdir ; $self->wx_thumb_dir($wxthumbdir); my $resizedir = File::Spec->catfile($self->userdata_dir, 'resize'); mkdir $resizedir unless -d $resizedir ; $self->resized_dir($resizedir); } sub _init_thumbimagelist { my ( $self ) = @_; $self->imagelist->wx_thumb_imglist( Wx::ImageList->new( $self->imagelist->wx_thumb_size, $self->imagelist->wx_thumb_size, 1, 0 ) ); # reload images $self->_reload_thumb_images; } sub _reload_thumb_images { my ( $self ) = @_; my $wximagelist = $self->imagelist->wx_thumb_imglist; my $sums = $self->imagelist->sums; map { my $image = $self->imagelist->image_sums->{$_}; $wximagelist->Add( Wx::Bitmap->new( $image->wx_thumb_file, $self->GetWxBitmapType($self->imagelist->type), ) ); } @$sums ; } sub GetWxBitmapType { my ( $self, $type ) = @_; $self->{IMGTYPE}->{$type}; } sub RescaleImage { my ( $self, $image_file, $image_file_out, $type, $ratio, $width, $height, $quality ) = @_; my $image = Wx::Image->new( $image_file, $self->GetWxBitmapType($type), 0 ); my $w; my $h; my $img_w = $image->GetWidth; my $img_h = $image->GetHeight; # use a ratio ( 25% default ) if defined # default ratio is used for preview. if($ratio){ $w = $ratio*$img_w/100 ; $h = $ratio*$img_h/100 ; } # use specified width and height else{ # portrait if( $img_w < $img_h ){ $w = $height; } else{ $w = $width; } # to respect aspect ratio $h = sprintf( "%.0f", ($w*$img_h)/$img_w ); } $image->Rescale( $w, $h, wxIMAGE_QUALITY_HIGH ); $quality ||= 90; $image->SetOption( "quality", $quality ); if(!$image->SaveFile( $image_file_out, $self->GetWxBitmapType($type), )){ Wx::LogMessage( gettext("An error has occured. Can not save file %s"), $image_file_out, ) }; } sub ResizeImage { my ( $self, $image_file, $image_file_out, $type, $width, $height, $quality ) = @_; my $image = Wx::Image->new( $image_file, $self->GetWxBitmapType($type), 0 ); my $w; my $h; my $img_w = $image->GetWidth; my $img_h = $image->GetHeight; # portrait if( $img_w < $img_h ){ $w = $height; } else{ $w = $width; } # to respect aspect ratio $h = sprintf( "%.0f", ($w*$img_h)/$img_w ); $image->Rescale( $w, $h, wxIMAGE_QUALITY_HIGH ); $image->Resize( [ $width, $height ], [ 0, 20], ); $quality ||= 90; $image->SetOption( "quality", $quality ); if(!$image->SaveFile( $image_file_out, $self->GetWxBitmapType($type), )){ Wx::LogMessage( gettext("An error has occured. Can not save file %s"), $image_file_out, ) }; } # some labels differ with branding ( piwigo.com or piwigo.org ) sub _init_branding { my ( $self ) =@_; if( $self->site_url =~ /\.piwigo\.com/ ){ $self->branding( { category => gettext("album"), Category => gettext("Album"), categories => gettext("albums"), Categories => gettext("Albums"), 'Add new category' => gettext("Add new album"), 'Category name' => gettext("Album name :"), 'New category' => gettext("New album"), } ); } else{ $self->branding( { category => gettext("categorie"), Category => gettext("Categorie"), categories => gettext("categories"), Categories => gettext("Categories"), 'Add new category' => gettext("Add new category"), 'Category name' => gettext("Category name :"), 'New category' => gettext("New category"), } ); } } sub SaveConfig { my ( $self, $params ) = @_; my $config = Wx::ConfigBase::Get; map { $config->WriteInt( $_, $params->{$_} ) } keys %$params; $config->Write( 'Perspective', $params->{Perspective} ) } sub _init_frame { my ( $self ) = @_; my $url = $self->site_url; if($self->use_offline){ $url = gettext("Work Offline"); } $self->frame( Uploader::GUI::wxFrameAUI->new( { title => sprintf("pLoader - Piwigo uploader %s - [%s]", $self->version, $url), pwg => $self->pwg, imagelist => $self->imagelist, perspective => $self->perspective, imageviewer_index => $self->imageviewerIndex, frameLayout => $self->frameLayout, } ) ); $self->frame->Show( 1 ); $self->SetTopWindow( $self->frame ); my $icon = Wx::Icon->new(); $icon->LoadFile('../res/favicon.ico', wxBITMAP_TYPE_ICO); $self->frame->SetIcon($icon); } sub _readParams { my( $self, $file ) = @_ ; my $expr_params ; eval { $expr_params = read_file( $file ); } ; my $paramValues = [] ; if($expr_params){ my $expr = '$paramValues = ' ; $expr .= "$expr_params ; " ; eval $expr ; } return unless 'ARRAY' eq ref $paramValues ; if(scalar(@$paramValues )){ my $params = $paramValues->[0] ; $self->SetKeyValues($params); } } sub SetKeyValues { my ( $self, $params )= @_; foreach( keys %$params ) { $self->{$_} = $params->{$_} ; } } 1;