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,4 @@
1
+ name: documents
2
+ require: document_list
3
+ class: Ruber::DocumentList
4
+ features: :docs
@@ -0,0 +1,506 @@
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 'ktexteditor'
22
+ require 'forwardable'
23
+ require 'dictionary'
24
+
25
+ require 'ruber/editor/ktexteditor_wrapper'
26
+ require 'ruber/editor/editor_view'
27
+ require 'ruber/utils'
28
+ require 'ruber/document_project'
29
+
30
+ module Ruber
31
+
32
+ class Document < Qt::Object
33
+
34
+ extend Forwardable
35
+
36
+ include Activable
37
+
38
+ include KTextEditorWrapper
39
+
40
+ def_delegator :@doc, :documentSave, :save_document
41
+
42
+ signal_data = {
43
+ 'text_changed' => ['KTextEditor::Document*', [nil]],
44
+ 'about_to_close' => ['KTextEditor::Document*', [nil]],
45
+ 'about_to_reload' => ['KTextEditor::Document*', [nil]],
46
+ 'highlighting_mode_changed' => ['KTextEditor::Document*', [nil]],
47
+ 'document_url_changed' => ['KTextEditor::Document*', [nil]],
48
+ 'mode_changed' => ['KTextEditor::Document*', [nil]]
49
+ }
50
+
51
+ @signal_table = KTextEditorWrapper.prepare_wrapper_connections self, signal_data
52
+
53
+ signals 'modified_changed(bool, QObject*)', 'document_name_changed(QString, QObject*)',
54
+ 'text_changed(QObject*)', 'about_to_close(QObject*)', 'about_to_reload(QObject*)',
55
+ 'document_url_changed(QObject*)', 'highlighting_mode_changed(QObject*)',
56
+ 'mode_changed(QObject*)', 'text_modified(KTextEditor::Range, KTextEditor::Range, QObject*)',
57
+ 'text_inserted(KTextEditor::Range, QObject*)', 'text_removed(KTextEditor::Range, QObject*)',
58
+ 'view_created(QObject*, QObject*)', 'closing(QObject*)', :activated, :deactivated,
59
+ 'modified_on_disk(QObject*, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)'
60
+
61
+ slots :document_save_as, :save
62
+
63
+ =begin rdoc
64
+ The view associated with the document. If a view hasn't been created, this is *nil*
65
+ =end
66
+ attr_reader :view
67
+
68
+ def inspect
69
+ if disposed? then "< #{self.class} #{object_id}DISPOSED >"
70
+ else super
71
+ end
72
+ end
73
+
74
+ =begin rdoc
75
+ Creates a new Ruber::Document.
76
+ =end
77
+ def initialize parent = nil, file = nil
78
+ super parent
79
+ @active = false
80
+ @doc = KTextEditor::EditorChooser.editor('katepart').create_document( self)
81
+ initialize_wrapper @doc, self.class.instance_variable_get(:@signal_table)
82
+ @view = nil
83
+ @doc.openUrl(file.is_a?(String) ? KDE::Url.from_path(file) : file) if file
84
+ @annotation_model = AnnotationModel.new self
85
+ interface('annotation_interface').annotation_model = @annotation_model
86
+ interface('modification_interface').modified_on_disk_warning = true
87
+ @modified_on_disk = false
88
+ @project = DocumentProject.new self
89
+
90
+ @doc.connect(SIGNAL('modifiedChanged(KTextEditor::Document*)')) do |doc|
91
+ emit modified_changed(@doc.modified?, self)
92
+ end
93
+ @doc.connect(SIGNAL('documentNameChanged(KTextEditor::Document*)')) do |doc|
94
+ Ruber[:components].each_component{|c| c.update_project @project}
95
+ emit document_name_changed doc.document_name, self
96
+ end
97
+
98
+ @doc.connect(SIGNAL('textChanged(KTextEditor::Document*, KTextEditor::Range, KTextEditor::Range)')){|_, o, n| emit text_modified(o, n, self)}
99
+
100
+ @doc.connect(SIGNAL('textInserted(KTextEditor::Document*, KTextEditor::Range)')) do |_, r|
101
+ begin
102
+ emit text_inserted(r, self)
103
+ rescue ArgumentError => e
104
+ ExceptionDialog.new e, nil, true, "An exception was raised when writing text. See issue number 6 at http://github.com/stcrocco/ruber/issues"
105
+ end
106
+ end
107
+
108
+ @doc.connect(SIGNAL('textRemoved(KTextEditor::Document*, KTextEditor::Range)')){|_, r| emit text_removed(r, self)}
109
+ @doc.connect(SIGNAL('modifiedOnDisk(KTextEditor::Document*, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)')) do |_, mod, reason|
110
+ @modified_on_disk = (reason != KTextEditor::ModificationInterface::OnDiskUnmodified)
111
+ emit modified_on_disk(self, mod, reason)
112
+ end
113
+
114
+ end
115
+
116
+ =begin rdoc
117
+ Executes the action with name _name_ contained in document's view's action
118
+ collection. This is made by having the action emit the <tt>triggered()</tt> or
119
+ <tt>toggled(bool)</tt> signal (depending on whether it's a standard action or a
120
+ <tt>KDE::ToggleAction</tt>). In the second case, _arg_ is the argument passed to
121
+ the signal.
122
+
123
+ Returns *true* if _name_ is the name of an action and *false* otherwise.
124
+
125
+ <b>Note:</b> for this method to work, a view should have been created for the
126
+ document, otherwise this method will always return *false.
127
+ =end
128
+ def execute_action name, arg = nil
129
+ @view ? @view.execute_action( name, arg) : false
130
+ end
131
+
132
+ =begin rdoc
133
+ Compares the mimetype and file name of the document with a list of mimetypes (
134
+ using <tt>KDE::MimeType#=~</tt>) and/or patterns (using <tt>File.fnmatch</tt>),
135
+ returning *true* if any of the comparisons is successful and
136
+ *false* if all fails. Both _mimetypes_ and _patterns_ can be either a string or
137
+ an array of strings (a single string will be treated as an array containing a
138
+ single string).
139
+
140
+ ====Notes:
141
+ * if both _mimetypes_ and _patterns_ are empty, the comparison always returns *true*.
142
+ * if the document is not associated with a file (that is, if +path+ returns an
143
+ empty string) it won't match any pattern. It will match the <tt>text/plain</tt>
144
+ mimetype, however.
145
+ * only the basename of the file will be taken into account for pattern matching.
146
+ For example, the pattern <tt>abc/xyz.jkl</tt> will match the pattern <tt>xyz.jkl</tt>,
147
+ which wouldn't be the case if the whole filename was included.
148
+ =end
149
+ def file_type_match? mimetypes = [], patterns = []
150
+ mime = KDE::MimeType.mime_type @doc.mime_type
151
+ mimetypes = Array(mimetypes).reject{|i| i.empty?}
152
+ patterns = Array(patterns).reject{|i| i.empty?}
153
+ base = File.basename path
154
+ if mimetypes.empty? and patterns.empty? then true
155
+ elsif mimetypes.any? {|m| mime =~ m} then true
156
+ elsif patterns.any? {|pat| File.fnmatch? pat, base, File::FNM_DOTMATCH} then true
157
+ else false
158
+ end
159
+ end
160
+
161
+ =begin rdoc
162
+ Returns the document extension with name _name_ or *nil* if such an extension
163
+ doesn't exist
164
+ =end
165
+ def extension name
166
+ @project.extension name
167
+ end
168
+
169
+ =begin rdoc
170
+ Returns an appropriate <tt>Qt::Icon</tt> for the document, depending on the mimetype and the
171
+ status of the document.
172
+ =end
173
+ def icon
174
+ if @modified_on_disk then ICONS[:modified_on_disk]
175
+ elsif @doc.modified? then ICONS[:modified]
176
+ else
177
+ icon_name = KDE::MimeType.mime_type(@doc.mime_type).icon_name
178
+ Qt::Icon.new(KDE::IconLoader.load_mime_type_pixmap icon_name)
179
+ end
180
+ end
181
+
182
+ =begin rdoc
183
+ Tells whether the document is associated with a file, that is if it has been
184
+ saved to file before or not.
185
+ =end
186
+ def has_file?
187
+ !@doc.url.empty?
188
+ end
189
+
190
+ =begin rdoc
191
+ Tells whether the document is _pristine_ or not. A pristine document is an empty,
192
+ unmodified document which hasn't a file associated with it. The document returned
193
+ by <tt>Document.new</tt> is pristine is the second argument is *nil*, but it's
194
+ not pristine if a non-*nil* second argument was given (because in that case the
195
+ document has a file associated with it).
196
+ =end
197
+ def pristine?
198
+ @doc.url.empty? and !@doc.modified? and @doc.text.nil?
199
+ end
200
+
201
+ =begin rdoc
202
+ Tells whether the document is modified on disk or not
203
+ =end
204
+ def modified_on_disk?
205
+ @modified_on_disk
206
+ end
207
+
208
+ =begin rdoc
209
+ Saves the document. If the document is already associated with a file, it's saved
210
+ in that file; otherwise, a Save As dialog is displayed for the user to choose a
211
+ file name. Returns *true* if the document was saved and *false* if it wasn't for
212
+ some reason (for example, if the user doesn't have write perimission on the file
213
+ or if he pressed the Cancel button in the Save As dialog).
214
+
215
+ This method is associated with the Save menu entry
216
+ =end
217
+ def save
218
+ if path.empty? || !is_read_write then document_save_as
219
+ else
220
+ res = @doc.save
221
+ @project.save
222
+ res
223
+ end
224
+ # unless url.empty? then save_document
225
+ # else document_save_as
226
+ # res = KDE::FileDialog.get_save_file_name KDE::Url.new, "*.rb|Ruby files (*.rb)\n*|All files", nil,
227
+ # KDE::i18n("Save \"#{@doc.document_name}\" as")
228
+ # return false unless res
229
+ # save_as KDE::Url.from_path(res)
230
+ # end
231
+ end
232
+
233
+ =begin rdoc
234
+ Creats a view for the document. _parent_ is the view's parent widget. Raises
235
+ +RuntimeError+ if the document already has a view.
236
+ =end
237
+ def create_view parent = nil
238
+ raise "Can't create a second view for this document" if @view
239
+ @doc.create_view nil
240
+ @view = EditorView.new self, @doc.views.first, parent
241
+ @view.connect(SIGNAL('closing()')){@view = nil}
242
+ @view.connect(SIGNAL('destroyed(QObject*)')){@view = nil}
243
+ gui = @view.send(:internal)
244
+ action = gui.action_collection.action('file_save_as')
245
+ disconnect action, SIGNAL(:triggered), @doc, SLOT('documentSaveAs()')
246
+ connect action, SIGNAL(:triggered), self, SLOT(:document_save_as)
247
+ action = gui.action_collection.action('file_save')
248
+ disconnect action, SIGNAL(:triggered), @doc, SLOT('documentSave()')
249
+ connect action, SIGNAL(:triggered), self, SLOT(:save)
250
+ emit view_created(@view, self)
251
+ @view
252
+ end
253
+
254
+ =begin rdoc
255
+ Return the project with wider scope the document belongs to. This is:
256
+ * the current global project if it exists and the document is associated with a file
257
+ belonging to it
258
+ * the document project if there's no active global project or the document isn't
259
+ associated with a file or the file doesn't belong the global project
260
+ =end
261
+ def project
262
+ prj = Ruber[:projects].current
263
+ return @project if path.empty? or !prj
264
+ prj.project_files.file_in_project?(path) ? prj : @project
265
+ end
266
+
267
+ =begin rdoc
268
+ Returns the DocumentProject associated with the document
269
+ =end
270
+ def own_project
271
+ @project
272
+ end
273
+
274
+ def save_settings
275
+ @project.save unless path.empty?
276
+ end
277
+
278
+ =begin rdoc
279
+ Returns the path of the document
280
+ =end
281
+ def path
282
+ @doc.url.path || ''
283
+ end
284
+
285
+ =begin rdoc
286
+ Returns the text in the document
287
+ ---
288
+ We can't just delegate this method to the internal <tt>KTextEditor::Document</tt>
289
+ because its +text+ method returns nil if there's no text in the document, instead
290
+ of an empty string.
291
+ =end
292
+ def text
293
+ @doc.text || ''
294
+ end
295
+
296
+ =begin rdoc
297
+ Executes the given block inside a pair of <tt>start_editing</tt>/<tt>end_editing</tt>
298
+ calls.
299
+ =end
300
+ def editing
301
+ begin
302
+ @doc.start_editing
303
+ yield
304
+ ensure @doc.end_editing
305
+ end
306
+ end
307
+
308
+ =begin rdoc
309
+ Closes the document. If _ask_ is *true*, the <tt>query_close</tt> method is called,
310
+ asking the user what to do if the document is modified. If the user confirms
311
+ closing or if there's no need of confirmation, the following happens:
312
+ * the <tt>closing(QObject*)</tt> signal is emitted
313
+ * the view (if it exists) is closed
314
+ * the <tt>close_url</tt> method is called
315
+ * all the documnent extensions are removed
316
+ * al singnals are disconnected from the document
317
+ * the document is disposed of
318
+
319
+ Returns *true* if the document was closed and *false* otherwise
320
+
321
+ TODO: maybe remove the argument, since this method is not called anymore at
322
+ =end
323
+ def close ask = true
324
+ if !ask || query_close
325
+ emit closing(self)
326
+ @project.save unless path.empty?
327
+ @view.close if @doc.views.size > 0
328
+ return false unless close_url false
329
+ @project.close false
330
+ self.disconnect
331
+ dispose
332
+ true
333
+ else false
334
+ end
335
+ end
336
+
337
+ =begin rdoc
338
+ Closes the view _view_ associated with the document.
339
+
340
+ Currently, the _view_ parameter is unused, as a document can only have one view,
341
+ and all this method does is to call the <tt>close</tt> method with _ask_ as argument.
342
+ =end
343
+ def close_view view, ask = true
344
+ close ask
345
+ end
346
+
347
+ =begin rdoc
348
+ The <tt>KParts::Part</tt> associated with the document
349
+ =end
350
+ alias_method :part, :internal
351
+
352
+ private
353
+
354
+ =begin rdoc
355
+ Works like <tt>KTextEditor::Document#documentSave</tt> but sets the starting directory to
356
+ either the project directory, if there's an active project, or to the default script
357
+ directory otherwise.
358
+
359
+ <b>Note:</b> there's a small difference with <tt>KTextEditor::Document#documentSave</tt>.
360
+ In that method, the encoding initially selected in the combo box is read from the
361
+ configuration. Since I couldn't figure how to access that, instead, the default
362
+ encoding here is set to UTF-8 if using ruby 1.9 and to ISO-8859-1 if using ruby
363
+ 1.8
364
+ =end
365
+ def document_save_as
366
+ enc = RUBY_VERSION.match(/1\.9/) ? 'UTF-8' : 'ISO-8859-1'
367
+ prj = Ruber[:projects].current
368
+ path = if !self.path.empty? then self.path
369
+ elsif prj then prj.project_directory
370
+ else ''
371
+ end
372
+
373
+ res = KDE::EncodingFileDialog.get_save_file_name_and_encoding enc, path, '',
374
+ Ruber[:main_window], KDE.i18n('Save File')
375
+ return false if res.file_names.empty? or res.file_names.first.empty?
376
+ u = KDE::Url.new res.file_names.first
377
+ if u.is_local_file and File.exist?(u.path)
378
+ ans = KDE::MessageBox.warning_continue_cancel Ruber[:main_window],
379
+ KDE.i18n(format("A file named \"%s\" already exists. \nAre you sure you want to overwrite it?", u.path)),
380
+ i18n( "Overwrite File?" ), KDE::StandardGuiItem.overwrite,
381
+ KDE::StandardGuiItem.cancel, '', KDE::MessageBox::Notify | KDE::MessageBox::Dangerous
382
+ return false if ans == KDE::MessageBox::Cancel
383
+ end
384
+ @doc.encoding = res.encoding
385
+ @project.save
386
+ @doc.saveAs u
387
+ end
388
+
389
+ =begin rdoc
390
+ A hash associating icon roles used by Document with icon names. It is used by
391
+ +ICONS+
392
+ =end
393
+ ICON_NAMES = {
394
+ :modified => 'document-save',
395
+ :modified_on_disk => 'dialog-warning'
396
+ }
397
+
398
+ =begin rdoc
399
+ Hash containing a list of predefined icons, each associated with a role (usually
400
+ a symbol describing the icon's use).
401
+
402
+ At the beginning, this hash is empty. It is automatically filled by loading icons
403
+ according with the associations specified in <tt>ICON_NAMES</tt> as they're requested.
404
+ This is necessary because the hash is created when this file is read, which may
405
+ happen before the application is created.
406
+ =end
407
+ ICONS = Hash.new do |h, k|
408
+ icon = KDE::IconLoader.load_icon ICON_NAMES[k]
409
+ h[k] = icon
410
+ end
411
+
412
+ end
413
+
414
+ class AnnotationModel < KTextEditor::AnnotationModel
415
+
416
+ include Enumerable
417
+
418
+ Annotation = Struct.new :type, :line, :msg, :tool_tip, :data
419
+
420
+ signals 'annotations_changed()', 'annotation_changed(int)'
421
+
422
+ @annotation_types = {}
423
+
424
+ class << self
425
+ attr_reader :annotation_types
426
+ end
427
+
428
+ def self.register_annotation_type type, back = nil, fore = nil
429
+ raise ArgumentError, "Annotation type #{type} has already been added" if
430
+ @annotation_types.has_key?(type)
431
+ @annotation_types[type] = [back, fore].map{|i| i ? Qt::Variant.fromValue(i) : Qt::Variant.new}
432
+ end
433
+
434
+ def initialize doc
435
+ super()
436
+ @doc = doc
437
+ @annotations = Dictionary.alpha
438
+ connect self, SIGNAL('annotation_changed(int)'), self, SIGNAL('lineChanged(int)')
439
+ end
440
+
441
+ def add_annotation *args
442
+ a = args.size == 1 ? args[0] : Annotation.new( *args )
443
+ # TODO: see why this sometimes gives extremely weird errors
444
+ #The begin/rescue clause is there to find out why sometimes I get a crash saying:
445
+ # `<': comparison of Fixnum with Qt::Variant failed (ArgumentError)
446
+ # begin
447
+
448
+ # raise IndexError, "Invalid line: #{a.line}" unless a.line < @doc.lines
449
+ # rescue ArgumentError
450
+ # puts "a.line: #{a.line}(#{a.line.class})"
451
+ # puts "@doc.lines: #{@doc.lines}(#{@doc.lines.class})"
452
+ # end
453
+ @annotations[a.line] = a
454
+ emit annotations_changed
455
+ emit annotation_changed( a.line)
456
+ end
457
+
458
+ def data line, role
459
+ a = @annotations[line]
460
+ return Qt::Variant.new unless a
461
+ case role
462
+ when Qt::DisplayRole then Qt::Variant.new a.msg
463
+ when Qt::ToolTipRole then Qt::Variant.new a.tool_tip
464
+ when Qt::ForegroundRole then self.class.annotation_types[a.type][1]
465
+ when Qt::BackgroundRole then self.class.annotation_types[a.type][0]
466
+ else Qt::Variant.new
467
+ end
468
+ end
469
+
470
+ def annotation line
471
+ @annotations[line]
472
+ end
473
+ alias_method :[], :annotation
474
+
475
+ def has_annotation? line
476
+ @annotations.has_key? line
477
+ end
478
+
479
+ def has_annotations?
480
+ !@annotations.empty?
481
+ end
482
+
483
+ def empty?
484
+ @annotations.empty?
485
+ end
486
+
487
+ def clear
488
+ @annotations.clear
489
+ emit annotations_changed
490
+ emit reset
491
+ end
492
+
493
+ def remove_annotation line
494
+ if @annotations.delete line
495
+ emit annotations_changed
496
+ emit annotation_changed( line )
497
+ end
498
+ end
499
+
500
+ def each
501
+ @annotations.each_pair{|k, v| yield v}
502
+ end
503
+
504
+ end
505
+
506
+ end