| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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/views/bookmark_manager_view.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "app/l10n_util.h" | |
| 10 #include "app/resource_bundle.h" | |
| 11 #include "base/keyboard_codes.h" | |
| 12 #include "base/thread.h" | |
| 13 #include "chrome/browser/bookmarks/bookmark_folder_tree_model.h" | |
| 14 #include "chrome/browser/bookmarks/bookmark_html_writer.h" | |
| 15 #include "chrome/browser/bookmarks/bookmark_manager.h" | |
| 16 #include "chrome/browser/bookmarks/bookmark_model.h" | |
| 17 #include "chrome/browser/bookmarks/bookmark_table_model.h" | |
| 18 #include "chrome/browser/bookmarks/bookmark_utils.h" | |
| 19 #include "chrome/browser/browser_list.h" | |
| 20 #include "chrome/browser/browser_process.h" | |
| 21 #include "chrome/browser/importer/importer.h" | |
| 22 #include "chrome/browser/importer/importer_data_types.h" | |
| 23 #include "chrome/browser/metrics/user_metrics.h" | |
| 24 #include "chrome/browser/pref_service.h" | |
| 25 #include "chrome/browser/profile.h" | |
| 26 #include "chrome/browser/sync/sync_ui_util.h" | |
| 27 #include "chrome/browser/views/bookmark_editor_view.h" | |
| 28 #include "chrome/browser/views/bookmark_folder_tree_view.h" | |
| 29 #include "chrome/browser/views/bookmark_table_view.h" | |
| 30 #include "chrome/common/pref_names.h" | |
| 31 #include "gfx/canvas.h" | |
| 32 #include "gfx/color_utils.h" | |
| 33 #include "gfx/skia_util.h" | |
| 34 #include "grit/generated_resources.h" | |
| 35 #include "grit/locale_settings.h" | |
| 36 #include "grit/theme_resources.h" | |
| 37 #include "third_party/skia/include/core/SkShader.h" | |
| 38 #include "views/controls/button/menu_button.h" | |
| 39 #include "views/controls/label.h" | |
| 40 #include "views/controls/menu/menu_item_view.h" | |
| 41 #include "views/controls/single_split_view.h" | |
| 42 #include "views/grid_layout.h" | |
| 43 #include "views/standard_layout.h" | |
| 44 #include "views/widget/widget.h" | |
| 45 #include "views/window/window.h" | |
| 46 | |
| 47 // If non-null, there is an open editor and this is the window it is contained | |
| 48 // in it. | |
| 49 static views::Window* open_window = NULL; | |
| 50 // And this is the manager contained in it. | |
| 51 static BookmarkManagerView* manager = NULL; | |
| 52 | |
| 53 // Delay, in ms, between when the user types and when we run the search. | |
| 54 static const int kSearchDelayMS = 200; | |
| 55 | |
| 56 static const int kOrganizeMenuButtonID = 1; | |
| 57 static const int kToolsMenuButtonID = 2; | |
| 58 | |
| 59 // Background color. | |
| 60 static const SkColor kBackgroundColorTop = SkColorSetRGB(242, 247, 253); | |
| 61 static const SkColor kBackgroundColorBottom = SkColorSetRGB(223, 234, 248); | |
| 62 static const int kBackgroundGradientHeight = 28; | |
| 63 | |
| 64 namespace { | |
| 65 | |
| 66 // Observer installed on the importer. When done importing the newly created | |
| 67 // folder is selected in the bookmark manager. | |
| 68 class ImportObserverImpl : public ImportObserver { | |
| 69 public: | |
| 70 explicit ImportObserverImpl(Profile* profile) : profile_(profile) { | |
| 71 BookmarkModel* model = profile->GetBookmarkModel(); | |
| 72 initial_other_count_ = model->other_node()->GetChildCount(); | |
| 73 } | |
| 74 | |
| 75 virtual void ImportCanceled() { | |
| 76 delete this; | |
| 77 } | |
| 78 | |
| 79 virtual void ImportComplete() { | |
| 80 // We aren't needed anymore. | |
| 81 MessageLoop::current()->DeleteSoon(FROM_HERE, this); | |
| 82 | |
| 83 BookmarkManagerView* manager = BookmarkManagerView::current(); | |
| 84 if (!manager || manager->profile() != profile_) | |
| 85 return; | |
| 86 | |
| 87 BookmarkModel* model = profile_->GetBookmarkModel(); | |
| 88 int other_count = model->other_node()->GetChildCount(); | |
| 89 if (other_count == initial_other_count_ + 1) { | |
| 90 const BookmarkNode* imported_node = | |
| 91 model->other_node()->GetChild(initial_other_count_); | |
| 92 manager->SelectInTree(imported_node); | |
| 93 manager->ExpandAll(imported_node); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 private: | |
| 98 Profile* profile_; | |
| 99 // Number of children in the other bookmarks folder at the time we were | |
| 100 // created. | |
| 101 int initial_other_count_; | |
| 102 | |
| 103 DISALLOW_COPY_AND_ASSIGN(ImportObserverImpl); | |
| 104 }; | |
| 105 | |
| 106 // Converts a virtual keycode into the CutCopyPasteType. | |
| 107 BookmarkManagerView::CutCopyPasteType KeyCodeToCutCopyPaste( | |
| 108 base::KeyboardCode keycode) { | |
| 109 switch (keycode) { | |
| 110 case base::VKEY_INSERT: | |
| 111 if (GetKeyState(VK_CONTROL) < 0) | |
| 112 return BookmarkManagerView::COPY; | |
| 113 if (GetKeyState(VK_SHIFT) < 0) | |
| 114 return BookmarkManagerView::PASTE; | |
| 115 return BookmarkManagerView::NONE; | |
| 116 | |
| 117 case base::VKEY_DELETE: | |
| 118 if (GetKeyState(VK_SHIFT) < 0) | |
| 119 return BookmarkManagerView::CUT; | |
| 120 return BookmarkManagerView::NONE; | |
| 121 | |
| 122 case base::VKEY_C: | |
| 123 if (GetKeyState(VK_CONTROL) < 0) | |
| 124 return BookmarkManagerView::COPY; | |
| 125 return BookmarkManagerView::NONE; | |
| 126 | |
| 127 case base::VKEY_V: | |
| 128 if (GetKeyState(VK_CONTROL) < 0) | |
| 129 return BookmarkManagerView::PASTE; | |
| 130 return BookmarkManagerView::NONE; | |
| 131 | |
| 132 case base::VKEY_X: | |
| 133 if (GetKeyState(VK_CONTROL) < 0) | |
| 134 return BookmarkManagerView::CUT; | |
| 135 return BookmarkManagerView::NONE; | |
| 136 | |
| 137 default: | |
| 138 return BookmarkManagerView::NONE; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 } // namespace | |
| 143 | |
| 144 namespace browser { | |
| 145 | |
| 146 // Declared in browser_dialogs.h so others don't need to depend on our header. | |
| 147 void ShowBookmarkManagerView(Profile* profile) { | |
| 148 BookmarkManagerView::Show(profile); | |
| 149 } | |
| 150 | |
| 151 } // namespace browser | |
| 152 | |
| 153 // BookmarkManager ------------------------------------------------------------- | |
| 154 | |
| 155 void BookmarkManager::SelectInTree(Profile* profile, const BookmarkNode* node) { | |
| 156 if (manager && manager->profile() == profile) | |
| 157 manager->SelectInTree(node); | |
| 158 } | |
| 159 | |
| 160 void BookmarkManager::Show(Profile* profile) { | |
| 161 BookmarkManagerView::Show(profile); | |
| 162 } | |
| 163 | |
| 164 // ----------------------------------------------------------------------------- | |
| 165 | |
| 166 BookmarkManagerView::BookmarkManagerView(Profile* profile) | |
| 167 : profile_(profile->GetOriginalProfile()), | |
| 168 table_view_(NULL), | |
| 169 tree_view_(NULL), | |
| 170 sync_status_button_(NULL), | |
| 171 sync_service_(NULL), | |
| 172 sync_relogin_required_(false), | |
| 173 ALLOW_THIS_IN_INITIALIZER_LIST(search_factory_(this)) { | |
| 174 views::Label* search_label = new views::Label( | |
| 175 l10n_util::GetString(IDS_BOOKMARK_MANAGER_SEARCH_TITLE)); | |
| 176 search_tf_ = new views::Textfield(); | |
| 177 search_tf_->set_default_width_in_chars(30); | |
| 178 search_tf_->SetAccessibleName(search_label->GetText()); | |
| 179 | |
| 180 table_view_ = new BookmarkTableView(profile_, NULL); | |
| 181 table_view_->SetObserver(this); | |
| 182 table_view_->SetContextMenuController(this); | |
| 183 | |
| 184 tree_view_ = new BookmarkFolderTreeView(profile_, NULL); | |
| 185 tree_view_->SetController(this); | |
| 186 tree_view_->SetContextMenuController(this); | |
| 187 | |
| 188 views::MenuButton* organize_menu_button = new views::MenuButton( | |
| 189 NULL, l10n_util::GetString(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU), | |
| 190 this, true); | |
| 191 organize_menu_button->SetID(kOrganizeMenuButtonID); | |
| 192 | |
| 193 views::MenuButton* tools_menu_button = new views::MenuButton( | |
| 194 NULL, l10n_util::GetString(IDS_BOOKMARK_MANAGER_TOOLS_MENU), | |
| 195 this, true); | |
| 196 tools_menu_button->SetID(kToolsMenuButtonID); | |
| 197 | |
| 198 split_view_ = new views::SingleSplitView(tree_view_, table_view_, | |
| 199 views::SingleSplitView::HORIZONTAL_SPLIT); | |
| 200 split_view_->set_resize_leading_on_bounds_change(false); | |
| 201 split_view_->set_background( | |
| 202 views::Background::CreateSolidBackground(kBackgroundColorBottom)); | |
| 203 | |
| 204 views::GridLayout* layout = new views::GridLayout(this); | |
| 205 SetLayoutManager(layout); | |
| 206 const int top_id = 1; | |
| 207 const int split_cs_id = 2; | |
| 208 layout->SetInsets(2, 0, 0, 0); // 2px padding above content. | |
| 209 views::ColumnSet* column_set = layout->AddColumnSet(top_id); | |
| 210 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, | |
| 211 0, views::GridLayout::USE_PREF, 0, 0); | |
| 212 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, | |
| 213 0, views::GridLayout::USE_PREF, 0, 0); | |
| 214 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, | |
| 215 0, views::GridLayout::USE_PREF, 0, 0); | |
| 216 column_set->AddPaddingColumn(1, kUnrelatedControlHorizontalSpacing); | |
| 217 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, | |
| 218 0, views::GridLayout::USE_PREF, 0, 0); | |
| 219 column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing); | |
| 220 column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, | |
| 221 0, views::GridLayout::USE_PREF, 0, 0); | |
| 222 column_set->AddPaddingColumn(0, 3); // 3px padding at end of row. | |
| 223 | |
| 224 column_set = layout->AddColumnSet(split_cs_id); | |
| 225 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, | |
| 226 views::GridLayout::USE_PREF, 0, 0); | |
| 227 | |
| 228 layout->StartRow(0, top_id); | |
| 229 layout->AddView(organize_menu_button); | |
| 230 layout->AddView(tools_menu_button); | |
| 231 sync_status_button_ = new views::TextButton(this, std::wstring()); | |
| 232 layout->AddView(sync_status_button_); | |
| 233 layout->AddView(search_label); | |
| 234 layout->AddView(search_tf_); | |
| 235 | |
| 236 layout->AddPaddingRow(0, 3); // 3px padding between rows. | |
| 237 | |
| 238 layout->StartRow(1, split_cs_id); | |
| 239 layout->AddView(split_view_); | |
| 240 | |
| 241 // Press Ctrl-W to close bookmark manager window. | |
| 242 AddAccelerator(views::Accelerator(base::VKEY_W, false, true, false)); | |
| 243 | |
| 244 BookmarkModel* bookmark_model = profile_->GetBookmarkModel(); | |
| 245 if (!bookmark_model->IsLoaded()) | |
| 246 bookmark_model->AddObserver(this); | |
| 247 | |
| 248 if (profile_->GetProfileSyncService()) { | |
| 249 sync_service_ = profile_->GetProfileSyncService(); | |
| 250 sync_service_->AddObserver(this); | |
| 251 UpdateSyncStatus(); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 BookmarkManagerView::~BookmarkManagerView() { | |
| 256 if (select_file_dialog_.get()) | |
| 257 select_file_dialog_->ListenerDestroyed(); | |
| 258 | |
| 259 if (!GetBookmarkModel()->IsLoaded()) { | |
| 260 GetBookmarkModel()->RemoveObserver(this); | |
| 261 } else { | |
| 262 // The models are deleted before the views. Make sure we set the models of | |
| 263 // the views to NULL so that they aren't left holding a reference to a | |
| 264 // deleted model. | |
| 265 table_view_->SetModel(NULL); | |
| 266 tree_view_->SetModel(NULL); | |
| 267 } | |
| 268 manager = NULL; | |
| 269 open_window = NULL; | |
| 270 | |
| 271 if (sync_service_) | |
| 272 sync_service_->RemoveObserver(this); | |
| 273 } | |
| 274 | |
| 275 // static | |
| 276 void BookmarkManagerView::Show(Profile* profile) { | |
| 277 if (!profile->GetBookmarkModel()) | |
| 278 return; | |
| 279 | |
| 280 if (open_window != NULL) { | |
| 281 open_window->Activate(); | |
| 282 return; | |
| 283 } | |
| 284 | |
| 285 // Both of these will be deleted when the dialog closes. | |
| 286 manager = new BookmarkManagerView(profile); | |
| 287 | |
| 288 // Create the window. | |
| 289 open_window = views::Window::CreateChromeWindow(NULL, gfx::Rect(), manager); | |
| 290 // Let the manager know it's parented. | |
| 291 manager->PrepareForShow(); | |
| 292 // And show it. | |
| 293 open_window->Show(); | |
| 294 | |
| 295 // Give initial focus to the search field. | |
| 296 manager->search_tf_->RequestFocus(); | |
| 297 } | |
| 298 | |
| 299 // static | |
| 300 BookmarkManagerView* BookmarkManagerView::current() { | |
| 301 return manager; | |
| 302 } | |
| 303 | |
| 304 void BookmarkManagerView::SelectInTree(const BookmarkNode* node) { | |
| 305 if (!node) | |
| 306 return; | |
| 307 | |
| 308 const BookmarkNode* parent = node->is_url() ? node->GetParent() : node; | |
| 309 FolderNode* folder_node = tree_model_->GetFolderNodeForBookmarkNode(parent); | |
| 310 if (!folder_node) { | |
| 311 NOTREACHED(); | |
| 312 return; | |
| 313 } | |
| 314 | |
| 315 tree_view_->SetSelectedNode(folder_node); | |
| 316 | |
| 317 if (node->is_url()) { | |
| 318 int index = table_model_->IndexOfNode(node); | |
| 319 if (index != -1) | |
| 320 table_view_->Select(index); | |
| 321 // TODO(sky): this doesn't work when invoked from add page. | |
| 322 table_view_->RequestFocus(); | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 void BookmarkManagerView::ExpandAll(const BookmarkNode* node) { | |
| 327 const BookmarkNode* parent = node->is_url() ? node->GetParent() : node; | |
| 328 FolderNode* folder_node = tree_model_->GetFolderNodeForBookmarkNode(parent); | |
| 329 if (!folder_node) { | |
| 330 NOTREACHED(); | |
| 331 return; | |
| 332 } | |
| 333 tree_view_->ExpandAll(folder_node); | |
| 334 } | |
| 335 | |
| 336 const BookmarkNode* BookmarkManagerView::GetSelectedFolder() { | |
| 337 return tree_view_->GetSelectedBookmarkNode(); | |
| 338 } | |
| 339 | |
| 340 std::vector<const BookmarkNode*> BookmarkManagerView::GetSelectedTableNodes() { | |
| 341 std::vector<const BookmarkNode*> nodes; | |
| 342 for (views::TableView::iterator i = table_view_->SelectionBegin(); | |
| 343 i != table_view_->SelectionEnd(); ++i) { | |
| 344 nodes.push_back(table_model_->GetNodeForRow(*i)); | |
| 345 } | |
| 346 // TableViews iterator iterates in reverse order. Reverse the nodes so they | |
| 347 // are opened in visual order. | |
| 348 std::reverse(nodes.begin(), nodes.end()); | |
| 349 return nodes; | |
| 350 } | |
| 351 | |
| 352 void BookmarkManagerView::PaintBackground(gfx::Canvas* canvas) { | |
| 353 canvas->drawColor(kBackgroundColorBottom, SkXfermode::kSrc_Mode); | |
| 354 | |
| 355 SkPaint paint; | |
| 356 paint.setShader(gfx::CreateGradientShader(0, kBackgroundGradientHeight, | |
| 357 kBackgroundColorTop, | |
| 358 kBackgroundColorBottom))->safeUnref(); | |
| 359 canvas->FillRectInt(0, 0, width(), kBackgroundGradientHeight, paint); | |
| 360 } | |
| 361 | |
| 362 gfx::Size BookmarkManagerView::GetPreferredSize() { | |
| 363 return gfx::Size(views::Window::GetLocalizedContentsSize( | |
| 364 IDS_BOOKMARK_MANAGER_DIALOG_WIDTH_CHARS, | |
| 365 IDS_BOOKMARK_MANAGER_DIALOG_HEIGHT_LINES)); | |
| 366 } | |
| 367 | |
| 368 std::wstring BookmarkManagerView::GetWindowTitle() const { | |
| 369 return l10n_util::GetString(IDS_BOOKMARK_MANAGER_TITLE); | |
| 370 } | |
| 371 | |
| 372 std::wstring BookmarkManagerView::GetWindowName() const { | |
| 373 return prefs::kBookmarkManagerPlacement; | |
| 374 } | |
| 375 | |
| 376 void BookmarkManagerView::WindowClosing() { | |
| 377 g_browser_process->local_state()->SetInteger( | |
| 378 prefs::kBookmarkManagerSplitLocation, split_view_->divider_offset()); | |
| 379 } | |
| 380 | |
| 381 void BookmarkManagerView::OnStateChanged() { | |
| 382 UpdateSyncStatus(); | |
| 383 } | |
| 384 | |
| 385 bool BookmarkManagerView::AcceleratorPressed( | |
| 386 const views::Accelerator& accelerator) { | |
| 387 // Ctrl-W to close bookmark manager. | |
| 388 DCHECK(accelerator.GetKeyCode() == 'W' && accelerator.IsCtrlDown()); | |
| 389 window()->Close(); | |
| 390 return true; | |
| 391 } | |
| 392 | |
| 393 void BookmarkManagerView::OnDoubleClick() { | |
| 394 std::vector<const BookmarkNode*> nodes = GetSelectedTableNodes(); | |
| 395 if (nodes.empty()) | |
| 396 return; | |
| 397 if (nodes.size() == 1 && nodes[0]->is_folder()) { | |
| 398 // Double click on a folder descends into the folder. | |
| 399 SelectInTree(nodes[0]); | |
| 400 return; | |
| 401 } | |
| 402 // TODO(sky): OnDoubleClick needs a handle to the current mouse event so that | |
| 403 // we can use | |
| 404 // event_utils::DispositionFromEventFlags(sender->mouse_event_flags()) . | |
| 405 bookmark_utils::OpenAll( | |
| 406 GetWidget()->GetNativeView(), profile_, NULL, nodes, CURRENT_TAB); | |
| 407 } | |
| 408 | |
| 409 void BookmarkManagerView::OnMiddleClick() { | |
| 410 std::vector<const BookmarkNode*> nodes = GetSelectedTableNodes(); | |
| 411 if (nodes.empty()) | |
| 412 return; | |
| 413 if (nodes.size() == 1 && nodes[0]->is_folder()) { | |
| 414 // Middle clicking on a folder results in no action. | |
| 415 return; | |
| 416 } | |
| 417 | |
| 418 bookmark_utils::OpenAll( | |
| 419 GetWidget()->GetNativeView(), profile_, NULL, nodes, NEW_FOREGROUND_TAB); | |
| 420 } | |
| 421 | |
| 422 void BookmarkManagerView::OnTableViewDelete(views::TableView* table) { | |
| 423 std::vector<const BookmarkNode*> nodes = GetSelectedTableNodes(); | |
| 424 if (nodes.empty()) | |
| 425 return; | |
| 426 for (size_t i = 0; i < nodes.size(); ++i) { | |
| 427 GetBookmarkModel()->Remove(nodes[i]->GetParent(), | |
| 428 nodes[i]->GetParent()->IndexOfChild(nodes[i])); | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 void BookmarkManagerView::OnKeyDown(base::KeyboardCode keycode) { | |
| 433 switch (keycode) { | |
| 434 case base::VKEY_RETURN: { | |
| 435 std::vector<const BookmarkNode*> selected_nodes = GetSelectedTableNodes(); | |
| 436 if (selected_nodes.size() == 1 && selected_nodes[0]->is_folder()) { | |
| 437 SelectInTree(selected_nodes[0]); | |
| 438 } else { | |
| 439 bookmark_utils::OpenAll( | |
| 440 GetWidget()->GetNativeView(), profile_, NULL, selected_nodes, | |
| 441 CURRENT_TAB); | |
| 442 } | |
| 443 break; | |
| 444 } | |
| 445 | |
| 446 case base::VKEY_BACK: { | |
| 447 const BookmarkNode* selected_folder = GetSelectedFolder(); | |
| 448 if (selected_folder != NULL && | |
| 449 selected_folder->GetParent() != GetBookmarkModel()->root_node()) { | |
| 450 SelectInTree(selected_folder->GetParent()); | |
| 451 } | |
| 452 break; | |
| 453 } | |
| 454 | |
| 455 default: | |
| 456 OnCutCopyPaste(KeyCodeToCutCopyPaste(keycode), true); | |
| 457 break; | |
| 458 } | |
| 459 } | |
| 460 | |
| 461 void BookmarkManagerView::OnTreeViewSelectionChanged( | |
| 462 views::TreeView* tree_view) { | |
| 463 TreeModelNode* node = tree_view_->GetSelectedNode(); | |
| 464 | |
| 465 BookmarkTableModel* new_table_model = NULL; | |
| 466 const BookmarkNode* table_parent_node = NULL; | |
| 467 bool is_search = false; | |
| 468 | |
| 469 if (node) { | |
| 470 switch (tree_model_->GetNodeType(node)) { | |
| 471 case BookmarkFolderTreeModel::BOOKMARK: | |
| 472 table_parent_node = tree_model_->TreeNodeAsBookmarkNode(node); | |
| 473 new_table_model = | |
| 474 BookmarkTableModel::CreateBookmarkTableModelForFolder( | |
| 475 profile_->GetBookmarkModel(), | |
| 476 table_parent_node); | |
| 477 break; | |
| 478 | |
| 479 case BookmarkFolderTreeModel::RECENTLY_BOOKMARKED: | |
| 480 new_table_model = BookmarkTableModel::CreateRecentlyBookmarkedModel( | |
| 481 profile_->GetBookmarkModel()); | |
| 482 break; | |
| 483 | |
| 484 case BookmarkFolderTreeModel::SEARCH: | |
| 485 is_search = true; | |
| 486 search_factory_.RevokeAll(); | |
| 487 new_table_model = CreateSearchTableModel(); | |
| 488 break; | |
| 489 | |
| 490 default: | |
| 491 NOTREACHED(); | |
| 492 break; | |
| 493 } | |
| 494 } | |
| 495 | |
| 496 SetTableModel(new_table_model, table_parent_node, is_search); | |
| 497 } | |
| 498 | |
| 499 void BookmarkManagerView::OnTreeViewKeyDown(base::KeyboardCode keycode) { | |
| 500 switch (keycode) { | |
| 501 case base::VKEY_DELETE: { | |
| 502 const BookmarkNode* node = GetSelectedFolder(); | |
| 503 if (!node || node->GetParent() == GetBookmarkModel()->root_node()) | |
| 504 return; | |
| 505 | |
| 506 const BookmarkNode* parent = node->GetParent(); | |
| 507 GetBookmarkModel()->Remove(parent, parent->IndexOfChild(node)); | |
| 508 break; | |
| 509 } | |
| 510 | |
| 511 default: | |
| 512 OnCutCopyPaste(KeyCodeToCutCopyPaste(keycode), false); | |
| 513 break; | |
| 514 } | |
| 515 } | |
| 516 | |
| 517 void BookmarkManagerView::ButtonPressed(views::Button* sender, | |
| 518 const views::Event& event) { | |
| 519 if (sender == sync_status_button_) { | |
| 520 if (sync_relogin_required_) { | |
| 521 DCHECK(sync_service_); | |
| 522 sync_service_->ShowLoginDialog(); | |
| 523 } else { | |
| 524 UserMetrics::RecordAction(UserMetricsAction("BookmarkManager_Sync"), | |
| 525 profile_); | |
| 526 sync_ui_util::OpenSyncMyBookmarksDialog( | |
| 527 profile_, ProfileSyncService::START_FROM_BOOKMARK_MANAGER); | |
| 528 } | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 void BookmarkManagerView::Loaded(BookmarkModel* model) { | |
| 533 model->RemoveObserver(this); | |
| 534 LoadedImpl(); | |
| 535 } | |
| 536 | |
| 537 void BookmarkManagerView::ContentsChanged(views::Textfield* sender, | |
| 538 const std::wstring& new_contents) { | |
| 539 search_factory_.RevokeAll(); | |
| 540 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
| 541 search_factory_.NewRunnableMethod(&BookmarkManagerView::PerformSearch), | |
| 542 kSearchDelayMS); | |
| 543 } | |
| 544 | |
| 545 bool BookmarkManagerView::HandleKeystroke( | |
| 546 views::Textfield* sender, | |
| 547 const views::Textfield::Keystroke& key) { | |
| 548 if (key.GetKeyboardCode() == base::VKEY_RETURN) { | |
| 549 PerformSearch(); | |
| 550 search_tf_->SelectAll(); | |
| 551 } | |
| 552 | |
| 553 return false; | |
| 554 } | |
| 555 | |
| 556 void BookmarkManagerView::ShowContextMenu(views::View* source, | |
| 557 const gfx::Point& p, | |
| 558 bool is_mouse_gesture) { | |
| 559 DCHECK(source == table_view_ || source == tree_view_); | |
| 560 bool is_table = (source == table_view_); | |
| 561 ShowMenu(p, is_table ? | |
| 562 BookmarkContextMenuControllerViews::BOOKMARK_MANAGER_TABLE : | |
| 563 BookmarkContextMenuControllerViews::BOOKMARK_MANAGER_TREE); | |
| 564 } | |
| 565 | |
| 566 void BookmarkManagerView::RunMenu(views::View* source, const gfx::Point& pt) { | |
| 567 // TODO(glen): when you change the buttons around and what not, futz with | |
| 568 // this to make it look good. If you end up keeping padding numbers make them | |
| 569 // constants. | |
| 570 if (!GetBookmarkModel()->IsLoaded()) | |
| 571 return; | |
| 572 | |
| 573 gfx::Point menu_pt(pt); | |
| 574 menu_pt.Offset(UILayoutIsRightToLeft() ? | |
| 575 (source->width() - 5) : (-source->width() + 5), 2); | |
| 576 if (source->GetID() == kOrganizeMenuButtonID) { | |
| 577 ShowMenu(menu_pt, | |
| 578 BookmarkContextMenuControllerViews::BOOKMARK_MANAGER_ORGANIZE_MENU); | |
| 579 } else if (source->GetID() == kToolsMenuButtonID) { | |
| 580 ShowToolsMenu(menu_pt); | |
| 581 } else { | |
| 582 NOTREACHED(); | |
| 583 } | |
| 584 } | |
| 585 | |
| 586 void BookmarkManagerView::ExecuteCommand(int id) { | |
| 587 switch (id) { | |
| 588 case IDS_BOOKMARK_MANAGER_IMPORT_MENU: | |
| 589 UserMetrics::RecordAction(UserMetricsAction("BookmarkManager_Import"), | |
| 590 profile_); | |
| 591 ShowImportBookmarksFileChooser(); | |
| 592 break; | |
| 593 | |
| 594 case IDS_BOOKMARK_MANAGER_EXPORT_MENU: | |
| 595 UserMetrics::RecordAction(UserMetricsAction("BookmarkManager_Export"), | |
| 596 profile_); | |
| 597 ShowExportBookmarksFileChooser(); | |
| 598 break; | |
| 599 | |
| 600 default: | |
| 601 NOTREACHED(); | |
| 602 break; | |
| 603 } | |
| 604 } | |
| 605 | |
| 606 void BookmarkManagerView::FileSelected(const FilePath& path, | |
| 607 int index, | |
| 608 void* params) { | |
| 609 int id = reinterpret_cast<int>(params); | |
| 610 if (id == IDS_BOOKMARK_MANAGER_IMPORT_MENU) { | |
| 611 // ImporterHost is ref counted and will delete itself when done. | |
| 612 ImporterHost* host = new ImporterHost(); | |
| 613 ProfileInfo profile_info; | |
| 614 profile_info.browser_type = importer::BOOKMARKS_HTML; | |
| 615 profile_info.source_path = path.ToWStringHack(); | |
| 616 StartImportingWithUI(GetWidget()->GetNativeView(), importer::FAVORITES, | |
| 617 host, profile_info, profile_, | |
| 618 new ImportObserverImpl(profile()), false); | |
| 619 } else if (id == IDS_BOOKMARK_MANAGER_EXPORT_MENU) { | |
| 620 bookmark_html_writer::WriteBookmarks(profile(), path, NULL); | |
| 621 } else { | |
| 622 NOTREACHED(); | |
| 623 } | |
| 624 } | |
| 625 | |
| 626 void BookmarkManagerView::FileSelectionCanceled(void* params) { | |
| 627 select_file_dialog_ = NULL; | |
| 628 } | |
| 629 | |
| 630 BookmarkTableModel* BookmarkManagerView::CreateSearchTableModel() { | |
| 631 std::wstring search_text = search_tf_->text(); | |
| 632 if (search_text.empty()) | |
| 633 return NULL; | |
| 634 std::wstring languages = | |
| 635 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); | |
| 636 return BookmarkTableModel::CreateSearchTableModel( | |
| 637 GetBookmarkModel(), search_text, languages); | |
| 638 } | |
| 639 | |
| 640 void BookmarkManagerView::SetTableModel(BookmarkTableModel* new_table_model, | |
| 641 const BookmarkNode* parent_node, | |
| 642 bool is_search) { | |
| 643 // Be sure and reset the model on the view before updating table_model_. | |
| 644 // Otherwise the view will attempt to use the deleted model when we set the | |
| 645 // new one. | |
| 646 table_view_->SetModel(NULL); | |
| 647 table_view_->SetShowPathColumn(!parent_node); | |
| 648 table_view_->SetModel(new_table_model); | |
| 649 table_view_->set_parent_node(parent_node); | |
| 650 table_model_.reset(new_table_model); | |
| 651 if (!is_search || (new_table_model && new_table_model->RowCount() > 0)) { | |
| 652 table_view_->SetAltText(std::wstring()); | |
| 653 } else if (search_tf_->text().empty()) { | |
| 654 table_view_->SetAltText( | |
| 655 l10n_util::GetString(IDS_BOOKMARK_MANAGER_NO_SEARCH_TEXT)); | |
| 656 } else { | |
| 657 table_view_->SetAltText( | |
| 658 l10n_util::GetStringF(IDS_BOOKMARK_MANAGER_NO_RESULTS, | |
| 659 search_tf_->text())); | |
| 660 } | |
| 661 } | |
| 662 | |
| 663 void BookmarkManagerView::PerformSearch() { | |
| 664 search_factory_.RevokeAll(); | |
| 665 // Reset the controller, otherwise when we change the selection we'll get | |
| 666 // notified and update the model twice. | |
| 667 tree_view_->SetController(NULL); | |
| 668 tree_view_->SetSelectedNode(tree_model_->search_node()); | |
| 669 tree_view_->SetController(this); | |
| 670 SetTableModel(CreateSearchTableModel(), NULL, true); | |
| 671 } | |
| 672 | |
| 673 void BookmarkManagerView::PrepareForShow() { | |
| 674 // Restore the split location, but don't let it get too small (or big), | |
| 675 // otherwise users might inadvertently not see the divider. | |
| 676 int split_x = g_browser_process->local_state()->GetInteger( | |
| 677 prefs::kBookmarkManagerSplitLocation); | |
| 678 if (split_x == -1) { | |
| 679 // First time running the bookmark manager, give a third of the width to | |
| 680 // the tree. | |
| 681 split_x = split_view_->width() / 3; | |
| 682 } | |
| 683 int min_split_size = split_view_->width() / 8; | |
| 684 // Make sure the user can see both the tree/table. | |
| 685 split_x = std::min(split_view_->width() - min_split_size, | |
| 686 std::max(min_split_size, split_x)); | |
| 687 split_view_->set_divider_offset(split_x); | |
| 688 if (!GetBookmarkModel()->IsLoaded()) { | |
| 689 search_tf_->SetReadOnly(true); | |
| 690 return; | |
| 691 } | |
| 692 | |
| 693 LoadedImpl(); | |
| 694 } | |
| 695 | |
| 696 void BookmarkManagerView::LoadedImpl() { | |
| 697 BookmarkModel* bookmark_model = GetBookmarkModel(); | |
| 698 const BookmarkNode* bookmark_bar_node = bookmark_model->GetBookmarkBarNode(); | |
| 699 table_model_.reset( | |
| 700 BookmarkTableModel::CreateBookmarkTableModelForFolder(bookmark_model, | |
| 701 bookmark_bar_node)); | |
| 702 table_view_->SetModel(table_model_.get()); | |
| 703 table_view_->set_parent_node(bookmark_bar_node); | |
| 704 | |
| 705 tree_model_.reset(new BookmarkFolderTreeModel(bookmark_model)); | |
| 706 tree_view_->SetModel(tree_model_.get()); | |
| 707 | |
| 708 tree_view_->ExpandAll(); | |
| 709 | |
| 710 tree_view_->SetSelectedNode( | |
| 711 tree_model_->GetFolderNodeForBookmarkNode(bookmark_bar_node)); | |
| 712 | |
| 713 search_tf_->SetReadOnly(false); | |
| 714 search_tf_->SetController(this); | |
| 715 | |
| 716 Layout(); | |
| 717 SchedulePaint(); | |
| 718 } | |
| 719 | |
| 720 BookmarkModel* BookmarkManagerView::GetBookmarkModel() const { | |
| 721 return profile_->GetBookmarkModel(); | |
| 722 } | |
| 723 | |
| 724 void BookmarkManagerView::ShowMenu( | |
| 725 const gfx::Point& p, | |
| 726 BookmarkContextMenuControllerViews::ConfigurationType config) { | |
| 727 if (!GetBookmarkModel()->IsLoaded()) | |
| 728 return; | |
| 729 | |
| 730 const BookmarkNode* parent = GetSelectedFolder(); | |
| 731 std::vector<const BookmarkNode*> nodes; | |
| 732 typedef BookmarkContextMenuControllerViews BCMCV; // Such a long name! | |
| 733 if (config == BCMCV::BOOKMARK_MANAGER_TABLE || | |
| 734 (config == BCMCV::BOOKMARK_MANAGER_ORGANIZE_MENU && | |
| 735 table_view_->HasFocus())) { | |
| 736 nodes = GetSelectedTableNodes(); | |
| 737 if (!parent) { | |
| 738 config = (config == BCMCV::BOOKMARK_MANAGER_TABLE) ? | |
| 739 BCMCV::BOOKMARK_MANAGER_TABLE_OTHER : | |
| 740 BCMCV::BOOKMARK_MANAGER_ORGANIZE_MENU_OTHER; | |
| 741 } | |
| 742 } else { | |
| 743 if (parent) | |
| 744 nodes.push_back(parent); | |
| 745 } | |
| 746 BookmarkContextMenu menu(GetWidget()->GetNativeView(), profile_, NULL, parent, | |
| 747 nodes, config); | |
| 748 menu.RunMenuAt(p); | |
| 749 } | |
| 750 | |
| 751 void BookmarkManagerView::OnCutCopyPaste(CutCopyPasteType type, | |
| 752 bool from_table) { | |
| 753 if (type == CUT || type == COPY) { | |
| 754 std::vector<const BookmarkNode*> nodes; | |
| 755 if (from_table) { | |
| 756 nodes = GetSelectedTableNodes(); | |
| 757 } else { | |
| 758 const BookmarkNode* node = GetSelectedFolder(); | |
| 759 if (!node || node->GetParent() == GetBookmarkModel()->root_node()) | |
| 760 return; | |
| 761 nodes.push_back(node); | |
| 762 } | |
| 763 if (nodes.empty()) | |
| 764 return; | |
| 765 | |
| 766 bookmark_utils::CopyToClipboard(GetBookmarkModel(), nodes, type == CUT); | |
| 767 } else if (type == PASTE) { | |
| 768 int index = from_table ? table_view_->FirstSelectedRow() : -1; | |
| 769 if (index != -1) | |
| 770 index++; | |
| 771 bookmark_utils::PasteFromClipboard(GetBookmarkModel(), GetSelectedFolder(), | |
| 772 index); | |
| 773 } | |
| 774 } | |
| 775 | |
| 776 void BookmarkManagerView::ShowToolsMenu(const gfx::Point& p) { | |
| 777 views::MenuItemView menu(this); | |
| 778 menu.AppendMenuItemWithLabel( | |
| 779 IDS_BOOKMARK_MANAGER_IMPORT_MENU, | |
| 780 l10n_util::GetString(IDS_BOOKMARK_MANAGER_IMPORT_MENU)); | |
| 781 menu.AppendMenuItemWithLabel( | |
| 782 IDS_BOOKMARK_MANAGER_EXPORT_MENU, | |
| 783 l10n_util::GetString(IDS_BOOKMARK_MANAGER_EXPORT_MENU)); | |
| 784 views::MenuItemView::AnchorPosition anchor = UILayoutIsRightToLeft() ? | |
| 785 views::MenuItemView::TOPRIGHT : views::MenuItemView::TOPLEFT; | |
| 786 menu.RunMenuAt(GetWidget()->GetNativeView(), NULL, gfx::Rect(p, gfx::Size()), | |
| 787 anchor, true); | |
| 788 } | |
| 789 | |
| 790 void BookmarkManagerView::ShowImportBookmarksFileChooser() { | |
| 791 if (select_file_dialog_.get()) | |
| 792 select_file_dialog_->ListenerDestroyed(); | |
| 793 | |
| 794 SelectFileDialog::FileTypeInfo file_type_info; | |
| 795 file_type_info.extensions.resize(1); | |
| 796 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html")); | |
| 797 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("htm")); | |
| 798 file_type_info.include_all_files = true; | |
| 799 select_file_dialog_ = SelectFileDialog::Create(this); | |
| 800 select_file_dialog_->SelectFile( | |
| 801 SelectFileDialog::SELECT_OPEN_FILE, std::wstring(), | |
| 802 FilePath(FILE_PATH_LITERAL("bookmarks.html")), &file_type_info, 0, | |
| 803 std::wstring(), GetWidget()->GetNativeView(), | |
| 804 reinterpret_cast<void*>(IDS_BOOKMARK_MANAGER_IMPORT_MENU)); | |
| 805 } | |
| 806 | |
| 807 void BookmarkManagerView::ShowExportBookmarksFileChooser() { | |
| 808 if (select_file_dialog_.get()) | |
| 809 select_file_dialog_->ListenerDestroyed(); | |
| 810 | |
| 811 SelectFileDialog::FileTypeInfo file_type_info; | |
| 812 file_type_info.extensions.resize(1); | |
| 813 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html")); | |
| 814 file_type_info.include_all_files = true; | |
| 815 select_file_dialog_ = SelectFileDialog::Create(this); | |
| 816 select_file_dialog_->SelectFile( | |
| 817 SelectFileDialog::SELECT_SAVEAS_FILE, std::wstring(), | |
| 818 FilePath(FILE_PATH_LITERAL("bookmarks.html")), &file_type_info, 0, | |
| 819 L"html", GetWidget()->GetNativeView(), | |
| 820 reinterpret_cast<void*>(IDS_BOOKMARK_MANAGER_EXPORT_MENU)); | |
| 821 } | |
| 822 | |
| 823 void BookmarkManagerView::UpdateSyncStatus() { | |
| 824 DCHECK(sync_service_); | |
| 825 string16 status_label; | |
| 826 string16 link_label; | |
| 827 sync_relogin_required_ = sync_ui_util::GetStatusLabels(sync_service_, | |
| 828 &status_label, &link_label) == sync_ui_util::SYNC_ERROR; | |
| 829 | |
| 830 if (sync_relogin_required_) { | |
| 831 sync_status_button_->SetText( | |
| 832 l10n_util::GetString(IDS_SYNC_BOOKMARK_BAR_ERROR)); | |
| 833 // The tooltip is the only way we have to display text explaining the error | |
| 834 // to the user. | |
| 835 sync_status_button_->SetTooltipText( | |
| 836 l10n_util::GetString(IDS_SYNC_BOOKMARK_BAR_ERROR_DESC)); | |
| 837 sync_status_button_->SetAccessibleName( | |
| 838 l10n_util::GetString(IDS_ACCNAME_SYNC_ERROR_BUTTON)); | |
| 839 sync_status_button_->SetIcon( | |
| 840 *ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING)); | |
| 841 sync_status_button_->GetParent()->Layout(); | |
| 842 return; | |
| 843 } | |
| 844 | |
| 845 if (sync_service_->HasSyncSetupCompleted()) { | |
| 846 string16 username = sync_service_->GetAuthenticatedUsername(); | |
| 847 status_label = l10n_util::GetStringFUTF16(IDS_SYNC_NTP_SYNCED_TO, | |
| 848 username); | |
| 849 } else if (sync_service_->SetupInProgress()) { | |
| 850 status_label = l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS); | |
| 851 } else { | |
| 852 status_label = l10n_util::GetStringUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL); | |
| 853 } | |
| 854 sync_status_button_->SetText(status_label); | |
| 855 sync_status_button_->SetTooltipText(L""); | |
| 856 sync_status_button_->SetAccessibleName(L""); | |
| 857 sync_status_button_->SetIcon(SkBitmap()); | |
| 858 sync_status_button_->GetParent()->Layout(); | |
| 859 } | |
| OLD | NEW |