ruber 0.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +339 -0
- data/INSTALL +137 -0
- data/LICENSE +8 -0
- data/bin/ruber +65 -0
- data/data/share/apps/ruber/core_components.yaml +31 -0
- data/data/share/apps/ruber/ruberui.rc +109 -0
- data/data/share/icons/ruber.png +0 -0
- data/data/share/pixmaps/ruby.png +0 -0
- data/icons/ruber-16.png +0 -0
- data/icons/ruber-32.png +0 -0
- data/icons/ruber-48.png +0 -0
- data/icons/ruber-8.png +0 -0
- data/lib/ruber/application/application.rb +288 -0
- data/lib/ruber/application/plugin.yaml +11 -0
- data/lib/ruber/component_manager.rb +899 -0
- data/lib/ruber/config/config.rb +82 -0
- data/lib/ruber/config/plugin.yaml +3 -0
- data/lib/ruber/document_project.rb +209 -0
- data/lib/ruber/documents/document_list.rb +416 -0
- data/lib/ruber/documents/plugin.yaml +4 -0
- data/lib/ruber/editor/document.rb +506 -0
- data/lib/ruber/editor/editor_view.rb +167 -0
- data/lib/ruber/editor/ktexteditor_wrapper.rb +202 -0
- data/lib/ruber/exception_widgets.rb +245 -0
- data/lib/ruber/external_program_plugin.rb +397 -0
- data/lib/ruber/filtered_output_widget.rb +342 -0
- data/lib/ruber/gui_states_handler.rb +231 -0
- data/lib/ruber/kde_config_option_backend.rb +167 -0
- data/lib/ruber/kde_sugar.rb +249 -0
- data/lib/ruber/main_window/choose_plugins_dlg.rb +353 -0
- data/lib/ruber/main_window/main_window.rb +524 -0
- data/lib/ruber/main_window/main_window_actions.rb +537 -0
- data/lib/ruber/main_window/main_window_internal.rb +239 -0
- data/lib/ruber/main_window/open_file_in_project_dlg.rb +212 -0
- data/lib/ruber/main_window/output_color_widget.rb +35 -0
- data/lib/ruber/main_window/plugin.yaml +58 -0
- data/lib/ruber/main_window/save_modified_files_dlg.rb +89 -0
- data/lib/ruber/main_window/status_bar.rb +156 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.rb +90 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.ui +77 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.rb +108 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.ui +89 -0
- data/lib/ruber/main_window/ui/new_project_widget.rb +119 -0
- data/lib/ruber/main_window/ui/new_project_widget.ui +178 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +109 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.ui +168 -0
- data/lib/ruber/main_window/ui/output_color_widget.rb +241 -0
- data/lib/ruber/main_window/ui/output_color_widget.ui +204 -0
- data/lib/ruber/main_window/workspace.rb +442 -0
- data/lib/ruber/output_widget.rb +1093 -0
- data/lib/ruber/plugin.rb +264 -0
- data/lib/ruber/plugin_like.rb +589 -0
- data/lib/ruber/plugin_specification.rb +106 -0
- data/lib/ruber/plugin_specification_reader.rb +451 -0
- data/lib/ruber/project.rb +493 -0
- data/lib/ruber/project_backend.rb +105 -0
- data/lib/ruber/projects/plugin.yaml +11 -0
- data/lib/ruber/projects/project_files_list.rb +314 -0
- data/lib/ruber/projects/project_files_widget.rb +301 -0
- data/lib/ruber/projects/project_list.rb +314 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +74 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.ui +61 -0
- data/lib/ruber/projects/ui/project_files_widget.rb +117 -0
- data/lib/ruber/projects/ui/project_files_widget.ui +123 -0
- data/lib/ruber/qt_sugar.rb +673 -0
- data/lib/ruber/settings_container.rb +515 -0
- data/lib/ruber/settings_dialog.rb +244 -0
- data/lib/ruber/settings_dialog_manager.rb +503 -0
- data/lib/ruber/utils.rb +414 -0
- data/lib/ruber/yaml_option_backend.rb +159 -0
- data/outsider_files +15 -0
- data/plugins/autosave/autosave.rb +404 -0
- data/plugins/autosave/plugin.yaml +16 -0
- data/plugins/autosave/ui/autosave_config_widget.rb +83 -0
- data/plugins/autosave/ui/autosave_config_widget.ui +68 -0
- data/plugins/command/command.png +0 -0
- data/plugins/command/command.rb +74 -0
- data/plugins/command/plugin.yaml +11 -0
- data/plugins/find_in_files/find_in_files.rb +337 -0
- data/plugins/find_in_files/find_in_files_dlg.rb +411 -0
- data/plugins/find_in_files/find_in_files_ui.rc +11 -0
- data/plugins/find_in_files/find_in_files_widgets.rb +485 -0
- data/plugins/find_in_files/plugin.yaml +23 -0
- data/plugins/find_in_files/ui/config_widget.rb +58 -0
- data/plugins/find_in_files/ui/config_widget.ui +41 -0
- data/plugins/find_in_files/ui/find_in_files_widget.rb +260 -0
- data/plugins/find_in_files/ui/find_in_files_widget.ui +324 -0
- data/plugins/project_browser/plugin.yaml +10 -0
- data/plugins/project_browser/project_browser.rb +245 -0
- data/plugins/rake/plugin.yaml +39 -0
- data/plugins/rake/rake.png +0 -0
- data/plugins/rake/rake.rb +567 -0
- data/plugins/rake/rake_extension.rb +153 -0
- data/plugins/rake/rake_widgets.rb +615 -0
- data/plugins/rake/rakeui.rc +27 -0
- data/plugins/rake/ui/add_quick_task_widget.rb +71 -0
- data/plugins/rake/ui/add_quick_task_widget.ui +59 -0
- data/plugins/rake/ui/choose_task_widget.rb +77 -0
- data/plugins/rake/ui/choose_task_widget.ui +72 -0
- data/plugins/rake/ui/config_widget.rb +127 -0
- data/plugins/rake/ui/config_widget.ui +123 -0
- data/plugins/rake/ui/project_widget.rb +217 -0
- data/plugins/rake/ui/project_widget.ui +246 -0
- data/plugins/rspec/plugin.yaml +30 -0
- data/plugins/rspec/rspec.png +0 -0
- data/plugins/rspec/rspec.rb +945 -0
- data/plugins/rspec/rspec.svg +90 -0
- data/plugins/rspec/rspecui.rc +20 -0
- data/plugins/rspec/ruber_rspec_formatter.rb +312 -0
- data/plugins/rspec/ui/rspec_project_widget.rb +170 -0
- data/plugins/rspec/ui/rspec_project_widget.ui +193 -0
- data/plugins/ruby_development/plugin.yaml +27 -0
- data/plugins/ruby_development/ruby_development.png +0 -0
- data/plugins/ruby_development/ruby_development.rb +453 -0
- data/plugins/ruby_development/ruby_developmentui.rc +19 -0
- data/plugins/ruby_development/ui/project_widget.rb +112 -0
- data/plugins/ruby_development/ui/project_widget.ui +108 -0
- data/plugins/ruby_runner/config_widget.rb +116 -0
- data/plugins/ruby_runner/plugin.yaml +26 -0
- data/plugins/ruby_runner/project_widget.rb +62 -0
- data/plugins/ruby_runner/ruby.png +0 -0
- data/plugins/ruby_runner/ruby_interpretersui.rc +26 -0
- data/plugins/ruby_runner/ruby_runner.rb +411 -0
- data/plugins/ruby_runner/ui/config_widget.rb +92 -0
- data/plugins/ruby_runner/ui/config_widget.ui +91 -0
- data/plugins/ruby_runner/ui/project_widget.rb +60 -0
- data/plugins/ruby_runner/ui/project_widget.ui +48 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +59 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.ui +44 -0
- data/plugins/state/plugin.yaml +28 -0
- data/plugins/state/state.rb +520 -0
- data/plugins/state/ui/config_widget.rb +92 -0
- data/plugins/state/ui/config_widget.ui +89 -0
- data/plugins/syntax_checker/plugin.yaml +18 -0
- data/plugins/syntax_checker/syntax_checker.rb +662 -0
- data/ruber.desktop +10 -0
- data/spec/annotation_model_spec.rb +174 -0
- data/spec/common.rb +119 -0
- data/spec/component_manager_spec.rb +1259 -0
- data/spec/document_list_spec.rb +626 -0
- data/spec/document_project_spec.rb +373 -0
- data/spec/document_spec.rb +779 -0
- data/spec/editor_view_spec.rb +167 -0
- data/spec/external_program_plugin_spec.rb +676 -0
- data/spec/filtered_output_widget_spec.rb +642 -0
- data/spec/gui_states_handler_spec.rb +304 -0
- data/spec/kde_config_option_backend_spec.rb +214 -0
- data/spec/kde_sugar_spec.rb +101 -0
- data/spec/ktexteditor_wrapper_spec.rb +305 -0
- data/spec/output_widget_spec.rb +1703 -0
- data/spec/plugin_spec.rb +1393 -0
- data/spec/plugin_specification_reader_spec.rb +1765 -0
- data/spec/plugin_specification_spec.rb +401 -0
- data/spec/project_backend_spec.rb +172 -0
- data/spec/project_files_list_spec.rb +401 -0
- data/spec/project_list_spec.rb +511 -0
- data/spec/project_spec.rb +990 -0
- data/spec/qt_sugar_spec.rb +328 -0
- data/spec/settings_container_spec.rb +617 -0
- data/spec/settings_dialog_manager_spec.rb +773 -0
- data/spec/settings_dialog_spec.rb +419 -0
- data/spec/state_spec.rb +991 -0
- data/spec/utils_spec.rb +406 -0
- data/spec/workspace_spec.rb +869 -0
- data/spec/yaml_option_backend_spec.rb +246 -0
- metadata +284 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2010 by Stefano Crocco
|
3
|
+
stefano.crocco@alice.it
|
4
|
+
|
5
|
+
This program is free software; you can redistribute it andor modify
|
6
|
+
it under the terms of the GNU General Public License as published by
|
7
|
+
the Free Software Foundation; either version 2 of the License, or
|
8
|
+
(at your option) any later version.
|
9
|
+
|
10
|
+
This program is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with this program; if not, write to the
|
17
|
+
Free Software Foundation, Inc.,
|
18
|
+
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
+
=end
|
20
|
+
|
21
|
+
require 'ruber/settings_dialog_manager'
|
22
|
+
|
23
|
+
module Ruber
|
24
|
+
|
25
|
+
=begin rdoc
|
26
|
+
Dialog used to change the options contained in an SettingsContainer. It is a specialized
|
27
|
+
<tt>KDE::PageDialog</tt>.
|
28
|
+
|
29
|
+
The widgets added to the option container are displayed in pages corresponding to
|
30
|
+
the captions included in the widgets' description. If more than one widget has
|
31
|
+
the same caption, they're displayed in the same page, one below the other (using
|
32
|
+
a Qt::VBoxLayout). The icon used for each page is the first one found among the
|
33
|
+
widgets in that page.
|
34
|
+
|
35
|
+
The process of keeping the options in sync between the widgets and the option container
|
36
|
+
can be automatized using the functionality provided by the internal SettingsDialogManager.
|
37
|
+
If this automatic management can't be achieved for a particular option (for example,
|
38
|
+
because the value should be obtained combining the data of more than one widget),
|
39
|
+
you can still automatically manage the other widgets and integrate the manual
|
40
|
+
management of the widgets which need it with the automatic management. To do so,
|
41
|
+
you'll need to define a read_settings, a store_settings and a read_default_settings
|
42
|
+
method in your widget. In this case, you can access the dialog using the @settings_dialog
|
43
|
+
instance variable, which is created by this class when the widget is created.
|
44
|
+
|
45
|
+
===Manual option management example
|
46
|
+
Suppose you have an option called <tt>:default_path</tt> in the :general group which stores a path. In
|
47
|
+
your widget, however, the path is split in two parts: the directory and the filename,
|
48
|
+
which are shown in two line edit widgets, called respectively <tt>default_dir_widget</tt>
|
49
|
+
and <tt>default_file_widget</tt>. Since the automatic option management system assumes
|
50
|
+
that each option corresponds to a single widget, you can't use it. So, you define
|
51
|
+
a <tt>read_settings</tt>, a <tt>store_settings</tt> and a <tt>read_default_settings</tt>
|
52
|
+
method in your widget class like this:
|
53
|
+
|
54
|
+
class MyWidget < Qt::Widget
|
55
|
+
|
56
|
+
def initialize parent = nil
|
57
|
+
super
|
58
|
+
@default_dir_widget = KDE::LineEdit.new self
|
59
|
+
@default_file_widget = KDE::LineEdit.new self
|
60
|
+
end
|
61
|
+
|
62
|
+
def read_settings
|
63
|
+
path = @settings_dialog.settings_container[:general, :default_path]
|
64
|
+
@default_dir_widget.text = File.dirname(path)
|
65
|
+
@default_file_widget.text = File.basename(path)
|
66
|
+
end
|
67
|
+
|
68
|
+
def store_settings
|
69
|
+
path = File.join @default_dir_widget.text, @default_file_widget.text
|
70
|
+
@settings_dialog.settings_container[:general, :default_path] = path
|
71
|
+
end
|
72
|
+
|
73
|
+
def read_default_settings
|
74
|
+
path = @settings_dialog.settings_container.default(:general, :default_path)
|
75
|
+
@default_dir_widget.text = File.dirname(path)
|
76
|
+
@default_file_widget.text = File.basename(path)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
Note that the <tt>@settings_dialog</tt> instance variable has been automatically
|
82
|
+
created by the dialog when the widget has been created and contains a reference
|
83
|
+
to the dialog itself.
|
84
|
+
|
85
|
+
=end
|
86
|
+
class SettingsDialog < KDE::PageDialog
|
87
|
+
|
88
|
+
slots :store_settings, :read_default_settings
|
89
|
+
|
90
|
+
=begin rdoc
|
91
|
+
The option container. This method can be used by the widgets which need to define
|
92
|
+
the read_settings, store_settings and read_default_settings methods to access the
|
93
|
+
option container of the dialog.
|
94
|
+
=end
|
95
|
+
attr_accessor :container
|
96
|
+
alias_method :settings_container, :container
|
97
|
+
|
98
|
+
=begin rdoc
|
99
|
+
Creates a new SettingsDialog. The dialog will store settings in the SettingsContainer
|
100
|
+
_container_, manage the options _options_ and display the widgets _widgets_.
|
101
|
+
_options_ is an array of option objects (see SettingsContainer#add_option).
|
102
|
+
_widgets_ is an array containing
|
103
|
+
the description of the widgets to be displayed in the dialog (see SettingsContainer#add_widget).
|
104
|
+
|
105
|
+
This method defines a new instance variable for each widget it creates. The variable
|
106
|
+
is called @settings_dialog and contains a reference to the dialog itself. It can be
|
107
|
+
used from custom widgets' implementation of read_settings, store_settings and
|
108
|
+
read_default_settings methods to access the option container corresponding to
|
109
|
+
the dialog.
|
110
|
+
=end
|
111
|
+
def initialize container, options, widgets, title = nil
|
112
|
+
super Ruber[:main_window]
|
113
|
+
self.buttons = Ok|Apply|Cancel|Default
|
114
|
+
self.window_title = title if title
|
115
|
+
@container = container
|
116
|
+
grouped_widgets = Hash.new{|h, k| h[k] = []}
|
117
|
+
widgets.each{|w| grouped_widgets[w.caption] << w }
|
118
|
+
@page_items = []
|
119
|
+
@widgets = Hash.new{|h, k| h[k] = []}
|
120
|
+
grouped_widgets.keys.sort.each do |c|
|
121
|
+
icon = nil
|
122
|
+
page = Qt::Widget.new self
|
123
|
+
page.layout = Qt::VBoxLayout.new page
|
124
|
+
grouped_widgets[c].each do |w|
|
125
|
+
widget = if w.respond_to?(:code) and w.code then widget_from_code(w.code)
|
126
|
+
else widget_from_class(w.class_obj)
|
127
|
+
end
|
128
|
+
widget.parent = page
|
129
|
+
widget.instance_variable_set :@settings_dialog, self
|
130
|
+
@widgets[c] << widget
|
131
|
+
page.layout.add_widget widget
|
132
|
+
icon ||= KDE::Icon.new w.pixmap if w.pixmap rescue nil
|
133
|
+
end
|
134
|
+
@page_items << add_page(page, c)
|
135
|
+
@page_items[-1].icon = icon if icon
|
136
|
+
end
|
137
|
+
@manager = SettingsDialogManager.new self, options, @widgets.values.flatten
|
138
|
+
connect self, SIGNAL(:okClicked), self, SLOT(:store_settings)
|
139
|
+
connect self, SIGNAL(:applyClicked), self, SLOT(:store_settings)
|
140
|
+
connect self, SIGNAL(:defaultClicked), self, SLOT(:read_default_settings)
|
141
|
+
enable_button_apply false
|
142
|
+
end
|
143
|
+
|
144
|
+
=begin rdoc
|
145
|
+
Returns an array containing all the widgets created for the dialog, that is the
|
146
|
+
widgets created from the data in the third argument of the constructor.
|
147
|
+
=end
|
148
|
+
def widgets
|
149
|
+
res = []
|
150
|
+
@widgets.each_value{|a| res += a}
|
151
|
+
res
|
152
|
+
end
|
153
|
+
|
154
|
+
=begin rdoc
|
155
|
+
This method reads settings from the option container and displays them in the widgets.
|
156
|
+
It calls the read_settings method of the option dialog manager and the read_settings
|
157
|
+
method of each widgets which provide it.
|
158
|
+
=end
|
159
|
+
def read_settings
|
160
|
+
@manager.read_settings
|
161
|
+
@widgets.values.flatten.each{|w| w.read_settings if w.respond_to? :read_settings}
|
162
|
+
end
|
163
|
+
|
164
|
+
=begin rdoc
|
165
|
+
This method takes the values from the dialog widgets and stores them in to option container.
|
166
|
+
It calls the store_settings method of the option dialog manager and the store_settings
|
167
|
+
method of each widgets which provide it, then calls the write method of the container
|
168
|
+
so that the options are written to file.
|
169
|
+
=end
|
170
|
+
def store_settings
|
171
|
+
@manager.store_settings
|
172
|
+
@widgets.values.flatten.each{|w| w.store_settings if w.respond_to? :store_settings}
|
173
|
+
@container.write
|
174
|
+
end
|
175
|
+
|
176
|
+
=begin rdoc
|
177
|
+
This method works as read_settings except for the fact that it reads the default
|
178
|
+
values of the options instad of the values set by the user.
|
179
|
+
=end
|
180
|
+
def read_default_settings
|
181
|
+
@manager.read_default_settings
|
182
|
+
@widgets.values.flatten.each{|w| w.read_default_settings if w.respond_to? :read_default_settings}
|
183
|
+
end
|
184
|
+
|
185
|
+
=begin rdoc
|
186
|
+
Override of <tt>KDE::PageDialog#exec</tt> which makes sure the first page is the current
|
187
|
+
page and gives focus to the first widget on the page and reads the settings from
|
188
|
+
the option container.
|
189
|
+
|
190
|
+
This is needed because usually the dialog object isn't deleted after having been
|
191
|
+
closed, so the interface should be updated manually.
|
192
|
+
=end
|
193
|
+
def exec
|
194
|
+
read_settings
|
195
|
+
if @page_items[0]
|
196
|
+
self.current_page = @page_items[0]
|
197
|
+
self.current_page.widget.layout.item_at(0).widget.set_focus
|
198
|
+
end
|
199
|
+
super
|
200
|
+
end
|
201
|
+
|
202
|
+
=begin rdoc
|
203
|
+
Override of <tt>KDE::PageDialog#show</tt> which makes sure the first page is the current
|
204
|
+
page and gives focus to the first widget on the page and reads the settings from
|
205
|
+
the option container.
|
206
|
+
|
207
|
+
This is needed because usually the dialog object isn't deleted after having been
|
208
|
+
closed, so the interface should be updated manually.
|
209
|
+
=end
|
210
|
+
def show
|
211
|
+
read_settings
|
212
|
+
self.current_page = @page_items[0]
|
213
|
+
self.current_page.widget.layout.item_at(0).widget.set_focus
|
214
|
+
super
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
=begin rdoc
|
220
|
+
Method called to create a widget which is specified using a <tt>code</tt> entry
|
221
|
+
in the third argument to initialize. _code_ is the string containing the code.
|
222
|
+
|
223
|
+
It evaluates _code_ in the toplevel binding and returns the resulting object. Derived
|
224
|
+
classes may override it to change this behaviour (for example, they may want to
|
225
|
+
evaluate the code in another binding).
|
226
|
+
=end
|
227
|
+
def widget_from_code code
|
228
|
+
eval code, TOPLEVEL_BINDING
|
229
|
+
end
|
230
|
+
|
231
|
+
=begin rdoc
|
232
|
+
Method called to create a widget which is specified using the <tt>class_obj</tt> entry
|
233
|
+
in the third argument to initialize. _cls_ is the widget's class.
|
234
|
+
|
235
|
+
The widget is created simply calling new on _cls_ (no arguments are passed to it).
|
236
|
+
Derived classes may want to override it to change this behaviour (for example,
|
237
|
+
they may want to pass some argument to +new+).
|
238
|
+
=end
|
239
|
+
def widget_from_class cls
|
240
|
+
cls.new
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,503 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2010 by Stefano Crocco
|
3
|
+
stefano.crocco@alice.it
|
4
|
+
|
5
|
+
This program is free software; you can redistribute it andor modify
|
6
|
+
it under the terms of the GNU General Public License as published by
|
7
|
+
the Free Software Foundation; either version 2 of the License, or
|
8
|
+
(at your option) any later version.
|
9
|
+
|
10
|
+
This program is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with this program; if not, write to the
|
17
|
+
Free Software Foundation, Inc.,
|
18
|
+
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
+
=end
|
20
|
+
|
21
|
+
require 'facets/string/snakecase'
|
22
|
+
require 'yaml'
|
23
|
+
|
24
|
+
module Ruber
|
25
|
+
|
26
|
+
=begin rdoc
|
27
|
+
Class which takes care of automatically syncronize (some of) the options in
|
28
|
+
an SettingsContainer with the widgets in the SettingsDialog.
|
29
|
+
|
30
|
+
In the following documentation, the widgets passed as argument to the constructor
|
31
|
+
will be called <i>upper level widgets</i>, because they're the topmost widgets
|
32
|
+
this class is interested in. The term <i>child widget</i> will be used to refer
|
33
|
+
to any widget which is child of an upper level widget, while the generic _widget_
|
34
|
+
can refer to both. The upper <i>level widget corresponding to </i> a widget is
|
35
|
+
the upper level widget the given widget is child of (or the widget itself if it
|
36
|
+
already is an upper level widget)
|
37
|
+
|
38
|
+
To syncronize options and widgets contents, this class looks among the upper level
|
39
|
+
widgets and their children for the widget which have an <tt>object_name</tt> of the form
|
40
|
+
<tt>_group__option</tt>, that is: an underscore, a group name, two underscores,
|
41
|
+
an option name (both the group and the option name can contain underscores. This
|
42
|
+
is the reason for which _two_ underscores are used to separate them). Such a
|
43
|
+
widget is associated with the option with name _name_ belonging to the group
|
44
|
+
_group_.
|
45
|
+
|
46
|
+
Having a widget associated with an option means three things:
|
47
|
+
1. when the widget emits some signals (which mean that the settings have changed,
|
48
|
+
the dialog's Apply button is enabled)
|
49
|
+
2. when the dialog is displayed, the widget is updated so that it displays the
|
50
|
+
value stored in the SettingsContainer
|
51
|
+
3. when the Apply or the OK button of the dialog is clicked, the option in the
|
52
|
+
SettingsContainer is updated with the value of the widget.
|
53
|
+
|
54
|
+
For all this to happen, who writes the widget (or the upper level widget the widget
|
55
|
+
is child of) must give it several custom properties
|
56
|
+
(this can be done with the UI designer, if it is used to create the widget, or
|
57
|
+
by hand). These properties are:
|
58
|
+
* signal: it's a string containing the signal(s) on which the Apply button of the
|
59
|
+
dialog will be enabled. If more than one signal should be used, you can specify
|
60
|
+
them using a YAML array (that is, enclose them in square brackets and separate
|
61
|
+
them with a comma followed by one space). If the widget has only a single signal
|
62
|
+
with that name, you can omit the signal's signature, otherwise you need to include
|
63
|
+
it (for example, <tt>Qt::LineEdit</tt> has a single signall called <tt>textChanged</tt>,
|
64
|
+
so you can simply use that instead of <tt>textChanged(QString)</tt>. On the other
|
65
|
+
hand, <tt>Qt::ComboBox</tt> has two signals called +currentIndexChanged+, so
|
66
|
+
you must fully specify them: <tt>currentIndexChanged(QString)</tt> or
|
67
|
+
<tt>currentIndexChanged(int)</tt>).
|
68
|
+
* read: is the method to use to sync the value in the widget with that in
|
69
|
+
the SettingsContainer. The reason of the name "read" is that it is used to
|
70
|
+
read the value from the container.
|
71
|
+
* store: is the method to use to sync the value in the SettingsContainer with that
|
72
|
+
in the widget.
|
73
|
+
* access: a method name to derive the +read+ and the +store+ methods from. The
|
74
|
+
+store+ method has the same name as this property, while the +read+
|
75
|
+
method has the same name with ending "?" and "!" removed and an ending
|
76
|
+
"=" added.
|
77
|
+
|
78
|
+
In the documentation for this class, the term <i>read method</i> refers to the
|
79
|
+
method specified in the +read+ property or derived from the +access+ property by
|
80
|
+
adding the <tt>=</tt> to it. The term <i>store method</i> refers to the
|
81
|
+
method specified in the +store+ or +access+ property.
|
82
|
+
|
83
|
+
If the +read+, +store+ or +access+ properties start with a <tt>$</tt>, they'll be called
|
84
|
+
on the upper level widget corresponding to the widget, instead than on the widget
|
85
|
+
itself.
|
86
|
+
|
87
|
+
Not all of the above properties need to be specified. In particular, the +access+
|
88
|
+
property can't coexhist the +read+ and the +store+ properties. On the other hand,
|
89
|
+
you can't give only one of the +read+ and +store+ properties. If you omit the
|
90
|
+
+access+, +store+ and +read+ property entierely, and the +signal+ property only
|
91
|
+
contains one signal, then an +access+ property is automatically created using the
|
92
|
+
name of the signal after having removed from its end the strings 'Edited', 'Changed',
|
93
|
+
'Modified', '_edited', '_changed', '_modified' (for example, if the signal is
|
94
|
+
'<tt>textChanged(QString)</tt>, then the +access+ property will become +text+.
|
95
|
+
|
96
|
+
If the +signal+ property hasn't been specified, a default one will be used, depending
|
97
|
+
on the class of the widgets. See the documentation for DEFAULT_PROPERTIES to see
|
98
|
+
which signals will be used for which classes. If neither the +access+ property
|
99
|
+
nor the +read+ and +store+ properties have been given, a default +access+ property
|
100
|
+
will also be used. If the class of the widget is not included in DEFAULT_PROPERTIES,
|
101
|
+
an exception will be raised.
|
102
|
+
|
103
|
+
A read method must accept one argument, which is the value of the option, and
|
104
|
+
display it in the corresponding widget in whichever way is appropriate. A store
|
105
|
+
method, instead, should take no arguments, retrieve the option value from the widget,
|
106
|
+
again using the most appropriate way, and return it.
|
107
|
+
|
108
|
+
Often, the default +access+ method is almost, but not completely, enough. For
|
109
|
+
example, if the widget is a <tt>KDE::UrlRequester</tt>, but you want to store
|
110
|
+
the option as a string, instead of using a <tt>KDE::Url</tt>, you'd need to create
|
111
|
+
a method whose only task is to convert a <tt>KDE::Url</tt> to a string and vice
|
112
|
+
versa. The same could happen with a symbol and a <tt>Qt::LineEdit</tt>. To avoid
|
113
|
+
such a need, this class also performs automatic conversions, when reading or storing
|
114
|
+
an option. It works this way: if the value to store in the +SettingsContainer+ or
|
115
|
+
in the widget are of a different class from the one previously contained there,
|
116
|
+
the +DEFAULT_CONVERSIONS+ hash is scanned for an entry corresponding to the two
|
117
|
+
classes and, if found, the value returned by the corresponding +Proc+ is stored
|
118
|
+
instead of the original one.
|
119
|
+
|
120
|
+
===Example
|
121
|
+
|
122
|
+
Consider the following situation:
|
123
|
+
|
124
|
+
<b>Options:</b>
|
125
|
+
<tt>OpenStruct.new({:name => :number, :group => :G1, :default => 4})</tt>::
|
126
|
+
this is an option which contains an integral value, with default 4
|
127
|
+
<tt>OpenStruct.new({:name => :path, :group => :G1, :default => ENV[['HOME']]})</tt>::
|
128
|
+
this is an option which contains a string representing a path. The default value
|
129
|
+
is the user's home directory (contained in the environment variable HOME)
|
130
|
+
<tt>OpenStruct.new({:name => :list, :group => :G2, :default => %w[a b c]})</tt>::
|
131
|
+
this is an option which contains an array of strings, with default value
|
132
|
+
<tt>['a', 'b', 'c']</tt>.
|
133
|
+
|
134
|
+
<b>Widgets:</b>
|
135
|
+
|
136
|
+
There's a single upper level widget, of class +MyWidget+, which contains a
|
137
|
+
<tt>Qt::SpinBox</tt>, a <tt>KDE::UrlRequester</tt> and a <tt>Qt::LineEdit</tt>.
|
138
|
+
The value displayed in the spin box should be associated to the <tt>:number</tt>
|
139
|
+
option, while the url requester should be associated to the <tt>:path</tt> option.
|
140
|
+
The line edit widget should be associated with the <tt>:list</tt> option, by splitting
|
141
|
+
the text on commas.
|
142
|
+
|
143
|
+
We can make some observations:
|
144
|
+
* the spin box doesn't need anything except having its name set to match the
|
145
|
+
<tt>:number</tt> option: the default signal and access method provided by
|
146
|
+
<tt>DEFAULT_PROPERTIES</tt> are perfectly adequate to this situation.
|
147
|
+
* the url requester doesn't need any special settings, aside from the object name:
|
148
|
+
the default signal and access method provided by <tt>DEFAULT_PROPERTIES</tt> are
|
149
|
+
almost what we need and the only issue is that the methods take and return a
|
150
|
+
<tt>KDE::Url</tt> instead of a string. But since the <tt>DEFAULT_CONVERSIONS</tt>
|
151
|
+
contains conversion procs for this pair of classes, even this is handled automatically
|
152
|
+
* the line edit requires custom +read+ and +store+ methods (which can be specified
|
153
|
+
with a signle +access+ property), because there's no default conversion from
|
154
|
+
array to string and vice versa. The default signal, instead, is suitable for
|
155
|
+
our needs, so we don't need to specify one.
|
156
|
+
|
157
|
+
Here's how the class +MyWidget+ could be written (here, all widgets are created
|
158
|
+
manually. In general, it's more likely that you'd use the Qt Designer to create
|
159
|
+
it. In that case, you can set the widgets' properties using the designer itself).
|
160
|
+
Note that in the constructor we make use of the block form of the widgets' constructors,
|
161
|
+
which evaluates the given block in the new widget's context.
|
162
|
+
|
163
|
+
class MyWidget < Qt::Widget
|
164
|
+
|
165
|
+
def initialize parent = nil
|
166
|
+
super
|
167
|
+
@spin_box = Qt::SpinBox.new{ self.object_name = '_G1__number'}
|
168
|
+
@url_req = KDE::UrlRequester.new{self.object_name = '_G1__path'}
|
169
|
+
@line_edit = Qt::LineEdit.new do
|
170
|
+
self.object_name = '_G2__list'
|
171
|
+
set_property 'access', '$items'
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# This is the store method for the list option. It takes the text in the line
|
176
|
+
# edit and splits it on commas, returning the array
|
177
|
+
def items
|
178
|
+
@line_edit.text.split ','
|
179
|
+
end
|
180
|
+
|
181
|
+
# This is the read method for the list option. It takes the array containing
|
182
|
+
# the value of the option (an array) as argument, then sets the text of the
|
183
|
+
# line edit to the string obtained by calling join on the array
|
184
|
+
def items= array
|
185
|
+
@line_edit.text = array.join ','
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
=end
|
191
|
+
class SettingsDialogManager < Qt::Object
|
192
|
+
|
193
|
+
=begin rdoc
|
194
|
+
A hash containing the default signal and access methods to use for a number of
|
195
|
+
classes, when the +signal+ property isn't given. The keys of the hash are the
|
196
|
+
classes, while the values are arrays of two elements. The first element is the
|
197
|
+
name of the signal, while the second is the name of the access method.
|
198
|
+
=end
|
199
|
+
DEFAULT_PROPERTIES = {
|
200
|
+
Qt::CheckBox => [ 'toggled(bool)', "checked?"],
|
201
|
+
Qt::PushButton => [ 'toggled(bool)', "checked?"],
|
202
|
+
KDE::PushButton => [ 'toggled(bool)', "checked?"],
|
203
|
+
KDE::ColorButton => [ 'changed(QColor)', "color"],
|
204
|
+
KDE::IconButton => [ 'iconChanged(QString)', "icon"],
|
205
|
+
Qt::LineEdit => [ 'textChanged(QString)', "text"],
|
206
|
+
KDE::LineEdit => [ 'textChanged(QString)', "text"],
|
207
|
+
KDE::RestrictedLine => [ 'textChanged(QString)', "text"],
|
208
|
+
Qt::ComboBox => [ 'currentIndexChanged(int)', "current_index"],
|
209
|
+
KDE::ComboBox => [ 'currentIndexChanged(int)', "current_index"],
|
210
|
+
KDE::ColorCombo => [ 'currentIndexChanged(int)', "color"],
|
211
|
+
Qt::TextEdit => [ 'textChanged(QString)', "text"],
|
212
|
+
KDE::TextEdit => [ 'textChanged(QString)', "text"],
|
213
|
+
Qt::PlainTextEdit => [ 'textChanged(QString)', "text"],
|
214
|
+
Qt::SpinBox => [ 'valueChanged(int)', "value"],
|
215
|
+
KDE::IntSpinBox => [ 'valueChanged(int)', "value"],
|
216
|
+
Qt::DoubleSpinBox => [ 'valueChanged(double)', "value"],
|
217
|
+
KDE::IntNumInput => [ 'valueChanged(int)', "value"],
|
218
|
+
KDE::DoubleNumInput => [ 'valueChanged(double)', "value"],
|
219
|
+
Qt::TimeEdit => [ 'timeChanged(QTime)', "time"],
|
220
|
+
Qt::DateEdit => [ 'dateChanged(QDate)', "date"],
|
221
|
+
Qt::DateTimeEdit => [ 'dateTimeChanged(QDateTime)', "date_time"],
|
222
|
+
Qt::Dial => [ 'valueChanged(int)', "value"],
|
223
|
+
Qt::Slider => [ 'valueChanged(int)', "value"],
|
224
|
+
KDE::DatePicker => [ 'dateChanged(QDate)', "date"],
|
225
|
+
KDE::DateTimeWidget => [ 'valueChanged(QDateTime)', "date_time"],
|
226
|
+
KDE::DateWidget => [ 'changed(QDate)', "date"],
|
227
|
+
KDE::FontComboBox => [ 'currentFontChanged(QFont)', "current_font"],
|
228
|
+
KDE::FontRequester => [ 'fontSelected(QFont)', "font"],
|
229
|
+
KDE::UrlRequester => [ 'textChanged(QString)', "url"]
|
230
|
+
}
|
231
|
+
|
232
|
+
=begin rdoc
|
233
|
+
Hash which contains the Procs used by <tt>convert_value</tt> to convert a value
|
234
|
+
from its class to another. Each key must an array of two classes, corresponding
|
235
|
+
respectively to the class to convert _from_ and to the class to convert _to_. The
|
236
|
+
values should be Procs which take one argument of class corresponding to the first
|
237
|
+
entry of the key and return an object of class equal to the second argument of the
|
238
|
+
key.
|
239
|
+
|
240
|
+
If you want to implement a new automatic conversion, all you need to do is to
|
241
|
+
add the appropriate entries here. Note that usually you'll want to add two entries:
|
242
|
+
one for the conversion from A to B and one for the conversion in the opposite
|
243
|
+
direction.
|
244
|
+
=end
|
245
|
+
DEFAULT_CONVERSIONS = {
|
246
|
+
[Symbol, String] => proc{|sym| sym.to_s},
|
247
|
+
[String, Symbol] => proc{|str| str.to_sym},
|
248
|
+
[String, KDE::Url] => proc{|str| KDE::Url.from_path(str)},
|
249
|
+
# KDE::Url#path_or_url returns nil if the KDE::Url is not valid, so, we use
|
250
|
+
# || '' to ensure the returned object is a string
|
251
|
+
[KDE::Url, String] => proc{|url| url.path_or_url || ''},
|
252
|
+
[String, Fixnum] => proc{|str| str.to_i},
|
253
|
+
[Fixnum, String] => proc{|n| n.to_s},
|
254
|
+
[String, Float] => proc{|str| str.to_f},
|
255
|
+
[Float, String] => proc{|x| x.to_s},
|
256
|
+
}
|
257
|
+
|
258
|
+
slots :settings_changed
|
259
|
+
|
260
|
+
|
261
|
+
=begin rdoc
|
262
|
+
Creates a new SettingsDialogManager. The first argument is the SettingsDialog whose
|
263
|
+
widgets will be managed by the new instance. The second argument is an array with
|
264
|
+
the option objects corresponding to the options which are candidates for automatic
|
265
|
+
management. The keys of the hash are the option
|
266
|
+
objects, which contain all the information about the options, while the values
|
267
|
+
are the values of the options. _widgets_ is a list of widgets where to look for
|
268
|
+
for widgets corresponding to the options.
|
269
|
+
|
270
|
+
When the +SettingsDialogManager+ instance is created, associations between options
|
271
|
+
and widgets are created, but the widgets themselves aren't updated with the values
|
272
|
+
of the options.
|
273
|
+
=end
|
274
|
+
def initialize dlg, options, widgets
|
275
|
+
super(dlg)
|
276
|
+
@widgets = widgets
|
277
|
+
@container = dlg.settings_container
|
278
|
+
@associations = {}
|
279
|
+
options.each{|o| setup_option o}
|
280
|
+
end
|
281
|
+
|
282
|
+
=begin rdoc
|
283
|
+
Updates all the widgets corresponding to an automatically managed option by calling
|
284
|
+
the associated reader methods, passing them the value read from the option container,
|
285
|
+
converted as described in the the documentation for SettingsDialogManager,
|
286
|
+
<tt>convert_value</tt> and <tt>DEFAULT_CONVERSIONS</tt>. It emits the
|
287
|
+
<tt>settings_changed</tt> signal with *true* as argument.
|
288
|
+
=end
|
289
|
+
def read_settings
|
290
|
+
@associations.each_pair do |o, data|
|
291
|
+
group, name = o[1..-1].split('__').map(&:to_sym)
|
292
|
+
value = @container.relative_path?(group, name) ? @container[group, name, :abs] : @container[group, name]
|
293
|
+
old_value = call_widget_method data
|
294
|
+
value = convert_value(value, old_value)
|
295
|
+
call_widget_method data, value
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
=begin rdoc
|
300
|
+
Updates all the automatically managed options by calling setting them to the values
|
301
|
+
returned by the associated 'store' methods,
|
302
|
+
converted as described in the the documentation for +SettingsDialogManager+,
|
303
|
+
<tt>convert_value</tt> and <tt>DEFAULT_CONVERSIONS</tt>. It emits the
|
304
|
+
<tt>settings_changed</tt> signal with *false* as argument.
|
305
|
+
=end
|
306
|
+
def store_settings
|
307
|
+
@associations.each_pair do |o, data|
|
308
|
+
group, name = o[1..-1].split('__').map(&:to_sym)
|
309
|
+
value = call_widget_method(data)
|
310
|
+
old_value = @container[group, name]
|
311
|
+
value = convert_value value, old_value
|
312
|
+
@container[group, name] = value
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
=begin rdoc
|
317
|
+
It works like <tt>read_settings</tt> except for the fact that it updates the
|
318
|
+
widgets with the default values of the options.
|
319
|
+
=end
|
320
|
+
def read_default_settings
|
321
|
+
@associations.each_pair do |o, data|
|
322
|
+
group, name = o[1..-1].split('__').map(&:to_sym)
|
323
|
+
value = @container.default(group, name)
|
324
|
+
old_value = call_widget_method data
|
325
|
+
value = convert_value(value, old_value)
|
326
|
+
call_widget_method data, value
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
private
|
331
|
+
|
332
|
+
=begin rdoc
|
333
|
+
Looks in the list of widgets passed to the constructor and their children for a
|
334
|
+
widget whose name corresponds to that of the option (see widget_name for the meaning
|
335
|
+
of _corresponds_). If one is found, the setup_automatic_option method is called
|
336
|
+
for that option. If no widget is found, nothing is done.
|
337
|
+
=end
|
338
|
+
def setup_option opt
|
339
|
+
name = widget_name opt.group, opt.name
|
340
|
+
widgets = @widgets.find! do |w|
|
341
|
+
if w.object_name == name then [w, w]
|
342
|
+
else
|
343
|
+
child = w.find_child(Qt::Widget, name)
|
344
|
+
child ? [child, w] : nil
|
345
|
+
end
|
346
|
+
end
|
347
|
+
setup_automatic_option opt, *widgets if widgets
|
348
|
+
end
|
349
|
+
|
350
|
+
=begin rdoc
|
351
|
+
Associates the widget _w_ with the option _option_. _ulw_ is the upper level
|
352
|
+
widget associated with _widget_. Associating a widget with an option means two
|
353
|
+
things:
|
354
|
+
* connecting each signal specified in the +signal+ property of the widget with
|
355
|
+
the settings_changed slot of *self*
|
356
|
+
* creating an entry in the <tt>@associations</tt> instance variable with the name
|
357
|
+
of the widget as key and a hash as value. Each hash has a +read+ and a +store+
|
358
|
+
entry, which are both arrays: the second element is the name of the +read+ and
|
359
|
+
+store+ method respectively, while the first is the widget on which it should
|
360
|
+
be called (which will be either _widget_ or the corresponding upper level widget,
|
361
|
+
_ulw_).
|
362
|
+
|
363
|
+
As explained in the documentation for this class, some of the properties may be
|
364
|
+
missing and be automatically determined. If one of the following situations happen,
|
365
|
+
+ArgumentError+ will be raised:
|
366
|
+
* no +signal+ property exist for _widget_ and its class is not in +DEFAULT_PROPERTIES+
|
367
|
+
* _widget_ doesn't specify neither the +access+ property nor the +read+ and the
|
368
|
+
+store+ properties and more than one signal is specified in the +signal+ property
|
369
|
+
* both the +access+ property and the +read+ and/or +store+ properties are specified
|
370
|
+
for _widget_
|
371
|
+
* the signature of one signal isn't given and it couldn't be determined automatically
|
372
|
+
because either no signal or more than one signals of _widget_ have that name
|
373
|
+
=end
|
374
|
+
def setup_automatic_option opt, widget, ulw
|
375
|
+
#Qt::Variant#to_string returns nil if the variant is invalid
|
376
|
+
signals = Array(YAML.load(widget.property('signal').to_string || '[]'))
|
377
|
+
#Qt::Variant#to_string returns nil if the variant is invalid
|
378
|
+
methods = %w[read store access].map{|m| prop = widget.property(m).to_string}
|
379
|
+
if signals.empty?
|
380
|
+
data = DEFAULT_PROPERTIES.fetch(widget.class) do
|
381
|
+
raise ArgumentError, "No default signal exists for class #{widget.class}, you need to specify one"
|
382
|
+
end
|
383
|
+
signals = [data[0]]
|
384
|
+
methods[2] = data[1] if methods == Array.new(3, nil)
|
385
|
+
end
|
386
|
+
signals.each_index do |i|
|
387
|
+
signals[i] = add_signal_signature signals[i], widget
|
388
|
+
connect widget, SIGNAL(signals[i]), self, SLOT(:settings_changed)
|
389
|
+
end
|
390
|
+
data = find_associated_methods signals, methods, widget, ulw
|
391
|
+
@associations[widget.object_name] = data
|
392
|
+
end
|
393
|
+
|
394
|
+
=begin rdoc
|
395
|
+
Returns the name of the widget corresponding to the option belonging to group
|
396
|
+
_group_ and with name _name_. The widget name has the form: underscore, group,
|
397
|
+
double underscore, name. For example if _group_ is :general and _name_ is _path_,
|
398
|
+
this method would return "_general__path"
|
399
|
+
=end
|
400
|
+
def widget_name group, name
|
401
|
+
"_#{group}__#{name}"
|
402
|
+
end
|
403
|
+
|
404
|
+
=begin rdoc
|
405
|
+
Returns the full signature of the signal with name _sig_ in the <tt>Qt::Object</tt>
|
406
|
+
obj. If _sig_ already has a signature, it is returned as it is. Otherwise, the list
|
407
|
+
of signals of _obj_ is searched for a signal whose name is equal to _sig_ and the
|
408
|
+
full name of that signal is returned. If no signal has that name, or if more than
|
409
|
+
one signal have that name, +ArgumentError+ is raised.
|
410
|
+
=end
|
411
|
+
def add_signal_signature sig, obj
|
412
|
+
return sig if sig.index '('
|
413
|
+
mo = obj.meta_object
|
414
|
+
reg = /^#{Regexp.quote sig}/
|
415
|
+
signals = mo.each_signal.find_all{|s| s.signature =~ reg}
|
416
|
+
raise ArgumentError, "Ambiguous signal name, '#{sig}'" if signals.size > 1
|
417
|
+
raise ArgumentError, "No signal with name '#{sig}' exist" if signals.empty?
|
418
|
+
signals.first.signature
|
419
|
+
end
|
420
|
+
|
421
|
+
=begin rdoc
|
422
|
+
Finds the read and store methods to associate to the widget _widget_. It returns
|
423
|
+
an hash with keys <tt>:read</tt> and <tt>:store</tt> and for values pairs of the
|
424
|
+
form <tt>[receiver, method]</tt>, where _method_ is the method to call to read
|
425
|
+
or store the option value and _receiver_ is the object to call the method on (it
|
426
|
+
may be either _widget_, if the corresponding property doesn't begin with +$+,
|
427
|
+
or its upper level widget, _ulw_, if the property begins with +$+).
|
428
|
+
|
429
|
+
_signals_ is an array with all the signals included in the +signal+ property of
|
430
|
+
the widget. _methods_ is an array with the contents of the +read+, +store+ and
|
431
|
+
+access+ properties, (in this order), converted to string (if one property is
|
432
|
+
missing, the corresponding entry will be *nil*). _widget_ is the widget for which
|
433
|
+
the association should be done and _ulw_ is the corresponding upper level widget.
|
434
|
+
|
435
|
+
See the documentation for <tt>setup_automatic_option</tt> for a description of
|
436
|
+
the situation on which an +ArgumentError+ exception is raised.
|
437
|
+
=end
|
438
|
+
def find_associated_methods signals, methods, widget, ulw
|
439
|
+
read, store, access = methods
|
440
|
+
if access and (read or store)
|
441
|
+
raise ArgumentError, "The widget #{widget.object_name} has both the access property and one or both of the store and read properties"
|
442
|
+
elsif access
|
443
|
+
read = access.sub(/[?!=]$/,'')+'='
|
444
|
+
store = access
|
445
|
+
elsif !access and !(store and read)
|
446
|
+
if signals.size > 1
|
447
|
+
raise ArgumentError, "When more signals are specified, you need to specify also the access property or both the read and store properties"
|
448
|
+
end
|
449
|
+
sig = signals.first
|
450
|
+
store ||= sig[/[^(]+/].snakecase.sub(/_(?:changed|modified|edited)$/, '')
|
451
|
+
read ||= sig[/[^(]+/].snakecase.sub(/_(?:changed|modified|edited)$/, '') + '='
|
452
|
+
end
|
453
|
+
data = {}
|
454
|
+
data[:read] = read[0,1] == '$' ? [ulw, read[1..-1]] : [widget, read]
|
455
|
+
data[:store] = store[0,1] == '$' ? [ulw, store[1..-1]] : [widget, store]
|
456
|
+
data
|
457
|
+
end
|
458
|
+
|
459
|
+
=begin rdoc
|
460
|
+
:call-seq:
|
461
|
+
manager.call_widget_method data
|
462
|
+
manager.call_widget_method data, value
|
463
|
+
|
464
|
+
Helper method which calls the 'read' or 'store' method for an association. If called
|
465
|
+
in the first form, calls the 'store' method, while in the first form it calls the
|
466
|
+
'read' method passing _value_ as argument. _data_ is one of the hashes contained
|
467
|
+
in the <tt>@associations</tt> instance variable as values: it has the following
|
468
|
+
form: <tt>{:read => [widget, read_method], :store => [widget, store_method]}. In
|
469
|
+
both versions, it returns what the called method returned.
|
470
|
+
=end
|
471
|
+
def call_widget_method *args
|
472
|
+
data = args[0]
|
473
|
+
if args.size == 1 then data[:store][0].send data[:store][1]
|
474
|
+
else data[:read][0].send data[:read][1], args[1]
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
=begin rdoc
|
479
|
+
Converts the value _new_ so that it has the same class as _old_. To do so, it looks
|
480
|
+
in the +DEFAULT_CONVERSIONS+ hash for an entry whose key is an array with the class
|
481
|
+
of _new_ as first entry and the class of _old_ as second entry. If the key is found,
|
482
|
+
the corresponding value (which is a +Proc+), is called, passing it _new_ and the
|
483
|
+
method returns its return value. If the key isn't found (including the case when
|
484
|
+
_new_ and _old_ have the same class), _new_ is returned as it is.
|
485
|
+
=end
|
486
|
+
def convert_value new, old
|
487
|
+
prc = DEFAULT_CONVERSIONS[[new.class, old.class]]
|
488
|
+
if prc then
|
489
|
+
prc.call new
|
490
|
+
else new
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
=begin rdoc
|
495
|
+
Enables the Apply button of the dialog
|
496
|
+
=end
|
497
|
+
def settings_changed
|
498
|
+
parent.enable_button_apply true
|
499
|
+
end
|
500
|
+
|
501
|
+
end
|
502
|
+
|
503
|
+
end
|