ruber 0.0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|