blob: 1a25556ad17f2d7bf8c9b6d151b62261061920f4 [file] [log] [blame]
[email protected]7f856be2008-10-29 23:38:061// Copyright (c) 2006-2008 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_utils.h"
6
[email protected]9333f182008-12-09 17:34:177#include "base/time.h"
[email protected]7f856be2008-10-29 23:38:068#include "chrome/browser/bookmarks/bookmark_drag_data.h"
9#include "chrome/browser/bookmarks/bookmark_model.h"
10#include "chrome/browser/browser.h"
11#include "chrome/browser/browser_list.h"
[email protected]9333f182008-12-09 17:34:1712#include "chrome/browser/history/query_parser.h"
[email protected]7f856be2008-10-29 23:38:0613#include "chrome/browser/page_navigator.h"
14#include "chrome/browser/tab_contents.h"
15#include "chrome/common/drag_drop_types.h"
16#include "chrome/common/l10n_util.h"
[email protected]fafc8a422008-11-07 17:53:0917#include "chrome/common/os_exchange_data.h"
[email protected]7f856be2008-10-29 23:38:0618#include "chrome/views/event.h"
[email protected]9333f182008-12-09 17:34:1719#include "chrome/views/tree_node_iterator.h"
[email protected]7f856be2008-10-29 23:38:0620
21#include "chromium_strings.h"
22#include "generated_resources.h"
23
24namespace {
25
[email protected]7f856be2008-10-29 23:38:0626// A PageNavigator implementation that creates a new Browser. This is used when
27// opening a url and there is no Browser open. The Browser is created the first
28// time the PageNavigator method is invoked.
29class NewBrowserPageNavigator : public PageNavigator {
30 public:
31 explicit NewBrowserPageNavigator(Profile* profile)
32 : profile_(profile),
33 browser_(NULL) {}
34
35 virtual ~NewBrowserPageNavigator() {
36 if (browser_)
[email protected]15952e462008-11-14 00:29:0537 browser_->window()->Show();
[email protected]7f856be2008-10-29 23:38:0638 }
39
40 Browser* browser() const { return browser_; }
41
42 virtual void OpenURL(const GURL& url,
43 const GURL& referrer,
44 WindowOpenDisposition disposition,
45 PageTransition::Type transition) {
46 if (!browser_) {
47 Profile* profile = (disposition == OFF_THE_RECORD) ?
48 profile_->GetOffTheRecordProfile() : profile_;
[email protected]15952e462008-11-14 00:29:0549 browser_ = Browser::Create(profile);
[email protected]7f856be2008-10-29 23:38:0650 // Always open the first tab in the foreground.
51 disposition = NEW_FOREGROUND_TAB;
52 }
53 browser_->OpenURLFromTab(NULL, url, referrer, NEW_FOREGROUND_TAB, transition);
54 }
55
56 private:
57 Profile* profile_;
58 Browser* browser_;
59
60 DISALLOW_COPY_AND_ASSIGN(NewBrowserPageNavigator);
61};
62
63void CloneDragDataImpl(BookmarkModel* model,
64 const BookmarkDragData::Element& element,
65 BookmarkNode* parent,
66 int index_to_add_at) {
67 if (element.is_url) {
68 model->AddURL(parent, index_to_add_at, element.title, element.url);
69 } else {
70 BookmarkNode* new_folder = model->AddGroup(parent, index_to_add_at,
71 element.title);
72 for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
73 CloneDragDataImpl(model, element.children[i], new_folder, i);
74 }
75}
76
77// Returns the number of descendants of node that are of type url.
78int DescendantURLCount(BookmarkNode* node) {
79 int result = 0;
80 for (int i = 0; i < node->GetChildCount(); ++i) {
81 BookmarkNode* child = node->GetChild(i);
82 if (child->is_url())
83 result++;
84 else
85 result += DescendantURLCount(child);
86 }
87 return result;
88}
89
90// Implementation of OpenAll. Opens all nodes of type URL and recurses for
91// groups. |navigator| is the PageNavigator used to open URLs. After the first
92// url is opened |opened_url| is set to true and |navigator| is set to the
93// PageNavigator of the last active tab. This is done to handle a window
94// disposition of new window, in which case we want subsequent tabs to open in
95// that window.
96void OpenAllImpl(BookmarkNode* node,
97 WindowOpenDisposition initial_disposition,
98 PageNavigator** navigator,
99 bool* opened_url) {
100 if (node->is_url()) {
101 WindowOpenDisposition disposition;
102 if (*opened_url)
103 disposition = NEW_BACKGROUND_TAB;
104 else
105 disposition = initial_disposition;
106 (*navigator)->OpenURL(node->GetURL(), GURL(), disposition,
107 PageTransition::AUTO_BOOKMARK);
108 if (!*opened_url) {
109 *opened_url = true;
110 // We opened the first URL which may have opened a new window or clobbered
111 // the current page, reset the navigator just to be sure.
112 Browser* new_browser = BrowserList::GetLastActive();
113 if (new_browser) {
114 TabContents* current_tab = new_browser->GetSelectedTabContents();
115 DCHECK(new_browser && current_tab);
116 if (new_browser && current_tab)
117 *navigator = current_tab;
118 } // else, new_browser == NULL, which happens during testing.
119 }
120 } else {
121 // Group, recurse through children.
122 for (int i = 0; i < node->GetChildCount(); ++i) {
123 OpenAllImpl(node->GetChild(i), initial_disposition, navigator,
124 opened_url);
125 }
126 }
127}
128
129bool ShouldOpenAll(HWND parent, const std::vector<BookmarkNode*>& nodes) {
130 int descendant_count = 0;
131 for (size_t i = 0; i < nodes.size(); ++i)
132 descendant_count += DescendantURLCount(nodes[i]);
[email protected]f785ad12008-11-19 23:01:28133 if (descendant_count < bookmark_utils::num_urls_before_prompting)
[email protected]7f856be2008-10-29 23:38:06134 return true;
135
136 std::wstring message =
137 l10n_util::GetStringF(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
138 IntToWString(descendant_count));
139 return MessageBox(parent, message.c_str(),
140 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
141 MB_YESNO | MB_ICONWARNING | MB_TOPMOST) == IDYES;
142}
143
[email protected]9333f182008-12-09 17:34:17144// Comparison function that compares based on date modified of the two nodes.
145bool MoreRecentlyModified(BookmarkNode* n1, BookmarkNode* n2) {
146 return n1->date_group_modified() > n2->date_group_modified();
147}
148
[email protected]7f856be2008-10-29 23:38:06149} // namespace
150
151namespace bookmark_utils {
152
[email protected]f785ad12008-11-19 23:01:28153int num_urls_before_prompting = 15;
154
[email protected]f28cbb72008-11-04 19:29:08155int PreferredDropOperation(int source_operations, int operations) {
156 int common_ops = (source_operations & operations);
[email protected]7f856be2008-10-29 23:38:06157 if (!common_ops)
158 return 0;
159 if (DragDropTypes::DRAG_COPY & common_ops)
160 return DragDropTypes::DRAG_COPY;
161 if (DragDropTypes::DRAG_LINK & common_ops)
162 return DragDropTypes::DRAG_LINK;
163 if (DragDropTypes::DRAG_MOVE & common_ops)
164 return DragDropTypes::DRAG_MOVE;
165 return DragDropTypes::DRAG_NONE;
166}
167
168bool IsValidDropLocation(Profile* profile,
169 const BookmarkDragData& data,
170 BookmarkNode* drop_parent,
171 int index) {
172 if (!drop_parent->is_folder()) {
173 NOTREACHED();
174 return false;
175 }
176
177 if (!data.is_valid())
178 return false;
179
180 if (data.IsFromProfile(profile)) {
181 std::vector<BookmarkNode*> nodes = data.GetNodes(profile);
182 for (size_t i = 0; i < nodes.size(); ++i) {
183 // Don't allow the drop if the user is attempting to drop on one of the
184 // nodes being dragged.
185 BookmarkNode* node = nodes[i];
186 int node_index = (drop_parent == node->GetParent()) ?
187 drop_parent->IndexOfChild(nodes[i]) : -1;
188 if (node_index != -1 && (index == node_index || index == node_index + 1))
189 return false;
190
191 // drop_parent can't accept a child that is an ancestor.
192 if (drop_parent->HasAncestor(node))
193 return false;
194 }
195 return true;
196 }
197 // From the same profile, always accept.
198 return true;
199}
200
201void CloneDragData(BookmarkModel* model,
202 const std::vector<BookmarkDragData::Element>& elements,
203 BookmarkNode* parent,
204 int index_to_add_at) {
205 if (!parent->is_folder() || !model) {
206 NOTREACHED();
207 return;
208 }
209 for (size_t i = 0; i < elements.size(); ++i)
210 CloneDragDataImpl(model, elements[i], parent, index_to_add_at + i);
211}
212
213void OpenAll(HWND parent,
214 Profile* profile,
215 PageNavigator* navigator,
216 const std::vector<BookmarkNode*>& nodes,
217 WindowOpenDisposition initial_disposition) {
218 if (!ShouldOpenAll(parent, nodes))
219 return;
220
221 NewBrowserPageNavigator navigator_impl(profile);
222 if (!navigator) {
223 Browser* browser =
[email protected]299dabd2008-11-19 02:27:16224 BrowserList::FindBrowserWithType(profile, Browser::TYPE_NORMAL);
[email protected]b6f2b9132008-11-17 16:27:51225 if (!browser || !browser->GetSelectedTabContents()) {
[email protected]7f856be2008-10-29 23:38:06226 navigator = &navigator_impl;
[email protected]b6f2b9132008-11-17 16:27:51227 } else {
228 browser->window()->Activate();
[email protected]7f856be2008-10-29 23:38:06229 navigator = browser->GetSelectedTabContents();
[email protected]b6f2b9132008-11-17 16:27:51230 }
[email protected]7f856be2008-10-29 23:38:06231 }
232
233 bool opened_url = false;
234 for (size_t i = 0; i < nodes.size(); ++i)
235 OpenAllImpl(nodes[i], initial_disposition, &navigator, &opened_url);
236}
237
238void OpenAll(HWND parent,
239 Profile* profile,
240 PageNavigator* navigator,
241 BookmarkNode* node,
242 WindowOpenDisposition initial_disposition) {
243 std::vector<BookmarkNode*> nodes;
244 nodes.push_back(node);
245 OpenAll(parent, profile, navigator, nodes, initial_disposition);
246}
247
[email protected]fafc8a422008-11-07 17:53:09248void CopyToClipboard(BookmarkModel* model,
249 const std::vector<BookmarkNode*>& nodes,
250 bool remove_nodes) {
251 if (nodes.empty())
252 return;
253
254 OSExchangeData* data = new OSExchangeData();
255 BookmarkDragData(nodes).Write(NULL, data);
256 OleSetClipboard(data);
257 // OLE takes ownership of OSExchangeData.
258
259 if (remove_nodes) {
260 for (size_t i = 0; i < nodes.size(); ++i) {
261 model->Remove(nodes[i]->GetParent(),
262 nodes[i]->GetParent()->IndexOfChild(nodes[i]));
263 }
264 }
265}
266
267void PasteFromClipboard(BookmarkModel* model,
268 BookmarkNode* parent,
269 int index) {
270 if (!parent)
271 return;
272
273 IDataObject* data;
274 if (OleGetClipboard(&data) != S_OK)
275 return;
276
277 OSExchangeData data_wrapper(data);
278 BookmarkDragData bookmark_data;
279 if (!bookmark_data.Read(data_wrapper))
280 return;
281
282 if (index == -1)
283 index = parent->GetChildCount();
284 bookmark_utils::CloneDragData(model, bookmark_data.elements, parent, index);
285}
286
287bool CanPasteFromClipboard(BookmarkNode* node) {
288 if (!node)
289 return false;
290
291 IDataObject* data;
292 if (OleGetClipboard(&data) != S_OK)
293 return false;
294
295 OSExchangeData data_wrapper(data);
296 BookmarkDragData bookmark_data;
297 return bookmark_data.Read(data_wrapper);
298}
299
[email protected]9333f182008-12-09 17:34:17300std::vector<BookmarkNode*> GetMostRecentlyModifiedGroups(
301 BookmarkModel* model,
302 size_t max_count) {
303 std::vector<BookmarkNode*> nodes;
304 views::TreeNodeIterator<BookmarkNode> iterator(model->root_node());
305 while (iterator.has_next()) {
306 BookmarkNode* parent = iterator.Next();
307 if (parent->is_folder() && parent->date_group_modified() > base::Time()) {
308 if (max_count == 0) {
309 nodes.push_back(parent);
310 } else {
311 std::vector<BookmarkNode*>::iterator i =
312 std::upper_bound(nodes.begin(), nodes.end(), parent,
313 &MoreRecentlyModified);
314 if (nodes.size() < max_count || i != nodes.end()) {
315 nodes.insert(i, parent);
316 while (nodes.size() > max_count)
317 nodes.pop_back();
318 }
319 }
320 } // else case, the root node, which we don't care about or imported nodes
321 // (which have a time of 0).
322 }
323
324 if (nodes.size() < max_count) {
325 // Add the bookmark bar and other nodes if there is space.
326 if (find(nodes.begin(), nodes.end(), model->GetBookmarkBarNode()) ==
327 nodes.end()) {
328 nodes.push_back(model->GetBookmarkBarNode());
329 }
330
331 if (nodes.size() < max_count &&
332 find(nodes.begin(), nodes.end(), model->other_node()) == nodes.end()) {
333 nodes.push_back(model->other_node());
334 }
335 }
336 return nodes;
337}
338
339void GetMostRecentlyAddedEntries(BookmarkModel* model,
340 size_t count,
341 std::vector<BookmarkNode*>* nodes) {
342 views::TreeNodeIterator<BookmarkNode> iterator(model->root_node());
343 while (iterator.has_next()) {
344 BookmarkNode* node = iterator.Next();
345 if (node->is_url()) {
346 std::vector<BookmarkNode*>::iterator insert_position =
347 std::upper_bound(nodes->begin(), nodes->end(), node,
348 &MoreRecentlyAdded);
349 if (nodes->size() < count || insert_position != nodes->end()) {
350 nodes->insert(insert_position, node);
351 while (nodes->size() > count)
352 nodes->pop_back();
353 }
354 }
355 }
356}
357
358void GetBookmarksMatchingText(BookmarkModel* model,
359 const std::wstring& text,
360 size_t max_count,
361 std::vector<TitleMatch>* matches) {
362 QueryParser parser;
363 ScopedVector<QueryNode> query_nodes;
364 parser.ParseQuery(text, &query_nodes.get());
365 if (query_nodes.empty())
366 return;
367
368 views::TreeNodeIterator<BookmarkNode> iterator(model->root_node());
369 Snippet::MatchPositions match_position;
370 while (iterator.has_next()) {
371 BookmarkNode* node = iterator.Next();
372 if (node->GetURL().spec() == "http://www.google.com/") {
373 DLOG(INFO) << "BLAH";
374 }
375 if (node->is_url() &&
376 parser.DoesQueryMatch(node->GetTitle(), query_nodes.get(),
377 &match_position)) {
378 matches->push_back(TitleMatch());
379 matches->back().node = node;
380 matches->back().match_positions.swap(match_position);
381 if (matches->size() == max_count)
382 break;
383 }
384 }
385}
386
387bool DoesBookmarkMatchText(const std::wstring& text, BookmarkNode* node) {
388 if (!node->is_url())
389 return false;
390
391 QueryParser parser;
392 ScopedVector<QueryNode> query_nodes;
393 parser.ParseQuery(text, &query_nodes.get());
394 if (query_nodes.empty())
395 return false;
396
397 Snippet::MatchPositions match_position;
398 return parser.DoesQueryMatch(node->GetTitle(), query_nodes.get(),
399 &match_position);
400}
401
402bool MoreRecentlyAdded(BookmarkNode* n1, BookmarkNode* n2) {
403 return n1->date_added() > n2->date_added();
404}
405
[email protected]7f856be2008-10-29 23:38:06406} // namespace bookmark_utils