source: extensions/pLoader/trunk/src/Uploader/GUI/Frame.pm @ 5261

Last change on this file since 5261 was 5261, checked in by ronosman, 14 years ago

Feature 1530 added : add button is displayed in the categories list when it is empty.

File size: 62.5 KB
Line 
1# +-----------------------------------------------------------------------+
2# | pLoader - a Perl photo uploader for Piwigo                            |
3# +-----------------------------------------------------------------------+
4# | Copyright(C) 2008-2010 Piwigo Team                  http://piwigo.org |
5# +-----------------------------------------------------------------------+
6# | This program is free software; you can redistribute it and/or modify  |
7# | it under the terms of the GNU General Public License as published by  |
8# | the Free Software Foundation                                          |
9# |                                                                       |
10# | This program is distributed in the hope that it will be useful, but   |
11# | WITHOUT ANY WARRANTY; without even the implied warranty of            |
12# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
13# | 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 Free Software           |
17# | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
18# | USA.                                                                  |
19# +-----------------------------------------------------------------------+
20package Uploader::GUI::Frame;
21use strict;
22use Data::Dumper;
23use Carp;
24use Wx;
25use Wx::DND;
26use Wx qw/
27             wxCLIP_CHILDREN
28             wxYES_NO
29             wxYES
30             wxICON_QUESTION
31             wxITEM_NORMAL
32             wxID_OK
33             wxDEFAULT_FRAME_STYLE
34             wxVERTICAL
35             wxGROW
36             wxSHAPED
37             wxBITMAP_TYPE_JPEG
38             wxBITMAP_TYPE_GIF
39             wxBITMAP_TYPE_PNG
40             wxBITMAP_TYPE_ANY
41             wxTB_FLAT
42             wxTB_TEXT
43             wxSIZE
44             wxWHITE
45             wxBLACK
46             wxID_CANCEL
47             wxFD_OPEN
48             wxFD_MULTIPLE
49             wxALL
50             wxGROW
51             wxDefaultPosition
52             wxDefaultSize
53             wxTheApp
54             wxIMAGE_LIST_NORMAL
55             wxNO_BORDER
56             wxTE_MULTILINE
57             wxTE_READONLY
58             wxITEM_NORMAL
59             wxBORDER_NONE
60             wxNullBitmap
61             wxTR_MULTIPLE
62             wxTR_EXTENDED
63             wxTR_HIDE_ROOT
64             wxTR_HAS_BUTTONS
65             wxTR_EDIT_LABELS
66             wxMAXIMIZE
67             wxOK
68             wxICON_EXCLAMATION
69             wxICON_INFORMATION
70             WXK_DELETE
71             wxHORIZONTAL
72             wxVERTICAL
73             wxEXPAND
74             wxSWISS
75             wxNORMAL
76             wxBOLD
77             wxALIGN_CENTER
78         /;
79use base qw/Wx::Frame Class::Accessor::Fast/;
80use File::Spec;
81use Wx::Locale qw/:default/;
82use POSIX qw(ceil floor);
83
84require Win32 if($^O =~ /MSWin32/);
85
86
87
88my @properties = 
89    qw/
90          progressdlg
91          upload_progressdlg
92          imageviewer
93          imageviewer_img
94          tree
95          tree_root
96          treeimglist
97          tree_item_default
98          pwg
99          logwnd
100          oldlogwnd
101          categories
102          imagelist
103          image_preview
104          image_prop_piwigo
105          image_prop_exif
106          image_prop_tags
107          global_settings_panel
108          piwigo_property_list
109          exif_properties
110          global_settings
111          toolbar
112          branding
113          current_imageviewer_index
114          imageviewer_mnu
115          tree_mnu
116          imageviewer_select_multi
117          frameLayout
118          piwigo_tags
119          image_tags
120          piwigo_photo_properties
121          dlg_piwigo_photo_properties
122          piwigo_photo_properties_tags
123          image_preview_refresh
124          imageviewer_refresh
125          imageviewer_item_refresh
126          horizontal_splitter
127      /;
128__PACKAGE__->mk_accessors( @properties );
129
130use Wx::Perl::TextValidator;
131use Uploader::GUI::wxImageListCtrl;
132use Uploader::GUI::wxPropertyListDlg;
133use Uploader::GUI::wxGlobalSettings;
134use Uploader::GUI::wxImagePreview;
135use Uploader::GUI::wxPhotoProperties;
136use Uploader::GUI::wxImageReuploadDlg;
137use Uploader::GUI::wxImageProcessingProgressDlg;
138use Uploader::GUI::wxChoiceFilteredPanel;
139use utf8;
140$|=1;
141
142my $ID_TREE_CTX_MENU = 20000 ;
143my $ID_IMAGEVIEWER_CTX_MENU = 20100 ;
144
145
146sub new {
147    my( $class, $params ) = @_;
148    my $self  = $class->SUPER::new( 
149        undef, 
150        -1, 
151        $params->{title},
152        wxDefaultPosition,
153        wxDefaultSize, 
154        wxDEFAULT_FRAME_STYLE
155    );
156
157
158    $self->pwg( $params->{pwg} );
159    $self->imagelist( $params->{imagelist} );
160
161    # callback for GUI refresh : add thumbnail images to the imageviewer control
162    $self->imagelist->SetNewFilesViewerRefreshCallback(
163        sub { $self->SetNewFilesViewerRefresh(@_) }
164    );
165
166    # callback for GUI refresh : progress dialog display of thumbnail image being created
167    $self->imagelist->SetNewFilesProgressCallback(
168        sub { $self->SetNewFilesProgress(@_) }
169    );
170
171    $self->imagelist->SetNewFilesDisplayEndInfoCallback(
172        sub { $self->SetNewFilesDisplayEndInfo(@_) }
173    );
174
175    # callback for GUI refresh : remove thumbnail images from imageviewer control
176    $self->imagelist->UploadImagesViewerCallback(
177        sub { $self->UploadImagesViewerRefresh(@_) }
178    );
179   
180    $self->imagelist->ReuploadCallback(
181    sub 
182    {
183        Uploader::GUI::wxImageReuploadDlg->new(
184        { 
185            title => gettext("Photo update management"),
186            properties => 
187            {
188                $main::ID_REUPLOAD_ACTION_FILES => 
189                {
190                    selection=>sub {$self->imagelist->reupload_action_files(@_)},
191                    label=>gettext("What shall we do with files? (thumbnail, resized, high resolution)"),
192                    labels=>[
193                        gettext("nothing"),
194                        gettext("replace"),
195                    ],
196                },
197                $main::ID_REUPLOAD_ACTION_PROPERTIES => 
198                { 
199                    selection=>sub{$self->imagelist->reupload_action_properties(@_)},
200                    label=>gettext("What shall we do with single value properties?(caption, comment, author, create date)"),
201                    labels=>[
202                        gettext("nothing"),
203                        gettext("fill if empty (only replace properties currently empty in Piwigo)"),
204                        gettext("replace"),
205                    ],
206                },
207                $main::ID_REUPLOAD_ACTION_PROPERTIES_M =>
208                { 
209                    selection=>sub{$self->imagelist->reupload_action_properties_m(@_)},
210                    label=>gettext("What shall we do with multiple values properties? (categories, tags)"),
211                    labels=>[
212                        gettext("nothing"),
213                        gettext("append (keep existing and add new)"),
214                        gettext("replace"),
215                    ],
216                },
217                $main::ID_REUPLOAD_NOT_ASK => 
218                { 
219                    value=>sub{$self->imagelist->reupload_not_ask(@_)}, 
220                    label=>gettext("Do not ask me again"),
221                },
222                $main::ID_REUPLOAD_TEXT => 
223                { 
224                    label=>gettext("A least one photo has already been added in the past."),
225                },
226            },
227        } )->ShowModal();}
228    );
229
230
231    # callback for GUI refresh : progress dialog display current uploaded image
232    $self->imagelist->progress_thumbnail_refresh(
233        sub { $self->UploadProgressThumbnailRefresh(@_) }
234    );
235
236    $self->imagelist->progress_msg_refresh(
237        sub { $self->UploadProgressMessageRefresh(@_) }
238    );
239
240    $self->imagelist->progress_msg_details_refresh(
241        sub { $self->UploadProgressMessageDetailsRefresh(@_) }
242    );
243
244    $self->imagelist->progressbar_refresh(
245        sub { $self->UploadProgressBarRefresh(@_) }
246    );
247   
248    $self->imagelist->progress_endinfo_refresh(
249        sub { $self->UploadDisplayEndInfo(@_) }
250    );
251
252
253    $self->imagelist->pwg(
254        $self->pwg
255    );
256
257    $self->imagelist->categories(
258        []
259    );
260
261
262    $self->_set_setting_properties;
263    $self->_initFrame;
264    $self->_initMenus;
265    $self->_initEventHandlers;
266    $self->_initImgTypes;   
267
268    $self->imageviewer->SelectItem(
269        $self->current_imageviewer_index
270    ) if $self->imageviewer->GetItemCount;
271   
272    # if file in command line parameters, try to load
273    my $files = wxTheApp->argv;
274    $self->SetNewFiles($files) if scalar @$files;
275
276    $self->Show;
277    $self;
278}
279
280
281sub _initImgTypes {
282    my ( $self ) = @_;
283
284    $self->{IMGTYPE} = {
285        'jpg' => wxBITMAP_TYPE_JPEG,
286        'gif' => wxBITMAP_TYPE_GIF,
287        'png' => wxBITMAP_TYPE_PNG,
288    };
289}
290
291
292sub GetWxBitmapType {
293    my ( $self, $type ) = @_;
294   
295    $self->{IMGTYPE}->{$type};
296}
297
298
299
300sub _set_setting_properties {
301    my ( $self ) = @_;
302
303    $self->piwigo_photo_properties(
304        {
305            $main::CAPTION => { label=>gettext("Caption")},
306            $main::PHOTO_PROPERTIES_CAPTION => {
307                multi_selection_mode => sub { $self->multi_selection_mode },
308                string_selection => sub { return 1; },
309                value => sub {
310                    $self->imagelist->SetImageSelectionName(@_);
311                    $self->multi_selection_mode ?
312                    $self->imagelist->image_selection_name :
313                    $self->imagelist->current_image->site_name;
314                },
315                pre_process => sub { sub { $self->imagelist->GetCurrentImageDefaultName(@_); } },
316                choices =>
317                [
318                    map { gettext $_ } @{wxTheApp->default_photo_names}
319                ],
320                frame_callback => sub { $self->imageviewer_item_refresh(1); },
321            },
322            $main::COMMENT => { label=>gettext("Comment")},
323            $main::PHOTO_PROPERTIES_COMMENT => {
324                value => sub { 
325                    $self->multi_selection_mode ?
326                    $self->imagelist->SetImageSelectionComment(@_):
327                    $self->imagelist->current_image->site_comment(@_) 
328                },
329                frame_callback => sub { $self->imageviewer_item_refresh(1); },
330            },
331            $main::AUTHOR => { label=>gettext("Author")},
332            $main::PHOTO_PROPERTIES_AUTHOR => { 
333                value => sub { 
334                    $self->multi_selection_mode ?
335                    $self->imagelist->SetImageSelectionAuthor(@_):
336                    $self->imagelist->current_image->site_author(@_) 
337                },
338                frame_callback => sub { $self->imageviewer_item_refresh(1); },
339            },
340            $main::TAGS => { label=>gettext("Tags")},
341            $main::CREATE_DATE => { label=>gettext("Create date")},
342            $main::PHOTO_PROPERTIES_CREATE_DATE => { 
343                value => sub { 
344                    $self->multi_selection_mode ?
345                    $self->imagelist->SetImageSelectionCreateDate(@_):
346                    $self->imagelist->current_image->create_date(@_) 
347                },
348                frame_callback => sub { $self->imageviewer_item_refresh(1); },
349            },
350            $main::PRIVACY_LEVEL => { label=>gettext("Who can see?")},
351            $main::PHOTO_PROPERTIES_PRIVACY_LEVEL => {
352                selection => sub {
353                    $self->multi_selection_mode ? 
354                    $self->imagelist->SetImageSelectionPrivacyLevel(@_):
355                    $self->imagelist->current_image->privacy_level(@_)
356                },
357                choices => wxTheApp->privacy_level_choices,
358            },
359            $main::PHOTO_PROPERTIES_NB => {
360                texts => [
361                    gettext("Properties"),
362                    gettext("Tags")
363                ], 
364            },
365            $main::PHOTO_PROPERTIES_UPLOAD => { label=>gettext("Upload to Piwigo") },
366        }
367    );   
368
369    $self->global_settings(
370        {
371            $main::DEFAULT_PHOTO_CAPTION => { label => gettext("Default photo caption") },
372            $main::CPANE_RESIZE_ADVANCED => { label => gettext("Advanced") },
373            $main::CPANE_HD_ADVANCED => { label => gettext("Advanced") },
374            $main::CPANE_TRANSFERT_ADVANCED => { label => gettext("Advanced") },
375            $main::GS_THUMBNAIL => { label => gettext("Thumbnail") },
376            $main::GS_SITE_IMG => { label => gettext("Web sized") },
377            $main::GS_HD => { label => gettext("High definition") },
378            $main::GS_CLOSE => { label => gettext("Close")},
379            $main::GS_DEFAULT_PHOTO_CAPTION => {
380                string_selection => sub { $self->imagelist->default_photo_name(@_) },
381                choices =>
382                [
383                    map { gettext $_ } @{wxTheApp->default_photo_names}
384                ],
385                pre_process => sub { my ( $value ) = @_; wxTheApp->eng_default_photo_names->{$value} },
386                frame_callback => sub { 
387                    my( $self, $ctrl, $event ) = @_; 
388                    $self->OnDefaultPhotoCaption($event);
389                },
390            },
391            $main::PHOTO_CAPTION_PREFIX => { label => gettext("Photo caption  prefix") },
392            $main::GS_PHOTO_CAPTION_PREFIX => {
393                value => sub { $self->imagelist->default_name_prefix(@_) },
394            },
395            $main::GS_AUTO_ROTATE => {
396                label => gettext("Auto rotate image"),
397                value => sub { $self->imagelist->auto_rotate(@_) }
398           },
399            $main::DEFAULT_AUTHOR => { label => gettext("Default author")},
400            $main::GS_DEFAULT_AUTHOR => { value => sub { $self->imagelist->author(@_) } },
401            $main::THUMBNAIL_SIZE => { label => gettext("Site thumbnail size") },
402            $main::GS_THUMBNAIL_SIZE => {
403                value => sub { $self->imagelist->thumb_size(@_) },
404                validator => Wx::Perl::TextValidator->new( '\d' ) 
405            },
406            $main::THUMBNAIL_JPEG_QUALITY => { label => gettext("Site thumbnail jpeg quality") },
407            $main::GS_THUMBNAIL_JPEG_QUALITY => {
408                value => sub { $self->imagelist->th_quality(@_) },
409                validator => Wx::Perl::TextValidator->new( '\d' ) 
410            },
411            $main::GS_CREATE_RESIZED => {
412                selection => sub { $self->imagelist->create_resized(@_) },
413                choices =>
414                [
415                    map { gettext $_ } ( "Use original", "Use resized original" ) 
416                ],
417                frame_callback => sub { 
418                    my( $dlg, $ctrl, $event ) = @_;
419                    $dlg->OnCreateResized($event);
420                },
421            },
422            $main::SITE_IMG_WIDTH => { label => gettext("Maximum width") },
423            $main::GS_SITE_IMG_WIDTH => {
424                value => sub { $self->imagelist->resize_w(@_) },
425                validator => Wx::Perl::TextValidator->new( '\d' ) 
426            },
427            $main::SITE_IMG_HEIGHT => { label => gettext("Maximum height") },
428            $main::GS_SITE_IMG_HEIGHT => {
429                value => sub { $self->imagelist->resize_h(@_) },
430                validator => Wx::Perl::TextValidator->new( '\d' ) 
431            },
432            $main::SITE_IMG_JPEG_QUALITY => { label => gettext("Site image jpeg quality") },
433            $main::GS_SITE_IMG_JPEG_QUALITY => {
434                value => sub { $self->imagelist->quality(@_) },
435                validator => Wx::Perl::TextValidator->new( '\d' ) 
436            },
437            $main::SITE_IMG_FILTER => { label => gettext("Site image filter") },
438            $main::GS_SITE_IMG_FILTER => {
439                string_selection =>  sub { $self->imagelist->filter(@_) },
440                choices =>
441                [
442                    qw/Point Box Triangle Hermite Hanning Hamming Blackman Gaussian Quadratic Cubic Catrom Mitchell Lanczos Bessel Sinc/ 
443                ],
444            },
445            $main::SITE_IMG_BLUR => { label => gettext("Site image blur") },
446            $main::GS_SITE_IMG_BLUR => {
447                value => sub { $self->imagelist->blur(@_) },
448                validator => Wx::Perl::TextValidator->new( '\d' ) 
449            },
450            $main::SITE_IMG_INTERLACE => { label => gettext("Site image interlace") },
451            $main::GS_SITE_IMG_INTERLACE => {
452                string_selection => sub { $self->imagelist->interlace(@_) },
453                choices =>
454                [
455                    qw/None Line Plane Partition JPEG GIF PNG/ 
456                ],
457            },
458            $main::GS_REMOVE_UPLOADED_FROM_SELECTION => {
459                label => gettext("Remove uploaded photo from selection"),
460                value => sub { $self->imagelist->remove_uploaded_from_selection(@_) },
461            },
462            $main::GS_HD_UPLOAD => {
463                choices  =>
464                [
465                    map { gettext $_ } @{wxTheApp->upload_hd}
466                ],
467                pre_process => sub { my ( $value ) = @_; wxTheApp->eng_upload_hd->{$value} },
468                string_selection => sub { $self->imagelist->upload_hd(@_) },
469                frame_callback => sub { 
470                    my( $self, $ctrl, $event ) = @_; 
471                    $self->OnHDUpload($event);
472                },
473            },
474            $main::HD_IMG_WIDTH => { label => gettext("Maximum width") },
475            $main::GS_HD_IMG_WIDTH => {
476                label => gettext("HD image width"),
477                value => sub { $self->imagelist->hd_w(@_) },
478                validator => Wx::Perl::TextValidator->new( '\d' ) 
479            },
480            $main::HD_IMG_HEIGHT => { label => gettext("Maximum height") },
481            $main::GS_HD_IMG_HEIGHT => {
482                label => gettext("HD image height"),
483                value => sub { $self->imagelist->hd_h(@_) },
484                validator => Wx::Perl::TextValidator->new( '\d' ) 
485            },
486            $main::HD_IMG_JPEG_QUALITY => { label => gettext("HD image jpeg quality") },
487            $main::GS_HD_IMG_JPEG_QUALITY => {
488                value => sub { $self->imagelist->hd_quality(@_) },
489                validator => Wx::Perl::TextValidator->new( '\d' ) 
490            },
491            $main::HD_IMG_FILTER => { label => gettext("HD image filter") },
492            $main::GS_HD_IMG_FILTER => {
493                string_selection =>  sub { $self->imagelist->hd_filter(@_) },
494                choices =>
495                [
496                    qw/Point Box Triangle Hermite Hanning Hamming Blackman Gaussian Quadratic Cubic Catrom Mitchell Lanczos Bessel Sinc/ 
497                ],
498            },
499            $main::HD_IMG_BLUR => { label => gettext("HD image blur") },
500            $main::GS_HD_IMG_BLUR => {
501                value => sub { $self->imagelist->hd_blur(@_) },
502                validator => Wx::Perl::TextValidator->new( '\d' ) 
503            },
504            $main::HD_IMG_INTERLACE => { label => gettext("HD image interlace") },
505            $main::GS_HD_IMG_INTERLACE => {
506                string_selection => sub { $self->imagelist->hd_interlace(@_) },
507                choices =>
508                [
509                    qw/None Line Plane Partition JPEG GIF PNG/ 
510                ],
511            },
512            $main::GS_WMARK_ACTIVATE => {
513              label  => gettext("Activate watermark"),
514              value  => sub { $self->imagelist->watermark_activate(@_) },
515              frame_callback => sub { 
516                  my( $self, $ctrl, $event ) = @_; 
517                  $self->OnWatermark($event);
518              },
519            },
520            $main::GS_WMARK_ACTIVATE_HD => {
521              label => gettext("Activate watermark on high definition"),
522              value => sub { $self->imagelist->watermark_activate_pwg_high(@_) },
523              frame_callback => sub { 
524                  my( $self, $ctrl, $event ) = @_; 
525                  $self->OnWatermark($event);
526              },
527            },
528            $main::WMARK_TEXT => { label  => gettext("Text") },
529            $main::GS_WMARK_TEXT => { 
530              value  => sub { $self->imagelist->watermark_text(@_) },
531            },
532            $main::WMARK_TEXT_SIZE => { label     => gettext("Text size") },
533            $main::GS_WMARK_TEXT_SIZE => {
534                value   => sub { $self->imagelist->watermark_text_size(@_) },
535            },
536            $main::WMARK_COLOR => { label     => gettext("Color") },
537            $main::GS_WMARK_COLOR => {
538                string_selection => sub { $self->imagelist->watermark_color(@_) },
539                choices   => [
540                               map { gettext $_ } @{wxTheApp->colors}
541                             ],
542                pre_process => sub { my ( $value ) = @_; wxTheApp->eng_colors->{$value} },
543            },
544            $main::WMARK_POSITION => { label     => gettext("Position") },
545            $main::GS_WMARK_POSITION => {
546                string_selection => sub { $self->imagelist->watermark_position(@_) },
547                choices  => [
548                               map { gettext $_ } @{wxTheApp->positions}
549                           ],
550                pre_process => sub { my ( $value ) = @_; wxTheApp->eng_positions->{$value} },
551            },
552            $main::WMARK_MARGIN_TOP => { label => gettext("Top margin") },
553            $main::GS_WMARK_MARGIN_TOP => {
554                value   => sub { $self->imagelist->watermark_y(@_) },
555            },
556            $main::WMARK_MARGIN_LEFT => { label => gettext("Left margin") },
557            $main::GS_WMARK_MARGIN_LEFT => {
558                value   => sub { $self->imagelist->watermark_x(@_) },
559            },
560            $main::CHUNK_SIZE => { label => gettext("Transfert chunk size") },
561            $main::GS_CHUNK_SIZE => {
562                value   => sub { wxTheApp->chunk_size(@_) },
563            },
564            $main::LANGUAGE => { label => gettext("Choose a language") },
565            $main::GS_LANGUAGE => {
566                selection => sub { wxTheApp->current_language(@_) },
567                choices => [ map { sprintf($_->[0], gettext($_->[2])) } @{wxTheApp->available_languages} ],
568                pre_process => sub { my ( $value ) = @_; wxTheApp->available_languages->[$value][1] },
569                frame_callback => sub { my ( $self, $ctrl, $event ) = @_;
570                    Wx::LogMessage(
571                        sprintf(
572                            "%s : %s", 
573                            gettext("pLoader needs to be restarted to display the new selected language"),
574                            gettext(wxTheApp->available_languages->[$event->GetSelection][2])
575                        )
576                    );
577                    wxTheApp->layout_clean(1);
578                },
579            },
580        }   
581    );
582
583    $self->piwigo_property_list(
584        [
585            {
586                label => gettext("Photo caption"),
587            },
588            {
589                label => gettext("Comment"),
590            },
591            {
592                label => gettext("Author"),
593            },
594            {
595                label => gettext("File name"),
596            },
597            {
598                label => gettext("Create date"),
599            },
600        ]   
601    );
602
603    $self->exif_properties(
604        [
605            {
606                label    => gettext("Create date"),
607                value    => sub { $self->imagelist->current_image->create_date }, 
608                readonly => 1,
609            },   
610            {
611                label    => gettext("Model"),
612                value    => sub { $self->imagelist->current_image->exif_tag('Model') }, 
613                readonly => 1,
614            },   
615            {
616                label    => gettext("Width"),
617                value    => sub { $self->imagelist->current_image->exif_tag('ImageWidth') }, 
618                readonly => 1,
619            },   
620            {
621                label    => gettext("Height"),
622                value    => sub { $self->imagelist->current_image->exif_tag('ImageHeight') }, 
623                readonly => 1,
624            },   
625            {
626                label    => gettext("Orientation"),
627                value    => sub { $self->imagelist->current_image->exif_tag('Orientation') }, 
628                readonly => 1,
629            },   
630            {
631                label    => "ISO",
632                value    => sub { $self->imagelist->current_image->exif_tag('ISO') }, 
633                readonly => 1,
634            },   
635            {
636                label    => gettext("Shutter speed"),
637                value    => sub { $self->imagelist->current_image->exif_tag('ExposureTime') }, 
638                readonly => 1,
639            },   
640            {
641                label    => gettext("Aperture"),
642                value    => sub { $self->imagelist->current_image->exif_tag('ApertureValue') }, 
643                readonly => 1,
644            },   
645            {
646                label    => gettext("Focal length"),
647                value    => sub { $self->imagelist->current_image->exif_tag('FocalLength') }, 
648                readonly => 1,
649            },   
650            {
651                label    => gettext("Lens"),
652                value    => sub { $self->imagelist->current_image->exif_tag('Lens') }, 
653                readonly => 1,
654            },   
655        ]
656    );   
657
658    $self->image_tags(
659        sub { scalar @{$self->imagelist->image_selection} > 1 ? $self->imagelist->SetImageSelectionTags(@_) : $self->imagelist->current_image->site_tags(@_)||[]  }
660    );
661
662    $self->piwigo_tags(
663        sub { wxTheApp->pwg->tags }
664    );   
665}
666
667sub _create_piwigo_tag {
668    my ( $self, $name ) = @_;
669   
670    if(
671        Wx::MessageBox( 
672            sprintf(
673                "%s \"%s\" ?",
674                gettext("Do you want to create"), 
675                $name,
676            ),
677            gettext("Piwigo search information"),
678            wxYES_NO | wxICON_QUESTION, 
679        ) == wxYES
680    ){   
681        $self->pwg->AddTags($name);
682        $self->pwg->RefreshTags;
683    }
684}
685
686sub _refreshFrame {
687    my ( $self ) = @_;
688   
689    eval { $self->_set_setting_properties; };
690    $self->_refresh_settings_panels_properties;
691    map {
692        $_->Refresh;
693    }
694    (
695        $self->image_prop_exif,
696        $self->global_settings_panel,
697        $self->piwigo_photo_properties_tags
698    );
699   
700}
701
702#
703sub _initFrame {
704    my ( $self ) = @_;
705   
706    $self->create_toolbar;
707
708    my $sizer_h = Wx::BoxSizer->new( wxHORIZONTAL );
709   
710
711
712    $self->imageviewer(
713        Uploader::GUI::wxImageListCtrl->new( 
714            { 
715                parentwnd => $self,
716                imagelist => $self->imagelist,
717                image_size => $self->imagelist->wx_thumb_size,
718                columns => $self->piwigo_property_list,
719            }
720        )
721    );
722
723    # message displayed in the image listctrl when empty
724    $self->imageviewer->InitEmptyMsg;
725
726    $self->init_panels;
727
728    $self->dlg_piwigo_photo_properties (
729        Uploader::GUI::wxPhotoProperties->new(
730            {
731                parentwnd       => $self,
732                properties      => $self->piwigo_photo_properties,
733                tags            =>
734                {
735                    id => $main::PHOTO_PROPERTIES_TAG,
736                    choices => $self->piwigo_tags,
737                    selection => $self->image_tags,
738                    creation_callback => sub { $self->_create_piwigo_tag(@_) },
739               },
740            }
741        )
742    );
743
744    $self->piwigo_photo_properties_tags(
745        $self->dlg_piwigo_photo_properties->FindWindow($main::PHOTO_PROPERTIES_TAG)
746    );
747
748    $self->create_tree(
749        $self->dlg_piwigo_photo_properties->FindWindow($main::PHOTO_PROPERTIES_CATEGORIES)
750    );
751
752    $self->init_empty_category_msg;
753    $self->set_empty_tree_panel_evt_paint;
754    $self->show_pwg_categories_empty_msg;
755
756    $self->init_dnd_targets;
757
758    if( $self->imagelist->wx_thumb_imglist->GetImageCount){
759        $self->ShowImageViewer;
760       
761        $self->imageviewer->Refresh(
762            $self->imagelist->wx_thumb_imglist
763        );
764       
765        $self->OnUpdateImageviewerNotEmpty;
766    }
767    else{
768        $self->OnUpdateImageviewerEmpty;
769    }
770
771    # the imageviewer has a stretch factor of 1 : expands its size on frame resize
772    $sizer_h->Add( $self->imageviewer, 1, wxEXPAND|wxALL, 2 );
773    $sizer_h->Add( $self->dlg_piwigo_photo_properties, 0, wxEXPAND|wxALL, 2 );
774
775
776    $self->SetSizer(
777        $sizer_h
778    );
779    my $new_size = $sizer_h->Fit(
780        $self
781    );
782    $self->SetMinSize([600, 660]);
783    $self->Center;
784}
785
786sub OnUpdateImageviewerEmpty {
787    my ( $self ) = @_;
788
789    $self->dlg_piwigo_photo_properties->{_properties_notebook}->Enable(0);
790    $self->imageviewer->OnEmpty;
791}
792
793sub OnUpdateImageviewerNotEmpty {
794    my ( $self ) = @_;
795
796    $self->dlg_piwigo_photo_properties->{_properties_notebook}->Enable(1);
797    $self->imageviewer->OnNotEmpty;
798}
799
800
801sub _refresh_settings_panels_properties {
802    my ( $self ) = @_;   
803
804    $self->dlg_piwigo_photo_properties->properties(
805        $self->piwigo_photo_properties
806    );
807
808   
809    $self->image_prop_exif->properties(
810        $self->exif_properties
811    );
812
813    $self->global_settings_panel->properties(
814        $self->global_settings
815    );   
816
817}
818
819sub _init_settings_panels {
820    my ( $self ) = @_;   
821
822
823    $self->image_prop_exif(
824        Uploader::GUI::wxPropertyListDlg->new( 
825            { 
826                parentwnd       => $self,
827                properties      => $self->exif_properties,
828                caption         => sprintf("%s - EXIF", gettext("Properties")),
829            }
830        )
831    );
832    $self->image_prop_exif->Hide;
833
834    $self->global_settings_panel(
835        Uploader::GUI::wxGlobalSettings->new( 
836            { 
837                parentwnd       => $self,
838                caption         => gettext("Global settings"),
839                properties      => $self->global_settings,
840            }
841        )
842    );
843    $self->global_settings_panel->Hide;
844
845
846
847    $self->image_preview(
848        Uploader::GUI::wxImagePreview->new(
849            { 
850                parentwnd    => $self,
851                caption      => gettext("Preview"),
852            }
853        )
854    );
855
856}
857
858sub init_panels {
859    my ( $self ) = @_;
860   
861    $self->_init_settings_panels;   
862}
863
864# HTML code for getting started dialog box
865
866
867sub init_dnd_targets {
868    my ( $self ) = @_;   
869
870    $self->imageviewer->SetDropTarget( 
871        DNDImageListDropTarget->new(
872            $self->imageviewer
873        ) 
874    );
875
876    $self->tree->SetDropTarget( 
877        DNDCategoryTreeDropTarget->new(
878            $self->tree
879        )
880    );
881   
882}
883
884
885
886sub OnPhotoProperties {
887    my ( $self ) = @_;
888
889    $self->image_prop_exif->Show(1);
890}
891
892sub OnPreview {
893    my ( $self ) = @_;
894
895    $self->image_preview->Show(1);
896
897
898
899}
900
901
902sub OnGlobalSettings {
903    my ( $self ) = @_;
904
905    $self->global_settings_panel->Show(1);
906}
907
908
909sub OnDefaultPhotoNameChanged {
910    my ( $self ) = @_;
911   
912    Wx::LogMessage("New default_photo_name %s", $self->imagelist->default_photo_name);   
913}
914
915sub OnGeneralSettingsClose {
916    my ( $self, $event ) = @_;
917
918    $self->global_settings_panel->Hide;
919}
920
921sub OnImageExifPropClose {
922    my ( $self, $event ) = @_;
923
924    $self->image_exif_prop->Hide;
925}
926
927sub create_tree {
928    my ( $self, $tree ) = @_;
929
930    my $images = [
931        map {
932            Wx::Bitmap->new( $_, wxBITMAP_TYPE_PNG )
933           
934        }
935        (
936          wxTheApp->resource_path('tree_pwg.png'),
937          wxTheApp->resource_path('tree_folder.png'),
938        )   
939    ];
940
941    $self->treeimglist( Wx::ImageList->new( 16, 16, 1 ) );
942    map {
943        $self->treeimglist->Add($_);
944    }
945    @$images;
946   
947    $self->tree( 
948        $tree || Wx::TreeCtrl->new( 
949           $self->horizontal_splitter, 
950           -1, 
951           wxDefaultPosition, 
952           wxDefaultSize, 
953           wxCLIP_CHILDREN|
954           wxTR_HAS_BUTTONS|
955           wxTR_EDIT_LABELS
956        ) 
957    );
958
959    $self->tree->SetImageList( $self->treeimglist );
960    if(wxTheApp->use_offline){
961        $self->tree->Enable(0);
962    }
963    else{
964        $self->populate_tree_categories ;
965    }
966
967    $self->tree ;
968}
969
970
971sub set_empty_tree_panel_evt_paint{
972    my ( $self ) = @_;
973    Wx::Event::EVT_PAINT( $self->tree, sub {
974            my ( $tree, $event ) = @_;
975            if(exists $tree->{_empty_panel} and ! exists $tree->{_empty_panel}{_paint}){
976                my ($w, $h) = $tree->GetSizeWH;
977                my ($x, $y) = $tree->{_empty_panel}->GetPositionXY;
978
979                $tree->{_empty_panel}{x} = $x if !exists $tree->{_empty_panel}{x} ;
980                $tree->{_empty_panel}{y} = $y if !exists $tree->{_empty_panel}{y};
981                my ($wp, $hp) = $tree->{_empty_panel}->GetSizeWH;
982                my $x_off = ($w-$wp)/2 > 0 ? ($w-$wp)/2 : 0;
983                my $y_off = ($h-$hp)/2 > 0 ? ($h-$hp)/2 : 0;
984                $tree->{_empty_panel}->Move(
985                    [
986                        $tree->{_empty_panel}{x}+$x_off,
987                        $tree->{_empty_panel}{y}+$y_off
988                    ]
989                );
990                $tree->{_empty_panel}{_paint} = 1;
991            }
992            $event->Skip;
993        }
994    ) if 1;
995
996}
997
998
999sub init_empty_category_msg {
1000
1001    my ( $self ) = @_;
1002    # add a panel
1003    my $empty_panel = Wx::Panel->new($self->tree, -1, wxDefaultPosition, wxDefaultSize);
1004    my( $item0 ) = Wx::BoxSizer->new( wxVERTICAL );
1005   
1006    my $fb = Wx::Font->new( 12, wxSWISS, wxNORMAL, wxBOLD );
1007
1008
1009    my( $item1 ) = Wx::Button->new(
1010        $empty_panel,
1011        $main::CATEGORIES_ADD,
1012        wxTheApp->branding->{'Add new category'},
1013        wxDefaultPosition,
1014        [-1,40],
1015        0
1016    );
1017    my $btfont = Wx::Font->new(12, wxSWISS, wxNORMAL, wxNORMAL );
1018    $item1->SetFont($btfont);
1019    $item0->AddWindow( $item1, 0, wxALIGN_CENTER|wxALL, 5 );
1020
1021
1022    $empty_panel->SetSizer( $item0 );
1023    $item0->SetSizeHints( $empty_panel );
1024
1025    $self->tree->{_empty_panel} = $empty_panel;
1026    $self->tree->{_empty_panel}->SetBackgroundColour(wxWHITE);
1027
1028    my ($wp, $hp) = $self->tree->{_empty_panel}->GetSizeWH;
1029
1030    $self->tree->SetMinSize( [20+$wp, -1 ]);
1031
1032}
1033
1034
1035sub pwg_categories_are_empty {
1036    my ( $self ) = @_;
1037
1038    scalar @{wxTheApp->pwg->categories} < 1 ? 1 : 0 ;
1039}
1040
1041
1042sub populate_tree_categories {
1043    my ( $self ) = @_;
1044
1045    $self->populate_tree(
1046        $self->tree,
1047        wxTheApp->pwg->categories,
1048    ) if defined wxTheApp->pwg ;
1049}
1050
1051
1052sub show_pwg_categories_empty_msg {
1053    my ( $self ) = @_;
1054
1055    if($self->pwg_categories_are_empty){
1056        $self->tree->{_empty_panel}->Show(1);
1057    }
1058    else{
1059        $self->tree->{_empty_panel}->Show(0);
1060    }
1061}
1062
1063
1064
1065# returns a valid itemData
1066sub itemData { Wx::TreeItemData->new( $_[0] ) }
1067
1068sub populate_tree {
1069    my ( $self, $tree, $tree_items ) = @_;
1070    my $root = shift @{$tree_items};
1071
1072    $self->tree_root(
1073        $tree->AddRoot( 
1074            $root->[0], 
1075            $root->[3], 
1076            $root->[4], 
1077            itemData( $root->[2] ) 
1078        )
1079    );
1080
1081  $self->populate_tree_helper( $tree, $self->tree_root, $tree_items );
1082 
1083  $tree->SelectItem( $self->tree_root );
1084  $tree->Expand( $self->tree_root );
1085}
1086
1087sub populate_tree_helper {
1088  my ( $self, $tree, $parent_id, $tree_items ) = @_;
1089
1090  my $id;
1091
1092  map {
1093      my $name = $_->[0];
1094
1095      $id = $tree->AppendItem( 
1096                                   $parent_id, 
1097                                   $name,
1098                                   defined($_->[3]) ? $_->[3] : 0, 
1099                                   defined($_->[4]) ? $_->[4] : 0, 
1100                                   itemData( $_->[2]) 
1101                              );
1102      $self->tree_item_default($id) if ! defined $self->tree_item_default;
1103      # current item has children
1104      if( ref( $_->[1] ) eq 'ARRAY' ) {
1105          $self->populate_tree_helper( $tree, $id, $_->[1] );
1106      } 
1107  }
1108  @{$tree_items};
1109
1110}
1111
1112sub _initMenus {
1113    my ( $self ) = @_ ;
1114
1115    $self->_tree_mnu;
1116    $self->_imageviewer_mnu;   
1117}
1118
1119
1120sub _tree_mnu {
1121    my ( $self ) = @_;   
1122
1123    my $ctx_mnu = Wx::Menu->new;
1124   
1125    map {
1126        $ctx_mnu->Append(
1127            @$_[0..2], wxITEM_NORMAL
1128        );
1129    }
1130    (
1131        # workaround : first item does not show bitmap
1132        [
1133            0, 
1134            "",
1135            "",
1136            wxTheApp->resource_path('mnu_folder_new.png'),
1137        ],
1138        [
1139            1+$ID_TREE_CTX_MENU, 
1140            wxTheApp->branding->{'Add new category'},
1141            sprintf(
1142                "%s %s %s %s", 
1143                gettext("Add a new"), 
1144                wxTheApp->branding->{category},
1145                gettext("to the currently selected"),
1146                wxTheApp->branding->{category},
1147            ),
1148            wxTheApp->resource_path('mnu_folder_new.png'),
1149        ],
1150        [
1151            2+$ID_TREE_CTX_MENU, 
1152            gettext("Refresh"),
1153            sprintf(
1154                "Refresh %s list.",
1155                wxTheApp->branding->{category},
1156            ),
1157            wxTheApp->resource_path('mnu_refresh.png'),
1158        ],
1159        [
1160            3+$ID_TREE_CTX_MENU, 
1161            gettext("Expand all"),
1162            sprintf(
1163                "Expand %s list.",
1164                wxTheApp->branding->{category},
1165            ),
1166            wxTheApp->resource_path('mnu_expandall.png'),
1167        ],
1168        [
1169            4+$ID_TREE_CTX_MENU, 
1170            gettext("Collapse all"),
1171            sprintf(
1172                "Collapse %s list.",
1173                wxTheApp->branding->{category},
1174            ),
1175            wxTheApp->resource_path('mnu_collapseall.png'),
1176        ],
1177    );
1178
1179    $ctx_mnu->Delete(0);
1180    $self->tree_mnu(
1181         $ctx_mnu
1182    );   
1183}   
1184
1185sub _imageviewer_mnu {
1186    my ( $self ) = @_;   
1187
1188    my $ctx_mnu = Wx::Menu->new;
1189   
1190    map {
1191        $ctx_mnu->Append(
1192            @$_[0..2]
1193        );
1194    }
1195    (
1196        # workaround : first item does not show bitmap
1197        [
1198            0, 
1199            "",
1200            "",
1201            wxTheApp->resource_path('mnu_properties.png'),
1202        ],
1203        [
1204            1+$ID_IMAGEVIEWER_CTX_MENU, 
1205            gettext("Properties"),
1206            gettext("Modify photo properties"),
1207            wxTheApp->resource_path('mnu_properties.png'),
1208        ],
1209        [
1210            2+$ID_IMAGEVIEWER_CTX_MENU, 
1211            gettext("Preview"),
1212            gettext("Display photo preview"),
1213            wxTheApp->resource_path('mnu_preview.png'),
1214        ],
1215    );
1216   
1217    $ctx_mnu->Delete(0);
1218    $self->imageviewer_mnu(
1219         $ctx_mnu
1220    );   
1221}
1222
1223sub _initEventHandlers {
1224    my ( $self ) = @_ ;
1225   
1226    Wx::Event::EVT_MENU( $self, 101, \&OnAddImages );
1227    Wx::Event::EVT_MENU( $self, 102, \&OnRemoveImages );
1228    Wx::Event::EVT_MENU( $self, 103, \&OnUploadImages );
1229    Wx::Event::EVT_MENU( $self, 104, \&OnGlobalSettings );
1230    Wx::Event::EVT_CHOICE( $self, 106, \&OnPhotoSelMode );
1231    Wx::Event::EVT_TREE_SEL_CHANGED( $self, $self->tree, \&OnTreeSelChanged );
1232    Wx::Event::EVT_TREE_ITEM_RIGHT_CLICK( $self, $self->tree, \&OnTreeItemRightClick );
1233    Wx::Event::EVT_TREE_END_LABEL_EDIT( $self, $self->tree, \&OnTreeEndLabelEdit );
1234
1235    Wx::Event::EVT_LIST_END_LABEL_EDIT( $self, $self->imageviewer, \&OnImageViewerEndLabelEdit );
1236    Wx::Event::EVT_LIST_ITEM_ACTIVATED( $self, $self->imageviewer, \&OnImageViewerItemActivated );
1237    Wx::Event::EVT_LIST_ITEM_SELECTED($self, $self->imageviewer, \&OnImageViewerItemSelected) ;
1238    Wx::Event::EVT_LIST_ITEM_DESELECTED($self, $self->imageviewer, \&OnImageViewerItemDeSelected) ;
1239    Wx::Event::EVT_LIST_ITEM_RIGHT_CLICK($self, $self->imageviewer, \&OnImageViewerItemRightClick) ;
1240
1241    Wx::Event::EVT_LIST_KEY_DOWN($self, $self->imageviewer, \&OnImageViewerKeyDown) ;
1242
1243    Wx::Event::EVT_CLOSE( $self, \&OnClose );
1244
1245    Wx::Event::EVT_MENU( $self, 1+$ID_TREE_CTX_MENU, \&OnAddCategories );
1246    Wx::Event::EVT_MENU( $self, 2+$ID_TREE_CTX_MENU, \&OnRefreshCategories );
1247    Wx::Event::EVT_MENU( $self, 3+$ID_TREE_CTX_MENU, \&OnExpandCategories );
1248    Wx::Event::EVT_MENU( $self, 4+$ID_TREE_CTX_MENU, \&OnCollapseCategories );
1249
1250    Wx::Event::EVT_MENU( $self, 1+$ID_IMAGEVIEWER_CTX_MENU, \&OnPhotoProperties );
1251    Wx::Event::EVT_MENU( $self, 2+$ID_IMAGEVIEWER_CTX_MENU, \&OnPreview );
1252
1253    Wx::Event::EVT_BUTTON( $self, $main::PHOTO_PROPERTIES_UPLOAD, \&OnUploadImages );
1254    Wx::Event::EVT_BUTTON( $self, $self->imageviewer->add_button->GetId, \&OnAddImages );
1255
1256    Wx::Event::EVT_BUTTON( $self, $main::CATEGORIES_ADD, \&OnAddCategories );
1257
1258    # only refresh when calling event is finished
1259    Wx::Event::EVT_IDLE(
1260        $self,
1261        sub {
1262            my ( $self, $event ) = @_;
1263
1264            if ( $self->image_preview_refresh and $self->image_preview->IsShown ){
1265                $self->set_preview_image; 
1266                $self->image_preview->Refresh;
1267                $self->image_preview_refresh(0);
1268            }
1269
1270            if($self->imageviewer_refresh){
1271                $self->imageviewer->Refresh;
1272                $self->imageviewer_refresh(0);
1273            }
1274
1275            if($self->imageviewer_item_refresh){
1276                # for batch mode
1277                map {
1278                    $self->imageviewer->ItemRefresh(
1279                        $_
1280                    )
1281                } @{$self->imageviewer->GetSelectedItems};
1282                $self->imageviewer_item_refresh(0);
1283            }
1284
1285            if(!scalar @{$self->imageviewer->GetSelectedItems} and $self->dlg_piwigo_photo_properties->{_properties_notebook}->IsEnabled){
1286                $self->dlg_piwigo_photo_properties->{_properties_notebook}->Enable(0);
1287            }
1288
1289            $self->OnUpdateToolbar;
1290            $event->Skip;
1291        }
1292    );
1293
1294}
1295
1296{
1297  my $prevdir;
1298  my $prevfile;
1299
1300  sub OnAddImages {
1301    my( $self, $event ) = @_;
1302    my $dialog = Wx::FileDialog->new
1303      ( $self, gettext("Select photos for upload"), $prevfile, $prevdir,
1304        sprintf("%s (*.JPG)|*.JPG|(*.jpg)|*.jpg|All(*.*)|*.*", gettext("JPEG files")),
1305        wxFD_OPEN|wxFD_MULTIPLE );
1306
1307    my $file_paths = [];
1308    if( $dialog->ShowModal != wxID_CANCEL ) {
1309        @$file_paths = $dialog->GetPaths;
1310        $self->SetNewFiles($file_paths) ;
1311    }
1312    $dialog->Destroy;
1313  }
1314}
1315
1316sub OnUpdateToolbar {
1317    my( $self ) = @_;
1318   
1319    if($self->global_settings_panel->IsShown){
1320        $self->toolbar->EnableTool(104, 0);
1321    }
1322    else{   
1323        $self->toolbar->EnableTool(104, 1);
1324    }
1325
1326    if($self->global_settings_panel->IsShown){
1327        $self->toolbar->EnableTool(104, 0);
1328    }
1329    else{   
1330        $self->toolbar->EnableTool(104, 1);
1331    }
1332
1333}
1334
1335
1336sub OnRemoveImages {
1337    my( $self, $event ) = @_;
1338
1339   
1340    $self->imagelist->RemoveImageSelection;
1341    $self->imageviewer->Refresh;   
1342
1343    if (!$self->imageviewer->GetItemCount){
1344        $self->image_preview->image(
1345            0
1346        ); 
1347        # have to reset
1348        $self->dlg_piwigo_photo_properties->ClearProperties;
1349        $self->piwigo_photo_properties_tags->ClearAllSelection;
1350        $self->imagelist->SetCurrentImage(-1);
1351        $self->OnUpdateImageviewerEmpty;
1352    }
1353    else{
1354        $self->OnUpdateImageviewerNotEmpty;
1355    }
1356
1357    if(!$self->multi_selection_mode){
1358        $self->OnUpdateSingleSelectionModeUI;
1359    }
1360
1361    $self->image_preview->Refresh;
1362    $self->image_prop_exif->Refresh;
1363}
1364
1365sub SetNewFiles {
1366    my ( $self, $file_paths ) = @_;
1367
1368    $self->ShowImageViewer();
1369   
1370    $self->progressdlg->Destroy if defined $self->progressdlg;
1371    $self->progressdlg( 
1372        Uploader::GUI::wxImageProcessingProgressDlg->new(
1373            { 
1374                title => gettext("Image processing progress information"),
1375                bt_label => gettext("Cancel image processing"), 
1376                bt_close_label => gettext("Close"),
1377                stop_processing => sub { $self->imagelist->stop_processing(1); Wx::Yield(); },
1378             }
1379        )       
1380    );
1381    $self->progressdlg->Show(1);
1382    Wx::Yield();
1383
1384   
1385    my $files = [
1386        map {
1387            # to make sure that unicode chars in filenames are supported
1388            {
1389                ANSIPathName => $^O =~ /MSWin32/ ? Win32::GetANSIPathName($_) : $_,
1390                PathName => $_,
1391            },
1392        }@$file_paths   
1393    ];
1394
1395    @$files = sort { $a->{PathName} cmp $b->{PathName} } @$files;   
1396
1397    $self->imagelist->SetNewFiles(
1398        $files
1399    );
1400
1401   
1402}
1403
1404sub OnTreeSelChanged {
1405    my( $self, $event ) = @_;
1406 
1407    my @items = $self->tree->GetSelections;
1408
1409    $self->imagelist->categories(
1410        [
1411            map {
1412                my $category = $self->tree->GetPlData( $_ );
1413                $category->{id} if $category != -1;
1414            }
1415            @items
1416        ]
1417    );
1418}
1419
1420sub OnTreeItemRightClick {
1421    my( $self, $event ) = @_;
1422
1423
1424    $self->PopupMenu($self->tree_mnu, wxDefaultPosition);
1425   
1426}
1427
1428sub OnTreeEndLabelEdit {
1429    my( $self, $event ) = @_;
1430
1431    my $label = $event->GetLabel;
1432   
1433    $label =~ s/^\s+$//;
1434
1435    if(defined($label) and !( "" eq $label )){
1436        $self->_SetLabel($event)
1437    }
1438    else{
1439        $event->Veto;
1440    }
1441}
1442
1443sub _SetLabel {
1444    my( $self, $event ) = @_;
1445   
1446    my $category = $self->tree->GetPlData($event->GetItem);
1447    my $category_id;
1448   
1449    $category_id = $category->{id} if 'HASH' eq ref($category) ;
1450    my $comment;
1451    my ( $success, $status_msg, $content ) = $self->pwg->SetInfoCategories( 
1452        $event->GetLabel, 
1453        $comment, 
1454        $category_id
1455    );
1456
1457    my $ok = 1;
1458   
1459    if(!$success){
1460        $ok = 0;
1461    }
1462
1463    if('fail' eq $content->{stat}){
1464        $ok = 0;
1465    }
1466
1467    # method call failed
1468    if(!$ok){
1469        $event->Veto;
1470        Wx::MessageBox( 
1471            sprintf(
1472                "%s %s", 
1473                gettext("Update failed : "),
1474                $status_msg
1475            ),
1476            gettext("Piwigo update error"),
1477            wxOK | wxICON_EXCLAMATION, 
1478        );
1479        Wx::LogMessage("%s\n\n%s", Dumper($content), gettext("This function is not available. A Piwigo upgrade may resolve this issue."));
1480    }
1481}
1482
1483sub OnImageViewerItemRightClick {
1484    my( $self, $event ) = @_;
1485
1486   
1487    $self->PopupMenu($self->imageviewer_mnu, wxDefaultPosition);
1488   
1489   
1490}
1491
1492sub OnExpandCategories {
1493    my ( $self, $event ) = @_;
1494
1495    my $parent_item = $self->tree->GetSelection;
1496    $self->tree->ExpandAllChildren($parent_item);
1497    $self->tree->EnsureVisible($parent_item);
1498}
1499
1500sub OnCollapseCategories {
1501    my ( $self, $event ) = @_;
1502
1503    my $parent_item = $self->tree->GetSelection;
1504    $self->tree->CollapseAllChildren($parent_item);
1505    $self->tree->Expand($parent_item) if -1 == $self->tree->GetPlData($parent_item);
1506}
1507
1508sub OnAddCategories {
1509    my ( $self, $event ) = @_;
1510
1511    my $parent_item = $self->tree->GetSelection;
1512
1513    my $category = $self->tree->GetPlData($parent_item);
1514    my $category_id;
1515   
1516    $category_id = $category->{id} if 'HASH' eq ref($category) ;
1517
1518    my $dialog = Wx::TextEntryDialog->new( 
1519        $self, 
1520        wxTheApp->branding->{'Category name'}, 
1521        wxTheApp->branding->{'Add new category'},
1522        wxTheApp->branding->{'New category'}, 
1523    );
1524
1525    if( $dialog->ShowModal != wxID_CANCEL ) {
1526        my $name = $dialog->GetValue;
1527        my ( $success, $status_msg, $content ) = $self->pwg->AddCategories( $name, $category_id);
1528
1529        if($success){
1530            $self->tree->{_empty_panel}->Show(0);
1531            $self->_append_category($parent_item, $name, $content->{result}{id});
1532        }
1533    }
1534
1535
1536    $dialog->Destroy;
1537}
1538
1539sub OnRefreshCategories {
1540    my ( $self, $event ) = @_;
1541
1542    $self->_refresh_all_categories_helper;
1543}
1544
1545
1546sub _refresh_all_categories_helper {
1547    my ( $self ) = @_;   
1548
1549    my $busycursor = Wx::BusyCursor->new();
1550    $self->tree->CollapseAll;
1551    $self->tree->DeleteAllItems;
1552    $self->imagelist->categories([]);
1553    $self->pwg->RefreshCategories();
1554    $self->populate_tree_categories;
1555    $self->show_pwg_categories_empty_msg;
1556}
1557
1558sub _append_category {
1559    my ( $self, $parent_id, $name, $id ) = @_;
1560
1561    $self->tree->SelectItem(
1562        $self->tree->AppendItem(
1563            $parent_id, 
1564            $name, 
1565            1, 
1566            -1, 
1567            Wx::TreeItemData->new( { id => $id } )
1568        )
1569    );
1570}
1571
1572sub OnImageViewerEndLabelEdit {
1573    my( $self, $event ) = @_;
1574 
1575    my $image = $self->imagelist->GetImage($event->GetIndex);
1576    $image->site_name(
1577        $event->GetLabel
1578    );
1579       
1580    $self->dlg_piwigo_photo_properties->SetProperties;
1581}
1582
1583sub OnImageViewerItemActivated {
1584    my( $self, $event ) = @_;
1585   
1586    $self->current_imageviewer_index(
1587        $event->GetIndex
1588    );
1589
1590    $self->OnPhotoProperties;
1591}
1592
1593
1594sub OnImageViewerItemSelected {
1595    my( $self, $event ) = @_;
1596
1597    my $bc = Wx::BusyCursor->new;
1598    my $indx = $event->GetIndex;
1599
1600    $self->dlg_piwigo_photo_properties->{_properties_notebook}->Enable(1) 
1601        if !$self->dlg_piwigo_photo_properties->{_properties_notebook}->IsEnabled;
1602    $self->_on_imageviewer_item_selected($indx);
1603
1604    $event->Skip;
1605
1606}
1607
1608sub OnImageViewerItemDeSelected {
1609    my( $self, $event ) = @_;
1610
1611    my $bc = Wx::BusyCursor->new;
1612    $self->_on_imageviewer_item_selection_changed;
1613
1614    $event->Skip;
1615
1616}
1617
1618sub _on_imageviewer_item_selected {
1619    my ( $self, $index ) = @_;   
1620    $self->current_imageviewer_index($index);
1621    $self->imagelist->SetCurrentImage($index);
1622   
1623    $self->_on_imageviewer_item_selection_changed;
1624}
1625
1626sub _on_imageviewer_item_selection_changed {
1627    my ( $self ) = @_;   
1628
1629    $self->imagelist->image_selection(
1630        $self->imageviewer->GetSelectedItems
1631    );
1632
1633    # for batch mode : reset the batch buffer if single selection
1634    if($self->multi_selection_mode){
1635        $self->OnUpdateMultiSelectionModeUI;
1636        $self->imagelist->SetImageSelectionTags([]);
1637        $self->imagelist->image_selection_privacy_level(-1);
1638        $self->imagelist->image_selection_name("");
1639        $self->imagelist->image_selection_author("");
1640        $self->imagelist->image_selection_comment("");
1641        $self->imagelist->image_selection_create_date(-1);
1642    }
1643    else{
1644        $self->OnUpdateSingleSelectionModeUI;
1645    }
1646    # process image_preview in idle time
1647    # and when current event is processed
1648    # see call to EVT_IDLE
1649    $self->image_preview_refresh(1);
1650
1651    $self->dlg_piwigo_photo_properties->SetProperties if defined $self->dlg_piwigo_photo_properties;
1652    $self->image_prop_exif->Refresh;
1653    $self->piwigo_photo_properties_tags->Refresh;
1654
1655}
1656
1657sub multi_selection_mode {
1658    my ( $self ) = @_;
1659
1660    scalar @{$self->imagelist->image_selection} > 1;
1661}
1662
1663sub OnUpdateMultiSelectionModeUI{
1664    my( $self ) = @_;
1665
1666
1667    $self->dlg_piwigo_photo_properties->{_multi_selection_mode_panel}{_text1}->SetLabel(
1668        sprintf(gettext("You have selected a batch of %s photos"), $self->imageviewer->GetSelectectItemsCount )
1669    );
1670
1671    $self->dlg_piwigo_photo_properties->{_multi_selection_mode_panel}{_text2}->SetLabel(
1672        gettext("Changes apply to the whole batch")
1673    );
1674
1675    $self->dlg_piwigo_photo_properties->{_multi_selection_mode_panel}->Show(1);
1676    $self->dlg_piwigo_photo_properties->Layout;
1677}
1678
1679sub OnUpdateSingleSelectionModeUI{
1680    my( $self ) = @_;
1681
1682    $self->dlg_piwigo_photo_properties->{_multi_selection_mode_panel}->Show(0);
1683    $self->dlg_piwigo_photo_properties->Layout;
1684
1685}
1686
1687
1688sub set_preview_image {
1689    my ( $self ) = @_;   
1690
1691    my $current_image = $self->imagelist->current_image;
1692    my $image = Wx::Image->new;
1693    $image->LoadFile(
1694        $current_image->file, 
1695        wxBITMAP_TYPE_ANY
1696    );
1697 
1698    if($self->imagelist->auto_rotate){
1699        # exif from original image
1700        my $orientation = $current_image->exif_metadata->{Orientation};
1701
1702        # Valid for Rotate 180, Rotate 90 CW, Rotate 270 CW
1703        if( $orientation =~ m/Rotate (\d+)/ ){
1704            for(my $i=0; $i < floor($1/90) ; $i++){
1705                $image = $image->Rotate90;
1706            }               
1707        }
1708    }
1709
1710    $self->image_preview->image_size(
1711        [$image->GetWidth, $image->GetHeight, ]
1712    );       
1713
1714    $self->image_preview->image(
1715        $image
1716    );       
1717}
1718
1719sub OnImageViewerKeyDown {
1720    my( $self, $event ) = @_;
1721
1722    if(WXK_DELETE == $event->GetKeyCode){
1723        $self->OnRemoveImages();
1724       
1725        my $index = $self->current_imageviewer_index < $self->imageviewer->GetItemCount ?
1726            $self->current_imageviewer_index : $self->imageviewer->GetItemCount -1 ;
1727        $self->imageviewer->SelectItem(
1728            $index
1729        );
1730        $self->imageviewer->EnsureVisible(
1731            $index
1732        );
1733    }   
1734
1735}
1736
1737sub OnUploadImages {
1738    my( $self, $event ) = @_;
1739
1740    eval {
1741        $self->ProcessImageSelection();   
1742    };
1743}
1744
1745# remove image from imagelist when uploaded
1746sub UploadImagesViewerRefresh {
1747    my ( $self ) = @_;   
1748
1749
1750    $self->imageviewer->Refresh;
1751
1752    if(!$self->imageviewer->GetItemCount){
1753        $self->image_preview->image(0);
1754        $self->OnUpdateImageviewerEmpty;
1755    }
1756    # reset previous selection
1757    $self->imagelist->image_selection(
1758        []
1759    );
1760
1761    if(!$self->multi_selection_mode){
1762        $self->OnUpdateSingleSelectionModeUI;
1763    }
1764
1765    $self->image_preview->Refresh;
1766    Wx::Yield();
1767}
1768
1769sub UploadProgressMessageRefresh {
1770    my ( $self, $msg ) = @_;   
1771
1772    Wx::Yield();
1773
1774    $self->upload_progressdlg->processing(
1775        $msg   
1776    );   
1777    $self->upload_progressdlg->LogProgress();
1778
1779    Wx::Yield();
1780}
1781
1782sub UploadProgressMessageDetailsRefresh {
1783    my ( $self, $msg ) = @_;   
1784
1785    Wx::Yield();
1786
1787    $self->upload_progressdlg->processing_details(
1788        $msg   
1789    );   
1790    $self->upload_progressdlg->LogProgress();
1791
1792    Wx::Yield();
1793}
1794
1795
1796sub UploadProgressThumbnailRefresh {
1797    my ( $self ) = @_;   
1798
1799    my $imagelist = $self->imagelist ;
1800
1801 
1802    $self->upload_progressdlg->image->SetBitmap(wxNullBitmap);
1803    $self->upload_progressdlg->image->SetBitmap(
1804        Wx::Bitmap->new( 
1805            $self->imagelist->current_image->wx_thumb_file, 
1806            $self->GetWxBitmapType($self->imagelist->type), 
1807        )
1808    );
1809
1810    Wx::Yield();
1811}
1812
1813sub UploadProgressBarRefresh {
1814    my ( $self, $value ) = @_;   
1815
1816    eval {
1817        $self->upload_progressdlg->progress(
1818            $value
1819        );
1820        $self->upload_progressdlg->LogProgress();
1821    };
1822    #croak gettext("Upload cancelled") if $@;
1823
1824    Wx::Yield();
1825}
1826
1827sub SetNewFilesDisplayEndInfo {
1828    my ( $self, $msg, $errors ) = @_;   
1829   
1830    if($errors){
1831        $self->progressdlg->DisplayEndInfo($msg);
1832    }
1833    else{
1834        $self->progressdlg->Show(0);
1835    }
1836}
1837
1838sub UploadDisplayEndInfo {
1839    my ( $self, $msg ) = @_;   
1840   
1841    my $imagelist = $self->imagelist ;
1842   
1843    $self->upload_progressdlg->DisplayEndInfo($msg);
1844}
1845
1846sub ShowImageViewer {
1847    my ( $self ) = @_;   
1848
1849    if(!$self->imageviewer->IsShown){
1850        $self->imageviewer->Show(1);
1851    }
1852}
1853
1854
1855sub ActivateImageViewer {
1856    my ( $self ) = @_;   
1857   
1858}
1859
1860
1861sub SetNewFilesViewerRefresh {
1862
1863    my ( $self ) = @_;   
1864
1865    my $wximagelist = $self->imagelist->wx_thumb_imglist;
1866    #print Dumper "SetNewFilesViewerRefresh", $self->imagelist->current_image;
1867    my $indx = $wximagelist->Add(
1868        Wx::Bitmap->new( 
1869            $self->imagelist->current_image->wx_thumb_file, 
1870            $self->GetWxBitmapType($self->imagelist->type), 
1871        )
1872    ) if defined $self->imagelist->current_image->wx_thumb_file;
1873    #print $self->imagelist->current_image->wx_thumb_file, " added with index ", $indx, "\n";   
1874   
1875    $self->imageviewer->Refresh(
1876        $wximagelist
1877    );
1878
1879    $self->OnUpdateImageviewerNotEmpty if $self->imageviewer->GetItemCount;
1880
1881    Wx::Yield();
1882}
1883
1884
1885
1886# prepare and upload image_selection
1887sub ProcessImageSelection {
1888    my ( $self ) = @_;
1889
1890    return if !scalar @{$self->imagelist->sums};
1891
1892    if( $self->destination_categories_are_empty ){
1893        Wx::MessageBox( 
1894            sprintf(
1895                "%s", 
1896                wxTheApp->branding->{'What is the destination category?'}
1897            ),
1898            gettext("Piwigo upload error"),
1899            wxOK | wxICON_EXCLAMATION, 
1900        );
1901
1902    }
1903    else {
1904        # all selected is implicit
1905        if(!scalar @{$self->imageviewer->GetSelectedItems}){
1906       
1907            $self->imagelist->image_selection(
1908                $self->imageviewer->GetAllItems
1909            );
1910        }
1911       
1912        return if( !defined $self->imagelist->image_selection );
1913        return if( !scalar @{$self->imagelist->image_selection} );
1914       
1915        $self->upload_progressdlg->Destroy if defined $self->upload_progressdlg;
1916        $self->upload_progressdlg(
1917            Uploader::GUI::wxImageProcessingProgressDlg->new(
1918                { 
1919                    title    => gettext("Image upload progress information"),
1920                    bt_label => gettext("Cancel upload"),
1921                    bt_close_label => gettext("Close"), 
1922                    stop_processing => sub { $self->imagelist->stop_processing(1); Wx::Yield(); }, 
1923                 }
1924            )       
1925        );
1926        # modeless dialog
1927        $self->upload_progressdlg->Show(1);
1928        Wx::Yield();
1929        eval {
1930            $self->imagelist->UploadSelection;
1931        };
1932        if($@){
1933            Wx::MessageBox( 
1934                sprintf(
1935                    gettext("Upload cancelled"),
1936                ),
1937                gettext("Piwigo upload information"),
1938                wxOK | wxICON_INFORMATION, 
1939            );
1940        }
1941    }
1942}
1943
1944
1945sub destination_categories_are_empty {
1946    my ( $self ) = @_;
1947
1948    !scalar @{$self->imagelist->categories};
1949}
1950
1951
1952sub SetNewFilesProgress {
1953    my ( $self ) = @_;
1954
1955    my $imagelist = $self->imagelist;
1956
1957    $self->progressdlg->processing(
1958        sprintf(
1959            $imagelist->progress_msg, 
1960            $imagelist->current_image->file,
1961        )
1962    );
1963
1964    eval {
1965        $self->progressdlg->image->SetSize([ $imagelist->wx_thumb_size, $imagelist->wx_thumb_size]);
1966        $self->progressdlg->image->SetBitmap(wxNullBitmap);
1967        $self->progressdlg->image->SetBitmap(
1968            Wx::Bitmap->new( 
1969                $imagelist->current_image->wx_thumb_file,
1970                $self->GetWxBitmapType( $imagelist->type )
1971            )
1972        );
1973        $self->progressdlg->progress(
1974            $imagelist->count * ( 100/scalar @{$imagelist->new_files} )
1975        );
1976        $self->progressdlg->LogProgress();
1977    };
1978    Wx::Yield();
1979}
1980
1981sub OnClose {
1982  my $self = shift;
1983
1984
1985    # Restaure previous log wnd
1986    Wx::Log::SetActiveTarget( $self->oldlogwnd );
1987
1988    # allways store
1989 
1990    wxTheApp->StoreConnectionProperties;
1991   
1992    $self->imagelist->Store;
1993    wxTheApp->login_dlg->Destroy;   
1994
1995   
1996    wxTheApp->imageviewerIndex(
1997        $self->current_imageviewer_index
1998    );
1999   
2000    my $frameLayout = {};
2001   
2002    ( $frameLayout->{pX}, $frameLayout->{pY}, $frameLayout->{W}, $frameLayout->{H} ) = ( $self->GetPositionXY, $self->GetSizeWH ) ; 
2003   
2004    wxTheApp->frameLayout(
2005        $frameLayout
2006    );
2007
2008    wxTheApp->StoreLayoutProperties;
2009
2010    #destroy hidden dialogs
2011    $self->global_settings_panel->Destroy;
2012    $self->image_preview->Destroy;
2013    $self->image_prop_exif->Destroy;
2014
2015    $self->progressdlg->Destroy if defined $self->progressdlg;
2016    $self->upload_progressdlg->Destroy if defined $self->upload_progressdlg;
2017
2018    $self->Destroy;
2019}
2020
2021
2022sub create_toolbar {
2023    my( $self ) = @_;
2024
2025    my $tb = Wx::ToolBar->new( $self, -1, wxDefaultPosition, [600, -1], wxTB_FLAT|wxTB_TEXT );
2026    $tb->SetToolBitmapSize( wxSIZE( 32, 32 ) );
2027    map {
2028        my $icon1 = Wx::Icon->new();
2029        eval {
2030            $icon1->LoadFile($_->[2], $_->[3]);
2031        };
2032        my $tb_icon1 = Wx::Bitmap->new( $icon1 );
2033
2034        my $icon2 = Wx::Icon->new();
2035        eval {
2036            $icon2->LoadFile($_->[5], $_->[3]);
2037        };
2038        my $tb_icon2 = Wx::Bitmap->new( $icon2 );
2039
2040
2041        $tb->AddTool( $_->[0], $_->[1], $tb_icon1, $tb_icon2, wxITEM_NORMAL, $_->[6] );
2042        $tb->EnableTool( $_->[0], $_->[4]);
2043    }
2044    (
2045        [
2046            101, 
2047            gettext("Add photos"), 
2048            wxTheApp->resource_path('tb_add.png'), 
2049            wxBITMAP_TYPE_PNG, 
2050            1, 
2051            wxTheApp->resource_path('tb_add.png'), 
2052            gettext("Add photos for resizing and uploading")
2053        ],
2054        [
2055            102, 
2056            gettext("Remove selected photos"), 
2057            wxTheApp->resource_path('tb_remove.png'), 
2058            wxBITMAP_TYPE_PNG, 
2059            1, 
2060            wxTheApp->resource_path('tb_remove.png'),
2061            gettext("Remove selected photos. Original files are not deleted ")
2062        ],
2063        [
2064            104, 
2065            gettext("Preferences"), 
2066            wxTheApp->resource_path('tb_settings.png'), 
2067            wxBITMAP_TYPE_PNG, 
2068            0, 
2069            wxTheApp->resource_path('tb_settings.png'),
2070            gettext("Change global settings.")
2071        ],
2072   
2073    );
2074   
2075    $tb->AddSeparator;
2076   
2077    $tb->AddControl(
2078        Wx::Choice->new(
2079        $tb,
2080            106,
2081            wxDefaultPosition,
2082            [300, -1],
2083            [],
2084        )
2085    );
2086    my $ch = $tb->FindWindow(106);
2087    $ch->SetToolTip(gettext("How photo selection is displayed"));
2088    map {
2089        $ch->Append(gettext($_), $_);
2090    }(
2091        "Thumbnail and caption",
2092        "Thumbnail",
2093        "Property list"
2094    );
2095   
2096    $ch->SetStringSelection(gettext($self->imagelist->display_mode));
2097    $tb->Realize;
2098
2099    $self->toolbar(
2100        $tb
2101    );
2102    $self->SetToolBar($tb);
2103
2104    return $tb;
2105}
2106
2107sub OnPhotoSelMode {
2108    my ( $self, $event )= @_;
2109   
2110    $self->imagelist->display_mode(
2111        $event->GetClientData
2112    );
2113
2114    $self->imageviewer->change_display_mode(1);
2115}
2116
2117sub _create_textctrl {
2118    my( $self, $parent, $text, $size ) = @_;
2119
2120    return Wx::TextCtrl->new( $parent, -1, $text, [0, 0], $size,
2121                              wxNO_BORDER | wxTE_MULTILINE | wxTE_READONLY  );
2122}
2123
2124sub create_textctrl {
2125    my( $self, $text, $size ) = @_;
2126
2127    return $self->_create_textctrl( $self, $text, $size );
2128}
2129
2130sub DESTROY {
2131    my( $self ) = @_;
2132
2133}
2134
2135
2136
21371;
2138
2139
2140
2141
2142
2143package DNDImageListDropTarget;
2144use Wx qw/wxTheApp/;
2145use base qw(Wx::FileDropTarget Class::Accessor::Fast);
2146
2147__PACKAGE__->mk_accessors( 
2148    qw/
2149          imageviewer
2150      /
2151);
2152
2153sub new {
2154  my $class = shift;
2155  my $imageviewer = shift;
2156  my $self = $class->SUPER::new( @_ );
2157
2158  $self->imageviewer($imageviewer);
2159
2160  return $self;
2161}
2162
2163sub OnDropFiles {
2164  my( $self, $x, $y, $files ) = @_;
2165
2166  wxTheApp->frame->SetNewFiles($files) ;
2167}
2168
2169
2170
2171
21721;
2173
2174
2175package DNDCategoryTreeDropTarget;
2176
2177use base qw(Wx::TextDropTarget Class::Accessor::Fast);
2178use Data::Dumper;
2179use Wx qw/
2180              wxDragNone
2181              wxDragCopy
2182              wxDragMove
2183              wxTheApp
2184         /;
2185
2186__PACKAGE__->mk_accessors( 
2187    qw/
2188          tree
2189      /
2190);
2191
2192sub new {
2193  my ( $class, $tree ) = @_;
2194  my $self = $class->SUPER::new();
2195
2196  $self->tree($tree);
2197
2198  return $self;
2199}
2200
2201
2202
2203sub OnDropText {
2204  my( $self, $x, $y, $textdata ) = @_;
2205
2206  # must be $VAR1 because $textdata is the result of Data::Dumper
2207  my $VAR1;
2208  eval $textdata;
2209  eval {
2210      if(scalar @$VAR1){
2211             my @items;
2212             if(scalar @items < 2) {
2213               my ($dropItem, $flag) = $self->tree->HitTest([$x, $y]);
2214                 push @items, $dropItem;
2215             }
2216             else {
2217                 @items = $self->tree->GetSelections;
2218             }
2219             
2220             # remove root item which is not a valid category
2221             @items = grep { $self->tree->GetPlData( $_ ) != -1 } @items;
2222             
2223             wxTheApp->frame->imagelist->categories(
2224                 [
2225                     map {
2226                         $self->tree->GetPlData( $_ )->{id};
2227                     }
2228                     @items
2229                 ]
2230             );
2231           
2232             wxTheApp->frame->imagelist->image_selection($VAR1);
2233             wxTheApp->frame->ProcessImageSelection ;
2234      }   
2235  };
2236}
2237
2238
22391;
Note: See TracBrowser for help on using the repository browser.