[Kde-bindings] [qtruby/qtruby-properties] src: * Add patch from Sven Moritz Hallberg to implement Q_PROPERTYs in

Richard Dale richard.dale at codethink.co.uk
Thu May 19 16:12:49 UTC 2011


Git commit 88fa3ff131217aa81e14bd516ec3f0cf27e338e9 by Richard Dale.
Committed on 19/05/2011 at 18:10.
Pushed by rdale into branch 'qtruby-properties'.

* Add patch from Sven Moritz Hallberg to implement Q_PROPERTYs in
QtRuby into a branch 'qtruby-properties'. Thanks for the patch!

CCMAIL: kde-bindings at kde.org

M  +229  -9    src/lib/Qt/qtruby4.rb     
M  +13   -5    src/qtruby.cpp     

http://commits.kde.org/qtruby/88fa3ff131217aa81e14bd516ec3f0cf27e338e9

diff --git a/src/lib/Qt/qtruby4.rb b/src/lib/Qt/qtruby4.rb
index 4920d1c..8f9323b 100644
--- a/src/lib/Qt/qtruby4.rb
+++ b/src/lib/Qt/qtruby4.rb
@@ -56,6 +56,30 @@ module Qt
 		MethodCompatibility = 0x10
 		MethodCloned = 0x20
 		MethodScriptable = 0x40
+
+		#
+		# From the enum PropertyFlags in qt-copy/src/tools/moc/generator.cpp
+		#
+		Invalid = 0x00000000
+		Readable = 0x00000001
+		Writable = 0x00000002
+		Resettable = 0x00000004
+		EnumOrFlag = 0x00000008
+		StdCppSet = 0x00000100
+		#Override = 0x00000200
+		Constant = 0x00000400
+		Final = 0x00000800
+		Designable = 0x00001000
+		ResolveDesignable = 0x00002000
+		Scriptable = 0x00004000
+		ResolveScriptable = 0x00008000
+		Stored = 0x00010000
+		ResolveStored = 0x00020000
+		Editable = 0x00040000
+		ResolveEditable = 0x00080000
+		User = 0x00100000
+		ResolveUser = 0x00200000
+		Notify = 0x00400000
 	end
 	
 	class Base
@@ -77,6 +101,12 @@ module Qt
 			meta.changed = true
 		end
 
+		def self.properties(*propertylist)
+			meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self)
+			meta.add_properties(propertylist)
+			meta.changed = true
+		end
+
 		def self.q_signal(signal)
 			meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self)
 			meta.add_signals([signal], Internal::MethodSignal | Internal::AccessProtected)
@@ -89,6 +119,12 @@ module Qt
 			meta.changed = true
 		end
 
+		def self.q_property(property)
+			meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self)
+			meta.add_properties([property])
+			meta.changed = true
+		end
+
 		def self.q_classinfo(key, value)
 			meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self)
 			meta.add_classinfo(key, value)
@@ -242,6 +278,54 @@ module Qt
 			ids.each { |c| Qt::Internal::findAllMethodNames(meths, c, flags) }
 			return meths.uniq
 		end
+
+		# helper to qt_metacall (from qtruby.cpp), for ReadProperty
+		def qt_readprop(idx)
+			meta = Qt::Meta[self.class.name] || Qt::MetaInfo.new(self.class)
+			idx -= metaObject.propertyOffset
+			if idx >= 0 && idx < meta.properties.length
+				prop = meta.properties[idx]
+				if prop.read
+					begin
+						ret = self.send(prop.read)
+					rescue NoMethodError
+						qWarning("#{self.class.name}: READ method #{prop.read.inspect} for property #{prop.name.inspect} not defined")
+					rescue Exception
+						qWarning("#{self.class.name}: READ method #{prop.read.inspect} for property #{prop.name.inspect} raised #{$!}")
+					end
+					#Qt::Variant.new(ret)	 # no-op if ret is already a Qt::Variant ?
+					ret.is_a?(Qt::Variant) ? ret : Qt::Variant.new(ret)
+				else
+					# shouldn't happen
+					qWarning("#{self.class.name}: no READ method found for property #{prop.name.inspect}")
+				end
+			else
+				# not our property, ignore. qt_metacall will return >=0.
+			end
+		end
+
+		# helper to qt_metacall (from qtruby.cpp), for WriteProperty
+		def qt_writeprop(idx, var)
+			meta = Qt::Meta[self.class.name] || Qt::MetaInfo.new(self.class)
+			idx -= metaObject.propertyOffset
+			if idx >= 0 && idx < meta.properties.length
+				prop = meta.properties[idx]
+				if prop.write
+					begin
+						val = prop.type == "QVariant" ? var : var.value
+						self.send(prop.write, val)
+					rescue NoMethodError
+						qWarning("#{self.class.name}: WRITE method #{prop.write.inspect} for property #{prop.name.inspect} not defined")
+					rescue Exception
+						qWarning("#{self.class.name}: WRITE method #{prop.write.inspect} for property #{prop.name.inspect} raised #{$!}")
+					end
+				else
+					qWarning("#{self.class.name}: no WRITE method found for property #{prop.name.inspect}")
+				end
+			else
+				# not our property, ignore. qt_metacall will return >=0.
+			end
+		end
 	end # Qt::Base
 	
 	# Provides a mutable numeric class for passing to methods with
@@ -2837,7 +2921,7 @@ module Qt
 			end
 		end
 
-		def Internal.makeMetaData(classname, classinfos, dbus, signals, slots)
+		def Internal.makeMetaData(classname, classinfos, dbus, signals, slots, properties)
 			# Each entry in 'stringdata' corresponds to a string in the
 			# qt_meta_stringdata_<classname> structure.
 			# 'pack_string' is used to convert 'stringdata' into the
@@ -2848,12 +2932,17 @@ module Qt
 
 			# This is used to create the array of uints that make up the
 			# qt_meta_data_<classname> structure in the metaObject
-			data = [1, 								# revision
-					string_table.call(classname), 	# classname
-					classinfos.length, classinfos.length > 0 ? 10 : 0, 	# classinfo
-					signals.length + slots.length, 
-					10 + (2*classinfos.length), 	# methods
-					0, 0, 							# properties
+			num_classinfo	= classinfos.length
+			offs_classinfo	= classinfos.length > 0 ? 10 : 0
+			num_methods		= signals.length + slots.length
+			offs_methods	= 10 + 2*num_classinfo
+			num_properties	= properties.length
+			offs_properties = offs_methods + 5*num_methods
+			data = [1,								# revision
+					string_table.call(classname),	# classname
+					num_classinfo, offs_classinfo,	# classinfo
+					num_methods, offs_methods,		# methods
+					num_properties, offs_properties, # properties
 					0, 0]							# enums/sets
 
 			classinfos.each do |entry|
@@ -2885,6 +2974,39 @@ module Qt
 				end
 			end
 
+			properties.each do |prop|
+				data.push string_table.call(prop.name)
+				data.push string_table.call(prop.type)
+
+				# determine property flags
+				flags = 0
+				flags |= Readable if prop.read
+				flags |= Writable if prop.write
+				# TODO: property flag StdCppSet ?
+				flags |= Resettable if prop.reset
+				# TODO: property flag EnumOrFlag ?
+				flags |= Constant if prop.constant
+				flags |= Final if prop.final
+				flags |= Designable if prop.designable
+				flags |= Scriptable if prop.scriptable
+				flags |= Stored if prop.stored
+				flags |= Editable			 # ? TODO: property flag Editable
+				flags |= User if prop.user
+				flags |= Notify if prop.notify
+				# TODO: property flags Resolve* ?
+
+				data.push flags
+			end
+			properties.each do |prop|
+				if prop.notify
+					notifyId = signals.find_index { |s| s.name == prop.notify }
+					if notifyId.nil?
+						raise "NOTIFY signal #{prop.notify} not found"
+					end
+					data.push(notifyId)
+				end
+			end
+
 			data.push 0		# eod
 
 			return [stringdata.pack(pack_string), data]
@@ -2909,8 +3031,9 @@ module Qt
 				stringdata, data = makeMetaData(	qobject.class.name,
 													meta.classinfos,  
 													meta.dbus,
-													meta.signals, 
-													meta.slots )
+													meta.signals,
+													meta.slots,
+													meta.properties )
 				meta.metaobject = make_metaObject(qobject, parentMeta, stringdata, data)
 				meta.changed = false
 			end
@@ -2977,8 +3100,67 @@ module Qt
 	#  :reply_type is 'int'
 	QObjectMember = Struct.new :name, :full_name, :arg_types, :reply_type, :access
 
+	# An entry for each property
+	class QObjectProperty
+		attr_reader :type, :name
+
+		def initialize(type, name, keys)
+			@type = type
+			@name = name
+			@keys = keys
+
+			raise "READ function required" unless @keys[:read]
+
+			# normalize function attributes
+			@keys[:write] ||= nil
+			@keys[:notify] ||= nil
+			@keys[:reset] ||= nil
+
+			# normalize booleans
+			%w[designable scriptable stored].each { |k|
+				k = k.to_sym
+				if @keys.key?(k)
+					@keys[k] = to_bool(@keys[k])
+				else
+					@keys[k] = true
+				end
+			}
+			%w[user constant final].each { |k|
+				k = k.to_sym
+				@keys[k] = to_bool(@keys[k])
+			}
+		end
+
+		def method_missing(name)
+			if @keys.key?(name)
+				@keys[name]
+			else
+				raise(NoMethodError.new("#{name}: no such property attribute"))
+			end
+		end
+
+		private
+		def to_bool(x)
+			if x.nil?
+				false
+			elsif x.is_a?(String)
+				x_ = x.downcase
+				if %[true 1 yes t].member?(x_)
+					true
+				elsif %[false 0 no f].member?(x_)
+					false
+				else
+					raise "not boolean: #{x}"
+				end
+			else
+				x && true
+			end
+		end
+	end
+
 	class MetaInfo
 		attr_accessor :classinfos, :dbus, :signals, :slots, :metaobject, :mocargs, :changed
+		attr_accessor :properties
 
 		def initialize(klass)
 			Meta[klass.name] = self
@@ -2986,6 +3168,7 @@ module Qt
 			@metaobject = nil
 			@signals = []
 			@slots = []
+			@properties = []
 			@classinfos = []
 			@dbus = false
 			@changed = false
@@ -3045,6 +3228,43 @@ module Qt
 			end
 		end
 
+		def add_properties(propertylist)
+			propertylist.each { |prop|
+				if prop =~ /^([\w,<>:]*)\s+([^\s]*)(.*)/
+					type = $1
+					name = $2
+					keys = $3.split
+
+					begin
+						# turn the keyword list into a key-value hash
+						keyhash = {}
+						while keyword = keys.shift
+							k = keyword.downcase.to_sym
+							case k
+							when :read			then keyhash[k] = keys.shift
+							when :write			then keyhash[k] = keys.shift
+							when :reset			then keyhash[k] = keys.shift
+							when :notify		then keyhash[k] = keys.shift
+							when :designable	then keyhash[k] = keys.shift
+							when :scriptable	then keyhash[k] = keys.shift
+							when :stored		then keyhash[k] = keys.shift
+							when :user			then keyhash[k] = keys.shift
+							when :constant		then keyhash[k] = true
+							when :final			then keyhash[k] = true
+							else raise "unrecognized keyword '#{keyword}'"
+							end
+						end
+
+						@properties.push QObjectProperty.new(type, name, keyhash)
+					rescue
+						qWarning( "#{@klass.name}: Property #{name}: #{$!}" )
+					end
+				else
+					qWarning( "#{@klass.name}: Invalid property format: '#{prop}'" )
+				end
+			}
+		end
+
 		def add_classinfo(key, value)
 			@classinfos.push [key, value]
 			if key == 'D-Bus Interface'
diff --git a/src/qtruby.cpp b/src/qtruby.cpp
index f83d903..f4b1499 100644
--- a/src/qtruby.cpp
+++ b/src/qtruby.cpp
@@ -1464,10 +1464,6 @@ qt_metacall(int /*argc*/, VALUE * argv, VALUE self)
 			o->smoke->classes[o->classId].className );
 	}
 
-    if (_c != QMetaObject::InvokeMetaMethod) {
-		return argv[1];
-	}
-
 	QObject * qobj = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
 	// get obj metaobject with a virtual call
 	const QMetaObject *metaobject = qobj->metaObject();
@@ -1499,7 +1495,19 @@ static QRegExp * rx = 0;
 		QtRuby::InvokeSlot slot(self, rb_intern(name.toLatin1()), mocArgs, _o);
 		slot.next();
 	}
-	
+	else if (_c == QMetaObject::ReadProperty) {
+		VALUE variant = rb_funcall(self, rb_intern("qt_readprop"), 1, argv[1]);
+		smokeruby_object *smoke_variant = value_obj_info(variant);
+		_o[0] = smoke_variant->ptr;
+	}
+	else if (_c == QMetaObject::WriteProperty) {
+		smokeruby_object *smoke_variant = alloc_smokeruby_object(true,
+				qtcore_Smoke, qtcore_Smoke->idClass("QVariant").index, _o[0]);
+		VALUE variant = set_obj_info("Qt::Variant", smoke_variant);
+		rb_funcall(self, rb_intern("qt_writeprop"), 2, argv[1], variant);
+	}
+
+
 	return INT2NUM(id - count);
 }
 



More information about the Kde-bindings mailing list