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