3 # A.k.a `Best web-album Of the world, Or your money back, Humerus'.
5 # The acronyn sucks, however this is a tribute to Dragon Ball by
6 # Akira Toriyama, where the last enemy beaten by heroes of Dragon
7 # Ball is named "Boo". But there was already a free software project
8 # called Boo, so this one will be it "Booh". Or whatever.
11 # Copyright (c) 2004 Guillaume Cottenceau <gc3 at bluewin.ch>
13 # This software may be freely redistributed under the terms of the GNU
14 # public license version 2.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 require 'booh/rexml/document'
28 bindtextdomain("booh")
30 require 'booh/config.rb'
31 require 'booh/version.rb'
38 require 'booh/libadds'
45 $CURRENT_CHARSET = `locale charmap`.chomp
46 #- check charset availability. a locale configuration of C or POSIX yields the unsupported 'ANSI_X3.4-1968'.
48 REXML::XMLDecl.new(REXML::XMLDecl::DEFAULT_VERSION, $CURRENT_CHARSET)
50 $CURRENT_CHARSET = 'UTF-8'
52 $convert = 'convert -interlace line +profile "*"'
53 $convert_enhance = '-contrast -enhance -normalize'
56 return Iconv::iconv("UTF-8", $CURRENT_CHARSET, string).to_s
59 def utf8cut(string, maxlen)
61 return Iconv::iconv("UTF-8", $CURRENT_CHARSET, string[0..maxlen-1]).to_s
62 rescue Iconv::InvalidCharacter
63 return utf8cut(string, maxlen-1)
67 def sizename(key, translate)
68 #- fake for gettext to find these; if themes need more sizes, english name for them should be added here
69 sizenames = { 'small' => N_("small"), 'medium' => N_("medium"), 'large' => N_("large"),
70 'x-large' => N_("x-large"), 'xx-large' => N_("xx-large"),
71 'original' => N_("original") }
72 sizename = sizenames[key] || key
74 return utf8(_(sizename))
80 SUPPORTED_LANGUAGES = %w(en de fr ja eo)
83 langnames = { 'en' => _("english"), 'de' => _("german"), 'fr' => _("french"), 'ja' => _("japanese"), 'eo' => _("esperanto") }
84 return langnames[lang]
88 return Iconv::iconv($CURRENT_CHARSET, "UTF-8", string).to_s
91 def from_utf8_safe(string)
93 return Iconv::iconv($CURRENT_CHARSET, "UTF-8", string).to_s
94 rescue Iconv::IllegalSequence
99 def make_dest_filename_old(orig_filename)
100 #- we remove non alphanumeric characters but need to do that
101 #- cleverly to not end up with two similar dest filenames. we won't
102 #- urlencode because urldecode might happen in the browser.
103 return orig_filename.unpack("C*").collect { |v| v.chr =~ /[a-zA-Z\-_0-9\.\/]/ ? v.chr : sprintf("%2X", v) }.to_s
106 def make_dest_filename(orig_filename)
107 #- we remove non alphanumeric characters but need to do that
108 #- cleverly to not end up with two similar dest filenames. we won't
109 #- urlencode because urldecode might happen in the browser.
110 return orig_filename.unpack("C*").collect { |v| v.chr =~ /[a-zA-Z\-_0-9\.\/]/ ? v.chr : sprintf("~%02X", v) }.to_s
113 def msg(verbose_level, msg)
114 if verbose_level <= $verbose_level
115 if verbose_level == 0
116 warn _("\t***ERROR***: %s\n") % msg
117 elsif verbose_level == 1
118 warn _("\tWarning: %s\n") % msg
125 def msg_(verbose_level, msg)
126 if verbose_level <= $verbose_level
127 if verbose_level == 0
128 warn _("\t***ERROR***: %s") % msg
129 elsif verbose_level == 1
130 warn _("\tWarning: %s") % msg
142 def select_theme(name, limit_sizes, optimizefor32, nperrow)
144 msg 3, _("Selecting theme '%s'") % $theme
145 themedir = "#{$FPATH}/themes/#{$theme}"
146 if !File.directory?(themedir)
147 die_ _("Theme was not found (tried %s directory).") % themedir
149 eval File.open("#{themedir}/metadata/parameters.rb").readlines.join
152 if limit_sizes != 'all'
153 sizes = limit_sizes.split(/,/)
154 $images_size = $images_size.find_all { |e| sizes.include?(e['name']) }
155 if $images_size.length == 0
156 die_ _("Can't carry on, no valid size selected.")
160 $images_size = $images_size.find_all { |e| !e['optional'] }
164 $images_size.each { |e|
165 e['fullscreen'].gsub!(/(\d+x)(\d+)/) { $1 + ($2.to_f*8/9).to_i.to_s }
166 e['thumbnails'].gsub!(/(\d+x)(\d+)/) { $1 + ($2.to_f*8/9).to_i.to_s }
168 $albums_thumbnail_size.gsub!(/(\d+x)(\d+)/) { $1 + ($2.to_f*8/9).to_i.to_s }
171 if nperrow && nperrow != $default_N
172 ratio = nperrow.to_f / $default_N.to_f
173 $images_size.each { |e|
174 e['thumbnails'].gsub!(/(\d+)x(\d+)/) { ($1.to_f/ratio).to_i.to_s + 'x' + ($2.to_f/ratio).to_i.to_s }
178 $default_size = $images_size.detect { |sizeobj| sizeobj['default'] }
179 if $default_size == nil
180 $default_size = $images_size[0]
184 def entry2type(entry)
185 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-bmp.so
186 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-gif.so
187 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-ico.so
188 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-jpeg.so
189 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-png.so
190 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-pnm.so
191 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-ras.so
192 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-tiff.so
193 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-xbm.so
194 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-xpm.so
195 if entry =~ /\.(bmp|gif|ico|jpg|jpe|png|pnm|tif|xbm|xpm)$/i && entry !~ /['"\[\]]/
197 elsif !$ignore_videos && entry =~ /\.(mov|avi|mpg|mpeg|mpe|wmv|asx|3gp|mp4)$/i && entry !~ /['"\[\]]/
198 #- might consider using file magic later..
211 finished = Process.wait2
212 $pids.delete(finished[0])
213 $pids = $pids.find_all { |pid| Process.waitpid(pid, Process::WNOHANG) == nil }
217 while $pids && $pids.length > 0
222 #- parallelizable sys
232 if $pids.length == $mproc
240 def get_image_size(fullpath)
245 if $sizes_cache[fullpath].nil?
246 #- identify is slow, try with gdk if available (negligible vs 35ms)
248 if `identify '#{fullpath}'` =~ / JPEG (\d+)x(\d+) /
249 $sizes_cache[fullpath] = { :x => $1.to_i, :y => $2.to_i }
252 format, width, height = Gdk::Pixbuf.get_file_info(fullpath)
254 $sizes_cache[fullpath] = { :x => width, :y => height }
258 return $sizes_cache[fullpath]
264 #- commify from http://pleac.sourceforge.net/ (pleac rulz)
266 n.to_s =~ /([^\.]*)(\..*)?/
267 int, dec = $1.reverse, $2 ? $2 : ""
269 while int.gsub!(/(#{Regexp.quote(sep)}|\.|^)(\d{3})(\d)/, '\1\2' + sep + '\3')
274 def guess_rotate(filename)
275 #- identify is slow, try with libexiv2 if available (4ms vs 35ms)
280 orientation = `identify -format "%[EXIF:orientation]" '#{filename}'`.chomp.to_i
282 orientation = Exif.orientation(filename)
287 elsif orientation == 8
293 #- remove rotate if image is obviously already in portrait (situation can come from gthumb)
294 size = get_image_size(filename)
295 if size && size[:x] < size[:y]
302 def angle_to_exif_orientation(angle)
305 elsif angle == 270 || angle == -90
312 def rotate_pixbuf(pixbuf, angle)
313 return pixbuf.rotate(angle == 90 ? Gdk::Pixbuf::ROTATE_CLOCKWISE :
314 angle == 180 ? Gdk::Pixbuf::ROTATE_UPSIDEDOWN :
315 (angle == 270 || angle == -90) ? Gdk::Pixbuf::ROTATE_COUNTERCLOCKWISE :
316 Gdk::Pixbuf::ROTATE_NONE)
319 def gen_thumbnails_element(orig, xmldirorelem, allow_background, dests)
320 if xmldirorelem.name == 'dir'
321 xmldirorelem = xmldirorelem.elements["*[@filename='#{utf8(File.basename(orig))}']"]
323 gen_thumbnails(orig, allow_background, dests, xmldirorelem, '')
326 def gen_thumbnails_subdir(orig, xmldirorelem, allow_background, dests, type)
327 #- type can be `subdirs' or `thumbnails'
328 gen_thumbnails(orig, allow_background, dests, xmldirorelem, type + '-')
331 def gen_video_thumbnail(orig, colorswap, seektime)
333 #- ignored for the moment. is mplayer subject to blue faces problem?
335 #- it's not possible to specify a basename for the output jpeg file with mplayer (file will be named 00000001.jpg); as this can
336 #- be called from multiple threads, we must come up with a unique directory where to put the file
337 tmpfile = Tempfile.new("boohvideotmp")
338 Thread.critical = true
339 tmpdirname = tmpfile.path
342 Dir.mkdir(tmpdirname)
344 raise "Tmp directory #{tmpdirname} already exists"
346 Thread.critical = false
348 cmd = "mplayer '#{orig}' -nosound -vo jpeg:outdir='#{tmpdirname}' -frames 1 -ss #{seektime} -slave >/dev/null 2>/dev/null"
350 if ! File.exists?("#{tmpdirname}/00000001.jpg")
351 msg 0, _("specified seektime too large? that may also be another probleme. try another value.")
352 Dir.rmdir(tmpdirname)
358 def gen_thumbnails(orig, allow_background, dests, felem, attributes_prefix)
359 if !dests.detect { |dest| !File.exists?(dest['filename']) }
364 dest_dir = make_dest_filename(File.dirname(dests[0]['filename']))
366 if entry2type(orig) == 'image'
368 if whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
369 neworig = "#{dest_dir}/#{File.basename(orig)}-whitebalance#{whitebalance}.jpg"
370 cmd = "booh-fix-whitebalance '#{orig}' '#{neworig}' #{whitebalance}"
372 if File.exists?(neworig)
376 if gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"]
377 neworig = "#{dest_dir}/#{File.basename(orig)}-gammacorrect#{gammacorrect}.jpg"
378 cmd = "booh-gamma-correction '#{orig}' '#{neworig}' #{gammacorrect}"
380 if File.exists?(neworig)
384 rotate = felem.attributes["#{attributes_prefix}rotate"]
386 felem.add_attribute("#{attributes_prefix}rotate", rotate = guess_rotate(orig).to_s)
388 convert_options += "-rotate #{rotate} "
389 if felem.attributes["#{attributes_prefix}enhance"]
390 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
394 if !File.exists?(dest['filename'])
396 cmd ||= "#{$convert} #{convert_options}-size #{dest['size']} -resize '#{dest['size']}>' '#{orig}' '#{dest['filename']}'"
411 #- can happen on race conditions for generating multiple times a thumbnail for a given image. for the moment,
412 #- silently ignore, it is not a so big deal.
417 elsif entry2type(orig) == 'video'
419 #- seektime is an attribute that allows to specify where the frame to use for the thumbnail must be taken
420 seektime = felem.attributes["#{attributes_prefix}seektime"]
422 felem.add_attribute("#{attributes_prefix}seektime", seektime = "0")
424 seektime = seektime.to_f
425 if rotate = felem.attributes["#{attributes_prefix}rotate"]
426 convert_options += "-rotate #{rotate} "
428 if felem.attributes["#{attributes_prefix}enhance"]
429 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
433 if ! File.exists?(dest['filename'])
434 tmpdir = gen_video_thumbnail(orig, felem && felem.attributes["#{attributes_prefix}color-swap"], seektime)
438 tmpfile = "#{tmpdir}/00000001.jpg"
439 alltmpfiles = [ tmpfile ]
440 if felem && whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
441 if whitebalance.to_f != 0
442 neworig = "#{tmpdir}/whitebalance#{whitebalance}.jpg"
443 cmd = "booh-fix-whitebalance '#{tmpfile}' '#{neworig}' #{whitebalance}"
445 if File.exists?(neworig)
447 alltmpfiles << neworig
451 if felem && gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"]
452 if gammacorrect.to_f != 0
453 neworig = "#{tmpdir}/gammacorrect#{gammacorrect}.jpg"
454 cmd = "booh-gamma-correction '#{tmpfile}' '#{neworig}' #{gammacorrect}"
456 if File.exists?(neworig)
458 alltmpfiles << neworig
462 sys("#{$convert} #{convert_options}-size #{dest['size']} -resize #{dest['size']} '#{tmpfile}' '#{dest['filename']}'")
463 alltmpfiles.each { |file| File.delete(file) }
471 def invornil(obj, methodname)
475 return obj.method(methodname).call
479 def find_subalbum_info_type(xmldir)
480 #- first look for subdirs info; if not, means there is no subdir
481 if xmldir.attributes['subdirs-caption']
488 def find_subalbum_caption_info(xmldir)
489 type = find_subalbum_info_type(xmldir)
490 return [ from_utf8(xmldir.attributes["#{type}-captionfile"]), xmldir.attributes["#{type}-caption"] ]
495 return File.size(path)
506 n < a ? a : n > b ? b : n
509 def pano_amount(elem)
510 if pano_amount = elem.attributes['pano-amount']
512 return clamp(pano_amount.to_f, 1, $N_per_row.to_i)
514 return clamp(pano_amount.to_f, 1, $default_N.to_i)
521 def substInFile(name)
522 newcontent = IO.readlines(name).collect { |l| yield l }
523 ios = File.open(name, "w")
524 ios.write(newcontent)
529 cmd = $config['browser'].gsub('%f', "'#{url}'") + ' &'
536 GNU GENERAL PUBLIC LICENSE
539 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
540 675 Mass Ave, Cambridge, MA 02139, USA
541 Everyone is permitted to copy and distribute verbatim copies
542 of this license document, but changing it is not allowed.
546 The licenses for most software are designed to take away your
547 freedom to share and change it. By contrast, the GNU General Public
548 License is intended to guarantee your freedom to share and change free
549 software--to make sure the software is free for all its users. This
550 General Public License applies to most of the Free Software
551 Foundation's software and to any other program whose authors commit to
552 using it. (Some other Free Software Foundation software is covered by
553 the GNU Library General Public License instead.) You can apply it to
556 When we speak of free software, we are referring to freedom, not
557 price. Our General Public Licenses are designed to make sure that you
558 have the freedom to distribute copies of free software (and charge for
559 this service if you wish), that you receive source code or can get it
560 if you want it, that you can change the software or use pieces of it
561 in new free programs; and that you know you can do these things.
563 To protect your rights, we need to make restrictions that forbid
564 anyone to deny you these rights or to ask you to surrender the rights.
565 These restrictions translate to certain responsibilities for you if you
566 distribute copies of the software, or if you modify it.
568 For example, if you distribute copies of such a program, whether
569 gratis or for a fee, you must give the recipients all the rights that
570 you have. You must make sure that they, too, receive or can get the
571 source code. And you must show them these terms so they know their
574 We protect your rights with two steps: (1) copyright the software, and
575 (2) offer you this license which gives you legal permission to copy,
576 distribute and/or modify the software.
578 Also, for each author's protection and ours, we want to make certain
579 that everyone understands that there is no warranty for this free
580 software. If the software is modified by someone else and passed on, we
581 want its recipients to know that what they have is not the original, so
582 that any problems introduced by others will not reflect on the original
583 authors' reputations.
585 Finally, any free program is threatened constantly by software
586 patents. We wish to avoid the danger that redistributors of a free
587 program will individually obtain patent licenses, in effect making the
588 program proprietary. To prevent this, we have made it clear that any
589 patent must be licensed for everyone's free use or not licensed at all.
591 The precise terms and conditions for copying, distribution and
595 GNU GENERAL PUBLIC LICENSE
596 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
598 0. This License applies to any program or other work which contains
599 a notice placed by the copyright holder saying it may be distributed
600 under the terms of this General Public License. The "Program", below,
601 refers to any such program or work, and a "work based on the Program"
602 means either the Program or any derivative work under copyright law:
603 that is to say, a work containing the Program or a portion of it,
604 either verbatim or with modifications and/or translated into another
605 language. (Hereinafter, translation is included without limitation in
606 the term "modification".) Each licensee is addressed as "you".
608 Activities other than copying, distribution and modification are not
609 covered by this License; they are outside its scope. The act of
610 running the Program is not restricted, and the output from the Program
611 is covered only if its contents constitute a work based on the
612 Program (independent of having been made by running the Program).
613 Whether that is true depends on what the Program does.
615 1. You may copy and distribute verbatim copies of the Program's
616 source code as you receive it, in any medium, provided that you
617 conspicuously and appropriately publish on each copy an appropriate
618 copyright notice and disclaimer of warranty; keep intact all the
619 notices that refer to this License and to the absence of any warranty;
620 and give any other recipients of the Program a copy of this License
621 along with the Program.
623 You may charge a fee for the physical act of transferring a copy, and
624 you may at your option offer warranty protection in exchange for a fee.
626 2. You may modify your copy or copies of the Program or any portion
627 of it, thus forming a work based on the Program, and copy and
628 distribute such modifications or work under the terms of Section 1
629 above, provided that you also meet all of these conditions:
631 a) You must cause the modified files to carry prominent notices
632 stating that you changed the files and the date of any change.
634 b) You must cause any work that you distribute or publish, that in
635 whole or in part contains or is derived from the Program or any
636 part thereof, to be licensed as a whole at no charge to all third
637 parties under the terms of this License.
639 c) If the modified program normally reads commands interactively
640 when run, you must cause it, when started running for such
641 interactive use in the most ordinary way, to print or display an
642 announcement including an appropriate copyright notice and a
643 notice that there is no warranty (or else, saying that you provide
644 a warranty) and that users may redistribute the program under
645 these conditions, and telling the user how to view a copy of this
646 License. (Exception: if the Program itself is interactive but
647 does not normally print such an announcement, your work based on
648 the Program is not required to print an announcement.)
651 These requirements apply to the modified work as a whole. If
652 identifiable sections of that work are not derived from the Program,
653 and can be reasonably considered independent and separate works in
654 themselves, then this License, and its terms, do not apply to those
655 sections when you distribute them as separate works. But when you
656 distribute the same sections as part of a whole which is a work based
657 on the Program, the distribution of the whole must be on the terms of
658 this License, whose permissions for other licensees extend to the
659 entire whole, and thus to each and every part regardless of who wrote it.
661 Thus, it is not the intent of this section to claim rights or contest
662 your rights to work written entirely by you; rather, the intent is to
663 exercise the right to control the distribution of derivative or
664 collective works based on the Program.
666 In addition, mere aggregation of another work not based on the Program
667 with the Program (or with a work based on the Program) on a volume of
668 a storage or distribution medium does not bring the other work under
669 the scope of this License.
671 3. You may copy and distribute the Program (or a work based on it,
672 under Section 2) in object code or executable form under the terms of
673 Sections 1 and 2 above provided that you also do one of the following:
675 a) Accompany it with the complete corresponding machine-readable
676 source code, which must be distributed under the terms of Sections
677 1 and 2 above on a medium customarily used for software interchange; or,
679 b) Accompany it with a written offer, valid for at least three
680 years, to give any third party, for a charge no more than your
681 cost of physically performing source distribution, a complete
682 machine-readable copy of the corresponding source code, to be
683 distributed under the terms of Sections 1 and 2 above on a medium
684 customarily used for software interchange; or,
686 c) Accompany it with the information you received as to the offer
687 to distribute corresponding source code. (This alternative is
688 allowed only for noncommercial distribution and only if you
689 received the program in object code or executable form with such
690 an offer, in accord with Subsection b above.)
692 The source code for a work means the preferred form of the work for
693 making modifications to it. For an executable work, complete source
694 code means all the source code for all modules it contains, plus any
695 associated interface definition files, plus the scripts used to
696 control compilation and installation of the executable. However, as a
697 special exception, the source code distributed need not include
698 anything that is normally distributed (in either source or binary
699 form) with the major components (compiler, kernel, and so on) of the
700 operating system on which the executable runs, unless that component
701 itself accompanies the executable.
703 If distribution of executable or object code is made by offering
704 access to copy from a designated place, then offering equivalent
705 access to copy the source code from the same place counts as
706 distribution of the source code, even though third parties are not
707 compelled to copy the source along with the object code.
710 4. You may not copy, modify, sublicense, or distribute the Program
711 except as expressly provided under this License. Any attempt
712 otherwise to copy, modify, sublicense or distribute the Program is
713 void, and will automatically terminate your rights under this License.
714 However, parties who have received copies, or rights, from you under
715 this License will not have their licenses terminated so long as such
716 parties remain in full compliance.
718 5. You are not required to accept this License, since you have not
719 signed it. However, nothing else grants you permission to modify or
720 distribute the Program or its derivative works. These actions are
721 prohibited by law if you do not accept this License. Therefore, by
722 modifying or distributing the Program (or any work based on the
723 Program), you indicate your acceptance of this License to do so, and
724 all its terms and conditions for copying, distributing or modifying
725 the Program or works based on it.
727 6. Each time you redistribute the Program (or any work based on the
728 Program), the recipient automatically receives a license from the
729 original licensor to copy, distribute or modify the Program subject to
730 these terms and conditions. You may not impose any further
731 restrictions on the recipients' exercise of the rights granted herein.
732 You are not responsible for enforcing compliance by third parties to
735 7. If, as a consequence of a court judgment or allegation of patent
736 infringement or for any other reason (not limited to patent issues),
737 conditions are imposed on you (whether by court order, agreement or
738 otherwise) that contradict the conditions of this License, they do not
739 excuse you from the conditions of this License. If you cannot
740 distribute so as to satisfy simultaneously your obligations under this
741 License and any other pertinent obligations, then as a consequence you
742 may not distribute the Program at all. For example, if a patent
743 license would not permit royalty-free redistribution of the Program by
744 all those who receive copies directly or indirectly through you, then
745 the only way you could satisfy both it and this License would be to
746 refrain entirely from distribution of the Program.
748 If any portion of this section is held invalid or unenforceable under
749 any particular circumstance, the balance of the section is intended to
750 apply and the section as a whole is intended to apply in other
753 It is not the purpose of this section to induce you to infringe any
754 patents or other property right claims or to contest validity of any
755 such claims; this section has the sole purpose of protecting the
756 integrity of the free software distribution system, which is
757 implemented by public license practices. Many people have made
758 generous contributions to the wide range of software distributed
759 through that system in reliance on consistent application of that
760 system; it is up to the author/donor to decide if he or she is willing
761 to distribute software through any other system and a licensee cannot
764 This section is intended to make thoroughly clear what is believed to
765 be a consequence of the rest of this License.
768 8. If the distribution and/or use of the Program is restricted in
769 certain countries either by patents or by copyrighted interfaces, the
770 original copyright holder who places the Program under this License
771 may add an explicit geographical distribution limitation excluding
772 those countries, so that distribution is permitted only in or among
773 countries not thus excluded. In such case, this License incorporates
774 the limitation as if written in the body of this License.
776 9. The Free Software Foundation may publish revised and/or new versions
777 of the General Public License from time to time. Such new versions will
778 be similar in spirit to the present version, but may differ in detail to
779 address new problems or concerns.
781 Each version is given a distinguishing version number. If the Program
782 specifies a version number of this License which applies to it and "any
783 later version", you have the option of following the terms and conditions
784 either of that version or of any later version published by the Free
785 Software Foundation. If the Program does not specify a version number of
786 this License, you may choose any version ever published by the Free Software
789 10. If you wish to incorporate parts of the Program into other free
790 programs whose distribution conditions are different, write to the author
791 to ask for permission. For software which is copyrighted by the Free
792 Software Foundation, write to the Free Software Foundation; we sometimes
793 make exceptions for this. Our decision will be guided by the two goals
794 of preserving the free status of all derivatives of our free software and
795 of promoting the sharing and reuse of software generally.
799 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
800 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
801 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
802 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
803 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
804 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
805 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
806 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
807 REPAIR OR CORRECTION.
809 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
810 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
811 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
812 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
813 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
814 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
815 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
816 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
817 POSSIBILITY OF SUCH DAMAGES.
822 Gtk::AboutDialog.set_url_hook { |dialog, url| open_url(url) }
823 Gtk::AboutDialog.show($main_window, { :name => 'booh',
824 :version => $VERSION,
825 :copyright => 'Copyright (c) 2005-2008 Guillaume Cottenceau',
826 :license => get_license,
827 :website => 'http://booh.org/',
828 :authors => [ 'Guillaume Cottenceau' ],
829 :artists => [ 'Ayo73' ],
830 :comments => utf8(_("''The Web-Album of choice for discriminating Linux users''")),
831 :translator_credits => utf8(_('Esperanto: Stephane Fillod
832 Japanese: Masao Mutoh
833 German: Roland Eckert
834 French: Guillaume Cottenceau')),
835 :logo => Gdk::Pixbuf.new("#{$FPATH}/images/logo.png") })
838 def smartsort(entries, sort_criterions)
839 #- sort "entries" according to "sort_criterions" but find a good fallback for all entries without a
840 #- criterion value (still next to the item they were next to)
841 sorted_entries = sort_criterions.keys.sort { |a,b| sort_criterions[a] <=> sort_criterions[b] }
842 for i in 0 .. entries.size - 1
843 if ! sorted_entries.include?(entries[i])
845 while j > 0 && ! sorted_entries.include?(entries[j])
848 sorted_entries[(sorted_entries.index(entries[j]) || -1 ) + 1, 0] = entries[i]
851 return sorted_entries
854 def defer_translation(msg)
860 w.icon_list = [ Gdk::Pixbuf.new("#{$FPATH}/images/booh-16x16.png"),
861 Gdk::Pixbuf.new("#{$FPATH}/images/booh-32x32.png"),
862 Gdk::Pixbuf.new("#{$FPATH}/images/booh-48x48.png") ]
870 if !self || self.to_s == 'false'
879 def File.reduce_path(path)
880 return path.gsub(/\w+\/\.\.\//, '')
885 def collect_with_index
887 each_with_index { |e,i|
897 each { |v| retval += v.to_i }
903 def previous_element_byname(name)
905 while n = n.previous_element
913 def previous_element_byname_notattr(name, attr)
915 while n = n.previous_element
916 if n.name == name && !n.attributes[attr]
923 def next_element_byname(name)
925 while n = n.next_element
933 def next_element_byname_notattr(name, attr)
935 while n = n.next_element
936 if n.name == name && !n.attributes[attr]
943 def child_byname_notattr(name, attr)
944 elements.each(name) { |element|
945 if !element.attributes[attr]