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.
- data/History.txt +10 -0
- data/Manifest.txt +209 -30
- data/README.txt +16 -20
- data/Rakefile +8 -8
- data/TODO +6 -7
- data/bin/clevic +12 -73
- data/lib/clevic/action_builder.rb +168 -0
- data/lib/clevic/ar_methods.rb +120 -0
- data/lib/clevic/attribute_list.rb +56 -0
- data/lib/clevic/cache_table.rb +60 -37
- data/lib/clevic/default_view.rb +3 -16
- data/lib/clevic/delegate.rb +46 -0
- data/lib/clevic/emitter.rb +38 -0
- data/lib/clevic/extensions.rb +61 -114
- data/lib/clevic/field.rb +159 -228
- data/lib/clevic/field_valuer.rb +165 -0
- data/lib/clevic/filter_command.rb +2 -6
- data/lib/clevic/generic_format.rb +52 -0
- data/lib/clevic/{ui → icons}/icon.png +0 -0
- data/lib/clevic/many_field.rb +7 -0
- data/lib/clevic/model_builder.rb +234 -146
- data/lib/clevic/model_column.rb +61 -13
- data/lib/clevic/order_attribute.rb +10 -0
- data/lib/clevic/qt.rb +35 -0
- data/lib/clevic/qt/action_builder.rb +47 -0
- data/lib/clevic/qt/boolean_delegate.rb +8 -0
- data/lib/clevic/{browser.rb → qt/browser.rb} +35 -14
- data/lib/clevic/qt/clipboard.rb +35 -0
- data/lib/clevic/qt/combo_delegate.rb +198 -0
- data/lib/clevic/qt/delegates.rb +1 -0
- data/lib/clevic/qt/distinct_delegate.rb +35 -0
- data/lib/clevic/qt/extensions.rb +52 -0
- data/lib/clevic/qt/field.rb +18 -0
- data/lib/clevic/{item_delegate.rb → qt/item_delegate.rb} +8 -4
- data/lib/clevic/qt/relational_delegate.rb +87 -0
- data/lib/clevic/{search_dialog.rb → qt/search_dialog.rb} +1 -11
- data/lib/clevic/qt/set_delegate.rb +44 -0
- data/lib/clevic/qt/table_model.rb +331 -0
- data/lib/clevic/qt/table_view.rb +344 -0
- data/lib/clevic/qt/text_area_delegate.rb +8 -0
- data/lib/clevic/{text_delegate.rb → qt/text_delegate.rb} +6 -4
- data/lib/clevic/{ui → qt/ui}/.gitignore +0 -0
- data/lib/clevic/{ui → qt/ui}/browser.ui +0 -0
- data/lib/clevic/{ui → qt/ui}/search_dialog.ui +0 -0
- data/lib/clevic/rails_models_loaders.rb +56 -0
- data/lib/clevic/record.rb +2 -17
- data/lib/clevic/sampler.rb +81 -0
- data/lib/clevic/sequel_ar_adapter.rb +215 -0
- data/lib/clevic/sequel_length_validation.rb +23 -0
- data/lib/clevic/sequel_meta.rb +65 -0
- data/lib/clevic/sequel_naked.rb +30 -0
- data/lib/clevic/swing.rb +38 -0
- data/lib/clevic/swing/action.rb +125 -0
- data/lib/clevic/swing/action_builder.rb +47 -0
- data/lib/clevic/swing/boolean_delegate.rb +26 -0
- data/lib/clevic/swing/browser.rb +282 -0
- data/lib/clevic/swing/cell_editor.rb +95 -0
- data/lib/clevic/swing/cell_renderer.rb +44 -0
- data/lib/clevic/swing/clipboard.rb +135 -0
- data/lib/clevic/swing/combo_delegate.rb +336 -0
- data/lib/clevic/swing/confirm_dialog.rb +57 -0
- data/lib/clevic/swing/delegate.rb +40 -0
- data/lib/clevic/swing/distinct_delegate.rb +30 -0
- data/lib/clevic/swing/extensions.rb +274 -0
- data/lib/clevic/swing/field.rb +35 -0
- data/lib/clevic/swing/relational_delegate.rb +48 -0
- data/lib/clevic/swing/row_header.rb +210 -0
- data/lib/clevic/swing/search_dialog.rb +230 -0
- data/lib/clevic/swing/selection_model.rb +90 -0
- data/lib/clevic/swing/set_delegate.rb +41 -0
- data/lib/clevic/swing/swing_table_index.rb +43 -0
- data/lib/clevic/swing/table_model.rb +200 -0
- data/lib/clevic/swing/table_view.rb +385 -0
- data/lib/clevic/swing/table_view_focus.rb +47 -0
- data/lib/clevic/swing/tag_delegate.rb +127 -0
- data/lib/clevic/swing/tag_editor.rb +101 -0
- data/lib/clevic/swing/text_area_delegate.rb +46 -0
- data/lib/clevic/swing/text_delegate.rb +31 -0
- data/lib/clevic/swing/ui/build.xml +74 -0
- data/lib/clevic/swing/ui/dist/README.TXT +33 -0
- data/lib/clevic/swing/ui/dist/lib/swing-layout-1.0.3.jar +0 -0
- data/lib/clevic/swing/ui/manifest.mf +3 -0
- data/lib/clevic/swing/ui/nbproject/build-impl.xml +731 -0
- data/lib/clevic/swing/ui/nbproject/genfiles.properties +8 -0
- data/lib/clevic/swing/ui/nbproject/private/config.properties +0 -0
- data/lib/clevic/swing/ui/nbproject/private/private.properties +6 -0
- data/lib/clevic/swing/ui/nbproject/private/private.xml +4 -0
- data/lib/clevic/swing/ui/nbproject/project.properties +70 -0
- data/lib/clevic/swing/ui/nbproject/project.xml +14 -0
- data/lib/clevic/swing/ui/src/SearchDialog.form +158 -0
- data/lib/clevic/swing/ui/src/SearchDialog.java +163 -0
- data/lib/clevic/swing/ui/src/TagEditor.form +106 -0
- data/lib/clevic/swing/ui/src/TagEditor.java +108 -0
- data/lib/clevic/swing/ui/src/resources/SearchDialog.properties +0 -0
- data/lib/clevic/table_index.rb +100 -0
- data/lib/clevic/table_model.rb +54 -425
- data/lib/clevic/table_searcher.rb +113 -116
- data/lib/clevic/table_view.rb +171 -399
- data/lib/clevic/table_view_paste.rb +199 -0
- data/lib/clevic/version.rb +3 -2
- data/lib/clevic/view.rb +94 -43
- data/models/accounts_models.rb +13 -13
- data/models/minimal_models.rb +5 -9
- data/models/times_models.rb +19 -14
- data/models/times_psql_models.rb +10 -0
- data/models/times_sqlite_models.rb +1 -8
- data/models/values_models.rb +2 -8
- data/tasks/clevic.rake +1 -1
- data/tasks/rdoc.rake +1 -5
- data/tasks/website.rake +1 -1
- data/test/test_cache_table.rb +15 -29
- data/test/test_helper.rb +14 -83
- data/test/test_order_attribute.rb +1 -1
- data/test/test_table_model.rb +0 -21
- data/test/test_table_searcher.rb +67 -61
- metadata +262 -78
- data/lib/clevic.rb +0 -4
- data/lib/clevic/db_options.rb +0 -112
- data/lib/clevic/delegates.rb +0 -386
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'clevic/qt/combo_delegate.rb'
|
2
|
+
|
3
|
+
module Clevic
|
4
|
+
|
5
|
+
# Provide a list of all values in this field,
|
6
|
+
# and allow new values to be entered.
|
7
|
+
# :frequency can be set as an option. Boolean. If it's true
|
8
|
+
# the options are sorted in order of most frequently used first.
|
9
|
+
class DistinctDelegate < ComboDelegate
|
10
|
+
|
11
|
+
def needs_combo?
|
12
|
+
# works except when there is a '' in the column
|
13
|
+
entity_class.adaptor.count( attribute.to_s, find_options ) > 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def populate_current( editor, model_index )
|
17
|
+
# already done in the SQL query in populate, so don't even check
|
18
|
+
end
|
19
|
+
|
20
|
+
def populate( editor, model_index )
|
21
|
+
# we only use the first column, so use the second
|
22
|
+
# column to sort by, since SQL requires the order by clause
|
23
|
+
# to be in the select list where distinct is involved
|
24
|
+
entity_class.adaptor.attribute_list( attribute, model_index.attribute_value, field.description, field.frequency, find_options ) do |row|
|
25
|
+
value = row[attribute]
|
26
|
+
editor.add_item( value, value.to_variant )
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def translate_from_editor_text( editor, text )
|
31
|
+
text
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'qtext/flags.rb'
|
4
|
+
require 'clevic/table_index.rb'
|
5
|
+
|
6
|
+
# convenience methods
|
7
|
+
module Qt
|
8
|
+
PasteRole = UserRole + 1 unless defined?( PasteRole )
|
9
|
+
|
10
|
+
class AbstractItemDelegate
|
11
|
+
# overridden in EntryDelegate subclasses
|
12
|
+
def full_edit
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# This provides a bunch of methods to get easy access to the entity
|
17
|
+
# and it's values directly from the index without having to keep
|
18
|
+
# asking the model and jumping through other unncessary hoops
|
19
|
+
class ModelIndex
|
20
|
+
include Clevic::TableIndex
|
21
|
+
end
|
22
|
+
|
23
|
+
class ItemSelectionModel
|
24
|
+
# return an array of integer indexes for currently selected rows
|
25
|
+
def row_indexes
|
26
|
+
selected_indexes.inject(Set.new) do |set,index|
|
27
|
+
set << index.row
|
28
|
+
end.to_a
|
29
|
+
end
|
30
|
+
|
31
|
+
# return a collection of selection ranges
|
32
|
+
# in Qt this means an ItemSelection instance
|
33
|
+
def ranges
|
34
|
+
selection
|
35
|
+
end
|
36
|
+
|
37
|
+
def single_cell?
|
38
|
+
ranges.size == 1 && ranges.first.single_cell?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# implement accepted? and rejected? for TableView#confirm_dialog and friends
|
43
|
+
class MessageBox
|
44
|
+
def accepted?
|
45
|
+
[ Qt::Dialog::Accepted, Qt::MessageBox::Yes, Qt::MessageBox::Ok ].include?( result )
|
46
|
+
end
|
47
|
+
|
48
|
+
def rejected?
|
49
|
+
[ Qt::Dialog::Rejected, Qt::MessageBox::No, Qt::MessageBox::Cancel ].include?( result )
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Clevic
|
2
|
+
|
3
|
+
class Field
|
4
|
+
# Convert something that responds to to_s into a Qt::Color,
|
5
|
+
# or just return the argument if it's already a Qt::Color
|
6
|
+
def string_or_color( s_or_c )
|
7
|
+
case s_or_c
|
8
|
+
when NilClass
|
9
|
+
nil
|
10
|
+
when Qt::Color
|
11
|
+
s_or_c
|
12
|
+
else
|
13
|
+
Qt::Color.new( s_or_c.to_s )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -13,8 +13,10 @@ module Clevic
|
|
13
13
|
class ItemDelegate < Qt::ItemDelegate
|
14
14
|
attr_reader :field
|
15
15
|
|
16
|
-
def initialize(
|
17
|
-
|
16
|
+
def initialize( field )
|
17
|
+
raise "field is nil" if field.nil?
|
18
|
+
# pass nil as Qt object's parent. Will set parent later.
|
19
|
+
super( nil )
|
18
20
|
@field = field
|
19
21
|
end
|
20
22
|
|
@@ -40,8 +42,10 @@ class ItemDelegate < Qt::ItemDelegate
|
|
40
42
|
end
|
41
43
|
|
42
44
|
# This catches the event that begins the edit process.
|
43
|
-
|
44
|
-
|
45
|
+
def editorEvent ( event, model, style_option_view_item, model_index )
|
46
|
+
parent.before_edit_index = model_index
|
47
|
+
super
|
48
|
+
end
|
45
49
|
|
46
50
|
# This is called when one of the EditTriggers is pressed. So
|
47
51
|
# it's only good for opening a generic keystroke editor, not
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'clevic/qt/combo_delegate.rb'
|
2
|
+
|
3
|
+
module Clevic
|
4
|
+
|
5
|
+
# Edit a relation from an id and display a list of relevant entries.
|
6
|
+
#
|
7
|
+
# attribute is the method to call on the row entity to retrieve the related object.
|
8
|
+
#
|
9
|
+
# The ids of the model objects are stored in the item data
|
10
|
+
# and the item text is fetched from them using attribute_path.
|
11
|
+
class RelationalDelegate < ComboDelegate
|
12
|
+
def initialize( field )
|
13
|
+
super
|
14
|
+
unless find_options[:conditions].nil?
|
15
|
+
find_options[:conditions].gsub!( /true/, field.related_class.adaptor.quoted_true )
|
16
|
+
find_options[:conditions].gsub!( /false/, field.related_class.adaptor.quoted_false )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def needs_combo?
|
21
|
+
field.related_class.adaptor.count( :conditions => find_options[:conditions] ) > 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def empty_set_message
|
25
|
+
"There must be records in #{field.related_class.name.humanize} for this field to be editable."
|
26
|
+
end
|
27
|
+
|
28
|
+
# add the current item, unless it's already in the combo data
|
29
|
+
def populate_current( editor, model_index )
|
30
|
+
# always add the current selection, if it isn't already there
|
31
|
+
# and it makes sense. This is to make sure that if the list
|
32
|
+
# is filtered, we always have the current value if the filter
|
33
|
+
# excludes it
|
34
|
+
unless model_index.nil?
|
35
|
+
item = model_index.attribute_value
|
36
|
+
if item
|
37
|
+
item_index = editor.find_data( item.id.to_variant )
|
38
|
+
if item_index == -1
|
39
|
+
add_to_list( editor, model_index, item )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def populate( editor, model_index )
|
46
|
+
# add set of all possible related entities
|
47
|
+
field.related_class.adaptor.find( :all, find_options ).each do |x|
|
48
|
+
add_to_list( editor, model_index, x )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_to_list( editor, model_index, item )
|
53
|
+
editor.add_item( model_index.field.transform_attribute( item ), item.id.to_variant )
|
54
|
+
end
|
55
|
+
|
56
|
+
# send data to the editor
|
57
|
+
def setEditorData( editor, model_index )
|
58
|
+
if is_combo?( editor )
|
59
|
+
unless model_index.attribute_value.nil?
|
60
|
+
editor.current_index = editor.find_data( model_index.attribute_value.id.to_variant )
|
61
|
+
end
|
62
|
+
editor.line_edit.select_all
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# don't allow new values
|
67
|
+
def restricted?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
# return an AR entity object
|
72
|
+
def translate_from_editor_text( editor, text )
|
73
|
+
item_index = editor.find_text( text )
|
74
|
+
|
75
|
+
# fetch record id from editor item_data
|
76
|
+
item_data = editor.item_data( item_index )
|
77
|
+
if item_data.valid?
|
78
|
+
# get the entity it refers to, if there is one
|
79
|
+
# use find_by_id so that if it's not found, nil will
|
80
|
+
# be returned
|
81
|
+
field.related_class.adaptor.find( item_data.to_int )
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'Qt4'
|
2
|
-
require 'clevic/ui/search_dialog_ui.rb'
|
2
|
+
require 'clevic/qt/ui/search_dialog_ui.rb'
|
3
3
|
require 'qtext/flags.rb'
|
4
4
|
|
5
5
|
module Clevic
|
@@ -59,16 +59,6 @@ module Clevic
|
|
59
59
|
search_combo.add_item( search_combo.current_text )
|
60
60
|
end
|
61
61
|
|
62
|
-
#~ Qt::MatchExactly 0 Performs QVariant-based matching.
|
63
|
-
#~ Qt::MatchFixedString 8 Performs string-based matching. String-based comparisons are case-insensitive unless the MatchCaseSensitive flag is also specified.
|
64
|
-
#~ Qt::MatchContains 1 The search term is contained in the item.
|
65
|
-
#~ Qt::MatchStartsWith 2 The search term matches the start of the item.
|
66
|
-
#~ Qt::MatchEndsWith 3 The search term matches the end of the item.
|
67
|
-
#~ Qt::MatchCaseSensitive 16 The search is case sensitive.
|
68
|
-
#~ Qt::MatchRegExp 4 Performs string-based matching using a regular expression as the search term.
|
69
|
-
#~ Qt::MatchWildcard 5 Performs string-based matching using a string with wildcards as the search term.
|
70
|
-
#~ Qt::MatchWrap 32 Perform a search that wraps around, so that when the search reaches the last item in the model, it begins again at the first item and continues until all items have been examined.
|
71
|
-
|
72
62
|
retval
|
73
63
|
end
|
74
64
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'clevic/qt/combo_delegate.rb'
|
2
|
+
|
3
|
+
module Clevic
|
4
|
+
|
5
|
+
# A Combo box which allows a set of values. May or may not
|
6
|
+
# be restricted to the set.
|
7
|
+
class SetDelegate < ComboDelegate
|
8
|
+
# options must contain a :set => [ ... ] to specify the set of values.
|
9
|
+
def initialize( field )
|
10
|
+
raise "RestrictedDelegate must have a :set in options" if field.set.nil?
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def needs_combo?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def restricted?
|
19
|
+
field.restricted || false
|
20
|
+
end
|
21
|
+
|
22
|
+
def populate( editor, model_index )
|
23
|
+
field.set_for( model_index.entity ).each do |item|
|
24
|
+
if item.is_a?( Array )
|
25
|
+
# this is a hash-like set, so use key as db value
|
26
|
+
# and value as display value
|
27
|
+
editor.add_item( item.last, item.first.to_variant )
|
28
|
+
else
|
29
|
+
editor.add_item( item, item.to_variant )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def createEditor( parent_widget, style_option_view_item, model_index )
|
35
|
+
editor = super
|
36
|
+
|
37
|
+
# the set is provided, so never insert things
|
38
|
+
editor.insert_policy = Qt::ComboBox::NoInsert
|
39
|
+
editor
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,331 @@
|
|
1
|
+
require 'Qt4'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
require 'andand'
|
5
|
+
|
6
|
+
require 'qtext/flags.rb'
|
7
|
+
require 'qtext/extensions.rb'
|
8
|
+
|
9
|
+
require 'clevic/extensions.rb'
|
10
|
+
require 'clevic/qt/extensions.rb'
|
11
|
+
require 'clevic/model_column'
|
12
|
+
|
13
|
+
module Clevic
|
14
|
+
|
15
|
+
=begin rdoc
|
16
|
+
An instance of Clevic::TableModel is constructed by Clevic::ModelBuilder from the
|
17
|
+
UI definition in a Clevic::View, or from the default Clevic::View created by
|
18
|
+
including the Clevic::Record module in a Sequel::Model subclass.
|
19
|
+
=end
|
20
|
+
class TableModel < Qt::AbstractTableModel
|
21
|
+
include QtFlags
|
22
|
+
|
23
|
+
signals(
|
24
|
+
# index where error occurred, value, message
|
25
|
+
'data_error(QModelIndex,QVariant,QString)'
|
26
|
+
)
|
27
|
+
|
28
|
+
def emit_data_error( index, data, string )
|
29
|
+
emit data_error( index, data.to_variant, string )
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize( parent = nil )
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
# add a new item, and set defaults from the Clevic::View
|
37
|
+
def add_new_item_start
|
38
|
+
begin_insert_rows( Qt::ModelIndex.invalid, row_count, row_count )
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_new_item_end
|
42
|
+
# notify listeners that the model has changed
|
43
|
+
end_insert_rows
|
44
|
+
end
|
45
|
+
|
46
|
+
def remove_notify( rows, &block )
|
47
|
+
begin_remove_rows( Qt::ModelIndex.invalid, rows.first, rows.last )
|
48
|
+
# do the removal
|
49
|
+
yield
|
50
|
+
end_remove_rows
|
51
|
+
end
|
52
|
+
|
53
|
+
# save the AR model at the given index, if it's dirty
|
54
|
+
def update_vertical_header( index )
|
55
|
+
raise "preferably use data_changed here, if possible"
|
56
|
+
emit headerDataChanged( Qt::Vertical, index.row, index.row )
|
57
|
+
end
|
58
|
+
|
59
|
+
def rowCount( parent = nil )
|
60
|
+
collection.size
|
61
|
+
end
|
62
|
+
|
63
|
+
# Not looked up or aliased properly by Qt bindings
|
64
|
+
def row_count
|
65
|
+
collection.size
|
66
|
+
end
|
67
|
+
|
68
|
+
def columnCount( parent = nil )
|
69
|
+
fields.size
|
70
|
+
end
|
71
|
+
|
72
|
+
# Not looked up or aliased properly by Qt bindings
|
73
|
+
def column_count
|
74
|
+
fields.size
|
75
|
+
end
|
76
|
+
|
77
|
+
def flags( model_index )
|
78
|
+
retval = super
|
79
|
+
|
80
|
+
# sometimes this actually happens.
|
81
|
+
# TODO probably a bug in the combo editor exit code
|
82
|
+
return retval if model_index.column >= columnCount
|
83
|
+
|
84
|
+
# TODO don't return IsEditable if the model is read-only
|
85
|
+
if model_index.meta.type == :boolean
|
86
|
+
retval = item_boolean_flags
|
87
|
+
end
|
88
|
+
|
89
|
+
unless model_index.field.read_only? || model_index.entity.andand.readonly? || read_only?
|
90
|
+
retval |= qt_item_is_editable.to_i
|
91
|
+
end
|
92
|
+
retval
|
93
|
+
end
|
94
|
+
|
95
|
+
# values for horizontal and vertical headers
|
96
|
+
def headerData( section, orientation, role )
|
97
|
+
value =
|
98
|
+
case role
|
99
|
+
when qt_display_role
|
100
|
+
case orientation
|
101
|
+
when Qt::Horizontal
|
102
|
+
labels[section]
|
103
|
+
when Qt::Vertical
|
104
|
+
# don't force a fetch from the db
|
105
|
+
if collection.cached_at?( section )
|
106
|
+
collection[section].id
|
107
|
+
else
|
108
|
+
section
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
when qt_text_alignment_role
|
113
|
+
case orientation
|
114
|
+
when Qt::Vertical
|
115
|
+
Qt::AlignRight | Qt::AlignVCenter
|
116
|
+
end
|
117
|
+
|
118
|
+
when Qt::SizeHintRole
|
119
|
+
# anything other than nil here makes the headers disappear.
|
120
|
+
nil
|
121
|
+
|
122
|
+
when qt_tooltip_role
|
123
|
+
case orientation
|
124
|
+
when Qt::Horizontal
|
125
|
+
fields[section].tooltip
|
126
|
+
|
127
|
+
when Qt::Vertical
|
128
|
+
case
|
129
|
+
when !collection[section].errors.empty?
|
130
|
+
'Invalid data'
|
131
|
+
when collection[section].changed?
|
132
|
+
'Unsaved changes'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
when qt_background_role
|
137
|
+
if orientation == Qt::Vertical
|
138
|
+
item = collection[section]
|
139
|
+
case
|
140
|
+
when !item.errors.empty?
|
141
|
+
Qt::Color.new( 'orange' )
|
142
|
+
when item.changed?
|
143
|
+
Qt::Color.new( 'yellow' )
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
else
|
148
|
+
#~ puts "headerData section: #{section}, role: #{const_as_string(role)}" if $options[:debug]
|
149
|
+
nil
|
150
|
+
end
|
151
|
+
|
152
|
+
return value.to_variant
|
153
|
+
end
|
154
|
+
|
155
|
+
# Provide data to UI.
|
156
|
+
def data( index, role = qt_display_role )
|
157
|
+
#~ puts "data for index: #{index.inspect}, field #{index.field.attribute.inspect} and role: #{const_as_string role}"
|
158
|
+
begin
|
159
|
+
case role
|
160
|
+
when qt_display_role
|
161
|
+
# boolean values generally don't have text next to them in this context
|
162
|
+
# check this explicitly to avoid fetching the entity from
|
163
|
+
# the model's collection (and maybe db) when we
|
164
|
+
# definitely don't need to
|
165
|
+
unless index.meta.type == :boolean
|
166
|
+
value = index.display_value
|
167
|
+
end
|
168
|
+
|
169
|
+
when qt_edit_role
|
170
|
+
# see comment for qt_display_role
|
171
|
+
unless index.meta.type == :boolean
|
172
|
+
value = index.edit_value
|
173
|
+
end
|
174
|
+
|
175
|
+
when qt_checkstate_role
|
176
|
+
if index.meta.type == :boolean
|
177
|
+
index.raw_value ? qt_checked : qt_unchecked
|
178
|
+
end
|
179
|
+
|
180
|
+
when qt_text_alignment_role
|
181
|
+
case index.field.alignment
|
182
|
+
when :left; qt_alignleft
|
183
|
+
when :right; qt_alignright
|
184
|
+
when :centre; qt_aligncenter
|
185
|
+
when :justified; qt_alignjustified
|
186
|
+
end
|
187
|
+
|
188
|
+
# just here to make debug output quieter
|
189
|
+
when qt_size_hint_role;
|
190
|
+
|
191
|
+
# show field with a red background if there's an error
|
192
|
+
when qt_background_role
|
193
|
+
index.field.background_for( index.entity ) || Qt::Color.new( 'red' ) if index.has_errors?
|
194
|
+
|
195
|
+
when qt_font_role;
|
196
|
+
|
197
|
+
when qt_foreground_role
|
198
|
+
index.field.foreground_for( index.entity ) ||
|
199
|
+
if index.field.read_only? || index.entity.andand.readonly? || read_only?
|
200
|
+
Qt::Color.new( 'dimgray' )
|
201
|
+
end
|
202
|
+
|
203
|
+
when qt_decoration_role;
|
204
|
+
index.field.decoration_for( index.entity )
|
205
|
+
|
206
|
+
when qt_tooltip_role
|
207
|
+
index.tooltip
|
208
|
+
|
209
|
+
else
|
210
|
+
puts "data index: #{index}, role: #{const_as_string(role)}" if $options[:debug]
|
211
|
+
nil
|
212
|
+
# return the variant
|
213
|
+
end.to_variant
|
214
|
+
|
215
|
+
rescue Exception => e
|
216
|
+
# this can generate a lot of errors from view code, so don't emit data_error every one
|
217
|
+
puts "#{entity_view.class.name}.#{index.field.id}: #{index.inspect} for role: #{const_as_string role} #{value.inspect} #{index.entity.inspect}"
|
218
|
+
puts e.message
|
219
|
+
puts e.backtrace
|
220
|
+
nil.to_variant
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# data sent from UI
|
225
|
+
# return true if conversion from variant was successful,
|
226
|
+
# or false if something went wrong.
|
227
|
+
def setData( index, variant, role = qt_edit_role )
|
228
|
+
if index.valid?
|
229
|
+
case role
|
230
|
+
when qt_edit_role
|
231
|
+
# Don't allow the primary key to be changed
|
232
|
+
return false if index.attribute == entity_class.primary_key.to_sym
|
233
|
+
|
234
|
+
if ( index.column < 0 || index.column >= column_count )
|
235
|
+
raise "invalid column #{index.column}"
|
236
|
+
end
|
237
|
+
|
238
|
+
begin
|
239
|
+
index.attribute_value =
|
240
|
+
case
|
241
|
+
when value.class.name == 'Qt::Date'
|
242
|
+
Date.new( value.year, value.month, value.day )
|
243
|
+
|
244
|
+
when value.class.name == 'Qt::Time'
|
245
|
+
Time.new( value.hour, value.min, value.sec )
|
246
|
+
|
247
|
+
else
|
248
|
+
translate_to_db_object( index, variant.value )
|
249
|
+
end
|
250
|
+
|
251
|
+
# value conversion was successful
|
252
|
+
data_changed( table_index )
|
253
|
+
true
|
254
|
+
rescue Exception => e
|
255
|
+
puts e.backtrace.join( "\n" )
|
256
|
+
puts e.message
|
257
|
+
emit data_error( index, variant, e.message )
|
258
|
+
# value conversion was not successful
|
259
|
+
false
|
260
|
+
end
|
261
|
+
|
262
|
+
when qt_checkstate_role
|
263
|
+
if index.meta.type == :boolean
|
264
|
+
index.entity.toggle( index.attribute )
|
265
|
+
true
|
266
|
+
else
|
267
|
+
false
|
268
|
+
end
|
269
|
+
|
270
|
+
# user-defined role
|
271
|
+
# TODO this only works with single-dotted paths
|
272
|
+
when qt_paste_role
|
273
|
+
if index.meta.type == :association
|
274
|
+
field = index.field
|
275
|
+
candidates = field.related_class.find( :all, :conditions => [ "#{field.attribute_path[1]} = ?", variant.value ] )
|
276
|
+
case candidates.size
|
277
|
+
when 0; puts "No match for #{variant.value}"
|
278
|
+
when 1; index.attribute_value = candidates[0]
|
279
|
+
else; puts "Too many for #{variant.value}"
|
280
|
+
end
|
281
|
+
else
|
282
|
+
index.attribute_value = variant.value
|
283
|
+
end
|
284
|
+
true
|
285
|
+
|
286
|
+
else
|
287
|
+
puts "role: #{role.inspect}"
|
288
|
+
true
|
289
|
+
|
290
|
+
end
|
291
|
+
else
|
292
|
+
false
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# A rubyish way of doing dataChanged
|
297
|
+
# - if args has one element, it's either a single ModelIndex
|
298
|
+
# or something that understands top_left and bottom_right. These
|
299
|
+
# will be turned into a ModelIndex by calling create_index
|
300
|
+
# - if args has two element, assume it's a two ModelIndex instances
|
301
|
+
# - otherwise create a new DataChange and pass it to the block.
|
302
|
+
def data_changed( *args, &block )
|
303
|
+
case args.size
|
304
|
+
when 1
|
305
|
+
arg = args.first
|
306
|
+
if ( arg.respond_to?( :top_left ) && arg.respond_to?( :bottom_right ) ) || arg.is_a?( Qt::ItemSelectionRange )
|
307
|
+
# object is a DataChange, or a SelectionRange
|
308
|
+
top_left_index = create_index( arg.top_left.row, arg.top_left.column )
|
309
|
+
bottom_right_index = create_index( arg.bottom_right.row, arg.bottom_right.column )
|
310
|
+
emit dataChanged( top_left_index, bottom_right_index )
|
311
|
+
else
|
312
|
+
# assume it's a ModelIndex
|
313
|
+
emit dataChanged( arg, arg )
|
314
|
+
end
|
315
|
+
|
316
|
+
when 2
|
317
|
+
emit dataChanged( args.first, args.last )
|
318
|
+
|
319
|
+
else
|
320
|
+
unless block.nil?
|
321
|
+
change = DataChange.new
|
322
|
+
block.call( change )
|
323
|
+
# recursive call
|
324
|
+
data_changed( change )
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
end #module
|