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 |