clevic 0.12.0 → 0.13.0.b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. data/History.txt +10 -0
  2. data/Manifest.txt +209 -30
  3. data/README.txt +16 -20
  4. data/Rakefile +8 -8
  5. data/TODO +6 -7
  6. data/bin/clevic +12 -73
  7. data/lib/clevic/action_builder.rb +168 -0
  8. data/lib/clevic/ar_methods.rb +120 -0
  9. data/lib/clevic/attribute_list.rb +56 -0
  10. data/lib/clevic/cache_table.rb +60 -37
  11. data/lib/clevic/default_view.rb +3 -16
  12. data/lib/clevic/delegate.rb +46 -0
  13. data/lib/clevic/emitter.rb +38 -0
  14. data/lib/clevic/extensions.rb +61 -114
  15. data/lib/clevic/field.rb +159 -228
  16. data/lib/clevic/field_valuer.rb +165 -0
  17. data/lib/clevic/filter_command.rb +2 -6
  18. data/lib/clevic/generic_format.rb +52 -0
  19. data/lib/clevic/{ui → icons}/icon.png +0 -0
  20. data/lib/clevic/many_field.rb +7 -0
  21. data/lib/clevic/model_builder.rb +234 -146
  22. data/lib/clevic/model_column.rb +61 -13
  23. data/lib/clevic/order_attribute.rb +10 -0
  24. data/lib/clevic/qt.rb +35 -0
  25. data/lib/clevic/qt/action_builder.rb +47 -0
  26. data/lib/clevic/qt/boolean_delegate.rb +8 -0
  27. data/lib/clevic/{browser.rb → qt/browser.rb} +35 -14
  28. data/lib/clevic/qt/clipboard.rb +35 -0
  29. data/lib/clevic/qt/combo_delegate.rb +198 -0
  30. data/lib/clevic/qt/delegates.rb +1 -0
  31. data/lib/clevic/qt/distinct_delegate.rb +35 -0
  32. data/lib/clevic/qt/extensions.rb +52 -0
  33. data/lib/clevic/qt/field.rb +18 -0
  34. data/lib/clevic/{item_delegate.rb → qt/item_delegate.rb} +8 -4
  35. data/lib/clevic/qt/relational_delegate.rb +87 -0
  36. data/lib/clevic/{search_dialog.rb → qt/search_dialog.rb} +1 -11
  37. data/lib/clevic/qt/set_delegate.rb +44 -0
  38. data/lib/clevic/qt/table_model.rb +331 -0
  39. data/lib/clevic/qt/table_view.rb +344 -0
  40. data/lib/clevic/qt/text_area_delegate.rb +8 -0
  41. data/lib/clevic/{text_delegate.rb → qt/text_delegate.rb} +6 -4
  42. data/lib/clevic/{ui → qt/ui}/.gitignore +0 -0
  43. data/lib/clevic/{ui → qt/ui}/browser.ui +0 -0
  44. data/lib/clevic/{ui → qt/ui}/search_dialog.ui +0 -0
  45. data/lib/clevic/rails_models_loaders.rb +56 -0
  46. data/lib/clevic/record.rb +2 -17
  47. data/lib/clevic/sampler.rb +81 -0
  48. data/lib/clevic/sequel_ar_adapter.rb +215 -0
  49. data/lib/clevic/sequel_length_validation.rb +23 -0
  50. data/lib/clevic/sequel_meta.rb +65 -0
  51. data/lib/clevic/sequel_naked.rb +30 -0
  52. data/lib/clevic/swing.rb +38 -0
  53. data/lib/clevic/swing/action.rb +125 -0
  54. data/lib/clevic/swing/action_builder.rb +47 -0
  55. data/lib/clevic/swing/boolean_delegate.rb +26 -0
  56. data/lib/clevic/swing/browser.rb +282 -0
  57. data/lib/clevic/swing/cell_editor.rb +95 -0
  58. data/lib/clevic/swing/cell_renderer.rb +44 -0
  59. data/lib/clevic/swing/clipboard.rb +135 -0
  60. data/lib/clevic/swing/combo_delegate.rb +336 -0
  61. data/lib/clevic/swing/confirm_dialog.rb +57 -0
  62. data/lib/clevic/swing/delegate.rb +40 -0
  63. data/lib/clevic/swing/distinct_delegate.rb +30 -0
  64. data/lib/clevic/swing/extensions.rb +274 -0
  65. data/lib/clevic/swing/field.rb +35 -0
  66. data/lib/clevic/swing/relational_delegate.rb +48 -0
  67. data/lib/clevic/swing/row_header.rb +210 -0
  68. data/lib/clevic/swing/search_dialog.rb +230 -0
  69. data/lib/clevic/swing/selection_model.rb +90 -0
  70. data/lib/clevic/swing/set_delegate.rb +41 -0
  71. data/lib/clevic/swing/swing_table_index.rb +43 -0
  72. data/lib/clevic/swing/table_model.rb +200 -0
  73. data/lib/clevic/swing/table_view.rb +385 -0
  74. data/lib/clevic/swing/table_view_focus.rb +47 -0
  75. data/lib/clevic/swing/tag_delegate.rb +127 -0
  76. data/lib/clevic/swing/tag_editor.rb +101 -0
  77. data/lib/clevic/swing/text_area_delegate.rb +46 -0
  78. data/lib/clevic/swing/text_delegate.rb +31 -0
  79. data/lib/clevic/swing/ui/build.xml +74 -0
  80. data/lib/clevic/swing/ui/dist/README.TXT +33 -0
  81. data/lib/clevic/swing/ui/dist/lib/swing-layout-1.0.3.jar +0 -0
  82. data/lib/clevic/swing/ui/manifest.mf +3 -0
  83. data/lib/clevic/swing/ui/nbproject/build-impl.xml +731 -0
  84. data/lib/clevic/swing/ui/nbproject/genfiles.properties +8 -0
  85. data/lib/clevic/swing/ui/nbproject/private/config.properties +0 -0
  86. data/lib/clevic/swing/ui/nbproject/private/private.properties +6 -0
  87. data/lib/clevic/swing/ui/nbproject/private/private.xml +4 -0
  88. data/lib/clevic/swing/ui/nbproject/project.properties +70 -0
  89. data/lib/clevic/swing/ui/nbproject/project.xml +14 -0
  90. data/lib/clevic/swing/ui/src/SearchDialog.form +158 -0
  91. data/lib/clevic/swing/ui/src/SearchDialog.java +163 -0
  92. data/lib/clevic/swing/ui/src/TagEditor.form +106 -0
  93. data/lib/clevic/swing/ui/src/TagEditor.java +108 -0
  94. data/lib/clevic/swing/ui/src/resources/SearchDialog.properties +0 -0
  95. data/lib/clevic/table_index.rb +100 -0
  96. data/lib/clevic/table_model.rb +54 -425
  97. data/lib/clevic/table_searcher.rb +113 -116
  98. data/lib/clevic/table_view.rb +171 -399
  99. data/lib/clevic/table_view_paste.rb +199 -0
  100. data/lib/clevic/version.rb +3 -2
  101. data/lib/clevic/view.rb +94 -43
  102. data/models/accounts_models.rb +13 -13
  103. data/models/minimal_models.rb +5 -9
  104. data/models/times_models.rb +19 -14
  105. data/models/times_psql_models.rb +10 -0
  106. data/models/times_sqlite_models.rb +1 -8
  107. data/models/values_models.rb +2 -8
  108. data/tasks/clevic.rake +1 -1
  109. data/tasks/rdoc.rake +1 -5
  110. data/tasks/website.rake +1 -1
  111. data/test/test_cache_table.rb +15 -29
  112. data/test/test_helper.rb +14 -83
  113. data/test/test_order_attribute.rb +1 -1
  114. data/test/test_table_model.rb +0 -21
  115. data/test/test_table_searcher.rb +67 -61
  116. metadata +262 -78
  117. data/lib/clevic.rb +0 -4
  118. data/lib/clevic/db_options.rb +0 -112
  119. data/lib/clevic/delegates.rb +0 -386
@@ -1,23 +1,71 @@
1
1
  =begin rdoc
2
- This responds to the same methods as ActiveRecord::ConnectionAdapter::Column.
3
- It exists to pretend that the accessors generated by association methods
4
- have a column type of :association, which lets us make a sensible paste
5
- decision using PasteRole, and makes the field/attribute distinction possible
6
- in ModelIndex.
2
+ Field metadata class. Includes information for type, reflections etc.
3
+
4
+ Also, it eases the migration from AR to Sequel, which returns metadata as
5
+ a hash instead of a class.
7
6
  =end
8
7
  class ModelColumn
9
- attr_accessor :primary, :scale, :sql_type, :name, :precision, :default, :limit, :type, :meta
8
+ # these are from AR
9
+ attr_accessor :primary, :scale, :sql_type, :name, :precision, :default, :type, :meta
10
10
 
11
- def initialize( name, type, meta )
11
+ attr_writer :limit
12
+
13
+ # if it's not here, it's probably from Sequel, so figure it out from
14
+ # the db_type
15
+ def limit
16
+ unless @limit
17
+ db_type =~ /\((\d+)\)/
18
+ @limit = $1.to_i
19
+ end
20
+ @limit
21
+ end
22
+
23
+ # these are from Sequel::Model.columns_hash
24
+ attr_accessor :ruby_default, :primary_key, :allow_null, :db_type
25
+
26
+ # sequel::Model.reflections
27
+ attr_accessor :key, :eager_block, :type, :eager_grapher, :before_add, :model, :graph_join_type, :class_name, :before_remove, :eager_loader, :uses_composite_keys, :order_eager_graph, :dataset, :cartesian_product_number, :after_add, :cache, :keys, :after_remove, :extend, :graph_conditions, :name, :orig_opts, :after_load, :before_set, :after_set, :reciprocal, :reciprocal_type
28
+
29
+ # for many_to_one targets
30
+ attr_accessor :primary_keys
31
+
32
+ # TODO not sure where these are from
33
+ attr_accessor :order, :class, :conditions
34
+
35
+ # For Sequel many_to_many
36
+ attr_accessor :left_key,
37
+ :left_keys,
38
+ :right_key,
39
+ :right_keys,
40
+ :left_primary_key,
41
+ :left_primary_keys,
42
+ :uses_left_composite_keys,
43
+ :uses_right_composite_keys,
44
+ :cartesian_product_number,
45
+ :join_table,
46
+ :left_key_alias,
47
+ :graph_join_table_conditions,
48
+ :graph_join_table_join_type
49
+
50
+ # added by us
51
+ attr_accessor :association
52
+ def association?; association; end
53
+
54
+ def initialize( name, hash )
55
+ @hash = hash
56
+ @hash.each do |key,value|
57
+ send( "#{key}=", value )
58
+ end
59
+
60
+ # must be after hash so it takes precedence
12
61
  @name = name
13
- @type = type
14
- @meta = meta
15
62
  end
16
63
 
17
- # return the underlying field name, ie "#{attribute}_id" if
18
- # it's an association
19
- alias_method :old_name, :name
20
64
  def name
21
- @meta.name
65
+ @name
66
+ end
67
+
68
+ def related_class
69
+ @related_class ||= eval class_name
22
70
  end
23
71
  end
@@ -1,6 +1,7 @@
1
1
  =begin rdoc
2
2
  Store the SQL order_by attributes with ascending and descending values
3
3
  =end
4
+ # TODO don't use this anymore
4
5
  class OrderAttribute
5
6
  attr_reader :direction, :attribute
6
7
 
@@ -50,4 +51,13 @@ class OrderAttribute
50
51
  self.direction == other.direction and
51
52
  self.attribute == other.attribute
52
53
  end
54
+
55
+ # return -1 for desc, 1 for asc
56
+ def to_i
57
+ case direction
58
+ when :asc; 1
59
+ when :desc; -1
60
+ else; raise "unknown direction #{direction}"
61
+ end
62
+ end
53
63
  end
data/lib/clevic/qt.rb ADDED
@@ -0,0 +1,35 @@
1
+ =begin
2
+ Require this file to do Clevic in Qt
3
+ =end
4
+
5
+ require 'pathname'
6
+
7
+ # require these first, so TableModel and TableView get the correct ancestors
8
+ require 'clevic/qt/table_model.rb'
9
+ require 'clevic/qt/table_view.rb'
10
+ ( Pathname.new( __FILE__ ).parent + 'qt' ).children.grep( /.rb$/ ).each do |child|
11
+ require child.to_s
12
+ end
13
+
14
+ # now require the generic parts
15
+ require 'clevic/table_model'
16
+ require 'clevic/table_view'
17
+ require 'clevic.rb'
18
+
19
+ module Clevic
20
+
21
+ def self.tahoma
22
+ if @font.nil?
23
+ @font =
24
+ begin
25
+ found = java.awt.GraphicsEnvironment.local_graphics_environment.all_fonts.select {|f| f.font_name == "Tahoma"}.first
26
+ found.deriveFont( 13.0 )
27
+ java.awt.Font.new( 'DialogInput', java.awt.Font::PLAIN, 13 )
28
+ rescue
29
+ java.awt.Font.new( 'DialogInput', java.awt.Font::PLAIN, 13 )
30
+ end
31
+ end
32
+ @font
33
+ end
34
+
35
+ end
@@ -0,0 +1,47 @@
1
+ module Clevic
2
+
3
+ module ActionBuilder
4
+ # Create a new separator and add a new separator.
5
+ def separator
6
+ Qt::Action.new( parent ) do |action|
7
+ action.separator = true
8
+ add_action action
9
+ collect_actions << action
10
+ end
11
+ end
12
+
13
+ def create_action( &block )
14
+ Qt::Action.new( parent, &block )
15
+ end
16
+
17
+ # TODO move this into Action, like the swing adapter
18
+ def create_key_sequence( sequence )
19
+ Qt::KeySequence.new( sequence )
20
+ end
21
+
22
+ # set up the code to be executed when an action is triggered,
23
+ def action_method_or_block( qt_action, options, &block )
24
+ signal_name = "triggered(#{options.has_key?( :checkable ) ? 'bool' : ''})"
25
+
26
+ # connect the action to some code
27
+ if options.has_key?( :method )
28
+ qt_action.connect SIGNAL( signal_name ) do |active|
29
+ action_triggered do
30
+ send_args = [ options[:method], options.has_key?( :checkable ) ? active : nil ].compact
31
+ send( *send_args )
32
+ end
33
+ end
34
+ else
35
+ unless block.nil?
36
+ action_triggered do
37
+ qt_action.connect SIGNAL( signal_name ) do |active|
38
+ yield( active )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,8 @@
1
+ require 'clevic/qt/text_delegate.rb'
2
+
3
+ module Clevic
4
+
5
+ # TODO alias this for now
6
+ BooleanDelegate = TextDelegate
7
+
8
+ end
@@ -1,5 +1,5 @@
1
- require 'clevic/search_dialog.rb'
2
- require 'clevic/ui/browser_ui.rb'
1
+ require 'clevic/qt/search_dialog.rb'
2
+ require 'clevic/qt/ui/browser_ui.rb'
3
3
  require 'clevic/table_view.rb'
4
4
  require 'clevic.rb'
5
5
 
@@ -23,8 +23,8 @@ class Browser < Qt::Widget
23
23
  @layout.setup_ui( main_window )
24
24
 
25
25
  # set icon. MUST come after call to setup_ui
26
- icon_path = Pathname.new( __FILE__ ).parent + "ui/icon.png"
27
- raise "icon.png not found" unless icon_path.file?
26
+ icon_path = Pathname.new( __FILE__ ).parent + "../icons/icon.png"
27
+ Kernel::raise "icon.png not found" unless icon_path.file?
28
28
  main_window.window_icon = Qt::Icon.new( icon_path.realpath.to_s )
29
29
 
30
30
  # add the tables tab
@@ -52,8 +52,7 @@ class Browser < Qt::Widget
52
52
 
53
53
  # Set the main window title to the name of the database, if we can find it.
54
54
  def database_name
55
- #~ FIXME #{__FILE__}:#{__LINE__}"
56
- #~ table_view.model.db_options.database
55
+ table_view.model.entity_class.db.url rescue ''
57
56
  end
58
57
 
59
58
  def update_menus
@@ -128,18 +127,18 @@ class Browser < Qt::Widget
128
127
 
129
128
  # Add all existing model objects as tabs, one each
130
129
  views.each do |view_class|
131
- view = view_class.new
132
- unless view.entity_class.table_exists?
133
- puts "No table for #{view.entity_class.inspect}"
134
- next
135
- end
136
-
137
130
  begin
131
+ view = view_class.new
132
+ unless view.entity_class.table_exists?
133
+ puts "No table for #{view.entity_class.inspect}"
134
+ next
135
+ end
136
+
138
137
  # create the the table_view and the table_model for the entity_class
139
138
  tab = Clevic::TableView.new( view )
140
139
 
141
140
  # show status messages
142
- tab.connect( SIGNAL( 'status_text(QString)' ) ) { |msg| @layout.statusbar.show_message( msg, 10000 ) }
141
+ tab.connect( SIGNAL( 'status_text_signal(QString)' ) ) { |msg| @layout.statusbar.show_message( msg, 10000 ) }
143
142
 
144
143
  # add a new tab
145
144
  tables_tab.add_tab( tab, translate( tab.title ) )
@@ -153,7 +152,7 @@ class Browser < Qt::Widget
153
152
  @layout.menu_model.add_action( action )
154
153
 
155
154
  # handle filter status changed, so we can provide a visual indication
156
- tab.connect SIGNAL( 'filter_status(bool)' ) do |status|
155
+ tab.connect SIGNAL( 'filter_status_signal(bool)' ) do |status|
157
156
  # update the tab, so there's a visual indication of filtering
158
157
  filter_title = ( tab.filtered ? '| ' : '' ) + translate( tab.title )
159
158
  tables_tab.set_tab_text( tables_tab.current_index, filter_title )
@@ -171,6 +170,28 @@ class Browser < Qt::Widget
171
170
  def save_all
172
171
  tables_tab.tabs.each {|x| x.save_row( x.current_index ) }
173
172
  end
173
+
174
+ def self.run( args )
175
+ # load model files
176
+ raise "no model definition file specified" if args.empty?
177
+ args.each { |arg| load_models( Pathname.new( arg ) ) }
178
+
179
+ app = Qt::Application.new( args )
180
+
181
+ # show UI
182
+ main_window = Qt::MainWindow.new
183
+ browser = Clevic::Browser.new( main_window )
184
+ # this must come after Clevic::Browser.new
185
+ main_window.show
186
+ # make sure any partially edited records are saved when the window is closed
187
+ app.connect( SIGNAL('lastWindowClosed()') ) { browser.save_all }
188
+ begin
189
+ app.exec
190
+ rescue
191
+ puts $!.message
192
+ puts $!.backtrace
193
+ end
194
+ end
174
195
  end
175
196
 
176
197
  end
@@ -0,0 +1,35 @@
1
+ module Clevic
2
+
3
+ # see swing clipboard if you need a stream with io/like
4
+
5
+ # Clevic wrapper for Qt::Application::clipboard
6
+ class Clipboard
7
+ def system
8
+ Qt::Application::clipboard
9
+ end
10
+
11
+ def text=( value )
12
+ system.text = value
13
+ end
14
+
15
+ def text
16
+ system.text
17
+ end
18
+
19
+ def text?
20
+ system.mime_data.has_text
21
+ end
22
+
23
+ def html?
24
+ system.mime_data.has_html
25
+ end
26
+
27
+ # TODO figure out why Qt never has anything other than text
28
+ # could be because the event loop isn't running when testing
29
+ # from irb
30
+ def html
31
+ system.mime_data.html
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,198 @@
1
+ require 'clevic/qt/item_delegate.rb'
2
+
3
+ module Clevic
4
+
5
+ =begin rdoc
6
+ Base class for other delegates using Combo boxes. Emit focus out signals,
7
+ because ComboBox stupidly doesn't.
8
+
9
+ Generally these will be created using a Clevic::ModelBuilder.
10
+ =end
11
+ class ComboDelegate < Clevic::ItemDelegate
12
+ def initialize( field )
13
+ super
14
+ end
15
+
16
+ # Convert Qt:: constants from the integer value to a string value.
17
+ def hint_string( hint )
18
+ hs = String.new
19
+ Qt::AbstractItemDelegate.constants.each do |x|
20
+ hs = x if eval( "Qt::AbstractItemDelegate::#{x}.to_i" ) == hint.to_i
21
+ end
22
+ hs
23
+ end
24
+
25
+ def dump_editor_state( editor )
26
+ if $options[:debug]
27
+ puts "#{self.class.name}"
28
+ puts "editor.completer.completion_count: #{editor.completer.completion_count}"
29
+ puts "editor.completer.current_completion: #{editor.completer.current_completion}"
30
+ puts "editor.find_text( editor.completer.current_completion ): #{editor.find_text( editor.completer.current_completion )}"
31
+ puts "editor.current_text: #{editor.current_text}"
32
+ puts "editor.count: #{editor.count}"
33
+ puts "editor.completer.current_row: #{editor.completer.current_row}"
34
+ puts "editor.item_data( editor.current_index ): #{editor.item_data( editor.current_index ).inspect}"
35
+ puts
36
+ end
37
+ end
38
+
39
+ # open the combo box, just like if f4 was pressed
40
+ def full_edit
41
+ if is_combo?( @editor )
42
+ @editor.show_popup
43
+ end
44
+ end
45
+
46
+ # returns true if the editor allows values outside of a predefined
47
+ # range, false otherwise.
48
+ def restricted?
49
+ false
50
+ end
51
+
52
+ # TODO fetch this from the model definition
53
+ def allow_null?
54
+ true
55
+ end
56
+
57
+ # Subclasses should override this to fill the combo box
58
+ # list with values.
59
+ def populate( editor, model_index )
60
+ raise "subclass responsibility"
61
+ end
62
+
63
+ # return true if this delegate needs a combo, false otherwise
64
+ def needs_combo?
65
+ raise "subclass responsibility"
66
+ end
67
+
68
+ def is_combo?( editor )
69
+ editor.class == Qt::ComboBox
70
+ end
71
+
72
+ # return true if this field has no data (needs_combo? is false)
73
+ # and is at the same time restricted (ie needs data from somewhere else)
74
+ def empty_set?
75
+ !needs_combo? && restricted?
76
+ end
77
+
78
+ # the message to display if the set is empty, and
79
+ # the delegate is restricted to a predefined set.
80
+ def empty_set_message
81
+ raise "subclass responsibility"
82
+ end
83
+
84
+ # if this delegate has an empty set, return the message, otherwise
85
+ # return nil.
86
+ def if_empty_message
87
+ if empty_set?
88
+ empty_set_message
89
+ end
90
+ end
91
+
92
+ def populate_current( editor, model_index )
93
+ # add the current entry, if it isn't there already
94
+ # TODO add it in the correct order
95
+ if ( editor.find_data( model_index.display_value.to_variant ) == -1 )
96
+ editor.add_item( model_index.display_value, model_index.display_value.to_variant )
97
+ end
98
+ end
99
+
100
+ def add_nil_item( editor )
101
+ if ( editor.find_data( nil.to_variant ) == -1 )
102
+ editor.add_item( '', nil.to_variant )
103
+ end
104
+ end
105
+
106
+ # Override the Qt method. Create a ComboBox widget and fill it with the possible values.
107
+ def createEditor( parent_widget, style_option_view_item, model_index )
108
+ if needs_combo?
109
+ @editor = Qt::ComboBox.new( parent_widget )
110
+
111
+ # subclasses fill in the rest of the entries
112
+ populate( @editor, model_index )
113
+
114
+ # add the current item, if it isn't there already
115
+ populate_current( @editor, model_index )
116
+
117
+ # create a nil entry
118
+ add_nil_item( @editor ) if allow_null?
119
+
120
+ # allow prefix matching from the keyboard
121
+ @editor.editable = true
122
+
123
+ # don't insert if restricted
124
+ @editor.insert_policy = Qt::ComboBox::NoInsert if restricted?
125
+ else
126
+ @editor =
127
+ if restricted?
128
+ emit parent.status_text( empty_set_message )
129
+ nil
130
+ else
131
+ Qt::LineEdit.new( model_index.edit_value, parent_widget )
132
+ end
133
+ end
134
+ @editor
135
+ end
136
+
137
+ # Override the Qt::ItemDelegate method.
138
+ def updateEditorGeometry( editor, style_option_view_item, model_index )
139
+ rect = style_option_view_item.rect
140
+
141
+ # ask the editor for how much space it wants, and set the editor
142
+ # to that size when it displays in the table
143
+ rect.set_width( [editor.size_hint.width,rect.width].max ) if is_combo?( editor )
144
+ editor.set_geometry( rect )
145
+ end
146
+
147
+ # Override the Qt method to send data to the editor from the model.
148
+ def setEditorData( editor, model_index )
149
+ if is_combo?( editor )
150
+ editor.current_index = editor.find_data( model_index.attribute_value.to_variant )
151
+ editor.line_edit.select_all if editor.editable
152
+ else
153
+ editor.text = model_index.edit_value
154
+ end
155
+ end
156
+
157
+ # This translates the text from the editor into something that is
158
+ # stored in an underlying model. Intended to be overridden by subclasses.
159
+ def translate_from_editor_text( editor, text )
160
+ index = editor.find_text( text )
161
+
162
+ if index == -1
163
+ text unless restricted?
164
+ else
165
+ editor.item_data( index ).value
166
+ end
167
+ end
168
+
169
+ # Send the data from the editor to the model. The data will
170
+ # be translated by translate_from_editor_text,
171
+ def setModelData( editor, abstract_item_model, model_index )
172
+ if is_combo?( editor )
173
+ dump_editor_state( editor )
174
+ value =
175
+ if editor.completer.current_row == -1
176
+ # item doesn't exist in the list, add it if not restricted
177
+ editor.current_text unless restricted?
178
+ elsif editor.completer.completion_count == editor.count
179
+ # selection from drop down. if it's empty, we want a nil
180
+ editor.current_text
181
+ else
182
+ # there is a matching completion, so use it
183
+ editor.completer.current_completion
184
+ end
185
+
186
+ if value != nil
187
+ model_index.attribute_value = translate_from_editor_text( editor, value )
188
+ end
189
+
190
+ else
191
+ model_index.attribute_value = editor.text
192
+ end
193
+ abstract_item_model.data_changed( model_index )
194
+ end
195
+
196
+ end
197
+
198
+ end