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.
Files changed (85) hide show
  1. data/.autotest +10 -0
  2. data/CHANGELOG +108 -0
  3. data/COPYING +20 -0
  4. data/README +221 -0
  5. data/Rakefile +97 -0
  6. data/TODO +21 -0
  7. data/bin/vimmate +95 -0
  8. data/config/environment.rb +33 -0
  9. data/controllers/file_filter_controller.rb +85 -0
  10. data/controllers/file_popup_menu_controller.rb +40 -0
  11. data/controllers/vim_controller.rb +28 -0
  12. data/controllers/vim_mate_controller.rb +76 -0
  13. data/images/file.png +0 -0
  14. data/images/file_green.png +0 -0
  15. data/images/file_orange.png +0 -0
  16. data/images/file_red.png +0 -0
  17. data/images/folder.png +0 -0
  18. data/images/folder_green.png +0 -0
  19. data/images/folder_orange.png +0 -0
  20. data/images/folder_red.png +0 -0
  21. data/images/processing.png +0 -0
  22. data/images/svn_added.png +0 -0
  23. data/images/svn_conflict.png +0 -0
  24. data/images/svn_deleted.png +0 -0
  25. data/images/svn_locked.png +0 -0
  26. data/images/svn_modified.png +0 -0
  27. data/images/svn_normal.png +0 -0
  28. data/images/svn_readonly.png +0 -0
  29. data/images/vimmate16.png +0 -0
  30. data/images/vimmate32.png +0 -0
  31. data/images/vimmate48.png +0 -0
  32. data/lib/active_window.rb +8 -0
  33. data/lib/active_window/active_column.rb +218 -0
  34. data/lib/active_window/active_tree_store.rb +13 -0
  35. data/lib/active_window/active_tree_store/columns.rb +88 -0
  36. data/lib/active_window/active_tree_store/extentions.rb +81 -0
  37. data/lib/active_window/active_tree_store/index.rb +53 -0
  38. data/lib/active_window/application.rb +133 -0
  39. data/lib/active_window/controller.rb +58 -0
  40. data/lib/active_window/dot_file.rb +29 -0
  41. data/lib/active_window/filtered_active_tree_store.rb +114 -0
  42. data/lib/active_window/listed_item.rb +127 -0
  43. data/lib/active_window/signal.rb +46 -0
  44. data/lib/config_window.rb +90 -0
  45. data/lib/file_tree_store.rb +73 -0
  46. data/lib/filtered_file_tree_store.rb +34 -0
  47. data/lib/gtk_thread_helper.rb +73 -0
  48. data/lib/listed_directory.rb +43 -0
  49. data/lib/listed_file.rb +67 -0
  50. data/lib/try.rb +9 -0
  51. data/lib/vim/buffers.rb +18 -0
  52. data/lib/vim/integration.rb +36 -0
  53. data/lib/vim/netbeans.rb +156 -0
  54. data/lib/vim/source.vim +18 -0
  55. data/lib/vim_mate/config.rb +132 -0
  56. data/lib/vim_mate/dummy_window.rb +14 -0
  57. data/lib/vim_mate/files_menu.rb +110 -0
  58. data/lib/vim_mate/icons.rb +156 -0
  59. data/lib/vim_mate/nice_singleton.rb +53 -0
  60. data/lib/vim_mate/plugins.rb +6 -0
  61. data/lib/vim_mate/plugins/inotify/init.rb +4 -0
  62. data/lib/vim_mate/plugins/inotify/lib/INotify.rb +206 -0
  63. data/lib/vim_mate/plugins/inotify/lib/directory.rb +58 -0
  64. data/lib/vim_mate/plugins/subversion/init.rb +7 -0
  65. data/lib/vim_mate/plugins/subversion/lib/file.rb +59 -0
  66. data/lib/vim_mate/plugins/subversion/lib/menu.rb +96 -0
  67. data/lib/vim_mate/plugins/subversion/lib/subversion.rb +157 -0
  68. data/lib/vim_mate/requirer.rb +68 -0
  69. data/lib/vim_mate/search_window.rb +227 -0
  70. data/lib/vim_mate/tags_window.rb +167 -0
  71. data/lib/vim_mate/terminals_window.rb +163 -0
  72. data/lib/vim_mate/version.rb +29 -0
  73. data/lib/vim_mate/vim_widget.rb +150 -0
  74. data/spec/active_window/active_column_spec.rb +41 -0
  75. data/spec/active_window/active_tree_store_spec.rb +312 -0
  76. data/spec/active_window/controller_spec.rb +6 -0
  77. data/spec/lib/file_tree_store_spec.rb +40 -0
  78. data/spec/lib/listed_directory_spec.rb +26 -0
  79. data/spec/lib/listed_file_spec.rb +53 -0
  80. data/spec/nice_singleton_spec.rb +23 -0
  81. data/spec/spec.opts +6 -0
  82. data/spec/spec_helper.rb +10 -0
  83. data/views/vim_mate.glade +308 -0
  84. data/vimmate.gemspec +131 -0
  85. 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
+