# Copyright (c) 2009-2010 Paolo Capriotti
#
# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
require 'rui/observer_utils'
require 'rui/descriptor'
require 'rui/toolkits/qtbase/gui_builder'
ParseException = Class.new(Exception)
class Qt::Variant
#
# Convert any marshallable ruby object into a QVariant.
#
def self.from_ruby(x)
new(Marshal.dump(x))
end
#
# Extract the ruby object contained in a QVariant.
#
def to_ruby
str = toString
Marshal.load(str) if str
end
end
class Qt::ByteArray
def self.from_hex(str)
new([str.gsub(/\W+/, '')].pack('H*'))
end
end
class Qt::Painter
#
# Ensure this painter is closed after the block is executed.
#
def paint
yield self
ensure
self.end
end
#
# Execute a block, then restore the painter state to what it
# was before execution.
#
def saving
save
yield self
ensure
restore
end
end
class Qt::Image
#
# Convert this image to a pixmap.
#
def to_pix
Qt::Pixmap.from_image self
end
#
# Paint on an image using the given block. The block is passed
# a painter to use for drawing.
#
def self.painted(size, &blk)
img = Qt::Image.new(size.x, size.y, Qt::Image::Format_ARGB32_Premultiplied)
img.fill(0)
Qt::Painter.new(img).paint(&blk)
img
end
#
# Render an svg object onto a new image of the specified size. If id is not
# specified, the whole svg file is rendered.
#
def self.from_renderer(size, renderer, id = nil)
img = Qt::Image.painted(size) do |p|
if id
renderer.render(p, id)
else
renderer.render(p)
end
end
img
end
end
module PrintablePoint
def ==(other)
self.x == other.x and self.y == other.y
end
def to_s
"(#{self.x}, #{self.y})"
end
end
module PrintableRect
def to_s
"[#{self.x}, #{self.y} - #{self.width}, #{self.height}]"
end
end
class Qt::Point
include PrintablePoint
def to_f
Qt::PointF.new(self.x, self.y)
end
end
class Qt::PointF
include PrintablePoint
def to_i
Qt::Point.new(self.x.to_i, self.y.to_i)
end
end
class Qt::Size
include PrintablePoint
def x
width
end
def y
height
end
end
class Qt::SizeF
include PrintablePoint
def x
width
end
def y
height
end
end
class Qt::Rect
include PrintableRect
def to_f
Qt::RectF.new(self.x, self.y, self.width, self.height)
end
end
class Qt::RectF
include PrintableRect
end
class Qt::Pixmap
#
# Qt > 4.6 provides effects to be applied to Qt::GraphicsItem's.
# Since kaya effects work at a lower level of abstraction (i.e. at
# pixmap/image level), we embed effects directly in a pixmap.
#
# When a pixmap is assigned to a Qt::GraphicsItem, its effects are
# transferred to the item.
#
def effects
@effects ||= []
end
private :effects
#
# Add an effect to this pixmap. If later this pixmap is assigned to an
# Item, all its effects will be transferred to it.
#
def add_effect(effect)
effects << effect
end
#
# Render a pixmap from an svg file. See also Qt::Image#renderer.
#
def self.from_svg(size, file, id = nil)
from_renderer(size, Qt::SvgRenderer.new(file), id)
end
#
# Render a pixmap using an svg renderer. See also Qt::Image#renderer.
#
def self.from_renderer(size, renderer, id = nil)
Qt::Image.from_renderer(size, renderer, id).to_pix
end
def to_pix
self
end
end
class Qt::MetaObject
def create_signal_map
map = {}
(0...methodCount).map do |i|
m = method(i)
if m.methodType == Qt::MetaMethod::Signal
sign = m.signature
sign =~ /^(.*)\(.*\)$/
sig = $1.underscore.to_sym
val = [sign, m.parameterTypes]
map[sig] ||= []
map[sig] << val
end
end
map
end
end
class Qt::Base
include Observable
class SignalDisconnecter
def initialize(obj, sig)
@obj = obj
@sig = sig
end
def disconnect!
@obj.disconnect(@sig)
end
end
class ObserverDisconnecter
def initialize(obj, observer)
@obj = obj
@observer = observer
end
def disconnect!
@obj.delete_observer(@observer)
end
end
class Signal
attr_reader :symbol
def initialize(signal, types)
raise "Only symbols are supported as signals" unless signal.is_a?(Symbol)
@symbol = signal
@types = types
end
def self.create(signal, types)
if signal.is_a?(self)
signal
else
new(signal, types)
end
end
def to_s
@symbol.to_s
end
end
def on(sig, types = nil, &blk)
sig = Signal.create(sig, types)
candidates = if is_a? Qt::Object
signal_map[sig.symbol]
end
if candidates
if types
# find candidate with the correct argument types
candidates = candidates.find_all{|s| s[1] == types }
end
if candidates.size > 1
# find candidate with the correct arity
arity = blk.arity
if blk.arity == -1
# take first
candidates = [candidates.first]
else
candidates = candidates.find_all{|s| s[1].size == arity }
end
end
if candidates.size > 1
raise "Ambiguous overload for #{sig} with arity #{arity}"
elsif candidates.empty?
msg = if types
"with types #{types.join(' ')}"
else
"with arity #{blk.arity}"
end
raise "No overload for #{sig} #{msg}"
end
sign = SIGNAL(candidates.first[0])
connect(sign, &blk)
SignalDisconnecter.new(self, sign)
else
observer = observe(sig.symbol, &blk)
ObserverDisconnecter.new(self, observer)
end
end
def in(interval, &blk)
Qt::Timer.in(interval, self, &blk)
end
def run_later(&blk)
self.in(0, &blk)
end
def signal_map
self.class.signal_map(self)
end
def self.signal_map(obj)
@signal_map ||= self.create_signal_map(obj)
end
def self.create_signal_map(obj)
obj.meta_object.create_signal_map
end
def gui=(g)
setGUI(g)
end
end
class Qt::Timer
#
# Execute the given block every interval milliseconds and return a timer
# object. Note that if the timer is garbage collected, the block will not
# be executed anymore, so the caller should keep a reference to it for as
# long as needed.
# To prevent further invocations of the block, use QTimer#stop.
#
def self.every(interval, &blk)
time = Qt::Time.new
time.restart
timer = new
timer.connect(SIGNAL('timeout()')) { blk[time.elapsed] }
timer.start(interval)
# return the timer, so that the caller
# has a chance to keep it referenced, so
# that it is not garbage collected
timer
end
#
# Execute the given block after interval milliseconds. If target is
# specified, the block is invoked in the context of target.
#
def self.in(interval, target = nil, &blk)
single_shot(interval,
Qt::BlockInvocation.new(target, blk, 'invoke()'),
SLOT('invoke()'))
end
end
module ListLike
module ClassMethods
#
# Create a list from an array of pairs (text, data)
# The data for each item can be retrieved using the
# item's get method.
# Note that if an array element is not a pair, its
# value will be used both for the text and for the
# data.
#
# For example: list.current_item.get
#
def from_a(parent, array)
list = new(parent)
list.reset_from_a(array)
list
end
end
#
# Select the item for which the given block
# evaluates to true.
#
def select_item(&blk)
(0...count).each do |i|
if blk[item(i).get]
self.current_index = i
break i
end
end
nil
end
#
# Populate the list with values from an array.
# See also from_a.
#
def reset_from_a(array)
clear
array.each do |values|
text, data = if values.is_a?(String)
[values, values]
else
values
end
create_item(text, data)
end
end
def self.included(base)
base.extend ClassMethods
end
end
class Qt::ListWidget
FROM_A_DATA_ROLE = Qt::UserRole
include ListLike
class Item < Qt::ListWidgetItem
def initialize(text, list, data)
super(text, list)
set_data(FROM_A_DATA_ROLE, Qt::Variant.from_ruby(data))
end
def get
data(FROM_A_DATA_ROLE).to_ruby
end
end
def current_index=(i)
self.current_row = i
end
def create_item(text, data)
Item.new(text, self, data)
end
end
class Qt::FileDialog
def self.get_open_url(dir, filter, parent, caption)
filename = get_open_file_name(parent, caption, dir.to_local_file, filter)
Qt::Url.from_local_file(filename)
end
def self.get_save_url(dir, filter, parent, caption)
filename = get_save_file_name(parent, caption, dir.to_local_file, filter)
Qt::Url.from_local_file(filename)
end
end
class Qt::Url
def is_local_file
true
end
end
module ModelUtils
#
# Helper method to delete model rows from within a block. This method
# ensures that the appropriate begin/end functions are called.
#
def removing_rows(parent, first, last)
if first > last
yield
else
begin
begin_remove_rows(parent || Qt::ModelIndex.new, first, last)
yield
ensure
end_remove_rows
end
end
end
#
# Helper method to insert model rows from within a block. This method
# ensures that the appropriate begin/end functions are called.
#
def inserting_rows(parent, first, last)
if first > last
yield
else
begin
begin_insert_rows(parent || Qt::ModelIndex.new, first, last)
yield
ensure
end_insert_rows
end
end
end
end
module Layoutable
attr_writer :owner
attr_accessor :main_layout
def add_layout(layout)
self.layout = layout
owner.main_layout = layout
end
def add_widget(w)
end
def add_accessor(name, result)
owner.metaclass_eval do
define_method(name) { result }
end
end
def buddies
@buddies ||= { }
end
def owner
@owner || self
end
end
class Qt::Widget
include Layoutable
def setGUI(gui)
RUI::GuiBuilder.build(self, gui)
buddies.each do |label, buddy|
label.buddy = owner.__send__(buddy)
end
end
end
class KDE::ComboBox
include ListLike
Item = Struct.new(:get)
def create_item(text, data)
add_item(text, Qt::Variant.from_ruby(data))
end
def current_item
item(current_index)
end
def item(i)
Item.new(item_data(i).to_ruby)
end
end
def KDE.download_tempfile(url, parent)
url.to_local_file
end