Home | History | Annotate | Download | only in bookmarks
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/bookmarks/bookmark_editor_gtk.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/logging.h"
     11 #include "base/string_util.h"
     12 #include "base/utf_string_conversions.h"
     13 #include "chrome/browser/bookmarks/bookmark_model.h"
     14 #include "chrome/browser/bookmarks/bookmark_utils.h"
     15 #include "chrome/browser/history/history.h"
     16 #include "chrome/browser/net/url_fixer_upper.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/ui/gtk/bookmarks/bookmark_tree_model.h"
     19 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
     20 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     21 #include "chrome/browser/ui/gtk/gtk_util.h"
     22 #include "googleurl/src/gurl.h"
     23 #include "grit/chromium_strings.h"
     24 #include "grit/generated_resources.h"
     25 #include "grit/locale_settings.h"
     26 #include "ui/base/l10n/l10n_util.h"
     27 #include "ui/base/models/simple_menu_model.h"
     28 #include "ui/gfx/gtk_util.h"
     29 #include "ui/gfx/point.h"
     30 
     31 #if defined(TOOLKIT_VIEWS)
     32 #include "views/controls/menu/menu_2.h"
     33 #else
     34 #include "chrome/browser/ui/gtk/menu_gtk.h"
     35 #endif
     36 
     37 namespace {
     38 
     39 // Background color of text field when URL is invalid.
     40 const GdkColor kErrorColor = GDK_COLOR_RGB(0xFF, 0xBC, 0xBC);
     41 
     42 // Preferred initial dimensions, in pixels, of the folder tree.
     43 static const int kTreeWidth = 300;
     44 static const int kTreeHeight = 150;
     45 
     46 }  // namespace
     47 
     48 class BookmarkEditorGtk::ContextMenuController
     49     : public ui::SimpleMenuModel::Delegate {
     50  public:
     51   explicit ContextMenuController(BookmarkEditorGtk* editor)
     52       : editor_(editor),
     53         running_menu_for_root_(false) {
     54     menu_model_.reset(new ui::SimpleMenuModel(this));
     55     menu_model_->AddItemWithStringId(COMMAND_EDIT, IDS_EDIT);
     56     menu_model_->AddItemWithStringId(
     57         COMMAND_NEW_FOLDER,
     58         IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
     59 #if defined(TOOLKIT_VIEWS)
     60     menu_.reset(new views::Menu2(menu_model_.get()));
     61 #else
     62     menu_.reset(new MenuGtk(NULL, menu_model_.get()));
     63 #endif
     64   }
     65   virtual ~ContextMenuController() {}
     66 
     67   void RunMenu(const gfx::Point& point, guint32 event_time) {
     68     const BookmarkNode* selected_node = GetSelectedNode();
     69     if (selected_node)
     70       running_menu_for_root_ = selected_node->parent()->IsRoot();
     71 #if defined(TOOLKIT_VIEWS)
     72     menu_->RunContextMenuAt(point);
     73 #else
     74     menu_->PopupAsContext(point, event_time);
     75 #endif
     76   }
     77 
     78   void Cancel() {
     79     editor_ = NULL;
     80 #if defined(TOOLKIT_VIEWS)
     81     menu_->CancelMenu();
     82 #else
     83     menu_->Cancel();
     84 #endif
     85   }
     86 
     87  private:
     88   enum ContextMenuCommand {
     89     COMMAND_EDIT,
     90     COMMAND_NEW_FOLDER
     91   };
     92 
     93   // Overridden from ui::SimpleMenuModel::Delegate:
     94   virtual bool IsCommandIdEnabled(int command_id) const {
     95     return !(command_id == COMMAND_EDIT && running_menu_for_root_) &&
     96         (editor_ != NULL);
     97   }
     98 
     99   virtual bool IsCommandIdChecked(int command_id) const {
    100     return false;
    101   }
    102 
    103   virtual bool GetAcceleratorForCommandId(int command_id,
    104                                           ui::Accelerator* accelerator) {
    105     return false;
    106   }
    107 
    108   virtual void ExecuteCommand(int command_id) {
    109     if (!editor_)
    110       return;
    111 
    112     switch (command_id) {
    113       case COMMAND_EDIT: {
    114         GtkTreeIter iter;
    115         if (!gtk_tree_selection_get_selected(editor_->tree_selection_,
    116                                              NULL,
    117                                              &iter)) {
    118           return;
    119         }
    120 
    121         GtkTreePath* path = gtk_tree_model_get_path(
    122             GTK_TREE_MODEL(editor_->tree_store_), &iter);
    123         gtk_tree_view_expand_to_path(GTK_TREE_VIEW(editor_->tree_view_), path);
    124 
    125         // Make the folder name editable.
    126         gtk_tree_view_set_cursor(GTK_TREE_VIEW(editor_->tree_view_), path,
    127             gtk_tree_view_get_column(GTK_TREE_VIEW(editor_->tree_view_), 0),
    128             TRUE);
    129 
    130         gtk_tree_path_free(path);
    131         break;
    132       }
    133       case COMMAND_NEW_FOLDER:
    134         editor_->NewFolder();
    135         break;
    136       default:
    137         NOTREACHED();
    138         break;
    139     }
    140   }
    141 
    142   int64 GetRowIdAt(GtkTreeModel* model, GtkTreeIter* iter) {
    143     GValue value = { 0, };
    144     gtk_tree_model_get_value(model, iter, bookmark_utils::ITEM_ID, &value);
    145     int64 id = g_value_get_int64(&value);
    146     g_value_unset(&value);
    147     return id;
    148   }
    149 
    150   const BookmarkNode* GetNodeAt(GtkTreeModel* model, GtkTreeIter* iter) {
    151     int64 id = GetRowIdAt(model, iter);
    152     return (id > 0) ? editor_->bb_model_->GetNodeByID(id) : NULL;
    153   }
    154 
    155   const BookmarkNode* GetSelectedNode() {
    156     GtkTreeModel* model;
    157     GtkTreeIter iter;
    158     if (!gtk_tree_selection_get_selected(editor_->tree_selection_,
    159                                          &model,
    160                                          &iter)) {
    161       return NULL;
    162     }
    163 
    164     return GetNodeAt(model, &iter);
    165   }
    166 
    167   // The model and view for the right click context menu.
    168   scoped_ptr<ui::SimpleMenuModel> menu_model_;
    169 #if defined(TOOLKIT_VIEWS)
    170   scoped_ptr<views::Menu2> menu_;
    171 #else
    172   scoped_ptr<MenuGtk> menu_;
    173 #endif
    174 
    175   // The context menu was brought up for. Set to NULL when the menu is canceled.
    176   BookmarkEditorGtk* editor_;
    177 
    178   // If true, we're running the menu for the bookmark bar or other bookmarks
    179   // nodes.
    180   bool running_menu_for_root_;
    181 
    182   DISALLOW_COPY_AND_ASSIGN(ContextMenuController);
    183 };
    184 
    185 // static
    186 void BookmarkEditor::Show(gfx::NativeWindow parent_hwnd,
    187                           Profile* profile,
    188                           const BookmarkNode* parent,
    189                           const EditDetails& details,
    190                           Configuration configuration) {
    191   DCHECK(profile);
    192   BookmarkEditorGtk* editor =
    193       new BookmarkEditorGtk(parent_hwnd, profile, parent, details,
    194                             configuration);
    195   editor->Show();
    196 }
    197 
    198 BookmarkEditorGtk::BookmarkEditorGtk(
    199     GtkWindow* window,
    200     Profile* profile,
    201     const BookmarkNode* parent,
    202     const EditDetails& details,
    203     BookmarkEditor::Configuration configuration)
    204     : profile_(profile),
    205       dialog_(NULL),
    206       parent_(parent),
    207       details_(details),
    208       running_menu_for_root_(false),
    209       show_tree_(configuration == SHOW_TREE) {
    210   DCHECK(profile);
    211   Init(window);
    212 }
    213 
    214 BookmarkEditorGtk::~BookmarkEditorGtk() {
    215   // The tree model is deleted before the view. Reset the model otherwise the
    216   // tree will reference a deleted model.
    217 
    218   bb_model_->RemoveObserver(this);
    219 }
    220 
    221 void BookmarkEditorGtk::Init(GtkWindow* parent_window) {
    222   bb_model_ = profile_->GetBookmarkModel();
    223   DCHECK(bb_model_);
    224   bb_model_->AddObserver(this);
    225 
    226   dialog_ = gtk_dialog_new_with_buttons(
    227       l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_TITLE).c_str(),
    228       parent_window,
    229       GTK_DIALOG_MODAL,
    230       GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
    231       GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
    232       NULL);
    233   gtk_dialog_set_has_separator(GTK_DIALOG(dialog_), FALSE);
    234 
    235   if (show_tree_) {
    236     GtkWidget* action_area = GTK_DIALOG(dialog_)->action_area;
    237     new_folder_button_ = gtk_button_new_with_label(
    238         l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_NEW_FOLDER_BUTTON).c_str());
    239     g_signal_connect(new_folder_button_, "clicked",
    240                      G_CALLBACK(OnNewFolderClickedThunk), this);
    241     gtk_container_add(GTK_CONTAINER(action_area), new_folder_button_);
    242     gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(action_area),
    243                                        new_folder_button_, TRUE);
    244   }
    245 
    246   gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_ACCEPT);
    247 
    248   // The GTK dialog content area layout (overview)
    249   //
    250   // +- GtkVBox |vbox| ----------------------------------------------+
    251   // |+- GtkTable |table| ------------------------------------------+|
    252   // ||+- GtkLabel ------+ +- GtkEntry |name_entry_| --------------+||
    253   // |||                 | |                                       |||
    254   // ||+-----------------+ +---------------------------------------+||
    255   // ||+- GtkLabel ------+ +- GtkEntry |url_entry_| ---------------+|| *
    256   // |||                 | |                                       |||
    257   // ||+-----------------+ +---------------------------------------+||
    258   // |+-------------------------------------------------------------+|
    259   // |+- GtkScrollWindow |scroll_window| ---------------------------+|
    260   // ||+- GtkTreeView |tree_view_| --------------------------------+||
    261   // |||+- GtkTreeViewColumn |name_column| -----------------------+|||
    262   // ||||                                                         ||||
    263   // ||||                                                         ||||
    264   // ||||                                                         ||||
    265   // ||||                                                         ||||
    266   // |||+---------------------------------------------------------+|||
    267   // ||+-----------------------------------------------------------+||
    268   // |+-------------------------------------------------------------+|
    269   // +---------------------------------------------------------------+
    270   //
    271   // * The url and corresponding label are not shown if creating a new folder.
    272   GtkWidget* content_area = GTK_DIALOG(dialog_)->vbox;
    273   gtk_box_set_spacing(GTK_BOX(content_area), gtk_util::kContentAreaSpacing);
    274 
    275   GtkWidget* vbox = gtk_vbox_new(FALSE, 12);
    276 
    277   name_entry_ = gtk_entry_new();
    278   std::string title;
    279   if (details_.type == EditDetails::EXISTING_NODE) {
    280     title = UTF16ToUTF8(details_.existing_node->GetTitle());
    281   } else if (details_.type == EditDetails::NEW_FOLDER) {
    282     title = l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME);
    283   }
    284   gtk_entry_set_text(GTK_ENTRY(name_entry_), title.c_str());
    285   g_signal_connect(name_entry_, "changed",
    286                    G_CALLBACK(OnEntryChangedThunk), this);
    287   gtk_entry_set_activates_default(GTK_ENTRY(name_entry_), TRUE);
    288 
    289   GtkWidget* table;
    290   if (details_.type != EditDetails::NEW_FOLDER) {
    291     url_entry_ = gtk_entry_new();
    292     std::string url_spec;
    293     if (details_.type == EditDetails::EXISTING_NODE)
    294       url_spec = details_.existing_node->GetURL().spec();
    295     gtk_entry_set_text(GTK_ENTRY(url_entry_), url_spec.c_str());
    296     g_signal_connect(url_entry_, "changed",
    297                      G_CALLBACK(OnEntryChangedThunk), this);
    298     gtk_entry_set_activates_default(GTK_ENTRY(url_entry_), TRUE);
    299     table = gtk_util::CreateLabeledControlsGroup(NULL,
    300         l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_NAME_LABEL).c_str(),
    301         name_entry_,
    302         l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_URL_LABEL).c_str(),
    303         url_entry_,
    304         NULL);
    305 
    306   } else {
    307     url_entry_ = NULL;
    308     table = gtk_util::CreateLabeledControlsGroup(NULL,
    309         l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_NAME_LABEL).c_str(),
    310         name_entry_,
    311         NULL);
    312   }
    313 
    314   gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
    315 
    316   if (show_tree_) {
    317     GtkTreeIter selected_iter;
    318     int64 selected_id = 0;
    319     if (details_.type == EditDetails::EXISTING_NODE)
    320       selected_id = details_.existing_node->parent()->id();
    321     else if (parent_)
    322       selected_id = parent_->id();
    323     tree_store_ = bookmark_utils::MakeFolderTreeStore();
    324     bookmark_utils::AddToTreeStore(bb_model_, selected_id, tree_store_,
    325                                    &selected_iter);
    326     tree_view_ = bookmark_utils::MakeTreeViewForStore(tree_store_);
    327     gtk_widget_set_size_request(tree_view_, kTreeWidth, kTreeHeight);
    328     tree_selection_ = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view_));
    329     g_signal_connect(tree_view_, "button-press-event",
    330                      G_CALLBACK(OnTreeViewButtonPressEventThunk), this);
    331 
    332     GtkTreePath* path = NULL;
    333     if (selected_id) {
    334       path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store_),
    335                                      &selected_iter);
    336     } else {
    337       // We don't have a selected parent (Probably because we're making a new
    338       // bookmark). Select the first item in the list.
    339       path = gtk_tree_path_new_from_string("0");
    340     }
    341 
    342     gtk_tree_view_expand_to_path(GTK_TREE_VIEW(tree_view_), path);
    343     gtk_tree_selection_select_path(tree_selection_, path);
    344     gtk_tree_path_free(path);
    345 
    346     GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL);
    347     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window),
    348                                     GTK_POLICY_NEVER,
    349                                    GTK_POLICY_AUTOMATIC);
    350     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window),
    351                                         GTK_SHADOW_ETCHED_IN);
    352     gtk_container_add(GTK_CONTAINER(scroll_window), tree_view_);
    353 
    354     gtk_box_pack_start(GTK_BOX(vbox), scroll_window, TRUE, TRUE, 0);
    355 
    356     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view_)),
    357                      "changed", G_CALLBACK(OnSelectionChangedThunk), this);
    358   }
    359 
    360   gtk_box_pack_start(GTK_BOX(content_area), vbox, TRUE, TRUE, 0);
    361 
    362   g_signal_connect(dialog_, "response",
    363                    G_CALLBACK(OnResponseThunk), this);
    364   g_signal_connect(dialog_, "delete-event",
    365                    G_CALLBACK(OnWindowDeleteEventThunk), this);
    366   g_signal_connect(dialog_, "destroy",
    367                    G_CALLBACK(OnWindowDestroyThunk), this);
    368 }
    369 
    370 void BookmarkEditorGtk::Show() {
    371   // Manually call our OnEntryChanged handler to set the initial state.
    372   OnEntryChanged(NULL);
    373 
    374   gtk_util::ShowDialog(dialog_);
    375 }
    376 
    377 void BookmarkEditorGtk::Close() {
    378   // Under the model that we've inherited from Windows, dialogs can receive
    379   // more than one Close() call inside the current message loop event.
    380   if (dialog_) {
    381     gtk_widget_destroy(dialog_);
    382     dialog_ = NULL;
    383   }
    384 }
    385 
    386 void BookmarkEditorGtk::BookmarkNodeMoved(BookmarkModel* model,
    387                                           const BookmarkNode* old_parent,
    388                                           int old_index,
    389                                           const BookmarkNode* new_parent,
    390                                           int new_index) {
    391   Reset();
    392 }
    393 
    394 void BookmarkEditorGtk::BookmarkNodeAdded(BookmarkModel* model,
    395                                           const BookmarkNode* parent,
    396                                           int index) {
    397   Reset();
    398 }
    399 
    400 void BookmarkEditorGtk::BookmarkNodeRemoved(BookmarkModel* model,
    401                                             const BookmarkNode* parent,
    402                                             int index,
    403                                             const BookmarkNode* node) {
    404   if ((details_.type == EditDetails::EXISTING_NODE &&
    405        details_.existing_node->HasAncestor(node)) ||
    406       (parent_ && parent_->HasAncestor(node))) {
    407     // The node, or its parent was removed. Close the dialog.
    408     Close();
    409   } else {
    410     Reset();
    411   }
    412 }
    413 
    414 void BookmarkEditorGtk::BookmarkNodeChildrenReordered(
    415     BookmarkModel* model, const BookmarkNode* node) {
    416   Reset();
    417 }
    418 
    419 void BookmarkEditorGtk::Reset() {
    420   // TODO(erg): The windows implementation tries to be smart. For now, just
    421   // close the window.
    422   Close();
    423 }
    424 
    425 GURL BookmarkEditorGtk::GetInputURL() const {
    426   if (!url_entry_)
    427     return GURL();  // Happens when we're editing a folder.
    428   return URLFixerUpper::FixupURL(gtk_entry_get_text(GTK_ENTRY(url_entry_)),
    429                                  std::string());
    430 }
    431 
    432 string16 BookmarkEditorGtk::GetInputTitle() const {
    433   return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(name_entry_)));
    434 }
    435 
    436 void BookmarkEditorGtk::ApplyEdits() {
    437   DCHECK(bb_model_->IsLoaded());
    438 
    439   GtkTreeIter currently_selected_iter;
    440   if (show_tree_) {
    441     if (!gtk_tree_selection_get_selected(tree_selection_, NULL,
    442                                          &currently_selected_iter)) {
    443       ApplyEdits(NULL);
    444       return;
    445     }
    446   }
    447 
    448   ApplyEdits(&currently_selected_iter);
    449 }
    450 
    451 void BookmarkEditorGtk::ApplyEdits(GtkTreeIter* selected_parent) {
    452   // We're going to apply edits to the bookmark bar model, which will call us
    453   // back. Normally when a structural edit occurs we reset the tree model.
    454   // We don't want to do that here, so we remove ourselves as an observer.
    455   bb_model_->RemoveObserver(this);
    456 
    457   GURL new_url(GetInputURL());
    458   string16 new_title(GetInputTitle());
    459 
    460   if (!show_tree_ || !selected_parent) {
    461     bookmark_utils::ApplyEditsWithNoFolderChange(
    462         bb_model_, parent_, details_, new_title, new_url);
    463     return;
    464   }
    465 
    466   // Create the new folders and update the titles.
    467   const BookmarkNode* new_parent =
    468       bookmark_utils::CommitTreeStoreDifferencesBetween(
    469       bb_model_, tree_store_, selected_parent);
    470 
    471   if (!new_parent) {
    472     // Bookmarks must be parented.
    473     NOTREACHED();
    474     return;
    475   }
    476 
    477   bookmark_utils::ApplyEditsWithPossibleFolderChange(
    478       bb_model_, new_parent, details_, new_title, new_url);
    479 }
    480 
    481 void BookmarkEditorGtk::AddNewFolder(GtkTreeIter* parent, GtkTreeIter* child) {
    482   gtk_tree_store_append(tree_store_, child, parent);
    483   gtk_tree_store_set(
    484       tree_store_, child,
    485       bookmark_utils::FOLDER_ICON, GtkThemeService::GetFolderIcon(true),
    486       bookmark_utils::FOLDER_NAME,
    487           l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME).c_str(),
    488       bookmark_utils::ITEM_ID, static_cast<int64>(0),
    489       bookmark_utils::IS_EDITABLE, TRUE,
    490       -1);
    491 }
    492 
    493 void BookmarkEditorGtk::OnSelectionChanged(GtkWidget* selection) {
    494   if (!gtk_tree_selection_get_selected(tree_selection_, NULL, NULL))
    495     gtk_widget_set_sensitive(new_folder_button_, FALSE);
    496   else
    497     gtk_widget_set_sensitive(new_folder_button_, TRUE);
    498 }
    499 
    500 void BookmarkEditorGtk::OnResponse(GtkWidget* dialog, int response_id) {
    501   if (response_id == GTK_RESPONSE_ACCEPT)
    502     ApplyEdits();
    503 
    504   Close();
    505 }
    506 
    507 gboolean BookmarkEditorGtk::OnWindowDeleteEvent(GtkWidget* widget,
    508                                                 GdkEvent* event) {
    509   Close();
    510 
    511   // Return true to prevent the gtk dialog from being destroyed. Close will
    512   // destroy it for us and the default gtk_dialog_delete_event_handler() will
    513   // force the destruction without us being able to stop it.
    514   return TRUE;
    515 }
    516 
    517 void BookmarkEditorGtk::OnWindowDestroy(GtkWidget* widget) {
    518   MessageLoop::current()->DeleteSoon(FROM_HERE, this);
    519 }
    520 
    521 void BookmarkEditorGtk::OnEntryChanged(GtkWidget* entry) {
    522   gboolean can_close = TRUE;
    523   if (details_.type == EditDetails::NEW_FOLDER) {
    524     if (GetInputTitle().empty()) {
    525       gtk_widget_modify_base(name_entry_, GTK_STATE_NORMAL,
    526                              &kErrorColor);
    527       can_close = FALSE;
    528     } else {
    529       gtk_widget_modify_base(name_entry_, GTK_STATE_NORMAL, NULL);
    530     }
    531   } else {
    532     GURL url(GetInputURL());
    533     if (!url.is_valid()) {
    534       gtk_widget_modify_base(url_entry_, GTK_STATE_NORMAL,
    535                              &kErrorColor);
    536       can_close = FALSE;
    537     } else {
    538       gtk_widget_modify_base(url_entry_, GTK_STATE_NORMAL, NULL);
    539     }
    540   }
    541   gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog_),
    542                                     GTK_RESPONSE_ACCEPT, can_close);
    543 }
    544 
    545 void BookmarkEditorGtk::OnNewFolderClicked(GtkWidget* button) {
    546   NewFolder();
    547 }
    548 
    549 gboolean BookmarkEditorGtk::OnTreeViewButtonPressEvent(GtkWidget* widget,
    550                                                        GdkEventButton* event) {
    551   if (event->button == 3) {
    552     if (!menu_controller_.get())
    553       menu_controller_.reset(new ContextMenuController(this));
    554     menu_controller_->RunMenu(gfx::Point(event->x_root, event->y_root),
    555                               event->time);
    556   }
    557 
    558   return FALSE;
    559 }
    560 
    561 void BookmarkEditorGtk::NewFolder() {
    562   GtkTreeIter iter;
    563   if (!gtk_tree_selection_get_selected(tree_selection_,
    564                                        NULL,
    565                                        &iter)) {
    566     NOTREACHED() << "Something should always be selected if New Folder " <<
    567                     "is clicked";
    568     return;
    569   }
    570 
    571   GtkTreeIter new_item_iter;
    572   AddNewFolder(&iter, &new_item_iter);
    573 
    574   GtkTreePath* path = gtk_tree_model_get_path(
    575       GTK_TREE_MODEL(tree_store_), &new_item_iter);
    576   gtk_tree_view_expand_to_path(GTK_TREE_VIEW(tree_view_), path);
    577 
    578   // Make the folder name editable.
    579   gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree_view_), path,
    580       gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view_), 0),
    581       TRUE);
    582 
    583   gtk_tree_path_free(path);
    584 }
    585