}
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)
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 }
}
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))
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], '')
}
$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
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
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
}
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] }) }
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) =~ /^\./
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
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)
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
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)
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~~/,
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~~/, "<a href='thumbnails.html'>" + img_element(thumbnail) + '</a>')
html_index.gsub!(/~~caption_iteration~~/, xmldir.attributes['thumbnails-caption'])
end
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))
#- 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'
#- 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,
}
~~iterate1_open~~
<tr>
~~iterate2_open_maxN~~
- <td align="center" valign="top">
+ <td align="center" valign="top" ~~colspan~~>
~~image_iteration~~
<p>~~ifvideo?~~<img src="video.png"/>~~fi~~ ~~caption_iteration~~</p>
</td>
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")