filetree.cc 28.3 KB
Newer Older
1 2
/*
 * Copyright (c) 2002-2007  Daniel Elstner  <daniel.kitta@gmail.com>
3
 *
4
 * This file is part of regexxer.
5
 *
6 7 8 9
 * regexxer is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
10
 *
11
 * regexxer is distributed in the hope that it will be useful,
12 13 14 15 16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
17 18
 * along with regexxer; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 20 21
 */

#include "filetree.h"
22
#include "filetreeprivate.h"
23
#include "globalstrings.h"
24
#include "stringutils.h"
25
#include "translation.h"
26
#include "settings.h"
27

28
#include <glibmm.h>
29
#include <gtkmm/stock.h>
30

31 32
#include <config.h>

33 34 35
namespace Regexxer
{

36 37
using namespace Regexxer::FileTreePrivate;

38 39 40 41
/**** Regexxer::FileTree ***************************************************/

FileTree::FileTree()
:
42
  treestore_      (Gtk::TreeStore::create(FileTreeColumns::instance())),
43
  color_modified_ ("#DF421E"), // accent red
44
  sum_matches_    (0)
45
{
46
  using namespace Gtk;
47
  using sigc::mem_fun;
48

49
  set_model(treestore_);
50
  const FileTreeColumns& model_columns = FileTreeColumns::instance();
51

52
  treestore_->set_default_sort_func(&default_sort_func);
53
  treestore_->set_sort_func(model_columns.collatekey, &collatekey_sort_func);
54
  treestore_->set_sort_column(TreeStore::DEFAULT_SORT_COLUMN_ID, SORT_ASCENDING);
55

56 57
  treestore_->signal_rows_reordered()
      .connect(mem_fun(*this, &FileTree::on_treestore_rows_reordered));
58

59
  {
60
    Column *const column = new Column(_("File"));
61 62 63 64 65 66 67 68 69
    append_column(*manage(column));

    CellRendererPixbuf *const cell_icon = new CellRendererPixbuf();
    column->pack_start(*manage(cell_icon), false);

    CellRendererText *const cell_filename = new CellRendererText();
    column->pack_start(*manage(cell_filename));

    column->add_attribute(cell_filename->property_text(), model_columns.filename);
70 71
    column->set_cell_data_func(*cell_icon,     mem_fun(*this, &FileTree::icon_cell_data_func));
    column->set_cell_data_func(*cell_filename, mem_fun(*this, &FileTree::text_cell_data_func));
72

73
    column->set_resizable(true);
74
    column->set_expand(true);
75

76
    column->set_sort_column(model_columns.collatekey);
77 78
  }
  {
79
    Column *const column = new Column(_("#"));
80 81 82 83
    append_column(*manage(column));

    CellRendererText *const cell_matchcount = new CellRendererText();
    column->pack_start(*manage(cell_matchcount));
84

85
    column->add_attribute(cell_matchcount->property_text(), model_columns.matchcount);
86
    column->set_cell_data_func(*cell_matchcount, mem_fun(*this, &FileTree::text_cell_data_func));
87 88 89

    column->set_alignment(1.0);
    cell_matchcount->property_xalign() = 1.0;
90

91
    column->set_sort_column(model_columns.matchcount);
92
  }
93

94
  set_search_column(model_columns.filename);
95

96
  const Glib::RefPtr<TreeSelection> selection = get_selection();
97 98

  selection->set_select_function(&FileTree::select_func);
99
  selection->signal_changed().connect(mem_fun(*this, &FileTree::on_selection_changed));
100

101
  Settings::instance()->signal_changed().connect(mem_fun(*this, &FileTree::on_conf_value_changed));
102 103 104 105 106
}

FileTree::~FileTree()
{}

107
void FileTree::find_files(const std::string& dirname, const Glib::RefPtr<Glib::Regex>& pattern,
108 109 110 111
                          bool recursive, bool hidden)
{
  FindData find_data (pattern, recursive, hidden);

112
  const bool modified_count_changed = (toplevel_.modified_count != 0);
113 114 115

  treestore_->clear();

116 117 118
  toplevel_.file_count     = 0;
  toplevel_.modified_count = 0;
  sum_matches_ = 0;
119 120 121 122

  signal_bound_state_changed(); // emit
  signal_match_count_changed(); // emit

123
  if (modified_count_changed)
124 125
    signal_modified_count_changed(); // emit

126
  find_recursively(dirname, find_data);
127

128 129 130 131 132
  // Work around a strange misbehavior: the tree is kept sorted while the
  // file search is in progress, which causes the scroll offset to change
  // slightly.  This in turn confuses TreeView::set_cursor() -- the first
  // call after the tree was completely filled just doesn't scroll.
  if (toplevel_.file_count > 0)
133
    scroll_to_row(Gtk::TreeModel::Path(1u, 0));
134

135 136
  signal_bound_state_changed(); // emit

137
  if (!find_data.error_list->empty())
138 139 140 141 142
    throw Error(find_data.error_list);
}

int FileTree::get_file_count() const
{
143
  g_return_val_if_fail(toplevel_.file_count >= 0, 0);
144
  return toplevel_.file_count;
145 146 147 148
}

void FileTree::save_current_file()
{
149
  if (const Gtk::TreeModel::iterator selected = get_selection()->get_selected())
150
  {
151
    Util::SharedPtr<MessageList> error_list (new MessageList());
152

153
    {
154
      Util::ScopedBlock block (conn_modified_changed_);
155
      save_file_at_iter(selected, error_list);
156
    }
157

158
    if (!error_list->empty())
159 160 161 162 163 164
      throw Error(error_list);
  }
}

void FileTree::save_all_files()
{
165
  Util::SharedPtr<MessageList> error_list (new MessageList());
166 167

  {
168
    Util::ScopedBlock block (conn_modified_changed_);
169 170 171

    treestore_->foreach_iter(sigc::bind(
        sigc::mem_fun(*this, &FileTree::save_file_at_iter),
172
        sigc::ref(error_list)));
173 174
  }

175
  if (!error_list->empty())
176 177 178 179 180
    throw Error(error_list);
}

void FileTree::select_first_file()
{
181
  if (sum_matches_ > 0)
182 183 184 185 186
    expand_and_select(path_match_first_);
}

bool FileTree::select_next_file(bool move_forward)
{
187
  if (Gtk::TreeModel::iterator iter = get_selection()->get_selected())
188
  {
189
    Gtk::TreeModel::Path collapse;
190

191
    if ((move_forward) ? next_match_file(iter, &collapse) : prev_match_file(iter, &collapse))
192
    {
193 194
      if (!collapse.empty())
        collapse_row(collapse);
195

196
      expand_and_select(Gtk::TreeModel::Path(iter));
197
      return true;
198 199 200 201 202 203 204 205 206 207
    }
  }

  return false;
}

BoundState FileTree::get_bound_state()
{
  BoundState bound = BOUND_FIRST | BOUND_LAST;

208
  if (sum_matches_ > 0)
209
  {
210
    if (const Gtk::TreeModel::iterator iter = get_selection()->get_selected())
211
    {
212
      Gtk::TreeModel::Path path (iter);
213

214
      if (path > path_match_first_)
215 216
        bound &= ~BOUND_FIRST;

217
      if (path < path_match_last_)
218 219 220 221 222 223 224
        bound &= ~BOUND_LAST;
    }
  }

  return bound;
}

225
void FileTree::find_matches(const Glib::RefPtr<Glib::Regex>& pattern, bool multiple)
226 227
{
  {
228
    Util::ScopedBlock  block_conn (conn_match_count_);
229
    ScopedBlockSorting block_sort (*this);
230
    FindMatchesData    find_data  (pattern, multiple);
231

232 233
    treestore_->foreach(sigc::bind(
        sigc::mem_fun(*this, &FileTree::find_matches_at_path_iter),
234
        sigc::ref(find_data)));
235 236 237 238 239 240 241
  }

  signal_bound_state_changed(); // emit
}

long FileTree::get_match_count() const
{
242
  g_return_val_if_fail(sum_matches_ >= 0, 0);
243 244 245 246 247 248
  return sum_matches_;
}

void FileTree::replace_all_matches(const Glib::ustring& substitution)
{
  {
249 250
    Util::ScopedBlock block_match_count      (conn_match_count_);
    Util::ScopedBlock block_modified_changed (conn_modified_changed_);
251
    Util::ScopedBlock block_undo_stack_push  (conn_undo_stack_push_);
252
    ScopedBlockSorting block_sort   (*this);
253
    ReplaceMatchesData replace_data (*this, substitution);
254

255 256
    treestore_->foreach(sigc::bind(
        sigc::mem_fun(*this, &FileTree::replace_matches_at_path_iter),
257
        sigc::ref(replace_data)));
258

259 260 261
    // Adjust the boundary range if the operation has been interrupted.
    if (sum_matches_ > 0)
    {
262
      Gtk::TreeModel::iterator first = treestore_->get_iter(path_match_first_);
263

264
      if ((*first)[FileTreeColumns::instance().matchcount] == 0 && next_match_file(first))
265 266 267
        path_match_first_ = first;
    }

268
    signal_undo_stack_push(replace_data.undo_stack); // emit
269 270 271 272 273 274 275
  }

  signal_bound_state_changed(); // emit
}

int FileTree::get_modified_count() const
{
276
  g_return_val_if_fail(toplevel_.modified_count >= 0, 0);
277
  return toplevel_.modified_count;
278 279 280 281
}

/**** Regexxer::FileTree -- protected **************************************/

282
void FileTree::on_style_updated()
283
{
284 285 286
  pixbuf_directory_   = render_icon_pixbuf(Gtk::Stock::DIRECTORY,     Gtk::ICON_SIZE_MENU);
  pixbuf_file_        = render_icon_pixbuf(Gtk::Stock::FILE,          Gtk::ICON_SIZE_MENU);
  pixbuf_load_failed_ = render_icon_pixbuf(Gtk::Stock::MISSING_IMAGE, Gtk::ICON_SIZE_MENU);
287

288
  Gdk::RGBA rgba = get_style_context()->get_color(Gtk::STATE_FLAG_INSENSITIVE);
289
  color_load_failed_.set_rgba(rgba.get_red(), rgba.get_green(), rgba.get_blue());
290

291
  Gtk::TreeView::on_style_updated();
292 293 294 295
}

/**** Regexxer::FileTree -- private ****************************************/

296 297 298
void FileTree::icon_cell_data_func(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter)
{
  Gtk::CellRendererPixbuf& renderer = dynamic_cast<Gtk::CellRendererPixbuf&>(*cell);
299
  const FileInfoBasePtr    infobase = (*iter)[FileTreeColumns::instance().fileinfo];
300

301
  if (const FileInfoPtr fileinfo = shared_dynamic_cast<FileInfo>(infobase))
302 303 304
  {
    renderer.property_pixbuf() = (fileinfo->load_failed) ? pixbuf_load_failed_ : pixbuf_file_;
  }
305
  else if (shared_dynamic_cast<DirInfo>(infobase))
306 307 308 309 310 311 312 313 314 315
  {
    renderer.property_pixbuf() = pixbuf_directory_;
  }
  else
  {
    renderer.property_pixbuf().reset_value();
  }
}

void FileTree::text_cell_data_func(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter)
316
{
317
  Gtk::CellRendererText& renderer = dynamic_cast<Gtk::CellRendererText&>(*cell);
318
  const FileInfoBasePtr  infobase = (*iter)[FileTreeColumns::instance().fileinfo];
319

320
  const Gdk::RGBA* color = 0;
321

322
  if (const FileInfoPtr fileinfo = shared_dynamic_cast<FileInfo>(infobase))
323
  {
324
    if (fileinfo->load_failed)
325
      color = &color_load_failed_;
326
    else if (fileinfo->buffer && fileinfo->buffer->get_modified())
327
      color = &color_modified_;
328
  }
329
  else if (const DirInfoPtr dirinfo = shared_dynamic_cast<DirInfo>(infobase))
330
  {
331
    if (dirinfo->modified_count > 0)
332
      color = &color_modified_;
333
  }
334

335
  if (color)
336
    renderer.property_foreground_rgba() = *color;
337
  else
338
    renderer.property_foreground_rgba().reset_value();
339 340 341 342 343

  if (color == &color_modified_)
    renderer.property_style() = Pango::STYLE_OBLIQUE;
  else
    renderer.property_style().reset_value();
344 345 346
}

// static
347 348
bool FileTree::select_func(const Glib::RefPtr<Gtk::TreeModel>& model,
                           const Gtk::TreeModel::Path& path, bool)
349
{
daniel_e's avatar
daniel_e committed
350
  // Don't allow selection of directory nodes.
351
  return (get_fileinfo_from_iter(model->get_iter(path)) != 0);
352 353
}

daniel_e's avatar
daniel_e committed
354
void FileTree::find_recursively(const std::string& dirname, FindData& find_data)
355 356 357
{
  using namespace Glib;

358
  try
359
  {
360 361
    int file_count = 0;
    Dir dir (dirname);
362

363
    for (Dir::iterator pos = dir.begin(); pos != dir.end(); ++pos)
364
    {
365 366
      if (signal_pulse()) // emit
        break;
367

368
      const std::string filename = *pos;
369

370
      if (!find_data.hidden && *filename.begin() == '.')
371
        continue;
372

373
      const std::string fullname = build_filename(dirname, filename);
374

375 376
      if (file_test(fullname, FILE_TEST_IS_SYMLINK))
        continue; // ignore symbolic links
377

378
      if (find_data.recursive && file_test(fullname, FILE_TEST_IS_DIR))
379
      {
380 381 382 383
        // Put the directory name on the stack instead of creating a new node
        // immediately.  The corresponding node will be created on demand if
        // there's actually a matching file in the directory or one of its
        // subdirectories.
384
        ScopedPushDir pushdir (find_data.dirstack, fullname);
385
        find_recursively(fullname, find_data); // recurse
386
      }
387
      else if (file_test(fullname, FILE_TEST_IS_REGULAR))
388
      {
389
        const ustring basename = Glib::filename_display_basename(fullname);
390

391
        if (find_data.pattern->match(basename))
392
        {
393
          find_add_file(basename, fullname, find_data);
394 395
          ++file_count;
        }
396
      }
397 398 399 400 401 402 403 404 405 406
    }

    find_increment_file_count(find_data, file_count);
  }
  catch (const FileError& error)
  {
    // Collect errors but don't interrupt the search.
    find_data.error_list->push_back(error.what());
  }
}
407

408 409 410 411 412 413 414 415
void FileTree::find_add_file(const Glib::ustring& basename, const std::string& fullname,
                             FindData& find_data)
{
  // Build the collate key with a leading '1' so that directories always
  // come first (they have a leading '0').  This is simpler and faster
  // than explicitely checking for directories in the sort function.
  std::string collate_key (1, '1');
  collate_key += basename.collate_key();
416

417 418
  const FileInfoBasePtr fileinfo (new FileInfo(fullname));

419
  Gtk::TreeModel::Row row;
420

421 422 423 424 425 426 427 428 429 430
  if (find_data.dirstack.empty())
  {
    row = *treestore_->prepend(); // new toplevel node
  }
  else
  {
    if (!find_data.dirstack.back().second)
      find_fill_dirstack(find_data); // build all directory nodes in the stack

    row = *treestore_->prepend(find_data.dirstack.back().second->children());
431 432
  }

433 434 435 436
  const FileTreeColumns& columns = FileTreeColumns::instance();

  row[columns.filename]   = basename;
  row[columns.collatekey] = collate_key;
437
  row[columns.fileinfo]   = fileinfo;
438 439 440 441
}

void FileTree::find_fill_dirstack(FindData& find_data)
{
442
  const FileTreeColumns& columns = FileTreeColumns::instance();
443 444 445 446

  const DirStack::iterator pend  = find_data.dirstack.end();
  DirStack::iterator       pprev = pend;

447
  for (DirStack::iterator pdir = find_data.dirstack.begin(); pdir != pend; pprev = pdir++)
448
  {
449
    if (pdir->second) // node already created
450 451
      continue;

452
    const Glib::ustring dirname = Glib::filename_display_basename(pdir->first);
453

454 455 456 457 458 459
    // Build the collate key with a leading '0' so that directories always
    // come first.  This is simpler and faster than explicitely checking for
    // directories in the sort function.
    std::string collate_key (1, '0');
    collate_key += dirname.collate_key();

460 461
    const FileInfoBasePtr dirinfo (new DirInfo());

462
    if (pprev == pend)
463 464 465 466
      pdir->second = treestore_->prepend(); // new toplevel node
    else
      pdir->second = treestore_->prepend(pprev->second->children());

467
    Gtk::TreeModel::Row row = *pdir->second;
468 469

    row[columns.filename]   = dirname;
470
    row[columns.collatekey] = collate_key;
471
    row[columns.fileinfo]   = dirinfo;
472 473 474 475 476
  }
}

void FileTree::find_increment_file_count(FindData& find_data, int file_count)
{
477
  if (file_count <= 0)
478 479
    return;

480
  const FileTreeColumns& columns = FileTreeColumns::instance();
481

482 483
  const DirStack::iterator pbegin = find_data.dirstack.begin();
  DirStack::iterator       pdir   = find_data.dirstack.end();
484

485
  while (pdir != pbegin)
486
  {
487
    const FileInfoBasePtr base = (*(--pdir)->second)[columns.fileinfo];
488
    shared_polymorphic_cast<DirInfo>(base)->file_count += file_count;
489 490
  }

491
  toplevel_.file_count += file_count;
492 493 494 495
  signal_file_count_changed(); // emit
}

bool FileTree::save_file_at_iter(const Gtk::TreeModel::iterator& iter,
496
                                 const Util::SharedPtr<MessageList>& error_list)
497
{
498
  const FileInfoPtr fileinfo = get_fileinfo_from_iter(iter);
499

500
  if (fileinfo && fileinfo->buffer && fileinfo->buffer->get_modified())
501 502 503 504 505
  {
    try
    {
      save_file(fileinfo);
    }
506
    catch (const Glib::Error& error)
507
    {
508
      error_list->push_back(Util::compose(_("Failed to save file \342\200\234%1\342\200\235: %2"),
509
                                          Glib::filename_display_basename(fileinfo->fullname),
510
                                          error.what()));
511 512
    }

513
    if (!fileinfo->buffer->get_modified())
514
      propagate_modified_change(iter, false);
515

516
    if (fileinfo != last_selected_ && fileinfo->buffer->is_freeable())
517
      Glib::RefPtr<FileBuffer>().swap(fileinfo->buffer); // reduce memory footprint
518 519 520 521 522
  }

  return false;
}

523 524
bool FileTree::find_matches_at_path_iter(const Gtk::TreeModel::Path& path,
                                         const Gtk::TreeModel::iterator& iter,
525
                                         FindMatchesData& find_data)
526
{
527
  if (signal_pulse()) // emit
528 529
    return true;

530
  if (const FileInfoPtr fileinfo = get_fileinfo_from_iter(iter))
531
  {
532
    if (!fileinfo->buffer)
533
      load_file_with_fallback(iter, fileinfo);
534

535
    if (fileinfo->load_failed)
536 537 538 539 540
      return false; // continue

    const Glib::RefPtr<FileBuffer> buffer = fileinfo->buffer;
    g_assert(buffer);

541
    Util::ScopedConnection conn (buffer->signal_pulse.connect(signal_pulse.make_slot()));
542 543

    const int old_match_count = buffer->get_match_count();
544 545 546 547 548

    // Optimize the common case and construct the feedback slot only if there
    // are actually any handlers connected to the signal.  find_matches() can
    // then check whether the slot is empty to avoid providing arguments that
    // are never going to be used.
549 550 551 552
    const int new_match_count =
        buffer->find_matches(find_data.pattern, find_data.multiple, (signal_feedback.empty())
                             ? sigc::slot<void, int, const Glib::ustring&>()
                             : sigc::bind(signal_feedback.make_slot(), fileinfo));
553

554
    if (new_match_count > 0)
555
    {
556
      if (!find_data.path_match_first_set)
557
      {
558
        find_data.path_match_first_set = true;
559
        path_match_first_ = path;
560
      }
561

562
      path_match_last_ = path;
563
    }
564

565
    if (new_match_count != old_match_count)
566
      propagate_match_count_change(iter, new_match_count - old_match_count);
567

568
    if (fileinfo != last_selected_ && buffer->is_freeable())
569
      Glib::RefPtr<FileBuffer>().swap(fileinfo->buffer); // reduce memory footprint
570 571 572 573 574
  }

  return false;
}

575 576
bool FileTree::replace_matches_at_path_iter(const Gtk::TreeModel::Path& path,
                                            const Gtk::TreeModel::iterator& iter,
577
                                            ReplaceMatchesData& replace_data)
578
{
579
  if (signal_pulse()) // emit
580
    return true;
581

582
  const FileInfoPtr fileinfo = get_fileinfo_from_iter(iter);
583

584
  if (fileinfo && fileinfo->buffer)
585
  {
586
    const Glib::RefPtr<FileBuffer> buffer = fileinfo->buffer;
587

588
    const int match_count = buffer->get_match_count();
589

590
    if (match_count > 0)
591
    {
592
      path_match_first_ = path;
593

594
      if (fileinfo != last_selected_)
595
        replace_data.row_reference.reset(new TreeRowRef(treestore_, path));
596
      else
597
        replace_data.row_reference = last_selected_rowref_;
598

599
      const bool was_modified = buffer->get_modified();
600

601
      {
602 603 604 605
        // Redirect the buffer's signal_undo_stack_push() in order to create
        // a single user action object for all replacements in all buffers.
        // Note that the caller must block conn_undo_stack_push_ to avoid
        // double notification.
606 607
        Util::ScopedConnection conn1 (buffer->signal_undo_stack_push
                                        .connect(replace_data.slot_undo_stack_push));
608
        Util::ScopedConnection conn2 (buffer->signal_pulse.connect(signal_pulse.make_slot()));
609

610
        buffer->replace_all_matches(replace_data.substitution);
611
      }
612

613
      const bool is_modified = buffer->get_modified();
614

615
      if (was_modified != is_modified)
616
        propagate_modified_change(iter, is_modified);
617

618
      propagate_match_count_change(iter, buffer->get_match_count() - match_count);
619 620 621
    }
  }

622
  return false;
623 624
}

625
void FileTree::expand_and_select(const Gtk::TreeModel::Path& path)
626
{
627
  expand_to_path(path);
628
  set_cursor(path);
629 630
}

631 632
void FileTree::on_treestore_rows_reordered(const Gtk::TreeModel::Path& path,
                                           const Gtk::TreeModel::iterator& iter, int*)
633
{
634
  if (sum_matches_ > 0)
635
  {
636 637
    const FileTreeColumns& columns = FileTreeColumns::instance();
    bool bounds_changed = false;
638

639
    if (path.is_ancestor(path_match_first_))
640
    {
641
      Gtk::TreeModel::iterator first = iter->children().begin();
642

643 644
      while (first->children() && (*first)[columns.matchcount] > 0)
        first = first->children().begin();
645

646 647 648 649 650
      if ((*first)[columns.matchcount] == 0)
      {
        const bool found_first = next_match_file(first);
        g_return_if_fail(found_first);
      }
651

652 653
      path_match_first_ = first;
      bounds_changed = true;
654 655
    }

656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
    if (path.is_ancestor(path_match_last_))
    {
      Gtk::TreeModel::iterator last = iter->children()[iter->children().size() - 1];

      while (last->children() && (*last)[columns.matchcount] > 0)
        last = last->children()[last->children().size() - 1];

      if ((*last)[columns.matchcount] == 0)
      {
        const bool found_last = prev_match_file(last);
        g_return_if_fail(found_last);
      }

      path_match_last_ = last;
      bounds_changed = true;
    }
672

673 674
    if (bounds_changed)
      signal_bound_state_changed(); // emit
675 676 677
  }
}

678 679
void FileTree::on_selection_changed()
{
680
  last_selected_rowref_.reset();
681

682
  conn_match_count_     .disconnect();
683
  conn_modified_changed_.disconnect();
684
  conn_undo_stack_push_ .disconnect();
685

686 687 688 689
  FileInfoPtr fileinfo;
  int file_index = 0;

  if (const Gtk::TreeModel::iterator iter = get_selection()->get_selected())
690
  {
691
    const FileInfoBasePtr base = (*iter)[FileTreeColumns::instance().fileinfo];
692

693
    fileinfo   = shared_polymorphic_cast<FileInfo>(base);
694 695
    file_index = calculate_file_index(iter) + 1;

696
    if (!fileinfo->buffer)
697
      load_file_with_fallback(iter, fileinfo);
698

699
    if (!fileinfo->load_failed)
700 701
    {
      conn_match_count_ = fileinfo->buffer->signal_match_count_changed.
702
          connect(sigc::mem_fun(*this, &FileTree::on_buffer_match_count_changed));
703 704

      conn_modified_changed_ = fileinfo->buffer->signal_modified_changed().
705
          connect(sigc::mem_fun(*this, &FileTree::on_buffer_modified_changed));
706

707
      conn_undo_stack_push_ = fileinfo->buffer->signal_undo_stack_push.
708
          connect(sigc::mem_fun(*this, &FileTree::on_buffer_undo_stack_push));
709
    }
710

711
    last_selected_rowref_.reset(new TreeRowRef(treestore_, Gtk::TreeModel::Path(iter)));
712 713
  }