| 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/bookmarks/bookmark_table_model.h" | |
| 6 | |
| 7 #include <limits> | |
| 8 | |
| 9 #include "app/l10n_util.h" | |
| 10 #include "app/resource_bundle.h" | |
| 11 #include "app/table_model_observer.h" | |
| 12 #include "base/i18n/rtl.h" | |
| 13 #include "base/i18n/time_formatting.h" | |
| 14 #include "base/string_util.h" | |
| 15 #include "chrome/browser/bookmarks/bookmark_model.h" | |
| 16 #include "chrome/browser/bookmarks/bookmark_utils.h" | |
| 17 #include "chrome/browser/pref_service.h" | |
| 18 #include "chrome/browser/profile.h" | |
| 19 #include "chrome/common/pref_names.h" | |
| 20 #include "googleurl/src/gurl.h" | |
| 21 #include "grit/app_resources.h" | |
| 22 #include "grit/generated_resources.h" | |
| 23 #include "grit/theme_resources.h" | |
| 24 #include "net/base/escape.h" | |
| 25 #include "net/base/net_util.h" | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // Number of bookmarks shown in recently bookmarked. | |
| 30 const int kRecentlyBookmarkedCount = 50; | |
| 31 | |
| 32 // VectorBackedBookmarkTableModel ---------------------------------------------- | |
| 33 | |
| 34 class VectorBackedBookmarkTableModel : public BookmarkTableModel { | |
| 35 public: | |
| 36 explicit VectorBackedBookmarkTableModel(BookmarkModel* model) | |
| 37 : BookmarkTableModel(model) { | |
| 38 } | |
| 39 | |
| 40 virtual const BookmarkNode* GetNodeForRow(int row) { | |
| 41 return nodes_[row]; | |
| 42 } | |
| 43 | |
| 44 virtual int RowCount() { | |
| 45 return static_cast<int>(nodes_.size()); | |
| 46 } | |
| 47 | |
| 48 virtual void BookmarkNodeMoved(BookmarkModel* model, | |
| 49 const BookmarkNode* old_parent, | |
| 50 int old_index, | |
| 51 const BookmarkNode* new_parent, | |
| 52 int new_index) { | |
| 53 NotifyObserverOfChange(new_parent->GetChild(new_index)); | |
| 54 } | |
| 55 | |
| 56 virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, | |
| 57 const BookmarkNode* node) { | |
| 58 NotifyObserverOfChange(node); | |
| 59 } | |
| 60 | |
| 61 virtual void BookmarkNodeChanged(BookmarkModel* model, | |
| 62 const BookmarkNode* node) { | |
| 63 NotifyObserverOfChange(node); | |
| 64 } | |
| 65 | |
| 66 protected: | |
| 67 void NotifyObserverOfChange(const BookmarkNode* node) { | |
| 68 if (!observer()) | |
| 69 return; | |
| 70 | |
| 71 int index = IndexOfNode(node); | |
| 72 if (index != -1) | |
| 73 observer()->OnItemsChanged(index, 1); | |
| 74 } | |
| 75 | |
| 76 typedef std::vector<const BookmarkNode*> Nodes; | |
| 77 Nodes& nodes() { return nodes_; } | |
| 78 | |
| 79 private: | |
| 80 Nodes nodes_; | |
| 81 | |
| 82 DISALLOW_COPY_AND_ASSIGN(VectorBackedBookmarkTableModel); | |
| 83 }; | |
| 84 | |
| 85 // FolderBookmarkTableModel ---------------------------------------------------- | |
| 86 | |
| 87 // FolderBookmarkTableModel is a TableModel implementation backed by the | |
| 88 // contents of a bookmark folder. FolderBookmarkTableModel caches the contents | |
| 89 // of the folder so that it can send out the correct events when a bookmark | |
| 90 // node is moved. | |
| 91 class FolderBookmarkTableModel : public VectorBackedBookmarkTableModel { | |
| 92 public: | |
| 93 FolderBookmarkTableModel(BookmarkModel* model, const BookmarkNode* root_node) | |
| 94 : VectorBackedBookmarkTableModel(model), | |
| 95 root_node_(root_node) { | |
| 96 PopulateNodesFromRoot(); | |
| 97 } | |
| 98 | |
| 99 virtual void BookmarkNodeMoved(BookmarkModel* model, | |
| 100 const BookmarkNode* old_parent, | |
| 101 int old_index, | |
| 102 const BookmarkNode* new_parent, | |
| 103 int new_index) { | |
| 104 if (old_parent == root_node_) { | |
| 105 nodes().erase(nodes().begin() + old_index); | |
| 106 if (observer()) | |
| 107 observer()->OnItemsRemoved(old_index, 1); | |
| 108 } | |
| 109 if (new_parent == root_node_) { | |
| 110 nodes().insert(nodes().begin() + new_index, | |
| 111 root_node_->GetChild(new_index)); | |
| 112 if (observer()) | |
| 113 observer()->OnItemsAdded(new_index, 1); | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 virtual void BookmarkNodeAdded(BookmarkModel* model, | |
| 118 const BookmarkNode* parent, | |
| 119 int index) { | |
| 120 if (root_node_ == parent) { | |
| 121 nodes().insert(nodes().begin() + index, parent->GetChild(index)); | |
| 122 if (observer()) | |
| 123 observer()->OnItemsAdded(index, 1); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 virtual void BookmarkNodeRemoved(BookmarkModel* model, | |
| 128 const BookmarkNode* parent, | |
| 129 int index, | |
| 130 const BookmarkNode* node) { | |
| 131 if (root_node_->HasAncestor(node)) { | |
| 132 // We, or one of our ancestors was removed. | |
| 133 root_node_ = NULL; | |
| 134 nodes().clear(); | |
| 135 if (observer()) | |
| 136 observer()->OnModelChanged(); | |
| 137 return; | |
| 138 } | |
| 139 if (root_node_ == parent) { | |
| 140 nodes().erase(nodes().begin() + index); | |
| 141 if (observer()) | |
| 142 observer()->OnItemsRemoved(index, 1); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 virtual void BookmarkNodeChanged(BookmarkModel* model, | |
| 147 const BookmarkNode* node) { | |
| 148 NotifyChanged(node); | |
| 149 } | |
| 150 | |
| 151 virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, | |
| 152 const BookmarkNode* node) { | |
| 153 NotifyChanged(node); | |
| 154 } | |
| 155 | |
| 156 virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, | |
| 157 const BookmarkNode* node) { | |
| 158 if (node != root_node_) | |
| 159 return; | |
| 160 | |
| 161 nodes().clear(); | |
| 162 PopulateNodesFromRoot(); | |
| 163 | |
| 164 if (observer()) | |
| 165 observer()->OnModelChanged(); | |
| 166 } | |
| 167 | |
| 168 private: | |
| 169 void NotifyChanged(const BookmarkNode* node) { | |
| 170 if (node->GetParent() == root_node_ && observer()) | |
| 171 observer()->OnItemsChanged(node->GetParent()->IndexOfChild(node), 1); | |
| 172 } | |
| 173 | |
| 174 void PopulateNodesFromRoot() { | |
| 175 for (int i = 0; i < root_node_->GetChildCount(); ++i) | |
| 176 nodes().push_back(root_node_->GetChild(i)); | |
| 177 } | |
| 178 | |
| 179 // The node we're showing the children of. This is set to NULL if the node | |
| 180 // (or one of its ancestors) is removed from the model. | |
| 181 const BookmarkNode* root_node_; | |
| 182 | |
| 183 DISALLOW_COPY_AND_ASSIGN(FolderBookmarkTableModel); | |
| 184 }; | |
| 185 | |
| 186 // RecentlyBookmarkedTableModel ------------------------------------------------ | |
| 187 | |
| 188 class RecentlyBookmarkedTableModel : public VectorBackedBookmarkTableModel { | |
| 189 public: | |
| 190 explicit RecentlyBookmarkedTableModel(BookmarkModel* model) | |
| 191 : VectorBackedBookmarkTableModel(model) { | |
| 192 UpdateRecentlyBookmarked(); | |
| 193 } | |
| 194 | |
| 195 virtual void BookmarkNodeAdded(BookmarkModel* model, | |
| 196 const BookmarkNode* parent, | |
| 197 int index) { | |
| 198 if (parent->GetChild(index)->is_url()) | |
| 199 UpdateRecentlyBookmarked(); | |
| 200 } | |
| 201 | |
| 202 virtual void BookmarkNodeRemoved(BookmarkModel* model, | |
| 203 const BookmarkNode* parent, | |
| 204 int old_index, | |
| 205 const BookmarkNode* old_node) { | |
| 206 if (old_node->is_url()) | |
| 207 UpdateRecentlyBookmarked(); | |
| 208 } | |
| 209 | |
| 210 private: | |
| 211 void UpdateRecentlyBookmarked() { | |
| 212 nodes().clear(); | |
| 213 bookmark_utils::GetMostRecentlyAddedEntries(model(), | |
| 214 kRecentlyBookmarkedCount, | |
| 215 &nodes()); | |
| 216 if (observer()) | |
| 217 observer()->OnModelChanged(); | |
| 218 } | |
| 219 | |
| 220 DISALLOW_COPY_AND_ASSIGN(RecentlyBookmarkedTableModel); | |
| 221 }; | |
| 222 | |
| 223 // BookmarkSearchTableModel ---------------------------------------------------- | |
| 224 | |
| 225 class BookmarkSearchTableModel : public VectorBackedBookmarkTableModel { | |
| 226 public: | |
| 227 BookmarkSearchTableModel(BookmarkModel* model, | |
| 228 const std::wstring& search_text, | |
| 229 const std::wstring& languages) | |
| 230 : VectorBackedBookmarkTableModel(model), | |
| 231 search_text_(search_text), | |
| 232 languages_(languages) { | |
| 233 bookmark_utils::GetBookmarksContainingText( | |
| 234 model, search_text, std::numeric_limits<int>::max(), | |
| 235 languages, &nodes()); | |
| 236 } | |
| 237 | |
| 238 virtual void BookmarkNodeAdded(BookmarkModel* model, | |
| 239 const BookmarkNode* parent, | |
| 240 int index) { | |
| 241 const BookmarkNode* node = parent->GetChild(index); | |
| 242 if (bookmark_utils::DoesBookmarkContainText( | |
| 243 node, search_text_, languages_)) { | |
| 244 nodes().push_back(node); | |
| 245 if (observer()) | |
| 246 observer()->OnItemsAdded(static_cast<int>(nodes().size() - 1), 1); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 virtual void BookmarkNodeRemoved(BookmarkModel* model, | |
| 251 const BookmarkNode* parent, | |
| 252 int index, | |
| 253 const BookmarkNode* node) { | |
| 254 int internal_index = IndexOfNode(node); | |
| 255 if (internal_index == -1) | |
| 256 return; | |
| 257 | |
| 258 nodes().erase(nodes().begin() + static_cast<int>(internal_index)); | |
| 259 if (observer()) | |
| 260 observer()->OnItemsRemoved(internal_index, 1); | |
| 261 } | |
| 262 | |
| 263 private: | |
| 264 const std::wstring search_text_; | |
| 265 const std::wstring languages_; | |
| 266 | |
| 267 DISALLOW_COPY_AND_ASSIGN(BookmarkSearchTableModel); | |
| 268 }; | |
| 269 | |
| 270 } // namespace | |
| 271 | |
| 272 // BookmarkTableModel ---------------------------------------------------------- | |
| 273 | |
| 274 // static | |
| 275 BookmarkTableModel* BookmarkTableModel::CreateRecentlyBookmarkedModel( | |
| 276 BookmarkModel* model) { | |
| 277 return new RecentlyBookmarkedTableModel(model); | |
| 278 } | |
| 279 | |
| 280 // static | |
| 281 BookmarkTableModel* BookmarkTableModel::CreateBookmarkTableModelForFolder( | |
| 282 BookmarkModel* model, const BookmarkNode* node) { | |
| 283 return new FolderBookmarkTableModel(model, node); | |
| 284 } | |
| 285 | |
| 286 // static | |
| 287 BookmarkTableModel* BookmarkTableModel::CreateSearchTableModel( | |
| 288 BookmarkModel* model, | |
| 289 const std::wstring& text, | |
| 290 const std::wstring& languages) { | |
| 291 return new BookmarkSearchTableModel(model, text, languages); | |
| 292 } | |
| 293 | |
| 294 BookmarkTableModel::BookmarkTableModel(BookmarkModel* model) | |
| 295 : model_(model), | |
| 296 observer_(NULL) { | |
| 297 model_->AddObserver(this); | |
| 298 } | |
| 299 | |
| 300 BookmarkTableModel::~BookmarkTableModel() { | |
| 301 if (model_) | |
| 302 model_->RemoveObserver(this); | |
| 303 } | |
| 304 | |
| 305 std::wstring BookmarkTableModel::GetText(int row, int column_id) { | |
| 306 const BookmarkNode* node = GetNodeForRow(row); | |
| 307 switch (column_id) { | |
| 308 case IDS_BOOKMARK_TABLE_TITLE: { | |
| 309 std::wstring title = node->GetTitle(); | |
| 310 // Adjust the text as well, for example, put LRE-PDF pair around LTR text | |
| 311 // in RTL enviroment, so that the ending punctuation in the text will not | |
| 312 // be rendered incorrectly (such as rendered as the leftmost character, | |
| 313 // and/or rendered as a mirrored punctuation character). | |
| 314 // | |
| 315 // TODO(xji): Consider adding a special case if the title text is a URL, | |
| 316 // since those should always be displayed LTR. Please refer to | |
| 317 // http://crbug.com/6726 for more information. | |
| 318 base::i18n::AdjustStringForLocaleDirection(title, &title); | |
| 319 return title; | |
| 320 } | |
| 321 | |
| 322 case IDS_BOOKMARK_TABLE_URL: { | |
| 323 if (!node->is_url()) | |
| 324 return std::wstring(); | |
| 325 std::wstring languages = model_ && model_->profile() | |
| 326 ? model_->profile()->GetPrefs()->GetString(prefs::kAcceptLanguages) | |
| 327 : std::wstring(); | |
| 328 std::wstring url_text = net::FormatUrl(node->GetURL(), languages, | |
| 329 net::kFormatUrlOmitAll, UnescapeRule::SPACES, NULL, NULL, NULL); | |
| 330 if (base::i18n::IsRTL()) | |
| 331 base::i18n::WrapStringWithLTRFormatting(&url_text); | |
| 332 return url_text; | |
| 333 } | |
| 334 | |
| 335 case IDS_BOOKMARK_TABLE_PATH: { | |
| 336 std::wstring path; | |
| 337 BuildPath(node->GetParent(), &path); | |
| 338 // Force path to have LTR directionality. The whole path (but not every | |
| 339 // single path component) is marked with LRE-PDF. For example, | |
| 340 // ALEPH/BET/GIMEL (using uppercase English for Hebrew) is supposed to | |
| 341 // appear (visually) as LEMIG/TEB/HPELA; foo/C/B/A.doc refers to file | |
| 342 // C.doc in directory B in directory A in directory foo, not to file | |
| 343 // A.doc in directory B in directory C in directory foo. The reason to | |
| 344 // mark the whole path, but not every single path component, as LTR is | |
| 345 // because paths need to get written in text documents, and that is how | |
| 346 // they will appear there. Being a saint and doing the tedious formatting | |
| 347 // to every single path component to get it to come out in the logical | |
| 348 // order will accomplish nothing but confuse people, since they will now | |
| 349 // see both formats being used, and will never know what anything means. | |
| 350 // Furthermore, doing the "logical" formatting with characters like LRM, | |
| 351 // LRE, and PDF to every single path component means that when someone | |
| 352 // copy/pastes your path, it will still contain those characters, and | |
| 353 // trying to access the file will fail because of them. Windows Explorer, | |
| 354 // Firefox, IE, Nautilus, gedit choose to format only the whole path as | |
| 355 // LTR too. The point here is to display the path the same way as it's | |
| 356 // displayed by other software. | |
| 357 if (base::i18n::IsRTL()) | |
| 358 base::i18n::WrapStringWithLTRFormatting(&path); | |
| 359 return path; | |
| 360 } | |
| 361 } | |
| 362 NOTREACHED(); | |
| 363 return std::wstring(); | |
| 364 } | |
| 365 | |
| 366 SkBitmap BookmarkTableModel::GetIcon(int row) { | |
| 367 static SkBitmap* folder_icon = ResourceBundle::GetSharedInstance(). | |
| 368 GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER); | |
| 369 static SkBitmap* default_icon = ResourceBundle::GetSharedInstance(). | |
| 370 GetBitmapNamed(IDR_DEFAULT_FAVICON); | |
| 371 | |
| 372 const BookmarkNode* node = GetNodeForRow(row); | |
| 373 if (node->is_folder()) | |
| 374 return *folder_icon; | |
| 375 | |
| 376 if (model_->GetFavIcon(node).empty()) | |
| 377 return *default_icon; | |
| 378 | |
| 379 return model_->GetFavIcon(node); | |
| 380 } | |
| 381 | |
| 382 void BookmarkTableModel::BookmarkModelBeingDeleted(BookmarkModel* model) { | |
| 383 model_->RemoveObserver(this); | |
| 384 model_ = NULL; | |
| 385 } | |
| 386 | |
| 387 int BookmarkTableModel::IndexOfNode(const BookmarkNode* node) { | |
| 388 for (int i = RowCount() - 1; i >= 0; --i) { | |
| 389 if (GetNodeForRow(i) == node) | |
| 390 return i; | |
| 391 } | |
| 392 return -1; | |
| 393 } | |
| 394 | |
| 395 void BookmarkTableModel::BuildPath(const BookmarkNode* node, | |
| 396 std::wstring* path) { | |
| 397 if (!node) { | |
| 398 NOTREACHED(); | |
| 399 return; | |
| 400 } | |
| 401 if (node == model()->GetBookmarkBarNode()) { | |
| 402 *path = l10n_util::GetString(IDS_BOOKMARK_TABLE_BOOKMARK_BAR_PATH); | |
| 403 return; | |
| 404 } | |
| 405 if (node == model()->other_node()) { | |
| 406 *path = l10n_util::GetString(IDS_BOOKMARK_TABLE_OTHER_BOOKMARKS_PATH); | |
| 407 return; | |
| 408 } | |
| 409 BuildPath(node->GetParent(), path); | |
| 410 path->append(l10n_util::GetString(IDS_BOOKMARK_TABLE_PATH_SEPARATOR)); | |
| 411 path->append(node->GetTitle()); | |
| 412 } | |
| OLD | NEW |