diff -Nru mapivi-1.2.369/debian/changelog mapivi-1.2.378/debian/changelog --- mapivi-1.2.369/debian/changelog 2017-01-19 19:52:33.000000000 +0000 +++ mapivi-1.2.378/debian/changelog 2017-01-31 10:07:22.000000000 +0000 @@ -1,3 +1,39 @@ +mapivi (1.2.378-2dhor~xenial) xenial; urgency=medium + + * refactoring: + - included light_table_update_selection() in light_table_reorder() if called from $ltw + - used bind_window_resize() also for light table + * mini change: improved progress dialog output in clean fingerprints + * show_multiple_pics(): + - added overlay (F3) to context menu + - tried to get rid of warning if context menu is used and window is closed - without success + - added invisible button to close win + - added window resize binding to resize and center picture: bind_window_resize() + * find duplicates: + - restriced column width to reasonable values + - moved key bindings from window to listbox because there were interfering with the text entries + - added German translations + * Improved and updated Compare Folders feature (see menu Extra): + - added picture compare by fingerprint + - added options to show only in A, only in B, or pictures in both folders (A+B) with or without differences + - displaying pictures in available in both folders without differences was not available before + - added German translations + - replaced progress bar by progress window (comes with a Cancel button) + - increased window size + * added progress window to cleanFingerprints() + * activated new feature picDB in searchDupsNameSimple(): + - first search is a little slower than before, but the next is very very fast now! + * addToSearchDB(): no picDB update needed when picture was already in searchDB + * experimental feature for faster name duplicate search + - %picDB hash with picture names as keys + - $picDBneedsupdate set in all placed where searchDB is altered + - added some time measurements to searchDupsNameSimple() + - added function update_picDB() + * searchDupsNameSimpleLog(): user info when search starts + * removed sort when looping through fingerprints in searchDupsDigest() - should improve speed + + -- Dariusz Duma Tue, 31 Jan 2017 07:59:36 +0100 + mapivi (1.2.369-1dhor~xenial) xenial; urgency=medium * https://sourceforge.net/p/mapivi/code/369/log/?path= diff -Nru mapivi-1.2.369/debian/patches/binary mapivi-1.2.378/debian/patches/binary --- mapivi-1.2.369/debian/patches/binary 2016-04-23 08:40:29.000000000 +0000 +++ mapivi-1.2.378/debian/patches/binary 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ ---- /dev/null -+++ mapivi-1.1.329/mapivi -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+cd /usr/share/mapivi/ -+perl mapivi.pl || return 1 diff -Nru mapivi-1.2.369/debian/patches/series mapivi-1.2.378/debian/patches/series --- mapivi-1.2.369/debian/patches/series 2016-04-23 08:40:22.000000000 +0000 +++ mapivi-1.2.378/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -binary diff -Nru mapivi-1.2.369/docs/ToDo-List.txt mapivi-1.2.378/docs/ToDo-List.txt --- mapivi-1.2.369/docs/ToDo-List.txt 2017-01-07 12:03:21.000000000 +0000 +++ mapivi-1.2.378/docs/ToDo-List.txt 2017-01-26 17:59:13.000000000 +0000 @@ -91,6 +91,6 @@ o 2015-02 showNonJPEGS should be renamed to show_hidden_files and show the diff between the files in $actdir and the displayed files in listbox - 2015-02 showNonJPEGS/show_hidden_files should offer a dialog to display the files e.g. in a video player like vlc o 2015-02 showNonJPEGS/show_hidden_files add option to select video tool -o 2016-12 find picture duplicates using Digest::SHA (sha256): store hash value in DB or only temporary?; hash from whole file or only from raw picture data (ignoring meta data)? -0 2016-12 find picture duplicates using fingerprint: update imageFingerprint hash when pictures and folders are deleted, moved, copied, renamed!!! +- 2016-12 find picture duplicates using Digest::SHA (sha256): store hash value in DB or only temporary?; hash from whole file or only from raw picture data (ignoring meta data)? +- 2016-12 find picture duplicates using fingerprint: update imageFingerprint hash when pictures and folders are deleted, moved, copied, renamed!!! o 2016-12 column width of the thumbnail table: is there a way to have this setting saved when I shutdown and reopen Mapivi? (Alan Cummings) diff -Nru mapivi-1.2.369/languages/mapivi-lang-de mapivi-1.2.378/languages/mapivi-lang-de --- mapivi-1.2.369/languages/mapivi-lang-de 2017-01-13 14:23:37.000000000 +0000 +++ mapivi-1.2.378/languages/mapivi-lang-de 2017-01-24 17:24:35.000000000 +0000 @@ -409,7 +409,13 @@ "Folder \"%s\" is not valid!" => "Verzeichnis \"%s\" ist ungültig!", "Please choose two different folders!" => "Bitte unterschiedliche Verzeichnisse auswählen!", "Found %d unique pictures in A, %d unique pictures in B and %d matching pictures" => "%d Einzelbilder in A, %d Einzelbilder in B und %d übereinstimmende Bilder gefunden", -" (%d of them differ)" => " (%d davon sind unterschiedlich)", +"Only in A" => "Nur in A", +"Only in B" => "Nur in B", +"In both and different" => "In beiden und unterschiedlich", +"In both and same" => "In beiden und gleich", +" (%d of them are different, %s are the same)" => " (%d davon sind unterschiedlich, %s sind gleich)", +"Differences" => "Unterschiede", +"Choose folders to compare and press the \"%s\" button." => "Verzeichnisse wählen und den \"%s\" Knopf drücken.", "Database Statistics" => "Datenbankstatistik", "No changes since last call." => "Keine Änderungen seit dem letzten Aufruf.", "pictures with " => "Bilder mit ", @@ -741,4 +747,8 @@ "fingerprint" => "Fingerabdruck", "Ignore folder ..." => "Verzeichnis ignorieren ...", "selected" => "ausgewählt", +"Searching for name duplicates for %s ..." => "Suche nach Namensduplikaten von %s ...", +"Calc fingerprints ..." => "Fingerabdrücke berechnen ...", +"Show picture info in popup" => "Bildinfo in Popup anzeigen", +"Overlay picture info" => "Bildinfo überlagern", ); diff -Nru mapivi-1.2.369/mapivi mapivi-1.2.378/mapivi --- mapivi-1.2.369/mapivi 1970-01-01 00:00:00.000000000 +0000 +++ mapivi-1.2.378/mapivi 2017-01-31 07:03:32.000000000 +0000 @@ -0,0 +1,4 @@ +#! /bin/sh + +cd /usr/share/mapivi/ +perl mapivi.pl || return 1 diff -Nru mapivi-1.2.369/mapivi.pl mapivi-1.2.378/mapivi.pl --- mapivi-1.2.369/mapivi.pl 2017-01-19 11:00:39.000000000 +0000 +++ mapivi-1.2.378/mapivi.pl 2017-01-26 18:57:52.000000000 +0000 @@ -193,10 +193,10 @@ # get version and date from subversion (SVN) # this works only if you enable subversion's keyword substitution on your machine for this file: # svn propset svn:keywords "Rev Date" mapivi.pl -my @svnversion = split / /, '$Rev: 369 $'; +my @svnversion = split / /, '$Rev: 378 $'; my $svnrevision = ''; $svnrevision = "($svnversion[1])" if defined $svnversion[1]; -my @svndate = split / /, '$Date: 2017-01-19 11:00:39 +0000 (Thu, 19 Jan 2017) $'; +my @svndate = split / /, '$Date: 2017-01-26 18:57:52 +0000 (Thu, 26 Jan 2017) $'; my $version_date = '2012-03-23'; $version_date = $svndate[1] if defined $svndate[1]; $main::VERSION = $version; @@ -381,6 +381,10 @@ my %searchDB; # fingerprint of raw image data (without meta data) to find image duplicates even if they have different file names, file sizes or meta data my %imageFingerprint; +# (experimental!) hash to support fast file name searches, key = file name without path, value: array withall files (with path) for this name +my %picDB; +# this flag should be set when searchDB has been changed (add or removed pictures ...) +my $picDBneedsupdate = 1; # location hash, will be filled on demand and should only be accessed from function get_locations my %locations; # flag indicating that the %locations hash needs to be filled (updated) from the %searchDB @@ -903,6 +907,7 @@ "dirDiffComment" => 1, "dirDiffEXIF" => 1, "dirDiffIPTC" => 1, + "dirDiffFingerprint" => 1, "MailPicNoChange" => 0, "MailPicMaxLength"=> 800, "MailPicQuality" => 75, @@ -4996,6 +5001,8 @@ -highlightthickness => 0, )->pack(-fill =>'both', -expand => 1, -padx => 2, -pady => 2); $ltw->{canvas} = $ltw->{frame}->Subwidget('canvas'); + # init thumbnail picture list - needed to be used in callback in bind_window_resize() + @{$ltw->{canvas}->{thumb_list}} = (); # add menu $ltw->{menu} = $ltw->Menu; $ltw->configure(-menu => $ltw->{menu}); @@ -5039,7 +5046,7 @@ $file_menu->command(-label => lang('Export to file as ...'), -command => sub { light_table_save_to_file_as(); }); $file_menu->separator; $file_menu->command(-label => lang('Clear'), -command => sub { undef @{$ltw->{canvas}->{thumb_list}}; light_table_clear(); light_table_reset_collection();}); - $file_menu->command(-label => lang('Update'), -command => sub { light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); light_table_update_selection();}); + $file_menu->command(-label => lang('Update'), -command => sub { light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); }); $file_menu->command(-label => lang('Reload thumbnails'), -command => sub { light_table_reload(); }); $file_menu->separator; $file_menu->command(-label => lang('Close'), -command => sub { light_table_close(); }); @@ -5051,7 +5058,6 @@ $ltw->{unsafed_changes} = 1; # set dirty bit $ltw->Unbusy; light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); }); $sort_menu->command(-label => lang('File name (Z - A)'), -command => sub { @@ -5060,7 +5066,6 @@ $ltw->{unsafed_changes} = 1; # set dirty bit $ltw->Unbusy; light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); }); $sort_menu->separator; $sort_menu->command(-label => lang('Date (new first)'), @@ -5070,7 +5075,6 @@ $ltw->{unsafed_changes} = 1; # set dirty bit $ltw->Unbusy; light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); }); $sort_menu->command(-label => lang('Date (old first)'), -command => sub { @@ -5079,7 +5083,6 @@ $ltw->{unsafed_changes} = 1; # set dirty bit $ltw->Unbusy; light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); }); $sort_menu->separator; $sort_menu->command(-label => lang('Rating (high first)'), @@ -5089,7 +5092,6 @@ $ltw->{unsafed_changes} = 1; # set dirty bit $ltw->Unbusy; light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); }); $sort_menu->command(-label => lang('Rating (low first)'), -command => sub { @@ -5098,35 +5100,15 @@ $ltw->{unsafed_changes} = 1; # set dirty bit $ltw->Unbusy; light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); }); my $opt_menu = $ltw->{menu}->cascade(-label => lang("Options")); $ltw->{show_balloon} = 1; # todo: move to config hash $ltw->{show_status} = 1; # todo: move to config hash $opt_menu->checkbutton(-label => lang("Show picture info"), -variable => \$ltw->{show_balloon}, -command => sub { light_table_balloon();}); - # window resize event - $ltw->bind("" => sub { - # get canvas size - my $cw = $ltw->{canvas}->width; - my $ch = $ltw->{canvas}->height; - # compare with last size - if (defined $ltw->{LAST_CANVAS_WIDTH} and defined $ltw->{LAST_CANVAS_HEIGHT}) { - # if the canvas size didn't change we need no reorder - return if (($cw == $ltw->{LAST_CANVAS_WIDTH}) and ($ch == $ltw->{LAST_CANVAS_HEIGHT})); - } - # store new size - $ltw->{LAST_CANVAS_WIDTH} = $cw; - $ltw->{LAST_CANVAS_HEIGHT} = $ch; - # if there is a timer running cancel it - $ltw->{LAST_RESIZE_TIMER_MH}->cancel if ($ltw->{LAST_RESIZE_TIMER_MH}); - $ltw->{LAST_RESIZE_MH} = Tk::timeofday; - # after 200 msec we reorder the thumbnails according to the new geometry to give a preview - $ltw->{LAST_RESIZE_TIMER_MH} = $ltw->after(200, sub { - light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); - }); - }); + # window resize event: widget, timeout, callback, callback arguments (thumb_list must be initialized!) + bind_window_resize($ltw, 200, \&light_table_reorder, $ltw->{canvas}, $ltw->{canvas}->{thumb_list}); + my $context_menu = $ltw->Menu(-title => lang("Context Menu")); $ltw->bind('', sub { $context_menu->Popup(-popover => "cursor", -popanchor => "nw"); @@ -5323,9 +5305,9 @@ my $pw = progressWinInit($ltw, "Read collection from file"); foreach my $line (@lines) { if (progressWinCheck($pw)) { - $error .= "error: user canceled at number: $pic_number\n"; - last; - } + $error .= "error: user canceled at number: $pic_number\n"; + last; + } $i++; progressWinUpdate($pw, "read line ($i/".scalar @lines.") ...", $i, scalar @lines); chomp($line); # no newline @@ -5338,10 +5320,10 @@ $dpic =~ s!\\!\/!g; if (-f $dpic) { if (isInList($dpic, \@pics)) { - $double++; - $info .= "info: $dpic is already in list\n"; - } - else { + $double++; + $info .= "info: $dpic is already in list\n"; + } + else { push @pics, $dpic; } } @@ -5362,22 +5344,22 @@ $asktosearchduplicates = 0 if (($rc eq $yesall) or ($rc eq $noall)); $searchduplicates = 0 if ($rc eq $noall); $searchduplicates_tmp = 0 if (($rc eq $no) or ($rc eq $noall)); - } + } if ($searchduplicates_tmp) { - progressWinUpdate($pw, "search duplicate ($i/".scalar @lines.") ...", $i, scalar @lines); + progressWinUpdate($pw, "search duplicate ($i/".scalar @lines.") ...", $i, scalar @lines); # try to find duplicate and replace entry by duplicate - my @dups = searchDupsNameSimple($dpic); - # check if dupfile is available if not try next from list - foreach my $duppic (@dups) { - if (-f $duppic) { - $duplicate++; - print "read slideshow replacing $dpic with $duppic\n"; - $error .= " --> replaced by duplicate $duppic\n"; + my @dups = searchDupsNameSimple($dpic); + # check if dupfile is available if not try next from list + foreach my $duppic (@dups) { + if (-f $duppic) { + $duplicate++; + print "read slideshow replacing $dpic with $duppic\n"; + $error .= " --> replaced by duplicate $duppic\n"; push @pics, $duppic; last; } } - } + } } } else { $info .= "info: ignoring line $i:\"$line\"\n"; } @@ -5762,7 +5744,6 @@ } progressWinEnd($pw) if $pw; light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); $ltw->{canvas}->yviewMoveto(1); $ltw->{label} = scalar @{$ltw->{canvas}->{thumb_list}}.' pictures'; } @@ -5997,6 +5978,8 @@ $c->coords('THUMB_MH'.$dpic, $col*$dis+$c->{thumb_distance}, $row*$dis+$c->{thumb_distance}); $index++; } + # special treatment only if called with light box canvas + light_table_update_selection() if ((defined($ltw)) and (ref($ltw) eq 'Tk::Toplevel') and ($c eq $ltw->{canvas})); } ############################################################## @@ -6121,7 +6104,6 @@ $ltw->{unsafed_changes} = 1; } light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); } ############################################################## @@ -6313,7 +6295,6 @@ } $ltw->{unsafed_changes} = 1; # set dirty bit light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); } ############################################################## @@ -6359,7 +6340,6 @@ } $ltw->{unsafed_changes} = 1; # set dirty bit light_table_reorder($ltw->{canvas}, $ltw->{canvas}->{thumb_list}); - light_table_update_selection(); } ############################################################## @@ -7055,6 +7035,7 @@ print "addToSearchDB: ignoring $dpic\n" if $verbose; return; } + $picDBneedsupdate = 1 if (not exists $searchDB{$dpic}); # only for new pictures my ($com, $exif, $ctime, $mtime, $iptc, $urgency, $size, $x, $y, $keys, @keys, $pop, $flag); # $meta is returned at the end of the sub, # the SOF segment is needed for the latter call of getAllFileInfo @@ -17619,6 +17600,7 @@ copy_thumbnail($dpic, $origsdir, \$errors); if ($operation == MOVE) { delete $searchDB{$dpic}; + $picDBneedsupdate = 1; rename_slideshow_pic($dpic, $targetfile); deleteCachedPics($dpic); delete_thumbnail($dpic); @@ -17768,6 +17750,7 @@ rename_slideshow_pic($dpic, $tpic); $searchDB{$tpic} = $searchDB{$dpic}; # copy meta info in search database delete $searchDB{$dpic}; # delete meta info of moved pic in search database + $picDBneedsupdate = 1; } } progressWinEnd($pw); @@ -18212,6 +18195,7 @@ # correct the searchDB $searchDB{$ndpic} = $searchDB{$dpic}; # copy meta info in search database delete $searchDB{$dpic}; # delete meta info of renamed pic in search database + $picDBneedsupdate = 1; renameCachedPic($dpic, $ndpic); rename_slideshow_pic($dpic, $ndpic); foreach (@resellist) { $_ = $ndpic if ($_ eq $dpic); } @@ -18336,6 +18320,7 @@ # correct the searchDB - copy meta info in search database $searchDB{$new_bak_name} = $searchDB{$bpic}; delete $searchDB{$bpic}; + $picDBneedsupdate = 1; # change the displayed name if ($lb->info("exists", $new_bak_name)) { $lb->itemConfigure($new_bak_name, $lb->{thumbcol}, -text => getThumbCaption($new_bak_name)); @@ -18546,7 +18531,7 @@ # correct the searchDB $searchDB{$ndpic} = $searchDB{$dpic}; # copy meta info in search database delete $searchDB{$dpic}; # delete meta info of renamed pic in search database - + $picDBneedsupdate = 1; renameCachedPic($dpic, $ndpic); rename_slideshow_pic($dpic, $ndpic); $actpic = $ndpic if (($dpic eq $actpic) and (-f $ndpic)); @@ -19232,6 +19217,7 @@ $searchDB{$tpic} = $searchDB{$dpic}; $searchDB{$tpic}{odir} = dirname($dpic); delete $searchDB{$dpic}; + $picDBneedsupdate = 1; deleteCachedPics($dpic); $lb->delete('entry', $dpic) unless $all; # only if move was successfull, we also move the thumbnail @@ -19264,6 +19250,7 @@ else { $searchDB{$dpic} = $searchDB{$bakpic}; delete $searchDB{$bakpic}; + $picDBneedsupdate = 1; # rename thumbnail rename ("$bakthumb", "$thumb"); if ($lb->info("exists", $bakpic)) { @@ -20101,6 +20088,7 @@ if (@{$pic_list} < 1) {warn "pic list is empty"; return; } my $balloon_addon = "\n\n(Click on picture to close window; use PgUp and PgDown for next/previous picture)"; my $dpic = @{$pic_list}[$index]; + my $Xbut; my $pic = basename($dpic); my ($photo, $zoomFactor); my $canvasw = 0.8*$top->screenwidth; @@ -20108,7 +20096,7 @@ my $rc = load_zoom_pic($dpic, \$photo, \$zoomFactor, $canvasw, $canvash); return unless ($rc); # open window - my $win = $top->Toplevel(-bg => 'black'); + my $win = $top->Toplevel(); $win->{pic_list} = $pic_list; $win->{index} = $index; $win->{photo} = $photo; @@ -20144,7 +20132,7 @@ # the context menu my $menu = $win->Menu(-title => "Menu"); $win->{menu} = $menu; - $menu->checkbutton(-label => "Balloon popup info", + $menu->checkbutton(-label => lang("Show picture info in popup"), -variable => \$config{PicWinBalloon}, -command => sub { if ($config{PicWinBalloon}) { @@ -20153,6 +20141,9 @@ $balloon->detach($win->{canvas}); } }); + $menu->checkbutton(-label => lang("Overlay picture info"), + -variable => \$conf{show_micro_meta}{value}, + -command => sub { show_text_on_canvas($win->{canvas}, get_meta_micro(@{$win->{pic_list}}[$win->{index}]));}, -accelerator => ""); if (scalar(@{$pic_list}) > 1) { $menu->separator; $menu->command(-label => lang("Next picture"), -command => sub { next_prev_pic($win, 1); }, -accelerator => ""); @@ -20180,22 +20171,19 @@ # Warning: The Close menu entry must always be the last item to be called by $menu->invoke('last'); $menu->command(-label => lang('Close'), -command => sub { - $win->{menu}->unpost(); # close menu - $win->{slideshow} = 0; # stop slideshow - $win->grabRelease(); - $win->withdraw(); # close window - $win->{photo}->delete; # free photo memory - $win->{iconPhoto}->delete if $win->{iconPhoto}; - $win->destroy(); + $Xbut->invoke(); + #$win->{menu}->unpost(); # close menu + #$win->{slideshow} = 0; # stop slideshow + #$win->grabRelease(); + #$win->withdraw(); # close window + #$win->{photo}->delete; # free photo memory + #$win->{iconPhoto}->delete if $win->{iconPhoto}; + #$win->destroy(); }, -accelerator => ''); # mouse and button bindings $win->bind('', sub { $menu->Popup(-popover => 'cursor', -popanchor => 'nw'); } ); - $win->bind('', sub { $menu->invoke('last'); }); - $win->bind('', sub { $menu->invoke('last'); }); - $win->bind('', sub { $menu->invoke('last'); }); - # invoke $win->{but} when the window is closed by the window manager (x-button) - $win->protocol('WM_DELETE_WINDOW' => sub { $menu->invoke('last'); }); + $win->bind('', sub { $Xbut->invoke(); }); # key-desc,F03,toggle overlay information (EXIF, IPTC, ...) $win->bind('', sub { toggle(\$conf{show_micro_meta}{value}); show_text_on_canvas($win->{canvas}, get_meta_micro(@{$win->{pic_list}}[$win->{index}]));} ); # next picture keys @@ -20254,9 +20242,26 @@ }); } } -# key-desc,F11,toggle fullscreen mode when displaying picture in own window + # window resize event: widget, timeout, callback, callback arguments + bind_window_resize($win, 200, \&next_prev_pic, $win, 0); + # key-desc,F11,toggle fullscreen mode when displaying picture in own window $win->bind('', sub { fullscreen($win);}); $win->{canvas}->focusForce if (Exists($win->{canvas})); + # Close button not packed, we need just the callback + $Xbut = $win->Button(-text => 'Close', + -command => sub { + $menu->update(); + $menu->unpost(); # close menu + $win->update; + $menu->destroy(); + $win->{slideshow} = 0; # stop slideshow + $win->grabRelease(); + $win->withdraw(); # close window + $win->{photo}->delete; # free photo memory + $win->{iconPhoto}->delete if $win->{iconPhoto}; + $win->destroy(); }); + bind_exit_keys_to_button($win, $Xbut); + if (defined $start_slideshow and $start_slideshow == SHOW) { $win->{slideshow} = 1; slideshow_pic($win); @@ -20265,6 +20270,56 @@ } ############################################################## +# execute a callback when window was resized +############################################################## +sub bind_window_resize { + my $win = shift; # window widget / toplevel + my $timeout = shift; # in ms + my $callback = shift; # callback function, e.g. \&reload + my @callbackargs = @_; # optional arguments for callback function + #print "bind_window_resize callbakargs:"; print $_ foreach (@callbackargs); print "\n"; + # check arguments + if ((not defined $win) or (ref($win) ne 'Tk::Toplevel')) { + warn "bind_window_resize called with invalid window reference."; + return 0; + } + if ((not defined $timeout) or ($timeout < 50) or ($timeout > 2000)) { + warn "bind_window_resize called with invalid timeout, should be between 50 and 2000ms."; + return 0; + } + if ((not defined $callback) or (ref($callback) ne 'CODE')) { + warn "bind_window_resize called with invalid callback function"; + return 0; + } + $win->bind('' => sub { + # get window size + #print "."; + my $cw = $win->width; + my $ch = $win->height; + #my $cw = $win->{canvas}->width; + #my $ch = $win->{canvas}->height; + # compare with last size + if (defined $win->{LAST_WINDOW_WIDTH} and defined $win->{LAST_WINDOW_HEIGHT}) { + # if the canvas size didn't change we need no reorder + return if (($cw == $win->{LAST_WINDOW_WIDTH}) and ($ch == $win->{LAST_WINDOW_HEIGHT})); + } + #print "${cw}x$ch\n"; + # store new size + $win->{LAST_WINDOW_WIDTH} = $cw; + $win->{LAST_WINDOW_HEIGHT} = $ch; + # if there is a timer running cancel it + $win->{LAST_RESIZE_TIMER_MH}->cancel if ($win->{LAST_RESIZE_TIMER_MH}); + $win->{LAST_RESIZE_MH} = Tk::timeofday; + # after 200 msec we reorder the thumbnails according to the new geometry to give a preview + $win->{LAST_RESIZE_TIMER_MH} = $win->after($timeout, sub { + # execute callback function + # print "bind_window_resize callback!!!!\n"; + &$callback(@callbackargs); + }); + }); +} + +############################################################## # slideshow_pic: slideshow all pics, to be used in sub show_multiple_pics() ############################################################## sub slideshow_pic { @@ -20290,9 +20345,9 @@ sub next_prev_pic { my $win = shift; my $step = shift; # int: e.g. 1 for next pic or -1 for previous pic, but 10 or 100 are also valid - return if ($step == 0); # no need to do anything + #return if ($step == 0); # no need to do anything -> reposition pic my $total_pics = scalar @{$win->{pic_list}}; - return if ($total_pics <= 1); + return if (($total_pics <= 1) and ($step != 0)); return unless $win; return unless $win->{canvas}; $win->{canvas}->Busy; # we can't use $win here else the cursor won't change @@ -20697,8 +20752,8 @@ $searchDB{$tpic} = $searchDB{$dpic}; $searchDB{$tpic}{odir} = dirname($dpic); delete $searchDB{$dpic}; + $picDBneedsupdate = 1; deleteCachedPics($dpic); - my $thumb = getThumbFileName($dpic); if ((-d "$trashdir/$thumbdirname") and (-f $thumb)) { if (!move($thumb, "$trashdir/$thumbdirname")) { @@ -22704,6 +22759,7 @@ else { # remove file from search database, if it exists delete $searchDB{$file}; + $picDBneedsupdate = 1; } return 1; } @@ -24741,17 +24797,23 @@ $ddw->withdraw; $ddw->title(lang("Compare folders")); $ddw->iconimage($mapiviicon) if $mapiviicon; + # some local config options + my $onlyA = 1; + my $onlyB = 1; + my $matchingSame = 0; + my $matchingDiff = 1; my $f1 = $ddw->Frame()->pack(-fill => 'x', -padx => 3, -pady => 3); my $f1a = $f1->Frame()->pack(-side => 'left', -fill => 'x', -expand => 1, -padx => 0, -pady => 0); my $f1b = $f1->Frame()->pack(-side => 'left', -fill => 'y', -padx => 0, -pady => 0); my $f2 = $ddw->Frame()->pack(-fill => 'x', -padx => 2, -pady => 3); my $f2a = $f2->Frame()->pack(-side => 'top', -fill => 'y', -expand => 0, -padx => 3, -pady => 3, -anchor => 'w'); + my $f2ab = $f2->Frame()->pack(-side => 'top', -fill => 'y', -expand => 0, -padx => 3, -pady => 3, -anchor => 'w'); my $f2b = $f2->Frame()->pack(-side => 'top', -fill => 'both', -expand => 1, -padx => 3, -pady => 3, -anchor => 'w'); my $ddlb; $ddw->Label(-textvariable => \$ddw->{label}, -anchor => 'w', -bg => $conf{color_bg}{value})->pack(-padx => 3, -anchor => 'w'); - $ddw->{label} = 'Choose folders to compare and press the "Compare" button.'; + $ddw->{label} = langf("Choose folders to compare and press the \"%s\" button.",lang("Compare")); my $dir_a_entry = labeledEntryButton($f1a,'top',15,lang("Folder")." A",'Set',\$config{dirDiffDirA},1); my $dir_b_entry = labeledEntryButton($f1a,'top',15,lang("Folder")." B",'Set',\$config{dirDiffDirB},1); @@ -24761,6 +24823,7 @@ $entry_menu->command(-label => "A: Use B folder", -command => sub { $config{dirDiffDirA} = $config{dirDiffDirB}; }); $entry_menu->command(-label => "B: Use current folder", -command => sub { $config{dirDiffDirB} = $actdir; }); $entry_menu->command(-label => "B: Use A folder", -command => sub { $config{dirDiffDirB} = $config{dirDiffDirA}; }); + $entry_menu->command(-label => "A+B: Use current folder", -command => sub { $config{dirDiffDirA} = $actdir; $config{dirDiffDirB} = $actdir; }); $ddlb = $ddw->Scrolled("HList", -header => 1, @@ -24770,7 +24833,7 @@ -scrollbars => 'osoe', -selectmode => "extended", -background => $conf{color_bg}{value}, #8fa8bf - -width => 40, + -width => 20, -height => 20, )->pack(-expand => 1, -fill => 'both'); # key-i opens IPTC data of both pictures @@ -24790,22 +24853,22 @@ showText($title, $iptc, NO_WAIT); } } - } ); + }); - $balloon->attach($ddlb, -msg => "left click : select\nmiddle click: Open picture in new window\nright click : open context menu\nkey i : show IPTC info"); + $balloon->attach($ddlb, -msg => "left click : select\nkey i : show IPTC info"); my $col = 0; - $ddlb->header('create', $col, -text => 'Differences', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Differences'), -headerbackground => $conf{color_entry}{value}); $ddlb->{diffcol} = $col; $col++; - $ddlb->header('create', $col, -text => 'Name', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Name'), -headerbackground => $conf{color_entry}{value}); $ddlb->{namecol} = $col; $col++; - $ddlb->header('create', $col, -text => 'Thumbnail A', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Thumbnail').' A', -headerbackground => $conf{color_entry}{value}); $ddlb->{thumbAcol} = $col; $col++; - $ddlb->header('create', $col, -text => 'Thumbnail B', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Thumbnail').' B', -headerbackground => $conf{color_entry}{value}); $ddlb->{thumbBcol} = $col; $col++; - $ddlb->header('create', $col, -text => 'Size A', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Size').' A', -headerbackground => $conf{color_entry}{value}); $ddlb->{sizeAcol} = $col; $col++; - $ddlb->header('create', $col, -text => 'Size B', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Size').' B', -headerbackground => $conf{color_entry}{value}); $ddlb->{sizeBcol} = $col; $col++; $ddlb->header('create', $col, -text => 'IPTC A', -headerbackground => $conf{color_entry}{value}); $ddlb->{iptcAcol} = $col; $col++; @@ -24815,13 +24878,11 @@ $ddlb->{exifAcol} = $col; $col++; $ddlb->header('create', $col, -text => 'EXIF B', -headerbackground => $conf{color_entry}{value}); $ddlb->{exifBcol} = $col; $col++; - $ddlb->header('create', $col, -text => 'Comments A', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Comments').' A', -headerbackground => $conf{color_entry}{value}); $ddlb->{comAcol} = $col; $col++; - $ddlb->header('create', $col, -text => 'Comments B', -headerbackground => $conf{color_entry}{value}); + $ddlb->header('create', $col, -text => lang('Comments').' B',, -headerbackground => $conf{color_entry}{value}); $ddlb->{comBcol} = $col; $col++; - my $progress = 0; - $f1b->Button(-image => $mapivi_icons{Preferences}, #-text => "Set", -command => sub { $entry_menu->Popup(-popover => 'cursor', -popanchor => 'nw'); @@ -24852,47 +24913,57 @@ $ddw->{label} = langf("Found %d unique pictures in A, %d unique pictures in B and %d matching pictures",scalar @onlyInDirA,scalar @onlyInDirB,scalar @intersec); $ddw->update; - my $pics = @onlyInDirA + @onlyInDirB + @intersec; + my $pics = 0; # this is an overapproximation, but better to much than to less + $pics += @onlyInDirA if ($onlyA); + $pics += @onlyInDirB if ($onlyB); + $pics += @intersec if ($matchingDiff or $matchingSame); my $last_time; my $i = 0; - foreach my $pic (sort @onlyInDirA) { - my $dpic = $config{dirDiffDirA}."/$pic"; - ddInsertPic($ddlb, $dpic, '', 'only in dir A'); - $i++; - # show progress and found pics every 0.5 seconds - idea from Slaven - if (!defined $last_time || Tk::timeofday()-$last_time > 0.5) { - $progress = int($i/$pics*100); $ddw->update; - $last_time = Tk::timeofday(); + my $pw = progressWinInit($top, "Show compare results"); + if ($onlyA) { + foreach my $pic (sort @onlyInDirA) { + last if progressWinCheck($pw); + progressWinUpdate($pw, "Only in A adding ($i/$pics) ...", $i, $pics); + my $dpic = $config{dirDiffDirA}."/$pic"; + ddInsertPic($ddlb, $dpic, '', 'only in dir A'); + $i++; } } - foreach my $pic (sort @onlyInDirB) { - my $dpic = $config{dirDiffDirB}."/$pic"; - ddInsertPic($ddlb, '', $dpic, 'only in dir B'); - $i++; - # show progress and found pics every 0.5 seconds - idea from Slaven - if (!defined $last_time || Tk::timeofday()-$last_time > 0.5) { - $progress = int($i/$pics*100); $ddw->update; - $last_time = Tk::timeofday(); + if ($onlyB) { + foreach my $pic (sort @onlyInDirB) { + last if progressWinCheck($pw); + progressWinUpdate($pw, "Only in B adding ($i/$pics) ...", $i, $pics); + my $dpic = $config{dirDiffDirB}."/$pic"; + ddInsertPic($ddlb, '', $dpic, 'only in dir B'); + $i++; } } - my $inter = 0; - foreach my $pic (sort @intersec) { - my $dpicA = $config{dirDiffDirA}."/$pic"; - my $dpicB = $config{dirDiffDirB}."/$pic"; - my $differences = ''; - if (compareTwoPics($dpicA, $dpicB, \$differences)) { - ddInsertPic($ddlb, $dpicA, $dpicB, $differences); - $inter++; - } - $i++; - # show progress and found pics every 0.5 seconds - idea from Slaven - if (!defined $last_time || Tk::timeofday()-$last_time > 0.5) { - $progress = int($i/$pics*100); $ddw->update; - $last_time = Tk::timeofday(); + my $differ = 0; + my $same = 0; + if ($matchingDiff or $matchingSame) { + foreach my $pic (sort @intersec) { + last if progressWinCheck($pw); + progressWinUpdate($pw, "In both folders adding ($i/$pics) ...", $i, $pics); + my $dpicA = $config{dirDiffDirA}."/$pic"; + my $dpicB = $config{dirDiffDirB}."/$pic"; + my $differences = ''; + if (compareTwoPics($dpicA, $dpicB, \$differences)) { + $differ++; + if ($matchingDiff) { + ddInsertPic($ddlb, $dpicA, $dpicB, $differences); + } + } + else { + $same++; + if ($matchingSame) { + ddInsertPic($ddlb, $dpicA, $dpicB, $differences); + } + } + $i++; } } - $progress = 100; - $ddw->{label} .= langf(" (%d of them differ)",$inter); + progressWinEnd($pw); + $ddw->{label} .= langf(" (%d of them are different, %s are the same)",$differ, $same); $ddw->Unbusy; })->pack(-fill => 'y', -side => 'left'); @@ -24901,12 +24972,20 @@ $ddw->destroy; })->pack(-fill => 'y', -side => 'left'); - $f2a->Label(-text => lang("Compare by "), -anchor => 'w', -bg => $conf{color_bg}{value})->pack(-side => 'left', -padx => 3); - $f2a->Checkbutton(-variable => \$config{dirDiffSize}, -text => lang("File size"))->pack(-side => 'left'); - $f2a->Checkbutton(-variable => \$config{dirDiffPixel}, -text => lang("Number of pixels"))->pack(-side => 'left'); - $f2a->Checkbutton(-variable => \$config{dirDiffComment}, -text => lang("Comments"))->pack(-side => 'left'); - $f2a->Checkbutton(-variable => \$config{dirDiffEXIF}, -text => "EXIF")->pack(-side => 'left'); - $f2a->Checkbutton(-variable => \$config{dirDiffIPTC}, -text => "IPTC")->pack(-side => 'left'); + my $cmpL = $f2a->Label(-text => lang("Compare by "), -anchor => 'w', -bg => $conf{color_bg}{value})->pack(-side => 'left', -padx => 3); + $balloon->attach($cmpL, -msg => "Compare pictures found in both folders by"); + $f2a->Checkbutton(-variable => \$config{dirDiffSize}, -text => lang("File size"))->pack(-side => 'left', -padx => 3); + $f2a->Checkbutton(-variable => \$config{dirDiffPixel}, -text => lang("Number of pixels"))->pack(-side => 'left', -padx => 3); + $f2a->Checkbutton(-variable => \$config{dirDiffComment}, -text => lang("Comments"))->pack(-side => 'left', -padx => 3); + $f2a->Checkbutton(-variable => \$config{dirDiffEXIF}, -text => "EXIF")->pack(-side => 'left', -padx => 3); + $f2a->Checkbutton(-variable => \$config{dirDiffIPTC}, -text => "IPTC")->pack(-side => 'left', -padx => 3); + $f2a->Checkbutton(-variable => \$config{dirDiffFingerprint}, -text => "Fingerprint")->pack(-side => 'left'); + my $showL = $f2ab->Label(-text => lang("Show").' ', -anchor => 'w', -bg => $conf{color_bg}{value})->pack(-side => 'left', -padx => 3); + $balloon->attach($showL, -msg => "Display pictures found only in A or B or in both folders -\neither if they are the same or if they are different."); + $f2ab->Checkbutton(-variable => \$onlyA, -text => lang("Only in A"))->pack(-side => 'left', -padx => 3); + $f2ab->Checkbutton(-variable => \$onlyB, -text => lang("Only in B"))->pack(-side => 'left', -padx => 3); + $f2ab->Checkbutton(-variable => \$matchingDiff, -text => lang("In both and different"))->pack(-side => 'left', -padx => 3); + $f2ab->Checkbutton(-variable => \$matchingSame, -text => lang("In both and same"))->pack(-side => 'left', -padx => 3); $f2b->Button(-text => lang("Copy A->B"), -command => sub { @@ -24993,6 +25072,7 @@ # change the location info in the search database $searchDB{$tpic} = $searchDB{$dpic}; delete $searchDB{$dpic}; + $picDBneedsupdate = 1; deleteCachedPics($dpic); # todo move thumbnail? # todo deleting the entry is wrong, if picture exists in both dirs @@ -25024,6 +25104,7 @@ # change the location info in the search database $searchDB{$tpic} = $searchDB{$dpic}; delete $searchDB{$dpic}; + $picDBneedsupdate = 1; deleteCachedPics($dpic); # todo move thumbnail? # todo deleting the entry is wrong, if picture exists in both dirs @@ -25033,31 +25114,9 @@ $ddw->{label} = "ready! ($n/".scalar @sellist." deleted)"; $ddw->update; })->pack(-fill => 'x', -side => 'left', -padx => 2, -pady => 2); - my $progBar = - $f2b->ProgressBar(-takefocus => 0, - -borderwidth => 1, - -relief => 'sunken', - -length => 100, - -height => 5, - -padx => 0, - -pady => 0, - -variable => \$progress, - -colors => [0 => $config{ColorProgress}], - -resolution => 1, - -blocks => 10, - -anchor => 'w', - -from => 0, - -to => 100, - )->pack(-side => 'left', -expand => 1, -fill => 'y', -padx => 3, -pady => 3, -anchor => 'w'); - $balloon->attach($progBar, -msg => lang("Compare progress")); - - my $ws = 0.7; - my $w = int($ws * $ddw->screenwidth); - my $h = int($ws * $ddw->screenheight); - my $x = int(((1 - $ws) * $ddw->screenwidth)/3); - my $y = int(((1 - $ws) * $ddw->screenheight)/3); - #print "geo==${w}x${h}+${x}+${y}\n"; - $ddw->geometry("${w}x${h}+${x}+${y}"); + my $ww = int(0.8 * $top->screenwidth); + my $wh = $top->screenheight-20; + $ddw->geometry("${ww}x${wh}+10+20"); $ddw->Popup; $ddw->waitWindow; } @@ -25094,6 +25153,13 @@ $$diff .= "IPTC\n"; $rc = 1; } + if ($config{dirDiffFingerprint} and + exists($imageFingerprint{$dpicA}) and + exists($imageFingerprint{$dpicB}) and + ($imageFingerprint{$dpicA}{FIN} ne $imageFingerprint{$dpicB}{FIN})) { + $$diff .= "fingerprint\n"; + $rc = 1; + } if ($config{dirDiffPixel}) { my ($wa, $ha) = getSize($dpicA); my ($wb, $hb) = getSize($dpicB); @@ -25102,6 +25168,7 @@ $rc = 1; } } + $$diff .= '-' if ($rc == 0); return $rc; } @@ -25347,6 +25414,7 @@ # delete the picture from the database if it does not exists if (!-f $pic) { delete $searchDB{$pic}; + $picDBneedsupdate = 1; $pics .= "$pic\n"; $count++; } @@ -25385,6 +25453,7 @@ # delete the picture from the database if it does not exists if (($pic =~ m/^$directory/) and (!-f $pic)) { delete $searchDB{$pic}; + $picDBneedsupdate = 1; } } progressWinEnd($pw); @@ -25419,6 +25488,7 @@ } # delete the picture from the database delete $searchDB{$dpic}; + $picDBneedsupdate = 1; $deleted++; } } @@ -28130,6 +28200,7 @@ foreach my $item (reverse @sellist) { my $dpic = $listBox->get($item); delete $searchDB{$dpic}; # delete key from hash + $picDBneedsupdate = 1; $listBox->delete($item); } $keys = keys %searchDB; # display the new number of database entries @@ -28261,8 +28332,9 @@ my $lb = shift; my @sellist = getSelection($lb); return unless checkSelection($top, 1, 1, \@sellist, lang("picture(s)")); - my $search_dpic = $sellist[0]; + my $search_pic = basename($search_dpic); + log_it(langf("Searching for name duplicates for %s ...",$search_pic)); $top->update(); my @dups = searchDupsNameSimple($search_dpic); my $dupnr = scalar @dups; my $text = langf("Found %d duplicate(s) to %s",$dupnr,basename($search_dpic)); @@ -28289,17 +28361,48 @@ my $search_dpic = shift; my $search_pic = basename($search_dpic); my @dups; - #log_it("searching duplicates by file name ..."); # loop through all database entries - foreach my $dpic (sort keys %searchDB) { - my $pic = basename($dpic); - if ($pic eq $search_pic) { - if ($dpic ne $search_dpic) { - push @dups, $dpic; + #my $start = Tk::timeofday(); # reset timer + #foreach my $dpic (keys %searchDB) { + # my $pic = basename($dpic); + # if ($pic eq $search_pic) { + # if ($dpic ne $search_dpic) { + # push @dups, $dpic; + # } + # } + #} + update_picDB(); + if (exists $picDB{$search_pic}) { + foreach (@{$picDB{$search_pic}}) { + if ($_ ne $search_dpic) { + push @dups, $_; } } } - return @dups; + #my $stop = Tk::timeofday(); my $dur = sprintf("%.5f",$stop-$start); print "searchDupsNameSimple: duration loop searchDB:$dur\n"; $start = $stop; + return sort(@dups); +} + +############################################################## +# create / update picDB hash +# update only if needed, e.g. pic has been added to searchDB +# hint: picdb has often less enties than searchDB due to similar +# file names in different folders +############################################################## +sub update_picDB { + #print "keys picdb:".keys(%picDB)." keys searchdb:".keys(%searchDB)."\n"; + if ($picDBneedsupdate) { + undef %picDB; + foreach my $dpic (keys %searchDB) { + push @{$picDB{basename($dpic)}}, $dpic; + } + #print "picDB updated ---------------------------!\n"; + } + else { + #print "picDB is still up to date! ++++++\n"; + } + $picDBneedsupdate = 0; + return; } ############################################################## @@ -28362,11 +28465,10 @@ } my $i = 0; my $available = 0; - my $pw = progressWinInit($top, "Create fingerprints"); # first step: clean up imageFingerprint hash and remove non-exiting files - progressWinUpdate($pw, "Cleaning outdated fingerprints ...", 0, 1); my $cleaninfo = cleanFingerprints(); log_it($cleaninfo); + my $pw = progressWinInit($top, "Create fingerprints"); my $start = Tk::timeofday(); # reset timer foreach my $dir (@sel_dirs) { last if progressWinCheck($pw); @@ -28411,8 +28513,13 @@ sub cleanFingerprints { my $fingerprintsbefore = keys %imageFingerprint; my $deleted = 0; + my $pw = progressWinInit($top, "Cleaning fingerprints"); my $start = Tk::timeofday(); + my $i = 0; + my $max = scalar(keys(%imageFingerprint)); foreach my $dpic (keys %imageFingerprint) { + last if progressWinCheck($pw); + progressWinUpdate($pw, "Deleting $deleted outdated fingerprints ($i/$max) ...", $i++, $max); if (not -f $dpic) { delete $imageFingerprint{$dpic}; $deleted++; @@ -28431,6 +28538,7 @@ # end special cleanup ########### } + progressWinEnd($pw); my $cleanduration = sprintf("%.1f",Tk::timeofday() - $start); my $info = "Cleaned $fingerprintsbefore fingerprints in $cleanduration seconds: deleted $deleted outdated fingerprints."; return $info; @@ -28487,7 +28595,7 @@ undef %$dpics; #log_it("searching duplicates by file name ..."); # loop through all database entries - foreach my $dpic (sort keys %imageFingerprint) { + foreach my $dpic (keys %imageFingerprint) { next if (($ignore_filter ne '') and ($dpic =~ m!$ignore_filter!i)); next if ($ignore_links and -l $dpic); next if (!exists $imageFingerprint{$dpic}{FIN}); @@ -28700,6 +28808,11 @@ # reset global style $thumbS = $thumbS_save; $fileS = $fileS_save; $iptcS = $iptcS_save; $label = " found $pcount duplicates with $dcount files."; + # ToDo: Absolute value might not be the best solution .... + $duplb->columnWidth($duplb->{thumbcol}, 140); + $duplb->columnWidth($duplb->{filecol}, 260); + $duplb->columnWidth($duplb->{iptccol}, 300); + $duplb->columnWidth($duplb->{comcol}, 120); $duplb->update(); })->pack(-side => 'left', -anchor => 'w', -fill => 'both'); @@ -28731,7 +28844,7 @@ return unless checkSelection($dupw, 1, 0, \@sellist, lang("picture(s)")); show_multiple_pics(\@sellist, 0); }); - $dupw->bind('', sub { + $duplb->bind('', sub { my @sellist = getSelection($duplb); return unless checkSelection($dupw, 1, 0, \@sellist, lang("picture(s)")); show_multiple_pics(\@sellist, 0); @@ -28741,7 +28854,7 @@ ############# open in external viewer $menu->command(-label => lang('Open pictures in external viewer'), -command => sub { openPicInViewer($duplb); }, -accelerator => ''); - $dupw->bind('', sub { openPicInViewer($duplb); }); + $duplb->bind('', sub { openPicInViewer($duplb); }); ############# open dir @@ -28820,7 +28933,7 @@ $dupw->Unbusy; } ); bind_exit_keys_to_button($dupw, $Xbut); - $dupw->bind('', sub { open_pic_in_main($duplb); }); + $duplb->bind('', sub { open_pic_in_main($duplb); }); my $w = int(0.95 * $dupw->screenwidth); my $h = int(0.9 * $dupw->screenheight); $dupw->geometry("${w}x${h}+10+10"); @@ -29526,8 +29639,9 @@ -title => "Remove ".scalar @sellist." picture(s) from search database?", -type => 'OKCancel'); return if ($rc !~ m/Ok/i); foreach (@sellist) { - delete $searchDB{$_}; + delete $searchDB{$_}; } + $picDBneedsupdate = 1; }); ############# open pic