blob: 753c38c1a310ccba1fecdd15a636bcc40ddfcdda [file] [log] [blame]
[email protected]bbbc1ef2010-02-12 18:03:411// Copyright (c) 2010 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]37126212009-05-06 02:23:317#include "app/drag_drop_types.h"
[email protected]a92b8642009-05-05 23:38:568#include "app/l10n_util.h"
[email protected]992c6252009-05-13 18:54:209#include "app/tree_node_iterator.h"
[email protected]a0368962009-02-24 21:47:1610#include "base/basictypes.h"
[email protected]630947c2009-11-04 18:37:3111#include "base/file_path.h"
[email protected]528c56d2010-07-30 19:28:4412#include "base/string_number_conversions.h"
[email protected]9c6a85d2010-01-21 22:53:4613#include "base/string16.h"
[email protected]9333f182008-12-09 17:34:1714#include "base/time.h"
[email protected]7f856be2008-10-29 23:38:0615#include "chrome/browser/bookmarks/bookmark_drag_data.h"
16#include "chrome/browser/bookmarks/bookmark_model.h"
[email protected]eda74d62010-03-07 03:43:5117#if defined(OS_MACOSX)
18#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
19#endif
[email protected]7f856be2008-10-29 23:38:0620#include "chrome/browser/browser.h"
21#include "chrome/browser/browser_list.h"
[email protected]b08cadb92009-08-04 21:52:2922#include "chrome/browser/browser_process.h"
[email protected]ce560f82009-06-03 09:39:4423#include "chrome/browser/browser_window.h"
[email protected]9333f182008-12-09 17:34:1724#include "chrome/browser/history/query_parser.h"
[email protected]e313f3b12010-06-25 21:29:1025#include "chrome/browser/platform_util.h"
[email protected]052313b2010-02-19 09:43:0826#include "chrome/browser/pref_service.h"
[email protected]505323e22009-01-24 02:47:5827#include "chrome/browser/profile.h"
[email protected]f3ec7742009-01-15 00:59:1628#include "chrome/browser/tab_contents/page_navigator.h"
[email protected]1132436e2009-04-08 20:06:3329#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]44b2c8852009-03-18 00:57:4930#include "chrome/common/notification_service.h"
31#include "chrome/common/pref_names.h"
[email protected]903e7a82009-07-28 00:45:3532#include "grit/app_strings.h"
[email protected]34ac8f32009-02-22 23:03:2733#include "grit/chromium_strings.h"
34#include "grit/generated_resources.h"
[email protected]fa5dfaf2009-06-02 22:12:0635#include "net/base/net_util.h"
[email protected]2362e4f2009-05-08 00:34:0536#include "views/event.h"
[email protected]7f856be2008-10-29 23:38:0637
[email protected]ced90ae12010-02-20 02:06:1638#if defined(TOOLKIT_VIEWS)
39#include "app/os_exchange_data.h"
40#include "views/drag_utils.h"
41#include "views/widget/root_view.h"
42#include "views/widget/widget.h"
[email protected]2ca8a062010-03-18 17:10:1843#elif defined(TOOLKIT_GTK)
[email protected]2ca8a062010-03-18 17:10:1844#include "chrome/browser/gtk/custom_drag.h"
[email protected]ced90ae12010-02-20 02:06:1645#endif
46
[email protected]140aea052009-05-05 00:35:0947using base::Time;
48
[email protected]7f856be2008-10-29 23:38:0649namespace {
50
[email protected]7f856be2008-10-29 23:38:0651// A PageNavigator implementation that creates a new Browser. This is used when
52// opening a url and there is no Browser open. The Browser is created the first
53// time the PageNavigator method is invoked.
54class NewBrowserPageNavigator : public PageNavigator {
55 public:
56 explicit NewBrowserPageNavigator(Profile* profile)
57 : profile_(profile),
58 browser_(NULL) {}
59
60 virtual ~NewBrowserPageNavigator() {
61 if (browser_)
[email protected]15952e462008-11-14 00:29:0562 browser_->window()->Show();
[email protected]7f856be2008-10-29 23:38:0663 }
64
65 Browser* browser() const { return browser_; }
66
67 virtual void OpenURL(const GURL& url,
68 const GURL& referrer,
69 WindowOpenDisposition disposition,
70 PageTransition::Type transition) {
71 if (!browser_) {
72 Profile* profile = (disposition == OFF_THE_RECORD) ?
73 profile_->GetOffTheRecordProfile() : profile_;
[email protected]15952e462008-11-14 00:29:0574 browser_ = Browser::Create(profile);
[email protected]7f856be2008-10-29 23:38:0675 // Always open the first tab in the foreground.
76 disposition = NEW_FOREGROUND_TAB;
77 }
[email protected]e0c7c262009-04-23 23:09:4378 browser_->OpenURL(url, referrer, NEW_FOREGROUND_TAB, transition);
[email protected]7f856be2008-10-29 23:38:0679 }
80
81 private:
82 Profile* profile_;
83 Browser* browser_;
84
85 DISALLOW_COPY_AND_ASSIGN(NewBrowserPageNavigator);
86};
87
[email protected]4e187ef652010-03-11 05:21:3588// TODO(mrossetti): Rename CloneDragDataImpl to CloneBookmarkNodeImpl.
89// See: http://crbug.com/37891
90
[email protected]7f856be2008-10-29 23:38:0691void CloneDragDataImpl(BookmarkModel* model,
92 const BookmarkDragData::Element& element,
[email protected]b3c33d462009-06-26 22:29:2093 const BookmarkNode* parent,
[email protected]7f856be2008-10-29 23:38:0694 int index_to_add_at) {
95 if (element.is_url) {
96 model->AddURL(parent, index_to_add_at, element.title, element.url);
97 } else {
[email protected]b3c33d462009-06-26 22:29:2098 const BookmarkNode* new_folder = model->AddGroup(parent,
99 index_to_add_at,
100 element.title);
[email protected]7f856be2008-10-29 23:38:06101 for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
102 CloneDragDataImpl(model, element.children[i], new_folder, i);
103 }
104}
105
106// Returns the number of descendants of node that are of type url.
[email protected]b3c33d462009-06-26 22:29:20107int DescendantURLCount(const BookmarkNode* node) {
[email protected]7f856be2008-10-29 23:38:06108 int result = 0;
109 for (int i = 0; i < node->GetChildCount(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20110 const BookmarkNode* child = node->GetChild(i);
[email protected]7f856be2008-10-29 23:38:06111 if (child->is_url())
112 result++;
113 else
114 result += DescendantURLCount(child);
115 }
116 return result;
117}
118
119// Implementation of OpenAll. Opens all nodes of type URL and recurses for
120// groups. |navigator| is the PageNavigator used to open URLs. After the first
121// url is opened |opened_url| is set to true and |navigator| is set to the
122// PageNavigator of the last active tab. This is done to handle a window
123// disposition of new window, in which case we want subsequent tabs to open in
124// that window.
[email protected]b3c33d462009-06-26 22:29:20125void OpenAllImpl(const BookmarkNode* node,
[email protected]7f856be2008-10-29 23:38:06126 WindowOpenDisposition initial_disposition,
127 PageNavigator** navigator,
128 bool* opened_url) {
129 if (node->is_url()) {
130 WindowOpenDisposition disposition;
131 if (*opened_url)
132 disposition = NEW_BACKGROUND_TAB;
133 else
134 disposition = initial_disposition;
135 (*navigator)->OpenURL(node->GetURL(), GURL(), disposition,
136 PageTransition::AUTO_BOOKMARK);
137 if (!*opened_url) {
138 *opened_url = true;
139 // We opened the first URL which may have opened a new window or clobbered
140 // the current page, reset the navigator just to be sure.
141 Browser* new_browser = BrowserList::GetLastActive();
142 if (new_browser) {
143 TabContents* current_tab = new_browser->GetSelectedTabContents();
144 DCHECK(new_browser && current_tab);
145 if (new_browser && current_tab)
146 *navigator = current_tab;
147 } // else, new_browser == NULL, which happens during testing.
148 }
149 } else {
150 // Group, recurse through children.
151 for (int i = 0; i < node->GetChildCount(); ++i) {
152 OpenAllImpl(node->GetChild(i), initial_disposition, navigator,
153 opened_url);
154 }
155 }
156}
157
[email protected]d6d6d582009-10-12 19:22:26158bool ShouldOpenAll(gfx::NativeWindow parent,
[email protected]b3c33d462009-06-26 22:29:20159 const std::vector<const BookmarkNode*>& nodes) {
[email protected]7f856be2008-10-29 23:38:06160 int descendant_count = 0;
161 for (size_t i = 0; i < nodes.size(); ++i)
162 descendant_count += DescendantURLCount(nodes[i]);
[email protected]f785ad12008-11-19 23:01:28163 if (descendant_count < bookmark_utils::num_urls_before_prompting)
[email protected]7f856be2008-10-29 23:38:06164 return true;
165
[email protected]e313f3b12010-06-25 21:29:10166 string16 message = l10n_util::GetStringFUTF16(
[email protected]d6310992010-03-31 18:03:35167 IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
[email protected]528c56d2010-07-30 19:28:44168 base::IntToString16(descendant_count));
[email protected]e313f3b12010-06-25 21:29:10169 string16 title = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
170 return platform_util::SimpleYesNoBox(parent, title, message);
[email protected]7f856be2008-10-29 23:38:06171}
172
[email protected]9333f182008-12-09 17:34:17173// Comparison function that compares based on date modified of the two nodes.
[email protected]b3c33d462009-06-26 22:29:20174bool MoreRecentlyModified(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]9333f182008-12-09 17:34:17175 return n1->date_group_modified() > n2->date_group_modified();
176}
177
[email protected]cb362ccc2008-12-10 17:22:32178// Returns true if |text| contains each string in |words|. This is used when
179// searching for bookmarks.
[email protected]e53668962010-06-23 15:35:25180bool DoesBookmarkTextContainWords(const string16& text,
181 const std::vector<string16>& words) {
[email protected]cb362ccc2008-12-10 17:22:32182 for (size_t i = 0; i < words.size(); ++i) {
183 if (text.find(words[i]) == std::wstring::npos)
184 return false;
185 }
186 return true;
187}
188
189// Returns true if |node|s title or url contains the strings in |words|.
[email protected]fa5dfaf2009-06-02 22:12:06190// |languages| argument is user's accept-language setting to decode IDN.
[email protected]b3c33d462009-06-26 22:29:20191bool DoesBookmarkContainWords(const BookmarkNode* node,
[email protected]e53668962010-06-23 15:35:25192 const std::vector<string16>& words,
[email protected]fa5dfaf2009-06-02 22:12:06193 const std::wstring& languages) {
[email protected]cb362ccc2008-12-10 17:22:32194 return
195 DoesBookmarkTextContainWords(
[email protected]e53668962010-06-23 15:35:25196 l10n_util::ToLower(WideToUTF16(node->GetTitle())), words) ||
[email protected]1f31bb72009-12-16 03:18:25197 DoesBookmarkTextContainWords(
[email protected]e53668962010-06-23 15:35:25198 l10n_util::ToLower(UTF8ToUTF16(node->GetURL().spec())), words) ||
199 DoesBookmarkTextContainWords(l10n_util::ToLower(WideToUTF16(
200 net::FormatUrl(
201 node->GetURL(), languages, net::kFormatUrlOmitNothing,
202 UnescapeRule::NORMAL, NULL, NULL, NULL))), words);
[email protected]cb362ccc2008-12-10 17:22:32203}
204
[email protected]7f856be2008-10-29 23:38:06205} // namespace
206
207namespace bookmark_utils {
208
[email protected]f785ad12008-11-19 23:01:28209int num_urls_before_prompting = 15;
210
[email protected]f28cbb72008-11-04 19:29:08211int PreferredDropOperation(int source_operations, int operations) {
212 int common_ops = (source_operations & operations);
[email protected]7f856be2008-10-29 23:38:06213 if (!common_ops)
214 return 0;
215 if (DragDropTypes::DRAG_COPY & common_ops)
216 return DragDropTypes::DRAG_COPY;
217 if (DragDropTypes::DRAG_LINK & common_ops)
218 return DragDropTypes::DRAG_LINK;
219 if (DragDropTypes::DRAG_MOVE & common_ops)
220 return DragDropTypes::DRAG_MOVE;
221 return DragDropTypes::DRAG_NONE;
222}
223
[email protected]b3c33d462009-06-26 22:29:20224int BookmarkDragOperation(const BookmarkNode* node) {
[email protected]7aa4c002009-03-12 19:10:22225 if (node->is_url()) {
226 return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE |
227 DragDropTypes::DRAG_LINK;
228 }
229 return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
230}
231
232int BookmarkDropOperation(Profile* profile,
233 const views::DropTargetEvent& event,
234 const BookmarkDragData& data,
[email protected]b3c33d462009-06-26 22:29:20235 const BookmarkNode* parent,
[email protected]7aa4c002009-03-12 19:10:22236 int index) {
237 if (data.IsFromProfile(profile) && data.size() > 1)
238 // Currently only accept one dragged node at a time.
239 return DragDropTypes::DRAG_NONE;
240
241 if (!bookmark_utils::IsValidDropLocation(profile, data, parent, index))
242 return DragDropTypes::DRAG_NONE;
243
244 if (data.GetFirstNode(profile)) {
245 // User is dragging from this profile: move.
246 return DragDropTypes::DRAG_MOVE;
247 }
248 // User is dragging from another app, copy.
249 return PreferredDropOperation(event.GetSourceOperations(),
250 DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK);
251}
252
253int PerformBookmarkDrop(Profile* profile,
254 const BookmarkDragData& data,
[email protected]b3c33d462009-06-26 22:29:20255 const BookmarkNode* parent_node,
[email protected]7aa4c002009-03-12 19:10:22256 int index) {
[email protected]7aa4c002009-03-12 19:10:22257 BookmarkModel* model = profile->GetBookmarkModel();
[email protected]eda74d62010-03-07 03:43:51258 if (data.IsFromProfile(profile)) {
259 const std::vector<const BookmarkNode*> dragged_nodes =
260 data.GetNodes(profile);
261 if (!dragged_nodes.empty()) {
262 // Drag from same profile. Move nodes.
263 for (size_t i = 0; i < dragged_nodes.size(); ++i) {
264 model->Move(dragged_nodes[i], parent_node, index);
265 index = parent_node->IndexOfChild(dragged_nodes[i]) + 1;
266 }
267 return DragDropTypes::DRAG_MOVE;
[email protected]ced90ae12010-02-20 02:06:16268 }
[email protected]eda74d62010-03-07 03:43:51269 return DragDropTypes::DRAG_NONE;
[email protected]7aa4c002009-03-12 19:10:22270 }
[email protected]eda74d62010-03-07 03:43:51271 // Dropping a group from different profile. Always accept.
272 bookmark_utils::CloneDragData(model, data.elements, parent_node, index);
273 return DragDropTypes::DRAG_COPY;
[email protected]7aa4c002009-03-12 19:10:22274}
275
[email protected]7f856be2008-10-29 23:38:06276bool IsValidDropLocation(Profile* profile,
277 const BookmarkDragData& data,
[email protected]b3c33d462009-06-26 22:29:20278 const BookmarkNode* drop_parent,
[email protected]7f856be2008-10-29 23:38:06279 int index) {
280 if (!drop_parent->is_folder()) {
281 NOTREACHED();
282 return false;
283 }
284
285 if (!data.is_valid())
286 return false;
287
288 if (data.IsFromProfile(profile)) {
[email protected]b3c33d462009-06-26 22:29:20289 std::vector<const BookmarkNode*> nodes = data.GetNodes(profile);
[email protected]7f856be2008-10-29 23:38:06290 for (size_t i = 0; i < nodes.size(); ++i) {
291 // Don't allow the drop if the user is attempting to drop on one of the
292 // nodes being dragged.
[email protected]b3c33d462009-06-26 22:29:20293 const BookmarkNode* node = nodes[i];
[email protected]7f856be2008-10-29 23:38:06294 int node_index = (drop_parent == node->GetParent()) ?
295 drop_parent->IndexOfChild(nodes[i]) : -1;
296 if (node_index != -1 && (index == node_index || index == node_index + 1))
297 return false;
298
299 // drop_parent can't accept a child that is an ancestor.
300 if (drop_parent->HasAncestor(node))
301 return false;
302 }
303 return true;
304 }
305 // From the same profile, always accept.
306 return true;
307}
308
309void CloneDragData(BookmarkModel* model,
310 const std::vector<BookmarkDragData::Element>& elements,
[email protected]b3c33d462009-06-26 22:29:20311 const BookmarkNode* parent,
[email protected]7f856be2008-10-29 23:38:06312 int index_to_add_at) {
313 if (!parent->is_folder() || !model) {
314 NOTREACHED();
315 return;
316 }
317 for (size_t i = 0; i < elements.size(); ++i)
318 CloneDragDataImpl(model, elements[i], parent, index_to_add_at + i);
319}
320
[email protected]ced90ae12010-02-20 02:06:16321
322// Bookmark dragging
323void DragBookmarks(Profile* profile,
324 const std::vector<const BookmarkNode*>& nodes,
325 gfx::NativeView view) {
326 DCHECK(!nodes.empty());
327
328#if defined(TOOLKIT_VIEWS)
329 // Set up our OLE machinery
330 OSExchangeData data;
331 BookmarkDragData drag_data(nodes);
332 drag_data.Write(profile, &data);
333
[email protected]e313f3b12010-06-25 21:29:10334 views::RootView* root_view =
335 views::Widget::GetWidgetFromNativeView(view)->GetRootView();
[email protected]ced90ae12010-02-20 02:06:16336
337 // Allow nested message loop so we get DnD events as we drag this around.
338 bool was_nested = MessageLoop::current()->IsNested();
339 MessageLoop::current()->SetNestableTasksAllowed(true);
340
341 root_view->StartDragForViewFromMouseEvent(NULL, data,
342 DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE |
343 DragDropTypes::DRAG_LINK);
344
345 MessageLoop::current()->SetNestableTasksAllowed(was_nested);
[email protected]eda74d62010-03-07 03:43:51346#elif defined(OS_MACOSX)
347 // Allow nested message loop so we get DnD events as we drag this around.
348 bool was_nested = MessageLoop::current()->IsNested();
349 MessageLoop::current()->SetNestableTasksAllowed(true);
350 bookmark_pasteboard_helper_mac::StartDrag(profile, nodes, view);
351 MessageLoop::current()->SetNestableTasksAllowed(was_nested);
[email protected]2ca8a062010-03-18 17:10:18352#elif defined(TOOLKIT_GTK)
353 BookmarkDrag::BeginDrag(profile, nodes);
[email protected]ced90ae12010-02-20 02:06:16354#endif
355}
356
[email protected]d6d6d582009-10-12 19:22:26357void OpenAll(gfx::NativeWindow parent,
[email protected]7f856be2008-10-29 23:38:06358 Profile* profile,
359 PageNavigator* navigator,
[email protected]b3c33d462009-06-26 22:29:20360 const std::vector<const BookmarkNode*>& nodes,
[email protected]7f856be2008-10-29 23:38:06361 WindowOpenDisposition initial_disposition) {
362 if (!ShouldOpenAll(parent, nodes))
363 return;
364
365 NewBrowserPageNavigator navigator_impl(profile);
366 if (!navigator) {
[email protected]f0a51fb52009-03-05 12:46:38367 Browser* browser =
[email protected]62b0b532010-03-26 22:44:31368 BrowserList::FindBrowserWithType(profile, Browser::TYPE_NORMAL, false);
[email protected]b6f2b9132008-11-17 16:27:51369 if (!browser || !browser->GetSelectedTabContents()) {
[email protected]7f856be2008-10-29 23:38:06370 navigator = &navigator_impl;
[email protected]b6f2b9132008-11-17 16:27:51371 } else {
[email protected]c9938e6d2009-03-13 19:54:11372 if (initial_disposition != NEW_WINDOW &&
373 initial_disposition != OFF_THE_RECORD) {
374 browser->window()->Activate();
375 }
[email protected]7f856be2008-10-29 23:38:06376 navigator = browser->GetSelectedTabContents();
[email protected]b6f2b9132008-11-17 16:27:51377 }
[email protected]7f856be2008-10-29 23:38:06378 }
379
380 bool opened_url = false;
381 for (size_t i = 0; i < nodes.size(); ++i)
382 OpenAllImpl(nodes[i], initial_disposition, &navigator, &opened_url);
383}
384
[email protected]d6d6d582009-10-12 19:22:26385void OpenAll(gfx::NativeWindow parent,
[email protected]7f856be2008-10-29 23:38:06386 Profile* profile,
387 PageNavigator* navigator,
[email protected]b3c33d462009-06-26 22:29:20388 const BookmarkNode* node,
[email protected]7f856be2008-10-29 23:38:06389 WindowOpenDisposition initial_disposition) {
[email protected]b3c33d462009-06-26 22:29:20390 std::vector<const BookmarkNode*> nodes;
[email protected]7f856be2008-10-29 23:38:06391 nodes.push_back(node);
392 OpenAll(parent, profile, navigator, nodes, initial_disposition);
393}
394
[email protected]fafc8a422008-11-07 17:53:09395void CopyToClipboard(BookmarkModel* model,
[email protected]b3c33d462009-06-26 22:29:20396 const std::vector<const BookmarkNode*>& nodes,
[email protected]fafc8a422008-11-07 17:53:09397 bool remove_nodes) {
398 if (nodes.empty())
399 return;
400
[email protected]b08cadb92009-08-04 21:52:29401 BookmarkDragData(nodes).WriteToClipboard(NULL);
[email protected]fafc8a422008-11-07 17:53:09402
403 if (remove_nodes) {
404 for (size_t i = 0; i < nodes.size(); ++i) {
405 model->Remove(nodes[i]->GetParent(),
406 nodes[i]->GetParent()->IndexOfChild(nodes[i]));
407 }
408 }
409}
410
411void PasteFromClipboard(BookmarkModel* model,
[email protected]b3c33d462009-06-26 22:29:20412 const BookmarkNode* parent,
[email protected]fafc8a422008-11-07 17:53:09413 int index) {
414 if (!parent)
415 return;
416
[email protected]fafc8a422008-11-07 17:53:09417 BookmarkDragData bookmark_data;
[email protected]b08cadb92009-08-04 21:52:29418 if (!bookmark_data.ReadFromClipboard())
[email protected]fafc8a422008-11-07 17:53:09419 return;
420
421 if (index == -1)
422 index = parent->GetChildCount();
423 bookmark_utils::CloneDragData(model, bookmark_data.elements, parent, index);
424}
425
[email protected]b3c33d462009-06-26 22:29:20426bool CanPasteFromClipboard(const BookmarkNode* node) {
[email protected]fafc8a422008-11-07 17:53:09427 if (!node)
428 return false;
[email protected]0fe52a02010-02-06 00:39:25429 return BookmarkDragData::ClipboardContainsBookmarks();
[email protected]fafc8a422008-11-07 17:53:09430}
431
[email protected]903e7a82009-07-28 00:45:35432std::string GetNameForURL(const GURL& url) {
433 if (url.is_valid()) {
434 return WideToUTF8(net::GetSuggestedFilename(
[email protected]630947c2009-11-04 18:37:31435 url, std::string(), std::string(), FilePath()).ToWStringHack());
[email protected]903e7a82009-07-28 00:45:35436 } else {
437 return l10n_util::GetStringUTF8(IDS_APP_UNTITLED_SHORTCUT_FILE_NAME);
438 }
439}
440
[email protected]b3c33d462009-06-26 22:29:20441std::vector<const BookmarkNode*> GetMostRecentlyModifiedGroups(
[email protected]9333f182008-12-09 17:34:17442 BookmarkModel* model,
443 size_t max_count) {
[email protected]b3c33d462009-06-26 22:29:20444 std::vector<const BookmarkNode*> nodes;
445 TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
[email protected]9333f182008-12-09 17:34:17446 while (iterator.has_next()) {
[email protected]b3c33d462009-06-26 22:29:20447 const BookmarkNode* parent = iterator.Next();
[email protected]9333f182008-12-09 17:34:17448 if (parent->is_folder() && parent->date_group_modified() > base::Time()) {
449 if (max_count == 0) {
450 nodes.push_back(parent);
451 } else {
[email protected]b3c33d462009-06-26 22:29:20452 std::vector<const BookmarkNode*>::iterator i =
[email protected]9333f182008-12-09 17:34:17453 std::upper_bound(nodes.begin(), nodes.end(), parent,
454 &MoreRecentlyModified);
455 if (nodes.size() < max_count || i != nodes.end()) {
456 nodes.insert(i, parent);
457 while (nodes.size() > max_count)
458 nodes.pop_back();
459 }
460 }
461 } // else case, the root node, which we don't care about or imported nodes
462 // (which have a time of 0).
463 }
464
465 if (nodes.size() < max_count) {
466 // Add the bookmark bar and other nodes if there is space.
467 if (find(nodes.begin(), nodes.end(), model->GetBookmarkBarNode()) ==
468 nodes.end()) {
469 nodes.push_back(model->GetBookmarkBarNode());
470 }
471
472 if (nodes.size() < max_count &&
473 find(nodes.begin(), nodes.end(), model->other_node()) == nodes.end()) {
474 nodes.push_back(model->other_node());
475 }
476 }
477 return nodes;
478}
479
480void GetMostRecentlyAddedEntries(BookmarkModel* model,
481 size_t count,
[email protected]b3c33d462009-06-26 22:29:20482 std::vector<const BookmarkNode*>* nodes) {
483 TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
[email protected]9333f182008-12-09 17:34:17484 while (iterator.has_next()) {
[email protected]b3c33d462009-06-26 22:29:20485 const BookmarkNode* node = iterator.Next();
[email protected]9333f182008-12-09 17:34:17486 if (node->is_url()) {
[email protected]b3c33d462009-06-26 22:29:20487 std::vector<const BookmarkNode*>::iterator insert_position =
[email protected]9333f182008-12-09 17:34:17488 std::upper_bound(nodes->begin(), nodes->end(), node,
489 &MoreRecentlyAdded);
490 if (nodes->size() < count || insert_position != nodes->end()) {
491 nodes->insert(insert_position, node);
492 while (nodes->size() > count)
493 nodes->pop_back();
494 }
495 }
496 }
497}
498
[email protected]b3c33d462009-06-26 22:29:20499bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]9333f182008-12-09 17:34:17500 return n1->date_added() > n2->date_added();
501}
502
[email protected]cb362ccc2008-12-10 17:22:32503void GetBookmarksContainingText(BookmarkModel* model,
504 const std::wstring& text,
505 size_t max_count,
[email protected]fa5dfaf2009-06-02 22:12:06506 const std::wstring& languages,
[email protected]b3c33d462009-06-26 22:29:20507 std::vector<const BookmarkNode*>* nodes) {
[email protected]e53668962010-06-23 15:35:25508 std::vector<string16> words;
[email protected]cb362ccc2008-12-10 17:22:32509 QueryParser parser;
[email protected]e53668962010-06-23 15:35:25510 parser.ExtractQueryWords(l10n_util::ToLower(WideToUTF16(text)), &words);
[email protected]cb362ccc2008-12-10 17:22:32511 if (words.empty())
512 return;
513
[email protected]b3c33d462009-06-26 22:29:20514 TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
[email protected]cb362ccc2008-12-10 17:22:32515 while (iterator.has_next()) {
[email protected]b3c33d462009-06-26 22:29:20516 const BookmarkNode* node = iterator.Next();
[email protected]fa5dfaf2009-06-02 22:12:06517 if (node->is_url() && DoesBookmarkContainWords(node, words, languages)) {
[email protected]cb362ccc2008-12-10 17:22:32518 nodes->push_back(node);
519 if (nodes->size() == max_count)
520 return;
521 }
522 }
523}
524
[email protected]b3c33d462009-06-26 22:29:20525bool DoesBookmarkContainText(const BookmarkNode* node,
[email protected]fa5dfaf2009-06-02 22:12:06526 const std::wstring& text,
527 const std::wstring& languages) {
[email protected]e53668962010-06-23 15:35:25528 std::vector<string16> words;
[email protected]cb362ccc2008-12-10 17:22:32529 QueryParser parser;
[email protected]e53668962010-06-23 15:35:25530 parser.ExtractQueryWords(l10n_util::ToLower(WideToUTF16(text)), &words);
[email protected]cb362ccc2008-12-10 17:22:32531 if (words.empty())
532 return false;
533
[email protected]fa5dfaf2009-06-02 22:12:06534 return (node->is_url() && DoesBookmarkContainWords(node, words, languages));
[email protected]cb362ccc2008-12-10 17:22:32535}
536
[email protected]ec12ffe2009-10-16 22:28:44537static const BookmarkNode* CreateNewNode(BookmarkModel* model,
538 const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
[email protected]24319272010-04-29 16:31:43539 const std::wstring& new_title, const GURL& new_url) {
[email protected]ec12ffe2009-10-16 22:28:44540 const BookmarkNode* node;
541 if (details.type == BookmarkEditor::EditDetails::NEW_URL) {
542 node = model->AddURL(parent, parent->GetChildCount(), new_title, new_url);
543 } else if (details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
544 node = model->AddGroup(parent, parent->GetChildCount(), new_title);
545 for (size_t i = 0; i < details.urls.size(); ++i) {
546 model->AddURL(node, node->GetChildCount(), details.urls[i].second,
547 details.urls[i].first);
548 }
[email protected]eea8fd5532009-12-16 00:08:10549 model->SetDateGroupModified(parent, Time::Now());
[email protected]ec12ffe2009-10-16 22:28:44550 } else {
551 NOTREACHED();
552 return NULL;
[email protected]140aea052009-05-05 00:35:09553 }
554
[email protected]ec12ffe2009-10-16 22:28:44555 return node;
556}
557
558const BookmarkNode* ApplyEditsWithNoGroupChange(BookmarkModel* model,
559 const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
[email protected]24319272010-04-29 16:31:43560 const std::wstring& new_title, const GURL& new_url) {
[email protected]ec12ffe2009-10-16 22:28:44561 if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
562 details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
[email protected]24319272010-04-29 16:31:43563 return CreateNewNode(model, parent, details, new_title, new_url);
[email protected]ec12ffe2009-10-16 22:28:44564 }
565
566 const BookmarkNode* node = details.existing_node;
567 DCHECK(node);
[email protected]ec12ffe2009-10-16 22:28:44568
[email protected]e5486602010-02-09 21:27:55569 if (node->is_url())
570 model->SetURL(node, new_url);
571 model->SetTitle(node, new_title);
[email protected]140aea052009-05-05 00:35:09572
[email protected]a8d71b02009-07-07 00:36:34573 return node;
[email protected]140aea052009-05-05 00:35:09574}
575
[email protected]a8d71b02009-07-07 00:36:34576const BookmarkNode* ApplyEditsWithPossibleGroupChange(BookmarkModel* model,
[email protected]ec12ffe2009-10-16 22:28:44577 const BookmarkNode* new_parent, const BookmarkEditor::EditDetails& details,
[email protected]24319272010-04-29 16:31:43578 const std::wstring& new_title, const GURL& new_url) {
[email protected]ec12ffe2009-10-16 22:28:44579 if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
580 details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
[email protected]24319272010-04-29 16:31:43581 return CreateNewNode(model, new_parent, details, new_title, new_url);
[email protected]ec12ffe2009-10-16 22:28:44582 }
583
584 const BookmarkNode* node = details.existing_node;
585 DCHECK(node);
[email protected]ec12ffe2009-10-16 22:28:44586
[email protected]e5486602010-02-09 21:27:55587 if (new_parent != node->GetParent())
[email protected]ec12ffe2009-10-16 22:28:44588 model->Move(node, new_parent, new_parent->GetChildCount());
[email protected]e5486602010-02-09 21:27:55589 if (node->is_url())
590 model->SetURL(node, new_url);
591 model->SetTitle(node, new_title);
592
593 return node;
[email protected]140aea052009-05-05 00:35:09594}
595
[email protected]44b2c8852009-03-18 00:57:49596// Formerly in BookmarkBarView
597void ToggleWhenVisible(Profile* profile) {
598 PrefService* prefs = profile->GetPrefs();
599 const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
600
601 // The user changed when the bookmark bar is shown, update the preferences.
602 prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
[email protected]6faa0e0d2009-04-28 06:50:36603 prefs->ScheduleSavePersistentPrefs();
[email protected]44b2c8852009-03-18 00:57:49604
605 // And notify the notification service.
606 Source<Profile> source(profile);
607 NotificationService::current()->Notify(
608 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
609 source,
610 NotificationService::NoDetails());
611}
612
[email protected]44b2c8852009-03-18 00:57:49613void RegisterUserPrefs(PrefService* prefs) {
[email protected]44b2c8852009-03-18 00:57:49614 prefs->RegisterBooleanPref(prefs::kShowBookmarkBar, false);
[email protected]44b2c8852009-03-18 00:57:49615}
616
[email protected]76624fde2009-10-09 18:13:23617void GetURLAndTitleToBookmark(TabContents* tab_contents,
[email protected]b3ac5c82009-10-08 20:56:54618 GURL* url,
619 std::wstring* title) {
[email protected]76624fde2009-10-09 18:13:23620 *url = tab_contents->GetURL();
621 *title = UTF16ToWideHack(tab_contents->GetTitle());
[email protected]b3ac5c82009-10-08 20:56:54622}
623
[email protected]ec12ffe2009-10-16 22:28:44624void GetURLsForOpenTabs(Browser* browser,
625 std::vector<std::pair<GURL, std::wstring> >* urls) {
[email protected]b3ac5c82009-10-08 20:56:54626 for (int i = 0; i < browser->tab_count(); ++i) {
[email protected]ec12ffe2009-10-16 22:28:44627 std::pair<GURL, std::wstring> entry;
628 GetURLAndTitleToBookmark(browser->GetTabContentsAt(i), &(entry.first),
629 &(entry.second));
630 urls->push_back(entry);
[email protected]b3ac5c82009-10-08 20:56:54631 }
[email protected]b3ac5c82009-10-08 20:56:54632}
633
[email protected]e160e8322010-03-25 17:06:12634const BookmarkNode* GetParentForNewNodes(
635 const BookmarkNode* parent,
636 const std::vector<const BookmarkNode*>& selection,
637 int* index) {
638 const BookmarkNode* real_parent = parent;
639
640 if (selection.size() == 1 && selection[0]->is_folder())
641 real_parent = selection[0];
642
643 if (index) {
644 if (selection.size() == 1 && selection[0]->is_url()) {
645 *index = real_parent->IndexOfChild(selection[0]) + 1;
646 if (*index == 0) {
647 // Node doesn't exist in parent, add to end.
648 NOTREACHED();
649 *index = real_parent->GetChildCount();
650 }
651 } else {
652 *index = real_parent->GetChildCount();
653 }
654 }
655
656 return real_parent;
657}
658
[email protected]7f856be2008-10-29 23:38:06659} // namespace bookmark_utils