support panoramas
[booh] / bin / booh
index d09a0c6d8502ae1c9b864d556b2c85a7c8f54f13..9232874bd193f8e2229b03210e450f8e4f7bcf74 100755 (executable)
--- a/bin/booh
+++ b/bin/booh
@@ -537,6 +537,70 @@ from. There are approximately 25 frames per second in a video.
     }
 end
 
+def change_pano_amount(xmldir, attributes_prefix, value)
+    $modified = true
+    if value.nil?
+        xmldir.delete_attribute("#{attributes_prefix}pano-amount")
+    else
+        xmldir.add_attribute("#{attributes_prefix}pano-amount", value)
+    end
+end
+
+def ask_new_pano_amount(xmldir, attributes_prefix)
+    if xmldir
+        value = xmldir.attributes["#{attributes_prefix}pano-amount"]
+    else
+        value = ''
+    end
+
+    dialog = Gtk::Dialog.new(utf8(_("Specify panorama amount")),
+                             $main_window,
+                             Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
+                             [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK],
+                             [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL])
+
+    lbl = Gtk::Label.new
+    lbl.markup = utf8(
+_("Please specify the <b>panorama 'amount'</b> of the image, which indicates the width
+of this panorama image compared to other regular images. For example, if the panorama
+was taken out of four photos on one row, counting the necessary overlap, the width of
+this panorama image should probably be roughly three times the width of regular images.
+
+With this information, booh will be able to generate panorama thumbnails looking
+the right 'size'.
+"))
+    dialog.vbox.add(lbl)
+    dialog.vbox.add(Gtk::Alignment.new(0.5, 0.5, 0, 0).add(Gtk::HBox.new.add(rb_no = Gtk::RadioButton.new(utf8(_("none (not a panorama image)")))).
+                                                                         add(rb_yes = Gtk::RadioButton.new(rb_no, utf8(_("amount of: ")))).
+                                                                         add(spin = Gtk::SpinButton.new(1, 8, 0.1)).
+                                                                         add(Gtk::Label.new(utf8(_("times the width of other images"))))))
+    dialog.window_position = Gtk::Window::POS_MOUSE
+    dialog.show_all
+    if value
+        spin.value = value.to_f
+        rb_yes.active = true
+        spin.grab_focus
+    else
+        rb_no.active = true
+    end
+
+    dialog.run { |response|
+        if rb_no.active?
+            newval = nil
+        else
+            newval = spin.value.to_f
+        end
+        dialog.destroy
+        if response == Gtk::Dialog::RESPONSE_OK
+            $modified = true
+            msg 3, "changing panorama amount to #{newval}"
+            return { :old => value, :new => newval }
+        else
+            return nil
+        end
+    }
+end
+
 def change_whitebalance(xmlelem, attributes_prefix, value)
     $modified = true
     xmlelem.add_attribute("#{attributes_prefix}white-balance", value)
@@ -630,6 +694,7 @@ def gen_real_thumbnail(type, origfile, destfile, xmldir, size, img, infotype)
         push_mousecursor_wait
         gen_real_thumbnail_core(type, origfile, destfile, xmldir, size, infotype)
         gtk_thread_protect {
+            puts "destroyed: " + img.destroyed?.to_s
             img.set(destfile)
             $modified_pixbufs[destfile] = { :orig => img.pixbuf, :pixbuf => img.pixbuf, :angle_to_orig => 0 }
         }
@@ -744,6 +809,11 @@ def popup_thumbnail_menu(event, optionals, type, xmldir, attributes_prefix, poss
     end
     enhance.image = Gtk::Image.new("#{$FPATH}/images/stock-channels-16.png")
     enhance.signal_connect('activate') { distribute_multiple_call.call(:enhance) }
+    if type == 'image' && possible_actions[:can_panorama]
+        menu.append(panorama = Gtk::ImageMenuItem.new(utf8(_("Set as panorama"))))
+        panorama.image = Gtk::Image.new("#{$FPATH}/images/stock-images-16.png")
+        panorama.signal_connect('activate') { closures[:pano].call }
+    end
     if optionals.include?('delete')
         menu.append(               Gtk::SeparatorMenuItem.new)
         menu.append(cut_item     = Gtk::ImageMenuItem.new(Gtk::Stock::CUT))
@@ -894,6 +964,33 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption)
         end
     }
 
+    change_pano_amount_and_cleanup_real = Proc.new { |values|
+        perform_change_pano_amount_and_cleanup = Proc.new { |val|
+            change_pano_amount($xmldir.elements["*[@filename='#{filename}']"], '', val)
+        }
+        perform_change_pano_amount_and_cleanup.call(values[:new])
+        
+        save_undo(_("change panorama amount"),
+                  Proc.new {
+                      perform_change_pano_amount_and_cleanup.call(values[:old])
+                      textview.grab_focus
+                      autoscroll_if_needed($autotable_sw, img, textview)
+                      $notebook.set_page(1)
+                      Proc.new {
+                          perform_change_pano_amount_and_cleanup.call(values[:new])
+                          textview.grab_focus
+                          autoscroll_if_needed($autotable_sw, img, textview)
+                          $notebook.set_page(1)
+                      }
+                  })
+    }
+
+    change_pano_amount_and_cleanup = Proc.new {
+        if values = ask_new_pano_amount($xmldir.elements["*[@filename='#{filename}']"], '')
+            change_pano_amount_and_cleanup_real.call(values)
+        end
+    }
+
     whitebalance_and_cleanup = Proc.new {
         if values = ask_whitebalance(fullpath, thumbnail_img, img,
                                      $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '')
@@ -1036,7 +1133,8 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption)
     }
 
     $name2closures[filename] = { :rotate => rotate_and_cleanup, :enhance => enhance_and_cleanup, :delete => delete, :cut => cut,
-                                 :color_swap => color_swap_and_cleanup, :frame_offset => change_frame_offset_and_cleanup_real }
+                                 :color_swap => color_swap_and_cleanup, :frame_offset => change_frame_offset_and_cleanup_real,
+                                 :pano => change_pano_amount_and_cleanup }
 
     textview.signal_connect('key-press-event') { |w, event|
         propagate = true
@@ -1151,10 +1249,11 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption)
                 next_ = autotable.get_next_widget(vbox)
                 popup_thumbnail_menu(event, ['delete'], type, $xmldir.elements["*[@filename='#{filename}']"], '',
                                      { :can_left => x > 0, :can_right => next_ && autotable.get_current_pos(next_)[0] > x,
-                                       :can_up => y > 0, :can_down => y < autotable.get_max_y, :can_multiple => true },
+                                       :can_up => y > 0, :can_down => y < autotable.get_max_y, :can_multiple => true, :can_panorama => true },
                                      { :rotate => rotate_and_cleanup, :move => move, :color_swap => color_swap_and_cleanup, :enhance => enhance_and_cleanup,
                                        :frame_offset => change_frame_offset_and_cleanup, :delete => delete, :whitebalance => whitebalance_and_cleanup,
-                                       :cut => cut, :paste => paste, :view => proc { view_element(filename, { :delete => delete }) } })
+                                       :cut => cut, :paste => paste, :view => proc { view_element(filename, { :delete => delete }) },
+                                       :pano => change_pano_amount_and_cleanup })
             end
             $ignore_next_release = false
             $gesture_press = nil
@@ -1541,32 +1640,32 @@ def backend_wait_message(parent, msg, infopipe_path, mode)
                     elements += sizes
                 end
                 element_counter = 0
-                gtk_thread_protect { pb1_1.fraction = 0 }
+                gtk_thread_protect { puts "destroyed: " + pb1_1.destroyed?.to_s; pb1_1.fraction = 0 }
                 if mode != 'one dir scan'
                     newtext = utf8(full_src_dir_to_rel($1, $2))
                     newtext = '/' if newtext == ''
-                    gtk_thread_protect { pb1_2.text = newtext }
+                    gtk_thread_protect { puts "destroyed: " + pb1_2.destroyed?.to_s; pb1_2.text = newtext }
                     directories_counter += 1
-                    gtk_thread_protect { pb1_2.fraction = directories_counter / directories }
+                    gtk_thread_protect { puts "destroyed: " + pb1_2.destroyed?.to_s; pb1_2.fraction = directories_counter / directories }
                 end
             elsif line =~ /^processing element$/
                 element_counter += 1
-                gtk_thread_protect { pb1_1.fraction = element_counter / elements }
+                gtk_thread_protect { puts "destroyed: " + pb1_1.destroyed?.to_s; pb1_1.fraction = element_counter / elements }
             elsif line =~ /^processing size$/
                 element_counter += 1
-                gtk_thread_protect { pb1_1.fraction = element_counter / elements }
+                gtk_thread_protect { puts "destroyed: " + pb1_1.destroyed?.to_s; pb1_1.fraction = element_counter / elements }
             elsif line =~ /^finished processing sizes$/
-                gtk_thread_protect { pb1_1.fraction = 1 }
+                gtk_thread_protect { puts "destroyed: " + pb1_1.destroyed?.to_s; pb1_1.fraction = 1 }
             elsif line =~ /^creating index.html$/
-                gtk_thread_protect { pb1_2.text = utf8(_("finished")) }
-                gtk_thread_protect { pb1_1.fraction = pb1_2.fraction = 1 }
+                gtk_thread_protect { puts "destroyed: " + pb1_2.destroyed?.to_s; pb1_2.text = utf8(_("finished")) }
+                gtk_thread_protect { puts "destroyed: " + pb1_1.destroyed?.to_s; pb1_1.fraction = pb1_2.fraction = 1 }
                 directories_counter = 0
             elsif line =~ /^index.html: (.+)\|(.+)/
                 newtext = utf8(full_src_dir_to_rel($1, $2))
                 newtext = '/' if newtext == ''
-                gtk_thread_protect { pb2.text = newtext }
+                gtk_thread_protect { puts "destroyed: " + pb2.destroyed?.to_s; pb2.text = newtext }
                 directories_counter += 1
-                gtk_thread_protect { pb2.fraction = directories_counter / directories }
+                gtk_thread_protect { puts "destroyed: " + pb2.destroyed?.to_s; pb2.fraction = directories_counter / directories }
             end
         end
     }
@@ -1598,7 +1697,7 @@ def call_backend(cmd, waitmsg, mode, params)
         msg 2, cmd
         if pid = fork
             id, exitstatus = Process.waitpid2(pid)
-            gtk_thread_protect { w8.destroy }
+            gtk_thread_protect { puts "destroyed: " + w8.destroyed?.to_s; w8.destroy }
             if exitstatus == 0
                 if params[:successmsg]
                     gtk_thread_protect { show_popup($main_window, params[:successmsg], { :linkurl => params[:successmsg_linkurl] }) }
@@ -2336,7 +2435,7 @@ def new_album
             if File.directory?(from_utf8(src_nb_calculated_for)) && src_nb_calculated_for != '/'
                 if File.readable?(from_utf8(src_nb_calculated_for))
                     src_nb_thread = Thread.new {
-                        gtk_thread_protect { src_nb.set_markup(utf8(_("<span size='small'><i>processing...</i></span>"))) }
+                        gtk_thread_protect { puts "destroyed: " + src_nb.destroyed?.to_s; src_nb.set_markup(utf8(_("<span size='small'><i>processing...</i></span>"))) }
                         total = { 'image' => 0, 'video' => 0, nil => 0 }
                         `find '#{from_utf8(src_nb_calculated_for)}' -type d -follow`.each { |dir|
                             if File.basename(dir) =~ /^\./
@@ -2350,7 +2449,7 @@ def new_album
                                 end
                             end
                         }
-                        gtk_thread_protect { src_nb.set_markup(utf8(_("<span size='small'><i>%s images and %s videos</i></span>") % [ total['image'], total['video'] ])) }
+                        gtk_thread_protect { puts "destroyed: " + src_nb.destroyed?.to_s; src_nb.set_markup(utf8(_("<span size='small'><i>%s images and %s videos</i></span>") % [ total['image'], total['video'] ])) }
                         src_nb_thread = nil
                     }
                 else