// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/panels/panel_browser_window_gtk.h"
#include "base/bind.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/panels/panel.h"
#include "chrome/browser/ui/panels/panel_manager.h"
#include "chrome/browser/ui/panels/panel_settings_menu_model.h"
#include "chrome/browser/ui/panels/panel_slide_animation.h"
#include "chrome/common/chrome_notification_types.h"
#include "content/public/browser/notification_service.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/base/dragdrop/gtk_dnd_util.h"
#include "ui/base/x/work_area_watcher_x.h"
namespace {
// RGB values for titlebar in draw attention state. A shade of orange.
const int kDrawAttentionR = 0xfa;
const int kDrawAttentionG = 0x98;
const int kDrawAttentionB = 0x3a;
const float kDrawAttentionRFraction = kDrawAttentionR / 255.0;
const float kDrawAttentionGFraction = kDrawAttentionG / 255.0;
const float kDrawAttentionBFraction = kDrawAttentionB / 255.0;
// Delay before click on a titlebar is allowed to minimize the panel after
// the 'draw attention' mode has been cleared.
const int kSuspendMinimizeOnClickIntervalMs = 500;
// Markup for title text in draw attention state. Set to color white.
const char* const kDrawAttentionTitleMarkupPrefix =
"";
const char* const kDrawAttentionTitleMarkupSuffix = "";
}
NativePanel* Panel::CreateNativePanel(Browser* browser, Panel* panel,
const gfx::Rect& bounds) {
PanelBrowserWindowGtk* panel_browser_window_gtk =
new PanelBrowserWindowGtk(browser, panel, bounds);
panel_browser_window_gtk->Init();
return panel_browser_window_gtk;
}
PanelBrowserWindowGtk::PanelBrowserWindowGtk(Browser* browser,
Panel* panel,
const gfx::Rect& bounds)
: BrowserWindowGtk(browser),
system_drag_disabled_for_testing_(false),
last_mouse_down_(NULL),
drag_widget_(NULL),
destroy_drag_widget_factory_(this),
drag_end_factory_(this),
panel_(panel),
bounds_(bounds),
window_size_known_(false),
is_drawing_attention_(false) {
}
PanelBrowserWindowGtk::~PanelBrowserWindowGtk() {
if (drag_widget_) {
// Terminate the grab if we have it. We could do this using any widget,
// |drag_widget_| is just convenient.
gtk_grab_add(drag_widget_);
gtk_grab_remove(drag_widget_);
DestroyDragWidget();
}
panel_->OnNativePanelClosed();
ui::WorkAreaWatcherX::RemoveObserver(this);
}
void PanelBrowserWindowGtk::Init() {
BrowserWindowGtk::Init();
// Keep the window docked to the bottom of the screen on resizes.
gtk_window_set_gravity(window(), GDK_GRAVITY_SOUTH_EAST);
// Keep the window always on top.
gtk_window_set_keep_above(window(), TRUE);
// Show the window on all the virtual desktops.
gtk_window_stick(window());
// Do not show an icon in the task bar. Window operations such as close,
// minimize etc. can only be done from the panel UI.
gtk_window_set_skip_taskbar_hint(window(), TRUE);
g_signal_connect(titlebar_widget(), "button-press-event",
G_CALLBACK(OnTitlebarButtonPressEventThunk), this);
g_signal_connect(titlebar_widget(), "button-release-event",
G_CALLBACK(OnTitlebarButtonReleaseEventThunk), this);
ui::WorkAreaWatcherX::AddObserver(this);
}
bool PanelBrowserWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
// Since panels are not resizable or movable by the user, we should not
// detect the window edge for behavioral purposes. The edge, if any,
// is present only for visual aspects.
return FALSE;
}
bool PanelBrowserWindowGtk::HandleTitleBarLeftMousePress(
GdkEventButton* event,
guint32 last_click_time,
gfx::Point last_click_position) {
// In theory we should never enter this function as we have a handler for
// button press on titlebar where we handle this. Not putting NOTREACHED()
// here because we don't want to crash if hit-testing for titlebar in window
// button press handler in BrowserWindowGtk is off by a pixel or two.
DLOG(WARNING) << "Hit-testing for titlebar off by a pixel or two?";
return TRUE;
}
void PanelBrowserWindowGtk::SaveWindowPosition() {
// We don't save window position for panels as it's controlled by
// PanelManager.
return;
}
void PanelBrowserWindowGtk::SetGeometryHints() {
// Set minimum height the window can be set to.
GdkGeometry hints;
hints.min_height = Panel::kMinimizedPanelHeight;
hints.min_width = panel_->min_size().width();
gtk_window_set_geometry_hints(
window(), GTK_WIDGET(window()), &hints, GDK_HINT_MIN_SIZE);
DCHECK(!window_size_known_);
}
void PanelBrowserWindowGtk::SetBounds(const gfx::Rect& bounds) {
// This should never be called.
DLOG(WARNING) << "Unexpected call to PanelBrowserWindowGtk::SetBounds()";
}
void PanelBrowserWindowGtk::OnSizeChanged(int width, int height) {
BrowserWindowGtk::OnSizeChanged(width, height);
if (window_size_known_)
return;
window_size_known_ = true;
int top = bounds_.bottom() - height;
int left = bounds_.right() - width;
gtk_window_move(window_, left, top);
StartBoundsAnimation(gfx::Rect(left, top, width, height), bounds_);
panel_->OnWindowSizeAvailable();
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_PANEL_WINDOW_SIZE_KNOWN,
content::Source(panel_.get()),
content::NotificationService::NoDetails());
}
bool PanelBrowserWindowGtk::UseCustomFrame() {
// We always use custom frame for panels.
return TRUE;
}
void PanelBrowserWindowGtk::ShowSettingsMenu(GtkWidget* widget,
GdkEventButton* event) {
if (!settings_menu_.get()) {
settings_menu_model_.reset(new PanelSettingsMenuModel(panel_.get()));
settings_menu_.reset(new MenuGtk(this, settings_menu_model_.get()));
}
settings_menu_->PopupForWidget(widget, event->button, event->time);
}
void PanelBrowserWindowGtk::DrawCustomFrame(cairo_t* cr,
GtkWidget* widget,
GdkEventExpose* event) {
BrowserWindowGtk::DrawCustomFrame(cr, widget, event);
if (is_drawing_attention_) {
cairo_set_source_rgb(cr, kDrawAttentionRFraction,
kDrawAttentionGFraction,
kDrawAttentionBFraction);
GdkRectangle dest_rectangle = GetTitlebarRectForDrawAttention();
GdkRegion* dest_region = gdk_region_rectangle(&dest_rectangle);
gdk_region_intersect(dest_region, event->region);
gdk_cairo_region(cr, dest_region);
cairo_clip(cr);
cairo_paint(cr);
gdk_region_destroy(dest_region);
}
}
void PanelBrowserWindowGtk::ActiveWindowChanged(GdkWindow* active_window) {
bool was_active = IsActive();
BrowserWindowGtk::ActiveWindowChanged(active_window);
if (was_active == IsActive()) // State didn't change.
return;
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_PANEL_CHANGED_ACTIVE_STATUS,
content::Source(panel_.get()),
content::NotificationService::NoDetails());
}
BrowserWindowGtk::TitleDecoration PanelBrowserWindowGtk::GetWindowTitle(
std::string* title) const {
if (is_drawing_attention_) {
std::string title_original;
BrowserWindowGtk::TitleDecoration title_decoration =
BrowserWindowGtk::GetWindowTitle(&title_original);
DCHECK_EQ(BrowserWindowGtk::PLAIN_TEXT, title_decoration);
gchar* title_escaped = g_markup_escape_text(title_original.c_str(), -1);
gchar* title_with_markup = g_strconcat(kDrawAttentionTitleMarkupPrefix,
title_escaped,
kDrawAttentionTitleMarkupSuffix,
NULL);
*title = title_with_markup;
g_free(title_escaped);
g_free(title_with_markup);
return BrowserWindowGtk::PANGO_MARKUP;
} else {
return BrowserWindowGtk::GetWindowTitle(title);
}
}
void PanelBrowserWindowGtk::WorkAreaChanged() {
panel_->manager()->OnDisplayChanged();
}
void PanelBrowserWindowGtk::ShowPanel() {
Show();
}
void PanelBrowserWindowGtk::ShowPanelInactive() {
ShowInactive();
}
gfx::Rect PanelBrowserWindowGtk::GetPanelBounds() const {
return bounds_;
}
void PanelBrowserWindowGtk::SetPanelBounds(const gfx::Rect& bounds) {
SetBoundsInternal(bounds, true);
}
void PanelBrowserWindowGtk::SetPanelBoundsInstantly(const gfx::Rect& bounds) {
SetBoundsInternal(bounds, false);
}
void PanelBrowserWindowGtk::SetBoundsInternal(const gfx::Rect& bounds,
bool animate) {
if (bounds == bounds_)
return;
if (drag_widget_ || !animate) {
DCHECK(!bounds_animator_.get() || !bounds_animator_->is_animating());
// If the current panel is being dragged, it should just move with the
// user drag, we should not animate.
gtk_window_move(window(), bounds.x(), bounds.y());
} else if (window_size_known_) {
StartBoundsAnimation(bounds_, bounds);
}
// If window size is not known, wait till the size is known before starting
// the animation.
bounds_ = bounds;
}
void PanelBrowserWindowGtk::ClosePanel() {
// Cancel any currently running animation since we're closing down.
if (bounds_animator_.get())
bounds_animator_.reset();
Close();
}
void PanelBrowserWindowGtk::ActivatePanel() {
gdk_window_set_accept_focus(
gtk_widget_get_window(GTK_WIDGET(window())), TRUE);
Activate();
}
void PanelBrowserWindowGtk::DeactivatePanel() {
BrowserWindow* browser_window =
panel_->manager()->GetNextBrowserWindowToActivate(panel_.get());
if (browser_window) {
browser_window->Activate();
} else {
Deactivate();
}
if (panel_->expansion_state() == Panel::MINIMIZED) {
gdk_window_set_accept_focus(
gtk_widget_get_window(GTK_WIDGET(window())), FALSE);
}
}
bool PanelBrowserWindowGtk::IsPanelActive() const {
return IsActive();
}
gfx::NativeWindow PanelBrowserWindowGtk::GetNativePanelHandle() {
return GetNativeHandle();
}
void PanelBrowserWindowGtk::UpdatePanelTitleBar() {
UpdateTitleBar();
}
void PanelBrowserWindowGtk::UpdatePanelLoadingAnimations(bool should_animate) {
UpdateLoadingAnimations(should_animate);
}
void PanelBrowserWindowGtk::ShowTaskManagerForPanel() {
ShowTaskManager();
}
FindBar* PanelBrowserWindowGtk::CreatePanelFindBar() {
return CreateFindBar();
}
void PanelBrowserWindowGtk::NotifyPanelOnUserChangedTheme() {
UserChangedTheme();
}
void PanelBrowserWindowGtk::PanelTabContentsFocused(TabContents* tab_contents) {
TabContentsFocused(tab_contents);
}
void PanelBrowserWindowGtk::PanelCut() {
Cut();
}
void PanelBrowserWindowGtk::PanelCopy() {
Copy();
}
void PanelBrowserWindowGtk::PanelPaste() {
Paste();
}
void PanelBrowserWindowGtk::DrawAttention() {
// Don't draw attention for active panel.
if (is_drawing_attention_ || IsActive())
return;
is_drawing_attention_ = true;
// Bring up the titlebar to get people's attention.
if (panel_->expansion_state() == Panel::MINIMIZED)
panel_->SetExpansionState(Panel::TITLE_ONLY);
GdkRectangle rect = GetTitlebarRectForDrawAttention();
gdk_window_invalidate_rect(
gtk_widget_get_window(GTK_WIDGET(window())), &rect, TRUE);
UpdateTitleBar();
}
bool PanelBrowserWindowGtk::IsDrawingAttention() const {
return is_drawing_attention_;
}
bool PanelBrowserWindowGtk::PreHandlePanelKeyboardEvent(
const NativeWebKeyboardEvent& event,
bool* is_keyboard_shortcut) {
return PreHandleKeyboardEvent(event, is_keyboard_shortcut);
}
void PanelBrowserWindowGtk::FullScreenModeChanged(bool is_full_screen) {
// Nothing to do here as z-order rules for panels ensures that they're below
// any app running in full screen mode.
// Full screen detection should not have been enabled for Linux.
NOTREACHED();
}
void PanelBrowserWindowGtk::HandlePanelKeyboardEvent(
const NativeWebKeyboardEvent& event) {
HandleKeyboardEvent(event);
}
Browser* PanelBrowserWindowGtk::GetPanelBrowser() const {
return browser();
}
void PanelBrowserWindowGtk::DestroyPanelBrowser() {
DestroyBrowser();
}
gfx::Size PanelBrowserWindowGtk::IconOnlySize() const {
// TODO(prasdt): to be implemented.
return gfx::Size();
}
void PanelBrowserWindowGtk::EnsurePanelFullyVisible() {
// TODO(prasdt): to be implemented.
}
void PanelBrowserWindowGtk::SetPanelAppIconVisibility(bool visible) {
// TODO(prasdt): to be implemented.
}
gfx::Size PanelBrowserWindowGtk::WindowSizeFromContentSize(
const gfx::Size& content_size) const {
gfx::Size frame = GetNonClientFrameSize();
return gfx::Size(content_size.width() + frame.width(),
content_size.height() + frame.height());
}
gfx::Size PanelBrowserWindowGtk::ContentSizeFromWindowSize(
const gfx::Size& window_size) const {
gfx::Size frame = GetNonClientFrameSize();
return gfx::Size(window_size.width() - frame.width(),
window_size.height() - frame.height());
}
int PanelBrowserWindowGtk::TitleOnlyHeight() const {
GtkAllocation allocation;
gtk_widget_get_allocation(titlebar_widget(), &allocation);
return allocation.height;
}
void PanelBrowserWindowGtk::StartBoundsAnimation(
const gfx::Rect& from_bounds, const gfx::Rect& to_bounds) {
if (bounds_animator_.get() && bounds_animator_->is_animating()) {
animation_start_bounds_ = last_animation_progressed_bounds_;
} else {
animation_start_bounds_ = from_bounds;
}
bounds_animator_.reset(new PanelSlideAnimation(
this, panel_.get(), from_bounds, to_bounds));
bounds_animator_->Start();
last_animation_progressed_bounds_ = animation_start_bounds_;
}
bool PanelBrowserWindowGtk::IsAnimatingBounds() const {
return bounds_animator_.get() && bounds_animator_->is_animating();
}
void PanelBrowserWindowGtk::WillProcessEvent(GdkEvent* event) {
// Nothing to do.
}
void PanelBrowserWindowGtk::DidProcessEvent(GdkEvent* event) {
DCHECK(last_mouse_down_);
if (event->type != GDK_MOTION_NOTIFY || !panel_->draggable())
return;
gdouble new_x_double;
gdouble new_y_double;
gdouble old_x_double;
gdouble old_y_double;
gdk_event_get_root_coords(event, &new_x_double, &new_y_double);
gdk_event_get_root_coords(last_mouse_down_, &old_x_double, &old_y_double);
gint new_x = static_cast(new_x_double);
gint new_y = static_cast(new_y_double);
gint old_x = static_cast(old_x_double);
gint old_y = static_cast(old_y_double);
if (!drag_widget_ &&
gtk_drag_check_threshold(titlebar_widget(), old_x,
old_y, new_x, new_y)) {
CreateDragWidget();
if (!system_drag_disabled_for_testing_) {
GtkTargetList* list = ui::GetTargetListFromCodeMask(ui::CHROME_TAB);
gtk_drag_begin(drag_widget_, list, GDK_ACTION_MOVE, 1, last_mouse_down_);
// gtk_drag_begin increments reference count for GtkTargetList. So unref
// it here to reduce the reference count.
gtk_target_list_unref(list);
}
panel_->manager()->StartDragging(panel_.get());
}
if (drag_widget_) {
panel_->manager()->Drag(new_x - old_x);
gdk_event_free(last_mouse_down_);
last_mouse_down_ = gdk_event_copy(event);
}
}
void PanelBrowserWindowGtk::AnimationEnded(const ui::Animation* animation) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_PANEL_BOUNDS_ANIMATIONS_FINISHED,
content::Source(panel_.get()),
content::NotificationService::NoDetails());
}
void PanelBrowserWindowGtk::AnimationProgressed(
const ui::Animation* animation) {
DCHECK(!drag_widget_);
DCHECK(window_size_known_);
gfx::Rect new_bounds = bounds_animator_->CurrentValueBetween(
animation_start_bounds_, bounds_);
// Resize if necessary.
if (animation_start_bounds_.size() != bounds_.size())
gtk_window_resize(window(), new_bounds.width(), new_bounds.height());
// Only move if bottom right corner will change.
// Panels use window gravity of GDK_GRAVITY_SOUTH_EAST which means the
// window is anchored to the bottom right corner on resize, making it
// unnecessary to move the window if the bottom right corner is unchanged.
// For example, when we minimize to the bottom, moving can actually
// result in the wrong behavior.
// - Say window is 100x100 with x,y=900,900 on a 1000x1000 screen.
// - Say you minimize the window to 100x3 and move it to 900,997 to keep it
// anchored to the bottom.
// - resize is an async operation and the window manager will decide that
// the move will take the window off screen and it won't honor the
// request.
// - When resize finally happens, you'll have a 100x3 window a x,y=900,900.
bool move = (animation_start_bounds_.bottom() != bounds_.bottom()) ||
(animation_start_bounds_.right() != bounds_.right());
if (move)
gtk_window_move(window_, new_bounds.x(), new_bounds.y());
last_animation_progressed_bounds_ = new_bounds;
}
void PanelBrowserWindowGtk::CreateDragWidget() {
DCHECK(!drag_widget_);
drag_widget_ = gtk_invisible_new();
g_signal_connect_after(drag_widget_, "drag-begin",
G_CALLBACK(OnDragBeginThunk), this);
g_signal_connect(drag_widget_, "drag-failed",
G_CALLBACK(OnDragFailedThunk), this);
g_signal_connect(drag_widget_, "button-release-event",
G_CALLBACK(OnDragButtonReleasedThunk), this);
}
void PanelBrowserWindowGtk::DestroyDragWidget() {
if (drag_widget_) {
gtk_widget_destroy(drag_widget_);
drag_widget_ = NULL;
}
}
void PanelBrowserWindowGtk::EndDrag(bool canceled) {
if (!system_drag_disabled_for_testing_)
DCHECK(drag_widget_);
// Make sure we only run EndDrag once by canceling any tasks that want
// to call EndDrag.
drag_end_factory_.InvalidateWeakPtrs();
CleanupDragDrop();
if (drag_widget_) {
// We must let gtk clean up after we handle the drag operation, otherwise
// there will be outstanding references to the drag widget when we try to
// destroy it.
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&PanelBrowserWindowGtk::DestroyDragWidget,
drag_end_factory_.GetWeakPtr()));
panel_->manager()->EndDragging(canceled);
}
}
void PanelBrowserWindowGtk::CleanupDragDrop() {
if (last_mouse_down_) {
MessageLoopForUI::current()->RemoveObserver(this);
gdk_event_free(last_mouse_down_);
last_mouse_down_ = NULL;
}
}
GdkRectangle PanelBrowserWindowGtk::GetTitlebarRectForDrawAttention() const {
GdkRectangle rect;
rect.x = 0;
rect.y = 0;
// We get the window width and not the titlebar_widget() width because we'd
// like for the window borders on either side of the title bar to be the same
// color.
GtkAllocation window_allocation;
gtk_widget_get_allocation(GTK_WIDGET(window()), &window_allocation);
rect.width = window_allocation.width;
GtkAllocation titlebar_allocation;
gtk_widget_get_allocation(titlebar_widget(), &titlebar_allocation);
rect.height = titlebar_allocation.height;
return rect;
}
gboolean PanelBrowserWindowGtk::OnTitlebarButtonPressEvent(
GtkWidget* widget, GdkEventButton* event) {
// Early return if animation in progress.
if (IsAnimatingBounds())
return TRUE;
// Every button press ensures either a button-release-event or a drag-fail
// signal for |widget|.
if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
// Store the button press event, used to initiate a drag.
DCHECK(!last_mouse_down_);
last_mouse_down_ = gdk_event_copy(reinterpret_cast(event));
MessageLoopForUI::current()->AddObserver(this);
}
return TRUE;
}
gboolean PanelBrowserWindowGtk::OnTitlebarButtonReleaseEvent(
GtkWidget* widget, GdkEventButton* event) {
if (event->button != 1) {
DCHECK(!last_mouse_down_);
return TRUE;
}
CleanupDragDrop();
if (panel_->expansion_state() == Panel::EXPANDED) {
if (base::Time::Now() < disableMinimizeUntilTime_)
return TRUE;
panel_->SetExpansionState(Panel::MINIMIZED);
} else {
panel_->Activate();
}
return TRUE;
}
void PanelBrowserWindowGtk::HandleFocusIn(GtkWidget* widget,
GdkEventFocus* event) {
BrowserWindowGtk::HandleFocusIn(widget, event);
if (!is_drawing_attention_)
return;
is_drawing_attention_ = false;
UpdateTitleBar();
DCHECK(panel_->expansion_state() == Panel::EXPANDED);
disableMinimizeUntilTime_ = base::Time::Now() +
base::TimeDelta::FromMilliseconds(kSuspendMinimizeOnClickIntervalMs);
}
void PanelBrowserWindowGtk::OnDragBegin(GtkWidget* widget,
GdkDragContext* context) {
// Set drag icon to be a transparent pixbuf.
GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
gdk_pixbuf_fill(pixbuf, 0);
gtk_drag_set_icon_pixbuf(context, pixbuf, 0, 0);
g_object_unref(pixbuf);
}
gboolean PanelBrowserWindowGtk::OnDragFailed(
GtkWidget* widget, GdkDragContext* context, GtkDragResult result) {
bool canceled = (result != GTK_DRAG_RESULT_NO_TARGET);
EndDrag(canceled);
return TRUE;
}
gboolean PanelBrowserWindowGtk::OnDragButtonReleased(GtkWidget* widget,
GdkEventButton* button) {
// We always get this event when gtk is releasing the grab and ending the
// drag. This gets fired before drag-failed handler gets fired. However,
// if the user ended the drag with space or enter, we don't get a follow up
// event to tell us the drag has finished (either a drag-failed or a
// drag-end). We post a task, instead of calling EndDrag right here, to give
// GTK+ a chance to send the drag-failed event with the right status. If
// GTK+ does send the drag-failed event, we cancel the task.
MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&PanelBrowserWindowGtk::EndDrag,
drag_end_factory_.GetWeakPtr(),
false));
return TRUE;
}
// NativePanelTesting implementation.
class NativePanelTestingGtk : public NativePanelTesting {
public:
explicit NativePanelTestingGtk(
PanelBrowserWindowGtk* panel_browser_window_gtk);
private:
virtual void PressLeftMouseButtonTitlebar(
const gfx::Point& point) OVERRIDE;
virtual void ReleaseMouseButtonTitlebar() OVERRIDE;
virtual void DragTitlebar(int delta_x, int delta_y) OVERRIDE;
virtual void CancelDragTitlebar() OVERRIDE;
virtual void FinishDragTitlebar() OVERRIDE;
virtual bool VerifyDrawingAttention() const OVERRIDE;
virtual bool VerifyActiveState(bool is_active) OVERRIDE;
virtual void WaitForWindowCreationToComplete() const OVERRIDE;
virtual bool IsWindowSizeKnown() const OVERRIDE;
virtual bool IsAnimatingBounds() const OVERRIDE;
PanelBrowserWindowGtk* panel_browser_window_gtk_;
};
// static
NativePanelTesting* NativePanelTesting::Create(NativePanel* native_panel) {
return new NativePanelTestingGtk(static_cast(
native_panel));
}
NativePanelTestingGtk::NativePanelTestingGtk(
PanelBrowserWindowGtk* panel_browser_window_gtk) :
panel_browser_window_gtk_(panel_browser_window_gtk) {
}
void NativePanelTestingGtk::PressLeftMouseButtonTitlebar(
const gfx::Point& point) {
// If there is an animation, wait for it to finish as we don't handle button
// clicks while animation is in progress.
while (panel_browser_window_gtk_->IsAnimatingBounds())
MessageLoopForUI::current()->RunAllPending();
GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS);
event->button.button = 1;
event->button.x_root = point.x();
event->button.y_root = point.y();
panel_browser_window_gtk_->OnTitlebarButtonPressEvent(
panel_browser_window_gtk_->titlebar_widget(),
reinterpret_cast(event));
gdk_event_free(event);
MessageLoopForUI::current()->RunAllPending();
}
void NativePanelTestingGtk::ReleaseMouseButtonTitlebar() {
GdkEvent* event = gdk_event_new(GDK_BUTTON_RELEASE);
event->button.button = 1;
panel_browser_window_gtk_->OnTitlebarButtonReleaseEvent(
panel_browser_window_gtk_->titlebar_widget(),
reinterpret_cast(event));
MessageLoopForUI::current()->RunAllPending();
}
void NativePanelTestingGtk::DragTitlebar(int delta_x, int delta_y) {
// Prevent extra unwanted signals and focus grabs.
panel_browser_window_gtk_->system_drag_disabled_for_testing_ = true;
GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
gdk_event_get_root_coords(panel_browser_window_gtk_->last_mouse_down_,
&event->motion.x_root, &event->motion.y_root);
event->motion.x_root += delta_x;
event->motion.y_root += delta_y;
panel_browser_window_gtk_->DidProcessEvent(event);
gdk_event_free(event);
MessageLoopForUI::current()->RunAllPending();
}
void NativePanelTestingGtk::CancelDragTitlebar() {
panel_browser_window_gtk_->OnDragFailed(
panel_browser_window_gtk_->drag_widget_, NULL,
GTK_DRAG_RESULT_USER_CANCELLED);
MessageLoopForUI::current()->RunAllPending();
}
void NativePanelTestingGtk::FinishDragTitlebar() {
panel_browser_window_gtk_->OnDragFailed(
panel_browser_window_gtk_->drag_widget_, NULL,
GTK_DRAG_RESULT_NO_TARGET);
MessageLoopForUI::current()->RunAllPending();
}
bool NativePanelTestingGtk::VerifyDrawingAttention() const {
std::string title;
BrowserWindowGtk::TitleDecoration decoration =
panel_browser_window_gtk_->GetWindowTitle(&title);
return panel_browser_window_gtk_->IsDrawingAttention() &&
decoration == BrowserWindowGtk::PANGO_MARKUP;
}
bool NativePanelTestingGtk::VerifyActiveState(bool is_active) {
// TODO(jianli): to be implemented. http://crbug.com/102737
return false;
}
void NativePanelTestingGtk::WaitForWindowCreationToComplete() const {
while (!panel_browser_window_gtk_->window_size_known_)
MessageLoopForUI::current()->RunAllPending();
while (panel_browser_window_gtk_->bounds_animator_.get() &&
panel_browser_window_gtk_->bounds_animator_->is_animating()) {
MessageLoopForUI::current()->RunAllPending();
}
}
bool NativePanelTestingGtk::IsWindowSizeKnown() const {
return panel_browser_window_gtk_->window_size_known_;
}
bool NativePanelTestingGtk::IsAnimatingBounds() const {
return panel_browser_window_gtk_->IsAnimatingBounds();
}