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.
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