clevic 0.13.0.b3 → 0.13.0.b5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +21 -0
- data/Manifest.txt +91 -85
- data/README.txt +33 -18
- data/Rakefile +2 -3
- data/TODO +8 -14
- data/bin/clevic +18 -20
- data/lib/clevic.rb +7 -1
- data/lib/clevic/action_builder.rb +4 -1
- data/lib/clevic/ar_methods.rb +72 -57
- data/lib/clevic/attribute_list.rb +4 -0
- data/lib/clevic/cache_table.rb +43 -69
- data/lib/clevic/dataset_roller.rb +22 -0
- data/lib/clevic/delegate.rb +11 -5
- data/lib/clevic/delegates/combo_delegate.rb +156 -0
- data/lib/clevic/delegates/distinct_delegate.rb +48 -0
- data/lib/clevic/delegates/relational_delegate.rb +59 -0
- data/lib/clevic/delegates/set_delegate.rb +31 -0
- data/lib/clevic/field.rb +39 -55
- data/lib/clevic/field_valuer.rb +23 -10
- data/lib/clevic/filter_command.rb +22 -36
- data/lib/clevic/framework.rb +37 -0
- data/lib/clevic/generic_format.rb +5 -1
- data/lib/clevic/many_field.rb +28 -3
- data/lib/clevic/model_builder.rb +27 -32
- data/lib/clevic/ordered_dataset.rb +45 -0
- data/lib/clevic/qt.rb +4 -1
- data/lib/clevic/qt/action_builder.rb +9 -1
- data/lib/clevic/qt/browser.rb +1 -1
- data/lib/clevic/qt/clipboard.rb +3 -3
- data/lib/clevic/qt/combo_delegate.rb +25 -89
- data/lib/clevic/qt/delegate.rb +25 -0
- data/lib/clevic/qt/distinct_delegate.rb +5 -23
- data/lib/clevic/qt/extensions.rb +8 -1
- data/lib/clevic/qt/qt_combo_box.rb +58 -0
- data/lib/clevic/qt/relational_delegate.rb +18 -58
- data/lib/clevic/qt/set_delegate.rb +4 -34
- data/lib/clevic/qt/simplest_delegate.rb +19 -0
- data/lib/clevic/qt/table_model.rb +10 -10
- data/lib/clevic/qt/table_view.rb +7 -23
- data/lib/clevic/qt/text_delegate.rb +2 -2
- data/lib/clevic/qt/ui/browser_ui.rb +1 -1
- data/lib/clevic/qt/ui/search_dialog_ui.rb +1 -1
- data/lib/clevic/rails_models_loaders.rb +13 -0
- data/lib/clevic/record.rb +2 -2
- data/lib/clevic/sampler.rb +85 -39
- data/lib/clevic/sequel_ar_adapter.rb +1 -28
- data/lib/clevic/sequel_clevic.rb +68 -0
- data/lib/clevic/sequel_meta.rb +1 -13
- data/lib/clevic/subclasses.rb +18 -0
- data/lib/clevic/swing.rb +2 -1
- data/lib/clevic/swing/action.rb +27 -3
- data/lib/clevic/swing/action_builder.rb +0 -2
- data/lib/clevic/swing/browser.rb +1 -10
- data/lib/clevic/swing/combo_delegate.rb +45 -133
- data/lib/clevic/swing/delegate.rb +2 -0
- data/lib/clevic/swing/distinct_delegate.rb +2 -14
- data/lib/clevic/swing/relational_delegate.rb +2 -20
- data/lib/clevic/swing/set_delegate.rb +13 -28
- data/lib/clevic/swing/table_view.rb +1 -1
- data/lib/clevic/table_model.rb +3 -4
- data/lib/clevic/table_searcher.rb +10 -31
- data/lib/clevic/table_view.rb +97 -43
- data/lib/clevic/ui/browser_ui.rb +133 -0
- data/lib/clevic/ui/search_dialog_ui.rb +106 -0
- data/lib/clevic/version.rb +2 -2
- data/models/accounts_models.rb +24 -21
- data/models/times_models.rb +34 -28
- data/models/times_psql_models.rb +9 -3
- data/models/times_sqlite_models.rb +24 -1
- data/sql/times_sqlite.sql +3 -3
- data/tasks/clevic.rake +2 -2
- data/test/test_cache_table.rb +9 -19
- data/test/test_table_searcher.rb +2 -5
- metadata +95 -91
- data/lib/clevic/order_attribute.rb +0 -63
- data/lib/clevic/qt/boolean_delegate.rb +0 -8
- data/lib/clevic/qt/delegates.rb +0 -1
- data/lib/clevic/qt/item_delegate.rb +0 -66
- data/lib/clevic/sql_dialects.rb +0 -33
- data/tasks/website.rake +0 -25
- data/test/test_order_attribute.rb +0 -62
- data/test/test_sql_dialects.rb +0 -77
@@ -24,9 +24,7 @@ module ActionBuilder
|
|
24
24
|
# connect the action to some code
|
25
25
|
if options.has_key?( :method )
|
26
26
|
action.handler do |event|
|
27
|
-
puts "#{__FILE__}:#{__LINE__}:action.name: #{action.name.inspect}"
|
28
27
|
action_triggered do
|
29
|
-
# active is from Qt checkbox-menu-items
|
30
28
|
send_args = [ options[:method], options.has_key?( :checkable ) ? action.menu_item.selected? : nil ].compact
|
31
29
|
send( *send_args )
|
32
30
|
end
|
data/lib/clevic/swing/browser.rb
CHANGED
@@ -9,8 +9,6 @@ The main application class. Display one tabs for each descendant of Clevic::View
|
|
9
9
|
in Clevic::View.order. DefaultView classes created by Clevic::Record are included.
|
10
10
|
=end
|
11
11
|
class Browser < javax.swing.JFrame
|
12
|
-
#~ slots *%w{dump() refresh_table() filter_by_current(bool) next_tab() previous_tab()}
|
13
|
-
|
14
12
|
attr_reader :menu_edit, :menu_search, :menu_table
|
15
13
|
|
16
14
|
def initialize
|
@@ -104,8 +102,6 @@ class Browser < javax.swing.JFrame
|
|
104
102
|
|
105
103
|
# tab navigation
|
106
104
|
tables_tab.add_change_listener do |change_event|
|
107
|
-
puts "change_event: #{change_event.source.inspect}"
|
108
|
-
puts "change_event.source.selected_index: #{change_event.source.selected_index.inspect}"
|
109
105
|
current_changed
|
110
106
|
# TODO tell exiting tab to save currently editing row/cell
|
111
107
|
end
|
@@ -247,12 +243,7 @@ class Browser < javax.swing.JFrame
|
|
247
243
|
# update the tab, so there's a visual indication of filtering
|
248
244
|
filter_title = ( tab.filtered? ? '| ' : '' ) + tab.title
|
249
245
|
tables_tab.set_title_at( tables_tab.selected_index, filter_title )
|
250
|
-
|
251
|
-
if tab.filtered?
|
252
|
-
tables_tab.set_tool_tip_text_at( tables_tab.selected_index, tab.filtered.status_message )
|
253
|
-
else
|
254
|
-
tables_tab.set_tool_tip_text_at( tables_tab.selected_index, nil )
|
255
|
-
end
|
246
|
+
tables_tab.set_tool_tip_text_at( tables_tab.selected_index, tab.filter_message )
|
256
247
|
end
|
257
248
|
end
|
258
249
|
|
@@ -6,11 +6,19 @@ module Clevic
|
|
6
6
|
# all this just to format a display item...
|
7
7
|
# .... and work around various other Swing stupidities
|
8
8
|
class ComboBox < javax.swing.JComboBox
|
9
|
+
attr_accessor :delegate
|
10
|
+
|
9
11
|
def initialize( field )
|
10
12
|
super()
|
11
13
|
@field = field
|
12
14
|
end
|
13
15
|
|
16
|
+
# For Clevic::ComboDelegate to call
|
17
|
+
def no_insert=( bool )
|
18
|
+
# Swing doesn't have combo policies like Qt.
|
19
|
+
# From what I can see, anyway.
|
20
|
+
end
|
21
|
+
|
14
22
|
# set to true by processKeyBinding when a character
|
15
23
|
# key is pressed. Used by the autocomplete code.
|
16
24
|
attr_reader :typing
|
@@ -52,11 +60,6 @@ class ComboDelegate < Delegate
|
|
52
60
|
@autocompleting = false
|
53
61
|
end
|
54
62
|
|
55
|
-
# Return the GUI component / widget that is displayed when editing.
|
56
|
-
# Usually this will be a combo box widget, but it can be a text editor
|
57
|
-
# in some cases.
|
58
|
-
attr_reader :editor
|
59
|
-
|
60
63
|
def combo_class
|
61
64
|
ComboBox
|
62
65
|
end
|
@@ -66,7 +69,7 @@ class ComboDelegate < Delegate
|
|
66
69
|
true
|
67
70
|
end
|
68
71
|
|
69
|
-
def create_combo_box
|
72
|
+
def create_combo_box( *args )
|
70
73
|
# create a new combo class each time, otherwise
|
71
74
|
# we have to get into managing cleaning out the model
|
72
75
|
# and so on
|
@@ -88,12 +91,6 @@ class ComboDelegate < Delegate
|
|
88
91
|
@original_renderer.getListCellRendererComponent(jlist, display_for( value ), index, selected, cell_has_focus)
|
89
92
|
end
|
90
93
|
|
91
|
-
# Return a string to be shown to the user.
|
92
|
-
# model_value is an item stored in the combo box model.
|
93
|
-
def display_for( model_value )
|
94
|
-
field.transform_attribute( model_value )
|
95
|
-
end
|
96
|
-
|
97
94
|
# return a new text editor. This is for distinct_delegate when there
|
98
95
|
# are no other values to choose from.
|
99
96
|
# TODO move into distinct_delegate then?
|
@@ -105,79 +102,44 @@ class ComboDelegate < Delegate
|
|
105
102
|
|
106
103
|
# Some GUIs (Qt) can just set this. Swing can't.
|
107
104
|
def configure_prefix
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
def init_component( cell_editor = nil )
|
118
|
-
if needs_combo?
|
119
|
-
@editor = create_combo_box
|
120
|
-
|
121
|
-
# add all entries from population
|
122
|
-
population.each do |item|
|
123
|
-
editor << item
|
124
|
-
end
|
125
|
-
|
126
|
-
# create a nil entry
|
127
|
-
add_nil_item if allow_null?
|
128
|
-
|
129
|
-
# allow prefix matching from the keyboard
|
130
|
-
configure_prefix
|
131
|
-
|
132
|
-
# don't allow text editing if restricted
|
133
|
-
configure_editable
|
134
|
-
|
135
|
-
# set the correct value in the list
|
136
|
-
select_current
|
137
|
-
|
138
|
-
# pick up events from editor
|
139
|
-
# but only after all the other config, otherwise we get
|
140
|
-
# events triggered by the setup, which isn't helpful.
|
141
|
-
editor.editor.editor_component.document.add_document_listener do |event|
|
142
|
-
# don't do anything if autocomplete manipulations are in progress
|
143
|
-
unless @autocompleting || !editor.typing
|
144
|
-
if event.type == javax.swing.event.DocumentEvent::EventType::REMOVE
|
145
|
-
invoke_later do
|
146
|
-
repopulate
|
147
|
-
end
|
148
|
-
else
|
149
|
-
# only suggest on inserts and updates. Not on deletes.
|
150
|
-
filter_prefix( editor.editor.item )
|
105
|
+
# pick up events from editor
|
106
|
+
# but only after all the other config, otherwise we get
|
107
|
+
# events triggered by the setup, which isn't helpful.
|
108
|
+
editor.editor.editor_component.document.add_document_listener do |event|
|
109
|
+
# don't do anything if autocomplete manipulations are in progress
|
110
|
+
unless @autocompleting || !editor.typing
|
111
|
+
if event.type == javax.swing.event.DocumentEvent::EventType::REMOVE
|
112
|
+
invoke_later do
|
113
|
+
repopulate
|
151
114
|
end
|
115
|
+
else
|
116
|
+
# only suggest on inserts and updates. Not on deletes.
|
117
|
+
filter_prefix( editor.editor.item )
|
152
118
|
end
|
153
119
|
end
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
nil
|
176
|
-
else
|
177
|
-
line_editor( edit_value )
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def framework_setup( *args )
|
124
|
+
# turn on typing the the text field of the combo
|
125
|
+
# otherwise prefix matching doesn't work
|
126
|
+
editor.editable = true
|
127
|
+
|
128
|
+
# catch the enter key action event
|
129
|
+
editor.editor.editor_component.add_action_listener do |event|
|
130
|
+
cell_editor.andand.stopCellEditing
|
131
|
+
end
|
132
|
+
|
133
|
+
# set initial focus and selection in edit part of combo
|
134
|
+
editor.editor.editor_component.with do |text_edit|
|
135
|
+
unless text_edit.text.nil?
|
136
|
+
# highlight the suggested match, and leave caret
|
137
|
+
# at the end of the selected text
|
138
|
+
text_edit.caret_position = 0
|
139
|
+
text_edit.move_caret_position( text_edit.text.length )
|
140
|
+
text_edit.request_focus_in_window
|
178
141
|
end
|
179
142
|
end
|
180
|
-
editor
|
181
143
|
end
|
182
144
|
|
183
145
|
# Recreate the model and fill it with anything in population that
|
@@ -260,77 +222,27 @@ class ComboDelegate < Delegate
|
|
260
222
|
editor.hide_popup if is_combo?
|
261
223
|
end
|
262
224
|
|
263
|
-
# returns true if the editor allows values outside of a predefined
|
264
|
-
# range, false otherwise.
|
265
|
-
def restricted?
|
266
|
-
false
|
267
|
-
end
|
268
|
-
|
269
|
-
# TODO fetch this from the model definition
|
270
|
-
def allow_null?
|
271
|
-
true
|
272
|
-
end
|
273
|
-
|
274
|
-
# Subclasses should override this to fill the combo box
|
275
|
-
# list with values.
|
276
|
-
# TODO resolve whether the current item is included, even
|
277
|
-
# if it isn't in the population already, ie it's excluded
|
278
|
-
# by date or something like that.
|
279
|
-
def population
|
280
|
-
raise "subclass responsibility"
|
281
|
-
end
|
282
|
-
|
283
|
-
# return true if this delegate needs a combo, false otherwise
|
284
|
-
def needs_combo?
|
285
|
-
raise "subclass responsibility"
|
286
|
-
end
|
287
|
-
|
288
225
|
def is_combo?
|
289
226
|
# Assume we're a combo if we don't have an editor yet, otherwise
|
290
227
|
# check
|
291
228
|
editor.nil? || editor.is_a?( javax.swing.JComboBox )
|
292
229
|
end
|
293
230
|
|
294
|
-
# return true if this field has no data (needs_combo? is false)
|
295
|
-
# and is at the same time restricted (ie needs data from somewhere else)
|
296
|
-
def empty_set?
|
297
|
-
!needs_combo? && restricted?
|
298
|
-
end
|
299
|
-
|
300
|
-
# the message to display if the set is empty, and
|
301
|
-
# the delegate is restricted to a predefined set.
|
302
|
-
def empty_set_message
|
303
|
-
raise "subclass responsibility"
|
304
|
-
end
|
305
|
-
|
306
|
-
# if this delegate has an empty set, return the message, otherwise
|
307
|
-
# return nil.
|
308
|
-
def if_empty_message
|
309
|
-
empty_set_message if empty_set?
|
310
|
-
end
|
311
|
-
|
312
|
-
def add_nil_item
|
313
|
-
editor << nil unless editor.include?( nil )
|
314
|
-
end
|
315
|
-
|
316
|
-
def select_current
|
317
|
-
editor.selected_item = attribute_value
|
318
|
-
end
|
319
|
-
|
320
231
|
def value
|
321
232
|
# editor could be either a combo or a line (DistinctDelegate with no values yet)
|
322
233
|
if is_combo?
|
323
234
|
if restricted?
|
324
235
|
editor.selected_item
|
325
236
|
else
|
326
|
-
puts "#{__FILE__}:#{__LINE__}:get the editor's text field value. Take away this output when we know it works. Ie when this gets printed."
|
327
237
|
editor.editor.item
|
328
238
|
end
|
329
239
|
else
|
330
|
-
puts "#{__FILE__}:#{__LINE__}:line item value: #{editor.text}"
|
240
|
+
puts "#{__FILE__}:#{__LINE__}:line item value: #{editor.text}. Take this away when it's printed."
|
331
241
|
editor.text
|
332
242
|
end
|
333
243
|
end
|
334
244
|
end
|
335
245
|
|
336
246
|
end
|
247
|
+
|
248
|
+
require 'clevic/delegates/combo_delegate.rb'
|
@@ -7,24 +7,12 @@ module Clevic
|
|
7
7
|
# :frequency can be set as an option. Boolean. If it's true
|
8
8
|
# the options are sorted in order of most frequently used first.
|
9
9
|
class DistinctDelegate < ComboDelegate
|
10
|
-
def needs_combo?
|
11
|
-
# works except when there is a '' in the column
|
12
|
-
entity_class.adaptor.count( attribute.to_s, find_options ) > 0
|
13
|
-
end
|
14
|
-
|
15
10
|
# strings are stored in the model
|
16
11
|
def display_for( model_value )
|
17
12
|
model_value
|
18
13
|
end
|
19
|
-
|
20
|
-
def population
|
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, attribute_value, field.description, field.frequency, find_options ) do |row|
|
25
|
-
row[attribute]
|
26
|
-
end
|
27
|
-
end
|
28
14
|
end
|
29
15
|
|
30
16
|
end
|
17
|
+
|
18
|
+
require 'clevic/delegates/distinct_delegate'
|
@@ -11,10 +11,6 @@ module Clevic
|
|
11
11
|
class RelationalDelegate < ComboDelegate
|
12
12
|
def initialize( field )
|
13
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
14
|
end
|
19
15
|
|
20
16
|
# use the Clevic::ComboBox class because JCombobox is remarkably stupid
|
@@ -23,22 +19,6 @@ class RelationalDelegate < ComboDelegate
|
|
23
19
|
ComboBox
|
24
20
|
end
|
25
21
|
|
26
|
-
def needs_combo?
|
27
|
-
field.related_class.adaptor.count( :conditions => find_options[:conditions] ) > 0
|
28
|
-
end
|
29
|
-
|
30
|
-
def empty_set_message
|
31
|
-
"There must be records in #{field.related_class.name.humanize} for this field to be editable."
|
32
|
-
end
|
33
|
-
|
34
|
-
def population
|
35
|
-
# add set of all possible related entities,
|
36
|
-
# including the currently selected entity
|
37
|
-
ary = field.related_class.adaptor.find( :all, find_options )
|
38
|
-
ary << attribute_value unless ary.include?( attribute_value )
|
39
|
-
ary
|
40
|
-
end
|
41
|
-
|
42
22
|
# don't allow new values
|
43
23
|
def restricted?
|
44
24
|
true
|
@@ -46,3 +26,5 @@ class RelationalDelegate < ComboDelegate
|
|
46
26
|
end
|
47
27
|
|
48
28
|
end
|
29
|
+
|
30
|
+
require 'clevic/delegates/relational_delegate.rb'
|
@@ -5,37 +5,22 @@ module Clevic
|
|
5
5
|
# A Combo box which allows a set of values. May or may not
|
6
6
|
# be restricted to the set.
|
7
7
|
class SetDelegate < ComboDelegate
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
field.restricted || false
|
20
|
-
end
|
21
|
-
|
22
|
-
def population
|
23
|
-
field.set_for( entity ).map do |item|
|
24
|
-
if item.is_a?( Array )
|
25
|
-
puts "#{__FILE__}:#{__LINE__}:probably can't deal with item: #{item.inspect}"
|
26
|
-
# this is a hash-like set, so use key as db value
|
27
|
-
# and value as display value
|
28
|
-
class << item
|
29
|
-
def toString; last; end
|
30
|
-
end
|
31
|
-
else
|
32
|
-
class << item
|
33
|
-
def toString; self; end
|
34
|
-
end
|
8
|
+
def item_to_editor( item )
|
9
|
+
if item.is_a?( Array )
|
10
|
+
puts "#{__FILE__}:#{__LINE__}:probably can't deal with item: #{item.inspect}"
|
11
|
+
# this is a hash-like set, so use key as db value
|
12
|
+
# and value as display value
|
13
|
+
class << item
|
14
|
+
def toString; last; end
|
15
|
+
end
|
16
|
+
else
|
17
|
+
class << item
|
18
|
+
def toString; self; end
|
35
19
|
end
|
36
|
-
item
|
37
20
|
end
|
38
21
|
end
|
39
22
|
end
|
40
23
|
|
41
24
|
end
|
25
|
+
|
26
|
+
require 'clevic/delegates/set_delegate.rb'
|
@@ -286,7 +286,7 @@ class TableView < javax.swing.JScrollPane
|
|
286
286
|
|
287
287
|
# calculate the size of the column from the string value of the data
|
288
288
|
def column_width( col, data )
|
289
|
-
@jtable.getFontMetrics( @jtable.font).stringWidth( data ) + 5
|
289
|
+
@jtable.getFontMetrics( @jtable.font ).stringWidth( data.to_s ) + 5
|
290
290
|
end
|
291
291
|
|
292
292
|
def trim_middle( value, max = 40 )
|
data/lib/clevic/table_model.rb
CHANGED
@@ -140,9 +140,9 @@ class TableModel
|
|
140
140
|
emit_data_error( index, nil, $!.message )
|
141
141
|
end
|
142
142
|
|
143
|
-
def reload_data(
|
143
|
+
def reload_data( dataset = nil, &dataset_block )
|
144
144
|
# renew cache. All records will be dropped and reloaded.
|
145
|
-
self.collection = self.cache_table.renew(
|
145
|
+
self.collection = self.cache_table.renew( dataset, &dataset_block )
|
146
146
|
# tell the UI we had a major data change
|
147
147
|
reset
|
148
148
|
end
|
@@ -151,9 +151,8 @@ class TableModel
|
|
151
151
|
# at the moment this only returns the first index found
|
152
152
|
# TODO could handle dataset creation better
|
153
153
|
def search( start_index, search_criteria )
|
154
|
-
ordered_dataset = entity_class.dataset.order( *cache_table.order_attributes.map{|oa| oa.attribute.to_sym.send( oa.direction ) } )
|
155
154
|
searcher = TableSearcher.new(
|
156
|
-
|
155
|
+
cache_table.dataset,
|
157
156
|
search_criteria,
|
158
157
|
start_index.field
|
159
158
|
)
|