ruber 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/COPYING +339 -0
  2. data/INSTALL +137 -0
  3. data/LICENSE +8 -0
  4. data/bin/ruber +65 -0
  5. data/data/share/apps/ruber/core_components.yaml +31 -0
  6. data/data/share/apps/ruber/ruberui.rc +109 -0
  7. data/data/share/icons/ruber.png +0 -0
  8. data/data/share/pixmaps/ruby.png +0 -0
  9. data/icons/ruber-16.png +0 -0
  10. data/icons/ruber-32.png +0 -0
  11. data/icons/ruber-48.png +0 -0
  12. data/icons/ruber-8.png +0 -0
  13. data/lib/ruber/application/application.rb +288 -0
  14. data/lib/ruber/application/plugin.yaml +11 -0
  15. data/lib/ruber/component_manager.rb +899 -0
  16. data/lib/ruber/config/config.rb +82 -0
  17. data/lib/ruber/config/plugin.yaml +3 -0
  18. data/lib/ruber/document_project.rb +209 -0
  19. data/lib/ruber/documents/document_list.rb +416 -0
  20. data/lib/ruber/documents/plugin.yaml +4 -0
  21. data/lib/ruber/editor/document.rb +506 -0
  22. data/lib/ruber/editor/editor_view.rb +167 -0
  23. data/lib/ruber/editor/ktexteditor_wrapper.rb +202 -0
  24. data/lib/ruber/exception_widgets.rb +245 -0
  25. data/lib/ruber/external_program_plugin.rb +397 -0
  26. data/lib/ruber/filtered_output_widget.rb +342 -0
  27. data/lib/ruber/gui_states_handler.rb +231 -0
  28. data/lib/ruber/kde_config_option_backend.rb +167 -0
  29. data/lib/ruber/kde_sugar.rb +249 -0
  30. data/lib/ruber/main_window/choose_plugins_dlg.rb +353 -0
  31. data/lib/ruber/main_window/main_window.rb +524 -0
  32. data/lib/ruber/main_window/main_window_actions.rb +537 -0
  33. data/lib/ruber/main_window/main_window_internal.rb +239 -0
  34. data/lib/ruber/main_window/open_file_in_project_dlg.rb +212 -0
  35. data/lib/ruber/main_window/output_color_widget.rb +35 -0
  36. data/lib/ruber/main_window/plugin.yaml +58 -0
  37. data/lib/ruber/main_window/save_modified_files_dlg.rb +89 -0
  38. data/lib/ruber/main_window/status_bar.rb +156 -0
  39. data/lib/ruber/main_window/ui/choose_plugins_widget.rb +90 -0
  40. data/lib/ruber/main_window/ui/choose_plugins_widget.ui +77 -0
  41. data/lib/ruber/main_window/ui/main_window_settings_widget.rb +108 -0
  42. data/lib/ruber/main_window/ui/main_window_settings_widget.ui +89 -0
  43. data/lib/ruber/main_window/ui/new_project_widget.rb +119 -0
  44. data/lib/ruber/main_window/ui/new_project_widget.ui +178 -0
  45. data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +109 -0
  46. data/lib/ruber/main_window/ui/open_file_in_project_dlg.ui +168 -0
  47. data/lib/ruber/main_window/ui/output_color_widget.rb +241 -0
  48. data/lib/ruber/main_window/ui/output_color_widget.ui +204 -0
  49. data/lib/ruber/main_window/workspace.rb +442 -0
  50. data/lib/ruber/output_widget.rb +1093 -0
  51. data/lib/ruber/plugin.rb +264 -0
  52. data/lib/ruber/plugin_like.rb +589 -0
  53. data/lib/ruber/plugin_specification.rb +106 -0
  54. data/lib/ruber/plugin_specification_reader.rb +451 -0
  55. data/lib/ruber/project.rb +493 -0
  56. data/lib/ruber/project_backend.rb +105 -0
  57. data/lib/ruber/projects/plugin.yaml +11 -0
  58. data/lib/ruber/projects/project_files_list.rb +314 -0
  59. data/lib/ruber/projects/project_files_widget.rb +301 -0
  60. data/lib/ruber/projects/project_list.rb +314 -0
  61. data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +74 -0
  62. data/lib/ruber/projects/ui/project_files_rule_chooser_widget.ui +61 -0
  63. data/lib/ruber/projects/ui/project_files_widget.rb +117 -0
  64. data/lib/ruber/projects/ui/project_files_widget.ui +123 -0
  65. data/lib/ruber/qt_sugar.rb +673 -0
  66. data/lib/ruber/settings_container.rb +515 -0
  67. data/lib/ruber/settings_dialog.rb +244 -0
  68. data/lib/ruber/settings_dialog_manager.rb +503 -0
  69. data/lib/ruber/utils.rb +414 -0
  70. data/lib/ruber/yaml_option_backend.rb +159 -0
  71. data/outsider_files +15 -0
  72. data/plugins/autosave/autosave.rb +404 -0
  73. data/plugins/autosave/plugin.yaml +16 -0
  74. data/plugins/autosave/ui/autosave_config_widget.rb +83 -0
  75. data/plugins/autosave/ui/autosave_config_widget.ui +68 -0
  76. data/plugins/command/command.png +0 -0
  77. data/plugins/command/command.rb +74 -0
  78. data/plugins/command/plugin.yaml +11 -0
  79. data/plugins/find_in_files/find_in_files.rb +337 -0
  80. data/plugins/find_in_files/find_in_files_dlg.rb +411 -0
  81. data/plugins/find_in_files/find_in_files_ui.rc +11 -0
  82. data/plugins/find_in_files/find_in_files_widgets.rb +485 -0
  83. data/plugins/find_in_files/plugin.yaml +23 -0
  84. data/plugins/find_in_files/ui/config_widget.rb +58 -0
  85. data/plugins/find_in_files/ui/config_widget.ui +41 -0
  86. data/plugins/find_in_files/ui/find_in_files_widget.rb +260 -0
  87. data/plugins/find_in_files/ui/find_in_files_widget.ui +324 -0
  88. data/plugins/project_browser/plugin.yaml +10 -0
  89. data/plugins/project_browser/project_browser.rb +245 -0
  90. data/plugins/rake/plugin.yaml +39 -0
  91. data/plugins/rake/rake.png +0 -0
  92. data/plugins/rake/rake.rb +567 -0
  93. data/plugins/rake/rake_extension.rb +153 -0
  94. data/plugins/rake/rake_widgets.rb +615 -0
  95. data/plugins/rake/rakeui.rc +27 -0
  96. data/plugins/rake/ui/add_quick_task_widget.rb +71 -0
  97. data/plugins/rake/ui/add_quick_task_widget.ui +59 -0
  98. data/plugins/rake/ui/choose_task_widget.rb +77 -0
  99. data/plugins/rake/ui/choose_task_widget.ui +72 -0
  100. data/plugins/rake/ui/config_widget.rb +127 -0
  101. data/plugins/rake/ui/config_widget.ui +123 -0
  102. data/plugins/rake/ui/project_widget.rb +217 -0
  103. data/plugins/rake/ui/project_widget.ui +246 -0
  104. data/plugins/rspec/plugin.yaml +30 -0
  105. data/plugins/rspec/rspec.png +0 -0
  106. data/plugins/rspec/rspec.rb +945 -0
  107. data/plugins/rspec/rspec.svg +90 -0
  108. data/plugins/rspec/rspecui.rc +20 -0
  109. data/plugins/rspec/ruber_rspec_formatter.rb +312 -0
  110. data/plugins/rspec/ui/rspec_project_widget.rb +170 -0
  111. data/plugins/rspec/ui/rspec_project_widget.ui +193 -0
  112. data/plugins/ruby_development/plugin.yaml +27 -0
  113. data/plugins/ruby_development/ruby_development.png +0 -0
  114. data/plugins/ruby_development/ruby_development.rb +453 -0
  115. data/plugins/ruby_development/ruby_developmentui.rc +19 -0
  116. data/plugins/ruby_development/ui/project_widget.rb +112 -0
  117. data/plugins/ruby_development/ui/project_widget.ui +108 -0
  118. data/plugins/ruby_runner/config_widget.rb +116 -0
  119. data/plugins/ruby_runner/plugin.yaml +26 -0
  120. data/plugins/ruby_runner/project_widget.rb +62 -0
  121. data/plugins/ruby_runner/ruby.png +0 -0
  122. data/plugins/ruby_runner/ruby_interpretersui.rc +26 -0
  123. data/plugins/ruby_runner/ruby_runner.rb +411 -0
  124. data/plugins/ruby_runner/ui/config_widget.rb +92 -0
  125. data/plugins/ruby_runner/ui/config_widget.ui +91 -0
  126. data/plugins/ruby_runner/ui/project_widget.rb +60 -0
  127. data/plugins/ruby_runner/ui/project_widget.ui +48 -0
  128. data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +59 -0
  129. data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.ui +44 -0
  130. data/plugins/state/plugin.yaml +28 -0
  131. data/plugins/state/state.rb +520 -0
  132. data/plugins/state/ui/config_widget.rb +92 -0
  133. data/plugins/state/ui/config_widget.ui +89 -0
  134. data/plugins/syntax_checker/plugin.yaml +18 -0
  135. data/plugins/syntax_checker/syntax_checker.rb +662 -0
  136. data/ruber.desktop +10 -0
  137. data/spec/annotation_model_spec.rb +174 -0
  138. data/spec/common.rb +119 -0
  139. data/spec/component_manager_spec.rb +1259 -0
  140. data/spec/document_list_spec.rb +626 -0
  141. data/spec/document_project_spec.rb +373 -0
  142. data/spec/document_spec.rb +779 -0
  143. data/spec/editor_view_spec.rb +167 -0
  144. data/spec/external_program_plugin_spec.rb +676 -0
  145. data/spec/filtered_output_widget_spec.rb +642 -0
  146. data/spec/gui_states_handler_spec.rb +304 -0
  147. data/spec/kde_config_option_backend_spec.rb +214 -0
  148. data/spec/kde_sugar_spec.rb +101 -0
  149. data/spec/ktexteditor_wrapper_spec.rb +305 -0
  150. data/spec/output_widget_spec.rb +1703 -0
  151. data/spec/plugin_spec.rb +1393 -0
  152. data/spec/plugin_specification_reader_spec.rb +1765 -0
  153. data/spec/plugin_specification_spec.rb +401 -0
  154. data/spec/project_backend_spec.rb +172 -0
  155. data/spec/project_files_list_spec.rb +401 -0
  156. data/spec/project_list_spec.rb +511 -0
  157. data/spec/project_spec.rb +990 -0
  158. data/spec/qt_sugar_spec.rb +328 -0
  159. data/spec/settings_container_spec.rb +617 -0
  160. data/spec/settings_dialog_manager_spec.rb +773 -0
  161. data/spec/settings_dialog_spec.rb +419 -0
  162. data/spec/state_spec.rb +991 -0
  163. data/spec/utils_spec.rb +406 -0
  164. data/spec/workspace_spec.rb +869 -0
  165. data/spec/yaml_option_backend_spec.rb +246 -0
  166. metadata +284 -0
@@ -0,0 +1,264 @@
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/plugin_like'
22
+
23
+ module Ruber
24
+
25
+ =begin rdoc
26
+ Base class for all plugins
27
+
28
+ Most of its functionality comes from the included {PluginLike} module.
29
+ =end
30
+ class Plugin < Qt::Object
31
+
32
+ =begin rdoc
33
+ A map between the license symbols used in the PSF and the @KDE::AboutData@ licenses
34
+ constants
35
+ =end
36
+ LICENSES = {
37
+ :unknown => KDE::AboutData::License_Unknown,
38
+ :gpl => KDE::AboutData::License_GPL,
39
+ :gpl2 => KDE::AboutData::License_GPL_V2,
40
+ :lgpl => KDE::AboutData::License_LGPL,
41
+ :lgpl2 => KDE::AboutData::License_LGPL_V2,
42
+ :bsd => KDE::AboutData::License_BSD,
43
+ :artistic => KDE::AboutData::License_Artistic,
44
+ :qpl => KDE::AboutData::License_QPL,
45
+ :qpl1 => KDE::AboutData::License_QPL_V1_0,
46
+ :gpl3 => KDE::AboutData::License_GPL_V3,
47
+ :lgpl3 => KDE::AboutData::License_LGPL_V3
48
+ }
49
+
50
+ =begin rdoc
51
+ Signal emitted after an extension has been added
52
+
53
+ @param [String] name the name of the extension
54
+ @param [Qt::Object] ext the extension object itself
55
+ =end
56
+ signals 'extension_added(QString, QObject*)'
57
+
58
+ =begin rdoc
59
+ Signal emitted before removing an extension
60
+
61
+ @param [String] name the the name of the extension being removed
62
+ @param [Qt::Object] ext the extension object being removed
63
+ =end
64
+ signals 'removing_extension(QString, QObject*)'
65
+
66
+ =begin rdoc
67
+ Signal emitted after removing an extension
68
+
69
+ @param [String] name the the name of the removed extension
70
+ @param [Qt::Object] ext the removed extension object
71
+ =end
72
+ signals 'extension_removed(QString, QObject*)'
73
+
74
+ slots 'load_settings()'
75
+
76
+ include PluginLike
77
+
78
+ =begin rdoc
79
+ Creates a new instance
80
+
81
+ This method takes care of calling the {#initialize_plugin} method required by
82
+ the {PluginLike} module
83
+
84
+ @param [Ruber::PluginSpecification] psf the plugin specification object associated
85
+ with the plugin
86
+ =end
87
+ def initialize psf
88
+ super(Ruber[:app])
89
+ initialize_plugin psf
90
+ end
91
+
92
+ =begin rdoc
93
+ Creates a @KDE::AboutData@ object for the plugin
94
+
95
+ *Note:* every time this method is called, a new @KDE::AboutData@ object is created
96
+
97
+ @return [KDE::AboutData] a @KDE::AboutData@ containing information about the plugin,
98
+ taken from the PSF
99
+ =end
100
+ def about_data
101
+ about = @plugin_description.about
102
+ app_name = @plugin_description.name.to_s
103
+ version = @plugin_description.version
104
+ license = @plugin_description.about.license
105
+ license_key, license_text =
106
+ if about.license.is_a? String then [KDE::AboutData::License_Custom, about.license]
107
+ else [LICENSES[about.license], nil]
108
+ end
109
+ res = KDE::AboutData.new app_name, '', KDE.ki18n(about.human_name), version,
110
+ KDE.ki18n(about.description), license_key
111
+ res.license_text = KDE.ki18n(license_text) if license_text
112
+ @plugin_description.about.authors.each do |a|
113
+ res.add_author KDE.ki18n(a[0]), KDE.ki18n(''), Qt::ByteArray.new(a[1] || '')
114
+ end
115
+ res.bug_address = Qt::ByteArray.new(about.bug_address) unless about.bug_address.empty?
116
+ res.copyright_statement = KDE.ki18n(about.copyright) unless about.copyright.empty?
117
+ res.homepage = Qt::ByteArray.new(about.homepage) unless about.homepage.empty?
118
+ res.program_icon_name = about.icon unless about.icon.empty?
119
+ res
120
+ end
121
+
122
+ end
123
+
124
+ =begin rdoc
125
+ Base class for all plugins which provide a GUI (that is, menu or toolbar entries).
126
+ =end
127
+ class GuiPlugin < Plugin
128
+
129
+ =begin rdoc
130
+ Creates an instance and initializes the GUI
131
+ @param [Ruber::PluginSpecification] psf the plugin specification object associated
132
+ with the plugin
133
+ =end
134
+ def initialize psf
135
+ super
136
+ @gui = KDE::XMLGUIClient.new Ruber[:main_window]
137
+ @gui.send :set_XML_file, psf.ui_file
138
+ # TODO when the KDE::ComponentData which takes a KDE::AboutData constructor
139
+ # works, construct the KDE::ComponentData using the value returned by the about_data
140
+ # method. As the following lines are (hopefully) temporarily, I only add the minimum
141
+ # to make the plugins show correctly in the shortcuts editor dialog.
142
+ @gui.component_data = KDE::ComponentData.new Qt::ByteArray.new(plugin_name.to_s)
143
+ data = @gui.component_data.about_data
144
+ data.program_name = KDE.ki18n(@plugin_description.about.human_name)
145
+ setup_actions
146
+ Ruber[:main_window].factory.add_client @gui
147
+ end
148
+
149
+ =begin rdoc
150
+ Removes the GUI provided by this plugin from the application's GUI
151
+ @return [nil]
152
+ =end
153
+ def unload
154
+ @gui.factory.remove_client @gui
155
+ super
156
+ end
157
+
158
+ =begin rdoc
159
+ @return [KDE::ActionCollection] the action collection used to contain the plugin's actions
160
+ =end
161
+ def action_collection
162
+ @gui.action_collection
163
+ end
164
+
165
+ =begin rdoc
166
+ Executes the action with a given name, if it belongs to the plugin's action collection
167
+
168
+ To execute the action, this method first checks whether it is included in the @actions@
169
+ entry of the PSF. If so, then the associated slot is directly called, passing
170
+ _<notextile>*</notextile>args_ as argument. If the action is not included in that entry, then
171
+ the action object is forced to emit a signal according to its class: @toggled(bool)@
172
+ for a @KDE::ToggleAction@, @triggered()@ for a @KDE::Action, and
173
+ @triggered(*args)@ for all other actions.
174
+
175
+ If a plugin needs a different behaviour, for example because the slot connected to
176
+ the action uses @Qt::Object.sender@, and thus can only be called from a
177
+ signal, you'll need to override this method and have the action emit the signal.
178
+ To do so, you can use the following code:
179
+
180
+ bc.
181
+
182
+ a = action_collection.action(name)
183
+ a.instance_eval{emit signal_name(arg)}
184
+
185
+ where @name@ is the name of the action, @signal_name@ is the name of the
186
+ signal to emit and @arg@ is the argument of the signal (you can pass more
187
+ than one argument, if needed).
188
+
189
+ If the slot is called directly, _args_ are the arguments to be passed to the slot.
190
+ If the signal is emitted, _args_ are the arguments passed to the signal.
191
+
192
+ *Note:* emitting the signal can (in rare cases) have unwanted results. This
193
+ can happen if there are more than one slot connected to the signal, in which case
194
+ all of them will be called. Usually, this shouldn't be an issue since it's common
195
+ to connect only one signal to each action, but it can happen. This is why this
196
+ method prefers to call the slot directly, whenever possible.
197
+
198
+ @param [String,Symbol] name the name of the action to execute
199
+ @param [<Object>] args the arguments to pass to the slot or the signal
200
+ @return [Boolean] *true* if an action called _name_ was found and *false* otherwise
201
+ =end
202
+ def execute_action name, *args
203
+ data = plugin_description.actions[name.to_s]
204
+ if data
205
+ slot = data.slot.sub(/\(.*/, '')
206
+ instance_eval(data.receiver).send slot, *args
207
+ true
208
+ elsif (action = action_collection.action(name))
209
+ if action.class == KDE::ToggleAction then KDE::ToggleAction
210
+ action.instance_eval{emit toggled(*args)}
211
+ elsif action.class == KDE::Action
212
+ action.instance_eval{emit triggered()}
213
+ else action.instance_eval{emit triggered(*args)}
214
+ end
215
+ true
216
+ else false
217
+ end
218
+ end
219
+
220
+
221
+ private
222
+
223
+ =begin rdoc
224
+ Override of {PluginLike#setup_actions}
225
+
226
+ It works as the base class method but doesn't need the action collection
227
+ @return [nil]
228
+ =end
229
+ def setup_actions
230
+ super @gui.action_collection
231
+ end
232
+
233
+ =begin rdoc
234
+ Registers an UI action handler with the main window
235
+
236
+ It works like {GuiStatesHandler#register_action_handler} but doesn't require to specify
237
+ neither the plugin (which will be *self*) nor the action object, which will be retrieved
238
+ from the plugin's action collection nor the states which are taken from the @states@
239
+ entry of the PSF entry corresponding to the action
240
+
241
+ *Note:* to use this method, the action description in the PSF must include the
242
+ @states@ entry (the @state@ entry isn't used).
243
+
244
+ @param [String] name the name of the action for which the action handler is registered
245
+ @param [Boolean] check whether or not the state of the action should be checked
246
+ when the action handler is registered
247
+ @param [Proc] blk the block to call when one of the states the action depends on
248
+ changes
249
+ @return [nil]
250
+ @see GuiStatesHandler
251
+ =end
252
+ def register_action_handler name, check = true, &blk
253
+ action = @gui.action_collection.action name
254
+ states = @plugin_description.actions[name].states
255
+ if action
256
+ Ruber[:main_window].register_action_handler action, states, :check => check, :extra_id => self, &blk
257
+ end
258
+ nil
259
+ end
260
+
261
+
262
+ end
263
+
264
+ end
@@ -0,0 +1,589 @@
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
+ module Ruber
22
+
23
+ =begin rdoc
24
+ Module providing basic functionality common to both plugin and core components.
25
+
26
+ This mainly is an helper module, used to avoid code duplication among the {Plugin}
27
+ class and the various core components ({Application}, {MainWindow} and so on which
28
+ *can't* inherit from {Plugin}). From
29
+ a logical point of view, all of the functionality provided by this module should
30
+ be in the Plugin class, instead.
31
+
32
+ *Note:* this module *MUST* only be included in classes which descend from
33
+ @Qt::Object@, otherwise it will produce crashes
34
+ =end
35
+ module PluginLike
36
+
37
+ =begin rdoc
38
+ @return [PluginSpecification] the plugins specification object for the plugin
39
+ =end
40
+ attr_reader :plugin_description
41
+
42
+ =begin rdoc
43
+ @return [Symbol] the internal name of the plugin
44
+ =end
45
+ def plugin_name
46
+ @plugin_description.name
47
+ end
48
+ alias component_name plugin_name
49
+
50
+ =begin rdoc
51
+ Does the required cleanup before the application closes.
52
+
53
+ This method is also called when the plugin is unloaded when the application is
54
+ running (for example because the user deselects it from the Choose Plugin dialog).
55
+ This happens because usually the {#unload} method needs to do all {#shutdown} does
56
+ and more. In the rare eventuality you need to do something before closing the
57
+ application which shouldn't be done when unloading the plugin, you can check
58
+ {Application#status} and see whether it's set to @:running@, which
59
+ means the plugin is being unloaded, or to @:quitting@, which means the application
60
+ is being closed.
61
+
62
+ The base class version of this method does nothing.
63
+ @return [nil]
64
+ =end
65
+ def shutdown
66
+ end
67
+
68
+ =begin rdoc
69
+ Method called before the plugin is unloaded. It is used to do any needed cleanup.
70
+
71
+ This method should be overridden by any plugin which needs to do custom cleanup,
72
+ but it's important that the base class's version of the method is called as well
73
+ (most likely _after_ the custom part).
74
+
75
+ This basic implementation does the following:
76
+ * calls {#shutdown}
77
+ * disconnects the {#load_settings} slot from the config manager
78
+ {Config#settings_changed} signal
79
+ * removes all the options belonging to the plugin from the config manager
80
+ * removes all tool widgets belonging to the plugin from the main window
81
+ @return [nil]
82
+ =end
83
+ def unload
84
+ shutdown
85
+ if Ruber[:config]
86
+ disconnect Ruber[:config], SIGNAL('settings_changed()'), self, SLOT('load_settings()')
87
+ @plugin_description.config_options.each_key do |g, n|
88
+ Ruber[:config].remove_option(g.to_sym, n)
89
+ end
90
+ end
91
+ @plugin_description.tool_widgets.each do |w|
92
+ Ruber[:main_window].remove_tool w.name if w.name
93
+ end
94
+ nil
95
+ end
96
+
97
+ =begin rdoc
98
+ Whether or not the plugin allows the application to shut down
99
+
100
+ If this method returns *false* for any plugin, the application won't be closed.
101
+
102
+ @return [Boolean] *true*. Plugins may override this method and return something else,
103
+ maybe depending on the status of the plugin itself. As an example, the Document
104
+ List component checks whether there are unsaved documents and asks the user what
105
+ to do. If the user decides not to close the appplication, the method will return
106
+ *false*.
107
+ =end
108
+ def query_close
109
+ true
110
+ end
111
+
112
+ =begin rdoc
113
+ Method called at application shutdown to allow plugins to save their settings
114
+
115
+ Plugins which need to save some settings need to override this method, as
116
+ the base class implementation does nothing.
117
+
118
+ *Note:* the plugin system calls this method for all plugins before starting
119
+ unloading them. This means that it's absolutely safe to access other plugins'
120
+ methods, objects, options,... from here
121
+ @return [nil]
122
+ =end
123
+ def save_settings
124
+ nil
125
+ end
126
+
127
+ =begin rdoc
128
+ The data the plugin wants to store when the application is shut down by session
129
+ management
130
+
131
+ @return [Hash] a hash containing the information which should be stored by the
132
+ session manager. The hash returned by this method is empty, so plugin which need
133
+ to store some information need to override it. Note that the hashes returned by
134
+ this method from various plugins are merged. To avoid name clashes, you should use
135
+ unique names for the keys in the hashes. The best way to do this is to return a
136
+ hash with a single key, named after the plugin, and corresponding to an inner
137
+ hash containing the information you actually need to store
138
+ @see MainWindow#query_close
139
+ @see ComponentManager#session_data
140
+ =end
141
+ def session_data
142
+ {}
143
+ end
144
+
145
+ =begin rdoc
146
+ Restores the state of the plugin from session management
147
+
148
+ This method is called by the component manager when a session needs to be restored.
149
+ Plugins need to override it if they have some state which needs to be restored
150
+ (there's no need to call _super_ when overriding, since the base class method does
151
+ nothing)
152
+
153
+ @param [Hash] cfg a hash containing the keys stored in the hash returned by {#session_data}
154
+ @see ComponentManager#restore_session
155
+ @see MainWindow#readProperties
156
+ =end
157
+ def restore_session cfg
158
+ end
159
+
160
+ =begin rdoc
161
+ Performs delayed initialization
162
+
163
+ This method is called by the component manager after the plugin object has been
164
+ stored in the component manager (and thus made availlable through {Ruber.[]}).
165
+ Plugins only need to override this method (the base class version does nothing)
166
+ if something which should happen during initialization requires to access the
167
+ plugin using {Ruber.[]}
168
+ @return [nil]
169
+ =end
170
+ def delayed_initialize
171
+ end
172
+ private :delayed_initialize
173
+
174
+ =begin rdoc
175
+ Adds the project options provided by the plugin to a project
176
+
177
+ Only the options whose rules match the project are added.
178
+
179
+ If one of the options
180
+ provided by the plugin (and whose rules matches the project) has already been
181
+ inserted in the project, this method can either raise an exception or ignore the
182
+ option. The first behaviour is desirable the first time the plugin's options are
183
+ added to the project, while the second is useful if this method has already been
184
+ called for the project. In the first case, the existing option most likely belongs
185
+ to another plugin, which may lead to conflict. In the second case, instead, the
186
+ option will belong to this plugin, so there's no risk.
187
+
188
+ @param [Ruber::AbstractProject] prj the project to add the options to
189
+ @param [Boolean] forbid_existing whether to raise an exception or do nothing if
190
+ an option already exists in the project.
191
+ @raise ArgumentError if _forbid_existing_ is *true* and one of the options provided
192
+ by the plugin already exists
193
+ @return [nil]
194
+ @see Ruber::AbstractProject#match_rule?
195
+ =end
196
+ def add_options_to_project prj, forbid_existing = true
197
+ @plugin_description.project_options.values.sort_by{|i| i.order || 0}.each do |o|
198
+ o = o.to_os(prj.obj_binding)
199
+ begin prj.add_option o if prj.match_rule?(o)
200
+ rescue ArgumentError
201
+ raise if forbid_existing
202
+ end
203
+ end
204
+ nil
205
+ end
206
+
207
+ =begin rdoc
208
+ Removes the project options provided by the plugin from a project
209
+
210
+ This method can operate in two ways: it can remove from the project all the options
211
+ it provides whose rules match or don't match the project. The first behaviour is
212
+ meant to be used when the plugin is unloaded or the project is closed; the second
213
+ when the project characteristics change, to remove those options which used to
214
+ match the project but don't anymore.
215
+
216
+ @param [Ruber::AbstractProject] prj the project to remove options from
217
+ @param [Boolean] matching whether to remove only
218
+ @return [nil]
219
+ @see Ruber::AbstractProject#match_rule?
220
+ =end
221
+ def remove_options_from_project prj, matching = true
222
+ if matching
223
+ @plugin_description.project_options.each_pair do |_, o|
224
+ o = o.to_os(prj.obj_binding)
225
+ prj.remove_option o.group, o.name if prj.match_rule? o
226
+ end
227
+ else
228
+ @plugin_description.project_options.each_pair do |_, o|
229
+ o = o.to_os(prj.obj_binding)
230
+ if prj.has_option?(o.group, o.name) and !prj.match_rule? o
231
+ prj.remove_option o.group, o.name
232
+ end
233
+ end
234
+ end
235
+ nil
236
+ end
237
+
238
+ =begin rdoc
239
+ Adds the project widgets provided by the plugin to a project
240
+
241
+ Only the widgets matching the project will be added.
242
+
243
+ @param [Ruber::AbstractProject] prj the project to adds the widgets to
244
+ @return [nil]
245
+ =end
246
+ def add_widgets_to_project prj
247
+ @plugin_description.project_widgets.each do |w|
248
+ prj.add_widget w if prj.match_rule? w
249
+ end
250
+ nil
251
+ end
252
+
253
+ =begin rdoc
254
+ Removes the project widgets provided by the plugin from a project
255
+
256
+ @param [Ruber::AbstractProject] prj the project to remove the widgets from
257
+ @return [nil]
258
+ =end
259
+ def remove_widgets_from_project prj
260
+ @plugin_description.project_widgets.each do |w|
261
+ prj.remove_widget w
262
+ end
263
+ nil
264
+ end
265
+
266
+ =begin rdoc
267
+ Adds the project extensions provided by the plugin to a project
268
+
269
+ Only the extensions matching the project will be added.
270
+
271
+ If the project already has one of the extensions this method wouold add, it can
272
+ either raise an exception or ignore the
273
+ exception. The first behaviour is desirable the first time the plugin's extensions are
274
+ added to the project, while the second is useful if this method has already been
275
+ called for the project. In the first case, the existing extension most likely belongs
276
+ to another plugin, which may lead to conflict. In the second case, instead, the
277
+ extension will belong to this plugin, so there's no risk.
278
+
279
+ @param [Ruber::AbstractProject] prj the project to add the extensions to
280
+ @param [Boolean] forbid_existing whether to raise an exception or do nothing if
281
+ an extension already exists in the project.
282
+ @raise ArgumentError if _forbid_existing_ is *true* and the project already has
283
+ one of the extension which this method would add
284
+ @return [nil]
285
+ @see Ruber::AbstractProject#match_rule?
286
+ =end
287
+ def add_extensions_to_project prj, forbid_existing = true
288
+ @plugin_description.extensions.each_pair do |name, o|
289
+ unless forbid_existing
290
+ next if prj.extension name
291
+ end
292
+ ext = nil
293
+ if o.is_a? Array
294
+ o = o.find{|i| prj.match_rule? i}
295
+ next unless o
296
+ ext = o.class_obj.new prj
297
+ elsif prj.match_rule? o
298
+ ext = o.class_obj.new prj
299
+ end
300
+ if ext
301
+ ext.plugin = self
302
+ prj.add_extension name, ext
303
+ emit extension_added(name.to_s, prj) rescue nil
304
+ end
305
+ end
306
+ end
307
+
308
+ =begin rdoc
309
+ Remove the extensions provided by the pluging from a project
310
+
311
+ Depending on the value of _all_, all the extensions provided by the plugin or
312
+ only the ones which dont' match the project are removed. In this case, a multi-class
313
+ extension will only be removed if the class of the extension object is the same
314
+ as the one specified in one of the entries which don't match the project.
315
+
316
+ *Note:* to decide whether an extension belongs to the plugin or not, this method
317
+ checks whether the object returned by the exension's @plugin@ method is the same
318
+ as @self@.
319
+
320
+ @param [Ruber::AbstractProject] prj the project to remove the extensions from
321
+ @param [Boolean] all whether to remove all extensions provided by the plugin or
322
+ only those which don't match the project
323
+ @return [nil]
324
+ @see Ruber::AbstractProject#match_rule?
325
+ =end
326
+ def remove_extensions_from_project prj, all = true
327
+ if all
328
+ prj.each_extension.select{|_, v| v.plugin.same? self}.each do |k, _|
329
+ emit removing_extension k.to_s, prj rescue nil
330
+ prj.remove_extension k
331
+ emit extension_removed k.to_s, prj rescue nil
332
+ end
333
+ else
334
+ exts = @plugin_description.extensions
335
+ prj.each_extension.select{|_, v| v.plugin.same? self}.each do |k, o|
336
+ data = exts[k]
337
+ data = data.find{|i| i.class_obj == o.class} if data.is_a? Array
338
+ if !prj.match_rule? data
339
+ emit removing_extension k.to_s, prj rescue nil
340
+ prj.remove_extension k
341
+ emit extension_removed k.to_s, prj rescue nil
342
+ end
343
+ end
344
+ end
345
+ nil
346
+ end
347
+
348
+ =begin rdoc
349
+ Informs a project of the existance of the plugin
350
+
351
+ The base class implemenetation adds all the known project options, poject widgets and project
352
+ extensions to the project. If a plugin needs to do something fancy with projects,
353
+ it can override this method and do it from here, after calling the base class
354
+ implementation.
355
+
356
+ @param [Ruber::AbstractProject] prj the project to register with
357
+ @return [nil]
358
+ =end
359
+ def register_with_project prj
360
+ add_options_to_project prj, true
361
+ add_widgets_to_project prj
362
+ add_extensions_to_project prj, true
363
+ end
364
+
365
+ =begin rdoc
366
+ Removes all traces of the plugin from a project
367
+
368
+ This method is called when the plugin is unloaded or when the project is closed
369
+ and takes care of removeing all project options, project widgets and project extensions
370
+ belonging to the plugin from the project.
371
+
372
+ If a plugin needs to do some other cleanup when removed from a project, it can
373
+ override this method and do what it needs here (usually before calling *super*)
374
+ @param [Ruber::AbstractProject] prj the project to remove the plugin from
375
+ @return [nil]
376
+ =end
377
+ def remove_from_project prj
378
+ remove_options_from_project prj, true
379
+ remove_widgets_from_project prj
380
+ remove_extensions_from_project prj, true
381
+ end
382
+
383
+ =begin rdoc
384
+ Ensures that all the project options, widgets and extensions which are provided
385
+ by the plugin and match the project have been added to it and that none which
386
+ doesn't match it have been added
387
+
388
+ This method is called when one of the characteristics of the project the rules
389
+ take into account change, so that the plugin always add to the project all the
390
+ pertinent content
391
+ @param [Ruber::AbstractProject] prj the project to check
392
+ @return [nil]
393
+ =end
394
+ def update_project prj
395
+ remove_options_from_project prj, false
396
+ add_options_to_project prj, false
397
+ remove_widgets_from_project prj
398
+ add_widgets_to_project prj
399
+ remove_extensions_from_project prj, false
400
+ add_extensions_to_project prj, false
401
+ end
402
+
403
+ private
404
+
405
+ =begin rdoc
406
+ Initializes the plugin
407
+
408
+ If this were a class rather than a module, this would be its initialize method.
409
+ Since this is a module, it can't have an initialize method in the sense classes
410
+ do, therefore it's up to classes including this module to call this method from
411
+ their @initialize@, before using any functionality provided by this module.
412
+
413
+ The most important things done here are (in order):
414
+ * adding the plugin to the component manager, so that it can be accessed using
415
+ {Ruber.[]}
416
+ * connects the {#load_settings} method with the {Ruber::ConfigManager configuration manager}'s
417
+ {Ruber::ConfigManager#settings_changed settings_changed} signal
418
+ * adds the options provided by the plugin to the configuration manager and loads
419
+ the settings
420
+ * creates the tool widgets provided by the plugin
421
+
422
+ *Note:* everything regarding the configuration manager is ignored if it doesn't
423
+ exist
424
+ @return [PluginLike] *self*
425
+ =end
426
+ def initialize_plugin pdf
427
+ @plugin_description = pdf
428
+ Ruber[:components].add self
429
+ if Ruber[:config]
430
+ connect Ruber[:config], SIGNAL(:settings_changed), self, SLOT(:load_settings)
431
+ register_options
432
+ load_settings
433
+ end
434
+ @plugin_description.tool_widgets.each{|w| create_tool_widget w}
435
+ self
436
+ end
437
+
438
+ =begin rdoc
439
+ Creates the actions provided by the plugin
440
+
441
+ Once created, the actions are stored in the @KDE::ActionCollection@ given as
442
+ argument. If any UI handler is provided for an action in the PSF, it's registered
443
+ with the main window. If the PSF entry for an acton contains a @:slot@, a @:receiver@ and a @:signal@ entry,
444
+ a signal-slot connection is made using those parameters.
445
+
446
+ @param [KDE::ActionCollection] the action collection to add the new actions to
447
+ @return [nil]
448
+ @see #setup_action
449
+ =end
450
+ def setup_actions coll
451
+ @plugin_description.actions.each_value do |a|
452
+ action = setup_action a, coll
453
+ coll.add_action a.name, action
454
+ end
455
+ nil
456
+ end
457
+
458
+ =begin rdoc
459
+ Applies the configuration settings
460
+
461
+ This method is called when the plugin is created and whenever the global settings
462
+ change. The base class implementation does nothing. Plugins which need to react
463
+ to changes in the global settings must reimplement it.
464
+
465
+ *Note:* this method *must* be a slot, so any class which directly includes this
466
+ module should have a line like
467
+
468
+ <code>slots :load_settings</code>
469
+ @return [nil]
470
+ =end
471
+ def load_settings
472
+ end
473
+
474
+ =begin rdoc
475
+ Adds the global options and configuration widgets provided by the plugin to the
476
+ configuration manager
477
+
478
+ It does nothing if the configuration manager hasn't as yet been created
479
+ @return [nil]
480
+ =end
481
+ def register_options
482
+ config = Ruber[:config]
483
+ return unless config
484
+ @plugin_description.config_options.values.sort_by{|o| o.order || 0}.each do |o|
485
+ config.add_option o.to_os(binding)
486
+ end
487
+ @plugin_description.config_widgets.each{|w| config.add_widget w}
488
+ nil
489
+ end
490
+
491
+ =begin rdoc
492
+ Creates a tool widget and inserts it in the main window
493
+
494
+ It uses the data contained in the PSF to find out the characteristics of the tool
495
+ widget. If the PSF contains a @var_name@ entry for the tool widget, then an
496
+ instance variable with that name will be created and set to the tool widget. If
497
+ the PSF contains a @name@ entry for the tool widget, its @object_name@ will be
498
+ set to that value.
499
+
500
+ If the tool widget object has a @load_settings@ method, it'll be connected with
501
+ the {ConfigManager configuration manager}'s {ConfigManager#settings_changed settings_changed}
502
+ signal
503
+
504
+ @param [OpenStruct] data the data from the PSF corresponding to the tool widget
505
+ @option data [String] code (nil) a string of code which, when evaluated in the
506
+ plugin's context, returns the tool widget. At least one between this entry and
507
+ the @class_obj@ entry must be not *nil*
508
+ @option data [Class] :class_obj (nil) the class of the tool widget. Ignored if
509
+ the @code@ entry is not *nil*At least one between this entry and
510
+ the @code@ entry must be not *nil*
511
+ @option data [String] pixmap the name of the file containing the icon to use for
512
+ the tool widget. It's mandatory
513
+ @option data [String] caption the caption for the tool widget. Mandatory
514
+ @option data [Symbol] position (:left) the side of the screen where to put the
515
+ tool widget. The values @:left@, @:right@ and @:bottom@ are valid
516
+ @return [nil]
517
+ =end
518
+ def create_tool_widget data
519
+ w = data.code ? eval(w, binding) : data.class_obj.new
520
+ w.object_name = data.name if data.name
521
+ Ruber[:main_window].add_tool data.position, w, Qt::Pixmap.new(data.pixmap), data.caption
522
+ instance_variable_set("@#{data.var_name}", w) if data.var_name
523
+ if w.respond_to? :load_settings and Ruber[:config]
524
+ connect Ruber[:config], SIGNAL("settings_changed()"), w, SLOT("load_settings()")
525
+ w.load_settings
526
+ end
527
+ end
528
+
529
+ =begin rdoc
530
+ Creates one of the actions described in the PSF
531
+
532
+ Besides creating the action and adddint it to the specified @KDE::ActionCollection@,
533
+ it registers a state handler for the action in the main window, if the PSF entry
534
+ for the action includes a @state@ entry (the plugin iself is used as @extra_id@
535
+ for the handler), and creates a signal-slot connection between
536
+ the action and the receiver specified in the @receiver@ PSF entry for the action,
537
+ provided that the @slot@ entry isn't *nil*.
538
+
539
+ @param [OpenStruct] data the structure containing the data used to create the
540
+ action
541
+ @param [KDE::ActionCollection] cool the action collection the new action belongs
542
+ to
543
+ @option data [Symbol,String,nil] standard_action (nil) if not nil, the name of the
544
+ @KDE::StandardAction@ method to call to create the action. If this entry isn't
545
+ given, the action won't be a standard action
546
+ @option data [Class] action_class (KDE::Action) the class object to instantiate
547
+ to create the action
548
+ @option data [String] text ('') the text for the action
549
+ @option data [String] help_text ('') the help text for the action
550
+ @option data [String] shortcut ('') a string representing the default shortcut
551
+ for the action
552
+ @option data [String] icon ('') the filename of the icon to use for the action
553
+ @option data [String] slot (nil) the slot to connect a signal from the action to.
554
+ If missing, no signal-slot connection is made.
555
+ @option data [String,Symbol] signal ('triggered()') the signature of the action
556
+ signal to connect to. Ignored unless the @slot@ entry is given
557
+ @option data [String] receiver ('self') a string which, when evalued in the
558
+ context of the plugin, returns the object the action should be connected to
559
+ @option data [String] state (nil) the name of a single GUI state the action depends
560
+ on. Note that this method doesn't register a state handler for multiple states
561
+ @return [KDE::Action] the newly created action
562
+ =end
563
+ def setup_action data, coll
564
+ action = if data.standard_action
565
+ if data.standard_action.to_s == 'open'
566
+ KDE::StandardAction.open nil, '', coll
567
+ else KDE::StandardAction.send data.standard_action, nil, '', coll
568
+ end
569
+ else data.action_class.new coll
570
+ end
571
+ action.text = data.text unless data.text.empty?
572
+ action.help_text = data.help unless data.help.empty?
573
+ action.shortcut = data.shortcut if data.shortcut
574
+ action.icon = Qt::Icon.new(data.icon) unless data.icon.empty?
575
+ if data.slot
576
+ rec = instance_eval(data.receiver)
577
+ connect action, SIGNAL(data.signal), rec, SLOT(data.slot)
578
+ end
579
+ state = data.state
580
+ if state
581
+ Ruber[:main_window].register_action_handler action, state, :extra_id => self
582
+ end
583
+ action
584
+ end
585
+
586
+ end
587
+
588
+
589
+ end