diff options
Diffstat (limited to 'bin/subtle-contrib/ruby/graviton.rb')
-rw-r--r-- | bin/subtle-contrib/ruby/graviton.rb | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/bin/subtle-contrib/ruby/graviton.rb b/bin/subtle-contrib/ruby/graviton.rb new file mode 100644 index 0000000..09d8a16 --- /dev/null +++ b/bin/subtle-contrib/ruby/graviton.rb @@ -0,0 +1,481 @@ +#!/usr/bin/ruby +# +# @file Graviton +# +# @copyright (c) 2010-2011, Christoph Kappel <unexist@dorfelite.net> +# @version $Id$ +# +# This program can be distributed under the terms of the GNU GPLv2. +# See the file COPYING for details. +# +# Graviton is a helper to create custom gravities +# +# http://subforge.org/projects/subtle-contrib/wiki/Graviton +# + +begin + require "subtle/subtlext" +rescue LoadError + puts ">>> ERROR: Couldn't find subtlext" + exit +end + +# Check for subtlext version +major, minor, teeny = Subtlext::VERSION.split(".").map(&:to_i) +if 0 == major and 9 == minor and 2829 > teeny + puts ">>> ERROR: launcher needs at least subtle `0.9.2829' (found: %s)" % [ + Subtlext::VERSION + ] + exit +end + +begin + require "gtk2" +rescue LoadError + puts <<EOF +>>> ERROR: Couldn't find the gem `gtk2' +>>> Please install it with following command: +>>> gem install gtk2 +EOF + exit +end + +# Styler class +module Subtle # {{{ + module Contrib # {{{ + # Colors + COLORS = { + :cyan => "#00FFFF", + :green => "#008000", + :olive => "#808000", + :teal => "#008080", + :blue => "#0000FF", + :silver => "#C0C0C0", + :lime => "#00FF00", + :navy => "#000080", + :purple => "#800080", + :magenta => "#FF00FF", + :maroon => "#800000", + :red => "#FF0000", + :yellow => "#FFFF00", + :gray => "#808080" + } + + # Rectangle class + class Rectangle # {{{ + attr_accessor :x, :y, :width, :height, :color + + ## initialize {{{ + # Create a new rectangle + # @param [Fixnum] x X position + # @param [Fixnum] y Y position + # @param [Fixnum] width Rectangle width + # @param [Fixnum] height Rectangle height + ## + + def initialize(x, y, width, height, color) + @x = x + @y = y + @width = width + @height = height + @color = color + end # }}} + + ## normalize # {{{ + # Normalize width/height and set left top border to origin + ## + + def normalize + if 0 > @width + @width *= -1 + @x -= @width + end + + if 0 > @height + @height *= -1 + @y -= @height + end + end # }}} + + ## is_edge? {{{ + # Check if x/y is edge of rectangle + # @param [Fixnum] x X position + # @param [Fixnum] y Y position + # @return [Symbol, nil] Selected edge or nil + ## + + def is_edge?(x, y) + if x == @x and y == @y + :top_left + elsif x == (@x + @width) and y == @y + :top_right + elsif x == @x and y == (@y + @height) + :bottom_left + elsif x == (@x + @width) and y == (@y + @height) + :bottom_right + else + nil + end + end # }}} + + ## to_gravity {{{ + # Calculate gravity from rectangle values for given width/height + # @param [Fixnum] width Width for calculation + # @param [Fixnum] height Height for calculation + + def to_gravity(width, height) + x = ratio(@x, width) + y = ratio(@y, height) + w = ratio(@width, width) + h = ratio(@height, height) + + "gravity :%s, [ %d, %d, %d, %d ]" % [ @color, x, y, w, h ] + end # }}} + + ## to_s {{{ + # Convert rectable to string + ## + + def to_s + "x=%d, y=%d, width=%d, height=%d" % [ @x, @y, @width, @height ] + end # }}} + + private + + def ratio(value, ratio) # {{{ + (value.to_f * 100.0 / ratio).to_i + end # }}} + end # }}} + + # Graviton class + class Graviton < Gtk::Window # {{{ + + ## initialize {{{ + # Init window + ## + + def initialize + super + + # Init sizes + @geom = Subtlext::Screen.current.geometry + @gridsize = 20 + @panel_x = 7 + @panel_y = 7 + @panel_width = @geom.width / 3 + @panel_height = @geom.height / 3 + + # Shrink grid + @panel_width -= @panel_width % @gridsize + @panel_height -= @panel_height % @gridsize + + @grid_x = @panel_width / @gridsize + @grid_y = @panel_height / @gridsize + + @window_width = @panel_width + 14 + @window_height = @panel_height + 44 + + # Variables + @rectangles = [] + @cur_rect = nil + @cur_edge = nil + @sx = 0 + @sy = 0 + @x = 0 + @y = 0 + + # Options + set_title("Graviton for subtle #{Subtlext::VERSION}") + set_wmclass("graviton", "subtle") + set_resizable(false) + set_keep_above(true) + set_size_request(@window_width, @window_height) + set_window_position(Gtk::Window::POS_CENTER) + stick + + # Signals + signal_connect("delete_event") do + false + end + + signal_connect("destroy") do + Gtk.main_quit + end + + # Alignment + align = Gtk::Alignment.new(0.5, 0.5, 0.5, 0.5) + align.set_padding(7, 7, 7, 7) + + add(align) + + # Vbox + vbox = Gtk::VBox.new + align.add(vbox) + + # Frame + frame = Gtk::Frame.new + frame.set_border_width(0) + frame.set_shadow_type(Gtk::SHADOW_NONE) + vbox.pack_start(frame) + + # Area + @area = Gtk::DrawingArea.new + @area.set_size_request(@panel_width, @panel_height) + @area.add_events( + Gdk::Event::POINTER_MOTION_MASK| + Gdk::Event::BUTTON1_MOTION_MASK| + Gdk::Event::BUTTON_PRESS_MASK| + Gdk::Event::BUTTON_RELEASE_MASK| + Gdk::Event::POINTER_MOTION_HINT_MASK + ) + frame.add(@area) + + # Create context and surface + @surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, + @panel_width, @panel_width + ) + @ctxt = Cairo::Context.new(@surface) + + # Area events + @area.signal_connect("expose_event") do |*args| + expose_event(*args) + end + + @area.signal_connect("motion_notify_event") do |*args| + motion_event(*args) + end + + @area.signal_connect("button_press_event") do |*args| + press_event(*args) + end + + @area.signal_connect("button_release_event") do |*args| + release_event(*args) + end + + # Hbox + hbox = Gtk::HButtonBox.new + vbox.pack_start(hbox, false, false, 2) + + # Print button + button = Gtk::Button.new("Print") + hbox.pack_start(button) + + button.signal_connect("clicked") do |*args| + button_print(*args) + end + + # Reset button + button = Gtk::Button.new("Reset") + hbox.pack_start(button) + + button.signal_connect("clicked") do + @rectangles = [] + @area.signal_emit("expose_event", nil) + end + + # Exit button + button = Gtk::Button.new("Exit") + hbox.pack_start(button) + + button.signal_connect("clicked") do + Gtk.main_quit + end + + show_all + end # }}} + + private + + def expose_event(widget, event) # {{{ + # Clear + @ctxt.set_source_rgba(0.0, 0.0, 0.0, 1.0) + @ctxt.set_operator(Cairo::OPERATOR_SOURCE) + @ctxt.paint + + # Grid + @ctxt.set_line_width(1) + @ctxt.set_source_rgba(0.6, 0.6, 0.6, 0.4) + + # Vertical lines + (0..@panel_width).step(@grid_x) do |x| + @ctxt.move_to(x, 0) + @ctxt.line_to(x, @panel_height) + end + + # Horizontal lines + (0..@panel_height).step(@grid_y) do |y| + @ctxt.move_to(0, y) + @ctxt.line_to(@panel_width, y) + end + @ctxt.stroke + + # Center + @ctxt.set_source_rgba(0.6, 0.0, 0.0, 0.4) + x = @grid_x * (@gridsize / 2) + y = @grid_y * (@gridsize / 2) + + # Vertical line + @ctxt.move_to(x, 0) + @ctxt.line_to(x, @panel_height) + + # Horizontal line + @ctxt.move_to(0, y) + @ctxt.line_to(@panel_width, y) + @ctxt.stroke + + # Rectangles + @ctxt.set_line_width(1.0) + @rectangles.each do |r| + #@ctxt.set_dash((@cur_rect == r ? 2 : 0)) + @ctxt.set_source_color(Gdk::Color.parse(COLORS[r.color])) + @ctxt.rectangle(r.x, r.y, r.width, r.height) + + # Cross + @ctxt.move_to(r.x, r.y) + @ctxt.line_to(r.x + r.width, r.y + r.height) + @ctxt.move_to(r.x + r.width, r.y) + @ctxt.line_to(r.x, r.y + r.height) + @ctxt.stroke + end + + # Position + @ctxt.move_to(@x, @y) + @ctxt.set_source_color(Gdk::Color.parse("#ff0000")) + @ctxt.rectangle(@x - 2, @y - 2, 4, 4) + @ctxt.stroke + + # Swap context + ctxt = widget.window.create_cairo_context + ctxt.set_source(@surface) + ctxt.paint + end # }}} + + def motion_event(widget, event) # {{{ + # Snap to closest grid knot + modx = event.x % @grid_x + mody = event.y % @grid_y + + @x = @grid_x / 2 < modx ? event.x - modx + @grid_x : event.x - modx + @y = @grid_y / 2 < mody ? event.y - mody + @grid_y : event.y - mody + + # Calculate new width/height + unless @cur_rect.nil? + case @cur_edge + when :top_left + @cur_rect.x = @x + @cur_rect.y = @y + @cur_rect.width = @sx - @x + @cur_rect.height = @sy - @y + when :top_right + @cur_rect.x = @sx + @cur_rect.y = @y + @cur_rect.width = @x - @sx + @cur_rect.height = @sy - @y + when :bottom_left + @cur_rect.x = @x + @cur_rect.y = @sy + @cur_rect.width = @sx - @x + @cur_rect.height = @y - @sy + when :bottom_right + @cur_rect.x = @sx + @cur_rect.y = @sy + @cur_rect.width = @x - @sx + @cur_rect.height = @y - @sy + end + end + + expose_event(widget, event) + end # }}} + + def press_event(widget, event) # {{{ + if 1 == event.button + # Find rectangles by edge + @rectangles.each do |r| + case r.is_edge?(@x, @y) + when :top_left + @cur_rect = r + @cur_edge = :top_left + @sx = r.x + r.width + @sy = r.y + r.height + + return + when :top_right + @cur_rect = r + @cur_edge = :top_right + @sx = r.x + @sy = r.y + r.height + + return + when :bottom_left + @cur_rect = r + @cur_edge = :bottom_left + @sx = r.x + r.width + @sy = r.y + + return + when :bottom_right + @cur_rect = r + @cur_edge = :bottom_right + @sx = r.x + @sy = r.y + + return + else + end + end + + if @rectangles.size < COLORS.size + # Create new rectangle + @mode = :resize + @cur_edge = :bottom_right + @cur_rect = Rectangle.new( + @x, @y, 20, 20, + COLORS.keys[@rectangles.size] + ) + @rectangles << @cur_rect + + @sx = @x + @sy = @y + @x += @gridsize + @y += @gridsize + + expose_event(widget, event) + else + puts ">>> ERROR: Out of colors" + end + end + end # }}} + + def release_event(widget, event) # {{{ + if 1 == event.button + @cur_rect.normalize unless @cur_rect.nil? + @cur_rect = nil + @cur_edge = nil + @sx = 0 + @sy = 0 + end + end # }}} + + def button_print(widget) # {{{ + puts + + # Print rectangles + @rectangles.each do |r| + puts r.to_gravity(@panel_width, @panel_height) + end + + puts + end # }}} + end # }}} + end # }}} +end # }}} + +# Implicitly run< +if __FILE__ == $0 + Gtk.init + Subtle::Contrib::Graviton.new + Gtk.main +end + +# vim:ts=2:bs=2:sw=2:et:fdm=marker |