ruber 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +25 -0
- data/bin/ruber +0 -0
- data/data/share/apps/ruber/ruberui.rc +15 -1
- data/data/share/icons/{ruber.png → ruber-old.pgn} +0 -0
- data/lib/ruber/application/application.rb +216 -73
- data/lib/ruber/application/plugin.yaml +2 -2
- data/lib/ruber/document_project.rb +25 -5
- data/lib/ruber/documents/document_list.rb +11 -15
- data/lib/ruber/editor/document.rb +106 -50
- data/lib/ruber/editor/editor_view.rb +4 -2
- data/lib/ruber/external_program_plugin.rb +8 -0
- data/lib/ruber/kde_config_option_backend.rb +12 -4
- data/lib/ruber/kde_sugar.rb +35 -1
- data/lib/ruber/main_window/choose_plugins_dlg.rb +10 -10
- data/lib/ruber/main_window/hint_solver.rb +263 -0
- data/lib/ruber/main_window/main_window.rb +462 -206
- data/lib/ruber/main_window/main_window_actions.rb +228 -62
- data/lib/ruber/main_window/main_window_internal.rb +169 -115
- data/lib/ruber/main_window/plugin.yaml +13 -3
- data/lib/ruber/main_window/save_modified_files_dlg.rb +1 -1
- data/lib/ruber/main_window/ui/choose_plugins_widget.rb +1 -1
- data/lib/ruber/main_window/ui/main_window_settings_widget.rb +1 -1
- data/lib/ruber/main_window/ui/new_project_widget.rb +1 -1
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +1 -1
- data/lib/ruber/main_window/ui/output_color_widget.rb +1 -1
- data/lib/ruber/main_window/ui/workspace_settings_widget.rb +51 -0
- data/lib/ruber/main_window/ui/workspace_settings_widget.ui +28 -0
- data/lib/ruber/main_window/view_manager.rb +418 -0
- data/lib/ruber/main_window/workspace.png +0 -0
- data/lib/ruber/output_widget.rb +43 -37
- data/lib/ruber/pane.rb +621 -0
- data/lib/ruber/plugin_specification_reader.rb +8 -1
- data/lib/ruber/projects/project_files_list.rb +6 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +1 -1
- data/lib/ruber/projects/ui/project_files_widget.rb +1 -1
- data/lib/ruber/qt_sugar.rb +94 -4
- data/lib/ruber/utils.rb +16 -7
- data/lib/ruber/version.rb +2 -2
- data/plugins/autosave/autosave.rb +62 -1
- data/plugins/autosave/plugin.yaml +1 -0
- data/plugins/autosave/ui/autosave_config_widget.rb +37 -14
- data/plugins/autosave/ui/autosave_config_widget.ui +62 -12
- data/plugins/find_in_files/find_in_files_widgets.rb +1 -3
- data/plugins/find_in_files/ui/config_widget.rb +1 -1
- data/plugins/find_in_files/ui/find_in_files_widget.rb +1 -1
- data/plugins/rake/plugin.yaml +1 -1
- data/plugins/rake/ui/add_quick_task_widget.rb +1 -1
- data/plugins/rake/ui/choose_task_widget.rb +1 -1
- data/plugins/rake/ui/config_widget.rb +1 -1
- data/plugins/rake/ui/project_widget.rb +1 -1
- data/plugins/rspec/rspec.rb +14 -22
- data/plugins/rspec/ruber_rspec_formatter.rb +4 -1
- data/plugins/rspec/ui/rspec_project_widget.rb +1 -1
- data/plugins/ruby_development/plugin.yaml +7 -2
- data/plugins/ruby_development/ruby_development.rb +134 -13
- data/plugins/ruby_development/ui/config_widget.rb +66 -0
- data/plugins/ruby_development/ui/config_widget.ui +58 -0
- data/plugins/ruby_development/ui/project_widget.rb +1 -1
- data/plugins/ruby_runner/plugin.yaml +2 -2
- data/plugins/ruby_runner/ruby_runner.rb +15 -3
- data/plugins/ruby_runner/ui/config_widget.rb +1 -1
- data/plugins/ruby_runner/ui/project_widget.rb +1 -1
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +1 -1
- data/plugins/state/plugin.yaml +6 -2
- data/plugins/state/state.rb +305 -81
- data/plugins/state/ui/config_widget.rb +1 -1
- data/spec/common.rb +11 -3
- data/spec/document_list_spec.rb +8 -8
- data/spec/document_project_spec.rb +98 -25
- data/spec/document_spec.rb +178 -152
- data/spec/editor_view_spec.rb +26 -5
- data/spec/framework.rb +5 -0
- data/spec/hint_solver_spec.rb +450 -0
- data/spec/kde_sugar_spec.rb +73 -6
- data/spec/output_widget_spec.rb +172 -156
- data/spec/pane_spec.rb +1165 -0
- data/spec/plugin_specification_reader_spec.rb +37 -1
- data/spec/project_files_list_spec.rb +30 -20
- data/spec/qt_sugar_spec.rb +269 -0
- data/spec/state_spec.rb +566 -353
- data/spec/utils_spec.rb +1 -1
- data/spec/view_manager_spec.rb +71 -0
- metadata +16 -4
data/lib/ruber/kde_sugar.rb
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
=end
|
|
20
20
|
|
|
21
21
|
require 'yaml'
|
|
22
|
+
require 'facets/boolean'
|
|
22
23
|
|
|
23
24
|
module KDE
|
|
24
25
|
|
|
@@ -30,6 +31,27 @@ module KDE
|
|
|
30
31
|
KDE::Url.new val
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
=begin rdoc
|
|
35
|
+
Tells whether a string looks like an url pointing to a file
|
|
36
|
+
|
|
37
|
+
An url is considered to _look like_ an url pointing to a file if it contains a
|
|
38
|
+
scheme part and an authority part, that is, if it starts with a scheme followed
|
|
39
|
+
by two slashes.
|
|
40
|
+
|
|
41
|
+
If _abs_only_ is *true*, this method returns *true* only if the file path is absolute,
|
|
42
|
+
that is if the scheme is followed by three slashes. If _abs_only_ is *false*, it'll
|
|
43
|
+
return *true* for both absolute and relative paths.
|
|
44
|
+
@param [String] str the string to test
|
|
45
|
+
@param [Boolean] abs_only whether this method should return *true* only if the path
|
|
46
|
+
is absolute or also for relative paths
|
|
47
|
+
@return [Boolean] *true* if _str_ looks like an URL pointing to a file (or pointing
|
|
48
|
+
to an absolute file if _abs_only_ is *true*) and *false* otherwise
|
|
49
|
+
=end
|
|
50
|
+
def self.file_url? str, abs_only = false
|
|
51
|
+
slash_number = abs_only ? 3 : 2
|
|
52
|
+
str.match(%r|[\w+.-]+:/{#{slash_number},3}|).to_b
|
|
53
|
+
end
|
|
54
|
+
|
|
33
55
|
def to_yaml opts = {}
|
|
34
56
|
YAML.quick_emit(self, opts) do |out|
|
|
35
57
|
out.scalar taguri, to_encoded.to_s, :plain
|
|
@@ -45,7 +67,11 @@ module KDE
|
|
|
45
67
|
end
|
|
46
68
|
|
|
47
69
|
def local_file?
|
|
48
|
-
|
|
70
|
+
scheme == "file"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def remote_file?
|
|
74
|
+
!(local_file? or relative?)
|
|
49
75
|
end
|
|
50
76
|
|
|
51
77
|
end
|
|
@@ -123,6 +149,14 @@ args[:overlays], nil
|
|
|
123
149
|
res
|
|
124
150
|
end
|
|
125
151
|
|
|
152
|
+
def urls
|
|
153
|
+
count.times.inject([]) do |res, i|
|
|
154
|
+
url = KDE::Url.new u
|
|
155
|
+
url.path = File.expand_path(u) if url.protocol.empty?
|
|
156
|
+
res << url
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
126
160
|
end
|
|
127
161
|
|
|
128
162
|
class InputDialog
|
|
@@ -125,14 +125,14 @@ deselected.
|
|
|
125
125
|
@ui = Ui::ChoosePluginsWidget.new
|
|
126
126
|
@ui.setupUi self
|
|
127
127
|
|
|
128
|
-
dirs = Ruber[:
|
|
128
|
+
dirs = Ruber[:app].plugin_dirs
|
|
129
129
|
|
|
130
130
|
@chosen_plugins = Ruber[:config][:general, :plugins].map(&:to_sym)
|
|
131
131
|
loaded = Ruber[:components].plugins.map(&:plugin_name)
|
|
132
132
|
@chosen_plugins.delete_if{|i| !loaded.include? i}
|
|
133
133
|
|
|
134
134
|
read_plugins dirs
|
|
135
|
-
res = find_deps(:
|
|
135
|
+
res = find_deps(:sorry) do |e|
|
|
136
136
|
"There were problems making dependencies. #{create_failure_message e}\nAll plugins will be deselected"
|
|
137
137
|
end
|
|
138
138
|
if res
|
|
@@ -170,7 +170,7 @@ deselected.
|
|
|
170
170
|
=end
|
|
171
171
|
def write_settings
|
|
172
172
|
dirs = @ui.directories.items
|
|
173
|
-
Ruber[:
|
|
173
|
+
Ruber[:app].plugin_dirs = dirs
|
|
174
174
|
plugins = []
|
|
175
175
|
@ui.plugins.model.each_row do |r|
|
|
176
176
|
plugins << r[0].data.to_string if r[0].fully_checked?
|
|
@@ -188,7 +188,7 @@ dependency problem occurs, the user is warned and all plugins are deselected
|
|
|
188
188
|
dirs = Ruber[:config].default :general, :plugin_dirs
|
|
189
189
|
@chosen_plugins = Ruber[:config].default( :general, :plugins).split(',').map{|i| i.to_sym}
|
|
190
190
|
read_plugins dirs
|
|
191
|
-
res = find_deps(:
|
|
191
|
+
res = find_deps(:sorry) do |e|
|
|
192
192
|
"There were problems making dependencies. #{create_failure_message e}\nAll plugins will be deselected"
|
|
193
193
|
end
|
|
194
194
|
if res
|
|
@@ -248,11 +248,11 @@ an error occurs and no block is passed, the exception is passed on.
|
|
|
248
248
|
This method returns *nil* if no error occurs and the value returned by the
|
|
249
249
|
message box otherwise.
|
|
250
250
|
=end
|
|
251
|
-
def find_deps msg_type = :
|
|
251
|
+
def find_deps msg_type = :sorry
|
|
252
252
|
chosen_data = @chosen_plugins.map{|i| @plugin_data[i]}
|
|
253
253
|
begin @needed_plugins = ComponentManager.fill_dependencies chosen_data, @plugin_data.values
|
|
254
254
|
rescue ComponentManager::UnresolvedDep, ComponentManager::CircularDep => e
|
|
255
|
-
if block_given? then KDE::MessageBox.send
|
|
255
|
+
if block_given? then KDE::MessageBox.send msg_type, nil, yield(e)
|
|
256
256
|
else raise
|
|
257
257
|
end
|
|
258
258
|
end
|
|
@@ -308,7 +308,7 @@ to the item _it_.
|
|
|
308
308
|
if it.check_state == Qt::Checked then @chosen_plugins << name.to_sym
|
|
309
309
|
else @chosen_plugins.delete name.to_sym
|
|
310
310
|
end
|
|
311
|
-
find_deps{|e| create_failure_message( e) + "\nPlease, be sure to correct the problem before
|
|
311
|
+
find_deps{|e| create_failure_message( e) + "\nPlease, be sure to correct the problem before pressing the OK or Apply button" }
|
|
312
312
|
update_plugin_status
|
|
313
313
|
end
|
|
314
314
|
|
|
@@ -320,7 +320,7 @@ If the dependencies aren't respected, the user is warned with a message box.
|
|
|
320
320
|
=end
|
|
321
321
|
def slot_directories_changed
|
|
322
322
|
new_dirs = @ui.directories.items
|
|
323
|
-
find_deps{|e| create_failure_message( e) + "\nPlease, be sure to correct the problem before
|
|
323
|
+
find_deps{|e| create_failure_message( e) + "\nPlease, be sure to correct the problem before pressing the OK or Apply button" }
|
|
324
324
|
fill_plugin_list
|
|
325
325
|
emit directories_changed
|
|
326
326
|
nil
|
|
@@ -337,7 +337,7 @@ happen.
|
|
|
337
337
|
case e
|
|
338
338
|
when ComponentManager::UnresolvedDep
|
|
339
339
|
deps = e.missing.map do |p1, p2|
|
|
340
|
-
"#{
|
|
340
|
+
"#{p1}, needed by #{p2.join ', '}"
|
|
341
341
|
end
|
|
342
342
|
"Some dependencies couldn't be satisifed:\n#{deps.join "\n"}"
|
|
343
343
|
when ComponentManager::CircularDep
|
|
@@ -350,4 +350,4 @@ happen.
|
|
|
350
350
|
|
|
351
351
|
end
|
|
352
352
|
|
|
353
|
-
end
|
|
353
|
+
end
|
|
@@ -0,0 +1,263 @@
|
|
|
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/pane'
|
|
22
|
+
require 'ruber/editor/editor_view'
|
|
23
|
+
|
|
24
|
+
module Ruber
|
|
25
|
+
|
|
26
|
+
class MainWindow
|
|
27
|
+
|
|
28
|
+
=begin rdoc
|
|
29
|
+
Helper class used by {MainWindow#editor_for} and friends to find out which editor
|
|
30
|
+
should use or where the new editor should be placed according to the hints given
|
|
31
|
+
them
|
|
32
|
+
=end
|
|
33
|
+
class HintSolver
|
|
34
|
+
|
|
35
|
+
=begin rdoc
|
|
36
|
+
@param [KDE::TabWidget] tabs the tab widget which contains the tabs
|
|
37
|
+
@param [KParts::PartManager] manager the part manager used by the main window
|
|
38
|
+
@param [Array<EditorView>] view_order an array specifying the order in which views
|
|
39
|
+
were activated. Most recently activated views corresponds to lower indexes. It's
|
|
40
|
+
assumed that the main window will keep a reference to this array and update it
|
|
41
|
+
whenever a view becomes activated
|
|
42
|
+
=end
|
|
43
|
+
def initialize tabs, manager, view_order
|
|
44
|
+
@tabs = tabs
|
|
45
|
+
@part_manager = manager
|
|
46
|
+
@view_order = view_order
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
=begin rdoc
|
|
50
|
+
Finds the editor to use for a document according to the given hints
|
|
51
|
+
|
|
52
|
+
If no editor associated with the document and respecting the given hints exists,
|
|
53
|
+
*nil* is returned. This always happens if the @:existing@ hint is @:never@.
|
|
54
|
+
@param [Document] doc the document to retrieve the editor for
|
|
55
|
+
@param hints (see MainWindow#editor_for). See {#MainWindow#editor_for} for the
|
|
56
|
+
values it can contain. Only the @:existing@ and @:strategy@ entries are used
|
|
57
|
+
@return [EditorView,nil] a view associated with the document which respects the
|
|
58
|
+
hints or *nil* if such a view doesn't exist
|
|
59
|
+
=end
|
|
60
|
+
def find_editor doc, hints
|
|
61
|
+
views = []
|
|
62
|
+
case hints[:existing]
|
|
63
|
+
when :never then return nil
|
|
64
|
+
when :current_tab
|
|
65
|
+
views = @tabs.current_widget.select{|v| v.document == doc}
|
|
66
|
+
else @tabs.each{|t| views += t.select{|v| v.document == doc} }
|
|
67
|
+
end
|
|
68
|
+
choose_editor doc, views, hints
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
=begin rdoc
|
|
72
|
+
Finds out where a new editor should respect the given hints
|
|
73
|
+
|
|
74
|
+
The return value tells where the new editor should be placed. If the return value
|
|
75
|
+
is *nil* then the editor should be placed in a new tab; if it's an {EditorView} then
|
|
76
|
+
the editor should be placed in the pane containing the view, splitting it at the
|
|
77
|
+
view
|
|
78
|
+
@param hints (see MainWindow#editor_for). See {#MainWindow#editor_for} for the
|
|
79
|
+
values it can contain. Only the @:newand entry is used
|
|
80
|
+
@return [EditorView, nil] an {EditorView} if the new editor should be placed inside
|
|
81
|
+
an existing pane. The returned view is the view where it's parent should be split
|
|
82
|
+
at. If *nil* is returned, the editor should be put in a new tab
|
|
83
|
+
=end
|
|
84
|
+
def place_editor hints
|
|
85
|
+
case hints[:new]
|
|
86
|
+
when :new_tab then nil
|
|
87
|
+
when :current then @view_order[0]
|
|
88
|
+
when :current_tab
|
|
89
|
+
current = @tabs.current_widget
|
|
90
|
+
current.each_view.to_a[0] if current
|
|
91
|
+
when Integer
|
|
92
|
+
pane = @tabs.widget hints[:new]
|
|
93
|
+
pane.each_view.to_a[0] if pane
|
|
94
|
+
when EditorView then
|
|
95
|
+
view = hints[:new]
|
|
96
|
+
view.parent.is_a?(Pane) ? view : nil
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
=begin rdoc
|
|
103
|
+
Chooses an editor according to the value of the :strategy hint
|
|
104
|
+
|
|
105
|
+
For each strategy specified in the hint, this method calls a method called @choose_editor_*_strategy@,
|
|
106
|
+
where the @*@ stands for the strategy name.
|
|
107
|
+
|
|
108
|
+
If there's no editor satsifying the given strategy, then the @:next@ strategy, which
|
|
109
|
+
always gives a result, is used.
|
|
110
|
+
@param [Document] doc the document associated with the editor
|
|
111
|
+
@param [Array<EditorView>] editors a list of the editors to choose among. They should
|
|
112
|
+
be ordered by tab and, within the same tab, from left to right and top to bottom
|
|
113
|
+
@param hints (see MainWindow#editor_for)
|
|
114
|
+
@return [EditorView,nil] an editor view according to the @:strategy@ hint or *nil*
|
|
115
|
+
if _editors_ is empty
|
|
116
|
+
=end
|
|
117
|
+
def choose_editor doc, editors, hints
|
|
118
|
+
case editors.size
|
|
119
|
+
when 0 then nil
|
|
120
|
+
when 1 then editors.first
|
|
121
|
+
else
|
|
122
|
+
editor = nil
|
|
123
|
+
(Array(hints[:strategy]) + [:next]).each do |s|
|
|
124
|
+
editor = send "choose_editor_#{s}_strategy", doc, editors, hints
|
|
125
|
+
break if editor
|
|
126
|
+
end
|
|
127
|
+
editor
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
=begin rdoc
|
|
133
|
+
A list of all the editors associated with the given document in a pane
|
|
134
|
+
@param [Document] doc the document
|
|
135
|
+
@param [Pane] tab the pane where to look for editors
|
|
136
|
+
@return [Array<EditorView>] the editor views associated with _doc_ in the pane
|
|
137
|
+
_pane_, in order
|
|
138
|
+
=end
|
|
139
|
+
def editors_in_tab doc, tab
|
|
140
|
+
tab.select{|v| v.document == doc}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
=begin rdoc
|
|
144
|
+
Chooses an editor according to the @current@ strategy
|
|
145
|
+
|
|
146
|
+
@param (see #choose_editor)
|
|
147
|
+
@return [EditorView,nil] the current editor if it is associated with _doc_
|
|
148
|
+
and *nil* otherwise
|
|
149
|
+
=end
|
|
150
|
+
def choose_editor_current_strategy doc, editors, hints
|
|
151
|
+
current = @view_order[0]
|
|
152
|
+
if current and current.document == doc then current
|
|
153
|
+
else nil
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
=begin rdoc
|
|
158
|
+
Chooses an editor according to the @current_tab@ strategy
|
|
159
|
+
|
|
160
|
+
@param (see #choose_editor)
|
|
161
|
+
@return [EditorView,nil] the first editor in the current tab associated with _doc_
|
|
162
|
+
or *nil* if the current tab doesn't contain any editor associated with _doc_
|
|
163
|
+
=end
|
|
164
|
+
def choose_editor_current_tab_strategy doc, editors, hints
|
|
165
|
+
tab = @tabs.current_widget
|
|
166
|
+
views = tab.find_children(EditorView).select do |c|
|
|
167
|
+
c.is_a?(EditorView) and c.document == doc
|
|
168
|
+
end
|
|
169
|
+
(views & editors).first
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
=begin rdoc
|
|
173
|
+
Chooses an editor according to the @last_current_tab@ strategy
|
|
174
|
+
|
|
175
|
+
@param (see #choose_editor)
|
|
176
|
+
@return [EditorView,nil] the last editor in the current tab associated with _doc_
|
|
177
|
+
or *nil* if the current tab doesn't contain any editor associated with _doc_
|
|
178
|
+
=end
|
|
179
|
+
|
|
180
|
+
def choose_editor_last_current_tab_strategy doc, editors, hints
|
|
181
|
+
tab = @tabs.current_widget
|
|
182
|
+
views = tab.find_children(EditorView).select do |c|
|
|
183
|
+
c.is_a?(EditorView) and c.document == doc
|
|
184
|
+
end
|
|
185
|
+
(views & editors).last
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
=begin rdoc
|
|
189
|
+
Chooses an editor according to the @next@ strategy
|
|
190
|
+
|
|
191
|
+
@param (see #choose_editor)
|
|
192
|
+
@return [EditorView] the first editor associated with _doc_ starting from
|
|
193
|
+
the current tab
|
|
194
|
+
=end
|
|
195
|
+
|
|
196
|
+
def choose_editor_next_strategy doc, editors, hints
|
|
197
|
+
current_index = @tabs.current_index
|
|
198
|
+
editor = editors.find do |e|
|
|
199
|
+
pane = e.parent
|
|
200
|
+
pane = pane.parent_pane while pane.parent_pane
|
|
201
|
+
@tabs.index_of(pane) >= current_index
|
|
202
|
+
end
|
|
203
|
+
editor || editors.first
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
=begin rdoc
|
|
207
|
+
Chooses an editor according to the @previous@ strategy
|
|
208
|
+
|
|
209
|
+
@param (see #choose_editor)
|
|
210
|
+
@return [EditorView] the first editor associated with _doc_ starting from
|
|
211
|
+
the current tab and going backwards
|
|
212
|
+
=end
|
|
213
|
+
def choose_editor_previous_strategy doc, editors, hints
|
|
214
|
+
current_index = @tabs.current_index
|
|
215
|
+
editor = editors.reverse_each.find do |e|
|
|
216
|
+
pane = e.parent
|
|
217
|
+
pane = pane.parent_pane while pane.parent_pane
|
|
218
|
+
@tabs.index_of(pane) < current_index
|
|
219
|
+
end
|
|
220
|
+
editor || editors.last
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
=begin rdoc
|
|
224
|
+
Chooses an editor according to the @first@ strategy
|
|
225
|
+
|
|
226
|
+
@param (see #choose_editor)
|
|
227
|
+
@return [EditorView] the first editor associated with _doc_ starting from
|
|
228
|
+
the first tab
|
|
229
|
+
=end
|
|
230
|
+
|
|
231
|
+
def choose_editor_first_strategy doc, editors, hints
|
|
232
|
+
editors.first
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
=begin rdoc
|
|
236
|
+
Chooses an editor according to the @last@ strategy
|
|
237
|
+
|
|
238
|
+
@param (see #choose_editor)
|
|
239
|
+
@return [EditorView] the last editor associated with _doc_ starting from
|
|
240
|
+
the first tab
|
|
241
|
+
=end
|
|
242
|
+
def choose_editor_last_strategy doc, editors, hints
|
|
243
|
+
editors.last
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
=begin rdoc
|
|
247
|
+
Chooses an editor according to the @last_used@ strategy
|
|
248
|
+
|
|
249
|
+
@param (see #choose_editor)
|
|
250
|
+
@return [EditorView] the last activated editor associated with _doc_
|
|
251
|
+
=end
|
|
252
|
+
def choose_editor_last_used_strategy doc, editors, hints
|
|
253
|
+
# The || part is there in case there\'s a view in editors which doesn't
|
|
254
|
+
# appears in the view order. In this case, it's given a fake index which
|
|
255
|
+
# is greater than all the other possible
|
|
256
|
+
editors.sort_by{|e| @view_order.index(e) || (@view_order.count + 1)}.first
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
end
|
|
@@ -29,9 +29,12 @@ require 'ruber/gui_states_handler'
|
|
|
29
29
|
|
|
30
30
|
require 'ruber/main_window/main_window_internal'
|
|
31
31
|
require 'ruber/main_window/main_window_actions'
|
|
32
|
+
require 'ruber/main_window/hint_solver'
|
|
33
|
+
require 'ruber/main_window/view_manager'
|
|
32
34
|
|
|
33
35
|
require 'ruber/main_window/status_bar'
|
|
34
36
|
require 'ruber/main_window/workspace'
|
|
37
|
+
require_relative 'ui/workspace_settings_widget'
|
|
35
38
|
|
|
36
39
|
module Ruber
|
|
37
40
|
|
|
@@ -47,22 +50,24 @@ tool widgets.
|
|
|
47
50
|
|
|
48
51
|
include GuiStatesHandler
|
|
49
52
|
|
|
50
|
-
extend Forwardable
|
|
51
|
-
##
|
|
52
|
-
# :method: current_document_changed(QObject* obj) [SIGNAL]
|
|
53
|
-
# Signal emitted whenever the current document changes. _obj_ is the new current
|
|
54
|
-
# document and will be <tt>Qt::NilObject</tt> if there's no current document.
|
|
55
|
-
|
|
56
|
-
##
|
|
57
|
-
# :method: active_editor_changed(QObject* obj) [SIGNAL]
|
|
58
|
-
# Signal emitted whenever the active editor changes _obj_ is the new active editor
|
|
59
|
-
# and will be <tt>Qt::NilObject</tt> if there's no active editor
|
|
60
|
-
|
|
53
|
+
extend Forwardable
|
|
61
54
|
|
|
62
55
|
signals 'current_document_changed(QObject*)', 'active_editor_changed(QObject*)'
|
|
63
56
|
|
|
64
57
|
slots :load_settings
|
|
65
58
|
|
|
59
|
+
=begin rdoc
|
|
60
|
+
The default hints used by methods like {#editor_for} and {#editor_for!}
|
|
61
|
+
=end
|
|
62
|
+
DEFAULT_HINTS = {
|
|
63
|
+
:exisiting => :always,
|
|
64
|
+
:strategy => [:current, :current_tab, :first],
|
|
65
|
+
:new => :new_tab,
|
|
66
|
+
:split => :horizontal,
|
|
67
|
+
:show => true,
|
|
68
|
+
:create_if_needed => true
|
|
69
|
+
}.freeze
|
|
70
|
+
|
|
66
71
|
=begin rdoc
|
|
67
72
|
The widget which contains the tool widgets.
|
|
68
73
|
|
|
@@ -70,6 +75,8 @@ The primary use of this method is to connect to the signals emitted from the wor
|
|
|
70
75
|
when a tool widget is raised, shown or hidden. All of its other methods are also
|
|
71
76
|
provided by the MainWindow, so you shouldn't need to access the workspace to use
|
|
72
77
|
them.
|
|
78
|
+
|
|
79
|
+
@return [Workspace] the workspace associated with the main window
|
|
73
80
|
=end
|
|
74
81
|
attr_reader :workspace
|
|
75
82
|
|
|
@@ -94,48 +101,87 @@ is the plugin description for this object.
|
|
|
94
101
|
self.central_widget = Workspace.new self
|
|
95
102
|
# We need the instance variable to use it with Forwardable
|
|
96
103
|
@workspace = central_widget
|
|
97
|
-
@
|
|
98
|
-
@
|
|
99
|
-
@
|
|
104
|
+
@tabs = central_widget.instance_variable_get :@views
|
|
105
|
+
@tabs.tabs_closable = Ruber[:config][:workspace, :close_buttons]
|
|
106
|
+
@view_manager = ViewManager.new @tabs, self
|
|
100
107
|
@auto_activate_editors = true
|
|
101
|
-
@editors_mapper = Qt::SignalMapper.new self
|
|
102
108
|
@ui_states = {}
|
|
103
109
|
@actions_state_handlers = Hash.new{|h, k| h[k] = []}
|
|
104
110
|
@about_plugin_actions = []
|
|
111
|
+
@switch_to_actions = []
|
|
105
112
|
@last_session_data = nil
|
|
106
113
|
self.status_bar = StatusBar.new self
|
|
107
114
|
self.connect(SIGNAL('current_document_changed(QObject*)')) do |doc|
|
|
108
|
-
change_state 'current_document', !doc.
|
|
115
|
+
change_state 'current_document', !doc.nil?
|
|
109
116
|
end
|
|
110
117
|
connect Ruber[:components], SIGNAL('component_loaded(QObject*)'), self, SLOT('add_about_plugin_action(QObject*)')
|
|
111
118
|
connect Ruber[:components], SIGNAL('unloading_component(QObject*)'), self, SLOT('remove_about_plugin_action(QObject*)')
|
|
112
119
|
connect Ruber[:components], SIGNAL('unloading_component(QObject*)'), self, SLOT('remove_plugin_ui_actions(QObject*)')
|
|
113
|
-
connect @
|
|
120
|
+
connect @view_manager, SIGNAL('active_editor_changed(QWidget*)'), self, SLOT('slot_active_editor_changed(QWidget*)')
|
|
121
|
+
connect Ruber[:documents], SIGNAL('document_created(QObject*)'), self, SLOT('document_created(QObject*)')
|
|
122
|
+
connect Ruber[:documents], SIGNAL('closing_document(QObject*)'), self, SLOT(:update_switch_to_list)
|
|
123
|
+
|
|
114
124
|
setup_actions action_collection
|
|
115
|
-
@views.connect(SIGNAL('currentChanged(int)')) do |idx|
|
|
116
|
-
if @auto_activate_editors then activate_editor idx
|
|
117
|
-
else activate_editor nil
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
125
|
Ruber[:projects].connect( SIGNAL('current_project_changed(QObject*)') ) do |prj|
|
|
121
126
|
change_state "active_project_exists", !prj.nil?
|
|
122
|
-
|
|
127
|
+
change_title
|
|
123
128
|
end
|
|
129
|
+
connect @tabs, SIGNAL('tabCloseRequested(int)'), self, SLOT('close_tab(int)')
|
|
124
130
|
connect Ruber[:projects], SIGNAL('closing_project(QObject*)'), self, SLOT('close_project_files(QObject*)')
|
|
125
|
-
|
|
126
131
|
setup_GUI
|
|
127
132
|
create_shell_GUI true
|
|
128
133
|
|
|
129
134
|
config_obj = Ruber[:config].kconfig
|
|
130
135
|
apply_main_window_settings KDE::ConfigGroup.new( config_obj, 'MainWindow')
|
|
131
136
|
recent_files = KDE::ConfigGroup.new config_obj, 'Recent files'
|
|
132
|
-
action_collection.action("file_open_recent")
|
|
137
|
+
open_recent_action =action_collection.action("file_open_recent")
|
|
138
|
+
open_recent_action.load_entries recent_files
|
|
139
|
+
switch_to_recent_action = action_collection.action("window-switch_to_recent_file")
|
|
140
|
+
switch_to_recent_action.load_entries recent_files
|
|
141
|
+
|
|
142
|
+
# Synchronize the two menus, so that when the user clears one of them the also
|
|
143
|
+
# is also cleared
|
|
144
|
+
connect open_recent_action, SIGNAL(:recentListCleared), switch_to_recent_action, SLOT(:clear)
|
|
145
|
+
connect switch_to_recent_action, SIGNAL(:recentListCleared), open_recent_action, SLOT(:clear)
|
|
146
|
+
|
|
133
147
|
recent_projects = KDE::ConfigGroup.new config_obj, 'Recent projects'
|
|
134
148
|
action_collection.action("project-open_recent").load_entries recent_projects
|
|
135
149
|
status_bar.show
|
|
136
150
|
setup_initial_states
|
|
137
151
|
end
|
|
138
152
|
|
|
153
|
+
=begin rdoc
|
|
154
|
+
The open tabs
|
|
155
|
+
|
|
156
|
+
@return [Array<Pane>] a list of the top-level pane for each tab
|
|
157
|
+
=end
|
|
158
|
+
def tabs
|
|
159
|
+
@tabs.to_a
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
=begin rdoc
|
|
163
|
+
The views contained in the main window
|
|
164
|
+
|
|
165
|
+
If a document is given as argument, returns all views associated with the document;
|
|
166
|
+
if no document is given, all views are returned.
|
|
167
|
+
|
|
168
|
+
The order of the views in the returned list is the activation order: the view
|
|
169
|
+
which was activated more recently is at position 0 in the array, the one activated
|
|
170
|
+
before that is at position 1 and so on. Views which have never been activated are
|
|
171
|
+
at the end of the array, in an arbitrary order
|
|
172
|
+
|
|
173
|
+
@param [Document,nil] doc the document to return the views for. If *nil*, all the
|
|
174
|
+
views will be returned
|
|
175
|
+
@return [Array<EditorView>] the views associated with the given document, if any,
|
|
176
|
+
or all the views, in activation order, from most recently activated to less recently
|
|
177
|
+
activated
|
|
178
|
+
=end
|
|
179
|
+
def views doc = nil
|
|
180
|
+
if doc then @view_manager.activation_order.select{|v| v.document == doc}
|
|
181
|
+
else @view_manager.activation_order.dup
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
139
185
|
##
|
|
140
186
|
# :method: add_tool
|
|
141
187
|
# Adds a tool widget to the main window. See Workspace#add_tool_widget for more information.
|
|
@@ -179,186 +225,353 @@ is the plugin description for this object.
|
|
|
179
225
|
def_delegators :@workspace, :tool_widgets
|
|
180
226
|
|
|
181
227
|
=begin rdoc
|
|
182
|
-
Returns
|
|
228
|
+
Returns an editor associated with the given document, creating it if needed
|
|
229
|
+
|
|
230
|
+
If _doc_ is not a document and no document for the file representing by the string
|
|
231
|
+
or URL _doc_ is open, it will be created.
|
|
232
|
+
|
|
233
|
+
Since there can be more than one editor associated with the document, the optional
|
|
234
|
+
_hints_ argument can be used to specify which one should be returned. If no editor
|
|
235
|
+
exists for the document, or none of the exisiting ones statisfy the @existing@
|
|
236
|
+
hint, a new one will be created, unless the @create_if_needed@ hint is *false*.
|
|
183
237
|
|
|
184
238
|
@param [Document, String, KDE::Url] doc the document. If it is a string representing
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
@
|
|
188
|
-
|
|
189
|
-
@
|
|
190
|
-
|
|
239
|
+
a relative path, it'll be considered relative to the current directory. If the
|
|
240
|
+
string is an Url, it'll be interpreted as such
|
|
241
|
+
@param [Hash] hints options to tailor the algorithm used to retrieve or create the editor
|
|
242
|
+
|
|
243
|
+
@option hints [Symbol] :existing (:always) when it is acceptable to return an already existing
|
|
244
|
+
editor. It can be:
|
|
245
|
+
* @:always@: if there's an existing editor, always use it
|
|
246
|
+
* @:never@: never use an exisiting editor
|
|
247
|
+
* @:current_tab@: use an exisiting editor only if it is in the current pane
|
|
248
|
+
@option hints [Boolean] :create_if_needed (true) whether or not a new editor for
|
|
249
|
+
the given document should be created if none exists. This is different from
|
|
250
|
+
passing @:always@ as the value of the @:existing@ option because the latter would
|
|
251
|
+
cause a new editor to be created in case no one already exists for the document
|
|
252
|
+
@option hints [Symbol, Array<Symbol>] :stategy ([:current, :current_tab, :first])
|
|
253
|
+
how to choose the exisiting editor to use in case there's more than one. If it
|
|
254
|
+
is an array, the strategies will be applied in turn until one has success.
|
|
255
|
+
If none of the given strategies succeed, @:first@ will be used. The possible
|
|
256
|
+
values are
|
|
257
|
+
* @:current@: if the current editor is associated with _doc_, use it
|
|
258
|
+
* @:current_tab@: if there are editors associated with _doc_ in the current
|
|
259
|
+
tab, the first of them will be used
|
|
260
|
+
* @:last_current_tab@: if there are editors associated with _doc_ in the current
|
|
261
|
+
tab, the first of them will be used
|
|
262
|
+
* @:next@: starting from the current tab, the first editor found associated
|
|
263
|
+
with _doc_ will be used
|
|
264
|
+
* @:previous@: the first editor associated with _doc_ found by looking in all
|
|
265
|
+
tabs from the current one in reverse order will be used (the current tab will
|
|
266
|
+
be the last one where the editor will be looked for)
|
|
267
|
+
* @:first@: the first editor associated with the document will be used
|
|
268
|
+
* @:last@: the last editor associated the document will be used
|
|
269
|
+
|
|
270
|
+
This option is only useful when using an existing editor
|
|
271
|
+
@option hints [Symbol,Integer,EditorView] :new (:new_tab) where to place the new editor, if
|
|
272
|
+
it needs to be created. It can have one of the following values:
|
|
273
|
+
* @:new_tab@: put the new editor as the single editor in a new tab
|
|
274
|
+
* @:current@: place the new editor in the pane obtained splitting the
|
|
275
|
+
current editor
|
|
276
|
+
* @:current_tab@: place the new editor in the pane obtained splitting the first
|
|
277
|
+
editor in the current tab
|
|
278
|
+
* an integer: place the new editor in the tab associated with that index (0-based),
|
|
279
|
+
in the pane obtained by splitting the first view
|
|
280
|
+
* an {EditorView}: place the new editor in the pane obtained splitting the
|
|
281
|
+
pane associated with the given editor
|
|
282
|
+
|
|
283
|
+
This option is only used when when creating a new editor
|
|
284
|
+
@option hints [Symbol] :split (:horizontal) the orientation in which an existing
|
|
285
|
+
editor should be split to accomodate the new editor. It can be @:horizontal@
|
|
286
|
+
or @:vertical@. This option is only used when when creating a new editor
|
|
287
|
+
@option hints [Boolean] :show (true) whether the new editor should be shown or
|
|
288
|
+
not. If it's *false*, the editor won't be placed in any pane. This option is
|
|
289
|
+
only used when when creating a new editor
|
|
290
|
+
@return [EditorView,nil] an editor associated with the document and *nil* if no
|
|
291
|
+
editor associated with the given document exists and the @:create_if_needed@ hint is
|
|
292
|
+
set to *false*.
|
|
293
|
+
@raise [ArgumentError] if _doc_ is a path or a @KDE::Url@ but the corresponding
|
|
294
|
+
file doesn't exist
|
|
191
295
|
=end
|
|
192
|
-
def editor_for! doc
|
|
296
|
+
def editor_for! doc, hints = DEFAULT_HINTS
|
|
297
|
+
hints = DEFAULT_HINTS.merge hints
|
|
298
|
+
docs = Ruber[:documents].documents
|
|
193
299
|
unless doc.is_a? Document
|
|
194
|
-
|
|
195
|
-
|
|
300
|
+
unless hints.has_key? :close_starting_document
|
|
301
|
+
hints[:close_starting_document] = docs.size == 1 &&
|
|
302
|
+
docs[0].extension(:ruber_default_document).default_document &&
|
|
303
|
+
docs[0].pristine?
|
|
196
304
|
end
|
|
305
|
+
url = doc
|
|
306
|
+
if url.is_a? String
|
|
307
|
+
url = KDE::Url.new url
|
|
308
|
+
if url.relative?
|
|
309
|
+
path = File.expand_path url.path
|
|
310
|
+
url.path = path
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
doc = Ruber[:documents].document url
|
|
197
314
|
end
|
|
198
315
|
return unless doc
|
|
199
|
-
|
|
316
|
+
@view_manager.without_activating{@view_manager.editor_for doc, hints}
|
|
200
317
|
end
|
|
201
318
|
|
|
202
319
|
=begin rdoc
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
doesn't
|
|
320
|
+
Returns an editor associated with the given document
|
|
321
|
+
|
|
322
|
+
It works mostly like {#editor_for!}, but it doesn't attempt to create the document
|
|
323
|
+
if it doesn't exist an always sets the @create_if_needed@ hint to *false*. See
|
|
324
|
+
{#editor_for!} for the values it can contain.
|
|
325
|
+
|
|
326
|
+
@param (see #editor_for!)
|
|
327
|
+
@return [EditorView,nil] an editor associated with the document and *nil* if no
|
|
328
|
+
editor associated with the given document exists or no document corresponds to _doc_
|
|
329
|
+
@raise [ArgumentError] if _doc_ is a path or a @KDE::Url@ but the corresponding
|
|
330
|
+
file doesn't exist
|
|
206
331
|
=end
|
|
207
|
-
def editor_for doc
|
|
208
|
-
|
|
332
|
+
def editor_for doc, hints = DEFAULT_HINTS
|
|
333
|
+
hints = DEFAULT_HINTS.merge hints
|
|
334
|
+
hints[:create_if_needed] = false
|
|
335
|
+
unless doc.is_a? Document
|
|
336
|
+
url = doc
|
|
337
|
+
if url.is_a? String
|
|
338
|
+
url = KDE::Url.new url
|
|
339
|
+
if url.relative?
|
|
340
|
+
path = File.expand_path url.path
|
|
341
|
+
url.path = path
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
doc = Ruber[:documents].document_for_url url
|
|
345
|
+
end
|
|
209
346
|
return unless doc
|
|
210
|
-
doc
|
|
347
|
+
@view_manager.editor_for doc, hints
|
|
211
348
|
end
|
|
212
349
|
|
|
213
350
|
=begin rdoc
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
351
|
+
The active editor
|
|
352
|
+
|
|
353
|
+
The active editor is the editor which has its GUI merged with the main window's.
|
|
354
|
+
This means it is the editor which last received focus and the one which would receive
|
|
355
|
+
focus when the tab widget does. If the focus already is in the tab widget, then
|
|
356
|
+
the active editor is the one whose @is_active_window@ method returns *true*.
|
|
219
357
|
|
|
220
|
-
|
|
221
|
-
<tt>active_editor</tt>.
|
|
358
|
+
@return [EditorView,nil] the active editor or *nil* if there's no active editor.
|
|
222
359
|
=end
|
|
223
360
|
def active_editor
|
|
224
|
-
@active_editor
|
|
361
|
+
@view_manager.active_editor
|
|
225
362
|
end
|
|
363
|
+
alias_method :current_editor, :active_editor
|
|
364
|
+
|
|
365
|
+
=begin rdoc
|
|
366
|
+
Activates an editor
|
|
367
|
+
|
|
368
|
+
If the editor is not in the current tab, the tab it belongs to becomes active.
|
|
369
|
+
|
|
370
|
+
Activating an editor means merging its GUI with the main window's GUI, changing
|
|
371
|
+
the title of the main window accordingly and telling the status bar to refer to
|
|
372
|
+
that view.
|
|
373
|
+
|
|
374
|
+
After activating the editor, the {#current_document_changed} and {#active_editor_changed}
|
|
375
|
+
signals are emitted.
|
|
376
|
+
@param [EditorView,nil] editor the editor to activate. If *nil*, then the currently active
|
|
377
|
+
editor will be deactivated
|
|
378
|
+
=end
|
|
379
|
+
def activate_editor editor
|
|
380
|
+
tab = @view_manager.tab editor
|
|
381
|
+
return unless tab
|
|
382
|
+
@tabs.current_widget = tab
|
|
383
|
+
return if active_editor == editor
|
|
384
|
+
@view_manager.make_editor_active editor
|
|
385
|
+
editor
|
|
386
|
+
end
|
|
226
387
|
|
|
227
388
|
=begin rdoc
|
|
228
|
-
|
|
229
|
-
|
|
389
|
+
Replaces an editor with another
|
|
390
|
+
|
|
391
|
+
If the editor to be replaced is the only one associated witha given document and
|
|
392
|
+
the document is modified, the user is asked whether he wants to save it or not. If
|
|
393
|
+
he chooses to abort, the editor is not replaced, otherwise the document is closed.
|
|
394
|
+
|
|
395
|
+
The new editor is put in the same pane which contained the old one (without splitting
|
|
396
|
+
it).
|
|
397
|
+
|
|
398
|
+
@overload replace_editor old, new_ed
|
|
399
|
+
@param [EditorView] old the editor to replace
|
|
400
|
+
@param [EditorView] new_ed the editor to replace _old_ with
|
|
401
|
+
@return [EditorView,nil]
|
|
402
|
+
@overload replace_editor old, doc
|
|
403
|
+
@note whether or not the user needs to be asked about saving the document is determined
|
|
404
|
+
regardless of the value of _doc_. This means that the user may be asked
|
|
405
|
+
to save the document even if _doc_ is the same document associated with
|
|
406
|
+
the view (or the URL corresponding to it). To avoid this, create the replacement
|
|
407
|
+
editor before calling this method, using @editor_for doc, :existing => :never, :show => false@,
|
|
408
|
+
then use the overloaded version of this method which takes an editor
|
|
409
|
+
@param [EditorView] old the editor to replace
|
|
410
|
+
@param [Document,String,KDE::Url] doc the document to create the editor for or
|
|
411
|
+
the name of the corresponding file (absolute or relative to the current directory)
|
|
412
|
+
or the corresponding URL
|
|
413
|
+
@return [EditorView,nil]
|
|
414
|
+
@return [EditorView,nil] the new editor or *nil* if the user choose to abort
|
|
230
415
|
=end
|
|
231
|
-
def
|
|
232
|
-
|
|
416
|
+
def replace_editor old, editor_or_doc
|
|
417
|
+
if old.document.views.size == 1
|
|
418
|
+
return unless old.document.query_close
|
|
419
|
+
close_doc = true
|
|
420
|
+
end
|
|
421
|
+
if editor_or_doc.is_a?(EditorView) then ed = editor_or_doc
|
|
422
|
+
else ed = editor_for! editor_or_doc, :existing => :never, :show => false
|
|
423
|
+
end
|
|
424
|
+
old.parent.replace_view old, ed
|
|
425
|
+
close_editor old, false
|
|
426
|
+
ed
|
|
233
427
|
end
|
|
234
428
|
|
|
235
429
|
=begin rdoc
|
|
236
|
-
|
|
237
|
-
(for internal uses only) or the editor to activate, or *nil*. If the editor to
|
|
238
|
-
activate is not in the current tab, the tab which contains it will become the current
|
|
239
|
-
one. Activating an editor deactivates the previously active one.
|
|
430
|
+
The toplevel pane corresponding to the given index or editor
|
|
240
431
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
432
|
+
@overload tab idx
|
|
433
|
+
@param [Integer] idx the index of the tab
|
|
434
|
+
@return [Pane] the toplevel pane in the tab number _idx_
|
|
435
|
+
@overload tab editor
|
|
436
|
+
@param [EditorView] editor the editor to retrieve the pane for
|
|
437
|
+
@return [Pane] the toplevel pane containing the given editor
|
|
245
438
|
=end
|
|
246
|
-
def
|
|
247
|
-
|
|
248
|
-
@views.current_widget = view if view and @views.current_widget != view
|
|
249
|
-
deactivate_editor @active_editor if @active_editor != view
|
|
250
|
-
@active_editor = view
|
|
251
|
-
if @active_editor
|
|
252
|
-
gui_factory.add_client @active_editor.doc.view.send(:internal)
|
|
253
|
-
@active_editor.document.activate
|
|
254
|
-
end
|
|
255
|
-
make_title
|
|
256
|
-
status_bar.view = @active_editor
|
|
257
|
-
emit current_document_changed @active_editor ? @active_editor.document : Qt::NilObject
|
|
258
|
-
emit active_editor_changed @active_editor || Qt::NilObject
|
|
439
|
+
def tab arg
|
|
440
|
+
@view_manager.tab arg
|
|
259
441
|
end
|
|
260
442
|
|
|
261
443
|
=begin rdoc
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
444
|
+
The document associated with the active editor
|
|
445
|
+
|
|
446
|
+
This is a convenience method for @active_editor.document@ which takes care of the
|
|
447
|
+
case when there's no active editor.
|
|
448
|
+
|
|
449
|
+
@return [Document,nil] the document associated with the active editor or *nil*
|
|
450
|
+
if there's no active editor
|
|
267
451
|
=end
|
|
268
452
|
def current_document
|
|
269
|
-
|
|
270
|
-
view ? view.doc : nil
|
|
453
|
+
(ed = active_editor) ? ed.document : nil
|
|
271
454
|
end
|
|
455
|
+
alias_method :active_document, :current_document
|
|
272
456
|
|
|
273
457
|
=begin rdoc
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
458
|
+
Displays an editor for the given document
|
|
459
|
+
|
|
460
|
+
This method is similar to {#editor_for!} but, after retrieving the editor (creating
|
|
461
|
+
it and/or the document as needed), it activates and gives focus to it and moves
|
|
462
|
+
the cursor to the given position.
|
|
463
|
+
|
|
464
|
+
@see #editor_for! editor_for! for the possible entries in _hints_
|
|
465
|
+
@param (see #editor_for!)
|
|
466
|
+
@param [Integer,nil] line the line number to move the cursor to (0-based). Ignored if eiter
|
|
467
|
+
it or _col_ is *nil*
|
|
468
|
+
@param [Integer,nil] col the column number to move the cursor to (0-based). Ignored if eiter
|
|
469
|
+
it or _col_ is *nil*
|
|
470
|
+
@return [EditorView,nil] the editor which has been activated or *nil* if the
|
|
471
|
+
editor couldn't be found (or created)
|
|
277
472
|
=end
|
|
278
|
-
def display_doc doc, line = nil, col =
|
|
279
|
-
ed = editor_for! doc
|
|
473
|
+
def display_doc doc, line = nil, col = nil, hints = DEFAULT_HINTS
|
|
474
|
+
ed = editor_for! doc, hints
|
|
280
475
|
return unless ed
|
|
281
476
|
activate_editor ed
|
|
282
477
|
ed.go_to line, col if line and col
|
|
283
478
|
ed.set_focus
|
|
479
|
+
ed
|
|
284
480
|
end
|
|
285
|
-
|
|
481
|
+
alias_method :display_document, :display_doc
|
|
286
482
|
|
|
287
483
|
=begin rdoc
|
|
288
|
-
Executes the
|
|
289
|
-
|
|
484
|
+
Executes the given block without automatically activating an editor whenever the
|
|
485
|
+
current tab changes
|
|
486
|
+
|
|
487
|
+
This method should be used, for example, when more than one editor should be opened at
|
|
290
488
|
once. Without this, every new editor would become (for a little time) the active
|
|
291
489
|
one, with its gui merged with the main window and so on. This slows things down
|
|
292
490
|
and should be avoided. To do so, you use this method:
|
|
293
|
-
|
|
491
|
+
|
|
492
|
+
bc. Ruber[:main_window].without_activating do
|
|
294
493
|
ed = nil
|
|
295
494
|
files.each do |f|
|
|
296
495
|
ed = Ruber[:main_window].editor_for! f
|
|
297
496
|
end
|
|
298
497
|
Ruber[:main_window].activate_editor ed
|
|
299
498
|
end
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
method (even if exceptions occur).
|
|
499
|
+
|
|
500
|
+
After calling this method, the focus widget of the current tab gets focus
|
|
501
|
+
|
|
502
|
+
@note automatical editor activation will be restored at the end of this
|
|
503
|
+
method (even if exceptions occur).
|
|
504
|
+
@yield the block which should be executed without automatically activating editors
|
|
505
|
+
@return [Object] the value returned by the block
|
|
305
506
|
=end
|
|
306
|
-
def without_activating
|
|
307
|
-
|
|
308
|
-
@auto_activate_editors = false
|
|
309
|
-
yield
|
|
310
|
-
ensure
|
|
311
|
-
@auto_activate_editors = true
|
|
312
|
-
if @views.current_index < 0 then activate_editor nil
|
|
313
|
-
elsif @views.current_widget != @active_editor
|
|
314
|
-
activate_editor @views.current_widget
|
|
315
|
-
end
|
|
316
|
-
end
|
|
507
|
+
def without_activating &blk
|
|
508
|
+
@view_manager.without_activating &blk
|
|
317
509
|
end
|
|
318
510
|
|
|
319
511
|
=begin rdoc
|
|
320
|
-
Closes
|
|
321
|
-
|
|
322
|
-
|
|
512
|
+
Closes an editor view
|
|
513
|
+
|
|
514
|
+
If the editor to be closed is the last editor associated with the document the
|
|
515
|
+
document will be closed. If _ask_ is *true* and the document is modified, the user
|
|
516
|
+
will be asked whether to save or discard the changes and will have the possibility
|
|
517
|
+
of aborting closing the editor (and the document). If _ask_ is false, the document
|
|
518
|
+
will be closed without user interaction.
|
|
519
|
+
|
|
520
|
+
If there are other editors associated with the document besides the one to close,
|
|
521
|
+
the latter will be closed without affecting the document.
|
|
522
|
+
|
|
523
|
+
@param [EditorView] editor the editor to close
|
|
524
|
+
@param [Boolean] ask whether or not to ask confirmation from the user if the document
|
|
525
|
+
associated with _editor_ should be closed
|
|
526
|
+
@return [Boolean] *true* if the editor is closed and *false* if it isn't.
|
|
527
|
+
@note Always use this method to close an editor, rather than calling its {EditorView#close close}
|
|
528
|
+
method directly, unless you want to leave the corresponding document without a view
|
|
323
529
|
=end
|
|
324
|
-
def close_editor
|
|
325
|
-
|
|
530
|
+
def close_editor editor, ask = true
|
|
531
|
+
editor_tab = self.tab(editor)
|
|
532
|
+
has_focus = editor_tab.is_active_window if editor_tab
|
|
533
|
+
if has_focus
|
|
534
|
+
views = editor_tab.to_a
|
|
535
|
+
idx = views.index(editor)
|
|
536
|
+
new_view = views[idx-1] || views[idx+1]
|
|
537
|
+
end
|
|
538
|
+
doc = editor.document
|
|
539
|
+
if doc.views.size > 1
|
|
540
|
+
editor.close
|
|
541
|
+
true
|
|
542
|
+
else doc.close ask
|
|
543
|
+
end
|
|
544
|
+
focus_on_editor new_view if new_view
|
|
326
545
|
end
|
|
327
546
|
|
|
328
547
|
=begin rdoc
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
example, if this is called from a method which closes all documents and returns
|
|
357
|
-
*false*, the documents should't be closed. If it returns true, instead, they
|
|
358
|
-
should be closed, without asking again to save them).
|
|
359
|
-
|
|
360
|
-
<b>Note:</b> if _docs_ only contains non-modified documents, this method will
|
|
361
|
-
do nothing and immediately return *true*.
|
|
548
|
+
Asks the user to save multiple documents
|
|
549
|
+
|
|
550
|
+
This method is meant to be called in situation where the user may want to save
|
|
551
|
+
a number of documents, for example when the application is quitting, as it avoids
|
|
552
|
+
displaying a dialog box for each modified document.
|
|
553
|
+
|
|
554
|
+
It displays a dialog where the user can choose, among the documents passed as
|
|
555
|
+
first argument, which ones he wants to save. The user has three choiches:
|
|
556
|
+
* save some (or all) the files, then proceed with the operation which has caused
|
|
557
|
+
the dialog to be shown (for example, quitting the application)
|
|
558
|
+
* don't save any file and go on with the operation
|
|
559
|
+
* abort the operation.
|
|
560
|
+
|
|
561
|
+
In the first case, this method attempts to perform save the selected files. If
|
|
562
|
+
any of them can't be saved, the dialog to choose the files to save will be
|
|
563
|
+
displayed again, with only those files which couldn't be saved (after informing
|
|
564
|
+
the user of the failure). The user can again chose which of those files this method
|
|
565
|
+
should attempt to save again, or whether to abort the operation or skip saving.
|
|
566
|
+
This will be repeated until all files have been saved or the user gives up
|
|
567
|
+
|
|
568
|
+
In the second and third cases, the method simply returns respectively *true* or
|
|
569
|
+
*false*.
|
|
570
|
+
|
|
571
|
+
@param [Array<Document>] docs the list of documents to save. If any document isn't
|
|
572
|
+
modified, it'll be ignored. If no document is mdified, nothing will be done
|
|
573
|
+
@return [Boolean] *true* if the operation which caused the dialog to be shown
|
|
574
|
+
can be carried on and *false* if the user chose to abort it
|
|
362
575
|
=end
|
|
363
576
|
def save_documents docs = nil
|
|
364
577
|
docs ||= Ruber[:docs]
|
|
@@ -380,11 +593,12 @@ will ask whether to save modifications, otherwise it won't
|
|
|
380
593
|
end
|
|
381
594
|
|
|
382
595
|
=begin rdoc
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
596
|
+
Override of {PluginLike#query_close}
|
|
597
|
+
|
|
598
|
+
It stores the session data retrieved by the component manager in case of session
|
|
599
|
+
saving and does nothing otherwise.
|
|
600
|
+
|
|
601
|
+
@return [TrueClass]
|
|
388
602
|
=end
|
|
389
603
|
def query_close
|
|
390
604
|
if Ruber[:app].session_saving
|
|
@@ -394,18 +608,26 @@ a window
|
|
|
394
608
|
end
|
|
395
609
|
|
|
396
610
|
=begin rdoc
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
message box
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
If
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
611
|
+
Opens a project, displaying amessage boxe in case of errors
|
|
612
|
+
|
|
613
|
+
This method provides a standard interface for creating a project from a project
|
|
614
|
+
file named, automatically handling the possible exceptions.
|
|
615
|
+
|
|
616
|
+
In particular, a message box will be displayed if the project file doesn't exist
|
|
617
|
+
or if it exists but it's not a valid project file.
|
|
618
|
+
|
|
619
|
+
If the project corresponding to the given file is already open, the behaviour
|
|
620
|
+
depends on the value of _allow_reuse_. If *false*, the user is warned with a message
|
|
621
|
+
box that the project is already open and nothing is done. If _allow_reuse_ is
|
|
622
|
+
*true*, the existing project is returned.
|
|
623
|
+
|
|
624
|
+
The new project will be made active and the existing one (if any) will be closed
|
|
625
|
+
@param [String] file the absolute path of the project file to open
|
|
626
|
+
@param [Boolean] allow_reuse what to do in case the project associated to _file_
|
|
627
|
+
is already open. If *true*, the existing project will be returned; if *false*,
|
|
628
|
+
the user will be warned and *nil* will be returned
|
|
629
|
+
@return [Project,nil] the project associated with _file_ or *nil* if an error occurs
|
|
630
|
+
(including the project being already open in case _allow_reuse_ is *false*)
|
|
409
631
|
=end
|
|
410
632
|
def safe_open_project file, allow_reuse = false
|
|
411
633
|
prj = Ruber[:projects][file]
|
|
@@ -415,38 +637,39 @@ old active project (if any) will be closed.
|
|
|
415
637
|
return nil
|
|
416
638
|
elsif prj then return prj
|
|
417
639
|
end
|
|
640
|
+
message = nil
|
|
418
641
|
begin prj = Ruber[:projects].project file
|
|
419
642
|
rescue Project::InvalidProjectFile => ex
|
|
420
|
-
text =
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
643
|
+
text = "%s isn't a valid project file. The error reported was:\n%s"
|
|
644
|
+
message = KDE.i18n(text) % [file, ex.message]
|
|
645
|
+
rescue LoadError then message = KDE.i18n(ex.message)
|
|
646
|
+
end
|
|
647
|
+
if prj
|
|
648
|
+
# The following two lines should be removed when we'll allow more than one project
|
|
649
|
+
# open at the same time
|
|
650
|
+
Ruber[:projects].current_project.close if Ruber[:projects].current_project
|
|
651
|
+
Ruber[:projects].current_project = prj
|
|
652
|
+
prj
|
|
653
|
+
else
|
|
654
|
+
KDE::MessageBox.sorry self, message
|
|
655
|
+
nil
|
|
429
656
|
end
|
|
430
|
-
# The following two lines should be removed when we'll allow more than one project
|
|
431
|
-
# open at the same time
|
|
432
|
-
Ruber[:projects].current_project.close if Ruber[:projects].current_project
|
|
433
|
-
Ruber[:projects].current_project = prj
|
|
434
|
-
prj
|
|
435
657
|
end
|
|
436
658
|
|
|
437
659
|
=begin rdoc
|
|
438
|
-
|
|
660
|
+
Override of {PluginLike#save_settings}
|
|
661
|
+
|
|
662
|
+
It saves recent files and projects, the position of the splitters and the size of
|
|
439
663
|
the window.
|
|
440
664
|
|
|
441
|
-
|
|
442
|
-
there's still an open bug, it seems to work, at least for me) see whether it's
|
|
443
|
-
still necessary to store the window size.
|
|
665
|
+
@return [nil]
|
|
444
666
|
=end
|
|
445
667
|
def save_settings
|
|
446
668
|
@workspace.store_sizes
|
|
447
|
-
# TODO
|
|
448
|
-
#
|
|
449
|
-
config =
|
|
669
|
+
# TODO see if the following line works. Otherwise, remove it and uncomment the one
|
|
670
|
+
# following it
|
|
671
|
+
config = Ruber[:config].kconfig
|
|
672
|
+
# config = KDE::Global.config
|
|
450
673
|
recent_files = KDE::ConfigGroup.new config, 'Recent files'
|
|
451
674
|
action_collection.action("file_open_recent").save_entries recent_files
|
|
452
675
|
recent_projects = KDE::ConfigGroup.new config, 'Recent projects'
|
|
@@ -454,52 +677,71 @@ still necessary to store the window size.
|
|
|
454
677
|
config.sync
|
|
455
678
|
c = Ruber[:config][:main_window]
|
|
456
679
|
c.window_size = rect.size
|
|
680
|
+
nil
|
|
457
681
|
end
|
|
458
682
|
|
|
459
683
|
=begin rdoc
|
|
460
|
-
|
|
461
|
-
|
|
684
|
+
Override of {PluginLike#load_settings}
|
|
685
|
+
@return [nil]
|
|
462
686
|
=end
|
|
463
687
|
def load_settings
|
|
464
688
|
c = Ruber[:config][:general]
|
|
465
689
|
@default_script_dir = KDE::Url.from_path c.default_script_directory
|
|
466
690
|
@default_project_dir = KDE::Url.from_path c.default_project_directory
|
|
691
|
+
@tabs.tabs_closable = Ruber[:config][:workspace, :close_buttons] if @tabs
|
|
692
|
+
nil
|
|
467
693
|
end
|
|
468
694
|
|
|
469
695
|
=begin rdoc
|
|
470
|
-
Gives
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
*
|
|
474
|
-
|
|
475
|
-
|
|
696
|
+
Gives focus to an editor view
|
|
697
|
+
|
|
698
|
+
Giving focus to the editor implies:
|
|
699
|
+
* bringing the tab containing it to the foreground
|
|
700
|
+
* activating the editor
|
|
701
|
+
* giving focus to the editor
|
|
702
|
+
|
|
703
|
+
@overload focus_on_editor
|
|
704
|
+
Gives focus to the active editor
|
|
705
|
+
@return [EditorView,nil]
|
|
706
|
+
@overload focus_on_editor editor
|
|
707
|
+
@param [EditorView] editor the editor to give focus to
|
|
708
|
+
@return [EditorView,nil]
|
|
709
|
+
@overload focus_on_editor doc, hints = DEFAULT_HINTS
|
|
710
|
+
@param [Document, String, KDE::Url] doc the document. If it is a string representing
|
|
711
|
+
a relative path, it'll be considered relative to the current directory. If the
|
|
712
|
+
string is an Url, it'll be interpreted as such
|
|
713
|
+
@param [Hash] hints options to tailor the algorithm used to retrieve or create the editor.
|
|
714
|
+
See {#editor_for!} for a description of the values it can contain
|
|
715
|
+
@return [EditorView,nil]
|
|
716
|
+
@return [EditorView,nil] the editor which was given focus or *nil* if no editor
|
|
717
|
+
received focus
|
|
476
718
|
=end
|
|
477
|
-
def focus_on_editor ed = nil
|
|
719
|
+
def focus_on_editor ed = nil, hints = DEFAULT_HINTS
|
|
478
720
|
if ed
|
|
479
|
-
ed = editor_for! ed unless ed.is_a? EditorView
|
|
480
|
-
activate_editor ed
|
|
721
|
+
ed = editor_for! ed, hints unless ed.is_a? EditorView
|
|
722
|
+
activate_editor ed
|
|
723
|
+
ed.set_focus
|
|
724
|
+
else active_editor.set_focus if active_editor
|
|
481
725
|
end
|
|
482
|
-
|
|
726
|
+
active_editor
|
|
483
727
|
end
|
|
484
|
-
|
|
485
|
-
=begin rdoc
|
|
486
|
-
The directory where to save files which don't belong to a project
|
|
487
|
-
=end
|
|
488
|
-
# def scripts_directory
|
|
489
|
-
# @default_script_dir
|
|
490
|
-
# end
|
|
491
|
-
|
|
728
|
+
|
|
492
729
|
=begin rdoc
|
|
493
|
-
|
|
730
|
+
@return [String] the default directory where to look for, and create, projects
|
|
494
731
|
=end
|
|
495
732
|
def projects_directory
|
|
496
733
|
@default_project_dir
|
|
497
734
|
end
|
|
498
735
|
|
|
499
736
|
=begin rdoc
|
|
500
|
-
Executes
|
|
737
|
+
Executes a given action
|
|
738
|
+
|
|
739
|
+
@param [String] name the name by which the action has been registered in the
|
|
740
|
+
main window's @action_collection@
|
|
741
|
+
@param [Array] args a list of parameters to pass to the signal or slot associated
|
|
742
|
+
to the action
|
|
501
743
|
|
|
502
|
-
|
|
744
|
+
@see GuiPlugin#execute_action
|
|
503
745
|
=end
|
|
504
746
|
def execute_action name, *args
|
|
505
747
|
data = plugin_description.actions[name.to_s]
|
|
@@ -518,6 +760,20 @@ See <tt>GuiPlugin#execute_action</tt> for more details
|
|
|
518
760
|
else false
|
|
519
761
|
end
|
|
520
762
|
end
|
|
763
|
+
|
|
764
|
+
=begin rdoc
|
|
765
|
+
Settings widget for the workspace group
|
|
766
|
+
=end
|
|
767
|
+
class WorkspaceSettingsWidget < Qt::Widget
|
|
768
|
+
=begin rdoc
|
|
769
|
+
@param [Qt::Widget,nil] parent the parent widget
|
|
770
|
+
=end
|
|
771
|
+
def initialize parent = nil
|
|
772
|
+
super
|
|
773
|
+
@ui = Ui::WorkspaceSettingsWidgetBase.new
|
|
774
|
+
@ui.setup_ui self
|
|
775
|
+
end
|
|
776
|
+
end
|
|
521
777
|
|
|
522
778
|
end
|
|
523
779
|
|