niklas-vimmate 0.8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +10 -0
- data/CHANGELOG +108 -0
- data/COPYING +20 -0
- data/README +221 -0
- data/Rakefile +97 -0
- data/TODO +21 -0
- data/bin/vimmate +95 -0
- data/config/environment.rb +33 -0
- data/controllers/file_filter_controller.rb +85 -0
- data/controllers/file_popup_menu_controller.rb +40 -0
- data/controllers/vim_controller.rb +28 -0
- data/controllers/vim_mate_controller.rb +76 -0
- data/images/file.png +0 -0
- data/images/file_green.png +0 -0
- data/images/file_orange.png +0 -0
- data/images/file_red.png +0 -0
- data/images/folder.png +0 -0
- data/images/folder_green.png +0 -0
- data/images/folder_orange.png +0 -0
- data/images/folder_red.png +0 -0
- data/images/processing.png +0 -0
- data/images/svn_added.png +0 -0
- data/images/svn_conflict.png +0 -0
- data/images/svn_deleted.png +0 -0
- data/images/svn_locked.png +0 -0
- data/images/svn_modified.png +0 -0
- data/images/svn_normal.png +0 -0
- data/images/svn_readonly.png +0 -0
- data/images/vimmate16.png +0 -0
- data/images/vimmate32.png +0 -0
- data/images/vimmate48.png +0 -0
- data/lib/active_window.rb +8 -0
- data/lib/active_window/active_column.rb +218 -0
- data/lib/active_window/active_tree_store.rb +13 -0
- data/lib/active_window/active_tree_store/columns.rb +88 -0
- data/lib/active_window/active_tree_store/extentions.rb +81 -0
- data/lib/active_window/active_tree_store/index.rb +53 -0
- data/lib/active_window/application.rb +133 -0
- data/lib/active_window/controller.rb +58 -0
- data/lib/active_window/dot_file.rb +29 -0
- data/lib/active_window/filtered_active_tree_store.rb +114 -0
- data/lib/active_window/listed_item.rb +127 -0
- data/lib/active_window/signal.rb +46 -0
- data/lib/config_window.rb +90 -0
- data/lib/file_tree_store.rb +73 -0
- data/lib/filtered_file_tree_store.rb +34 -0
- data/lib/gtk_thread_helper.rb +73 -0
- data/lib/listed_directory.rb +43 -0
- data/lib/listed_file.rb +67 -0
- data/lib/try.rb +9 -0
- data/lib/vim/buffers.rb +18 -0
- data/lib/vim/integration.rb +36 -0
- data/lib/vim/netbeans.rb +156 -0
- data/lib/vim/source.vim +18 -0
- data/lib/vim_mate/config.rb +132 -0
- data/lib/vim_mate/dummy_window.rb +14 -0
- data/lib/vim_mate/files_menu.rb +110 -0
- data/lib/vim_mate/icons.rb +156 -0
- data/lib/vim_mate/nice_singleton.rb +53 -0
- data/lib/vim_mate/plugins.rb +6 -0
- data/lib/vim_mate/plugins/inotify/init.rb +4 -0
- data/lib/vim_mate/plugins/inotify/lib/INotify.rb +206 -0
- data/lib/vim_mate/plugins/inotify/lib/directory.rb +58 -0
- data/lib/vim_mate/plugins/subversion/init.rb +7 -0
- data/lib/vim_mate/plugins/subversion/lib/file.rb +59 -0
- data/lib/vim_mate/plugins/subversion/lib/menu.rb +96 -0
- data/lib/vim_mate/plugins/subversion/lib/subversion.rb +157 -0
- data/lib/vim_mate/requirer.rb +68 -0
- data/lib/vim_mate/search_window.rb +227 -0
- data/lib/vim_mate/tags_window.rb +167 -0
- data/lib/vim_mate/terminals_window.rb +163 -0
- data/lib/vim_mate/version.rb +29 -0
- data/lib/vim_mate/vim_widget.rb +150 -0
- data/spec/active_window/active_column_spec.rb +41 -0
- data/spec/active_window/active_tree_store_spec.rb +312 -0
- data/spec/active_window/controller_spec.rb +6 -0
- data/spec/lib/file_tree_store_spec.rb +40 -0
- data/spec/lib/listed_directory_spec.rb +26 -0
- data/spec/lib/listed_file_spec.rb +53 -0
- data/spec/nice_singleton_spec.rb +23 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +10 -0
- data/views/vim_mate.glade +308 -0
- data/vimmate.gemspec +131 -0
- metadata +168 -0
@@ -0,0 +1,163 @@
|
|
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 'vte'
|
26
|
+
|
27
|
+
module VimMate
|
28
|
+
|
29
|
+
# Do not load the terminals if they are disabled
|
30
|
+
Requirer.raise_load_error_if do
|
31
|
+
not Config[:terminals_enabled]
|
32
|
+
end
|
33
|
+
|
34
|
+
class ::Vte::Terminal
|
35
|
+
attr_accessor :pid
|
36
|
+
end
|
37
|
+
|
38
|
+
# The window that contains the terminals
|
39
|
+
class TerminalsWindow
|
40
|
+
|
41
|
+
# Create a TerminalsWindow
|
42
|
+
def initialize
|
43
|
+
@expander_signal = Set.new
|
44
|
+
|
45
|
+
# Create the tabbed page
|
46
|
+
@gtk_notebook = Gtk::Notebook.new
|
47
|
+
@gtk_notebook.scrollable = true
|
48
|
+
# Add a terminal at startup
|
49
|
+
@gtk_notebook.append_page(new_terminal)
|
50
|
+
# The last page is just an icon to create new tabs
|
51
|
+
@gtk_notebook.append_page(Gtk::EventBox.new,
|
52
|
+
Gtk::Image.new(Gtk::Stock::NEW, Gtk::IconSize::MENU))
|
53
|
+
# When we try to go to the last page, we create a new terminal instead
|
54
|
+
@gtk_notebook.signal_connect_after("switch-page") do |notebook, page, page_num|
|
55
|
+
add_new_terminal if page_num == (@gtk_notebook.n_pages - 1)
|
56
|
+
end
|
57
|
+
@gtk_notebook.set_size_request(0, Config[:terminals_height])
|
58
|
+
end
|
59
|
+
|
60
|
+
# The "window" for this object
|
61
|
+
def gtk_window
|
62
|
+
@gtk_notebook
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add a block that will be called when the user choose to expand or
|
66
|
+
# close the expander. The block takes one argument: if the expander
|
67
|
+
# is opened or closed
|
68
|
+
def add_expander_signal(&block)
|
69
|
+
@expander_signal << block
|
70
|
+
end
|
71
|
+
|
72
|
+
# Set the focus to the current terminal
|
73
|
+
def focus_terminal
|
74
|
+
page = @gtk_notebook.get_nth_page(@gtk_notebook.page)
|
75
|
+
page.has_focus = true if page
|
76
|
+
end
|
77
|
+
|
78
|
+
# Add a new terminal at the left of the tab bar.
|
79
|
+
def add_new_terminal
|
80
|
+
page_num = @gtk_notebook.n_pages - 1
|
81
|
+
@gtk_notebook.insert_page(page_num, new_terminal)
|
82
|
+
@gtk_notebook.page = page_num
|
83
|
+
end
|
84
|
+
|
85
|
+
# Deletes terminal with number *page_num*, defaults to current page number.
|
86
|
+
def delete_current_terminal
|
87
|
+
page = @gtk_notebook.get_nth_page(@gtk_notebook.page)
|
88
|
+
prev_terminal
|
89
|
+
Process.kill 'HUP', page.pid
|
90
|
+
end
|
91
|
+
|
92
|
+
# Switch the next (right) terminal, if there exists one. Otherwise start
|
93
|
+
# again with the first terminal on the left.
|
94
|
+
def next_terminal
|
95
|
+
if @gtk_notebook.page < @gtk_notebook.n_pages - 2
|
96
|
+
@gtk_notebook.next_page
|
97
|
+
else
|
98
|
+
@gtk_notebook.page = 0
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Switch the previous (left) terminal, if there exists one. Otherwise start
|
103
|
+
# with again with the last terminal on the right.
|
104
|
+
def prev_terminal
|
105
|
+
if @gtk_notebook.page > 0
|
106
|
+
@gtk_notebook.prev_page
|
107
|
+
else
|
108
|
+
@gtk_notebook.page = @gtk_notebook.n_pages - 2
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# Create a new terminal and return it
|
115
|
+
def new_terminal
|
116
|
+
# Setup of the terminal
|
117
|
+
gtk_terminal = Vte::Terminal.new
|
118
|
+
gtk_terminal.audible_bell = Config[:terminals_audible_bell]
|
119
|
+
gtk_terminal.visible_bell = Config[:terminals_visible_bell]
|
120
|
+
gtk_terminal.set_font(Config[:terminals_font])
|
121
|
+
forecolor = Gdk::Color.parse(Config[:terminals_foreground_color])
|
122
|
+
backcolor = Gdk::Color.parse(Config[:terminals_background_color])
|
123
|
+
gtk_terminal.set_colors(forecolor, backcolor, [])
|
124
|
+
|
125
|
+
# Hide and destroy the terminal when the shell exits
|
126
|
+
gtk_terminal.signal_connect("child-exited") do |terminal|
|
127
|
+
break if @gtk_notebook.destroyed?
|
128
|
+
# Select the page before
|
129
|
+
page_num = @gtk_notebook.page_num(terminal)
|
130
|
+
if page_num == 0
|
131
|
+
@gtk_notebook.page = 0
|
132
|
+
else
|
133
|
+
@gtk_notebook.page = page_num - 1
|
134
|
+
end
|
135
|
+
# Hide it and destroy it after a while
|
136
|
+
terminal.hide
|
137
|
+
Thread.new do
|
138
|
+
sleep(5)
|
139
|
+
terminal.destroy unless terminal.destroyed?
|
140
|
+
end
|
141
|
+
end
|
142
|
+
# When the title of the terminal changes, set the name of the page
|
143
|
+
gtk_terminal.signal_connect("window-title-changed") do |terminal|
|
144
|
+
@gtk_notebook.set_tab_label_text(terminal, terminal.window_title)
|
145
|
+
end
|
146
|
+
if (Config[:terminals_login_shell])
|
147
|
+
begin
|
148
|
+
require 'etc'
|
149
|
+
rescue LoadError
|
150
|
+
end
|
151
|
+
shell = (ENV["SHELL"] || Etc.getpwnam(Etc.getlogin).shell rescue nil || "/bin/sh")
|
152
|
+
gtk_terminal.pid = gtk_terminal.fork_command(shell, ["-l"])
|
153
|
+
else
|
154
|
+
gtk_terminal.pid = gtk_terminal.fork_command
|
155
|
+
end
|
156
|
+
gtk_terminal.feed_child(Config[:terminals_autoexec])
|
157
|
+
gtk_terminal.show
|
158
|
+
gtk_terminal
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
@@ -0,0 +1,29 @@
|
|
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
|
+
module VimMate
|
25
|
+
|
26
|
+
# VimMate's version
|
27
|
+
VERSION = "0.6.6"
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,150 @@
|
|
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
|
+
|
25
|
+
module VimMate
|
26
|
+
|
27
|
+
# A window that can display and send information to the
|
28
|
+
# GTK GUI of Vim (gVim)
|
29
|
+
class VimWidget
|
30
|
+
|
31
|
+
include Vim::Integration
|
32
|
+
|
33
|
+
# Create the VimWindow. You must call start after this window is visible
|
34
|
+
def initialize
|
35
|
+
# A unique Vim server name
|
36
|
+
@vim_server_name = "VimMate_#{Process.pid}"
|
37
|
+
@gtk_socket = Gtk::Socket.new
|
38
|
+
@gtk_socket.show_all
|
39
|
+
@gtk_socket.signal_connect("delete_event") do
|
40
|
+
false
|
41
|
+
end
|
42
|
+
@gtk_socket.signal_connect("destroy") do
|
43
|
+
Gtk.main_quit
|
44
|
+
end
|
45
|
+
@gtk_socket.can_focus = true
|
46
|
+
@gtk_socket.has_focus = true
|
47
|
+
@vim_started = false
|
48
|
+
@extras_sourced = false
|
49
|
+
end
|
50
|
+
|
51
|
+
# The "window" for this object
|
52
|
+
def window
|
53
|
+
@gtk_socket
|
54
|
+
end
|
55
|
+
|
56
|
+
#generate multiline functions that will be sourced on startup
|
57
|
+
def source_vimmate_extras
|
58
|
+
return if @extras_sourced
|
59
|
+
if @vim_started
|
60
|
+
source_path = File.join( File.dirname(__FILE__), '..', 'vim', 'source.vim')
|
61
|
+
remote_send "<ESC><ESC><ESC>:source #{source_path}<CR>"
|
62
|
+
@extras_sourced = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Open the specified file in Vim
|
67
|
+
def open(path, kind = :open)
|
68
|
+
start
|
69
|
+
path = path.gsub "'", "\\'"
|
70
|
+
case kind
|
71
|
+
when :split
|
72
|
+
remote_send '<ESC><ESC><ESC>:split<CR>'
|
73
|
+
when :open, :split
|
74
|
+
exec_gvim "--remote '#{path}'"
|
75
|
+
when :tab
|
76
|
+
exec_gvim "--remote-tab '#{path}'"
|
77
|
+
else
|
78
|
+
raise "Unknow open kind: #{kind}"
|
79
|
+
end
|
80
|
+
remote_send "<ESC><ESC><ESC>:buffer #{path}<CR>"
|
81
|
+
focus!
|
82
|
+
#Signal.emit_file_opened(path)
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def open_and_jump_to_line(path,lnum)
|
87
|
+
unless buffer = buffers.index(path)
|
88
|
+
open path, Config[:files_default_open_in_tabs] ? :tab_open : :open
|
89
|
+
sleep 0.5
|
90
|
+
end
|
91
|
+
send_command buffer, 'setDot', [lnum,0]
|
92
|
+
focus!
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def jump_to_line(line)
|
97
|
+
start
|
98
|
+
if line >= 0
|
99
|
+
# TODO use neatbeans
|
100
|
+
remote_send "<ESC><ESC><ESC>:#{line}<CR>"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Start Vim's window. This must be called after the window which
|
105
|
+
# will contain Vim is visible.
|
106
|
+
def start
|
107
|
+
return if @vim_started
|
108
|
+
@vim_started = true
|
109
|
+
listen
|
110
|
+
fork do
|
111
|
+
exec_gvim "--socketid #{@gtk_socket.id} -nb:localhost:#{port}:#{Password}"
|
112
|
+
end
|
113
|
+
sleep 0.5
|
114
|
+
source_vimmate_extras
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
# Set the focus to Vim
|
119
|
+
def focus!
|
120
|
+
@gtk_socket.can_focus = true
|
121
|
+
@gtk_socket.has_focus = true
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_current_buffer_path
|
125
|
+
if @vim_started
|
126
|
+
#`gvim --servername #{@vim_server_name} --remote-send ':redir! > outputfile<cr>'`
|
127
|
+
#`gvim --servername #{@vim_server_name} --remote-send ':echo getcwd()<cr>'`
|
128
|
+
#`gvim --servername #{@vim_server_name} --remote-send ':echo bufname(bufnr(""))<cr>'`
|
129
|
+
#`gvim --servername #{@vim_server_name} --remote-send ':redir END<cr>'`
|
130
|
+
|
131
|
+
get_current_buffer_number
|
132
|
+
|
133
|
+
cwd = exec_gvim "--remote-expr 'getcwd()'".chomp+'/'
|
134
|
+
if cwd
|
135
|
+
return cwd + exec_gvim(%Q~--remote-expr 'bufname(bufnr(""))'~)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_current_buffer_number
|
141
|
+
send_function('getCursor')
|
142
|
+
end
|
143
|
+
|
144
|
+
def get_all_buffer_paths
|
145
|
+
buffers[1..-1]
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe ActiveWindow::ActiveColumn do
|
4
|
+
before( :each ) do
|
5
|
+
@class = ActiveWindow::ActiveColumn
|
6
|
+
end
|
7
|
+
|
8
|
+
it "shold be a Class" do
|
9
|
+
@class.should be_a(Class)
|
10
|
+
end
|
11
|
+
|
12
|
+
shared_examples_for "any column" do
|
13
|
+
it "should be a ActiveColumn" do
|
14
|
+
@col.should be_a(ActiveWindow::ActiveColumn)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have a view" do
|
18
|
+
@col.view.should_not be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "of type String" do
|
24
|
+
before( :each ) do
|
25
|
+
@col = @class.create(0,'name', :string)
|
26
|
+
end
|
27
|
+
it_should_behave_like 'any column'
|
28
|
+
|
29
|
+
it "should be a ActiveTextColumn" do
|
30
|
+
@col.should be_a(ActiveWindow::ActiveTextColumn)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should take a String" do
|
34
|
+
@col.data_class.should == String
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,312 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe ActiveWindow::ActiveTreeStore do
|
4
|
+
before( :each ) do
|
5
|
+
@ats = ActiveWindow::ActiveTreeStore
|
6
|
+
end
|
7
|
+
it "should inherit from Gtk::TreeStore" do
|
8
|
+
@ats.should < Gtk::TreeStore
|
9
|
+
end
|
10
|
+
it "should store columns" do
|
11
|
+
@ats.column_id.should_not be_nil
|
12
|
+
@ats.column_id.should be_a(Hash)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have basic columns" do
|
16
|
+
@ats.column_id.should have_key(:visible)
|
17
|
+
@ats.column_id.should have_key(:object)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should store column index in constants" do
|
21
|
+
@ats::VISIBLE.should == 0
|
22
|
+
@ats::OBJECT.should == 1
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should provide classes for columns" do
|
26
|
+
@ats.column_classes.should == [TrueClass, Object]
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "subclassing for Person with name and age" do
|
30
|
+
before( :each ) do
|
31
|
+
class PersonTree < @ats
|
32
|
+
column :name, String
|
33
|
+
column :age, Fixnum
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
after( :each ) do
|
38
|
+
Object::send :remove_const, :PersonTree
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should still have basic columns" do
|
42
|
+
PersonTree.column_id.should have_key(:visible)
|
43
|
+
PersonTree.column_id.should have_key(:object)
|
44
|
+
end
|
45
|
+
it "should have both new columns defined" do
|
46
|
+
PersonTree.column_id.should have_key(:name)
|
47
|
+
PersonTree.column_id.should have_key(:age)
|
48
|
+
end
|
49
|
+
it "should have 4 columns" do
|
50
|
+
PersonTree.column_count.should == 4
|
51
|
+
end
|
52
|
+
it "should store column index in constants" do
|
53
|
+
PersonTree::VISIBLE.should == 0
|
54
|
+
PersonTree::OBJECT.should == 1
|
55
|
+
PersonTree::NAME.should == 2
|
56
|
+
PersonTree::AGE.should == 3
|
57
|
+
end
|
58
|
+
it "should provde classes for columns" do
|
59
|
+
PersonTree.column_classes.should == [TrueClass, Object, String, Integer]
|
60
|
+
end
|
61
|
+
it "should store column index in hash" do
|
62
|
+
PersonTree.column_id.should == {:visible => 0, :object => 1, :name => 2, :age => 3}
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "with index on name" do
|
66
|
+
before( :each ) do
|
67
|
+
PersonTree.index_by :name
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should define a method to remember People by name" do
|
71
|
+
PersonTree.public_instance_methods.should include('remember_iter_by_name')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should define a method to find People by name" do
|
75
|
+
PersonTree.public_instance_methods.should include('find_by_name')
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "added Peter, Paul and Mary" do
|
79
|
+
before( :each ) do
|
80
|
+
@person_tree = PersonTree.new
|
81
|
+
@person_tree.add :name => 'Peter', :age => 23
|
82
|
+
@person_tree.add :name => 'Paul', :age => 42
|
83
|
+
@person_tree.add :name => 'Mary', :age => 19
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should have indexed the items" do
|
87
|
+
@person_tree.index_by_name.should be_a(Hash)
|
88
|
+
@person_tree.index_by_name.should have_key('Peter')
|
89
|
+
@person_tree.index_by_name.should have_key('Paul')
|
90
|
+
@person_tree.index_by_name.should have_key('Mary')
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should index by Strings (the names)" do
|
94
|
+
@person_tree.index_by_name.keys.each do |key|
|
95
|
+
key.should be_a(String)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should index TreeRowReferences" do
|
100
|
+
@person_tree.index_by_name.values.each do |value|
|
101
|
+
value.should be_a(Gtk::TreeRowReference)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should find the row for Peter by its name" do
|
106
|
+
peter = nil
|
107
|
+
lambda { peter = @person_tree.find_by_name('Peter') }.should_not raise_error
|
108
|
+
peter.should_not be_nil
|
109
|
+
peter.should be_a(Gtk::TreeIter)
|
110
|
+
peter[2].should == 'Peter'
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
describe "instancing" do
|
118
|
+
before( :each ) do
|
119
|
+
@person_tree = PersonTree.new
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "adding a Person" do
|
123
|
+
before( :each ) do
|
124
|
+
@person = mock(:name => 'Kate', :age => 23)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should succeed" do
|
128
|
+
lambda { @person_tree.add @person }.should_not raise_error
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "subclassing twice with different columns" do
|
139
|
+
before( :each ) do
|
140
|
+
class AppleTree < @ats
|
141
|
+
column :apple_count, Fixnum
|
142
|
+
end
|
143
|
+
class LemonTree < @ats
|
144
|
+
column :lemon_count, Fixnum
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
after( :each ) do
|
149
|
+
Object::send :remove_const, :AppleTree
|
150
|
+
Object::send :remove_const, :LemonTree
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
it "should both still have basic columns" do
|
155
|
+
AppleTree.column_id[:visible].should == 0
|
156
|
+
AppleTree.column_id[:object].should == 1
|
157
|
+
LemonTree.column_id[:visible].should == 0
|
158
|
+
LemonTree.column_id[:object].should == 1
|
159
|
+
end
|
160
|
+
it "should define columns for both subclasses" do
|
161
|
+
AppleTree.column_id[:apple_count].should == 2
|
162
|
+
LemonTree.column_id[:lemon_count].should == 2
|
163
|
+
end
|
164
|
+
it "should keep the new columns to their Classes" do
|
165
|
+
AppleTree.column_id.should_not have_key(:lemon_count)
|
166
|
+
LemonTree.column_id.should_not have_key(:apple_count)
|
167
|
+
end
|
168
|
+
it "should both have 3 columns" do
|
169
|
+
AppleTree.column_count.should == 3
|
170
|
+
LemonTree.column_count.should == 3
|
171
|
+
end
|
172
|
+
it "should store first column index in constants" do
|
173
|
+
AppleTree::VISIBLE.should == 0
|
174
|
+
AppleTree::OBJECT.should == 1
|
175
|
+
AppleTree::APPLE_COUNT.should == 2
|
176
|
+
end
|
177
|
+
it "should store second column index in constants" do
|
178
|
+
LemonTree::VISIBLE.should == 0
|
179
|
+
LemonTree::OBJECT.should == 1
|
180
|
+
LemonTree::LEMON_COUNT.should == 2
|
181
|
+
end
|
182
|
+
it "should provde classes for columns" do
|
183
|
+
AppleTree.column_classes.should == [TrueClass, Object, Integer]
|
184
|
+
LemonTree.column_classes.should == [TrueClass, Object, Integer]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "Adding a column to a subclass" do
|
189
|
+
before( :each ) do
|
190
|
+
@sc = Class.new(@ats)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should return it to use it somewhere else" do
|
194
|
+
col = @sc.column(:name, :string)
|
195
|
+
col.should_not be_nil
|
196
|
+
col.should be_a(ActiveWindow::ActiveColumn)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "subclassing with a composite column with 2 subcolumns" do
|
201
|
+
before( :each ) do
|
202
|
+
class ComplexTree < @ats
|
203
|
+
composite_column "Name and Age" do |pack|
|
204
|
+
pack.add column(:name, :string)
|
205
|
+
pack.add column(:age, :integer)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
after( :each ) do
|
211
|
+
Object::send :remove_const, :ComplexTree
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should create 3 columns" do
|
215
|
+
ComplexTree.should have_at_least(3).columns
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should have 2 invisible columns" do
|
219
|
+
ComplexTree.should have_at_least(2).invisible_columns
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should have 2 data columns" do
|
223
|
+
ComplexTree.should have_at_least(2).data_columns
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should have constants for column ids" do
|
227
|
+
ComplexTree::NAME.should == 2
|
228
|
+
ComplexTree::AGE.should == 3
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should have max 1 visible column (the composite one)" do
|
232
|
+
names = ComplexTree.visible_columns.map(&:name)
|
233
|
+
names.should == ['Name and Age']
|
234
|
+
ComplexTree.should have_at_most(1).visible_columns
|
235
|
+
names.should include('Name and Age')
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should have 2 data columns (the subs)" do
|
239
|
+
ComplexTree.should have_at_least(2).data_columns
|
240
|
+
names = ComplexTree.data_columns.map(&:name)
|
241
|
+
names.should include('name')
|
242
|
+
names.should include('age')
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "creating a class filtering it" do
|
246
|
+
before( :each ) do
|
247
|
+
class FilteredComplexTree < ActiveWindow::FilteredActiveTreeStore
|
248
|
+
def iter_visible?(iter)
|
249
|
+
!( iter[ self.class.column_id[:name] ].index(filter_string) ).nil?
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
after( :each ) do
|
254
|
+
Object::send :remove_const, :FilteredComplexTree
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should take its columns from ComplexTree" do
|
258
|
+
FilteredComplexTree.columns.should == ComplexTree.columns
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should take constants for the column ids from ComplexTrr" do
|
262
|
+
FilteredComplexTree::NAME.should == 2
|
263
|
+
FilteredComplexTree::AGE.should == 3
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "instancing it with a model" do
|
267
|
+
before( :each ) do
|
268
|
+
@tree = ComplexTree.new
|
269
|
+
@filtered_tree = FilteredComplexTree.new @tree
|
270
|
+
end
|
271
|
+
|
272
|
+
describe "and filtering some data" do
|
273
|
+
before( :each ) do
|
274
|
+
@tree.add(:name => 'Niklas', :age => 27)
|
275
|
+
@tree.add(:name => 'Grandpa', :age => 99)
|
276
|
+
@filtering = lambda { @filtered_tree.filter = 'something' }
|
277
|
+
@vis_col = 0
|
278
|
+
end
|
279
|
+
it "should not break on filtering" do
|
280
|
+
@filtering.should_not raise_error
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should check iter for visibility" do
|
284
|
+
@filtered_tree.should_receive(:iter_visible?).at_least(:twice)
|
285
|
+
@filtering.call
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should hide rows that do not match" do
|
289
|
+
@filtered_tree.stub(:iter_visible?).and_return(false)
|
290
|
+
@filtering.call
|
291
|
+
@tree.each do |model, path, iter|
|
292
|
+
iter[@vis_col].should be_false
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should show rows that do match" do
|
297
|
+
@filtered_tree.stub(:iter_visible?).and_return(true)
|
298
|
+
@filtering.call
|
299
|
+
@filtered_tree.found_count.should >= 2
|
300
|
+
@tree.each do |model, path, iter|
|
301
|
+
iter[@vis_col].should be_true
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|