puts get_mem
end
+def min(v1, v2)
+ return v1 < v2 ? v1 : v2
+end
+def max(v1, v2)
+ return v1 > v2 ? v1 : v2
+end
+
+class Gdk::Color
+ def darker
+ color = dup
+ color.red = max(color.red - 10000, 0)
+ color.green = max(color.green - 10000, 0)
+ color.blue = max(color.blue - 10000, 0)
+ return color
+ end
+ def lighter
+ color = dup
+ color.red = min(color.red + 10000, 65535)
+ color.green = min(color.green + 10000, 65535)
+ color.blue = min(color.blue + 10000, 65535)
+ return color
+ end
+end
+
+$colors = [ Gdk::Color.new(0, 65535, 0),
+ Gdk::Color.new(0, 0, 65535),
+ Gdk::Color.new(65535, 65535, 0),
+ Gdk::Color.new(0, 65535, 65535),
+ Gdk::Color.new(65535, 0, 65535) ]
+
+class Tag
+ attr_accessor :color, :name
+ def initialize(name)
+ @name = name
+ end
+end
+$tags = {}
+
class Entry
@@thumbnails_height = 64
@@max_height = nil
return @@thumbnails_height
end
- attr_accessor :path, :type, :button
+ attr_accessor :path, :type, :angle, :button, :removed, :tagged
def initialize(path, type)
@path = path
return @pixbuf_thumbnail
}
end
+ def free_pixbuf_thumbnail
+ @protect_cleanup.synchronize {
+ if @pixbuf_thumbnail.nil?
+ return false
+ else
+ puts ">>> free_pixbuf_thumbnail #{path}"
+ @pixbuf_thumbnail = nil
+ return true
+ end
+ }
+ end
+
+ def show_bg
+ if removed
+ red = Gdk::Color.new(65535, 0, 0)
+ button.modify_bg(Gtk::StateType::NORMAL, red)
+ button.modify_bg(Gtk::StateType::PRELIGHT, red.lighter)
+ button.modify_bg(Gtk::StateType::ACTIVE, red)
+ elsif tagged
+ button.modify_bg(Gtk::StateType::NORMAL, tagged.color)
+ button.modify_bg(Gtk::StateType::PRELIGHT, tagged.color.lighter)
+ button.modify_bg(Gtk::StateType::ACTIVE, tagged.color)
+ else
+ # TODO need to add proper undo support :/
+ white = Gdk::Color.new(55535, 55535, 55535)
+ button.modify_bg(Gtk::StateType::NORMAL, white)
+ button.modify_bg(Gtk::StateType::PRELIGHT, white.lighter)
+ button.modify_bg(Gtk::StateType::ACTIVE, white)
+ end
+ end
private
def load_into_pixbuf_full
return
end
if @pixbuf_full
- angle = guess_rotate(path)
- if angle != 0
- puts ">>> load_into_pixbuf_full #{path} => rotate #{angle}"
- @pixbuf_full = rotate_pixbuf(@pixbuf_full, angle)
+ if @angle.nil?
+ @angle = guess_rotate(path)
+ end
+ if @angle != 0
+ puts ">>> load_into_pixbuf_full #{path} => rotate #{@angle}"
+ @pixbuf_full = rotate_pixbuf(@pixbuf_full, @angle)
end
if @pixbuf_full.height > @@max_height
#- save a lot of memory, don't store in actual full size
else
@entry = $allentries[index]
end
- update_shown
- #- should "freeze" or something to prevent blinking
- window.clear
- draw
+ redraw
@preloader.run
end
return @index
end
+ def redraw
+ update_shown
+ w, h = window.size
+ window.begin_paint(Gdk::Rectangle.new(0, 0, w, h))
+ window.clear
+ draw
+ window.end_paint
+ end
+
def update_shown
if @entry
width, height = window.size
end
end
+ def show_next
+ if @index < $allentries.size - 1
+ next_entry = $allentries[get_shown_entry + 1]
+ next_entry.button.grab_focus
+ end
+ end
+
end
def check_memory_free_cache_if_needed
end
end
+def show_popup(parent, msg, *options)
+ dialog = Gtk::Dialog.new
+ if options[0]
+ options = options[0]
+ else
+ options = {}
+ end
+ if options[:title]
+ dialog.title = options[:title]
+ else
+ dialog.title = utf8(_("Booh message"))
+ end
+ lbl = Gtk::Label.new
+ if options[:nomarkup]
+ lbl.text = msg
+ else
+ lbl.markup = msg
+ end
+ if options[:centered]
+ lbl.set_justify(Gtk::Justification::CENTER)
+ end
+ if options[:selectable]
+ lbl.selectable = true
+ end
+ if options[:topwidget]
+ dialog.vbox.add(options[0][:topwidget])
+ end
+ if options[:scrolled]
+ sw = Gtk::ScrolledWindow.new(nil, nil)
+ sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
+ sw.add_with_viewport(lbl)
+ dialog.vbox.add(sw)
+ dialog.set_default_size(500, 600)
+ else
+ dialog.vbox.add(lbl)
+ dialog.set_default_size(200, 120)
+ end
+ if options[:bottomwidget]
+ dialog.vbox.add(options[:bottomwidget])
+ end
+ if options[:okcancel]
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL)
+ end
+ dialog.add_button(Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK)
+
+ if options[:pos_centered]
+ dialog.window_position = Gtk::Window::POS_CENTER
+ else
+ dialog.window_position = Gtk::Window::POS_MOUSE
+ end
+
+ if options[:linkurl]
+ linkbut = Gtk::Button.new('')
+ linkbut.child.markup = "<span foreground=\"#00000000FFFF\" underline=\"single\">#{options[0][:linkurl]}</span>"
+ linkbut.signal_connect('clicked') {
+ open_url(options[0][:linkurl] + '/index.html')
+ dialog.response(Gtk::Dialog::RESPONSE_OK)
+ set_mousecursor_normal
+ }
+ linkbut.relief = Gtk::RELIEF_NONE
+ linkbut.signal_connect('enter-notify-event') { set_mousecursor(Gdk::Cursor::HAND2, linkbut); false }
+ linkbut.signal_connect('leave-notify-event') { set_mousecursor(nil, linkbut); false }
+ dialog.vbox.add(Gtk::Alignment.new(0.5, 0.5, 0, 0).add(linkbut))
+ end
+
+ dialog.show_all
+
+ if options[:stuff_connector]
+ options[:stuff_connector].call({ :dialog => dialog })
+ end
+
+ if !options[:not_transient]
+ dialog.transient_for = parent
+ dialog.run { |response|
+ if options[:data_getter]
+ options[:data_getter].call
+ end
+ dialog.destroy
+ if options[:okcancel]
+ return response == Gtk::Dialog::RESPONSE_OK
+ end
+ }
+ else
+ dialog.signal_connect('response') { dialog.destroy }
+ end
+end
+
+def thumbnail_keypressed(entry, event)
+ if event.state & Gdk::Window::MOD1_MASK != 0
+ #- ALT pressed: Alt-Left and Alft-Right rotate
+ if event.keyval == Gdk::Keyval::GDK_Left || event.keyval == Gdk::Keyval::GDK_Right
+ if event.keyval == Gdk::Keyval::GDK_Left
+ entry.angle = (entry.angle - 90) % 360
+ else
+ entry.angle = (entry.angle + 90) % 360
+ end
+ entry.free_pixbuf_full
+ entry.free_pixbuf_main
+ entry.free_pixbuf_thumbnail
+ $mainview.redraw
+ entry.button.set_image(img = Gtk::Image.new(entry.pixbuf_thumbnail))
+ end
+
+ else
+ if event.keyval == Gdk::Keyval::GDK_Delete
+ entry.removed = true
+ entry.tagged = nil
+ entry.show_bg
+ $mainview.show_next
+
+ elsif event.keyval == Gdk::Keyval::GDK_space
+ entry.removed = false
+ entry.tagged = nil
+ entry.show_bg
+ $mainview.show_next
+
+ else
+ char = [ Gdk::Keyval.to_unicode(event.keyval) ].pack("C*")
+ if char =~ /^[a-zA-z0-9]$/
+ tag = $tags[char]
+
+ if tag.nil?
+ vb = Gtk::VBox.new(false, 0)
+ vb.pack_start(entry = Gtk::Entry.new.set_text(char), false, false)
+ vb.pack_start(Gtk::Alignment.new(0.5, 0.5, 0, 0).add(bt = Gtk::Button.new(utf8(_(" Change color ")))))
+ text = nil
+ color = nil
+ bt.signal_connect('clicked') {
+ color = $colors.shift
+ if color.nil?
+ color = Gdk::Color.new(16384 + rand(49151), 16384 + rand(49151), 16384 + rand(49151))
+ end
+ bt.modify_bg(Gtk::StateType::NORMAL, color)
+ bt.modify_bg(Gtk::StateType::PRELIGHT, color)
+ bt.modify_bg(Gtk::StateType::ACTIVE, color.darker)
+ }
+ bt.clicked
+ if show_popup($main_window,
+ utf8(_("You typed the text character '%s', which is not associated with a tag.\nType in the full name of the tag below to create a new one.")) % char,
+ { :okcancel => true, :bottomwidget => vb, :data_getter => proc { text = entry.text },
+ :stuff_connector => proc { |stuff| entry.select_region(0, 0); entry.position = -1;
+ entry.signal_connect('activate') { stuff[:dialog].response(Gtk::Dialog::RESPONSE_OK) } } } )
+ if text.length > 0
+ char = text[0,1] #- in case it changed
+ tag = Tag.new(text)
+ tag.color = color
+ $tags[char] = tag
+ label = Gtk::Label.new.set_markup('<b>(' + char + ')</b>' + text[1..-1]).set_justify(Gtk::Justification::CENTER)
+ $tags_vbox.pack_start(evt = Gtk::EventBox.new.add(label).modify_bg(Gtk::StateType::NORMAL, tag.color).show_all)
+ end
+ end
+
+ else
+ entry.removed = false
+ entry.tagged = tag
+ entry.show_bg
+ $mainview.show_next
+ end
+ end
+ end
+ end
+end
+
def show_entries
e = Thread.new {
t1 = Time.now
$imagesline.pack_start(entry.button.show_all, false, false)
entry.button.signal_connect('clicked') { $mainview.set_shown_entry(i) }
entry.button.signal_connect('focus-in-event') { entry.button.clicked; autoscroll_if_needed(entry.button) }
+ entry.button.signal_connect('key-press-event') { |w, e| thumbnail_keypressed(entry, e) }
if i == 0
entry.button.grab_focus
end
end
Dir.entries(dir).each { |file|
type = entry2type(file)
- if type
-# && $allentries.size < 5
+ if type && $allentries.size < 2
$allentries << Entry.new(File.join(dir, file), type)
end
}
return mb
end
+def reset_tags
+ for child in $tags_vbox.children
+ $tags_vbox.remove(child)
+ end
+ $tags_vbox.pack_start(Gtk::Label.new(utf8(_("Tags list:"))).set_justify(Gtk::Justification::CENTER), false, false)
+end
+
def create_main_window
mb = create_menubar
main_vbox = Gtk::VBox.new(false, 0)
main_vbox.pack_start(mb, false, false)
- main_vbox.pack_start($mainview = MainView.new, true, true)
+ mainview_hbox = Gtk::HBox.new
+ mainview_hbox.pack_start(tags_vbox_vbox = Gtk::VBox.new(false, 0), false, true)
+ tags_vbox_vbox.pack_start($tags_vbox = Gtk::VBox.new(false, 5), false, false)
+ reset_tags
+ mainview_hbox.pack_start($mainview = MainView.new, true, true)
+ main_vbox.pack_start(mainview_hbox, true, true)
$imagesline_sw = Gtk::ScrolledWindow.new(nil, nil)
$imagesline_sw.set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_NEVER)
$imagesline_sw.add_with_viewport($imagesline = Gtk::HBox.new(false, 0))
#- Gdk::Pixbuf#rotate memory leak check (in ruby-gnome2 <= 0.16.0)
-pb = Gdk::Pixbuf.new("#{$FPATH}/images/logo.png")
-1.upto(5) { pb = pb.rotate(Gdk::Pixbuf::ROTATE_CLOCKWISE) }
-GC.start
-mem = get_mem
-1.upto(5) { pb = pb.rotate(Gdk::Pixbuf::ROTATE_CLOCKWISE) }
-GC.start
-mem2 = get_mem
-if mem2 != mem
- puts _("Gdk::Pixbuf#scale memory leak detected (this is normal with unpatched ruby-gnome2 <= 0.16.0). Application would slow down to a crawl, won't proceed.")
- exit 1
-end
+#pb = Gdk::Pixbuf.new("#{$FPATH}/images/logo.png")
+#1.upto(5) { pb = pb.rotate(Gdk::Pixbuf::ROTATE_CLOCKWISE) }
+#GC.start
+#mem = get_mem
+#1.upto(5) { pb = pb.rotate(Gdk::Pixbuf::ROTATE_CLOCKWISE) }
+#GC.start
+#mem2 = get_mem
+#if mem2 != mem
+# puts _("Gdk::Pixbuf#scale memory leak detected (this is normal with unpatched ruby-gnome2 <= 0.16.0). Application would slow down to a crawl, won't proceed.")
+# exit 1
+#end
create_main_window