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 !~ /['"\[\]]/
178 if entry =~ /\.(jpg|jpeg|jpe|gif|bmp|png)$/i && entry !~ /['"\[\]]/
180 elsif !$ignore_videos && entry =~ /\.(mov|avi|mpg|mpeg|mpe|wmv|asx|3gp|mp4)$/i && entry !~ /['"\[\]]/
181 #- might consider using file magic later..
194 finished = Process.wait2
195 $pids.delete(finished[0])
196 $pids = $pids.find_all { |pid| Process.waitpid(pid, Process::WNOHANG) == nil }
200 while $pids && $pids.length > 0
205 #- parallelizable sys
215 if $pids.length == $mproc
223 def get_image_size(fullpath)
228 if $sizes_cache[fullpath].nil?
229 #- identify is slow, try with gdk if available (negligible vs 35ms)
231 if `identify '#{fullpath}'` =~ / JPEG (\d+)x(\d+) /
232 $sizes_cache[fullpath] = { :x => $1.to_i, :y => $2.to_i }
235 format, width, height = Gdk::Pixbuf.get_file_info(fullpath)
237 $sizes_cache[fullpath] = { :x => width, :y => height }
241 return $sizes_cache[fullpath]
247 #- commify from http://pleac.sourceforge.net/ (pleac rulz)
249 n.to_s =~ /([^\.]*)(\..*)?/
250 int, dec = $1.reverse, $2 ? $2 : ""
251 while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2' + _(",") + '\3')
256 def guess_rotate(filename)
257 #- identify is slow, try with libexiv2 if available (4ms vs 35ms)
262 orientation = `identify -format "%[EXIF:orientation]" '#{filename}'`.chomp.to_i
264 orientation = Exif.orientation(filename)
269 elsif orientation == 8
275 #- remove rotate if image is obviously already in portrait (situation can come from gthumb)
276 size = get_image_size(filename)
277 if size && size[:x] < size[:y]
284 def angle_to_exif_orientation(angle)
287 elsif angle == 270 || angle == -90
294 def rotate_pixbuf(pixbuf, angle)
295 return pixbuf.rotate(angle == 90 ? Gdk::Pixbuf::ROTATE_CLOCKWISE :
296 angle == 180 ? Gdk::Pixbuf::ROTATE_UPSIDEDOWN :
297 (angle == 270 || angle == -90) ? Gdk::Pixbuf::ROTATE_COUNTERCLOCKWISE :
298 Gdk::Pixbuf::ROTATE_NONE)
301 def gen_thumbnails_element(orig, xmldirorelem, allow_background, dests)
302 if xmldirorelem.name == 'dir'
303 xmldirorelem = xmldirorelem.elements["*[@filename='#{utf8(File.basename(orig))}']"]
305 gen_thumbnails(orig, allow_background, dests, xmldirorelem, '')
308 def gen_thumbnails_subdir(orig, xmldirorelem, allow_background, dests, type)
309 #- type can be `subdirs' or `thumbnails'
310 gen_thumbnails(orig, allow_background, dests, xmldirorelem, type + '-')
313 def gen_video_thumbnail(orig, colorswap, seektime)
315 #- ignored for the moment. is mplayer subject to blue faces problem?
317 #- it's not possible to specify a basename for the output jpeg file with mplayer (file will be named 00000001.jpg); as this can
318 #- be called from multiple threads, we must come up with a unique directory where to put the file
319 tmpfile = Tempfile.new("boohvideotmp")
322 Dir.mkdir(tmpdirname = tmpfile.path)
324 raise "Tmp directory #{tmpfile.path} already exists"
326 cmd = "mplayer '#{orig}' -nosound -vo jpeg:outdir='#{tmpdirname}' -frames 1 -ss #{seektime} >/dev/null 2>/dev/null"
329 if ! File.exists?("#{tmpdirname}/00000001.jpg")
330 msg 0, _("specified seektime too large? that may also be another probleme. try another value.")
331 Dir.rmdir(tmpdirname)
337 def gen_thumbnails(orig, allow_background, dests, felem, attributes_prefix)
338 if !dests.detect { |dest| !File.exists?(dest['filename']) }
343 dest_dir = make_dest_filename(File.dirname(dests[0]['filename']))
345 if entry2type(orig) == 'image'
347 if whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
348 neworig = "#{dest_dir}/#{File.basename(orig)}-whitebalance#{whitebalance}.jpg"
349 cmd = "booh-fix-whitebalance '#{orig}' '#{neworig}' #{whitebalance}"
351 if File.exists?(neworig)
355 if gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"]
356 neworig = "#{dest_dir}/#{File.basename(orig)}-gammacorrect#{gammacorrect}.jpg"
357 cmd = "booh-gamma-correction '#{orig}' '#{neworig}' #{gammacorrect}"
359 if File.exists?(neworig)
363 rotate = felem.attributes["#{attributes_prefix}rotate"]
365 felem.add_attribute("#{attributes_prefix}rotate", rotate = guess_rotate(orig).to_s)
367 convert_options += "-rotate #{rotate} "
368 if felem.attributes["#{attributes_prefix}enhance"]
369 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
373 if !File.exists?(dest['filename'])
375 cmd ||= "#{$convert} #{convert_options}-size #{dest['size']} -resize '#{dest['size']}>' '#{orig}' '#{dest['filename']}'"
391 elsif entry2type(orig) == 'video'
393 #- seektime is an attribute that allows to specify where the frame to use for the thumbnail must be taken
394 seektime = felem.attributes["#{attributes_prefix}seektime"]
396 felem.add_attribute("#{attributes_prefix}seektime", seektime = "0")
398 seektime = seektime.to_f
399 if rotate = felem.attributes["#{attributes_prefix}rotate"]
400 convert_options += "-rotate #{rotate} "
402 if felem.attributes["#{attributes_prefix}enhance"]
403 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
407 if ! File.exists?(dest['filename'])
408 tmpdir = gen_video_thumbnail(orig, felem && felem.attributes["#{attributes_prefix}color-swap"], seektime)
412 tmpfile = "#{tmpdir}/00000001.jpg"
413 alltmpfiles = [ tmpfile ]
414 if felem && whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
415 if whitebalance.to_f != 0
416 neworig = "#{tmpdir}/whitebalance#{whitebalance}.jpg"
417 cmd = "booh-fix-whitebalance '#{tmpfile}' '#{neworig}' #{whitebalance}"
419 if File.exists?(neworig)
421 alltmpfiles << neworig
425 if felem && gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"]
426 if gammacorrect.to_f != 0
427 neworig = "#{tmpdir}/gammacorrect#{gammacorrect}.jpg"
428 cmd = "booh-gamma-correction '#{tmpfile}' '#{neworig}' #{gammacorrect}"
430 if File.exists?(neworig)
432 alltmpfiles << neworig
436 sys("#{$convert} #{convert_options}-size #{dest['size']} -resize #{dest['size']} '#{tmpfile}' '#{dest['filename']}'")
437 alltmpfiles.each { |file| File.delete(file) }
445 def invornil(obj, methodname)
449 return obj.method(methodname).call
453 def find_subalbum_info_type(xmldir)
454 #- first look for subdirs info; if not, means there is no subdir
455 if xmldir.attributes['subdirs-caption']
462 def find_subalbum_caption_info(xmldir)
463 type = find_subalbum_info_type(xmldir)
464 return [ from_utf8(xmldir.attributes["#{type}-captionfile"]), xmldir.attributes["#{type}-caption"] ]
469 return File.size(path)
480 n < a ? a : n > b ? b : n
483 def pano_amount(elem)
484 if pano_amount = elem.attributes['pano-amount']
486 return clamp(pano_amount.to_f, 1, $N_per_row.to_i)
488 return clamp(pano_amount.to_f, 1, $default_N.to_i)
495 def substInFile(name)
496 newcontent = IO.readlines(name).collect { |l| yield l }
497 ios = File.open(name, "w")
498 ios.write(newcontent)
503 cmd = $config['browser'].gsub('%f', "'#{url}'") + ' &'
510 GNU GENERAL PUBLIC LICENSE
513 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
514 675 Mass Ave, Cambridge, MA 02139, USA
515 Everyone is permitted to copy and distribute verbatim copies
516 of this license document, but changing it is not allowed.
520 The licenses for most software are designed to take away your
521 freedom to share and change it. By contrast, the GNU General Public
522 License is intended to guarantee your freedom to share and change free
523 software--to make sure the software is free for all its users. This
524 General Public License applies to most of the Free Software
525 Foundation's software and to any other program whose authors commit to
526 using it. (Some other Free Software Foundation software is covered by
527 the GNU Library General Public License instead.) You can apply it to
530 When we speak of free software, we are referring to freedom, not
531 price. Our General Public Licenses are designed to make sure that you
532 have the freedom to distribute copies of free software (and charge for
533 this service if you wish), that you receive source code or can get it
534 if you want it, that you can change the software or use pieces of it
535 in new free programs; and that you know you can do these things.
537 To protect your rights, we need to make restrictions that forbid
538 anyone to deny you these rights or to ask you to surrender the rights.
539 These restrictions translate to certain responsibilities for you if you
540 distribute copies of the software, or if you modify it.
542 For example, if you distribute copies of such a program, whether
543 gratis or for a fee, you must give the recipients all the rights that
544 you have. You must make sure that they, too, receive or can get the
545 source code. And you must show them these terms so they know their
548 We protect your rights with two steps: (1) copyright the software, and
549 (2) offer you this license which gives you legal permission to copy,
550 distribute and/or modify the software.
552 Also, for each author's protection and ours, we want to make certain
553 that everyone understands that there is no warranty for this free
554 software. If the software is modified by someone else and passed on, we
555 want its recipients to know that what they have is not the original, so
556 that any problems introduced by others will not reflect on the original
557 authors' reputations.
559 Finally, any free program is threatened constantly by software
560 patents. We wish to avoid the danger that redistributors of a free
561 program will individually obtain patent licenses, in effect making the
562 program proprietary. To prevent this, we have made it clear that any
563 patent must be licensed for everyone's free use or not licensed at all.
565 The precise terms and conditions for copying, distribution and
569 GNU GENERAL PUBLIC LICENSE
570 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
572 0. This License applies to any program or other work which contains
573 a notice placed by the copyright holder saying it may be distributed
574 under the terms of this General Public License. The "Program", below,
575 refers to any such program or work, and a "work based on the Program"
576 means either the Program or any derivative work under copyright law:
577 that is to say, a work containing the Program or a portion of it,
578 either verbatim or with modifications and/or translated into another
579 language. (Hereinafter, translation is included without limitation in
580 the term "modification".) Each licensee is addressed as "you".
582 Activities other than copying, distribution and modification are not
583 covered by this License; they are outside its scope. The act of
584 running the Program is not restricted, and the output from the Program
585 is covered only if its contents constitute a work based on the
586 Program (independent of having been made by running the Program).
587 Whether that is true depends on what the Program does.
589 1. You may copy and distribute verbatim copies of the Program's
590 source code as you receive it, in any medium, provided that you
591 conspicuously and appropriately publish on each copy an appropriate
592 copyright notice and disclaimer of warranty; keep intact all the
593 notices that refer to this License and to the absence of any warranty;
594 and give any other recipients of the Program a copy of this License
595 along with the Program.
597 You may charge a fee for the physical act of transferring a copy, and
598 you may at your option offer warranty protection in exchange for a fee.
600 2. You may modify your copy or copies of the Program or any portion
601 of it, thus forming a work based on the Program, and copy and
602 distribute such modifications or work under the terms of Section 1
603 above, provided that you also meet all of these conditions:
605 a) You must cause the modified files to carry prominent notices
606 stating that you changed the files and the date of any change.
608 b) You must cause any work that you distribute or publish, that in
609 whole or in part contains or is derived from the Program or any
610 part thereof, to be licensed as a whole at no charge to all third
611 parties under the terms of this License.
613 c) If the modified program normally reads commands interactively
614 when run, you must cause it, when started running for such
615 interactive use in the most ordinary way, to print or display an
616 announcement including an appropriate copyright notice and a
617 notice that there is no warranty (or else, saying that you provide
618 a warranty) and that users may redistribute the program under
619 these conditions, and telling the user how to view a copy of this
620 License. (Exception: if the Program itself is interactive but
621 does not normally print such an announcement, your work based on
622 the Program is not required to print an announcement.)
625 These requirements apply to the modified work as a whole. If
626 identifiable sections of that work are not derived from the Program,
627 and can be reasonably considered independent and separate works in
628 themselves, then this License, and its terms, do not apply to those
629 sections when you distribute them as separate works. But when you
630 distribute the same sections as part of a whole which is a work based
631 on the Program, the distribution of the whole must be on the terms of
632 this License, whose permissions for other licensees extend to the
633 entire whole, and thus to each and every part regardless of who wrote it.
635 Thus, it is not the intent of this section to claim rights or contest
636 your rights to work written entirely by you; rather, the intent is to
637 exercise the right to control the distribution of derivative or
638 collective works based on the Program.
640 In addition, mere aggregation of another work not based on the Program
641 with the Program (or with a work based on the Program) on a volume of
642 a storage or distribution medium does not bring the other work under
643 the scope of this License.
645 3. You may copy and distribute the Program (or a work based on it,
646 under Section 2) in object code or executable form under the terms of
647 Sections 1 and 2 above provided that you also do one of the following:
649 a) Accompany it with the complete corresponding machine-readable
650 source code, which must be distributed under the terms of Sections
651 1 and 2 above on a medium customarily used for software interchange; or,
653 b) Accompany it with a written offer, valid for at least three
654 years, to give any third party, for a charge no more than your
655 cost of physically performing source distribution, a complete
656 machine-readable copy of the corresponding source code, to be
657 distributed under the terms of Sections 1 and 2 above on a medium
658 customarily used for software interchange; or,
660 c) Accompany it with the information you received as to the offer
661 to distribute corresponding source code. (This alternative is
662 allowed only for noncommercial distribution and only if you
663 received the program in object code or executable form with such
664 an offer, in accord with Subsection b above.)
666 The source code for a work means the preferred form of the work for
667 making modifications to it. For an executable work, complete source
668 code means all the source code for all modules it contains, plus any
669 associated interface definition files, plus the scripts used to
670 control compilation and installation of the executable. However, as a
671 special exception, the source code distributed need not include
672 anything that is normally distributed (in either source or binary
673 form) with the major components (compiler, kernel, and so on) of the
674 operating system on which the executable runs, unless that component
675 itself accompanies the executable.
677 If distribution of executable or object code is made by offering
678 access to copy from a designated place, then offering equivalent
679 access to copy the source code from the same place counts as
680 distribution of the source code, even though third parties are not
681 compelled to copy the source along with the object code.
684 4. You may not copy, modify, sublicense, or distribute the Program
685 except as expressly provided under this License. Any attempt
686 otherwise to copy, modify, sublicense or distribute the Program is
687 void, and will automatically terminate your rights under this License.
688 However, parties who have received copies, or rights, from you under
689 this License will not have their licenses terminated so long as such
690 parties remain in full compliance.
692 5. You are not required to accept this License, since you have not
693 signed it. However, nothing else grants you permission to modify or
694 distribute the Program or its derivative works. These actions are
695 prohibited by law if you do not accept this License. Therefore, by
696 modifying or distributing the Program (or any work based on the
697 Program), you indicate your acceptance of this License to do so, and
698 all its terms and conditions for copying, distributing or modifying
699 the Program or works based on it.
701 6. Each time you redistribute the Program (or any work based on the
702 Program), the recipient automatically receives a license from the
703 original licensor to copy, distribute or modify the Program subject to
704 these terms and conditions. You may not impose any further
705 restrictions on the recipients' exercise of the rights granted herein.
706 You are not responsible for enforcing compliance by third parties to
709 7. If, as a consequence of a court judgment or allegation of patent
710 infringement or for any other reason (not limited to patent issues),
711 conditions are imposed on you (whether by court order, agreement or
712 otherwise) that contradict the conditions of this License, they do not
713 excuse you from the conditions of this License. If you cannot
714 distribute so as to satisfy simultaneously your obligations under this
715 License and any other pertinent obligations, then as a consequence you
716 may not distribute the Program at all. For example, if a patent
717 license would not permit royalty-free redistribution of the Program by
718 all those who receive copies directly or indirectly through you, then
719 the only way you could satisfy both it and this License would be to
720 refrain entirely from distribution of the Program.
722 If any portion of this section is held invalid or unenforceable under
723 any particular circumstance, the balance of the section is intended to
724 apply and the section as a whole is intended to apply in other
727 It is not the purpose of this section to induce you to infringe any
728 patents or other property right claims or to contest validity of any
729 such claims; this section has the sole purpose of protecting the
730 integrity of the free software distribution system, which is
731 implemented by public license practices. Many people have made
732 generous contributions to the wide range of software distributed
733 through that system in reliance on consistent application of that
734 system; it is up to the author/donor to decide if he or she is willing
735 to distribute software through any other system and a licensee cannot
738 This section is intended to make thoroughly clear what is believed to
739 be a consequence of the rest of this License.
742 8. If the distribution and/or use of the Program is restricted in
743 certain countries either by patents or by copyrighted interfaces, the
744 original copyright holder who places the Program under this License
745 may add an explicit geographical distribution limitation excluding
746 those countries, so that distribution is permitted only in or among
747 countries not thus excluded. In such case, this License incorporates
748 the limitation as if written in the body of this License.
750 9. The Free Software Foundation may publish revised and/or new versions
751 of the General Public License from time to time. Such new versions will
752 be similar in spirit to the present version, but may differ in detail to
753 address new problems or concerns.
755 Each version is given a distinguishing version number. If the Program
756 specifies a version number of this License which applies to it and "any
757 later version", you have the option of following the terms and conditions
758 either of that version or of any later version published by the Free
759 Software Foundation. If the Program does not specify a version number of
760 this License, you may choose any version ever published by the Free Software
763 10. If you wish to incorporate parts of the Program into other free
764 programs whose distribution conditions are different, write to the author
765 to ask for permission. For software which is copyrighted by the Free
766 Software Foundation, write to the Free Software Foundation; we sometimes
767 make exceptions for this. Our decision will be guided by the two goals
768 of preserving the free status of all derivatives of our free software and
769 of promoting the sharing and reuse of software generally.
773 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
774 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
775 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
776 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
777 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
778 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
779 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
780 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
781 REPAIR OR CORRECTION.
783 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
784 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
785 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
786 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
787 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
788 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
789 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
790 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
791 POSSIBILITY OF SUCH DAMAGES.
796 Gtk::AboutDialog.set_url_hook { |dialog, url| open_url(url) }
797 Gtk::AboutDialog.show($main_window, { :name => 'booh',
798 :version => $VERSION,
799 :copyright => 'Copyright (c) 2005-2007 Guillaume Cottenceau',
800 :license => get_license,
801 :website => 'http://booh.org/',
802 :authors => [ 'Guillaume Cottenceau' ],
803 :artists => [ 'Ayo73' ],
804 :comments => utf8(_("''The Web-Album of choice for discriminating Linux users''")),
805 :translator_credits => utf8(_('Esperanto: Stephane Fillod
806 Japanese: Masao Mutoh
807 German: Roland Eckert
808 French: Guillaume Cottenceau')),
809 :logo => Gdk::Pixbuf.new("#{$FPATH}/images/logo.png") })
812 def smartsort(entries, sort_criterions)
813 #- sort "entries" according to "sort_criterions" but find a good fallback for all entries without a
814 #- criterion value (still next to the item they were next to)
815 sorted_entries = sort_criterions.keys.sort { |a,b| sort_criterions[a] <=> sort_criterions[b] }
816 for i in 0 .. entries.size - 1
817 if ! sorted_entries.include?(entries[i])
819 while j > 0 && ! sorted_entries.include?(entries[j])
822 sorted_entries[(sorted_entries.index(entries[j]) || -1 ) + 1, 0] = entries[i]
825 return sorted_entries
831 def File.reduce_path(path)
832 return path.gsub(/\w+\/\.\.\//, '')
837 def collect_with_index
839 each_with_index { |e,i|
849 each { |v| retval += v.to_i }
855 def previous_element_byname(name)
857 while n = n.previous_element
865 def previous_element_byname_notattr(name, attr)
867 while n = n.previous_element
868 if n.name == name && !n.attributes[attr]
875 def next_element_byname(name)
877 while n = n.next_element
885 def next_element_byname_notattr(name, attr)
887 while n = n.next_element
888 if n.name == name && !n.attributes[attr]
895 def child_byname_notattr(name, attr)
896 elements.each(name) { |element|
897 if !element.attributes[attr]