blob: 816d6d8172adc258e705cb582c24c5421eb3f290 [file] [log] [blame]
[email protected]90f39902009-10-03 04:25:371// 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/bookmarks/bookmark_utils.h"
6
[email protected]90f39902009-10-03 04:25:377#include "app/clipboard/clipboard.h"
[email protected]37126212009-05-06 02:23:318#include "app/drag_drop_types.h"
[email protected]a92b8642009-05-05 23:38:569#include "app/l10n_util.h"
[email protected]992c6252009-05-13 18:54:2010#include "app/tree_node_iterator.h"
[email protected]a0368962009-02-24 21:47:1611#include "base/basictypes.h"
[email protected]630947c2009-11-04 18:37:3112#include "base/file_path.h"
[email protected]cb362ccc2008-12-10 17:22:3213#include "base/string_util.h"
[email protected]9c6a85d2010-01-21 22:53:4614#include "base/string16.h"
[email protected]9333f182008-12-09 17:34:1715#include "base/time.h"
[email protected]7f856be2008-10-29 23:38:0616#include "chrome/browser/bookmarks/bookmark_drag_data.h"
17#include "chrome/browser/bookmarks/bookmark_model.h"
18#include "chrome/browser/browser.h"
19#include "chrome/browser/browser_list.h"
[email protected]b08cadb92009-08-04 21:52:2920#include "chrome/browser/browser_process.h"
[email protected]ce560f82009-06-03 09:39:4421#include "chrome/browser/browser_window.h"
[email protected]9333f182008-12-09 17:34:1722#include "chrome/browser/history/query_parser.h"
[email protected]505323e22009-01-24 02:47:5823#include "chrome/browser/profile.h"
[email protected]f3ec7742009-01-15 00:59:1624#include "chrome/browser/tab_contents/page_navigator.h"
[email protected]1132436e2009-04-08 20:06:3325#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]44b2c8852009-03-18 00:57:4926#include "chrome/common/notification_service.h"
27#include "chrome/common/pref_names.h"
28#include "chrome/common/pref_service.h"
[email protected]903e7a82009-07-28 00:45:3529#include "grit/app_strings.h"
[email protected]34ac8f32009-02-22 23:03:2730#include "grit/chromium_strings.h"
31#include "grit/generated_resources.h"
[email protected]fa5dfaf2009-06-02 22:12:0632#include "net/base/net_util.h"
[email protected]2362e4f2009-05-08 00:34:0533#include "views/event.h"
[email protected]7f856be2008-10-29 23:38:0634
[email protected]140aea052009-05-05 00:35:0935using base::Time;
36
[email protected]7f856be2008-10-29 23:38:0637namespace {
38
[email protected]7f856be2008-10-29 23:38:0639// A PageNavigator implementation that creates a new Browser. This is used when
40// opening a url and there is no Browser open. The Browser is created the first
41// time the PageNavigator method is invoked.
42class NewBrowserPageNavigator : public PageNavigator {
43 public:
44 explicit NewBrowserPageNavigator(Profile* profile)
45 : profile_(profile),
46 browser_(NULL) {}
47
48 virtual ~NewBrowserPageNavigator() {
49 if (browser_)
[email protected]15952e462008-11-14 00:29:0550 browser_->window()->Show();
[email protected]7f856be2008-10-29 23:38:0651 }
52
53 Browser* browser() const { return browser_; }
54
55 virtual void OpenURL(const GURL& url,
56 const GURL& referrer,
57 WindowOpenDisposition disposition,
58 PageTransition::Type transition) {
59 if (!browser_) {
60 Profile* profile = (disposition == OFF_THE_RECORD) ?
61 profile_->GetOffTheRecordProfile() : profile_;
[email protected]15952e462008-11-14 00:29:0562 browser_ = Browser::Create(profile);
[email protected]7f856be2008-10-29 23:38:0663 // Always open the first tab in the foreground.
64 disposition = NEW_FOREGROUND_TAB;
65 }
[email protected]e0c7c262009-04-23 23:09:4366 browser_->OpenURL(url, referrer, NEW_FOREGROUND_TAB, transition);
[email protected]7f856be2008-10-29 23:38:0667 }
68
69 private:
70 Profile* profile_;
71 Browser* browser_;
72
73 DISALLOW_COPY_AND_ASSIGN(NewBrowserPageNavigator);
74};
75
76void CloneDragDataImpl(BookmarkModel* model,
77 const BookmarkDragData::Element& element,
[email protected]b3c33d462009-06-26 22:29:2078 const BookmarkNode* parent,
[email protected]7f856be2008-10-29 23:38:0679 int index_to_add_at) {
80 if (element.is_url) {
81 model->AddURL(parent, index_to_add_at, element.title, element.url);
82 } else {
[email protected]b3c33d462009-06-26 22:29:2083 const BookmarkNode* new_folder = model->AddGroup(parent,
84 index_to_add_at,
85 element.title);
[email protected]7f856be2008-10-29 23:38:0686 for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
87 CloneDragDataImpl(model, element.children[i], new_folder, i);
88 }
89}
90
91// Returns the number of descendants of node that are of type url.
[email protected]b3c33d462009-06-26 22:29:2092int DescendantURLCount(const BookmarkNode* node) {
[email protected]7f856be2008-10-29 23:38:0693 int result = 0;
94 for (int i = 0; i < node->GetChildCount(); ++i) {
[email protected]b3c33d462009-06-26 22:29:2095 const BookmarkNode* child = node->GetChild(i);
[email protected]7f856be2008-10-29 23:38:0696 if (child->is_url())
97 result++;
98 else
99 result += DescendantURLCount(child);
100 }
101 return result;
102}
103
104// Implementation of OpenAll. Opens all nodes of type URL and recurses for
105// groups. |navigator| is the PageNavigator used to open URLs. After the first
106// url is opened |opened_url| is set to true and |navigator| is set to the
107// PageNavigator of the last active tab. This is done to handle a window
108// disposition of new window, in which case we want subsequent tabs to open in
109// that window.
[email protected]b3c33d462009-06-26 22:29:20110void OpenAllImpl(const BookmarkNode* node,
[email protected]7f856be2008-10-29 23:38:06111 WindowOpenDisposition initial_disposition,
112 PageNavigator** navigator,
113 bool* opened_url) {
114 if (node->is_url()) {
115 WindowOpenDisposition disposition;
116 if (*opened_url)
117 disposition = NEW_BACKGROUND_TAB;
118 else
119 disposition = initial_disposition;
120 (*navigator)->OpenURL(node->GetURL(), GURL(), disposition,
121 PageTransition::AUTO_BOOKMARK);
122 if (!*opened_url) {
123 *opened_url = true;
124 // We opened the first URL which may have opened a new window or clobbered
125 // the current page, reset the navigator just to be sure.
126 Browser* new_browser = BrowserList::GetLastActive();
127 if (new_browser) {
128 TabContents* current_tab = new_browser->GetSelectedTabContents();
129 DCHECK(new_browser && current_tab);
130 if (new_browser && current_tab)
131 *navigator = current_tab;
132 } // else, new_browser == NULL, which happens during testing.
133 }
134 } else {
135 // Group, recurse through children.
136 for (int i = 0; i < node->GetChildCount(); ++i) {
137 OpenAllImpl(node->GetChild(i), initial_disposition, navigator,
138 opened_url);
139 }
140 }
141}
142
[email protected]d6d6d582009-10-12 19:22:26143bool ShouldOpenAll(gfx::NativeWindow parent,
[email protected]b3c33d462009-06-26 22:29:20144 const std::vector<const BookmarkNode*>& nodes) {
[email protected]7f856be2008-10-29 23:38:06145 int descendant_count = 0;
146 for (size_t i = 0; i < nodes.size(); ++i)
147 descendant_count += DescendantURLCount(nodes[i]);
[email protected]f785ad12008-11-19 23:01:28148 if (descendant_count < bookmark_utils::num_urls_before_prompting)
[email protected]7f856be2008-10-29 23:38:06149 return true;
150
151 std::wstring message =
152 l10n_util::GetStringF(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
153 IntToWString(descendant_count));
[email protected]a0368962009-02-24 21:47:16154#if defined(OS_WIN)
[email protected]7f856be2008-10-29 23:38:06155 return MessageBox(parent, message.c_str(),
156 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
157 MB_YESNO | MB_ICONWARNING | MB_TOPMOST) == IDYES;
[email protected]a0368962009-02-24 21:47:16158#else
159 // TODO(port): Display a dialog prompt.
[email protected]2083180a2010-02-03 20:11:30160 // http://crbug.com/34481
[email protected]a0368962009-02-24 21:47:16161 NOTIMPLEMENTED();
162 return true;
163#endif
[email protected]7f856be2008-10-29 23:38:06164}
165
[email protected]9333f182008-12-09 17:34:17166// Comparison function that compares based on date modified of the two nodes.
[email protected]b3c33d462009-06-26 22:29:20167bool MoreRecentlyModified(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]9333f182008-12-09 17:34:17168 return n1->date_group_modified() > n2->date_group_modified();
169}
170
[email protected]cb362ccc2008-12-10 17:22:32171// Returns true if |text| contains each string in |words|. This is used when
172// searching for bookmarks.
173bool DoesBookmarkTextContainWords(const std::wstring& text,
174 const std::vector<std::wstring>& words) {
175 for (size_t i = 0; i < words.size(); ++i) {
176 if (text.find(words[i]) == std::wstring::npos)
177 return false;
178 }
179 return true;
180}
181
182// Returns true if |node|s title or url contains the strings in |words|.
[email protected]fa5dfaf2009-06-02 22:12:06183// |languages| argument is user's accept-language setting to decode IDN.
[email protected]b3c33d462009-06-26 22:29:20184bool DoesBookmarkContainWords(const BookmarkNode* node,
[email protected]fa5dfaf2009-06-02 22:12:06185 const std::vector<std::wstring>& words,
186 const std::wstring& languages) {
[email protected]cb362ccc2008-12-10 17:22:32187 return
188 DoesBookmarkTextContainWords(
189 l10n_util::ToLower(node->GetTitle()), words) ||
[email protected]1f31bb72009-12-16 03:18:25190 DoesBookmarkTextContainWords(
191 l10n_util::ToLower(UTF8ToWide(node->GetURL().spec())), words) ||
192 DoesBookmarkTextContainWords(l10n_util::ToLower(net::FormatUrl(
193 node->GetURL(), languages, false, true, NULL, NULL, NULL)), words);
[email protected]cb362ccc2008-12-10 17:22:32194}
195
[email protected]7f856be2008-10-29 23:38:06196} // namespace
197
198namespace bookmark_utils {
199
[email protected]f785ad12008-11-19 23:01:28200int num_urls_before_prompting = 15;
201
[email protected]f28cbb72008-11-04 19:29:08202int PreferredDropOperation(int source_operations, int operations) {
203 int common_ops = (source_operations & operations);
[email protected]7f856be2008-10-29 23:38:06204 if (!common_ops)
205 return 0;
206 if (DragDropTypes::DRAG_COPY & common_ops)
207 return DragDropTypes::DRAG_COPY;
208 if (DragDropTypes::DRAG_LINK & common_ops)
209 return DragDropTypes::DRAG_LINK;
210 if (DragDropTypes::DRAG_MOVE & common_ops)
211 return DragDropTypes::DRAG_MOVE;
212 return DragDropTypes::DRAG_NONE;
213}
214
[email protected]b3c33d462009-06-26 22:29:20215int BookmarkDragOperation(const BookmarkNode* node) {
[email protected]7aa4c002009-03-12 19:10:22216 if (node->is_url()) {
217 return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE |
218 DragDropTypes::DRAG_LINK;
219 }
220 return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
221}
222
223int BookmarkDropOperation(Profile* profile,
224 const views::DropTargetEvent& event,
225 const BookmarkDragData& data,
[email protected]b3c33d462009-06-26 22:29:20226 const BookmarkNode* parent,
[email protected]7aa4c002009-03-12 19:10:22227 int index) {
228 if (data.IsFromProfile(profile) && data.size() > 1)
229 // Currently only accept one dragged node at a time.
230 return DragDropTypes::DRAG_NONE;
231
232 if (!bookmark_utils::IsValidDropLocation(profile, data, parent, index))
233 return DragDropTypes::DRAG_NONE;
234
235 if (data.GetFirstNode(profile)) {
236 // User is dragging from this profile: move.
237 return DragDropTypes::DRAG_MOVE;
238 }
239 // User is dragging from another app, copy.
240 return PreferredDropOperation(event.GetSourceOperations(),
241 DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK);
242}
243
244int PerformBookmarkDrop(Profile* profile,
245 const BookmarkDragData& data,
[email protected]b3c33d462009-06-26 22:29:20246 const BookmarkNode* parent_node,
[email protected]7aa4c002009-03-12 19:10:22247 int index) {
[email protected]b3c33d462009-06-26 22:29:20248 const BookmarkNode* dragged_node = data.GetFirstNode(profile);
[email protected]7aa4c002009-03-12 19:10:22249 BookmarkModel* model = profile->GetBookmarkModel();
250 if (dragged_node) {
251 // Drag from same profile, do a move.
252 model->Move(dragged_node, parent_node, index);
253 return DragDropTypes::DRAG_MOVE;
254 } else if (data.has_single_url()) {
255 // New URL, add it at the specified location.
[email protected]9c6a85d2010-01-21 22:53:46256 string16 title = data.elements[0].title;
[email protected]7aa4c002009-03-12 19:10:22257 if (title.empty()) {
258 // No title, use the host.
[email protected]9c6a85d2010-01-21 22:53:46259 title = UTF8ToUTF16(data.elements[0].url.host());
[email protected]7aa4c002009-03-12 19:10:22260 if (title.empty())
[email protected]9c6a85d2010-01-21 22:53:46261 title = l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_UNKNOWN_DRAG_TITLE);
[email protected]7aa4c002009-03-12 19:10:22262 }
263 model->AddURL(parent_node, index, title, data.elements[0].url);
264 return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK;
265 } else {
266 // Dropping a group from different profile. Always accept.
267 bookmark_utils::CloneDragData(model, data.elements, parent_node, index);
268 return DragDropTypes::DRAG_COPY;
269 }
270}
271
[email protected]7f856be2008-10-29 23:38:06272bool IsValidDropLocation(Profile* profile,
273 const BookmarkDragData& data,
[email protected]b3c33d462009-06-26 22:29:20274 const BookmarkNode* drop_parent,
[email protected]7f856be2008-10-29 23:38:06275 int index) {
276 if (!drop_parent->is_folder()) {
277 NOTREACHED();
278 return false;
279 }
280
281 if (!data.is_valid())
282 return false;
283
284 if (data.IsFromProfile(profile)) {
[email protected]b3c33d462009-06-26 22:29:20285 std::vector<const BookmarkNode*> nodes = data.GetNodes(profile);
[email protected]7f856be2008-10-29 23:38:06286 for (size_t i = 0; i < nodes.size(); ++i) {
287 // Don't allow the drop if the user is attempting to drop on one of the
288 // nodes being dragged.
[email protected]b3c33d462009-06-26 22:29:20289 const BookmarkNode* node = nodes[i];
[email protected]7f856be2008-10-29 23:38:06290 int node_index = (drop_parent == node->GetParent()) ?
291 drop_parent->IndexOfChild(nodes[i]) : -1;
292 if (node_index != -1 && (index == node_index || index == node_index + 1))
293 return false;
294
295 // drop_parent can't accept a child that is an ancestor.
296 if (drop_parent->HasAncestor(node))
297 return false;
298 }
299 return true;
300 }
301 // From the same profile, always accept.
302 return true;
303}
304
305void CloneDragData(BookmarkModel* model,
306 const std::vector<BookmarkDragData::Element>& elements,
[email protected]b3c33d462009-06-26 22:29:20307 const BookmarkNode* parent,
[email protected]7f856be2008-10-29 23:38:06308 int index_to_add_at) {
309 if (!parent->is_folder() || !model) {
310 NOTREACHED();
311 return;
312 }
313 for (size_t i = 0; i < elements.size(); ++i)
314 CloneDragDataImpl(model, elements[i], parent, index_to_add_at + i);
315}
316
[email protected]d6d6d582009-10-12 19:22:26317void OpenAll(gfx::NativeWindow parent,
[email protected]7f856be2008-10-29 23:38:06318 Profile* profile,
319 PageNavigator* navigator,
[email protected]b3c33d462009-06-26 22:29:20320 const std::vector<const BookmarkNode*>& nodes,
[email protected]7f856be2008-10-29 23:38:06321 WindowOpenDisposition initial_disposition) {
322 if (!ShouldOpenAll(parent, nodes))
323 return;
324
325 NewBrowserPageNavigator navigator_impl(profile);
326 if (!navigator) {
[email protected]f0a51fb52009-03-05 12:46:38327 Browser* browser =
[email protected]299dabd2008-11-19 02:27:16328 BrowserList::FindBrowserWithType(profile, Browser::TYPE_NORMAL);
[email protected]b6f2b9132008-11-17 16:27:51329 if (!browser || !browser->GetSelectedTabContents()) {
[email protected]7f856be2008-10-29 23:38:06330 navigator = &navigator_impl;
[email protected]b6f2b9132008-11-17 16:27:51331 } else {
[email protected]c9938e6d2009-03-13 19:54:11332 if (initial_disposition != NEW_WINDOW &&
333 initial_disposition != OFF_THE_RECORD) {
334 browser->window()->Activate();
335 }
[email protected]7f856be2008-10-29 23:38:06336 navigator = browser->GetSelectedTabContents();
[email protected]b6f2b9132008-11-17 16:27:51337 }
[email protected]7f856be2008-10-29 23:38:06338 }
339
340 bool opened_url = false;
341 for (size_t i = 0; i < nodes.size(); ++i)
342 OpenAllImpl(nodes[i], initial_disposition, &navigator, &opened_url);
343}
344
[email protected]d6d6d582009-10-12 19:22:26345void OpenAll(gfx::NativeWindow parent,
[email protected]7f856be2008-10-29 23:38:06346 Profile* profile,
347 PageNavigator* navigator,
[email protected]b3c33d462009-06-26 22:29:20348 const BookmarkNode* node,
[email protected]7f856be2008-10-29 23:38:06349 WindowOpenDisposition initial_disposition) {
[email protected]b3c33d462009-06-26 22:29:20350 std::vector<const BookmarkNode*> nodes;
[email protected]7f856be2008-10-29 23:38:06351 nodes.push_back(node);
352 OpenAll(parent, profile, navigator, nodes, initial_disposition);
353}
354
[email protected]fafc8a422008-11-07 17:53:09355void CopyToClipboard(BookmarkModel* model,
[email protected]b3c33d462009-06-26 22:29:20356 const std::vector<const BookmarkNode*>& nodes,
[email protected]fafc8a422008-11-07 17:53:09357 bool remove_nodes) {
[email protected]b2aa3ed72010-02-01 18:37:14358// Not implemented on mac yet.
359#if !defined(OS_MACOSX)
[email protected]fafc8a422008-11-07 17:53:09360 if (nodes.empty())
361 return;
362
[email protected]b08cadb92009-08-04 21:52:29363 BookmarkDragData(nodes).WriteToClipboard(NULL);
[email protected]fafc8a422008-11-07 17:53:09364
365 if (remove_nodes) {
366 for (size_t i = 0; i < nodes.size(); ++i) {
367 model->Remove(nodes[i]->GetParent(),
368 nodes[i]->GetParent()->IndexOfChild(nodes[i]));
369 }
370 }
[email protected]7018c5f32009-07-31 23:37:57371#endif
[email protected]fafc8a422008-11-07 17:53:09372}
373
374void PasteFromClipboard(BookmarkModel* model,
[email protected]b3c33d462009-06-26 22:29:20375 const BookmarkNode* parent,
[email protected]fafc8a422008-11-07 17:53:09376 int index) {
[email protected]b2aa3ed72010-02-01 18:37:14377// Not implemented on mac yet.
378#if !defined(OS_MACOSX)
[email protected]fafc8a422008-11-07 17:53:09379 if (!parent)
380 return;
381
[email protected]fafc8a422008-11-07 17:53:09382 BookmarkDragData bookmark_data;
[email protected]b08cadb92009-08-04 21:52:29383 if (!bookmark_data.ReadFromClipboard())
[email protected]fafc8a422008-11-07 17:53:09384 return;
385
386 if (index == -1)
387 index = parent->GetChildCount();
388 bookmark_utils::CloneDragData(model, bookmark_data.elements, parent, index);
[email protected]a0368962009-02-24 21:47:16389#endif
[email protected]fafc8a422008-11-07 17:53:09390}
391
[email protected]b3c33d462009-06-26 22:29:20392bool CanPasteFromClipboard(const BookmarkNode* node) {
[email protected]fafc8a422008-11-07 17:53:09393 if (!node)
394 return false;
395
[email protected]eda57132009-10-01 14:55:28396#if defined(OS_MACOSX)
397 NOTIMPLEMENTED();
398 return false;
399#else
400 return g_browser_process->clipboard()->IsFormatAvailableByString(
[email protected]65a6150d2009-09-08 22:16:05401 BookmarkDragData::kClipboardFormatString, Clipboard::BUFFER_STANDARD);
[email protected]eda57132009-10-01 14:55:28402#endif
[email protected]fafc8a422008-11-07 17:53:09403}
404
[email protected]903e7a82009-07-28 00:45:35405std::string GetNameForURL(const GURL& url) {
406 if (url.is_valid()) {
407 return WideToUTF8(net::GetSuggestedFilename(
[email protected]630947c2009-11-04 18:37:31408 url, std::string(), std::string(), FilePath()).ToWStringHack());
[email protected]903e7a82009-07-28 00:45:35409 } else {
410 return l10n_util::GetStringUTF8(IDS_APP_UNTITLED_SHORTCUT_FILE_NAME);
411 }
412}
413
[email protected]b3c33d462009-06-26 22:29:20414std::vector<const BookmarkNode*> GetMostRecentlyModifiedGroups(
[email protected]9333f182008-12-09 17:34:17415 BookmarkModel* model,
416 size_t max_count) {
[email protected]b3c33d462009-06-26 22:29:20417 std::vector<const BookmarkNode*> nodes;
418 TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
[email protected]9333f182008-12-09 17:34:17419 while (iterator.has_next()) {
[email protected]b3c33d462009-06-26 22:29:20420 const BookmarkNode* parent = iterator.Next();
[email protected]9333f182008-12-09 17:34:17421 if (parent->is_folder() && parent->date_group_modified() > base::Time()) {
422 if (max_count == 0) {
423 nodes.push_back(parent);
424 } else {
[email protected]b3c33d462009-06-26 22:29:20425 std::vector<const BookmarkNode*>::iterator i =
[email protected]9333f182008-12-09 17:34:17426 std::upper_bound(nodes.begin(), nodes.end(), parent,
427 &MoreRecentlyModified);
428 if (nodes.size() < max_count || i != nodes.end()) {
429 nodes.insert(i, parent);
430 while (nodes.size() > max_count)
431 nodes.pop_back();
432 }
433 }
434 } // else case, the root node, which we don't care about or imported nodes
435 // (which have a time of 0).
436 }
437
438 if (nodes.size() < max_count) {
439 // Add the bookmark bar and other nodes if there is space.
440 if (find(nodes.begin(), nodes.end(), model->GetBookmarkBarNode()) ==
441 nodes.end()) {
442 nodes.push_back(model->GetBookmarkBarNode());
443 }
444
445 if (nodes.size() < max_count &&
446 find(nodes.begin(), nodes.end(), model->other_node()) == nodes.end()) {
447 nodes.push_back(model->other_node());
448 }
449 }
450 return nodes;
451}
452
453void GetMostRecentlyAddedEntries(BookmarkModel* model,
454 size_t count,
[email protected]b3c33d462009-06-26 22:29:20455 std::vector<const BookmarkNode*>* nodes) {
456 TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
[email protected]9333f182008-12-09 17:34:17457 while (iterator.has_next()) {
[email protected]b3c33d462009-06-26 22:29:20458 const BookmarkNode* node = iterator.Next();
[email protected]9333f182008-12-09 17:34:17459 if (node->is_url()) {
[email protected]b3c33d462009-06-26 22:29:20460 std::vector<const BookmarkNode*>::iterator insert_position =
[email protected]9333f182008-12-09 17:34:17461 std::upper_bound(nodes->begin(), nodes->end(), node,
462 &MoreRecentlyAdded);
463 if (nodes->size() < count || insert_position != nodes->end()) {
464 nodes->insert(insert_position, node);
465 while (nodes->size() > count)
466 nodes->pop_back();
467 }
468 }
469 }
470}
471
[email protected]b3c33d462009-06-26 22:29:20472bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]9333f182008-12-09 17:34:17473 return n1->date_added() > n2->date_added();
474}
475
[email protected]cb362ccc2008-12-10 17:22:32476void GetBookmarksContainingText(BookmarkModel* model,
477 const std::wstring& text,
478 size_t max_count,
[email protected]fa5dfaf2009-06-02 22:12:06479 const std::wstring& languages,
[email protected]b3c33d462009-06-26 22:29:20480 std::vector<const BookmarkNode*>* nodes) {
[email protected]cb362ccc2008-12-10 17:22:32481 std::vector<std::wstring> words;
482 QueryParser parser;
483 parser.ExtractQueryWords(l10n_util::ToLower(text), &words);
484 if (words.empty())
485 return;
486
[email protected]b3c33d462009-06-26 22:29:20487 TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
[email protected]cb362ccc2008-12-10 17:22:32488 while (iterator.has_next()) {
[email protected]b3c33d462009-06-26 22:29:20489 const BookmarkNode* node = iterator.Next();
[email protected]fa5dfaf2009-06-02 22:12:06490 if (node->is_url() && DoesBookmarkContainWords(node, words, languages)) {
[email protected]cb362ccc2008-12-10 17:22:32491 nodes->push_back(node);
492 if (nodes->size() == max_count)
493 return;
494 }
495 }
496}
497
[email protected]b3c33d462009-06-26 22:29:20498bool DoesBookmarkContainText(const BookmarkNode* node,
[email protected]fa5dfaf2009-06-02 22:12:06499 const std::wstring& text,
500 const std::wstring& languages) {
[email protected]cb362ccc2008-12-10 17:22:32501 std::vector<std::wstring> words;
502 QueryParser parser;
503 parser.ExtractQueryWords(l10n_util::ToLower(text), &words);
504 if (words.empty())
505 return false;
506
[email protected]fa5dfaf2009-06-02 22:12:06507 return (node->is_url() && DoesBookmarkContainWords(node, words, languages));
[email protected]cb362ccc2008-12-10 17:22:32508}
509
[email protected]ec12ffe2009-10-16 22:28:44510static const BookmarkNode* CreateNewNode(BookmarkModel* model,
511 const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
[email protected]a8d71b02009-07-07 00:36:34512 const std::wstring& new_title, const GURL& new_url,
513 BookmarkEditor::Handler* handler) {
[email protected]ec12ffe2009-10-16 22:28:44514 const BookmarkNode* node;
515 if (details.type == BookmarkEditor::EditDetails::NEW_URL) {
516 node = model->AddURL(parent, parent->GetChildCount(), new_title, new_url);
517 } else if (details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
518 node = model->AddGroup(parent, parent->GetChildCount(), new_title);
519 for (size_t i = 0; i < details.urls.size(); ++i) {
520 model->AddURL(node, node->GetChildCount(), details.urls[i].second,
521 details.urls[i].first);
522 }
[email protected]eea8fd5532009-12-16 00:08:10523 model->SetDateGroupModified(parent, Time::Now());
[email protected]ec12ffe2009-10-16 22:28:44524 } else {
525 NOTREACHED();
526 return NULL;
[email protected]140aea052009-05-05 00:35:09527 }
528
[email protected]ec12ffe2009-10-16 22:28:44529 if (handler)
530 handler->NodeCreated(node);
531 return node;
532}
533
534const BookmarkNode* ApplyEditsWithNoGroupChange(BookmarkModel* model,
535 const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
536 const std::wstring& new_title, const GURL& new_url,
537 BookmarkEditor::Handler* handler) {
538 if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
539 details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
540 return CreateNewNode(model, parent, details, new_title, new_url, handler);
541 }
542
543 const BookmarkNode* node = details.existing_node;
544 DCHECK(node);
545 const BookmarkNode* old_parent = node->GetParent();
546 int old_index = old_parent ? old_parent->IndexOfChild(node) : -1;
547
[email protected]140aea052009-05-05 00:35:09548 // If we're not showing the tree we only need to modify the node.
549 if (old_index == -1) {
550 NOTREACHED();
[email protected]a8d71b02009-07-07 00:36:34551 return node;
[email protected]140aea052009-05-05 00:35:09552 }
553
554 if (new_url != node->GetURL()) {
[email protected]ec12ffe2009-10-16 22:28:44555 // TODO(sky): need SetURL on the model.
[email protected]a8d71b02009-07-07 00:36:34556 const BookmarkNode* new_node = model->AddURLWithCreationTime(old_parent,
557 old_index, new_title, new_url, node->date_added());
[email protected]140aea052009-05-05 00:35:09558 model->Remove(old_parent, old_index + 1);
[email protected]a8d71b02009-07-07 00:36:34559 return new_node;
[email protected]140aea052009-05-05 00:35:09560 } else {
561 model->SetTitle(node, new_title);
562 }
[email protected]a8d71b02009-07-07 00:36:34563 return node;
[email protected]140aea052009-05-05 00:35:09564}
565
[email protected]a8d71b02009-07-07 00:36:34566const BookmarkNode* ApplyEditsWithPossibleGroupChange(BookmarkModel* model,
[email protected]ec12ffe2009-10-16 22:28:44567 const BookmarkNode* new_parent, const BookmarkEditor::EditDetails& details,
[email protected]a8d71b02009-07-07 00:36:34568 const std::wstring& new_title, const GURL& new_url,
569 BookmarkEditor::Handler* handler) {
[email protected]ec12ffe2009-10-16 22:28:44570 if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
571 details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
572 return CreateNewNode(model, new_parent, details, new_title, new_url,
573 handler);
574 }
575
576 const BookmarkNode* node = details.existing_node;
577 DCHECK(node);
578 const BookmarkNode* old_parent = node->GetParent();
579 int old_index = old_parent->IndexOfChild(node);
[email protected]a8d71b02009-07-07 00:36:34580 const BookmarkNode* return_node = node;
[email protected]ec12ffe2009-10-16 22:28:44581
582 Time date_added = node->date_added();
583 if (new_parent == node->GetParent()) {
584 // The parent is the same.
585 if (node->is_url() && new_url != node->GetURL()) {
[email protected]140aea052009-05-05 00:35:09586 model->Remove(old_parent, old_index);
[email protected]ec12ffe2009-10-16 22:28:44587 return_node = model->AddURLWithCreationTime(old_parent, old_index,
588 new_title, new_url, date_added);
[email protected]140aea052009-05-05 00:35:09589 } else {
[email protected]140aea052009-05-05 00:35:09590 model->SetTitle(node, new_title);
591 }
[email protected]ec12ffe2009-10-16 22:28:44592 } else if (node->is_url() && new_url != node->GetURL()) {
593 // The parent and URL changed.
594 model->Remove(old_parent, old_index);
595 return_node = model->AddURLWithCreationTime(new_parent,
596 new_parent->GetChildCount(), new_title, new_url, date_added);
[email protected]140aea052009-05-05 00:35:09597 } else {
[email protected]ec12ffe2009-10-16 22:28:44598 // The parent and title changed. Move the node and change the title.
599 model->Move(node, new_parent, new_parent->GetChildCount());
600 model->SetTitle(node, new_title);
[email protected]140aea052009-05-05 00:35:09601 }
[email protected]a8d71b02009-07-07 00:36:34602 return return_node;
[email protected]140aea052009-05-05 00:35:09603}
604
[email protected]44b2c8852009-03-18 00:57:49605// Formerly in BookmarkBarView
606void ToggleWhenVisible(Profile* profile) {
607 PrefService* prefs = profile->GetPrefs();
608 const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
609
610 // The user changed when the bookmark bar is shown, update the preferences.
611 prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
[email protected]6faa0e0d2009-04-28 06:50:36612 prefs->ScheduleSavePersistentPrefs();
[email protected]44b2c8852009-03-18 00:57:49613
614 // And notify the notification service.
615 Source<Profile> source(profile);
616 NotificationService::current()->Notify(
617 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
618 source,
619 NotificationService::NoDetails());
620}
621
[email protected]1e7bab72009-06-03 17:23:36622void RegisterPrefs(PrefService* prefs) {
623 prefs->RegisterDictionaryPref(prefs::kBookmarkManagerPlacement);
624 prefs->RegisterIntegerPref(prefs::kBookmarkManagerSplitLocation, -1);
625}
626
[email protected]44b2c8852009-03-18 00:57:49627void RegisterUserPrefs(PrefService* prefs) {
628 // Formerly in BookmarkBarView
629 prefs->RegisterBooleanPref(prefs::kShowBookmarkBar, false);
630
631 // Formerly in BookmarkTableView
632 prefs->RegisterIntegerPref(prefs::kBookmarkTableNameWidth1, -1);
633 prefs->RegisterIntegerPref(prefs::kBookmarkTableURLWidth1, -1);
634 prefs->RegisterIntegerPref(prefs::kBookmarkTableNameWidth2, -1);
635 prefs->RegisterIntegerPref(prefs::kBookmarkTableURLWidth2, -1);
636 prefs->RegisterIntegerPref(prefs::kBookmarkTablePathWidth, -1);
637}
638
[email protected]76624fde2009-10-09 18:13:23639void GetURLAndTitleToBookmark(TabContents* tab_contents,
[email protected]b3ac5c82009-10-08 20:56:54640 GURL* url,
641 std::wstring* title) {
[email protected]76624fde2009-10-09 18:13:23642 *url = tab_contents->GetURL();
643 *title = UTF16ToWideHack(tab_contents->GetTitle());
[email protected]b3ac5c82009-10-08 20:56:54644}
645
[email protected]ec12ffe2009-10-16 22:28:44646void GetURLsForOpenTabs(Browser* browser,
647 std::vector<std::pair<GURL, std::wstring> >* urls) {
[email protected]b3ac5c82009-10-08 20:56:54648 for (int i = 0; i < browser->tab_count(); ++i) {
[email protected]ec12ffe2009-10-16 22:28:44649 std::pair<GURL, std::wstring> entry;
650 GetURLAndTitleToBookmark(browser->GetTabContentsAt(i), &(entry.first),
651 &(entry.second));
652 urls->push_back(entry);
[email protected]b3ac5c82009-10-08 20:56:54653 }
[email protected]b3ac5c82009-10-08 20:56:54654}
655
[email protected]7f856be2008-10-29 23:38:06656} // namespace bookmark_utils