Class: VimMate::SearchWindow

Inherits:
Object
  • Object
show all
Defined in:
lib/vimmatelib/search_window.rb

Overview

A window that can be used to search for files

Constant Summary collapse

NAME =

Column for the file name

0
PATH =

Column for the full path of the file

1

Instance Method Summary collapse

Constructor Details

#initialize(file_tree) ⇒ SearchWindow

Create the SearchWindow



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/vimmatelib/search_window.rb', line 38

def initialize(file_tree)
  @open_signal = Set.new
  @menu_signal = Set.new
  @file_tree = file_tree
  @filter = /.*/
  
  # File name, Path
  @gtk_list_model = Gtk::ListStore.new(String, String)
  @gtk_list_model.set_sort_column_id(PATH)
  @gtk_filtered_list_model = Gtk::TreeModelFilter.new(@gtk_list_model)
  @gtk_filtered_list_model.set_visible_func do |model, iter|
    if iter[NAME] =~ @filter
      true
    else
      false
    end
  end

  @gtk_list_view = Gtk::TreeView.new(@gtk_filtered_list_model)
  @gtk_list_view.selection.mode = Gtk::SELECTION_SINGLE
  @gtk_list_view.headers_visible = false
  
  # Double-click, Enter, Space: Signal to open the file
  @gtk_list_view.signal_connect("row-activated") do |view, path, column|
    path = @gtk_filtered_list_model.get_iter(path)[PATH]
    @open_signal.each do |signal|
      signal.call(path,
                  Config[:files_default_open_in_tabs] ? :tab_open : :open)
    end
    @gtk_entry.text = ""
  end

  # Left-click: Select and Signal to open the menu
  @gtk_list_view.signal_connect("button_press_event") do |widget, event|
    if event.kind_of? Gdk::EventButton and event.button == 3
      path = @gtk_list_view.get_path_at_pos(event.x, event.y)
      @gtk_list_view.selection.select_path(path[NAME]) if path

      selected = @gtk_list_view.selection.selected
      if selected
        @menu_signal.each do |signal|
          signal.call(selected[PATH])
        end
      end
    end
  end

  # Same thing as Left-click, but with the keyboard
  @gtk_list_view.signal_connect("popup_menu") do
    selected = @gtk_list_view.selection.selected
    if selected
      @menu_signal.each do |signal|
        signal.call(selected[PATH])
      end
    end
  end

  # Add the columns
  column = Gtk::TreeViewColumn.new
  column.title = "Files"
  
  # File name
  text_cell_renderer = Gtk::CellRendererText.new
  if Config[:files_use_ellipsis]
    text_cell_renderer.ellipsize = Pango::Layout::EllipsizeMode::MIDDLE
  end
  column.pack_start(text_cell_renderer, true)
  column.set_attributes(text_cell_renderer, :text => NAME)
  
  @gtk_list_view.append_column(column)

  # Put the tree view in a scroll window
  @gtk_scrolled_window = Gtk::ScrolledWindow.new
  @gtk_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC,
                                  Gtk::POLICY_AUTOMATIC)
  @gtk_scrolled_window.add(@gtk_list_view)

  # Create a label to show the path of the file
  gtk_label = Gtk::Label.new
  gtk_label.ellipsize = Pango::Layout::EllipsizeMode::START

  # When a selection is changed in the list view, we change the label
  # to show the path of the file and which characters matches in the
  # file name
  @gtk_list_view.selection.signal_connect("changed") do
    gtk_label.markup = ""
    # Nothing to do if there are no selections or if the entry
    # is empty
    next if (selected_row = @gtk_list_view.selection.selected).nil?
    next if @gtk_entry.text.empty?
    # Build a regexp to add markup information on the file name
    match = []
    Regexp.escape(@gtk_entry.text).gsub(/\\.|./) {|c| match << c}
    match_regexp = Regexp.new(match.join("|"), Config[:files_search_ignore_case])
    file_name_markup = selected_row[NAME].gsub(match_regexp) do |c|
      "<b><i>#{c}</i></b>"
    end
    # Join the path and the file name with the markup
    gtk_label.markup = File.join(File.dirname(selected_row[PATH]), file_name_markup)
  end
  
  # Build a box to contain the entry for the filter
  gtk_filter_box = Gtk::HBox.new
  gtk_filter_box.spacing = 10
  gtk_filter_box.border_width = 10
  gtk_filter_box.add(@gtk_entry = Gtk::Entry.new)

  # When the filter changes, create a new regex to filter the file names
  @gtk_entry.signal_connect("changed") do
    @filter = Regexp.new(".*" + Regexp.escape(@gtk_entry.text).gsub(/\\.|./) {|c| "#{c}.*"},
                         Config[:files_search_ignore_case])
    # Unselect everything, filter and reselect the first row
    @gtk_list_view.selection.unselect_all
    @gtk_filtered_list_model.refilter
    if first_row = @gtk_filtered_list_model.iter_first
      @gtk_list_view.selection.select_iter(first_row)
    end
    # Scroll at the top
    @gtk_scrolled_window.vscrollbar.value = @gtk_scrolled_window.vscrollbar.adjustment.lower
  end
  
  # When we press Enter in the entry, open the first file of the list
  @gtk_entry.signal_connect("activate") do
    next if (first_row = @gtk_filtered_list_model.iter_first).nil?
    @open_signal.each do |signal|
      signal.call(first_row[PATH],
                  Config[:files_default_open_in_tabs] ? :tab_open : :open)
    end
    @gtk_entry.text = ""
  end

  # Add the components in a box
  @gtk_container_box = Gtk::VBox.new
  @gtk_container_box.pack_start(gtk_filter_box, false, false)
  @gtk_container_box.pack_start(@gtk_scrolled_window, true, true)      
  @gtk_container_box.pack_start(gtk_label, false, false)

  # Process file tree event
  @file_tree.add_refresh_signal do |method, file|
    next if file.instance_of? ListedDirectory 
    case method
    when :add
      # Add the new file
      new_row = @gtk_list_model.append
      new_row[NAME] = file.name
      new_row[PATH] = file.path
    when :remove
      # A file is removed. Find it and remove it
      to_remove = []
      @gtk_list_model.each do |model,path,iter|
        if iter[PATH] == file.path
          to_remove << Gtk::TreeRowReference.new(model, path)
          break
        end
      end
      to_remove.each do |element|
        @gtk_list_model.remove(@gtk_list_model.get_iter(element.path))
      end
    end
  end
end

Instance Method Details

#add_menu_signal(&block) ⇒ Object

Add a block that will be called when the user choose to open the menu. The block takes one argument: the path to the file to open.



219
220
221
# File 'lib/vimmatelib/search_window.rb', line 219

def add_menu_signal(&block)
  @menu_signal << block
end

#add_open_signal(&block) ⇒ Object

Add a block that will be called when the user choose to open a file The block take two argument: the path to the file to open, and a symbol to indicate the kind: :open, :split_open, :tab_open



213
214
215
# File 'lib/vimmatelib/search_window.rb', line 213

def add_open_signal(&block)
  @open_signal << block
end

#focus_file_searchObject

Set the focus to the entry field in the file search list



206
207
208
# File 'lib/vimmatelib/search_window.rb', line 206

def focus_file_search
  @gtk_entry.has_focus = true if @gtk_entry
end

#gtk_windowObject

The “window” for this object



201
202
203
# File 'lib/vimmatelib/search_window.rb', line 201

def gtk_window
  @gtk_container_box
end