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 '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 $convert = 'convert -interlace line +profile "*"'
47 $convert_enhance = '-contrast -enhance -normalize'
50 return Iconv::iconv("UTF-8", $CURRENT_CHARSET, string).to_s
53 def utf8cut(string, maxlen)
55 return Iconv::iconv("UTF-8", $CURRENT_CHARSET, string[0..maxlen-1]).to_s
56 rescue Iconv::InvalidCharacter
57 return utf8cut(string, maxlen-1)
62 #- fake for gettext to find these; if themes need more sizes, english name for them should be added here
63 sizenames = { 'small' => utf8(_("small")), 'medium' => utf8(_("medium")), 'large' => utf8(_("large")),
64 'x-large' => utf8(_("x-large")), 'xx-large' => utf8(_("xx-large")),
65 'original' => utf8(_("original")) }
66 return sizenames[key] || key
70 return Iconv::iconv($CURRENT_CHARSET, "UTF-8", string).to_s
73 def from_utf8_safe(string)
75 return Iconv::iconv($CURRENT_CHARSET, "UTF-8", string).to_s
76 rescue Iconv::IllegalSequence
81 def make_dest_filename_old(orig_filename)
82 #- we remove non alphanumeric characters but need to do that
83 #- cleverly to not end up with two similar dest filenames. we won't
84 #- urlencode because urldecode might happen in the browser.
85 return orig_filename.unpack("C*").collect { |v| v.chr =~ /[a-zA-Z\-_0-9\.\/]/ ? v.chr : sprintf("%2X", v) }.to_s
88 def make_dest_filename(orig_filename)
89 #- we remove non alphanumeric characters but need to do that
90 #- cleverly to not end up with two similar dest filenames. we won't
91 #- urlencode because urldecode might happen in the browser.
92 return orig_filename.unpack("C*").collect { |v| v.chr =~ /[a-zA-Z\-_0-9\.\/]/ ? v.chr : sprintf("~%02X", v) }.to_s
95 def msg(verbose_level, msg)
96 if verbose_level <= $verbose_level
98 warn _("\t***ERROR***: %s\n") % msg
99 elsif verbose_level == 1
100 warn _("\tWarning: %s\n") % msg
107 def msg_(verbose_level, msg)
108 if verbose_level <= $verbose_level
109 if verbose_level == 0
110 warn _("\t***ERROR***: %s") % msg
111 elsif verbose_level == 1
112 warn _("\tWarning: %s") % msg
124 def select_theme(name, limit_sizes, optimizefor32, nperrow)
126 msg 3, _("Selecting theme '%s'") % $theme
127 themedir = "#{$FPATH}/themes/#{$theme}"
128 if !File.directory?(themedir)
129 die _("Theme was not found (tried %s directory).") % themedir
131 eval File.open("#{themedir}/metadata/parameters.rb").readlines.join
134 if limit_sizes != 'all'
135 sizes = limit_sizes.split(/,/)
136 $images_size = $images_size.find_all { |e| sizes.include?(e['name']) }
137 if $images_size.length == 0
138 die _("Can't carry on, no valid size selected.")
142 $images_size = $images_size.find_all { |e| !e['optional'] }
146 $images_size.each { |e|
147 e['fullscreen'].gsub!(/(\d+x)(\d+)/) { $1 + ($2.to_f*8/9).to_i.to_s }
148 e['thumbnails'].gsub!(/(\d+x)(\d+)/) { $1 + ($2.to_f*8/9).to_i.to_s }
150 $albums_thumbnail_size.gsub!(/(\d+x)(\d+)/) { $1 + ($2.to_f*8/9).to_i.to_s }
153 if nperrow && nperrow != $default_N
154 ratio = nperrow.to_f / $default_N.to_f
155 $images_size.each { |e|
156 e['thumbnails'].gsub!(/(\d+)x(\d+)/) { ($1.to_f/ratio).to_i.to_s + 'x' + ($2.to_f/ratio).to_i.to_s }
160 $default_size = $images_size.detect { |sizeobj| sizeobj['default'] }
161 if $default_size == nil
162 $default_size = $images_size[0]
166 def entry2type(entry)
167 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-bmp.so
168 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-gif.so
169 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-ico.so
170 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-jpeg.so
171 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-png.so
172 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-pnm.so
173 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-ras.so
174 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-tiff.so
175 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-xbm.so
176 #- /usr/lib/gdk-pixbuf/loaders/libpixbufloader-xpm.so
177 if entry =~ /\.(bmp|gif|ico|jpg|jpe|png|pnm|tif|xbm|xpm)$/i && entry !~ /['"\[\]]/
179 elsif !$ignore_videos && entry =~ /\.(mov|avi|mpg|mpeg|mpe|wmv|asx|3gp|mp4)$/i && entry !~ /['"\[\]]/
180 #- might consider using file magic later..
193 finished = Process.wait2
194 $pids.delete(finished[0])
195 $pids = $pids.find_all { |pid| Process.waitpid(pid, Process::WNOHANG) == nil }
199 while $pids && $pids.length > 0
204 #- parallelizable sys
214 if $pids.length == $mproc
222 def get_image_size(fullpath)
227 if $sizes_cache[fullpath].nil?
228 #- identify is slow, try with gdk if available (negligible vs 35ms)
230 if `identify '#{fullpath}'` =~ / JPEG (\d+)x(\d+) /
231 $sizes_cache[fullpath] = { :x => $1.to_i, :y => $2.to_i }
234 format, width, height = Gdk::Pixbuf.get_file_info(fullpath)
236 $sizes_cache[fullpath] = { :x => width, :y => height }
240 return $sizes_cache[fullpath]
246 #- commify from http://pleac.sourceforge.net/ (pleac rulz)
248 n.to_s =~ /([^\.]*)(\..*)?/
249 int, dec = $1.reverse, $2 ? $2 : ""
250 while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2' + _(",") + '\3')
255 def guess_rotate(filename)
256 #- identify is slow, try with libexiv2 if available (4ms vs 35ms)
261 orientation = `identify -format "%[EXIF:orientation]" '#{filename}'`.chomp.to_i
263 orientation = Exif.orientation(filename)
268 elsif orientation == 8
274 #- remove rotate if image is obviously already in portrait (situation can come from gthumb)
275 size = get_image_size(filename)
276 if size && size[:x] < size[:y]
283 def angle_to_exif_orientation(angle)
286 elsif angle == 270 || angle == -90
293 def rotate_pixbuf(pixbuf, angle)
294 return pixbuf.rotate(angle == 90 ? Gdk::Pixbuf::ROTATE_CLOCKWISE :
295 angle == 180 ? Gdk::Pixbuf::ROTATE_UPSIDEDOWN :
296 (angle == 270 || angle == -90) ? Gdk::Pixbuf::ROTATE_COUNTERCLOCKWISE :
297 Gdk::Pixbuf::ROTATE_NONE)
300 def gen_thumbnails_element(orig, xmldirorelem, allow_background, dests)
301 if xmldirorelem.name == 'dir'
302 xmldirorelem = xmldirorelem.elements["*[@filename='#{utf8(File.basename(orig))}']"]
304 gen_thumbnails(orig, allow_background, dests, xmldirorelem, '')
307 def gen_thumbnails_subdir(orig, xmldirorelem, allow_background, dests, type)
308 #- type can be `subdirs' or `thumbnails'
309 gen_thumbnails(orig, allow_background, dests, xmldirorelem, type + '-')
312 def gen_video_thumbnail(orig, colorswap, seektime)
314 #- ignored for the moment. is mplayer subject to blue faces problem?
316 #- it's not possible to specify a basename for the output jpeg file with mplayer (file will be named 00000001.jpg); as this can
317 #- be called from multiple threads, we must come up with a unique directory where to put the file
318 tmpfile = Tempfile.new("boohvideotmp")
321 Dir.mkdir(tmpdirname = tmpfile.path)
323 raise "Tmp directory #{tmpfile.path} already exists"
325 cmd = "mplayer '#{orig}' -nosound -vo jpeg:outdir='#{tmpdirname}' -frames 1 -ss #{seektime} >/dev/null 2>/dev/null"
328 if ! File.exists?("#{tmpdirname}/00000001.jpg")
329 msg 0, _("specified seektime too large? that may also be another probleme. try another value.")
330 Dir.rmdir(tmpdirname)
336 def gen_thumbnails(orig, allow_background, dests, felem, attributes_prefix)
337 if !dests.detect { |dest| !File.exists?(dest['filename']) }
342 dest_dir = make_dest_filename(File.dirname(dests[0]['filename']))
344 if entry2type(orig) == 'image'
346 if whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
347 neworig = "#{dest_dir}/#{File.basename(orig)}-whitebalance#{whitebalance}.jpg"
348 cmd = "booh-fix-whitebalance '#{orig}' '#{neworig}' #{whitebalance}"
350 if File.exists?(neworig)
354 if gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"]
355 neworig = "#{dest_dir}/#{File.basename(orig)}-gammacorrect#{gammacorrect}.jpg"
356 cmd = "booh-gamma-correction '#{orig}' '#{neworig}' #{gammacorrect}"
358 if File.exists?(neworig)
362 rotate = felem.attributes["#{attributes_prefix}rotate"]
364 felem.add_attribute("#{attributes_prefix}rotate", rotate = guess_rotate(orig).to_s)
366 convert_options += "-rotate #{rotate} "
367 if felem.attributes["#{attributes_prefix}enhance"]
368 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
372 if !File.exists?(dest['filename'])
374 cmd ||= "#{$convert} #{convert_options}-size #{dest['size']} -resize '#{dest['size']}>' '#{orig}' '#{dest['filename']}'"
390 elsif entry2type(orig) == 'video'
392 #- seektime is an attribute that allows to specify where the frame to use for the thumbnail must be taken
393 seektime = felem.attributes["#{attributes_prefix}seektime"]
395 felem.add_attribute("#{attributes_prefix}seektime", seektime = "0")
397 seektime = seektime.to_f
398 if rotate = felem.attributes["#{attributes_prefix}rotate"]
399 convert_options += "-rotate #{rotate} "
401 if felem.attributes["#{attributes_prefix}enhance"]
402 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
406 if ! File.exists?(dest['filename'])
407 tmpdir = gen_video_thumbnail(orig, felem && felem.attributes["#{attributes_prefix}color-swap"], seektime)
411 tmpfile = "#{tmpdir}/00000001.jpg"
412 alltmpfiles = [ tmpfile ]
413 if felem && whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
414 if whitebalance.to_f != 0
415 neworig = "#{tmpdir}/whitebalance#{whitebalance}.jpg"
416 cmd = "booh-fix-whitebalance '#{tmpfile}' '#{neworig}' #{whitebalance}"
418 if File.exists?(neworig)
420 alltmpfiles << neworig
424 if felem && gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"]
425 if gammacorrect.to_f != 0
426 neworig = "#{tmpdir}/gammacorrect#{gammacorrect}.jpg"
427 cmd = "booh-gamma-correction '#{tmpfile}' '#{neworig}' #{gammacorrect}"
429 if File.exists?(neworig)
431 alltmpfiles << neworig
435 sys("#{$convert} #{convert_options}-size #{dest['size']} -resize #{dest['size']} '#{tmpfile}' '#{dest['filename']}'")
436 alltmpfiles.each { |file| File.delete(file) }
444 def invornil(obj, methodname)
448 return obj.method(methodname).call
452 def find_subalbum_info_type(xmldir)
453 #- first look for subdirs info; if not, means there is no subdir
454 if xmldir.attributes['subdirs-caption']
461 def find_subalbum_caption_info(xmldir)
462 type = find_subalbum_info_type(xmldir)
463 return [ from_utf8(xmldir.attributes["#{type}-captionfile"]), xmldir.attributes["#{type}-caption"] ]
468 return File.size(path)
479 n < a ? a : n > b ? b : n
482 def pano_amount(elem)
483 if pano_amount = elem.attributes['pano-amount']
485 return clamp(pano_amount.to_f, 1, $N_per_row.to_i)
487 return clamp(pano_amount.to_f, 1, $default_N.to_i)
494 def substInFile(name)
495 newcontent = IO.readlines(name).collect { |l| yield l }
496 ios = File.open(name, "w")
497 ios.write(newcontent)
502 cmd = $config['browser'].gsub('%f', "'#{url}'") + ' &'
509 GNU GENERAL PUBLIC LICENSE
512 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
513 675 Mass Ave, Cambridge, MA 02139, USA
514 Everyone is permitted to copy and distribute verbatim copies
515 of this license document, but changing it is not allowed.
519 The licenses for most software are designed to take away your
520 freedom to share and change it. By contrast, the GNU General Public
521 License is intended to guarantee your freedom to share and change free
522 software--to make sure the software is free for all its users. This
523 General Public License applies to most of the Free Software
524 Foundation's software and to any other program whose authors commit to
525 using it. (Some other Free Software Foundation software is covered by
526 the GNU Library General Public License instead.) You can apply it to
529 When we speak of free software, we are referring to freedom, not
530 price. Our General Public Licenses are designed to make sure that you
531 have the freedom to distribute copies of free software (and charge for
532 this service if you wish), that you receive source code or can get it
533 if you want it, that you can change the software or use pieces of it
534 in new free programs; and that you know you can do these things.
536 To protect your rights, we need to make restrictions that forbid
537 anyone to deny you these rights or to ask you to surrender the rights.
538 These restrictions translate to certain responsibilities for you if you
539 distribute copies of the software, or if you modify it.
541 For example, if you distribute copies of such a program, whether
542 gratis or for a fee, you must give the recipients all the rights that
543 you have. You must make sure that they, too, receive or can get the
544 source code. And you must show them these terms so they know their
547 We protect your rights with two steps: (1) copyright the software, and
548 (2) offer you this license which gives you legal permission to copy,
549 distribute and/or modify the software.
551 Also, for each author's protection and ours, we want to make certain
552 that everyone understands that there is no warranty for this free
553 software. If the software is modified by someone else and passed on, we
554 want its recipients to know that what they have is not the original, so
555 that any problems introduced by others will not reflect on the original
556 authors' reputations.
558 Finally, any free program is threatened constantly by software
559 patents. We wish to avoid the danger that redistributors of a free
560 program will individually obtain patent licenses, in effect making the
561 program proprietary. To prevent this, we have made it clear that any
562 patent must be licensed for everyone's free use or not licensed at all.
564 The precise terms and conditions for copying, distribution and
568 GNU GENERAL PUBLIC LICENSE
569 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
571 0. This License applies to any program or other work which contains
572 a notice placed by the copyright holder saying it may be distributed
573 under the terms of this General Public License. The "Program", below,
574 refers to any such program or work, and a "work based on the Program"
575 means either the Program or any derivative work under copyright law:
576 that is to say, a work containing the Program or a portion of it,
577 either verbatim or with modifications and/or translated into another
578 language. (Hereinafter, translation is included without limitation in
579 the term "modification".) Each licensee is addressed as "you".
581 Activities other than copying, distribution and modification are not
582 covered by this License; they are outside its scope. The act of
583 running the Program is not restricted, and the output from the Program
584 is covered only if its contents constitute a work based on the
585 Program (independent of having been made by running the Program).
586 Whether that is true depends on what the Program does.
588 1. You may copy and distribute verbatim copies of the Program's
589 source code as you receive it, in any medium, provided that you
590 conspicuously and appropriately publish on each copy an appropriate
591 copyright notice and disclaimer of warranty; keep intact all the
592 notices that refer to this License and to the absence of any warranty;
593 and give any other recipients of the Program a copy of this License
594 along with the Program.
596 You may charge a fee for the physical act of transferring a copy, and
597 you may at your option offer warranty protection in exchange for a fee.
599 2. You may modify your copy or copies of the Program or any portion
600 of it, thus forming a work based on the Program, and copy and
601 distribute such modifications or work under the terms of Section 1
602 above, provided that you also meet all of these conditions:
604 a) You must cause the modified files to carry prominent notices
605 stating that you changed the files and the date of any change.
607 b) You must cause any work that you distribute or publish, that in
608 whole or in part contains or is derived from the Program or any
609 part thereof, to be licensed as a whole at no charge to all third
610 parties under the terms of this License.
612 c) If the modified program normally reads commands interactively
613 when run, you must cause it, when started running for such
614 interactive use in the most ordinary way, to print or display an
615 announcement including an appropriate copyright notice and a
616 notice that there is no warranty (or else, saying that you provide
617 a warranty) and that users may redistribute the program under
618 these conditions, and telling the user how to view a copy of this
619 License. (Exception: if the Program itself is interactive but
620 does not normally print such an announcement, your work based on
621 the Program is not required to print an announcement.)
624 These requirements apply to the modified work as a whole. If
625 identifiable sections of that work are not derived from the Program,
626 and can be reasonably considered independent and separate works in
627 themselves, then this License, and its terms, do not apply to those
628 sections when you distribute them as separate works. But when you
629 distribute the same sections as part of a whole which is a work based
630 on the Program, the distribution of the whole must be on the terms of
631 this License, whose permissions for other licensees extend to the
632 entire whole, and thus to each and every part regardless of who wrote it.
634 Thus, it is not the intent of this section to claim rights or contest
635 your rights to work written entirely by you; rather, the intent is to
636 exercise the right to control the distribution of derivative or
637 collective works based on the Program.
639 In addition, mere aggregation of another work not based on the Program
640 with the Program (or with a work based on the Program) on a volume of
641 a storage or distribution medium does not bring the other work under
642 the scope of this License.
644 3. You may copy and distribute the Program (or a work based on it,
645 under Section 2) in object code or executable form under the terms of
646 Sections 1 and 2 above provided that you also do one of the following:
648 a) Accompany it with the complete corresponding machine-readable
649 source code, which must be distributed under the terms of Sections
650 1 and 2 above on a medium customarily used for software interchange; or,
652 b) Accompany it with a written offer, valid for at least three
653 years, to give any third party, for a charge no more than your
654 cost of physically performing source distribution, a complete
655 machine-readable copy of the corresponding source code, to be
656 distributed under the terms of Sections 1 and 2 above on a medium
657 customarily used for software interchange; or,
659 c) Accompany it with the information you received as to the offer
660 to distribute corresponding source code. (This alternative is
661 allowed only for noncommercial distribution and only if you
662 received the program in object code or executable form with such
663 an offer, in accord with Subsection b above.)
665 The source code for a work means the preferred form of the work for
666 making modifications to it. For an executable work, complete source
667 code means all the source code for all modules it contains, plus any
668 associated interface definition files, plus the scripts used to
669 control compilation and installation of the executable. However, as a
670 special exception, the source code distributed need not include
671 anything that is normally distributed (in either source or binary
672 form) with the major components (compiler, kernel, and so on) of the
673 operating system on which the executable runs, unless that component
674 itself accompanies the executable.
676 If distribution of executable or object code is made by offering
677 access to copy from a designated place, then offering equivalent
678 access to copy the source code from the same place counts as
679 distribution of the source code, even though third parties are not
680 compelled to copy the source along with the object code.
683 4. You may not copy, modify, sublicense, or distribute the Program
684 except as expressly provided under this License. Any attempt
685 otherwise to copy, modify, sublicense or distribute the Program is
686 void, and will automatically terminate your rights under this License.
687 However, parties who have received copies, or rights, from you under
688 this License will not have their licenses terminated so long as such
689 parties remain in full compliance.
691 5. You are not required to accept this License, since you have not
692 signed it. However, nothing else grants you permission to modify or
693 distribute the Program or its derivative works. These actions are
694 prohibited by law if you do not accept this License. Therefore, by
695 modifying or distributing the Program (or any work based on the
696 Program), you indicate your acceptance of this License to do so, and
697 all its terms and conditions for copying, distributing or modifying
698 the Program or works based on it.
700 6. Each time you redistribute the Program (or any work based on the
701 Program), the recipient automatically receives a license from the
702 original licensor to copy, distribute or modify the Program subject to
703 these terms and conditions. You may not impose any further
704 restrictions on the recipients' exercise of the rights granted herein.
705 You are not responsible for enforcing compliance by third parties to
708 7. If, as a consequence of a court judgment or allegation of patent
709 infringement or for any other reason (not limited to patent issues),
710 conditions are imposed on you (whether by court order, agreement or
711 otherwise) that contradict the conditions of this License, they do not
712 excuse you from the conditions of this License. If you cannot
713 distribute so as to satisfy simultaneously your obligations under this
714 License and any other pertinent obligations, then as a consequence you
715 may not distribute the Program at all. For example, if a patent
716 license would not permit royalty-free redistribution of the Program by
717 all those who receive copies directly or indirectly through you, then
718 the only way you could satisfy both it and this License would be to
719 refrain entirely from distribution of the Program.
721 If any portion of this section is held invalid or unenforceable under
722 any particular circumstance, the balance of the section is intended to
723 apply and the section as a whole is intended to apply in other
726 It is not the purpose of this section to induce you to infringe any
727 patents or other property right claims or to contest validity of any
728 such claims; this section has the sole purpose of protecting the
729 integrity of the free software distribution system, which is
730 implemented by public license practices. Many people have made
731 generous contributions to the wide range of software distributed
732 through that system in reliance on consistent application of that
733 system; it is up to the author/donor to decide if he or she is willing
734 to distribute software through any other system and a licensee cannot
737 This section is intended to make thoroughly clear what is believed to
738 be a consequence of the rest of this License.
741 8. If the distribution and/or use of the Program is restricted in
742 certain countries either by patents or by copyrighted interfaces, the
743 original copyright holder who places the Program under this License
744 may add an explicit geographical distribution limitation excluding
745 those countries, so that distribution is permitted only in or among
746 countries not thus excluded. In such case, this License incorporates
747 the limitation as if written in the body of this License.
749 9. The Free Software Foundation may publish revised and/or new versions
750 of the General Public License from time to time. Such new versions will
751 be similar in spirit to the present version, but may differ in detail to
752 address new problems or concerns.
754 Each version is given a distinguishing version number. If the Program
755 specifies a version number of this License which applies to it and "any
756 later version", you have the option of following the terms and conditions
757 either of that version or of any later version published by the Free
758 Software Foundation. If the Program does not specify a version number of
759 this License, you may choose any version ever published by the Free Software
762 10. If you wish to incorporate parts of the Program into other free
763 programs whose distribution conditions are different, write to the author
764 to ask for permission. For software which is copyrighted by the Free
765 Software Foundation, write to the Free Software Foundation; we sometimes
766 make exceptions for this. Our decision will be guided by the two goals
767 of preserving the free status of all derivatives of our free software and
768 of promoting the sharing and reuse of software generally.
772 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
773 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
774 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
775 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
776 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
777 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
778 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
779 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
780 REPAIR OR CORRECTION.
782 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
783 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
784 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
785 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
786 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
787 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
788 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
789 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
790 POSSIBILITY OF SUCH DAMAGES.
795 Gtk::AboutDialog.set_url_hook { |dialog, url| open_url(url) }
796 Gtk::AboutDialog.show($main_window, { :name => 'booh',
797 :version => $VERSION,
798 :copyright => 'Copyright (c) 2005-2008 Guillaume Cottenceau',
799 :license => get_license,
800 :website => 'http://booh.org/',
801 :authors => [ 'Guillaume Cottenceau' ],
802 :artists => [ 'Ayo73' ],
803 :comments => utf8(_("''The Web-Album of choice for discriminating Linux users''")),
804 :translator_credits => utf8(_('Esperanto: Stephane Fillod
805 Japanese: Masao Mutoh
806 German: Roland Eckert
807 French: Guillaume Cottenceau')),
808 :logo => Gdk::Pixbuf.new("#{$FPATH}/images/logo.png") })
811 def smartsort(entries, sort_criterions)
812 #- sort "entries" according to "sort_criterions" but find a good fallback for all entries without a
813 #- criterion value (still next to the item they were next to)
814 sorted_entries = sort_criterions.keys.sort { |a,b| sort_criterions[a] <=> sort_criterions[b] }
815 for i in 0 .. entries.size - 1
816 if ! sorted_entries.include?(entries[i])
818 while j > 0 && ! sorted_entries.include?(entries[j])
821 sorted_entries[(sorted_entries.index(entries[j]) || -1 ) + 1, 0] = entries[i]
824 return sorted_entries
830 def File.reduce_path(path)
831 return path.gsub(/\w+\/\.\.\//, '')
836 def collect_with_index
838 each_with_index { |e,i|
848 each { |v| retval += v.to_i }
854 def previous_element_byname(name)
856 while n = n.previous_element
864 def previous_element_byname_notattr(name, attr)
866 while n = n.previous_element
867 if n.name == name && !n.attributes[attr]
874 def next_element_byname(name)
876 while n = n.next_element
884 def next_element_byname_notattr(name, attr)
886 while n = n.next_element
887 if n.name == name && !n.attributes[attr]
894 def child_byname_notattr(name, attr)
895 elements.each(name) { |element|
896 if !element.attributes[attr]