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.
@@ -0,0 +1,395 @@
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 'set'
26
+ require 'thread'
27
+ require 'vimmatelib/config'
28
+ require 'vimmatelib/files'
29
+ require 'vimmatelib/icons'
30
+ require 'vimmatelib/search_window'
31
+
32
+ module VimMate
33
+
34
+ # The window that contains the file tree
35
+ class FilesWindow
36
+
37
+ # Column for the file name
38
+ NAME = 0
39
+ # Column for the full path of the file
40
+ PATH = 1
41
+ # Column for the icon of the file
42
+ ICON = 2
43
+ # Column used to sort the files
44
+ SORT = 3
45
+ # Column used to store the type of row
46
+ TYPE = 4
47
+ # Column used to store the status of the file
48
+ STATUS = 5
49
+ # Type of row: file
50
+ TYPE_FILE = 0
51
+ # Type of row: directory
52
+ TYPE_DIRECTORY = 1
53
+ # Type of row: separator
54
+ TYPE_SEPARATOR = 2
55
+
56
+ # Create a FilesWindow
57
+ def initialize(exclude_file_list = [])
58
+ @open_signal = Set.new
59
+ @menu_signal = Set.new
60
+ @expander_signal = Set.new
61
+
62
+ @filter_string = ""
63
+
64
+ # Tree Store: Filename, Full path, Icon, Sort, Type, Status
65
+ @gtk_tree_store = Gtk::TreeStore.new(String,
66
+ String,
67
+ Gdk::Pixbuf,
68
+ String,
69
+ Fixnum,
70
+ String)
71
+ @gtk_tree_store.set_sort_column_id(SORT)
72
+
73
+ # Filtered Tree Store
74
+ @gtk_filtered_tree_model = Gtk::TreeModelFilter.new(@gtk_tree_store)
75
+ @gtk_filtered_tree_model.set_visible_func do |model, iter|
76
+ if @filter_string.nil? or @filter_string.empty?
77
+ true
78
+ elsif iter[TYPE] == TYPE_DIRECTORY or iter[TYPE] == TYPE_SEPARATOR
79
+ true
80
+ else
81
+ if not iter[NAME] or iter[NAME].index(@filter_string)
82
+ true
83
+ else
84
+ false
85
+ end
86
+ end
87
+ end
88
+
89
+ # Tree View
90
+ @gtk_tree_view = Gtk::TreeView.new(@gtk_filtered_tree_model)
91
+ @gtk_tree_view.selection.mode = Gtk::SELECTION_SINGLE
92
+ @gtk_tree_view.headers_visible = Config[:file_headers_visible]
93
+ @gtk_tree_view.hover_selection = Config[:file_hover_selection]
94
+
95
+ # Double-click, Enter, Space: Signal to open the file
96
+ @gtk_tree_view.signal_connect("row-activated") do |view, path, column|
97
+ path = @gtk_filtered_tree_model.get_iter(path)[PATH]
98
+ @open_signal.each do |signal|
99
+ signal.call(path,
100
+ Config[:files_default_open_in_tabs] ? :tab_open : :open)
101
+ end
102
+ end
103
+
104
+ # Left-click: Select and Signal to open the menu
105
+ @gtk_tree_view.signal_connect("button_press_event") do |widget, event|
106
+ if event.kind_of? Gdk::EventButton and event.button == 3
107
+ path = @gtk_tree_view.get_path_at_pos(event.x, event.y)
108
+ @gtk_tree_view.selection.select_path(path[0]) if path
109
+
110
+ selected = @gtk_tree_view.selection.selected
111
+ if selected
112
+ @menu_signal.each do |signal|
113
+ signal.call(selected[PATH])
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ # Create a label to show the path of the file
120
+ gtk_label = Gtk::Label.new
121
+ gtk_label.ellipsize = Pango::Layout::EllipsizeMode::START
122
+
123
+ # When a selection is changed in the tree view, we change the label
124
+ # to show the path of the file
125
+ @gtk_tree_view.selection.signal_connect("changed") do
126
+ gtk_label.text = ""
127
+ next if (selected_row = @gtk_tree_view.selection.selected).nil?
128
+ gtk_label.text = File.join(File.dirname(selected_row[PATH]), selected_row[NAME])
129
+ end
130
+
131
+ # Same thing as Left-click, but with the keyboard
132
+ @gtk_tree_view.signal_connect("popup_menu") do
133
+ selected = @gtk_tree_view.selection.selected
134
+ if selected
135
+ @menu_signal.each do |signal|
136
+ signal.call(selected[PATH])
137
+ end
138
+ end
139
+ end
140
+
141
+ # Separator between directories
142
+ @gtk_tree_view.set_row_separator_func do |model, iter|
143
+ iter[TYPE] == TYPE_SEPARATOR
144
+ end
145
+
146
+ # Add the columns
147
+ column = Gtk::TreeViewColumn.new
148
+ column.title = "Files"
149
+
150
+ # Icon
151
+ icon_cell_renderer = Gtk::CellRendererPixbuf.new
152
+ column.pack_start(icon_cell_renderer, false)
153
+ column.set_attributes(icon_cell_renderer, :pixbuf => ICON)
154
+
155
+ # File name
156
+ text_cell_renderer = Gtk::CellRendererText.new
157
+ if Config[:files_use_ellipsis]
158
+ text_cell_renderer.ellipsize = Pango::Layout::EllipsizeMode::MIDDLE
159
+ end
160
+ column.pack_start(text_cell_renderer, true)
161
+ column.set_attributes(text_cell_renderer, :text => NAME)
162
+
163
+ # Status
164
+ text_cell_renderer2 = Gtk::CellRendererText.new
165
+ if Config[:files_use_ellipsis]
166
+ text_cell_renderer2.ellipsize = Pango::Layout::EllipsizeMode::END
167
+ end
168
+ column.pack_start(text_cell_renderer2, true)
169
+ column.set_attributes(text_cell_renderer2, :text => STATUS)
170
+
171
+ @gtk_tree_view.append_column(column)
172
+
173
+ # Put the tree view in a scroll window
174
+ @gtk_scrolled_window = Gtk::ScrolledWindow.new
175
+ @gtk_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC,
176
+ Gtk::POLICY_AUTOMATIC)
177
+ @gtk_scrolled_window.add(@gtk_tree_view)
178
+
179
+ # Set the default size for the file list
180
+ @gtk_scrolled_window.set_size_request(Config[:files_opened_width], -1)
181
+
182
+ # Create a box to filter the list
183
+ gtk_filter_box = Gtk::HBox.new
184
+ gtk_filter_box.pack_start(gtk_filter_button = Gtk::ToggleButton.new("Filter"), false, false)
185
+ gtk_filter_box.pack_start(gtk_entry = Gtk::Entry.new, true, true)
186
+ changed_lambda = lambda do
187
+ if gtk_filter_button.active?
188
+ self.filter = gtk_entry.text
189
+ else
190
+ self.clear_filter
191
+ end
192
+ end
193
+ gtk_entry.signal_connect("changed", &changed_lambda)
194
+ gtk_filter_button.signal_connect("toggled", &changed_lambda)
195
+ gtk_filter_button.active = Config[:files_filter_active]
196
+ gtk_filter_box.spacing = 10
197
+ gtk_filter_box.border_width = 10
198
+
199
+ # Create the file tree
200
+ initialize_file_tree(exclude_file_list)
201
+
202
+ gtk_top_box = Gtk::VBox.new
203
+ gtk_top_box.pack_start(gtk_filter_box, false, false)
204
+ gtk_top_box.pack_start(@gtk_scrolled_window, true, true)
205
+ gtk_top_box.pack_start(gtk_label, false, false)
206
+
207
+ # Create the search file list if it's enabled
208
+ if Config[:files_use_search]
209
+ @gtk_paned_box = Gtk::VPaned.new
210
+ @gtk_paned_box.add(gtk_top_box)
211
+ @gtk_paned_box.add((search_window = SearchWindow.new(@file_tree)).gtk_window)
212
+ @gtk_paned_box.position = Config[:files_search_separator_position]
213
+
214
+ # Set the signals for the search window
215
+ search_window.add_open_signal do |path, kind|
216
+ @open_signal.each do |signal|
217
+ signal.call(path, kind)
218
+ end
219
+ end
220
+ search_window.add_menu_signal do |path|
221
+ @menu_signal.each do |signal|
222
+ signal.call(path)
223
+ end
224
+ end
225
+ end
226
+
227
+
228
+ @gtk_expander = Gtk::Expander.new("File list")
229
+ @gtk_expander.expanded = Config[:files_expanded]
230
+ if Config[:files_use_search]
231
+ @gtk_expander.add(@gtk_paned_box)
232
+ else
233
+ @gtk_expander.add(gtk_top_box)
234
+ end
235
+ @gtk_expander.signal_connect("notify::expanded") do
236
+ @expander_signal.each do |signal|
237
+ signal.call(@gtk_expander.expanded?)
238
+ end
239
+ end
240
+
241
+ gtk_window.border_width = 5
242
+
243
+ # Launch a timer to refresh the file list
244
+ @file_tree_mutex = Mutex.new
245
+ Gtk.timeout_add(Config[:files_refresh_interval] * 1000) do
246
+ do_refresh
247
+ true
248
+ end
249
+ end
250
+
251
+ # Recursively add a path at the root of the tree
252
+ def add_path(path)
253
+ @file_tree_mutex.synchronize do
254
+ @file_tree.add_path(path)
255
+ end
256
+ self
257
+ end
258
+
259
+ # The "window" for this object
260
+ def gtk_window
261
+ @gtk_expander
262
+ end
263
+
264
+ # Refresh the file list
265
+ def refresh
266
+ do_refresh
267
+ self
268
+ end
269
+
270
+ # Get the filter: files must contain this string
271
+ def filter
272
+ @filter_string
273
+ end
274
+
275
+ # Set a filter: files must contain this string
276
+ def filter=(filter)
277
+ @filter_string = filter
278
+ @gtk_filtered_tree_model.refilter
279
+ end
280
+
281
+ # Clear the filter
282
+ def clear_filter
283
+ @filter_string = ""
284
+ @gtk_filtered_tree_model.refilter
285
+ filter
286
+ end
287
+
288
+ # Expand the first row of the file tree
289
+ def expand_first_row
290
+ @gtk_tree_view.collapse_all
291
+ @gtk_tree_view.expand_row(Gtk::TreePath.new("0"), false)
292
+ end
293
+
294
+ # Add a block that will be called when the user choose to open a file
295
+ # The block take two argument: the path to the file to open, and a
296
+ # symbol to indicate the kind: :open, :split_open, :tab_open
297
+ def add_open_signal(&block)
298
+ @open_signal << block
299
+ end
300
+
301
+ # Add a block that will be called when the user choose to open the
302
+ # menu. The block takes one argument: the path to the file to open.
303
+ def add_menu_signal(&block)
304
+ @menu_signal << block
305
+ end
306
+
307
+ # Add a block that will be called when the user choose to expand or
308
+ # close the expander. The block takes one argument: if the expander
309
+ # is opened or closed
310
+ def add_expander_signal(&block)
311
+ @expander_signal << block
312
+ end
313
+
314
+ private
315
+
316
+ # Lunch the refresh of the tree
317
+ def do_refresh
318
+ @file_tree_mutex.synchronize do
319
+ @file_tree.refresh
320
+ end
321
+ end
322
+
323
+ # Create the file tree
324
+ def initialize_file_tree(exclude_file_list)
325
+ @file_tree = ListedTree.new(exclude_file_list)
326
+
327
+ # Register to receive a signal when a file is added or removed
328
+ @file_tree.add_refresh_signal do |method, file|
329
+ case method
330
+ when :add
331
+ # A file is added. Find it's parent and add it there
332
+ if file.parent
333
+ @gtk_tree_store.each do |model,path,iter|
334
+ if iter[PATH] == file.parent.path
335
+ add_to_tree(file, iter)
336
+ break
337
+ end
338
+ end
339
+ else
340
+ add_to_tree(file)
341
+ end
342
+ when :remove
343
+ # A file is removed. Find it and remove it
344
+ to_remove = []
345
+ @gtk_tree_store.each do |model,path,iter|
346
+ if iter[PATH] == file.path
347
+ to_remove << Gtk::TreeRowReference.new(model, path)
348
+ if iter.next! and iter[TYPE] == TYPE_SEPARATOR
349
+ to_remove << Gtk::TreeRowReference.new(model, path.next!)
350
+ end
351
+ break
352
+ end
353
+ end
354
+ to_remove.each do |element|
355
+ @gtk_tree_store.remove(@gtk_tree_store.get_iter(element.path))
356
+ end
357
+ when :refresh
358
+ # Called when the status of the file has changed
359
+ @gtk_tree_store.each do |model,path,iter|
360
+ if iter[PATH] == file.path
361
+ iter[ICON] = file.icon
362
+ iter[STATUS] = file.status_text
363
+ break
364
+ end
365
+ end
366
+ end
367
+ @gtk_filtered_tree_model.refilter
368
+ end
369
+ end
370
+
371
+ # Add a file to the tree
372
+ def add_to_tree(file, parent = nil)
373
+ # If we need a separator and it's a directory, we add it
374
+ if Config[:file_directory_separator] and file.instance_of? ListedDirectory
375
+ new_row = @gtk_tree_store.append(parent)
376
+ new_row[TYPE] = TYPE_SEPARATOR
377
+ new_row[SORT] = "1-#{file.path}-2"
378
+ end
379
+ # Add the row for the file
380
+ new_row = @gtk_tree_store.append(parent)
381
+ new_row[NAME] = file.name
382
+ new_row[PATH] = file.path
383
+ new_row[ICON] = file.icon
384
+ new_row[STATUS] = file.status_text
385
+ if file.instance_of? ListedDirectory
386
+ new_row[SORT] = "1-#{file.path}-1"
387
+ new_row[TYPE] = TYPE_DIRECTORY
388
+ else
389
+ new_row[SORT] = "2-#{file.path}-1"
390
+ new_row[TYPE] = TYPE_FILE
391
+ end
392
+ end
393
+ end
394
+ end
395
+
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,104 @@
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 'vimmatelib/nice_singleton'
25
+
26
+ module VimMate
27
+
28
+ # Manages the icons that can be loaded from the disk
29
+ class Icons
30
+ include NiceSingleton
31
+
32
+ # The filenames for the icons of the windows
33
+ WINDOW_ICON_FILENAME = 'vimmate%d.png'.freeze
34
+ # The size for the icons of the windows
35
+ WINDOW_ICON_SIZES = [16, 32, 48].freeze
36
+
37
+ # Name of the icons to load. Will create methods named after
38
+ # the icon's name, with _icon: folder_icon for example.
39
+ ICONS_NAME = [:folder, :file].collect do |f|
40
+ ["", :green, :orange, :red].collect do |c|
41
+ if c.to_s.empty?
42
+ f.to_sym
43
+ else
44
+ "#{f}_#{c}".to_sym
45
+ end
46
+ end
47
+ end.flatten.freeze
48
+
49
+ # Create the Icons class. Cannot be called directly
50
+ def initialize
51
+ @gtk_window_icons = []
52
+ end
53
+
54
+ # Get an array of icons for the windows
55
+ def window_icons
56
+ # Load them
57
+ load_window_icons
58
+ @gtk_window_icons.freeze
59
+ # Once loaded, we only need a reader
60
+ self.class.send(:define_method, :window_icons) do
61
+ @gtk_window_icons
62
+ end
63
+ # Return the value
64
+ window_icons
65
+ end
66
+
67
+ # Define a method with _icon for each icon's name
68
+ ICONS_NAME.each do |method|
69
+ define_method("#{method}_icon") do
70
+ # Load the file
71
+ icon = nil
72
+ file = File.join(Config.lib_path, "#{method}.png")
73
+ begin
74
+ icon = Gdk::Pixbuf.new(file) if File.exist? file
75
+ rescue StandardError => e
76
+ $stderr.puts e.to_s
77
+ $stderr.puts "Problem loading #{method} icon #{file}"
78
+ end
79
+ icon.freeze
80
+ # Once loaded, we only need a reader
81
+ self.class.send(:define_method, "#{method}_icon") do
82
+ icon
83
+ end
84
+ icon
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ # Load the icons for the windows
91
+ def load_window_icons
92
+ WINDOW_ICON_SIZES.each do |size|
93
+ file = File.join(Config.lib_path, WINDOW_ICON_FILENAME % [size])
94
+ begin
95
+ @gtk_window_icons << Gdk::Pixbuf.new(file) if File.exist? file
96
+ rescue StandardError => e
97
+ $stderr.puts e.to_s
98
+ $stderr.puts "Problem loading window icon #{file}"
99
+ end
100
+ end
101
+ end
102
+
103
+ end
104
+ end