Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: chrome/browser/ui/gtk/tabs/dragged_tab_controller_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/ui/gtk/tabs/dragged_tab_controller_gtk.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/i18n/rtl.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/platform_util.h"
14 #include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
17 #include "chrome/browser/ui/gtk/gtk_util.h"
18 #include "chrome/browser/ui/gtk/tabs/dragged_view_gtk.h"
19 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
20 #include "chrome/browser/ui/gtk/tabs/window_finder.h"
21 #include "chrome/browser/ui/media_utils.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_contents_view.h"
27 #include "ui/base/gtk/gtk_screen_util.h"
28 #include "ui/gfx/screen.h"
29
30 using content::OpenURLParams;
31 using content::WebContents;
32
33 namespace {
34
35 // Delay, in ms, during dragging before we bring a window to front.
36 const int kBringToFrontDelay = 750;
37
38 // Used to determine how far a tab must obscure another tab in order to swap
39 // their indexes.
40 const int kHorizontalMoveThreshold = 16; // pixels
41
42 // How far a drag must pull a tab out of the tabstrip in order to detach it.
43 const int kVerticalDetachMagnetism = 15; // pixels
44
45 // Returns the bounds that will be used for insertion index calculation.
46 // |bounds| is the actual tab bounds, but subtracting the overlapping areas from
47 // both sides makes the calculations much simpler.
48 gfx::Rect GetEffectiveBounds(const gfx::Rect& bounds) {
49 gfx::Rect effective_bounds(bounds);
50 effective_bounds.set_width(effective_bounds.width() - 2 * 16);
51 effective_bounds.set_x(effective_bounds.x() + 16);
52 return effective_bounds;
53 }
54
55 } // namespace
56
57 DraggedTabControllerGtk::DraggedTabControllerGtk(
58 TabStripGtk* source_tabstrip,
59 TabGtk* source_tab,
60 const std::vector<TabGtk*>& tabs)
61 : source_tabstrip_(source_tabstrip),
62 attached_tabstrip_(source_tabstrip),
63 in_destructor_(false),
64 last_move_screen_x_(0),
65 initial_move_(true) {
66 DCHECK(!tabs.empty());
67 DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end());
68
69 std::vector<DraggedTabData> drag_data;
70 for (size_t i = 0; i < tabs.size(); i++)
71 drag_data.push_back(InitDraggedTabData(tabs[i]));
72
73 int source_tab_index =
74 std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
75 drag_data_.reset(new DragData(drag_data, source_tab_index));
76 }
77
78 DraggedTabControllerGtk::~DraggedTabControllerGtk() {
79 in_destructor_ = true;
80 // Need to delete the dragged tab here manually _before_ we reset the dragged
81 // contents to NULL, otherwise if the view is animating to its destination
82 // bounds, it won't be able to clean up properly since its cleanup routine
83 // uses GetIndexForDraggedContents, which will be invalid.
84 CleanUpDraggedTabs();
85 dragged_view_.reset();
86 drag_data_.reset();
87 }
88
89 void DraggedTabControllerGtk::CaptureDragInfo(const gfx::Point& mouse_offset) {
90 start_screen_point_ = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
91 mouse_offset_ = mouse_offset;
92 }
93
94 void DraggedTabControllerGtk::Drag() {
95 if (!drag_data_->GetSourceTabData()->tab_ ||
96 !drag_data_->GetSourceWebContents()) {
97 return;
98 }
99
100 bring_to_front_timer_.Stop();
101
102 EnsureDraggedView();
103
104 // Before we get to dragging anywhere, ensure that we consider ourselves
105 // attached to the source tabstrip.
106 if (drag_data_->GetSourceTabData()->tab_->IsVisible()) {
107 Attach(source_tabstrip_, gfx::Point());
108 }
109
110 if (!drag_data_->GetSourceTabData()->tab_->IsVisible()) {
111 // TODO(jhawkins): Save focus.
112 ContinueDragging();
113 }
114 }
115
116 bool DraggedTabControllerGtk::EndDrag(bool canceled) {
117 return EndDragImpl(canceled ? CANCELED : NORMAL);
118 }
119
120 TabGtk* DraggedTabControllerGtk::GetDraggedTabForContents(
121 WebContents* contents) {
122 if (attached_tabstrip_ == source_tabstrip_) {
123 for (size_t i = 0; i < drag_data_->size(); i++) {
124 if (contents == drag_data_->get(i)->contents_)
125 return drag_data_->get(i)->tab_;
126 }
127 }
128 return NULL;
129 }
130
131 bool DraggedTabControllerGtk::IsDraggingTab(const TabGtk* tab) {
132 for (size_t i = 0; i < drag_data_->size(); i++) {
133 if (tab == drag_data_->get(i)->tab_)
134 return true;
135 }
136 return false;
137 }
138
139 bool DraggedTabControllerGtk::IsDraggingWebContents(
140 const WebContents* web_contents) {
141 for (size_t i = 0; i < drag_data_->size(); i++) {
142 if (web_contents == drag_data_->get(i)->contents_)
143 return true;
144 }
145 return false;
146 }
147
148 bool DraggedTabControllerGtk::IsTabDetached(const TabGtk* tab) {
149 return IsDraggingTab(tab) && attached_tabstrip_ == NULL;
150 }
151
152 DraggedTabData DraggedTabControllerGtk::InitDraggedTabData(TabGtk* tab) {
153 int source_model_index = source_tabstrip_->GetIndexOfTab(tab);
154 WebContents* contents =
155 source_tabstrip_->model()->GetWebContentsAt(source_model_index);
156 bool pinned = source_tabstrip_->IsTabPinned(tab);
157 bool mini = source_tabstrip_->model()->IsMiniTab(source_model_index);
158 // We need to be the delegate so we receive messages about stuff,
159 // otherwise our dragged_contents() may be replaced and subsequently
160 // collected/destroyed while the drag is in process, leading to
161 // nasty crashes.
162 content::WebContentsDelegate* original_delegate = contents->GetDelegate();
163 contents->SetDelegate(this);
164
165 DraggedTabData dragged_tab_data(tab, contents, original_delegate,
166 source_model_index, pinned, mini);
167 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
168 content::Source<WebContents>(contents));
169 return dragged_tab_data;
170 }
171
172 ////////////////////////////////////////////////////////////////////////////////
173 // DraggedTabControllerGtk, content::WebContentsDelegate implementation:
174
175 WebContents* DraggedTabControllerGtk::OpenURLFromTab(
176 WebContents* source,
177 const OpenURLParams& params) {
178 if (drag_data_->GetSourceTabData()->original_delegate_) {
179 OpenURLParams forward_params = params;
180 if (params.disposition == CURRENT_TAB)
181 forward_params.disposition = NEW_WINDOW;
182
183 return drag_data_->GetSourceTabData()->original_delegate_->
184 OpenURLFromTab(source, forward_params);
185 }
186 return NULL;
187 }
188
189 void DraggedTabControllerGtk::NavigationStateChanged(const WebContents* source,
190 unsigned changed_flags) {
191 if (dragged_view_.get())
192 dragged_view_->Update();
193 }
194
195 void DraggedTabControllerGtk::AddNewContents(WebContents* source,
196 WebContents* new_contents,
197 WindowOpenDisposition disposition,
198 const gfx::Rect& initial_pos,
199 bool user_gesture,
200 bool* was_blocked) {
201 DCHECK(disposition != CURRENT_TAB);
202
203 // Theoretically could be called while dragging if the page tries to
204 // spawn a window. Route this message back to the browser in most cases.
205 if (drag_data_->GetSourceTabData()->original_delegate_) {
206 drag_data_->GetSourceTabData()->original_delegate_->AddNewContents(
207 source, new_contents, disposition, initial_pos, user_gesture,
208 was_blocked);
209 }
210 }
211
212 void DraggedTabControllerGtk::LoadingStateChanged(WebContents* source,
213 bool to_different_document) {
214 // TODO(jhawkins): It would be nice to respond to this message by changing the
215 // screen shot in the dragged tab.
216 if (dragged_view_.get())
217 dragged_view_->Update();
218 }
219
220 content::JavaScriptDialogManager*
221 DraggedTabControllerGtk::GetJavaScriptDialogManager() {
222 return GetJavaScriptDialogManagerInstance();
223 }
224
225 ////////////////////////////////////////////////////////////////////////////////
226 // DraggedTabControllerGtk, content::NotificationObserver implementation:
227
228 void DraggedTabControllerGtk::Observe(
229 int type,
230 const content::NotificationSource& source,
231 const content::NotificationDetails& details) {
232 DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type);
233 WebContents* destroyed_web_contents =
234 content::Source<WebContents>(source).ptr();
235 for (size_t i = 0; i < drag_data_->size(); ++i) {
236 if (drag_data_->get(i)->contents_ == destroyed_web_contents) {
237 // One of the tabs we're dragging has been destroyed. Cancel the drag.
238 if (destroyed_web_contents->GetDelegate() == this)
239 destroyed_web_contents->SetDelegate(NULL);
240 drag_data_->get(i)->contents_ = NULL;
241 drag_data_->get(i)->original_delegate_ = NULL;
242 EndDragImpl(TAB_DESTROYED);
243 return;
244 }
245 }
246 // If we get here it means we got notification for a tab we don't know about.
247 NOTREACHED();
248 }
249
250 void DraggedTabControllerGtk::RequestMediaAccessPermission(
251 content::WebContents* web_contents,
252 const content::MediaStreamRequest& request,
253 const content::MediaResponseCallback& callback) {
254 ::RequestMediaAccessPermission(
255 web_contents,
256 Profile::FromBrowserContext(web_contents->GetBrowserContext()),
257 request,
258 callback);
259 }
260
261 gfx::Point DraggedTabControllerGtk::GetWindowCreatePoint() const {
262 gfx::Point creation_point =
263 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
264 gfx::Point distance_from_origin =
265 dragged_view_->GetDistanceFromTabStripOriginToMousePointer();
266 // TODO(dpapad): offset also because of tabstrip origin being different than
267 // window origin.
268 creation_point.Offset(-distance_from_origin.x(), -distance_from_origin.y());
269 return creation_point;
270 }
271
272 void DraggedTabControllerGtk::ContinueDragging() {
273 // TODO(jhawkins): We don't handle the situation where the last tab is dragged
274 // out of a window, so we'll just go with the way Windows handles dragging for
275 // now.
276 gfx::Point screen_point =
277 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
278
279 // Determine whether or not we have dragged over a compatible TabStrip in
280 // another browser window. If we have, we should attach to it and start
281 // dragging within it.
282 TabStripGtk* target_tabstrip = GetTabStripForPoint(screen_point);
283 if (target_tabstrip != attached_tabstrip_) {
284 // Make sure we're fully detached from whatever TabStrip we're attached to
285 // (if any).
286 if (attached_tabstrip_)
287 Detach();
288
289 if (target_tabstrip)
290 Attach(target_tabstrip, screen_point);
291 }
292
293 if (!target_tabstrip) {
294 bring_to_front_timer_.Start(FROM_HERE,
295 base::TimeDelta::FromMilliseconds(kBringToFrontDelay), this,
296 &DraggedTabControllerGtk::BringWindowUnderMouseToFront);
297 }
298
299 if (attached_tabstrip_)
300 MoveAttached(screen_point);
301 else
302 MoveDetached(screen_point);
303 }
304
305 void DraggedTabControllerGtk::RestoreSelection(TabStripModel* model) {
306 for (size_t i = 0; i < drag_data_->size(); ++i) {
307 WebContents* contents = drag_data_->get(i)->contents_;
308 // If a tab is destroyed while dragging contents might be null. See
309 // http://crbug.com/115409.
310 if (contents) {
311 int index = model->GetIndexOfWebContents(contents);
312 CHECK(index != TabStripModel::kNoTab);
313 model->AddTabAtToSelection(index);
314 }
315 }
316 }
317
318 void DraggedTabControllerGtk::MoveAttached(const gfx::Point& screen_point) {
319 DCHECK(attached_tabstrip_);
320
321 gfx::Point dragged_view_point = GetDraggedViewPoint(screen_point);
322 TabStripModel* attached_model = attached_tabstrip_->model();
323
324 std::vector<TabGtk*> tabs(drag_data_->GetDraggedTabs());
325
326 // Determine the horizontal move threshold. This is dependent on the width
327 // of tabs. The smaller the tabs compared to the standard size, the smaller
328 // the threshold.
329 double unselected, selected;
330 attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected);
331 double ratio = unselected / TabGtk::GetStandardSize().width();
332 int threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
333
334 // Update the model, moving the WebContents from one index to another.
335 // Do this only if we have moved a minimum distance since the last reorder (to
336 // prevent jitter) or if this is the first move and the tabs are not
337 // consecutive.
338 if (abs(screen_point.x() - last_move_screen_x_) > threshold ||
339 (initial_move_ && !AreTabsConsecutive())) {
340 if (initial_move_ && !AreTabsConsecutive()) {
341 // Making dragged tabs adjacent, this is done only once, if necessary.
342 attached_tabstrip_->model()->MoveSelectedTabsTo(
343 drag_data_->GetSourceTabData()->source_model_index_ -
344 drag_data_->source_tab_index());
345 }
346 gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
347 int to_index = GetInsertionIndexForDraggedBounds(
348 GetEffectiveBounds(bounds));
349 to_index = NormalizeIndexToAttachedTabStrip(to_index);
350 last_move_screen_x_ = screen_point.x();
351 attached_model->MoveSelectedTabsTo(to_index);
352 }
353
354 dragged_view_->MoveAttachedTo(dragged_view_point);
355 initial_move_ = false;
356 }
357
358 void DraggedTabControllerGtk::MoveDetached(const gfx::Point& screen_point) {
359 DCHECK(!attached_tabstrip_);
360 // Just moving the dragged view. There are no changes to the model if we're
361 // detached.
362 dragged_view_->MoveDetachedTo(screen_point);
363 }
364
365 TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint(
366 const gfx::Point& screen_point) {
367 gfx::NativeWindow local_window = GetLocalProcessWindow(screen_point);
368 if (!local_window)
369 return NULL;
370
371 BrowserWindowGtk* browser =
372 BrowserWindowGtk::GetBrowserWindowForNativeWindow(local_window);
373 if (!browser)
374 return NULL;
375
376 TabStripGtk* other_tabstrip = browser->tabstrip();
377 if (!other_tabstrip->IsCompatibleWith(source_tabstrip_))
378 return NULL;
379
380 return GetTabStripIfItContains(other_tabstrip, screen_point);
381 }
382
383 TabStripGtk* DraggedTabControllerGtk::GetTabStripIfItContains(
384 TabStripGtk* tabstrip, const gfx::Point& screen_point) const {
385 // Make sure the specified screen point is actually within the bounds of the
386 // specified tabstrip...
387 gfx::Rect tabstrip_bounds =
388 ui::GetWidgetScreenBounds(tabstrip->tabstrip_.get());
389 if (screen_point.x() < tabstrip_bounds.right() &&
390 screen_point.x() >= tabstrip_bounds.x()) {
391 // TODO(beng): make this be relative to the start position of the mouse for
392 // the source TabStrip.
393 int upper_threshold = tabstrip_bounds.bottom() + kVerticalDetachMagnetism;
394 int lower_threshold = tabstrip_bounds.y() - kVerticalDetachMagnetism;
395 if (screen_point.y() >= lower_threshold &&
396 screen_point.y() <= upper_threshold) {
397 return tabstrip;
398 }
399 }
400
401 return NULL;
402 }
403
404 void DraggedTabControllerGtk::Attach(TabStripGtk* attached_tabstrip,
405 const gfx::Point& screen_point) {
406 attached_tabstrip_ = attached_tabstrip;
407 attached_tabstrip_->GenerateIdealBounds();
408
409 std::vector<TabGtk*> attached_dragged_tabs =
410 GetTabsMatchingDraggedContents(attached_tabstrip_);
411
412 // Update the tab first, so we can ask it for its bounds and determine
413 // where to insert the hidden tab.
414
415 // If this is the first time Attach is called for this drag, we're attaching
416 // to the source tabstrip, and we should assume the tab count already
417 // includes this tab since we haven't been detached yet. If we don't do this,
418 // the dragged representation will be a different size to others in the
419 // tabstrip.
420 int tab_count = attached_tabstrip_->GetTabCount();
421 int mini_tab_count = attached_tabstrip_->GetMiniTabCount();
422 if (attached_dragged_tabs.size() == 0) {
423 tab_count += drag_data_->size();
424 mini_tab_count += drag_data_->mini_tab_count();
425 }
426
427 double unselected_width = 0, selected_width = 0;
428 attached_tabstrip_->GetDesiredTabWidths(tab_count, mini_tab_count,
429 &unselected_width, &selected_width);
430
431 GtkWidget* parent_window = gtk_widget_get_parent(
432 gtk_widget_get_parent(attached_tabstrip_->tabstrip_.get()));
433 gfx::Rect window_bounds = ui::GetWidgetScreenBounds(parent_window);
434 dragged_view_->Attach(static_cast<int>(floor(selected_width + 0.5)),
435 TabGtk::GetMiniWidth(), window_bounds.width());
436
437 if (attached_dragged_tabs.size() == 0) {
438 // There is no tab in |attached_tabstrip| that corresponds to the dragged
439 // WebContents. We must now create one.
440
441 // Remove ourselves as the delegate now that the dragged WebContents
442 // is being inserted back into a Browser.
443 for (size_t i = 0; i < drag_data_->size(); ++i) {
444 drag_data_->get(i)->contents_->SetDelegate(NULL);
445 drag_data_->get(i)->original_delegate_ = NULL;
446 }
447
448 // We need to ask the tabstrip we're attached to ensure that the ideal
449 // bounds for all its tabs are correctly generated, because the calculation
450 // in GetInsertionIndexForDraggedBounds needs them to be to figure out the
451 // appropriate insertion index.
452 attached_tabstrip_->GenerateIdealBounds();
453
454 // Inserting counts as a move. We don't want the tabs to jitter when the
455 // user moves the tab immediately after attaching it.
456 last_move_screen_x_ = screen_point.x();
457
458 // Figure out where to insert the tab based on the bounds of the dragged
459 // representation and the ideal bounds of the other tabs already in the
460 // strip. ("ideal bounds" are stable even if the tabs' actual bounds are
461 // changing due to animation).
462 gfx::Rect bounds =
463 GetDraggedViewTabStripBounds(GetDraggedViewPoint(screen_point));
464 int index = GetInsertionIndexForDraggedBounds(GetEffectiveBounds(bounds));
465 for (size_t i = 0; i < drag_data_->size(); ++i) {
466 attached_tabstrip_->model()->InsertWebContentsAt(
467 index + i, drag_data_->get(i)->contents_,
468 drag_data_->GetAddTypesForDraggedTabAt(i));
469 }
470 RestoreSelection(attached_tabstrip_->model());
471 attached_dragged_tabs = GetTabsMatchingDraggedContents(attached_tabstrip_);
472 }
473 // We should now have a tab.
474 DCHECK(attached_dragged_tabs.size() == drag_data_->size());
475 SetDraggedTabsVisible(false, false);
476 // TODO(jhawkins): Move the corresponding window to the front.
477 }
478
479 void DraggedTabControllerGtk::Detach() {
480 // Update the Model.
481 TabStripModel* attached_model = attached_tabstrip_->model();
482 for (size_t i = 0; i < drag_data_->size(); ++i) {
483 int index =
484 attached_model->GetIndexOfWebContents(drag_data_->get(i)->contents_);
485 if (index >= 0 && index < attached_model->count()) {
486 // Sometimes, DetachWebContentsAt has consequences that result in
487 // attached_tabstrip_ being set to NULL, so we need to save it first.
488 attached_model->DetachWebContentsAt(index);
489 }
490 }
491
492 // If we've removed the last tab from the tabstrip, hide the frame now.
493 if (attached_model->empty())
494 HideWindow();
495
496 // Update the dragged tab. This NULL check is necessary apparently in some
497 // conditions during automation where the view_ is destroyed inside a
498 // function call preceding this point but after it is created.
499 if (dragged_view_.get()) {
500 dragged_view_->Detach();
501 }
502
503 // Detaching resets the delegate, but we still want to be the delegate.
504 for (size_t i = 0; i < drag_data_->size(); ++i)
505 drag_data_->get(i)->contents_->SetDelegate(this);
506
507 attached_tabstrip_ = NULL;
508 }
509
510 gfx::Point DraggedTabControllerGtk::ConvertScreenPointToTabStripPoint(
511 TabStripGtk* tabstrip, const gfx::Point& screen_point) {
512 return screen_point - ui::GetWidgetScreenOffset(tabstrip->tabstrip_.get());
513 }
514
515 gfx::Rect DraggedTabControllerGtk::GetDraggedViewTabStripBounds(
516 const gfx::Point& screen_point) {
517 gfx::Point client_point =
518 ConvertScreenPointToTabStripPoint(attached_tabstrip_, screen_point);
519 gfx::Size tab_size = dragged_view_->attached_tab_size();
520 return gfx::Rect(client_point.x(), client_point.y(),
521 dragged_view_->GetTotalWidthInTabStrip(), tab_size.height());
522 }
523
524 int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds(
525 const gfx::Rect& dragged_bounds) {
526 int dragged_bounds_start = gtk_util::MirroredLeftPointForRect(
527 attached_tabstrip_->widget(),
528 dragged_bounds);
529 int dragged_bounds_end = gtk_util::MirroredRightPointForRect(
530 attached_tabstrip_->widget(),
531 dragged_bounds);
532 int right_tab_x = 0;
533 int index = -1;
534
535 for (int i = 0; i < attached_tabstrip_->GetTabCount(); i++) {
536 // Divides each tab into two halves to see if the dragged tab has crossed
537 // the halfway boundary necessary to move past the next tab.
538 gfx::Rect ideal_bounds = attached_tabstrip_->GetIdealBounds(i);
539 gfx::Rect left_half, right_half;
540 ideal_bounds.SplitVertically(&left_half, &right_half);
541 right_tab_x = right_half.x();
542
543 if (dragged_bounds_start >= right_half.x() &&
544 dragged_bounds_start < right_half.right()) {
545 index = i + 1;
546 break;
547 } else if (dragged_bounds_start >= left_half.x() &&
548 dragged_bounds_start < left_half.right()) {
549 index = i;
550 break;
551 }
552 }
553
554 if (index == -1) {
555 if (dragged_bounds_end > right_tab_x)
556 index = attached_tabstrip_->GetTabCount();
557 else
558 index = 0;
559 }
560
561 TabGtk* tab = GetTabMatchingDraggedContents(
562 attached_tabstrip_, drag_data_->GetSourceWebContents());
563 if (tab == NULL) {
564 // If dragged tabs are not attached yet, we don't need to constrain the
565 // index.
566 return index;
567 }
568
569 int max_index =
570 attached_tabstrip_-> GetTabCount() - static_cast<int>(drag_data_->size());
571 return std::max(0, std::min(max_index, index));
572 }
573
574 gfx::Point DraggedTabControllerGtk::GetDraggedViewPoint(
575 const gfx::Point& screen_point) {
576 int x = screen_point.x() -
577 dragged_view_->GetWidthInTabStripUpToMousePointer();
578 int y = screen_point.y() - mouse_offset_.y();
579
580 // If we're not attached, we just use x and y from above.
581 if (attached_tabstrip_) {
582 gfx::Rect tabstrip_bounds =
583 ui::GetWidgetScreenBounds(attached_tabstrip_->tabstrip_.get());
584 // Snap the dragged tab to the tabstrip if we are attached, detaching
585 // only when the mouse position (screen_point) exceeds the screen bounds
586 // of the tabstrip.
587 if (x < tabstrip_bounds.x() && screen_point.x() >= tabstrip_bounds.x())
588 x = tabstrip_bounds.x();
589
590 gfx::Size tab_size = dragged_view_->attached_tab_size();
591 int vertical_drag_magnetism = tab_size.height() * 2;
592 int vertical_detach_point = tabstrip_bounds.y() - vertical_drag_magnetism;
593 if (y < tabstrip_bounds.y() && screen_point.y() >= vertical_detach_point)
594 y = tabstrip_bounds.y();
595
596 // Make sure the tab can't be dragged off the right side of the tabstrip
597 // unless the mouse pointer passes outside the bounds of the strip by
598 // clamping the position of the dragged window to the tabstrip width less
599 // the width of one tab until the mouse pointer (screen_point) exceeds the
600 // screen bounds of the tabstrip.
601 int max_x =
602 tabstrip_bounds.right() - dragged_view_->GetTotalWidthInTabStrip();
603 int max_y = tabstrip_bounds.bottom() - tab_size.height();
604 if (x > max_x && screen_point.x() <= tabstrip_bounds.right())
605 x = max_x;
606 if (y > max_y && screen_point.y() <=
607 (tabstrip_bounds.bottom() + vertical_drag_magnetism)) {
608 y = max_y;
609 }
610 }
611 return gfx::Point(x, y);
612 }
613
614 int DraggedTabControllerGtk::NormalizeIndexToAttachedTabStrip(int index) const {
615 if (index >= attached_tabstrip_->model_->count())
616 return attached_tabstrip_->model_->count() - 1;
617 if (index == TabStripModel::kNoTab)
618 return 0;
619 return index;
620 }
621
622 TabGtk* DraggedTabControllerGtk::GetTabMatchingDraggedContents(
623 TabStripGtk* tabstrip, WebContents* web_contents) {
624 int index = tabstrip->model()->GetIndexOfWebContents(web_contents);
625 return index == TabStripModel::kNoTab ? NULL : tabstrip->GetTabAt(index);
626 }
627
628 std::vector<TabGtk*> DraggedTabControllerGtk::GetTabsMatchingDraggedContents(
629 TabStripGtk* tabstrip) {
630 std::vector<TabGtk*> dragged_tabs;
631 for (size_t i = 0; i < drag_data_->size(); ++i) {
632 if (!drag_data_->get(i)->contents_)
633 continue;
634 TabGtk* tab = GetTabMatchingDraggedContents(tabstrip,
635 drag_data_->get(i)->contents_);
636 if (tab)
637 dragged_tabs.push_back(tab);
638 }
639 return dragged_tabs;
640 }
641
642 void DraggedTabControllerGtk::SetDraggedTabsVisible(bool visible,
643 bool repaint) {
644 std::vector<TabGtk*> dragged_tabs(GetTabsMatchingDraggedContents(
645 attached_tabstrip_));
646 for (size_t i = 0; i < dragged_tabs.size(); ++i) {
647 dragged_tabs[i]->SetVisible(visible);
648 dragged_tabs[i]->set_dragging(!visible);
649 if (repaint)
650 dragged_tabs[i]->SchedulePaint();
651 }
652 }
653
654 bool DraggedTabControllerGtk::EndDragImpl(EndDragType type) {
655 bring_to_front_timer_.Stop();
656
657 // WARNING: this may be invoked multiple times. In particular, if deletion
658 // occurs after a delay (as it does when the tab is released in the original
659 // tab strip) and the navigation controller/tab contents is deleted before
660 // the animation finishes, this is invoked twice. The second time through
661 // type == TAB_DESTROYED.
662
663 bool destroy_now = true;
664 if (type != TAB_DESTROYED) {
665 // If we never received a drag-motion event, the drag will never have
666 // started in the sense that |dragged_view_| will be NULL. We don't need to
667 // revert or complete the drag in that case.
668 if (dragged_view_.get()) {
669 if (type == CANCELED) {
670 RevertDrag();
671 } else {
672 destroy_now = CompleteDrag();
673 }
674 }
675 } else if (drag_data_->size() > 0) {
676 RevertDrag();
677 }
678
679 ResetDelegates();
680
681 // If we're not destroyed now, we'll be destroyed asynchronously later.
682 if (destroy_now)
683 source_tabstrip_->DestroyDragController();
684
685 return destroy_now;
686 }
687
688 void DraggedTabControllerGtk::RevertDrag() {
689 // We save this here because code below will modify |attached_tabstrip_|.
690 bool restore_window = attached_tabstrip_ != source_tabstrip_;
691 if (attached_tabstrip_) {
692 if (attached_tabstrip_ != source_tabstrip_) {
693 // The tabs were inserted into another tabstrip. We need to put them back
694 // into the original one.
695 for (size_t i = 0; i < drag_data_->size(); ++i) {
696 if (!drag_data_->get(i)->contents_)
697 continue;
698 int index = attached_tabstrip_->model()->GetIndexOfWebContents(
699 drag_data_->get(i)->contents_);
700 attached_tabstrip_->model()->DetachWebContentsAt(index);
701 }
702 // TODO(beng): (Cleanup) seems like we should use Attach() for this
703 // somehow.
704 attached_tabstrip_ = source_tabstrip_;
705 for (size_t i = 0; i < drag_data_->size(); ++i) {
706 if (!drag_data_->get(i)->contents_)
707 continue;
708 attached_tabstrip_->model()->InsertWebContentsAt(
709 drag_data_->get(i)->source_model_index_,
710 drag_data_->get(i)->contents_,
711 drag_data_->GetAddTypesForDraggedTabAt(i));
712 }
713 } else {
714 // The tabs were moved within the tabstrip where the drag was initiated.
715 // Move them back to their starting locations.
716 for (size_t i = 0; i < drag_data_->size(); ++i) {
717 if (!drag_data_->get(i)->contents_)
718 continue;
719 int index = attached_tabstrip_->model()->GetIndexOfWebContents(
720 drag_data_->get(i)->contents_);
721 source_tabstrip_->model()->MoveWebContentsAt(
722 index, drag_data_->get(i)->source_model_index_, true);
723 }
724 }
725 } else {
726 // TODO(beng): (Cleanup) seems like we should use Attach() for this
727 // somehow.
728 attached_tabstrip_ = source_tabstrip_;
729 // The tab was detached from the tabstrip where the drag began, and has not
730 // been attached to any other tabstrip. We need to put it back into the
731 // source tabstrip.
732 for (size_t i = 0; i < drag_data_->size(); ++i) {
733 if (!drag_data_->get(i)->contents_)
734 continue;
735 source_tabstrip_->model()->InsertWebContentsAt(
736 drag_data_->get(i)->source_model_index_,
737 drag_data_->get(i)->contents_,
738 drag_data_->GetAddTypesForDraggedTabAt(i));
739 }
740 }
741 RestoreSelection(source_tabstrip_->model());
742
743 // If we're not attached to any tab strip, or attached to some other tab
744 // strip, we need to restore the bounds of the original tab strip's frame, in
745 // case it has been hidden.
746 if (restore_window)
747 ShowWindow();
748
749 SetDraggedTabsVisible(true, false);
750 }
751
752 bool DraggedTabControllerGtk::CompleteDrag() {
753 bool destroy_immediately = true;
754 if (attached_tabstrip_) {
755 // We don't need to do anything other than make the tabs visible again,
756 // since the dragged view is going away.
757 dragged_view_->AnimateToBounds(GetAnimateBounds(),
758 base::Bind(&DraggedTabControllerGtk::OnAnimateToBoundsComplete,
759 base::Unretained(this)));
760 destroy_immediately = false;
761 } else {
762 // Compel the model to construct a new window for the detached
763 // WebContentses.
764 BrowserWindowGtk* window = source_tabstrip_->window();
765 gfx::Rect window_bounds = window->GetRestoredBounds();
766 window_bounds.set_origin(GetWindowCreatePoint());
767
768 std::vector<TabStripModelDelegate::NewStripContents> contentses;
769 for (size_t i = 0; i < drag_data_->size(); ++i) {
770 TabStripModelDelegate::NewStripContents item;
771 item.web_contents = drag_data_->get(i)->contents_;
772 item.add_types = drag_data_->GetAddTypesForDraggedTabAt(i);
773 contentses.push_back(item);
774 };
775
776 Browser* new_browser =
777 source_tabstrip_->model()->delegate()->CreateNewStripWithContents(
778 contentses, window_bounds, window->IsMaximized());
779 RestoreSelection(new_browser->tab_strip_model());
780 new_browser->window()->Show();
781 CleanUpHiddenFrame();
782 }
783
784 return destroy_immediately;
785 }
786
787 void DraggedTabControllerGtk::ResetDelegates() {
788 for (size_t i = 0; i < drag_data_->size(); ++i) {
789 if (drag_data_->get(i)->contents_ &&
790 drag_data_->get(i)->contents_->GetDelegate() == this) {
791 drag_data_->get(i)->ResetDelegate();
792 }
793 }
794 }
795
796 void DraggedTabControllerGtk::EnsureDraggedView() {
797 if (!dragged_view_.get()) {
798 gfx::Rect rect;
799 drag_data_->GetSourceWebContents()->GetView()->GetContainerBounds(&rect);
800 dragged_view_.reset(new DraggedViewGtk(drag_data_.get(), mouse_offset_,
801 rect.size()));
802 }
803 }
804
805 gfx::Rect DraggedTabControllerGtk::GetAnimateBounds() {
806 // A hidden widget moved with gtk_fixed_move in a GtkFixed container doesn't
807 // update its allocation until after the widget is shown, so we have to use
808 // the tab bounds we keep track of.
809 //
810 // We use the requested bounds instead of the allocation because the
811 // allocation is relative to the first windowed widget ancestor of the tab.
812 // Because of this, we can't use the tabs allocation to get the screen bounds.
813 std::vector<TabGtk*> tabs = GetTabsMatchingDraggedContents(
814 attached_tabstrip_);
815 TabGtk* tab = !base::i18n::IsRTL() ? tabs.front() : tabs.back();
816 gfx::Rect bounds = tab->GetRequisition();
817 GtkWidget* widget = tab->widget();
818 GtkWidget* parent = gtk_widget_get_parent(widget);
819 bounds.Offset(ui::GetWidgetScreenOffset(parent));
820
821 return gfx::Rect(bounds.x(), bounds.y(),
822 dragged_view_->GetTotalWidthInTabStrip(), bounds.height());
823 }
824
825 void DraggedTabControllerGtk::HideWindow() {
826 GtkWidget* tabstrip = source_tabstrip_->widget();
827 GtkWindow* window = platform_util::GetTopLevel(tabstrip);
828 gtk_widget_hide(GTK_WIDGET(window));
829 }
830
831 void DraggedTabControllerGtk::ShowWindow() {
832 GtkWidget* tabstrip = source_tabstrip_->widget();
833 GtkWindow* window = platform_util::GetTopLevel(tabstrip);
834 gtk_window_present(window);
835 }
836
837 void DraggedTabControllerGtk::CleanUpHiddenFrame() {
838 // If the model we started dragging from is now empty, we must ask the
839 // delegate to close the frame.
840 if (source_tabstrip_->model()->empty())
841 source_tabstrip_->model()->delegate()->CloseFrameAfterDragSession();
842 }
843
844 void DraggedTabControllerGtk::CleanUpDraggedTabs() {
845 // If we were attached to the source tabstrip, dragged tabs will be in use. If
846 // we were detached or attached to another tabstrip, we can safely remove
847 // them and delete them now.
848 if (attached_tabstrip_ != source_tabstrip_) {
849 for (size_t i = 0; i < drag_data_->size(); ++i) {
850 if (drag_data_->get(i)->contents_) {
851 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
852 content::Source<WebContents>(drag_data_->get(i)->contents_));
853 }
854 source_tabstrip_->DestroyDraggedTab(drag_data_->get(i)->tab_);
855 drag_data_->get(i)->tab_ = NULL;
856 }
857 }
858 }
859
860 void DraggedTabControllerGtk::OnAnimateToBoundsComplete() {
861 // Sometimes, for some reason, in automation we can be called back on a
862 // detach even though we aren't attached to a tabstrip. Guard against that.
863 if (attached_tabstrip_)
864 SetDraggedTabsVisible(true, true);
865
866 CleanUpHiddenFrame();
867
868 if (!in_destructor_)
869 source_tabstrip_->DestroyDragController();
870 }
871
872 void DraggedTabControllerGtk::BringWindowUnderMouseToFront() {
873 // If we're going to dock to another window, bring it to the front.
874 gfx::NativeWindow window = GetLocalProcessWindow(
875 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint());
876 if (window)
877 gtk_window_present(GTK_WINDOW(window));
878 }
879
880 bool DraggedTabControllerGtk::AreTabsConsecutive() {
881 for (size_t i = 1; i < drag_data_->size(); ++i) {
882 if (drag_data_->get(i - 1)->source_model_index_ + 1 !=
883 drag_data_->get(i)->source_model_index_) {
884 return false;
885 }
886 }
887 return true;
888 }
889
890 gfx::NativeWindow DraggedTabControllerGtk::GetLocalProcessWindow(
891 const gfx::Point& screen_point) {
892 std::set<GtkWidget*> dragged_window;
893 dragged_window.insert(dragged_view_->widget());
894 return GetLocalProcessWindowAtPoint(
895 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(),
896 dragged_window);
897
898 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698