#! ruby -Ks
# Phi::ObjectForm
# Kazuhiro Yoshida <moriq@moriq.com>
# PropGrid sample

require 'phi'
require 'dialogs'
require 'pgrid'

module Phi
	class ObjectTree < TreeView
		def initialize(*args)
			node_screen = self.items.add(nil, 'SCREEN')
			node_screen.data = SCREEN
			SCREEN.forms.each do |i|
				control_p i, node_screen 
				menu_node i.menu.items, node_screen if i.menu
			end
			self.full_expand
		end
		
		def component_p(this, node=nil)
			if this.is_a? Component
				obj_name = Phi.downcase(this.name)
				class_name = this.class.name
			else
				obj_name = 'unknown'
				class_name = this.inspect
			end
			node = self.items.add_child node, "#{obj_name}: #{class_name}"
			node.data = this
		end
		
		def control_p(this, node=nil)
			this.components.each {|i|
				next unless i
				next if i.is_a? Control
				component_p i, node
			}
			if this.is_a? Control
				obj_name = Phi.downcase(this.name)
				class_name = this.class.name
			else
				obj_name = 'unknown: '
				class_name = this.inspect
			end
			node = self.items.add_child node, "#{obj_name}: #{class_name}"
			node.data = this
			if this.is_a? WinControl
				this.controls.each {|i| control_p i, node }
			end
		end
		
		def menu_node(this, node=nil)
			if this.is_a? MenuItem
				obj_name = Phi.downcase(this.name)
				class_name = this.class.name
			else
				obj_name = 'unknown: '
				class_name = this.inspect
			end
			node = self.items.add_child node, "#{obj_name}: #{class_name}"
			node.data = this
			if this.is_a? MenuItem
				this.each { |i| menu_node i, node }
			end
		end
	end
	
	class Component
		def self.find_type_info(component_class)
			component_class.ancestors.each do |mod|
				type_info = mod.type_info
				return type_info if type_info
			end
			nil
		end
	end
	
	class ObjectGrid < PropGrid
		def initialize(*args)
			self.boolean_list[0] = 'true'
			self.boolean_list[1] = 'false'
		end
		
		def obj=(obj)
		#	p obj
			target_class = obj.class
			self.items.clear
			if obj.is_a? Component
				@obj = obj
				type_info = Component.find_type_info(target_class)
				self.items.update {
					print_type_info type_info
				}
			end
		end
		
		def print_with_tab(lev, s)
		#	Kernel.print "\t"*lev, s
		end
		
		def print_class_prop_info(pi, lev, obj)
			print_with_tab lev, "name: #{pi.name}\n"
			ti = pi.type_info
		#	p [pi, ti]
			return if pi.name == "Tag"
			item = self.items.add
			item.data = obj
			item.level = lev
			if ti.kind == TK_ENUMERATION && ti.name == "Boolean"
				item.style = PS_BOOLEAN_COMBO_LIST
				item.on_value_change = proc {|sender, name, before_value, after_value|
					sender.data.send(sender.key+'=', after_value[0] == ?t)
					nil
				}
			elsif ti.kind == TK_CLASS && ti.name == "TFont"
				item.style = PS_ELLIPSIS
				item.on_need_value = proc {|sender, (value, canceled), name|
					dialog = FontDialog.new
					dialog.font = sender.data.send(sender.key)
					if dialog.execute
						sender.data.send(sender.key+'=', dialog.font)
					#	sender.data.font = dialog.font
						value = dialog.font.inspect
					else
						canceled = true
					end
					[value, canceled]
				}
			elsif ti.kind == TK_INTEGER && ti.name == "TColor"
				item.style = PS_COLOR_ELLIPSIS
				item.on_need_value = proc {|sender, (value, canceled), name|
					dialog = ColorDialog.new
					if dialog.execute
						sender.data.send(sender.key+'=', dialog.color)
					#	sender.data.color = dialog.color
						value = dialog.color.inspect
					else
						canceled = true
					end
					[value, canceled]
				}
			elsif ti.kind == TK_INTEGER
				item.on_value_change = proc {|sender, name, before_value, after_value|
					sender.data.send(sender.key+'=', after_value.to_i)
					nil
				}
			elsif ti.kind == TK_LSTRING
				item.on_value_change = proc {|sender, name, before_value, after_value|
					sender.data.send(sender.key+'=', after_value)
					nil
				}
			end
			item.key = Phi.downcase(pi.name)
			begin
				value = obj.send(item.key)
				item.value = 
					case ti.kind
					when TK_INTEGER
						value.to_s
					when TK_LSTRING
						value
					else
						value.inspect
					end
			rescue
				value = $!
				item.value = value.inspect
			end
			print_type_info(ti, lev+1, value)
		end
		
		def method_prop_info_to_s(pi)
			pi[0] = '[' << pi[0].collect{|i| TYPE_INFO['TParamFlag'].enum_name(i)}.join(', ') << ']'
			pi.join("\t")
		end
		
		def print_method_prop_info(pi, lev, obj)
			print_with_tab lev+1, "#{method_prop_info_to_s(pi)}\n"
		end
		
		def print_type_data(ti, lev=0, obj=@obj)
			td = ti.type_data
			return if td.nil?
			print_with_tab lev, "=== type_data #{td}\n"
			return if obj.nil?
			case ti.kind
			when TK_INTEGER, TK_CHAR, TK_WCHAR
				print_with_tab lev, "ord_type: #{
					TYPE_INFO['TOrdType'].enum_name(td.ord_type)
				}\n"
				print_with_tab lev, "min_value: #{td.min_value}\n"
				print_with_tab lev, "max_value: #{td.max_value}\n"
			when TK_ENUMERATION
				print_with_tab lev, "ord_type: #{
					TYPE_INFO['TOrdType'].enum_name(td.ord_type)
				}\n"
				print_with_tab lev, "min_value: #{td.min_value}\n"
				print_with_tab lev, "max_value: #{td.max_value}\n"
				print_with_tab lev, "base_type: #{td.base_type}\n"
				print_with_tab lev, '[' << (td.min_value..td.max_value).collect{|i| ti.enum_name(i)}.join(', ') << ']' << "\n"
			#	base_type = td.base_type
			#	base_type.use_type_data
			#	print_type_info(base_type, lev+1)
			when TK_SET
				print_with_tab lev, "ord_type: #{
					TYPE_INFO['TOrdType'].enum_name(td.ord_type)
				}\n"
				print_with_tab lev, "comp_type: #{td.comp_type}\n"
				comp_type = td.comp_type
				comp_type.use_type_data
				print_type_info(comp_type, lev+1)
			when TK_FLOAT
				print_with_tab lev, "float_type: #{
					TYPE_INFO['TFloatType'].enum_name(td.float_type)
				}\n"
			when TK_STRING
				print_with_tab lev, "max_length: #{td.max_length}\n"
			when TK_CLASS
				print_with_tab lev, "class_type: #{td.class_type}\n"
				print_with_tab lev, "parent_info: #{td.parent_info}\n"
				print_with_tab lev, "count: #{td.count}\n"
				td.items.each do |pi|
					print_class_prop_info(pi, lev, obj)
				end
				print_with_tab lev, "unit_name: #{td.unit_name}\n"
			when TK_METHOD
				print_with_tab lev, "method_kind: #{
					TYPE_INFO['TMethodKind'].enum_name(td.method_kind)
				}\n"
				print_with_tab lev, "count: #{td.count}\n"
				td.items.each do |pi|
					print_method_prop_info(pi, lev, obj)
				end
			when TK_INT64
				print_with_tab lev, "min_value: #{td.min_value}\n"
				print_with_tab lev, "max_value: #{td.max_value}\n"
			end
		end
		
		def print_type_info(ti, lev=0, obj=@obj)
			return if ti.nil?
			print_with_tab lev, "=== type_info #{ti}\n"
			print_with_tab lev, "kind: #{
				TYPE_INFO['TTypeKind'].enum_name(ti.kind)
			}\n"
			print_with_tab lev, "name: #{ti.name}\n"
			print_type_data(ti, lev, obj)
		end
	end
	
	class ObjectForm < Form
		def initialize
			og = Phi::ObjectGrid.new(self) do |og|
				og.align = Phi::AL_CLIENT
			end
			Phi::Splitter.new(self) do |i|
				i.align = Phi::AL_TOP
			end
			ot = Phi::ObjectTree.new(self) do |ot|
				ot.align = Phi::AL_TOP
				ot.on_key_press = proc do |sender, key|
					case key
					when 13
						og.obj = ot.selected.data
						key = 0
					end
					key
				end
				ot.on_change = proc do |sender, node|
					og.obj = ot.selected.data
					ot.set_focus
				end
				ot.show_lines = false
				ot.row_select = true
			end
			self.top = 0
			self.height = 400  # SCREEN.height
		end
	end
end

if __FILE__ == $0
	form = Phi::ObjectForm.new
	form.show
	Phi.mainloop
end
