Class: Clevic::TableModel
- Includes:
- Emitter, QtFlags
- Defined in:
- lib/clevic/table_model.rb,
lib/clevic/qt/table_model.rb,
lib/clevic/swing/table_model.rb
Overview
An instance of Clevic::TableModel is constructed by Clevic::ModelBuilder from the UI definition in a Clevic::View, or from the default Clevic::View created by including the Clevic::Record module in a Sequel::Model subclass.
Defined Under Namespace
Classes: DataChange
Instance Attribute Summary collapse
-
#auto_new ⇒ Object
should this model create a new empty record by default?.
-
#builder ⇒ Object
Returns the value of attribute builder.
-
#collection ⇒ Object
(also: #cache_table)
the CacheTable of Clevic::Record or Sequel::Model objects.
-
#entity_view ⇒ Object
Returns the value of attribute entity_view.
-
#fields ⇒ Object
the collection of Clevic::Field objects.
-
#one ⇒ Object
If somre or all the entities in the collection is related to a single entity somewhere else, this is it.
-
#read_only ⇒ Object
Returns the value of attribute read_only.
Instance Method Summary collapse
-
#add_new_item ⇒ Object
add a new item, and set defaults from the Clevic::View add_new_item_start and add_new_item_end are provided by the GUI framework-specific class.
- #add_new_item_end ⇒ Object
-
#add_new_item_start ⇒ Object
add a new item, and set defaults from the Clevic::View.
- #attributes ⇒ Object
- #auto_new? ⇒ Boolean
- #columnCount(parent = nil) ⇒ Object
- #create_index(row, column) ⇒ Object
-
#data(index, role = qt_display_role) ⇒ Object
Provide data to UI.
-
#data_changed(*args, &block) ⇒ Object
A rubyish way of doing dataChanged - if args has one element, it’s either a single ModelIndex or something that understands top_left and bottom_right.
- #emit_data_error(index, data, string) ⇒ Object
- #entity_class ⇒ Object
-
#field_column(field) ⇒ Object
field is a symbol or string referring to a column.
- #field_for_index(model_index) ⇒ Object
- #flags(model_index) ⇒ Object
-
#getColumnClass(column_index) ⇒ Object
override TableModel method.
-
#getColumnCount ⇒ Object
(also: #column_count)
override TableModel method.
-
#getColumnName(column_index) ⇒ Object
override TableModel method.
-
#getRowCount ⇒ Object
(also: #row_count)
override TableModel method.
-
#getValueAt(row_index, column_index) ⇒ Object
Provide raw value to renderers.
-
#headerData(section, orientation, role) ⇒ Object
values for horizontal and vertical headers.
-
#initialize ⇒ TableModel
constructor
A new instance of TableModel.
- #isCellEditable(row_index, column_index) ⇒ Object
- #labels ⇒ Object
- #read_only? ⇒ Boolean
- #reload_data(dataset = nil, &dataset_block) ⇒ Object
- #remove_notify(rows, &block) ⇒ Object
-
#remove_rows(rows) ⇒ Object
rows is a collection of integers specifying row indices to remove.
-
#reset ⇒ Object
Tell the UI we had a major data change.
- #rowCount(parent = nil) ⇒ Object
-
#save(index) ⇒ Object
save the model at the given index, if it’s dirty TODO Sequel uses modified?.
-
#search(start_index, search_criteria) ⇒ Object
return a collection of indexes that match the search criteria at the moment this only returns the first index found TODO could handle dataset creation better.
-
#setData(index, variant, role = qt_edit_role) ⇒ Object
data sent from UI return true if conversion from variant was successful, or false if something went wrong.
- #setValueAt(value, row_index, column_index) ⇒ Object
-
#update_vertical_header(index) ⇒ Object
save the AR model at the given index, if it’s dirty.
- #valuer_for(index) ⇒ Object
Methods included from Emitter
Constructor Details
#initialize ⇒ TableModel
Returns a new instance of TableModel.
31 32 33 |
# File 'lib/clevic/qt/table_model.rb', line 31 def initialize( parent = nil ) super end |
Instance Attribute Details
#auto_new ⇒ Object
should this model create a new empty record by default?
26 27 28 |
# File 'lib/clevic/table_model.rb', line 26 def auto_new @auto_new end |
#builder ⇒ Object
Returns the value of attribute builder.
31 32 33 |
# File 'lib/clevic/table_model.rb', line 31 def builder @builder end |
#collection ⇒ Object Also known as: cache_table
the CacheTable of Clevic::Record or Sequel::Model objects
16 17 18 |
# File 'lib/clevic/table_model.rb', line 16 def collection @collection end |
#entity_view ⇒ Object
Returns the value of attribute entity_view.
30 31 32 |
# File 'lib/clevic/table_model.rb', line 30 def entity_view @entity_view end |
#fields ⇒ Object
the collection of Clevic::Field objects
20 21 22 |
# File 'lib/clevic/table_model.rb', line 20 def fields @fields end |
#one ⇒ Object
If somre or all the entities in the collection is related to a single entity somewhere else, this is it. Not sure if this is the right way to do it, but try anyway.
36 37 38 |
# File 'lib/clevic/table_model.rb', line 36 def one @one end |
#read_only ⇒ Object
Returns the value of attribute read_only.
22 23 24 |
# File 'lib/clevic/table_model.rb', line 22 def read_only @read_only end |
Instance Method Details
#add_new_item ⇒ Object
add a new item, and set defaults from the Clevic::View add_new_item_start and add_new_item_end are provided by the GUI framework-specific class
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/clevic/table_model.rb', line 77 def add_new_item add_new_item_start entity = entity_class.new # set default values without triggering changed fields.each do |f| f.set_default_for( entity ) unless f.default.nil? end collection << entity add_new_item_end end |
#add_new_item_end ⇒ Object
40 41 42 43 |
# File 'lib/clevic/qt/table_model.rb', line 40 def add_new_item_end # notify listeners that the model has changed end_insert_rows end |
#add_new_item_start ⇒ Object
add a new item, and set defaults from the Clevic::View
36 37 38 |
# File 'lib/clevic/qt/table_model.rb', line 36 def add_new_item_start begin_insert_rows( Qt::ModelIndex.invalid, row_count, row_count ) end |
#attributes ⇒ Object
64 65 66 |
# File 'lib/clevic/table_model.rb', line 64 def attributes @attributes ||= fields.map {|x| x.attribute } end |
#auto_new? ⇒ Boolean
27 |
# File 'lib/clevic/table_model.rb', line 27 def auto_new?; auto_new; end |
#columnCount(parent = nil) ⇒ Object
67 68 69 |
# File 'lib/clevic/qt/table_model.rb', line 67 def columnCount( parent = nil ) fields.size end |
#create_index(row, column) ⇒ Object
30 31 32 |
# File 'lib/clevic/swing/table_model.rb', line 30 def create_index( row, column ) SwingTableIndex.new( self, row, column ) end |
#data(index, role = qt_display_role) ⇒ Object
Provide data to UI.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/clevic/qt/table_model.rb', line 157 def data( index, role = qt_display_role ) #~ puts "data for index: #{index.inspect}, field #{index.field.attribute.inspect} and role: #{const_as_string role}" begin case role when qt_display_role # boolean values generally don't have text next to them in this context # check this explicitly to avoid fetching the entity from # the model's collection (and maybe db) when we # definitely don't need to unless index..type == :boolean value = index.display_value end when qt_edit_role # see comment for qt_display_role unless index..type == :boolean value = index.edit_value end when qt_checkstate_role if index..type == :boolean index.raw_value ? qt_checked : qt_unchecked end when qt_text_alignment_role case index.field.alignment when :left; qt_alignleft when :right; qt_alignright when :centre; qt_aligncenter when :justified; qt_alignjustified end # just here to make debug output quieter when qt_size_hint_role; # show field with a red background if there's an error when qt_background_role index.field.background_for( index.entity ) || Qt::Color.new( 'red' ) if index.has_errors? when qt_font_role; when qt_foreground_role index.field.foreground_for( index.entity ) || if index.field.read_only? || read_only? Qt::Color.new( 'dimgray' ) end when qt_decoration_role; index.field.decoration_for( index.entity ) when qt_tooltip_role index.tooltip else puts "data index: #{index}, role: #{const_as_string(role)}" if $options[:debug] nil # return the variant end.to_variant rescue Exception => e # this can generate a lot of errors from view code, so don't emit data_error every one puts "#{entity_view.class.name}.#{index.field.id}: #{index.inspect} for role: #{const_as_string role} #{value.inspect} #{index.entity.inspect}" puts e. puts e.backtrace nil.to_variant end end |
#data_changed(*args, &block) ⇒ Object
A rubyish way of doing dataChanged
-
if args has one element, it’s either a single ModelIndex or something that understands top_left and bottom_right. These will be turned into a ModelIndex by calling create_index
-
if args has two element, assume it’s a two ModelIndex instances
-
otherwise create a new DataChange and pass it to the block.
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/clevic/qt/table_model.rb', line 303 def data_changed( *args, &block ) case args.size when 1 arg = args.first if ( arg.respond_to?( :top_left ) && arg.respond_to?( :bottom_right ) ) || arg.is_a?( Qt::ItemSelectionRange ) # object is a DataChange, or a SelectionRange top_left_index = create_index( arg.top_left.row, arg.top_left.column ) bottom_right_index = create_index( arg.bottom_right.row, arg.bottom_right.column ) emit dataChanged( top_left_index, bottom_right_index ) else # assume it's a ModelIndex emit dataChanged( arg, arg ) end when 2 emit dataChanged( args.first, args.last ) else unless block.nil? change = DataChange.new block.call( change ) # recursive call data_changed( change ) end end end |
#emit_data_error(index, data, string) ⇒ Object
27 28 29 |
# File 'lib/clevic/qt/table_model.rb', line 27 def emit_data_error( index, data, string ) emit data_error( index, data.to_variant, string ) end |
#entity_class ⇒ Object
38 39 40 |
# File 'lib/clevic/table_model.rb', line 38 def entity_class entity_view.entity_class end |
#field_column(field) ⇒ Object
field is a symbol or string referring to a column. returns the index of that field.
52 53 54 |
# File 'lib/clevic/table_model.rb', line 52 def field_column( field ) fields.each_with_index {|x,i| return i if x.id == field.to_sym } end |
#field_for_index(model_index) ⇒ Object
56 57 58 |
# File 'lib/clevic/table_model.rb', line 56 def field_for_index( model_index ) fields[model_index.column] end |
#flags(model_index) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/clevic/qt/table_model.rb', line 76 def flags( model_index ) retval = super # sometimes this actually happens. # TODO probably a bug in the combo editor exit code return retval if model_index.column >= columnCount # TODO don't return IsEditable if the model is read-only if model_index..type == :boolean retval = item_boolean_flags end unless model_index.field.read_only? || read_only? retval |= qt_item_is_editable.to_i end retval end |
#getColumnClass(column_index) ⇒ Object
override TableModel method
78 79 80 81 82 83 84 85 86 |
# File 'lib/clevic/swing/table_model.rb', line 78 def getColumnClass( column_index ) if fields[column_index]..type == :boolean || fields[column_index].delegate.is_a?( BooleanDelegate ) # easiest way to display a checkbox java.lang.Boolean else # This will be a treated as a String value java.lang.Object end end |
#getColumnCount ⇒ Object Also known as: column_count
override TableModel method
65 66 67 |
# File 'lib/clevic/swing/table_model.rb', line 65 def getColumnCount fields.size end |
#getColumnName(column_index) ⇒ Object
override TableModel method
73 74 75 |
# File 'lib/clevic/swing/table_model.rb', line 73 def getColumnName( column_index ) fields[column_index].label end |
#getRowCount ⇒ Object Also known as: row_count
override TableModel method
57 58 59 |
# File 'lib/clevic/swing/table_model.rb', line 57 def getRowCount collection.size end |
#getValueAt(row_index, column_index) ⇒ Object
Provide raw value to renderers
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/clevic/swing/table_model.rb', line 110 def getValueAt( row_index, column_index ) index = create_index( row_index, column_index ) #~ valuer = valuer_for( index ) valuer = index if index.field.delegate.native valuer.display_value else valuer.attribute_value end rescue puts $!.inspect puts $!.backtrace nil end |
#headerData(section, orientation, role) ⇒ Object
values for horizontal and vertical headers
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/clevic/qt/table_model.rb', line 95 def headerData( section, orientation, role ) value = case role when qt_display_role case orientation when Qt::Horizontal labels[section] when Qt::Vertical # display record number. Object id is in tooltip. section+1 end when qt_text_alignment_role case orientation when Qt::Vertical Qt::AlignRight | Qt::AlignVCenter end when Qt::SizeHintRole # anything other than nil here makes the headers disappear. nil when qt_tooltip_role case orientation when Qt::Horizontal fields[section].tooltip when Qt::Vertical case when !collection[section].errors.empty? 'Invalid data' when collection[section].modified? 'Unsaved changes' else if collection.cached_at?( section ) collection[section].pk.inspect end end end when qt_background_role if orientation == Qt::Vertical item = collection[section] unless item.nil? case when !item.errors.empty? Qt::Color.new( 'orange' ) when item.changed? Qt::Color.new( 'yellow' ) end end end else #~ puts "headerData section: #{section}, role: #{const_as_string(role)}" if $options[:debug] nil end return value.to_variant end |
#isCellEditable(row_index, column_index) ⇒ Object
88 89 90 91 |
# File 'lib/clevic/swing/table_model.rb', line 88 def isCellEditable( row_index, column_index ) index = create_index( row_index, column_index ) !( index.field.read_only? || index.entity.andand.readonly? || read_only? ) end |
#labels ⇒ Object
60 61 62 |
# File 'lib/clevic/table_model.rb', line 60 def labels @labels ||= fields.map {|x| x.label } end |
#read_only? ⇒ Boolean
23 |
# File 'lib/clevic/table_model.rb', line 23 def read_only?; read_only; end |
#reload_data(dataset = nil, &dataset_block) ⇒ Object
145 146 147 148 149 150 |
# File 'lib/clevic/table_model.rb', line 145 def reload_data( dataset = nil, &dataset_block ) # renew cache. All records will be dropped and reloaded. self.collection = self.cache_table.renew( dataset, &dataset_block ) # tell the UI we had a major data change reset end |
#remove_notify(rows, &block) ⇒ Object
45 46 47 48 49 50 |
# File 'lib/clevic/qt/table_model.rb', line 45 def remove_notify( rows, &block ) begin_remove_rows( Qt::ModelIndex.invalid, rows.first, rows.last ) # do the removal yield end_remove_rows end |
#remove_rows(rows) ⇒ Object
rows is a collection of integers specifying row indices to remove
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/clevic/table_model.rb', line 93 def remove_rows( rows ) # don't delete rows twice # put the entities to be removed in a separate collection # I can't figure out why the collection fails when they're # removed directly rows_in_order = rows.uniq.sort removals = rows_in_order.map do |index| collection[index] end remove_notify( rows_in_order ) do removals.each do |to_remove, index| # destroy the db object, and its associated table row to_remove.destroy unless to_remove.nil? || to_remove.new? end end # because it's too bloody complicated to figure out which items # were deleted and then remove them from the collection. And there # most likely isn't a big hit for doing this, unless there's a lot # of cached calcuation in the entities. self.collection = collection.renew # create a new row if auto_new is on # should really be in a signal handler add_new_item if collection.empty? && auto_new? end |
#reset ⇒ Object
Tell the UI we had a major data change
51 52 53 54 |
# File 'lib/clevic/swing/table_model.rb', line 51 def reset # could also use fireTableStructureChanged(), but it doesn't seem necessary fireTableDataChanged end |
#rowCount(parent = nil) ⇒ Object
58 59 60 |
# File 'lib/clevic/qt/table_model.rb', line 58 def rowCount( parent = nil ) collection.size end |
#save(index) ⇒ Object
save the model at the given index, if it’s dirty TODO Sequel uses modified?
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/clevic/table_model.rb', line 123 def save( index ) item = collection[index.row] return false if item.nil? if item.changed? if item.valid? retval = item.save data_changed( index ) retval else false end else # model not changed true end rescue puts "Error saving #{item.inspect}" puts $!. puts $!.backtrace emit_data_error( index, nil, $!. ) end |
#search(start_index, search_criteria) ⇒ Object
return a collection of indexes that match the search criteria at the moment this only returns the first index found TODO could handle dataset creation better
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/clevic/table_model.rb', line 155 def search( start_index, search_criteria ) searcher = TableSearcher.new( cache_table.dataset, search_criteria, start_index.field ) entity = searcher.search( start_index.entity ) # return matched indexes unless entity.nil? found_row = collection.index_for_entity( entity ) raise "found_row should find something" if found_row.nil? [ create_index( found_row, start_index.column, 0 ) ] else [] end end |
#setData(index, variant, role = qt_edit_role) ⇒ Object
data sent from UI return true if conversion from variant was successful, or false if something went wrong.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/clevic/qt/table_model.rb', line 228 def setData( index, variant, role = qt_edit_role ) if index.valid? case role when qt_edit_role # Don't allow the primary key to be changed return false if index.attribute == entity_class.primary_key.to_sym if ( index.column < 0 || index.column >= column_count ) raise "invalid column #{index.column}" end begin case when variant.class.name == 'Qt::Date' index.attribute_value = Date.new( variant.year, variant.month, variant.day ) when variant.class.name == 'Qt::Time' index.attribute_value = Time.new( variant.hour, variant.min, variant.sec ) else index.edit_value = variant.value end # value conversion was successful data_changed( index ) true rescue Exception => e puts e.backtrace.join( "\n" ) puts e. emit data_error( index, variant, e. ) # value conversion was not successful false end when qt_checkstate_role if index..type == :boolean index.attribute_value = !index.attribute_value true else false end # user-defined role # TODO this only works with single-dotted paths when qt_paste_role puts "WARNING Qt::PasteRole Deprecated" if index..type == :association field = index.field candidates = field..find( :all, :conditions => [ "#{field.attribute_path[1]} = ?", variant.value ] ) case candidates.size when 0; puts "No match for #{variant.value}" when 1; index.attribute_value = candidates[0] else; puts "Too many for #{variant.value}" end else index.attribute_value = variant.value end true else puts "role: #{role.inspect}" true end else false end end |
#setValueAt(value, row_index, column_index) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/clevic/swing/table_model.rb', line 128 def setValueAt( value, row_index, column_index ) index = create_index( row_index, column_index ) #~ puts "setting index: #{index.inspect} to #{value.inspect}" #~ valuer = valuer_for( index ) valuer = index # Don't allow the primary key to be changed return if index.attribute == entity_class.primary_key.to_sym # translate the value from the ui to something that # the DB entity will understand begin if value.is_a?( java.util.Date ) valuer.attribute_value = case value # more specific descendant first when java.util.Time Time.new( value.hour, value.min, value.sec ) when java.util.Date Date.new( value.year, value.month, value.day ) else raise "don't know how to convert a #{value.class.name}:#{value.inspect}" end else valuer.edit_value = value end valuer.entity.save data_changed( index ) rescue Exception => e puts "#{__FILE__}:#{__LINE__}:e.message: #{e.}" puts e.backtrace emit_data_error( index, value, e. ) end end |
#update_vertical_header(index) ⇒ Object
save the AR model at the given index, if it’s dirty
53 54 55 56 |
# File 'lib/clevic/qt/table_model.rb', line 53 def update_vertical_header( index ) raise "preferably use data_changed here, if possible" emit headerDataChanged( Qt::Vertical, index.row, index.row ) end |
#valuer_for(index) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/clevic/swing/table_model.rb', line 93 def valuer_for( index ) case # pull values from entity at index when index.field.entity_class == entity_class index # pull values from the Clevic::View class when entity_view.class.ancestors.include?( Clevic::View ) #~ entity_view.entity = index.entity FieldValuer.valuer( index.field, entity_view ) else raise "No valuer for #{index.inspect}" end end |