• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ContextMenuController(BookmarkEditorGtk * editor)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   }
~ContextMenuController()65   virtual ~ContextMenuController() {}
66 
RunMenu(const gfx::Point & point,guint32 event_time)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 
Cancel()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:
IsCommandIdEnabled(int command_id) const94   virtual bool IsCommandIdEnabled(int command_id) const {
95     return !(command_id == COMMAND_EDIT && running_menu_for_root_) &&
96         (editor_ != NULL);
97   }
98 
IsCommandIdChecked(int command_id) const99   virtual bool IsCommandIdChecked(int command_id) const {
100     return false;
101   }
102 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)103   virtual bool GetAcceleratorForCommandId(int command_id,
104                                           ui::Accelerator* accelerator) {
105     return false;
106   }
107 
ExecuteCommand(int command_id)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 
GetRowIdAt(GtkTreeModel * model,GtkTreeIter * iter)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 
GetNodeAt(GtkTreeModel * model,GtkTreeIter * iter)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 
GetSelectedNode()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
Show(gfx::NativeWindow parent_hwnd,Profile * profile,const BookmarkNode * parent,const EditDetails & details,Configuration configuration)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 
BookmarkEditorGtk(GtkWindow * window,Profile * profile,const BookmarkNode * parent,const EditDetails & details,BookmarkEditor::Configuration configuration)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 
~BookmarkEditorGtk()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 
Init(GtkWindow * parent_window)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 
Show()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 
Close()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 
BookmarkNodeMoved(BookmarkModel * model,const BookmarkNode * old_parent,int old_index,const BookmarkNode * new_parent,int new_index)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 
BookmarkNodeAdded(BookmarkModel * model,const BookmarkNode * parent,int index)394 void BookmarkEditorGtk::BookmarkNodeAdded(BookmarkModel* model,
395                                           const BookmarkNode* parent,
396                                           int index) {
397   Reset();
398 }
399 
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int index,const BookmarkNode * node)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 
BookmarkNodeChildrenReordered(BookmarkModel * model,const BookmarkNode * node)414 void BookmarkEditorGtk::BookmarkNodeChildrenReordered(
415     BookmarkModel* model, const BookmarkNode* node) {
416   Reset();
417 }
418 
Reset()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 
GetInputURL() const425 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 
GetInputTitle() const432 string16 BookmarkEditorGtk::GetInputTitle() const {
433   return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(name_entry_)));
434 }
435 
ApplyEdits()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 
ApplyEdits(GtkTreeIter * selected_parent)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 
AddNewFolder(GtkTreeIter * parent,GtkTreeIter * child)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 
OnSelectionChanged(GtkWidget * selection)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 
OnResponse(GtkWidget * dialog,int response_id)500 void BookmarkEditorGtk::OnResponse(GtkWidget* dialog, int response_id) {
501   if (response_id == GTK_RESPONSE_ACCEPT)
502     ApplyEdits();
503 
504   Close();
505 }
506 
OnWindowDeleteEvent(GtkWidget * widget,GdkEvent * event)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 
OnWindowDestroy(GtkWidget * widget)517 void BookmarkEditorGtk::OnWindowDestroy(GtkWidget* widget) {
518   MessageLoop::current()->DeleteSoon(FROM_HERE, this);
519 }
520 
OnEntryChanged(GtkWidget * entry)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 
OnNewFolderClicked(GtkWidget * button)545 void BookmarkEditorGtk::OnNewFolderClicked(GtkWidget* button) {
546   NewFolder();
547 }
548 
OnTreeViewButtonPressEvent(GtkWidget * widget,GdkEventButton * event)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 
NewFolder()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