VimMate 0.6.2
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/CHANGELOG +53 -0
- data/COPYING +20 -0
- data/README +198 -0
- data/Rakefile +72 -0
- data/TODO +9 -0
- data/bin/vimmate +179 -0
- data/lib/vimmatelib/config.rb +124 -0
- data/lib/vimmatelib/dummy_window.rb +39 -0
- data/lib/vimmatelib/file.png +0 -0
- data/lib/vimmatelib/file_green.png +0 -0
- data/lib/vimmatelib/file_orange.png +0 -0
- data/lib/vimmatelib/file_red.png +0 -0
- data/lib/vimmatelib/files.rb +230 -0
- data/lib/vimmatelib/files_menu.rb +325 -0
- data/lib/vimmatelib/files_window.rb +395 -0
- data/lib/vimmatelib/folder.png +0 -0
- data/lib/vimmatelib/folder_green.png +0 -0
- data/lib/vimmatelib/folder_orange.png +0 -0
- data/lib/vimmatelib/folder_red.png +0 -0
- data/lib/vimmatelib/icons.rb +104 -0
- data/lib/vimmatelib/main_window.rb +59 -0
- data/lib/vimmatelib/nice_singleton.rb +53 -0
- data/lib/vimmatelib/requirer.rb +68 -0
- data/lib/vimmatelib/search_window.rb +220 -0
- data/lib/vimmatelib/subversion.rb +157 -0
- data/lib/vimmatelib/terminals_window.rb +115 -0
- data/lib/vimmatelib/version.rb +29 -0
- data/lib/vimmatelib/vim_window.rb +84 -0
- data/lib/vimmatelib/vimmate16.png +0 -0
- data/lib/vimmatelib/vimmate32.png +0 -0
- data/lib/vimmatelib/vimmate48.png +0 -0
- data/setup.rb +1586 -0
- metadata +77 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
=begin
|
2
|
+
= VimMate: Vim graphical add-on
|
3
|
+
Copyright (c) 2006 Guillaume Benny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'yaml'
|
25
|
+
require 'vimmatelib/nice_singleton'
|
26
|
+
|
27
|
+
module VimMate
|
28
|
+
|
29
|
+
# Holds the configurations for VimMate. Also read and write this
|
30
|
+
# configuration so the user can change it.
|
31
|
+
class Config
|
32
|
+
include NiceSingleton
|
33
|
+
|
34
|
+
BASE_FILENAME = '.vimmaterc'
|
35
|
+
DEFAULT_CONFIG = {
|
36
|
+
:window_title => 'VimMate',
|
37
|
+
:window_width => 950,
|
38
|
+
:window_height => 600,
|
39
|
+
:layout_big_terminals => false,
|
40
|
+
:files_opened_width => 250,
|
41
|
+
:files_closed_width => 25,
|
42
|
+
:files_expanded => true,
|
43
|
+
:file_headers_visible => false,
|
44
|
+
:file_hover_selection => false,
|
45
|
+
:file_directory_separator => true,
|
46
|
+
:files_filter_active => true,
|
47
|
+
:files_refresh_interval => 10,
|
48
|
+
:files_default_open_in_tabs => true,
|
49
|
+
:files_use_ellipsis => true,
|
50
|
+
:files_use_search => true,
|
51
|
+
:files_search_ignore_case => true,
|
52
|
+
:files_search_separator_position => 400,
|
53
|
+
:terminals_enabled => true,
|
54
|
+
:terminals_height => 50,
|
55
|
+
:terminals_font => "10",
|
56
|
+
:terminals_foreground_color => "#000000",
|
57
|
+
:terminals_background_color => "#FFFFDD",
|
58
|
+
:terminals_audible_bell => false,
|
59
|
+
:terminals_visible_bell => false,
|
60
|
+
:terminals_autoexec => "",
|
61
|
+
:subversion_enabled => true,
|
62
|
+
}.freeze
|
63
|
+
|
64
|
+
# Create the Config class. Cannot be called directly
|
65
|
+
def initialize
|
66
|
+
# Set the full path to the configuration file. In the user's
|
67
|
+
# HOME or the current directory
|
68
|
+
if ENV['HOME']
|
69
|
+
self.class.const_set(:FILENAME, File.join(ENV['HOME'], BASE_FILENAME))
|
70
|
+
else
|
71
|
+
self.class.const_set(:FILENAME, BASE_FILENAME)
|
72
|
+
end
|
73
|
+
@config = DEFAULT_CONFIG.dup
|
74
|
+
end
|
75
|
+
|
76
|
+
# Access the configuration hash
|
77
|
+
def config
|
78
|
+
read_config
|
79
|
+
@config.freeze
|
80
|
+
# Once read, we only need a simple reader
|
81
|
+
self.class.send(:attr_reader, :config)
|
82
|
+
config
|
83
|
+
end
|
84
|
+
|
85
|
+
# Easy access to the configuration hash
|
86
|
+
def [](symbol)
|
87
|
+
config[symbol.to_sym]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get the lib path
|
91
|
+
def lib_path
|
92
|
+
File.dirname(File.expand_path(__FILE__))
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Read the configuration file
|
98
|
+
def read_config
|
99
|
+
# Write the default if it doesn't exist
|
100
|
+
unless File.exist? FILENAME
|
101
|
+
write_config
|
102
|
+
return
|
103
|
+
end
|
104
|
+
# Read the configuration file and merge it with the default
|
105
|
+
# so if the user doesn't specify an option, it's set to the default
|
106
|
+
@config.merge!(YAML.load_file(FILENAME))
|
107
|
+
write_config
|
108
|
+
rescue StandardError => e
|
109
|
+
$stderr.puts e.to_s
|
110
|
+
$stderr.puts "Problem reading config file #{FILENAME}, using default"
|
111
|
+
end
|
112
|
+
|
113
|
+
# Write the configuration file
|
114
|
+
def write_config
|
115
|
+
File.open(FILENAME, 'w') do |file|
|
116
|
+
YAML.dump(@config, file)
|
117
|
+
end
|
118
|
+
rescue StandardError => e
|
119
|
+
$stderr.puts e.to_s
|
120
|
+
$stderr.puts "Problem writing config file #{FILENAME}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
=begin
|
2
|
+
= VimMate: Vim graphical add-on
|
3
|
+
Copyright (c) 2006 Guillaume Benny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'gtk2'
|
25
|
+
|
26
|
+
module VimMate
|
27
|
+
|
28
|
+
# Represents a dummy window used when a feature is missing
|
29
|
+
class DummyWindow
|
30
|
+
|
31
|
+
attr_reader :gtk_window
|
32
|
+
|
33
|
+
# Create a DummyWindow
|
34
|
+
def initialize
|
35
|
+
@gtk_window = Gtk::EventBox.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,230 @@
|
|
1
|
+
=begin
|
2
|
+
= VimMate: Vim graphical add-on
|
3
|
+
Copyright (c) 2006 Guillaume Benny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'set'
|
25
|
+
require 'vimmatelib/icons'
|
26
|
+
require 'vimmatelib/requirer'
|
27
|
+
|
28
|
+
module VimMate
|
29
|
+
|
30
|
+
# A file within the tree
|
31
|
+
class ListedFile
|
32
|
+
attr_reader :name, :path, :parent
|
33
|
+
|
34
|
+
# Create a ListedFile from a path and an optional parent. A block
|
35
|
+
# must be passed so it can be called to signal changes.
|
36
|
+
def initialize(path, parent = nil, &block)
|
37
|
+
@path = path
|
38
|
+
@name = File.basename(path)
|
39
|
+
@parent = parent
|
40
|
+
@tree_signal = block
|
41
|
+
@tree_signal.call(:add, self)
|
42
|
+
@last_status = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
# Refresh the file. Doesn't do anything since it's the directory
|
46
|
+
# that does the job.
|
47
|
+
def refresh
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the icon for this file
|
52
|
+
def icon
|
53
|
+
Icons.file_icon
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the status text for this file
|
57
|
+
def status_text
|
58
|
+
""
|
59
|
+
end
|
60
|
+
|
61
|
+
# If subversion can be required, change the definition of some functions
|
62
|
+
Requirer.require_if('vimmatelib/subversion') do
|
63
|
+
# Refresh the file. If the file status has changed, send a refresh
|
64
|
+
# signal
|
65
|
+
def refresh
|
66
|
+
status = Subversion.status(@path)
|
67
|
+
if @last_status != status
|
68
|
+
@last_status = status
|
69
|
+
@tree_signal.call(:refresh, self)
|
70
|
+
end
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return the icon for this file depending on the file status
|
75
|
+
def icon
|
76
|
+
status = Subversion.status(@path)
|
77
|
+
if @last_status != status
|
78
|
+
@last_status = status
|
79
|
+
end
|
80
|
+
case status
|
81
|
+
when Subversion::UNVERSIONED, Subversion::EXTERNAL,
|
82
|
+
Subversion::IGNORED, Subversion::UNKNOWN
|
83
|
+
Icons.send("#{icon_type}_icon")
|
84
|
+
when Subversion::NONE, Subversion::NORMAL
|
85
|
+
Icons.send("#{icon_type}_green_icon")
|
86
|
+
when Subversion::ADDED, Subversion::DELETED,
|
87
|
+
Subversion::REPLACED, Subversion::MODIFIED
|
88
|
+
Icons.send("#{icon_type}_orange_icon")
|
89
|
+
when Subversion::MISSING, Subversion::MERGED,
|
90
|
+
Subversion::CONFLICTED, Subversion::OBSTRUCTED,
|
91
|
+
Subversion::INCOMPLETE
|
92
|
+
Icons.send("#{icon_type}_red_icon")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return the status text for this file depending on the file status
|
97
|
+
def status_text
|
98
|
+
Subversion.status_text(@path)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# The type of icon to use
|
103
|
+
def icon_type
|
104
|
+
:file
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# A directory within the tree. Can contain files and other directories.
|
109
|
+
class ListedDirectory < ListedFile
|
110
|
+
include Enumerable
|
111
|
+
|
112
|
+
# Create a ListedDirectory from a path and an optional parent. A block
|
113
|
+
# must be passed so it can be called to signal changes.
|
114
|
+
def initialize(path, exclude_file_list, parent = nil, &block)
|
115
|
+
super(path, parent, &block)
|
116
|
+
@files = Set.new
|
117
|
+
@exclude_file_list = exclude_file_list
|
118
|
+
refresh
|
119
|
+
end
|
120
|
+
|
121
|
+
# Yield each files and directory within this directory
|
122
|
+
def each(&block)
|
123
|
+
@files.each(&block)
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# Refresh the files from this directory. If it doesn't exist, the
|
128
|
+
# file is removed. If it didn't exist before, the file is added.
|
129
|
+
def refresh
|
130
|
+
super
|
131
|
+
# Find files to remove
|
132
|
+
files_to_remove = Set.new
|
133
|
+
all_paths = Set.new
|
134
|
+
each do |file|
|
135
|
+
file.refresh
|
136
|
+
if File.exist? file.path
|
137
|
+
all_paths << file.path
|
138
|
+
else
|
139
|
+
files_to_remove << file
|
140
|
+
@tree_signal.call(:remove, file)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
@files -= files_to_remove
|
144
|
+
|
145
|
+
# Find files to add
|
146
|
+
begin
|
147
|
+
Dir.foreach(@path) do |file|
|
148
|
+
# Skip hidden files
|
149
|
+
next if file =~ /^\./
|
150
|
+
path = File.join(@path, file)
|
151
|
+
next if @exclude_file_list.any? {|f| path[-(f.size+1)..-1] == "/#{f}" }
|
152
|
+
# Skip files that we already have
|
153
|
+
next if all_paths.include? path
|
154
|
+
# Add the new file
|
155
|
+
@files << if File.directory? path
|
156
|
+
ListedDirectory.new(path, @exclude_file_list, self, &@tree_signal)
|
157
|
+
else
|
158
|
+
ListedFile.new(path, self, &@tree_signal)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
rescue Errno::ENOENT
|
162
|
+
end
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
# The type of icon to use
|
167
|
+
def icon_type
|
168
|
+
:folder
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# A tree of files and directory. Can signal added and removed files.
|
173
|
+
class ListedTree
|
174
|
+
include Enumerable
|
175
|
+
|
176
|
+
# Create a ListedTree which contains ListedFile and ListedDirectory
|
177
|
+
def initialize(exclude_file_list = [])
|
178
|
+
@paths = Set.new
|
179
|
+
@refresh_signal = Set.new
|
180
|
+
@signal_method = method(:signal)
|
181
|
+
@exclude_file_list = exclude_file_list
|
182
|
+
end
|
183
|
+
|
184
|
+
# Yield each files and directory at the root of the tree
|
185
|
+
def each(&block)
|
186
|
+
@paths.each(&block)
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
# Add a path: a file or a directory. If it's a directory, all files
|
191
|
+
# within this directory are also added
|
192
|
+
def add_path(path)
|
193
|
+
return unless File.exist? path
|
194
|
+
return if @exclude_file_list.any? {|f| path[-(f.size+1)..-1] == "/#{f}" }
|
195
|
+
@paths << if File.directory? path
|
196
|
+
ListedDirectory.new(path, @exclude_file_list, &@signal_method)
|
197
|
+
else
|
198
|
+
ListedFile.new(path, &@signal_method)
|
199
|
+
end
|
200
|
+
self
|
201
|
+
end
|
202
|
+
|
203
|
+
# Refresh the files from the tree. Inexistent files are removed and
|
204
|
+
# new files are added
|
205
|
+
def refresh
|
206
|
+
each do |path|
|
207
|
+
path.refresh
|
208
|
+
end
|
209
|
+
self
|
210
|
+
end
|
211
|
+
|
212
|
+
# Add a block that will be called when a file is added or removed.
|
213
|
+
# The block take 2 arguments: method and file:
|
214
|
+
# method: :add, :remove or :refresh
|
215
|
+
# file: the ListedFile or ListedDirectory that is affected
|
216
|
+
def add_refresh_signal(&block)
|
217
|
+
@refresh_signal << block
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
# Signal that a file has been added or removed.
|
223
|
+
def signal(method, file)
|
224
|
+
@refresh_signal.each do |block|
|
225
|
+
block.call(method, file)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
@@ -0,0 +1,325 @@
|
|
1
|
+
=begin
|
2
|
+
= VimMate: Vim graphical add-on
|
3
|
+
Copyright (c) 2006 Guillaume Benny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'gtk2'
|
25
|
+
require 'fileutils'
|
26
|
+
require 'set'
|
27
|
+
require 'vimmatelib/config'
|
28
|
+
require 'vimmatelib/requirer'
|
29
|
+
|
30
|
+
module VimMate
|
31
|
+
|
32
|
+
# The pop-up menu used in the file tree
|
33
|
+
class FilesMenu
|
34
|
+
|
35
|
+
# Create a FilesMenu
|
36
|
+
def initialize(parent_window)
|
37
|
+
@parent_window = parent_window
|
38
|
+
@open_signals = Set.new
|
39
|
+
@refresh_signals = Set.new
|
40
|
+
|
41
|
+
# The last path is the path of the file that was used when
|
42
|
+
# the menu was opened
|
43
|
+
@last_path = nil
|
44
|
+
|
45
|
+
# Build the menu items
|
46
|
+
@gtk_menu = Gtk::Menu.new
|
47
|
+
|
48
|
+
@gtk_menu.append(open = Gtk::ImageMenuItem.new(Gtk::Stock::OPEN))
|
49
|
+
open.signal_connect("activate") do
|
50
|
+
menu_open
|
51
|
+
end
|
52
|
+
|
53
|
+
@gtk_menu.append(split_open = Gtk::MenuItem.new("_Split Open"))
|
54
|
+
split_open.signal_connect("activate") do
|
55
|
+
menu_split_open
|
56
|
+
end
|
57
|
+
|
58
|
+
@gtk_menu.append(tab_open = Gtk::MenuItem.new("_Tab Open"))
|
59
|
+
tab_open.signal_connect("activate") do
|
60
|
+
menu_tab_open
|
61
|
+
end
|
62
|
+
|
63
|
+
@gtk_menu.append(Gtk::SeparatorMenuItem.new)
|
64
|
+
|
65
|
+
@gtk_menu.append(new = Gtk::ImageMenuItem.new(Gtk::Stock::NEW))
|
66
|
+
new.signal_connect("activate") do
|
67
|
+
menu_new
|
68
|
+
end
|
69
|
+
|
70
|
+
@gtk_menu.append(new_folder = Gtk::MenuItem.new("New _Folder"))
|
71
|
+
new_folder.signal_connect("activate") do
|
72
|
+
menu_new_folder
|
73
|
+
end
|
74
|
+
|
75
|
+
@gtk_menu.append(Gtk::SeparatorMenuItem.new)
|
76
|
+
|
77
|
+
@gtk_menu.append(rename = Gtk::MenuItem.new("R_ename"))
|
78
|
+
rename.signal_connect("activate") do
|
79
|
+
menu_rename
|
80
|
+
end
|
81
|
+
|
82
|
+
@gtk_menu.append(delete = Gtk::ImageMenuItem.new(Gtk::Stock::DELETE))
|
83
|
+
delete.signal_connect("activate") do
|
84
|
+
menu_delete
|
85
|
+
end
|
86
|
+
|
87
|
+
@gtk_menu.append(Gtk::SeparatorMenuItem.new)
|
88
|
+
|
89
|
+
Requirer.require_if('vimmatelib/subversion') do
|
90
|
+
svn_sub_menu = Gtk::Menu.new
|
91
|
+
|
92
|
+
svn_sub_menu.append(svn_add = Gtk::ImageMenuItem.new(Gtk::Stock::ADD))
|
93
|
+
svn_add.signal_connect("activate") do
|
94
|
+
menu_svn_add
|
95
|
+
end
|
96
|
+
|
97
|
+
svn_sub_menu.append(svn_rename = Gtk::MenuItem.new("R_ename"))
|
98
|
+
svn_rename.signal_connect("activate") do
|
99
|
+
menu_svn_rename
|
100
|
+
end
|
101
|
+
|
102
|
+
svn_sub_menu.append(svn_delete = Gtk::ImageMenuItem.new(Gtk::Stock::DELETE))
|
103
|
+
svn_delete.signal_connect("activate") do
|
104
|
+
menu_svn_delete
|
105
|
+
end
|
106
|
+
|
107
|
+
svn_sub_menu.append(svn_revert = Gtk::ImageMenuItem.new(Gtk::Stock::REVERT_TO_SAVED))
|
108
|
+
svn_revert.signal_connect("activate") do
|
109
|
+
menu_svn_revert
|
110
|
+
end
|
111
|
+
|
112
|
+
@gtk_menu.append(subversion = Gtk::MenuItem.new("S_ubversion"))
|
113
|
+
subversion.submenu = svn_sub_menu
|
114
|
+
end
|
115
|
+
|
116
|
+
@gtk_menu.append(refresh = Gtk::ImageMenuItem.new(Gtk::Stock::REFRESH))
|
117
|
+
refresh.signal_connect("activate") do
|
118
|
+
menu_refresh
|
119
|
+
end
|
120
|
+
|
121
|
+
@gtk_menu.show_all
|
122
|
+
end
|
123
|
+
|
124
|
+
# Open the menu. Specify a path to show where the menu was opened.
|
125
|
+
def open(path)
|
126
|
+
@last_path = path
|
127
|
+
@gtk_menu.popup(nil, nil, 0, 0)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Add a block that will be called when the user choose to open a file
|
131
|
+
# The block take two argument: the path to the file to open, and a
|
132
|
+
# symbol to indicate the kind: :open, :split_open, :tab_open
|
133
|
+
def add_open_signal(&block)
|
134
|
+
@open_signals << block
|
135
|
+
end
|
136
|
+
|
137
|
+
# Add a block that will be called when the user choose to refresh the
|
138
|
+
# file tree. The block doesn't take an argument.
|
139
|
+
def add_refresh_signal(&block)
|
140
|
+
@refresh_signals << block
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# Signals that a file must be opened
|
146
|
+
def menu_open
|
147
|
+
@open_signals.each do |signal|
|
148
|
+
signal.call(@last_path, :open)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def menu_split_open
|
153
|
+
@open_signals.each do |signal|
|
154
|
+
signal.call(@last_path, :split_open)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def menu_tab_open
|
159
|
+
@open_signals.each do |signal|
|
160
|
+
signal.call(@last_path, :tab_open)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Open a dialog to enter a new file name to create
|
165
|
+
def menu_new
|
166
|
+
dialog = Gtk::FileChooserDialog.new("New file",
|
167
|
+
@parent_window.gtk_window,
|
168
|
+
Gtk::FileChooser::ACTION_SAVE,
|
169
|
+
nil,
|
170
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
171
|
+
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])
|
172
|
+
dialog.set_icon_list(Icons.window_icons)
|
173
|
+
dialog.current_folder = if File.directory? @last_path
|
174
|
+
@last_path
|
175
|
+
else
|
176
|
+
File.dirname(@last_path)
|
177
|
+
end
|
178
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
179
|
+
begin
|
180
|
+
FileUtils.touch(dialog.filename)
|
181
|
+
rescue
|
182
|
+
$stderr.puts "Cannot touch #{dialog.filename}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
dialog.destroy
|
186
|
+
menu_refresh
|
187
|
+
end
|
188
|
+
|
189
|
+
# Open a dialog to enter a new folder name to create
|
190
|
+
def menu_new_folder
|
191
|
+
dialog = Gtk::FileChooserDialog.new("New folder",
|
192
|
+
@parent_window.gtk_window,
|
193
|
+
Gtk::FileChooser::ACTION_CREATE_FOLDER,
|
194
|
+
nil,
|
195
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
196
|
+
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])
|
197
|
+
dialog.set_icon_list(Icons.window_icons)
|
198
|
+
dialog.current_folder = if File.directory? @last_path
|
199
|
+
@last_path
|
200
|
+
else
|
201
|
+
File.dirname(@last_path)
|
202
|
+
end
|
203
|
+
dialog.run
|
204
|
+
dialog.destroy
|
205
|
+
menu_refresh
|
206
|
+
end
|
207
|
+
|
208
|
+
# Open a dialog to enter a new name for a file or directory
|
209
|
+
def menu_rename
|
210
|
+
dialog = Gtk::FileChooserDialog.new("Rename #{File.basename(@last_path)}",
|
211
|
+
@parent_window.gtk_window,
|
212
|
+
Gtk::FileChooser::ACTION_SAVE,
|
213
|
+
nil,
|
214
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
215
|
+
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])
|
216
|
+
dialog.set_icon_list(Icons.window_icons)
|
217
|
+
dialog.current_folder = File.dirname(@last_path)
|
218
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
219
|
+
begin
|
220
|
+
File.rename(@last_path, dialog.filename)
|
221
|
+
rescue SystemCallError
|
222
|
+
$stderr.puts "Cannot rename from #{@last_path} to #{dialog.filename}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
dialog.destroy
|
226
|
+
menu_refresh
|
227
|
+
end
|
228
|
+
|
229
|
+
# Open a dialog and ask the user if he really wants to delete the target
|
230
|
+
# file or directory. Note that for safety, directories can only be removed
|
231
|
+
# if they are empty.
|
232
|
+
def menu_delete
|
233
|
+
name = File.basename(@last_path)
|
234
|
+
dialog = Gtk::MessageDialog.new(@parent_window.gtk_window,
|
235
|
+
Gtk::MessageDialog::Flags::MODAL,
|
236
|
+
Gtk::MessageDialog::Type::QUESTION,
|
237
|
+
Gtk::MessageDialog::ButtonsType::YES_NO,
|
238
|
+
if File.directory? @last_path
|
239
|
+
"Delete directory #{name} ?"
|
240
|
+
else
|
241
|
+
"Delete file #{name} ?"
|
242
|
+
end)
|
243
|
+
dialog.set_icon_list(Icons.window_icons)
|
244
|
+
if dialog.run == Gtk::Dialog::RESPONSE_YES
|
245
|
+
begin
|
246
|
+
if File.directory? @last_path
|
247
|
+
FileUtils.rmdir(@last_path)
|
248
|
+
else
|
249
|
+
FileUtils.rm(@last_path)
|
250
|
+
end
|
251
|
+
rescue
|
252
|
+
$stderr.puts "Cannot remove #{@last_path}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
dialog.destroy
|
256
|
+
menu_refresh
|
257
|
+
end
|
258
|
+
|
259
|
+
# Only define the Subversion methods if subversion is available
|
260
|
+
Requirer.require_if('vimmatelib/subversion') do
|
261
|
+
|
262
|
+
# Add the selected file to subversion
|
263
|
+
def menu_svn_add
|
264
|
+
Subversion.add(@last_path)
|
265
|
+
menu_refresh
|
266
|
+
end
|
267
|
+
|
268
|
+
# Rename the selected file with subversion
|
269
|
+
def menu_svn_rename
|
270
|
+
dialog = Gtk::FileChooserDialog.new("Rename #{File.basename(@last_path)}",
|
271
|
+
@parent_window.gtk_window,
|
272
|
+
Gtk::FileChooser::ACTION_SAVE,
|
273
|
+
nil,
|
274
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
275
|
+
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])
|
276
|
+
dialog.set_icon_list(Icons.window_icons)
|
277
|
+
dialog.current_folder = File.dirname(@last_path)
|
278
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
279
|
+
Subversion.move(@last_path, dialog.filename)
|
280
|
+
end
|
281
|
+
dialog.destroy
|
282
|
+
menu_refresh
|
283
|
+
end
|
284
|
+
|
285
|
+
# Revert the selected file in subversion
|
286
|
+
def menu_svn_revert
|
287
|
+
dialog = Gtk::MessageDialog.new(@parent_window.gtk_window,
|
288
|
+
Gtk::MessageDialog::Flags::MODAL,
|
289
|
+
Gtk::MessageDialog::Type::QUESTION,
|
290
|
+
Gtk::MessageDialog::ButtonsType::YES_NO,
|
291
|
+
"Do a Subversion Revert on #{File.basename(@last_path)} ?")
|
292
|
+
dialog.set_icon_list(Icons.window_icons)
|
293
|
+
if dialog.run == Gtk::Dialog::RESPONSE_YES
|
294
|
+
Subversion.revert(@last_path)
|
295
|
+
end
|
296
|
+
dialog.destroy
|
297
|
+
menu_refresh
|
298
|
+
end
|
299
|
+
|
300
|
+
# Delete the selected file from subversion
|
301
|
+
def menu_svn_delete
|
302
|
+
dialog = Gtk::MessageDialog.new(@parent_window.gtk_window,
|
303
|
+
Gtk::MessageDialog::Flags::MODAL,
|
304
|
+
Gtk::MessageDialog::Type::QUESTION,
|
305
|
+
Gtk::MessageDialog::ButtonsType::YES_NO,
|
306
|
+
"Do a Subversion Delete on #{File.basename(@last_path)} ?")
|
307
|
+
dialog.set_icon_list(Icons.window_icons)
|
308
|
+
if dialog.run == Gtk::Dialog::RESPONSE_YES
|
309
|
+
Subversion.remove(@last_path)
|
310
|
+
end
|
311
|
+
dialog.destroy
|
312
|
+
menu_refresh
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Signals that the file tree must be refreshed
|
317
|
+
def menu_refresh
|
318
|
+
@refresh_signals.each do |signal|
|
319
|
+
signal.call
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|