blob: 1cad27351309d1cb33c15f99b62c96c13ed6d024 [file] [log] [blame]
[email protected]05d478752009-04-08 23:38:161// Copyright (c) 2009 The Chromium Authors. All rights reserved.
[email protected]7f856be2008-10-29 23:38:062// 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/views/bookmark_manager_view.h"
6
7#include <algorithm>
8
[email protected]82522512009-05-15 07:37:299#include "app/gfx/canvas.h"
[email protected]91e81ae2009-05-08 22:14:3810#include "app/gfx/color_utils.h"
[email protected]e80daf622009-05-31 02:36:2311#include "app/l10n_util.h"
[email protected]bc1407f2009-09-29 00:33:3512#include "base/keyboard_codes.h"
[email protected]dcccb942009-02-01 18:23:0013#include "base/thread.h"
[email protected]7f856be2008-10-29 23:38:0614#include "chrome/browser/bookmarks/bookmark_folder_tree_model.h"
[email protected]b5048992008-11-07 04:31:3515#include "chrome/browser/bookmarks/bookmark_html_writer.h"
[email protected]1e7bab72009-06-03 17:23:3616#include "chrome/browser/bookmarks/bookmark_manager.h"
[email protected]7f856be2008-10-29 23:38:0617#include "chrome/browser/bookmarks/bookmark_model.h"
18#include "chrome/browser/bookmarks/bookmark_table_model.h"
19#include "chrome/browser/bookmarks/bookmark_utils.h"
20#include "chrome/browser/browser_list.h"
21#include "chrome/browser/browser_process.h"
[email protected]b5048992008-11-07 04:31:3522#include "chrome/browser/importer/importer.h"
[email protected]cd1adc22009-01-16 01:29:2223#include "chrome/browser/metrics/user_metrics.h"
[email protected]7f856be2008-10-29 23:38:0624#include "chrome/browser/profile.h"
25#include "chrome/browser/views/bookmark_editor_view.h"
26#include "chrome/browser/views/bookmark_folder_tree_view.h"
27#include "chrome/browser/views/bookmark_table_view.h"
[email protected]7f856be2008-10-29 23:38:0628#include "chrome/common/pref_names.h"
29#include "chrome/common/pref_service.h"
[email protected]f90f5c512009-02-18 19:10:5830#include "grit/generated_resources.h"
31#include "grit/locale_settings.h"
[email protected]05b7f992008-12-03 21:28:3332#include "skia/ext/skia_utils.h"
[email protected]cab34d6a2009-09-24 01:14:5233#include "third_party/skia/include/core/SkShader.h"
[email protected]2362e4f2009-05-08 00:34:0534#include "views/grid_layout.h"
35#include "views/controls/button/menu_button.h"
[email protected]8cd89cca2009-08-20 03:58:1136#include "views/controls/menu/menu_item_view.h"
[email protected]2362e4f2009-05-08 00:34:0537#include "views/controls/label.h"
38#include "views/controls/single_split_view.h"
[email protected]91e81ae2009-05-08 22:14:3839#include "views/standard_layout.h"
[email protected]2362e4f2009-05-08 00:34:0540#include "views/widget/widget.h"
41#include "views/window/window.h"
[email protected]ae615162008-12-03 01:11:5842
[email protected]b220d13f2009-10-09 04:12:4643#if defined(CHROME_PERSONALIZATION)
44#include "chrome/browser/options_window.h"
45#include "chrome/browser/sync/sync_status_ui_helper.h"
46#endif
47
[email protected]7f856be2008-10-29 23:38:0648// If non-null, there is an open editor and this is the window it is contained
49// in it.
50static views::Window* open_window = NULL;
51// And this is the manager contained in it.
52static BookmarkManagerView* manager = NULL;
53
54// Delay, in ms, between when the user types and when we run the search.
55static const int kSearchDelayMS = 200;
56
[email protected]b5048992008-11-07 04:31:3557static const int kOrganizeMenuButtonID = 1;
58static const int kToolsMenuButtonID = 2;
59
[email protected]516c89fd2008-11-14 20:40:5160// Background color.
61static const SkColor kBackgroundColorTop = SkColorSetRGB(242, 247, 253);
62static const SkColor kBackgroundColorBottom = SkColorSetRGB(223, 234, 248);
63static const int kBackgroundGradientHeight = 28;
64
[email protected]b5048992008-11-07 04:31:3565namespace {
66
67// Observer installed on the importer. When done importing the newly created
68// folder is selected in the bookmark manager.
69class ImportObserverImpl : public ImportObserver {
70 public:
71 explicit ImportObserverImpl(Profile* profile) : profile_(profile) {
72 BookmarkModel* model = profile->GetBookmarkModel();
73 initial_other_count_ = model->other_node()->GetChildCount();
74 }
75
76 virtual void ImportCanceled() {
77 delete this;
78 }
79
80 virtual void ImportComplete() {
81 // We aren't needed anymore.
82 MessageLoop::current()->DeleteSoon(FROM_HERE, this);
83
84 BookmarkManagerView* manager = BookmarkManagerView::current();
85 if (!manager || manager->profile() != profile_)
86 return;
87
88 BookmarkModel* model = profile_->GetBookmarkModel();
89 int other_count = model->other_node()->GetChildCount();
90 if (other_count == initial_other_count_ + 1) {
[email protected]b3c33d462009-06-26 22:29:2091 const BookmarkNode* imported_node =
[email protected]b5048992008-11-07 04:31:3592 model->other_node()->GetChild(initial_other_count_);
93 manager->SelectInTree(imported_node);
94 manager->ExpandAll(imported_node);
95 }
96 }
97
98 private:
99 Profile* profile_;
100 // Number of children in the other bookmarks folder at the time we were
101 // created.
102 int initial_other_count_;
103
104 DISALLOW_COPY_AND_ASSIGN(ImportObserverImpl);
105};
106
[email protected]fafc8a422008-11-07 17:53:09107// Converts a virtual keycode into the CutCopyPasteType.
108BookmarkManagerView::CutCopyPasteType KeyCodeToCutCopyPaste(
109 unsigned short virtual_keycode) {
110 switch (virtual_keycode) {
111 case VK_INSERT:
112 if (GetKeyState(VK_CONTROL) < 0)
113 return BookmarkManagerView::COPY;
114 if (GetKeyState(VK_SHIFT) < 0)
115 return BookmarkManagerView::PASTE;
116 return BookmarkManagerView::NONE;
117
118 case VK_DELETE:
119 if (GetKeyState(VK_SHIFT) < 0)
120 return BookmarkManagerView::CUT;
121 return BookmarkManagerView::NONE;
122
123 case 'C':
124 if (GetKeyState(VK_CONTROL) < 0)
125 return BookmarkManagerView::COPY;
126 return BookmarkManagerView::NONE;
127
128 case 'V':
129 if (GetKeyState(VK_CONTROL) < 0)
130 return BookmarkManagerView::PASTE;
131 return BookmarkManagerView::NONE;
132
133 case 'X':
134 if (GetKeyState(VK_CONTROL) < 0)
135 return BookmarkManagerView::CUT;
136 return BookmarkManagerView::NONE;
137
138 default:
139 return BookmarkManagerView::NONE;
140 }
141}
142
[email protected]b5048992008-11-07 04:31:35143} // namespace
144
[email protected]a984b912009-05-22 21:29:22145namespace browser {
146
147// Declared in browser_dialogs.h so others don't need to depend on our header.
148void ShowBookmarkManagerView(Profile* profile) {
149 BookmarkManagerView::Show(profile);
150}
151
152} // namespace browser
153
[email protected]1e7bab72009-06-03 17:23:36154// BookmarkManager -------------------------------------------------------------
155
[email protected]b3c33d462009-06-26 22:29:20156void BookmarkManager::SelectInTree(Profile* profile, const BookmarkNode* node) {
[email protected]1e7bab72009-06-03 17:23:36157 if (manager && manager->profile() == profile)
158 manager->SelectInTree(node);
159}
160
161void BookmarkManager::Show(Profile* profile) {
162 BookmarkManagerView::Show(profile);
163}
164
165// -----------------------------------------------------------------------------
166
[email protected]7f856be2008-10-29 23:38:06167BookmarkManagerView::BookmarkManagerView(Profile* profile)
168 : profile_(profile->GetOriginalProfile()),
169 table_view_(NULL),
170 tree_view_(NULL),
[email protected]b220d13f2009-10-09 04:12:46171#if defined(CHROME_PERSONALIZATION)
172 sync_status_button_(NULL),
173 sync_service_(NULL),
174#endif
[email protected]f71e7262009-05-14 19:13:13175 ALLOW_THIS_IN_INITIALIZER_LIST(search_factory_(this)) {
[email protected]ba96e33f2009-05-27 19:47:23176 search_tf_ = new views::Textfield();
[email protected]b24740e32008-12-11 03:59:44177 search_tf_->set_default_width_in_chars(30);
[email protected]7f856be2008-10-29 23:38:06178
179 table_view_ = new BookmarkTableView(profile_, NULL);
180 table_view_->SetObserver(this);
181 table_view_->SetContextMenuController(this);
182
183 tree_view_ = new BookmarkFolderTreeView(profile_, NULL);
184 tree_view_->SetController(this);
185 tree_view_->SetContextMenuController(this);
186
[email protected]3ec1a3a2008-11-03 23:14:01187 views::MenuButton* organize_menu_button = new views::MenuButton(
[email protected]48c8fa62009-03-16 23:25:13188 NULL, l10n_util::GetString(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU),
[email protected]3ec1a3a2008-11-03 23:14:01189 this, true);
[email protected]b5048992008-11-07 04:31:35190 organize_menu_button->SetID(kOrganizeMenuButtonID);
191
192 views::MenuButton* tools_menu_button = new views::MenuButton(
[email protected]48c8fa62009-03-16 23:25:13193 NULL, l10n_util::GetString(IDS_BOOKMARK_MANAGER_TOOLS_MENU),
[email protected]b5048992008-11-07 04:31:35194 this, true);
195 tools_menu_button->SetID(kToolsMenuButtonID);
[email protected]3ec1a3a2008-11-03 23:14:01196
[email protected]ea36e322009-06-24 09:40:35197 split_view_ = new views::SingleSplitView(tree_view_, table_view_,
198 views::SingleSplitView::HORIZONTAL_SPLIT);
[email protected]e46318e2009-07-09 14:13:08199 split_view_->set_resize_leading_on_bounds_change(false);
[email protected]516c89fd2008-11-14 20:40:51200 split_view_->set_background(
201 views::Background::CreateSolidBackground(kBackgroundColorBottom));
[email protected]7f856be2008-10-29 23:38:06202
203 views::GridLayout* layout = new views::GridLayout(this);
204 SetLayoutManager(layout);
[email protected]3ec1a3a2008-11-03 23:14:01205 const int top_id = 1;
[email protected]7f856be2008-10-29 23:38:06206 const int split_cs_id = 2;
[email protected]fa5dfaf2009-06-02 22:12:06207 layout->SetInsets(2, 0, 0, 0); // 2px padding above content.
[email protected]3ec1a3a2008-11-03 23:14:01208 views::ColumnSet* column_set = layout->AddColumnSet(top_id);
209 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
[email protected]b5048992008-11-07 04:31:35210 0, views::GridLayout::USE_PREF, 0, 0);
[email protected]b5048992008-11-07 04:31:35211 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
212 0, views::GridLayout::USE_PREF, 0, 0);
[email protected]b220d13f2009-10-09 04:12:46213#if defined(CHROME_PERSONALIZATION)
214 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
215 0, views::GridLayout::USE_PREF, 0, 0);
216#endif
[email protected]3ec1a3a2008-11-03 23:14:01217 column_set->AddPaddingColumn(1, kUnrelatedControlHorizontalSpacing);
[email protected]b24740e32008-12-11 03:59:44218 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
219 0, views::GridLayout::USE_PREF, 0, 0);
220 column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
[email protected]7f856be2008-10-29 23:38:06221 column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
[email protected]b24740e32008-12-11 03:59:44222 0, views::GridLayout::USE_PREF, 0, 0);
[email protected]fa5dfaf2009-06-02 22:12:06223 column_set->AddPaddingColumn(0, 3); // 3px padding at end of row.
[email protected]7f856be2008-10-29 23:38:06224
225 column_set = layout->AddColumnSet(split_cs_id);
226 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
227 views::GridLayout::USE_PREF, 0, 0);
228
[email protected]3ec1a3a2008-11-03 23:14:01229 layout->StartRow(0, top_id);
230 layout->AddView(organize_menu_button);
[email protected]b5048992008-11-07 04:31:35231 layout->AddView(tools_menu_button);
[email protected]b220d13f2009-10-09 04:12:46232#if defined(CHROME_PERSONALIZATION)
233 sync_status_button_ = new views::TextButton(this, std::wstring());
234 layout->AddView(sync_status_button_);
235#endif
[email protected]b24740e32008-12-11 03:59:44236 layout->AddView(new views::Label(
237 l10n_util::GetString(IDS_BOOKMARK_MANAGER_SEARCH_TITLE)));
[email protected]7f856be2008-10-29 23:38:06238 layout->AddView(search_tf_);
239
[email protected]fa5dfaf2009-06-02 22:12:06240 layout->AddPaddingRow(0, 3); // 3px padding between rows.
[email protected]7f856be2008-10-29 23:38:06241
242 layout->StartRow(1, split_cs_id);
[email protected]3ec1a3a2008-11-03 23:14:01243 layout->AddView(split_view_);
[email protected]7f856be2008-10-29 23:38:06244
[email protected]3da579b2009-05-27 16:36:19245 // Press Ctrl-W to close bookmark manager window.
[email protected]bc1407f2009-09-29 00:33:35246 AddAccelerator(views::Accelerator(base::VKEY_W, false, true, false));
[email protected]3da579b2009-05-27 16:36:19247
[email protected]7f856be2008-10-29 23:38:06248 BookmarkModel* bookmark_model = profile_->GetBookmarkModel();
249 if (!bookmark_model->IsLoaded())
250 bookmark_model->AddObserver(this);
[email protected]b220d13f2009-10-09 04:12:46251
252#if defined(CHROME_PERSONALIZATION)
253 if (profile->GetProfileSyncService()) {
254 sync_service_ = profile_->GetProfileSyncService();
255 sync_service_->AddObserver(this);
256 UpdateSyncStatus();
257 }
258#endif
[email protected]7f856be2008-10-29 23:38:06259}
260
261BookmarkManagerView::~BookmarkManagerView() {
[email protected]b5048992008-11-07 04:31:35262 if (select_file_dialog_.get())
263 select_file_dialog_->ListenerDestroyed();
264
[email protected]7f856be2008-10-29 23:38:06265 if (!GetBookmarkModel()->IsLoaded()) {
266 GetBookmarkModel()->RemoveObserver(this);
267 } else {
268 // The models are deleted before the views. Make sure we set the models of
269 // the views to NULL so that they aren't left holding a reference to a
270 // deleted model.
271 table_view_->SetModel(NULL);
272 tree_view_->SetModel(NULL);
273 }
274 manager = NULL;
275 open_window = NULL;
[email protected]7f856be2008-10-29 23:38:06276
[email protected]b220d13f2009-10-09 04:12:46277#if defined(CHROME_PERSONALIZATION)
278 if (sync_service_)
279 sync_service_->RemoveObserver(this);
280#endif
281}
[email protected]7f856be2008-10-29 23:38:06282
283// static
284void BookmarkManagerView::Show(Profile* profile) {
285 if (!profile->GetBookmarkModel())
286 return;
287
288 if (open_window != NULL) {
[email protected]71995172008-11-04 04:44:43289 open_window->Activate();
[email protected]7f856be2008-10-29 23:38:06290 return;
291 }
292
293 // Both of these will be deleted when the dialog closes.
294 manager = new BookmarkManagerView(profile);
295
296 // Create the window.
297 open_window = views::Window::CreateChromeWindow(NULL, gfx::Rect(), manager);
298 // Let the manager know it's parented.
299 manager->PrepareForShow();
300 // And show it.
301 open_window->Show();
[email protected]71995172008-11-04 04:44:43302
303 // Give initial focus to the search field.
304 manager->search_tf_->RequestFocus();
[email protected]7f856be2008-10-29 23:38:06305}
306
307// static
308BookmarkManagerView* BookmarkManagerView::current() {
309 return manager;
310}
311
[email protected]b3c33d462009-06-26 22:29:20312void BookmarkManagerView::SelectInTree(const BookmarkNode* node) {
[email protected]7f856be2008-10-29 23:38:06313 if (!node)
314 return;
315
[email protected]b3c33d462009-06-26 22:29:20316 const BookmarkNode* parent = node->is_url() ? node->GetParent() : node;
[email protected]7f856be2008-10-29 23:38:06317 FolderNode* folder_node = tree_model_->GetFolderNodeForBookmarkNode(parent);
318 if (!folder_node) {
319 NOTREACHED();
320 return;
321 }
322
323 tree_view_->SetSelectedNode(folder_node);
324
325 if (node->is_url()) {
326 int index = table_model_->IndexOfNode(node);
327 if (index != -1)
328 table_view_->Select(index);
[email protected]3ec1a3a2008-11-03 23:14:01329 // TODO(sky): this doesn't work when invoked from add page.
330 table_view_->RequestFocus();
[email protected]7f856be2008-10-29 23:38:06331 }
332}
333
[email protected]b3c33d462009-06-26 22:29:20334void BookmarkManagerView::ExpandAll(const BookmarkNode* node) {
335 const BookmarkNode* parent = node->is_url() ? node->GetParent() : node;
[email protected]b5048992008-11-07 04:31:35336 FolderNode* folder_node = tree_model_->GetFolderNodeForBookmarkNode(parent);
337 if (!folder_node) {
338 NOTREACHED();
339 return;
340 }
341 tree_view_->ExpandAll(folder_node);
342}
343
[email protected]b3c33d462009-06-26 22:29:20344const BookmarkNode* BookmarkManagerView::GetSelectedFolder() {
[email protected]3ec1a3a2008-11-03 23:14:01345 return tree_view_->GetSelectedBookmarkNode();
346}
[email protected]7f856be2008-10-29 23:38:06347
[email protected]b3c33d462009-06-26 22:29:20348std::vector<const BookmarkNode*> BookmarkManagerView::GetSelectedTableNodes() {
349 std::vector<const BookmarkNode*> nodes;
[email protected]7f856be2008-10-29 23:38:06350 for (views::TableView::iterator i = table_view_->SelectionBegin();
351 i != table_view_->SelectionEnd(); ++i) {
352 nodes.push_back(table_model_->GetNodeForRow(*i));
353 }
354 // TableViews iterator iterates in reverse order. Reverse the nodes so they
355 // are opened in visual order.
356 std::reverse(nodes.begin(), nodes.end());
357 return nodes;
358}
359
[email protected]82522512009-05-15 07:37:29360void BookmarkManagerView::PaintBackground(gfx::Canvas* canvas) {
[email protected]57978992009-06-25 01:48:04361 canvas->drawColor(kBackgroundColorBottom, SkXfermode::kSrc_Mode);
[email protected]516c89fd2008-11-14 20:40:51362
363 SkPaint paint;
[email protected]0d74f1e2008-12-12 23:07:42364 paint.setShader(skia::CreateGradientShader(0, kBackgroundGradientHeight,
[email protected]516c89fd2008-11-14 20:40:51365 kBackgroundColorTop,
366 kBackgroundColorBottom))->safeUnref();
367 canvas->FillRectInt(0, 0, width(), kBackgroundGradientHeight, paint);
[email protected]7f856be2008-10-29 23:38:06368}
369
370gfx::Size BookmarkManagerView::GetPreferredSize() {
371 return gfx::Size(views::Window::GetLocalizedContentsSize(
372 IDS_BOOKMARK_MANAGER_DIALOG_WIDTH_CHARS,
373 IDS_BOOKMARK_MANAGER_DIALOG_HEIGHT_LINES));
374}
375
376std::wstring BookmarkManagerView::GetWindowTitle() const {
377 return l10n_util::GetString(IDS_BOOKMARK_MANAGER_TITLE);
378}
379
[email protected]15952e462008-11-14 00:29:05380std::wstring BookmarkManagerView::GetWindowName() const {
381 return prefs::kBookmarkManagerPlacement;
[email protected]7f856be2008-10-29 23:38:06382}
383
[email protected]3ec1a3a2008-11-03 23:14:01384void BookmarkManagerView::WindowClosing() {
385 g_browser_process->local_state()->SetInteger(
[email protected]ea36e322009-06-24 09:40:35386 prefs::kBookmarkManagerSplitLocation, split_view_->divider_offset());
[email protected]3ec1a3a2008-11-03 23:14:01387}
388
[email protected]b220d13f2009-10-09 04:12:46389#if defined(CHROME_PERSONALIZATION)
390void BookmarkManagerView::OnStateChanged() {
391 UpdateSyncStatus();
392}
393#endif
394
[email protected]3da579b2009-05-27 16:36:19395bool BookmarkManagerView::AcceleratorPressed(
396 const views::Accelerator& accelerator) {
397 // Ctrl-W to close bookmark manager.
398 DCHECK(accelerator.GetKeyCode() == 'W' && accelerator.IsCtrlDown());
399 window()->Close();
400 return true;
401}
402
[email protected]7f856be2008-10-29 23:38:06403void BookmarkManagerView::OnDoubleClick() {
[email protected]b3c33d462009-06-26 22:29:20404 std::vector<const BookmarkNode*> nodes = GetSelectedTableNodes();
[email protected]7f856be2008-10-29 23:38:06405 if (nodes.empty())
406 return;
407 if (nodes.size() == 1 && nodes[0]->is_folder()) {
408 // Double click on a folder descends into the folder.
409 SelectInTree(nodes[0]);
410 return;
411 }
412 // TODO(sky): OnDoubleClick needs a handle to the current mouse event so that
413 // we can use
414 // event_utils::DispositionFromEventFlags(sender->mouse_event_flags()) .
415 bookmark_utils::OpenAll(
[email protected]ed8b1b82009-03-12 19:29:06416 GetWidget()->GetNativeView(), profile_, NULL, nodes, CURRENT_TAB);
[email protected]7f856be2008-10-29 23:38:06417}
418
[email protected]03ce2f5b2009-05-22 18:15:16419void BookmarkManagerView::OnMiddleClick() {
[email protected]b3c33d462009-06-26 22:29:20420 std::vector<const BookmarkNode*> nodes = GetSelectedTableNodes();
[email protected]03ce2f5b2009-05-22 18:15:16421 if (nodes.empty())
422 return;
423 if (nodes.size() == 1 && nodes[0]->is_folder()) {
424 // Middle clicking on a folder results in no action.
425 return;
426 }
427
428 bookmark_utils::OpenAll(
429 GetWidget()->GetNativeView(), profile_, NULL, nodes, NEW_FOREGROUND_TAB);
430}
431
[email protected]7f856be2008-10-29 23:38:06432void BookmarkManagerView::OnTableViewDelete(views::TableView* table) {
[email protected]b3c33d462009-06-26 22:29:20433 std::vector<const BookmarkNode*> nodes = GetSelectedTableNodes();
[email protected]7f856be2008-10-29 23:38:06434 if (nodes.empty())
435 return;
436 for (size_t i = 0; i < nodes.size(); ++i) {
437 GetBookmarkModel()->Remove(nodes[i]->GetParent(),
438 nodes[i]->GetParent()->IndexOfChild(nodes[i]));
439 }
440}
441
[email protected]3ec1a3a2008-11-03 23:14:01442void BookmarkManagerView::OnKeyDown(unsigned short virtual_keycode) {
443 switch (virtual_keycode) {
444 case VK_RETURN: {
[email protected]b3c33d462009-06-26 22:29:20445 std::vector<const BookmarkNode*> selected_nodes = GetSelectedTableNodes();
[email protected]fafc8a422008-11-07 17:53:09446 if (selected_nodes.size() == 1 && selected_nodes[0]->is_folder()) {
[email protected]3ec1a3a2008-11-03 23:14:01447 SelectInTree(selected_nodes[0]);
[email protected]fafc8a422008-11-07 17:53:09448 } else {
449 bookmark_utils::OpenAll(
[email protected]ed8b1b82009-03-12 19:29:06450 GetWidget()->GetNativeView(), profile_, NULL, selected_nodes,
[email protected]fafc8a422008-11-07 17:53:09451 CURRENT_TAB);
452 }
[email protected]3ec1a3a2008-11-03 23:14:01453 break;
454 }
455
456 case VK_BACK: {
[email protected]b3c33d462009-06-26 22:29:20457 const BookmarkNode* selected_folder = GetSelectedFolder();
[email protected]3ec1a3a2008-11-03 23:14:01458 if (selected_folder != NULL &&
459 selected_folder->GetParent() != GetBookmarkModel()->root_node()) {
460 SelectInTree(selected_folder->GetParent());
461 }
462 break;
463 }
[email protected]fafc8a422008-11-07 17:53:09464
465 default:
466 OnCutCopyPaste(KeyCodeToCutCopyPaste(virtual_keycode), true);
467 break;
[email protected]3ec1a3a2008-11-03 23:14:01468 }
469}
470
[email protected]7f856be2008-10-29 23:38:06471void BookmarkManagerView::OnTreeViewSelectionChanged(
472 views::TreeView* tree_view) {
[email protected]992c6252009-05-13 18:54:20473 TreeModelNode* node = tree_view_->GetSelectedNode();
[email protected]7f856be2008-10-29 23:38:06474
475 BookmarkTableModel* new_table_model = NULL;
[email protected]b3c33d462009-06-26 22:29:20476 const BookmarkNode* table_parent_node = NULL;
[email protected]b24740e32008-12-11 03:59:44477 bool is_search = false;
[email protected]7f856be2008-10-29 23:38:06478
479 if (node) {
480 switch (tree_model_->GetNodeType(node)) {
481 case BookmarkFolderTreeModel::BOOKMARK:
482 table_parent_node = tree_model_->TreeNodeAsBookmarkNode(node);
483 new_table_model =
484 BookmarkTableModel::CreateBookmarkTableModelForFolder(
485 profile_->GetBookmarkModel(),
486 table_parent_node);
487 break;
488
489 case BookmarkFolderTreeModel::RECENTLY_BOOKMARKED:
490 new_table_model = BookmarkTableModel::CreateRecentlyBookmarkedModel(
491 profile_->GetBookmarkModel());
492 break;
493
494 case BookmarkFolderTreeModel::SEARCH:
[email protected]b24740e32008-12-11 03:59:44495 is_search = true;
[email protected]7f856be2008-10-29 23:38:06496 search_factory_.RevokeAll();
497 new_table_model = CreateSearchTableModel();
498 break;
499
500 default:
501 NOTREACHED();
502 break;
503 }
504 }
505
[email protected]b24740e32008-12-11 03:59:44506 SetTableModel(new_table_model, table_parent_node, is_search);
[email protected]7f856be2008-10-29 23:38:06507}
508
[email protected]fafc8a422008-11-07 17:53:09509void BookmarkManagerView::OnTreeViewKeyDown(unsigned short virtual_keycode) {
510 switch (virtual_keycode) {
511 case VK_DELETE: {
[email protected]b3c33d462009-06-26 22:29:20512 const BookmarkNode* node = GetSelectedFolder();
[email protected]fafc8a422008-11-07 17:53:09513 if (!node || node->GetParent() == GetBookmarkModel()->root_node())
514 return;
515
[email protected]b3c33d462009-06-26 22:29:20516 const BookmarkNode* parent = node->GetParent();
[email protected]fafc8a422008-11-07 17:53:09517 GetBookmarkModel()->Remove(parent, parent->IndexOfChild(node));
518 break;
519 }
520
521 default:
522 OnCutCopyPaste(KeyCodeToCutCopyPaste(virtual_keycode), false);
523 break;
524 }
525}
526
[email protected]b220d13f2009-10-09 04:12:46527#if defined(CHROME_PERSONALIZATION)
528void BookmarkManagerView::ButtonPressed(views::Button* sender,
529 const views::Event& event) {
530 if (sender == sync_status_button_) {
531 UserMetrics::RecordAction(L"BookmarkManager_Sync", profile_);
532 OpenSyncMyBookmarksDialog();
533 }
534}
535#endif
536
[email protected]7f856be2008-10-29 23:38:06537void BookmarkManagerView::Loaded(BookmarkModel* model) {
538 model->RemoveObserver(this);
539 LoadedImpl();
540}
541
[email protected]ba96e33f2009-05-27 19:47:23542void BookmarkManagerView::ContentsChanged(views::Textfield* sender,
[email protected]7f856be2008-10-29 23:38:06543 const std::wstring& new_contents) {
544 search_factory_.RevokeAll();
545 MessageLoop::current()->PostDelayedTask(FROM_HERE,
546 search_factory_.NewRunnableMethod(&BookmarkManagerView::PerformSearch),
547 kSearchDelayMS);
548}
549
[email protected]eea372e2009-05-21 03:43:45550bool BookmarkManagerView::HandleKeystroke(
[email protected]ba96e33f2009-05-27 19:47:23551 views::Textfield* sender,
552 const views::Textfield::Keystroke& key) {
[email protected]5c9e97a2009-09-09 23:48:30553 if (key.GetKeyboardCode() == base::VKEY_RETURN) {
[email protected]7f856be2008-10-29 23:38:06554 PerformSearch();
555 search_tf_->SelectAll();
556 }
[email protected]05d478752009-04-08 23:38:16557
558 return false;
[email protected]7f856be2008-10-29 23:38:06559}
560
561void BookmarkManagerView::ShowContextMenu(views::View* source,
562 int x,
563 int y,
564 bool is_mouse_gesture) {
[email protected]3ec1a3a2008-11-03 23:14:01565 DCHECK(source == table_view_ || source == tree_view_);
566 bool is_table = (source == table_view_);
[email protected]d6d6d582009-10-12 19:22:26567 ShowMenu(x, y,
[email protected]5aa458fc2009-06-27 06:07:55568 is_table ? BookmarkContextMenuController::BOOKMARK_MANAGER_TABLE :
569 BookmarkContextMenuController::BOOKMARK_MANAGER_TREE);
[email protected]3ec1a3a2008-11-03 23:14:01570}
[email protected]7f856be2008-10-29 23:38:06571
[email protected]d6d6d582009-10-12 19:22:26572void BookmarkManagerView::RunMenu(views::View* source, const gfx::Point& pt) {
[email protected]3ec1a3a2008-11-03 23:14:01573 // TODO(glen): when you change the buttons around and what not, futz with
574 // this to make it look good. If you end up keeping padding numbers make them
575 // constants.
[email protected]b5048992008-11-07 04:31:35576 if (!GetBookmarkModel()->IsLoaded())
577 return;
578
[email protected]f71e7262009-05-14 19:13:13579 int menu_x = pt.x();
[email protected]a40c12a2008-12-12 18:32:22580 menu_x += UILayoutIsRightToLeft() ? (source->width() - 5) :
581 (-source->width() + 5);
[email protected]b5048992008-11-07 04:31:35582 if (source->GetID() == kOrganizeMenuButtonID) {
[email protected]d6d6d582009-10-12 19:22:26583 ShowMenu(menu_x, pt.y() + 2,
[email protected]5aa458fc2009-06-27 06:07:55584 BookmarkContextMenuController::BOOKMARK_MANAGER_ORGANIZE_MENU);
[email protected]b5048992008-11-07 04:31:35585 } else if (source->GetID() == kToolsMenuButtonID) {
[email protected]d6d6d582009-10-12 19:22:26586 ShowToolsMenu(menu_x, pt.y() + 2);
[email protected]b5048992008-11-07 04:31:35587 } else {
588 NOTREACHED();
589 }
590}
591
592void BookmarkManagerView::ExecuteCommand(int id) {
593 switch (id) {
594 case IDS_BOOKMARK_MANAGER_IMPORT_MENU:
595 UserMetrics::RecordAction(L"BookmarkManager_Import", profile_);
596 ShowImportBookmarksFileChooser();
597 break;
598
599 case IDS_BOOKMARK_MANAGER_EXPORT_MENU:
600 UserMetrics::RecordAction(L"BookmarkManager_Export", profile_);
601 ShowExportBookmarksFileChooser();
602 break;
603
604 default:
605 NOTREACHED();
606 break;
607 }
608}
609
[email protected]561abe62009-04-06 18:08:34610void BookmarkManagerView::FileSelected(const FilePath& path,
[email protected]23b357b2009-03-30 20:02:36611 int index,
[email protected]b5048992008-11-07 04:31:35612 void* params) {
613 int id = reinterpret_cast<int>(params);
614 if (id == IDS_BOOKMARK_MANAGER_IMPORT_MENU) {
615 // ImporterHost is ref counted and will delete itself when done.
616 ImporterHost* host = new ImporterHost();
617 ProfileInfo profile_info;
618 profile_info.browser_type = BOOKMARKS_HTML;
[email protected]561abe62009-04-06 18:08:34619 profile_info.source_path = path.ToWStringHack();
[email protected]ed8b1b82009-03-12 19:29:06620 StartImportingWithUI(GetWidget()->GetNativeView(), FAVORITES, host,
[email protected]b5048992008-11-07 04:31:35621 profile_info, profile_,
622 new ImportObserverImpl(profile()), false);
623 } else if (id == IDS_BOOKMARK_MANAGER_EXPORT_MENU) {
624 if (g_browser_process->io_thread()) {
625 bookmark_html_writer::WriteBookmarks(
626 g_browser_process->io_thread()->message_loop(), GetBookmarkModel(),
[email protected]304ec81a2009-09-25 15:15:04627 path);
[email protected]b5048992008-11-07 04:31:35628 }
629 } else {
630 NOTREACHED();
631 }
632}
633
634void BookmarkManagerView::FileSelectionCanceled(void* params) {
635 select_file_dialog_ = NULL;
[email protected]7f856be2008-10-29 23:38:06636}
637
638BookmarkTableModel* BookmarkManagerView::CreateSearchTableModel() {
[email protected]91632662009-05-28 20:54:06639 std::wstring search_text = search_tf_->text();
[email protected]7f856be2008-10-29 23:38:06640 if (search_text.empty())
641 return NULL;
[email protected]fa5dfaf2009-06-02 22:12:06642 std::wstring languages =
643 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages);
644 return BookmarkTableModel::CreateSearchTableModel(
645 GetBookmarkModel(), search_text, languages);
[email protected]7f856be2008-10-29 23:38:06646}
647
648void BookmarkManagerView::SetTableModel(BookmarkTableModel* new_table_model,
[email protected]b3c33d462009-06-26 22:29:20649 const BookmarkNode* parent_node,
[email protected]b24740e32008-12-11 03:59:44650 bool is_search) {
[email protected]7f856be2008-10-29 23:38:06651 // Be sure and reset the model on the view before updating table_model_.
652 // Otherwise the view will attempt to use the deleted model when we set the
653 // new one.
654 table_view_->SetModel(NULL);
655 table_view_->SetShowPathColumn(!parent_node);
656 table_view_->SetModel(new_table_model);
657 table_view_->set_parent_node(parent_node);
658 table_model_.reset(new_table_model);
[email protected]b24740e32008-12-11 03:59:44659 if (!is_search || (new_table_model && new_table_model->RowCount() > 0)) {
660 table_view_->SetAltText(std::wstring());
[email protected]91632662009-05-28 20:54:06661 } else if (search_tf_->text().empty()) {
[email protected]b24740e32008-12-11 03:59:44662 table_view_->SetAltText(
663 l10n_util::GetString(IDS_BOOKMARK_MANAGER_NO_SEARCH_TEXT));
664 } else {
665 table_view_->SetAltText(
666 l10n_util::GetStringF(IDS_BOOKMARK_MANAGER_NO_RESULTS,
[email protected]91632662009-05-28 20:54:06667 search_tf_->text()));
[email protected]b24740e32008-12-11 03:59:44668 }
[email protected]7f856be2008-10-29 23:38:06669}
670
671void BookmarkManagerView::PerformSearch() {
672 search_factory_.RevokeAll();
673 // Reset the controller, otherwise when we change the selection we'll get
674 // notified and update the model twice.
675 tree_view_->SetController(NULL);
676 tree_view_->SetSelectedNode(tree_model_->search_node());
677 tree_view_->SetController(this);
[email protected]b24740e32008-12-11 03:59:44678 SetTableModel(CreateSearchTableModel(), NULL, true);
[email protected]7f856be2008-10-29 23:38:06679}
680
681void BookmarkManagerView::PrepareForShow() {
[email protected]3ec1a3a2008-11-03 23:14:01682 // Restore the split location, but don't let it get too small (or big),
683 // otherwise users might inadvertently not see the divider.
684 int split_x = g_browser_process->local_state()->GetInteger(
685 prefs::kBookmarkManagerSplitLocation);
686 if (split_x == -1) {
687 // First time running the bookmark manager, give a third of the width to
688 // the tree.
689 split_x = split_view_->width() / 3;
690 }
691 int min_split_size = split_view_->width() / 8;
692 // Make sure the user can see both the tree/table.
693 split_x = std::min(split_view_->width() - min_split_size,
694 std::max(min_split_size, split_x));
[email protected]ea36e322009-06-24 09:40:35695 split_view_->set_divider_offset(split_x);
[email protected]7f856be2008-10-29 23:38:06696 if (!GetBookmarkModel()->IsLoaded()) {
697 search_tf_->SetReadOnly(true);
698 return;
699 }
700
701 LoadedImpl();
702}
703
704void BookmarkManagerView::LoadedImpl() {
705 BookmarkModel* bookmark_model = GetBookmarkModel();
[email protected]b3c33d462009-06-26 22:29:20706 const BookmarkNode* bookmark_bar_node = bookmark_model->GetBookmarkBarNode();
[email protected]7f856be2008-10-29 23:38:06707 table_model_.reset(
708 BookmarkTableModel::CreateBookmarkTableModelForFolder(bookmark_model,
709 bookmark_bar_node));
710 table_view_->SetModel(table_model_.get());
711 table_view_->set_parent_node(bookmark_bar_node);
712
713 tree_model_.reset(new BookmarkFolderTreeModel(bookmark_model));
714 tree_view_->SetModel(tree_model_.get());
715
716 tree_view_->ExpandAll();
717
718 tree_view_->SetSelectedNode(
719 tree_model_->GetFolderNodeForBookmarkNode(bookmark_bar_node));
720
721 search_tf_->SetReadOnly(false);
722 search_tf_->SetController(this);
723
724 Layout();
725 SchedulePaint();
726}
727
728BookmarkModel* BookmarkManagerView::GetBookmarkModel() const {
729 return profile_->GetBookmarkModel();
730}
[email protected]3ec1a3a2008-11-03 23:14:01731
732void BookmarkManagerView::ShowMenu(
[email protected]d6d6d582009-10-12 19:22:26733 int x, int y, BookmarkContextMenuController::ConfigurationType config) {
[email protected]3ec1a3a2008-11-03 23:14:01734 if (!GetBookmarkModel()->IsLoaded())
735 return;
736
[email protected]5aa458fc2009-06-27 06:07:55737 if (config == BookmarkContextMenuController::BOOKMARK_MANAGER_TABLE ||
[email protected]580522632009-08-17 21:55:55738 (config ==
739 BookmarkContextMenuController::BOOKMARK_MANAGER_ORGANIZE_MENU &&
[email protected]3ec1a3a2008-11-03 23:14:01740 table_view_->HasFocus())) {
[email protected]b3c33d462009-06-26 22:29:20741 std::vector<const BookmarkNode*> nodes = GetSelectedTableNodes();
742 const BookmarkNode* parent = GetSelectedFolder();
[email protected]5a433972008-11-05 15:39:44743 if (!parent) {
[email protected]5aa458fc2009-06-27 06:07:55744 if (config == BookmarkContextMenuController::BOOKMARK_MANAGER_TABLE) {
745 config = BookmarkContextMenuController::BOOKMARK_MANAGER_TABLE_OTHER;
746 } else {
747 config =
748 BookmarkContextMenuController::BOOKMARK_MANAGER_ORGANIZE_MENU_OTHER;
749 }
[email protected]5a433972008-11-05 15:39:44750 }
[email protected]d6d6d582009-10-12 19:22:26751 BookmarkContextMenu menu(GetWindow()->GetNativeWindow(), profile_, NULL,
752 parent, nodes, config);
[email protected]5aa458fc2009-06-27 06:07:55753 menu.RunMenuAt(gfx::Point(x, y));
[email protected]3ec1a3a2008-11-03 23:14:01754 } else {
[email protected]b3c33d462009-06-26 22:29:20755 const BookmarkNode* node = GetSelectedFolder();
756 std::vector<const BookmarkNode*> nodes;
[email protected]5a433972008-11-05 15:39:44757 if (node)
758 nodes.push_back(node);
[email protected]5aa458fc2009-06-27 06:07:55759 BookmarkContextMenu menu(GetWidget()->GetNativeView(), profile_, NULL,
[email protected]3ec1a3a2008-11-03 23:14:01760 node, nodes, config);
[email protected]5aa458fc2009-06-27 06:07:55761 menu.RunMenuAt(gfx::Point(x, y));
[email protected]3ec1a3a2008-11-03 23:14:01762 }
763}
[email protected]b5048992008-11-07 04:31:35764
[email protected]fafc8a422008-11-07 17:53:09765void BookmarkManagerView::OnCutCopyPaste(CutCopyPasteType type,
766 bool from_table) {
767 if (type == CUT || type == COPY) {
[email protected]b3c33d462009-06-26 22:29:20768 std::vector<const BookmarkNode*> nodes;
[email protected]fafc8a422008-11-07 17:53:09769 if (from_table) {
770 nodes = GetSelectedTableNodes();
771 } else {
[email protected]b3c33d462009-06-26 22:29:20772 const BookmarkNode* node = GetSelectedFolder();
[email protected]fafc8a422008-11-07 17:53:09773 if (!node || node->GetParent() == GetBookmarkModel()->root_node())
774 return;
775 nodes.push_back(node);
776 }
777 if (nodes.empty())
778 return;
779
780 bookmark_utils::CopyToClipboard(GetBookmarkModel(), nodes, type == CUT);
781 } else if (type == PASTE) {
782 int index = from_table ? table_view_->FirstSelectedRow() : -1;
783 if (index != -1)
784 index++;
785 bookmark_utils::PasteFromClipboard(GetBookmarkModel(), GetSelectedFolder(),
786 index);
787 }
788}
789
[email protected]d6d6d582009-10-12 19:22:26790void BookmarkManagerView::ShowToolsMenu(int x, int y) {
[email protected]b5048992008-11-07 04:31:35791 views::MenuItemView menu(this);
792 menu.AppendMenuItemWithLabel(
793 IDS_BOOKMARK_MANAGER_IMPORT_MENU,
794 l10n_util::GetString(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
795 menu.AppendMenuItemWithLabel(
796 IDS_BOOKMARK_MANAGER_EXPORT_MENU,
797 l10n_util::GetString(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
[email protected]a40c12a2008-12-12 18:32:22798 views::MenuItemView::AnchorPosition anchor =
799 UILayoutIsRightToLeft() ? views::MenuItemView::TOPRIGHT :
800 views::MenuItemView::TOPLEFT;
[email protected]ed8b1b82009-03-12 19:29:06801 menu.RunMenuAt(GetWidget()->GetNativeView(), gfx::Rect(x, y, 0, 0), anchor,
802 true);
[email protected]b5048992008-11-07 04:31:35803}
804
805void BookmarkManagerView::ShowImportBookmarksFileChooser() {
806 if (select_file_dialog_.get())
807 select_file_dialog_->ListenerDestroyed();
808
[email protected]b949f1112009-04-12 20:03:08809 SelectFileDialog::FileTypeInfo file_type_info;
810 file_type_info.extensions.resize(1);
811 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html"));
812 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("htm"));
813 file_type_info.include_all_files = true;
[email protected]b5048992008-11-07 04:31:35814 select_file_dialog_ = SelectFileDialog::Create(this);
815 select_file_dialog_->SelectFile(
[email protected]561abe62009-04-06 18:08:34816 SelectFileDialog::SELECT_OPEN_FILE, std::wstring(),
[email protected]b949f1112009-04-12 20:03:08817 FilePath(FILE_PATH_LITERAL("bookmarks.html")), &file_type_info, 0,
818 std::wstring(), GetWidget()->GetNativeView(),
[email protected]b5048992008-11-07 04:31:35819 reinterpret_cast<void*>(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
820}
821
822void BookmarkManagerView::ShowExportBookmarksFileChooser() {
823 if (select_file_dialog_.get())
824 select_file_dialog_->ListenerDestroyed();
825
[email protected]b949f1112009-04-12 20:03:08826 SelectFileDialog::FileTypeInfo file_type_info;
827 file_type_info.extensions.resize(1);
828 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html"));
829 file_type_info.include_all_files = true;
[email protected]b5048992008-11-07 04:31:35830 select_file_dialog_ = SelectFileDialog::Create(this);
831 select_file_dialog_->SelectFile(
[email protected]561abe62009-04-06 18:08:34832 SelectFileDialog::SELECT_SAVEAS_FILE, std::wstring(),
[email protected]b949f1112009-04-12 20:03:08833 FilePath(FILE_PATH_LITERAL("bookmarks.html")), &file_type_info, 0,
834 L"html", GetWidget()->GetNativeView(),
[email protected]b5048992008-11-07 04:31:35835 reinterpret_cast<void*>(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
836}
[email protected]b220d13f2009-10-09 04:12:46837
838#if defined(CHROME_PERSONALIZATION)
839void BookmarkManagerView::UpdateSyncStatus() {
840 DCHECK(sync_service_);
841 std::wstring status_label;
842 std::wstring link_label;
843 bool synced = SyncStatusUIHelper::GetLabels(sync_service_,
844 &status_label, &link_label) == SyncStatusUIHelper::SYNCED;
845
846 if (sync_service_->HasSyncSetupCompleted()) {
847 std::wstring username = UTF16ToWide(
848 sync_service_->GetAuthenticatedUsername());
849 status_label = l10n_util::GetStringF(IDS_SYNC_NTP_SYNCED_TO, username);
850 } else if (!sync_service_->SetupInProgress() && !synced) {
851 status_label = l10n_util::GetString(IDS_SYNC_START_SYNC_BUTTON_LABEL);
852 }
853 sync_status_button_->SetText(status_label);
854 sync_status_button_->GetParent()->Layout();
855}
856
857void BookmarkManagerView::OpenSyncMyBookmarksDialog() {
858 if (!sync_service_)
859 return;
860 if (sync_service_->HasSyncSetupCompleted()) {
861 ShowOptionsWindow(OPTIONS_PAGE_CONTENT, OPTIONS_GROUP_NONE, profile_);
862 } else {
863 sync_service_->EnableForUser();
864 ProfileSyncService::SyncEvent(
865 ProfileSyncService::START_FROM_BOOKMARK_MANAGER);
866 }
867}
868#endif