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 ¤tly_selected_iter)) {
443 ApplyEdits(NULL);
444 return;
445 }
446 }
447
448 ApplyEdits(¤tly_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