From fe737fd2efbdfa14a68cc2bf5435a0d073f62f80 Mon Sep 17 00:00:00 2001 From: gc Date: Tue, 25 Oct 2005 20:24:28 +0000 Subject: [PATCH] support panoramas --- bin/booh | 131 +++++++++++++++--- bin/booh-backend | 53 +++++-- data/booh/themes/dark/metadata/parameters.rb | 24 ++-- .../booh/themes/simple/metadata/parameters.rb | 22 +-- .../themes/simple/skeleton_thumbnails.html | 2 +- lib/booh/booh-lib.rb | 16 +++ 6 files changed, 196 insertions(+), 52 deletions(-) diff --git a/bin/booh b/bin/booh index d09a0c6..9232874 100755 --- 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 panorama 'amount' 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(_("processing..."))) } + gtk_thread_protect { puts "destroyed: " + src_nb.destroyed?.to_s; src_nb.set_markup(utf8(_("processing..."))) } 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(_("%s images and %s videos") % [ total['image'], total['video'] ])) } + gtk_thread_protect { puts "destroyed: " + src_nb.destroyed?.to_s; src_nb.set_markup(utf8(_("%s images and %s videos") % [ total['image'], total['video'] ])) } src_nb_thread = nil } else diff --git a/bin/booh-backend b/bin/booh-backend index a9b4b76..35ec7b4 100755 --- a/bin/booh-backend +++ b/bin/booh-backend @@ -406,24 +406,35 @@ end def reset_iterations(iterations) for iter in iterations.values - iter['value'] = 1 + iter['value'] = 0 end end -def run_iterations(iterations) +def run_iterations(iterations, amount) html = '' + should_rerun = false for level in iterations.keys.sort - if iterations[level]['value'] == 1 || level == iterations.keys.max + if iterations[level]['value'] == 0 html += iterations[level]['opening'] + elsif level == iterations.keys.max + if !iterations[level]['max'] || iterations[level]['max'] && iterations[level]['value'] + amount <= iterations[level]['max'].to_i + html += iterations[level]['opening'] + else + should_rerun = true + end end - iterations[level]['value'] += 1 + iterations[level]['value'] += amount if iterations[level]['max'] && iterations[level]['value'] > iterations[level]['max'].to_i - iterations[level]['value'] = 1 - iterations[level-1]['value'] = 1 + iterations[level]['value'] = 0 + iterations[level-1]['value'] = 0 html += iterations[level-1]['closing'] end end - return html + if should_rerun + return html + run_iterations(iterations, amount) + else + return html + end end def close_iterations(iterations) @@ -696,6 +707,8 @@ def walk_source_dir thumbnail_videos ||= {} thumbnail_videos[sizeobj['name']] = [] end + #- a special dummy size to keep 'references' to thumbnails in case of panorama, because the GUI will use the regular thumbnails + thumbnail_images['dont-delete-file-for-gui'] = [] if $limit_sizes =~ /original/ fullscreen_images['original'] = [] end @@ -711,14 +724,19 @@ def walk_source_dir gen_thumbnails_element("#{dir}/#{img}", xmldir, true, [ { 'filename' => thumbnail_dest_img, 'size' => $default_size['thumbnails'] } ]) else todo = [] + elem = xmldir.elements["image[@filename='#{utf8(img)}']"] for sizeobj in $images_size size_fullscreen = sizeobj['fullscreen'] size_thumbnails = sizeobj['thumbnails'] fullscreen_dest_img = base_dest_img + "-#{size_fullscreen}.jpg" - thumbnail_dest_img = base_dest_img + "-#{size_thumbnails}.jpg" fullscreen_images[sizeobj['name']] << File.basename(fullscreen_dest_img) - thumbnail_images[sizeobj['name']] << File.basename(thumbnail_dest_img) todo << { 'filename' => fullscreen_dest_img, 'size' => size_fullscreen } + if pano = pano_amount(elem) + thumbnail_images['dont-delete-file-for-gui'] << File.basename(base_dest_img + "-#{size_thumbnails}.jpg") + size_thumbnails = size_thumbnails.sub(/(\d+)/) { ($1.to_i * pano).to_i } + end + thumbnail_dest_img = base_dest_img + "-#{size_thumbnails}.jpg" + thumbnail_images[sizeobj['name']] << File.basename(thumbnail_dest_img) todo << { 'filename' => thumbnail_dest_img, 'size' => size_thumbnails } end gen_thumbnails_element("#{dir}/#{img}", xmldir, true, todo) @@ -795,7 +813,18 @@ def walk_source_dir for file in entries type = images.include?(file) ? 'image' : videos.include?(file) ? 'video' : nil if type - html_thumbnails += run_iterations(iterations) + if type == 'image' && elem = xmldir.elements["image[@filename='#{utf8(file)}']"] + if pano = pano_amount(elem) + html_thumbnails += run_iterations(iterations, pano) + html_thumbnails.gsub!(/~~colspan~~/) { 'colspan="' + pano.ceil.to_s + '"' } + else + html_thumbnails += run_iterations(iterations, 1) + html_thumbnails.gsub!(/~~colspan~~/, '') + end + else + html_thumbnails += run_iterations(iterations, 1) + html_thumbnails.gsub!(/~~colspan~~/, '') + end if type == 'image' index = images.index(file) html_thumbnails.gsub!(/~~image_iteration~~/, @@ -976,7 +1005,7 @@ def walk_source_dir thumbnail = "#{dest_dir}/thumbnails-thumbnail.jpg" gen_thumbnails_subdir(from_utf8(xmldir.attributes['thumbnails-captionfile']), xmldir, false, [ { 'filename' => thumbnail, 'size' => $albums_thumbnail_size } ], 'thumbnails') - html_index += run_iterations(iterations) + html_index += run_iterations(iterations, 1) html_index.gsub!(/~~image_iteration~~/, "" + img_element(thumbnail) + '') html_index.gsub!(/~~caption_iteration~~/, xmldir.attributes['thumbnails-caption']) end @@ -988,7 +1017,7 @@ def walk_source_dir end subdir = make_dest_filename(from_utf8(File.basename(child.attributes['path']))) thumbnail = "#{dest_dir}/thumbnails-#{subdir}.jpg" - html_index += run_iterations(iterations) + html_index += run_iterations(iterations, 1) captionfile, caption = find_subalbum_caption_info(child) gen_thumbnails_subdir(captionfile, child, false, [ { 'filename' => thumbnail, 'size' => $albums_thumbnail_size } ], find_subalbum_info_type(child)) diff --git a/data/booh/themes/dark/metadata/parameters.rb b/data/booh/themes/dark/metadata/parameters.rb index 4e67d3b..80bde98 100644 --- a/data/booh/themes/dark/metadata/parameters.rb +++ b/data/booh/themes/dark/metadata/parameters.rb @@ -25,45 +25,45 @@ bindtextdomain("booh") #- it's necessary to fit according to the typical space taken by #- widgets defined in the skeleton of the theme #- -#- ***IMPORTANT***: CHOOSE 4/3 ASPECT RATIO SIZES! +#- ***IMPORTANT***: CHOOSE 4/3 ASPECT RATIO SIZES (for thumbnails)! $images_size = [ { 'name' => 'small', - 'description' => _("Fullscreen 552x414, thumbnails 192x144, should fit 800x600 screens"), - 'fullscreen' => '552x414', + 'description' => _("Sizes that should fit browsers in fullscreen for 800x600 screens"), + 'fullscreen' => '750x414', 'thumbnails' => '192x144', 'optional' => true, }, { 'name' => 'medium', - 'description' => _("Fullscreen 704x528, thumbnails 240x180, should fit 1024x768 screens"), - 'fullscreen' => '704x528', + 'description' => _("Sizes that should fit browsers in fullscreen for 1024x768 screens"), + 'fullscreen' => '960x528', 'thumbnails' => '240x180', 'default' => true, }, { 'name' => 'large', - 'description' => _("Fullscreen 880x660, thumbnails 300x225, should fit 1280x1024 screens"), - 'fullscreen' => '880x660', + 'description' => _("Sizes that should fit browsers in fullscreen for 1280x1024 screens"), + 'fullscreen' => '1200x660', 'thumbnails' => '300x225', }, { 'name' => 'x-large', - 'description' => _("Fullscreen 962x721, thumbnails 328x245, should fit 1400x1050 screens"), - 'fullscreen' => '962x721', + 'description' => _("Sizes that should fit browsers in fullscreen for 1400x1050 screens"), + 'fullscreen' => '1312x721', 'thumbnails' => '328x245', 'optional' => true, }, { 'name' => 'xx-large', - 'description' => _("Fullscreen 1100x825, thumbnails 375x281, should fit 1600x1200 screens"), - 'fullscreen' => '1100x825', + 'description' => _("Sizes that should fit browsers in fullscreen for 1600x1200 screens"), + 'fullscreen' => '1500x825', 'thumbnails' => '375x281', 'optional' => true, } ] -$allowed_N_values = [ 3, 4, 6, 8 ] +$allowed_N_values = [ 3, 4, 5, 6, 8, 12 ] $default_N = 4 $albums_thumbnail_size = '300x225' diff --git a/data/booh/themes/simple/metadata/parameters.rb b/data/booh/themes/simple/metadata/parameters.rb index 543ee2e..80bde98 100644 --- a/data/booh/themes/simple/metadata/parameters.rb +++ b/data/booh/themes/simple/metadata/parameters.rb @@ -25,39 +25,39 @@ bindtextdomain("booh") #- it's necessary to fit according to the typical space taken by #- widgets defined in the skeleton of the theme #- -#- ***IMPORTANT***: CHOOSE 4/3 ASPECT RATIO SIZES! +#- ***IMPORTANT***: CHOOSE 4/3 ASPECT RATIO SIZES (for thumbnails)! $images_size = [ { 'name' => 'small', - 'description' => _("Fullscreen 552x414, thumbnails 192x144, should fit 800x600 screens"), - 'fullscreen' => '552x414', + 'description' => _("Sizes that should fit browsers in fullscreen for 800x600 screens"), + 'fullscreen' => '750x414', 'thumbnails' => '192x144', 'optional' => true, }, { 'name' => 'medium', - 'description' => _("Fullscreen 704x528, thumbnails 240x180, should fit 1024x768 screens"), - 'fullscreen' => '704x528', + 'description' => _("Sizes that should fit browsers in fullscreen for 1024x768 screens"), + 'fullscreen' => '960x528', 'thumbnails' => '240x180', 'default' => true, }, { 'name' => 'large', - 'description' => _("Fullscreen 880x660, thumbnails 300x225, should fit 1280x1024 screens"), - 'fullscreen' => '880x660', + 'description' => _("Sizes that should fit browsers in fullscreen for 1280x1024 screens"), + 'fullscreen' => '1200x660', 'thumbnails' => '300x225', }, { 'name' => 'x-large', - 'description' => _("Fullscreen 962x721, thumbnails 328x245, should fit 1400x1050 screens"), - 'fullscreen' => '962x721', + 'description' => _("Sizes that should fit browsers in fullscreen for 1400x1050 screens"), + 'fullscreen' => '1312x721', 'thumbnails' => '328x245', 'optional' => true, }, { 'name' => 'xx-large', - 'description' => _("Fullscreen 1100x825, thumbnails 375x281, should fit 1600x1200 screens"), - 'fullscreen' => '1100x825', + 'description' => _("Sizes that should fit browsers in fullscreen for 1600x1200 screens"), + 'fullscreen' => '1500x825', 'thumbnails' => '375x281', 'optional' => true, } diff --git a/data/booh/themes/simple/skeleton_thumbnails.html b/data/booh/themes/simple/skeleton_thumbnails.html index 2213e90..8ad867c 100644 --- a/data/booh/themes/simple/skeleton_thumbnails.html +++ b/data/booh/themes/simple/skeleton_thumbnails.html @@ -56,7 +56,7 @@ input { ~~iterate1_open~~ ~~iterate2_open_maxN~~ - + ~~image_iteration~~

~~ifvideo?~~~~fi~~ ~~caption_iteration~~

diff --git a/lib/booh/booh-lib.rb b/lib/booh/booh-lib.rb index 6f87d90..b660846 100644 --- a/lib/booh/booh-lib.rb +++ b/lib/booh/booh-lib.rb @@ -404,6 +404,22 @@ module Booh a > b ? a : b end + def clamp(n, a, b) + n < a ? a : n > b ? b : n + end + + def pano_amount(elem) + if pano_amount = elem.attributes['pano-amount'] + if $N_per_row + return clamp(pano_amount.to_f, 1, $N_per_row.to_i) + else + return clamp(pano_amount.to_f, 1, $default_N.to_i) + end + else + return nil + end + end + def substInFile(name) newcontent = IO.readlines(name).collect { |l| yield l } ios = File.open(name, "w") -- 2.30.2