// Copyright (c) 2009 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 "views/widget/widget_gtk.h" #include "app/drag_drop_types.h" #include "app/gfx/path.h" #include "app/os_exchange_data.h" #include "app/os_exchange_data_provider_gtk.h" #include "base/compiler_specific.h" #include "base/message_loop.h" #include "views/widget/default_theme_provider.h" #include "views/widget/drop_target_gtk.h" #include "views/widget/root_view.h" #include "views/widget/tooltip_manager_gtk.h" #include "views/window/window_gtk.h" namespace views { // During drag and drop GTK sends a drag-leave during a drop. This means we // have no way to tell the difference between a normal drag leave and a drop. // To work around that we listen for DROP_START, then ignore the subsequent // drag-leave that GTK generates. class WidgetGtk::DropObserver : public MessageLoopForUI::Observer { public: DropObserver() { } virtual void WillProcessEvent(GdkEvent* event) { if (event->type == GDK_DROP_START) { WidgetGtk* widget = GetWidgetGtkForEvent(event); if (widget) widget->ignore_drag_leave_ = true; } } virtual void DidProcessEvent(GdkEvent* event) { } private: WidgetGtk* GetWidgetGtkForEvent(GdkEvent* event) { GtkWidget* gtk_widget = gtk_get_event_widget(event); if (!gtk_widget) return NULL; return WidgetGtk::GetViewForNative(gtk_widget); } DISALLOW_COPY_AND_ASSIGN(DropObserver); }; // Returns the position of a widget on screen. static void GetWidgetPositionOnScreen(GtkWidget* widget, int* x, int *y) { while (widget) { if (GTK_IS_WINDOW(widget)) { int window_x, window_y; gtk_window_get_position(GTK_WINDOW(widget), &window_x, &window_y); *x += window_x; *y += window_y; return; } // Not a window. *x += widget->allocation.x; *y += widget->allocation.y; widget = gtk_widget_get_parent(widget); } } // Returns the view::Event::flags for a GdkEventButton. static int GetFlagsForEventButton(const GdkEventButton& event) { int flags = Event::GetFlagsFromGdkState(event.state); switch (event.button) { case 1: flags |= Event::EF_LEFT_BUTTON_DOWN; break; case 2: flags |= Event::EF_MIDDLE_BUTTON_DOWN; break; case 3: flags |= Event::EF_RIGHT_BUTTON_DOWN; break; default: // We only deal with 1-3. break; } if (event.type == GDK_2BUTTON_PRESS) flags |= MouseEvent::EF_IS_DOUBLE_CLICK; return flags; } // static GtkWidget* WidgetGtk::null_parent_ = NULL; //////////////////////////////////////////////////////////////////////////////// // WidgetGtk, public: WidgetGtk::WidgetGtk(Type type) : is_window_(false), type_(type), widget_(NULL), window_contents_(NULL), is_mouse_down_(false), has_capture_(false), last_mouse_event_was_move_(false), ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)), delete_on_destroy_(true), transparent_(false), ignore_drag_leave_(false), opacity_(255), drag_data_(NULL) { static bool installed_message_loop_observer = false; if (!installed_message_loop_observer) { installed_message_loop_observer = true; MessageLoopForUI* loop = MessageLoopForUI::current(); if (loop) loop->AddObserver(new DropObserver()); } if (type_ != TYPE_CHILD) focus_manager_.reset(new FocusManager(this)); } WidgetGtk::~WidgetGtk() { MessageLoopForUI::current()->RemoveObserver(this); } bool WidgetGtk::MakeTransparent() { // Transparency can only be enabled for windows/popups and only if we haven't // realized the widget. DCHECK(!widget_ && type_ != TYPE_CHILD); if (!gdk_screen_is_composited(gdk_screen_get_default())) { // Transparency is only supported for compositing window managers. DLOG(WARNING) << "compsiting not supported"; return false; } if (!gdk_screen_get_rgba_colormap(gdk_screen_get_default())) { // We need rgba to make the window transparent. return false; } transparent_ = true; return true; } void WidgetGtk::AddChild(GtkWidget* child) { gtk_container_add(GTK_CONTAINER(window_contents_), child); } void WidgetGtk::RemoveChild(GtkWidget* child) { // We can be called after the contents widget has been destroyed, e.g. any // NativeViewHost not removed from the view hierarchy before the window is // closed. if (GTK_IS_CONTAINER(window_contents_)) gtk_container_remove(GTK_CONTAINER(window_contents_), child); } void WidgetGtk::ReparentChild(GtkWidget* child) { gtk_widget_reparent(child, window_contents_); } void WidgetGtk::PositionChild(GtkWidget* child, int x, int y, int w, int h) { GtkAllocation alloc = { x, y, w, h }; // For some reason we need to do both of these to size a widget. gtk_widget_size_allocate(child, &alloc); gtk_widget_set_size_request(child, w, h); gtk_fixed_move(GTK_FIXED(window_contents_), child, x, y); } void WidgetGtk::DoDrag(const OSExchangeData& data, int operation) { const OSExchangeDataProviderGtk& data_provider = static_cast(data.provider()); GtkTargetList* targets = data_provider.GetTargetList(); GdkEvent* current_event = gtk_get_current_event(); DCHECK(current_event); gtk_drag_begin(window_contents_, targets, static_cast( DragDropTypes::DragOperationToGdkDragAction(operation)), 1, current_event); gdk_event_free(current_event); gtk_target_list_unref(targets); drag_data_ = &data_provider; // Block the caller until drag is done by running a nested message loop. MessageLoopForUI::current()->Run(NULL); drag_data_ = NULL; } //////////////////////////////////////////////////////////////////////////////// // WidgetGtk, Widget implementation: void WidgetGtk::Init(GtkWidget* parent, const gfx::Rect& bounds) { // Force creation of the RootView if it hasn't been created yet. GetRootView(); #if !defined(OS_CHROMEOS) default_theme_provider_.reset(new DefaultThemeProvider()); #endif // Make container here. CreateGtkWidget(parent, bounds); if (opacity_ != 255) SetOpacity(opacity_); // Make sure we receive our motion events. // In general we register most events on the parent of all widgets. At a // minimum we need painting to happen on the parent (otherwise painting // doesn't work at all), and similarly we need mouse release events on the // parent as windows don't get mouse releases. gtk_widget_add_events(window_contents_, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); SetRootViewForWidget(widget_, root_view_.get()); MessageLoopForUI::current()->AddObserver(this); // TODO(beng): make these take this rather than NULL. g_signal_connect_after(G_OBJECT(window_contents_), "size_allocate", G_CALLBACK(CallSizeAllocate), NULL); g_signal_connect(G_OBJECT(window_contents_), "expose_event", G_CALLBACK(CallPaint), NULL); g_signal_connect(G_OBJECT(window_contents_), "enter_notify_event", G_CALLBACK(CallEnterNotify), NULL); g_signal_connect(G_OBJECT(window_contents_), "leave_notify_event", G_CALLBACK(CallLeaveNotify), NULL); g_signal_connect(G_OBJECT(window_contents_), "motion_notify_event", G_CALLBACK(CallMotionNotify), NULL); g_signal_connect(G_OBJECT(window_contents_), "button_press_event", G_CALLBACK(CallButtonPress), NULL); g_signal_connect(G_OBJECT(window_contents_), "button_release_event", G_CALLBACK(CallButtonRelease), NULL); g_signal_connect(G_OBJECT(window_contents_), "grab_broken_event", G_CALLBACK(CallGrabBrokeEvent), NULL); g_signal_connect(G_OBJECT(window_contents_), "grab_notify", G_CALLBACK(CallGrabNotify), NULL); g_signal_connect(G_OBJECT(window_contents_), "key_press_event", G_CALLBACK(CallKeyPress), NULL); g_signal_connect(G_OBJECT(window_contents_), "key_release_event", G_CALLBACK(CallKeyRelease), NULL); g_signal_connect(G_OBJECT(window_contents_), "scroll_event", G_CALLBACK(CallScroll), NULL); g_signal_connect(G_OBJECT(window_contents_), "visibility_notify_event", G_CALLBACK(CallVisibilityNotify), NULL); // In order to receive notification when the window is no longer the front // window, we need to install these on the widget. // NOTE: this doesn't work with focus follows mouse. g_signal_connect(G_OBJECT(widget_), "focus_in_event", G_CALLBACK(CallFocusIn), NULL); g_signal_connect(G_OBJECT(widget_), "focus_out_event", G_CALLBACK(CallFocusOut), NULL); g_signal_connect(G_OBJECT(widget_), "destroy", G_CALLBACK(CallDestroy), NULL); if (transparent_) { g_signal_connect(G_OBJECT(widget_), "expose_event", G_CALLBACK(CallWindowPaint), this); } // Drag and drop. gtk_drag_dest_set(window_contents_, static_cast(0), NULL, 0, GDK_ACTION_COPY); g_signal_connect(G_OBJECT(window_contents_), "drag_motion", G_CALLBACK(CallDragMotion), this); g_signal_connect(G_OBJECT(window_contents_), "drag_data_received", G_CALLBACK(CallDragDataReceived), this); g_signal_connect(G_OBJECT(window_contents_), "drag_drop", G_CALLBACK(CallDragDrop), this); g_signal_connect(G_OBJECT(window_contents_), "drag_leave", G_CALLBACK(CallDragLeave), this); g_signal_connect(G_OBJECT(window_contents_), "drag_data_get", G_CALLBACK(CallDragDataGet), this); g_signal_connect(G_OBJECT(window_contents_), "drag_end", G_CALLBACK(CallDragEnd), this); g_signal_connect(G_OBJECT(window_contents_), "drag_failed", G_CALLBACK(CallDragFailed), this); tooltip_manager_.reset(new TooltipManagerGtk(this)); if (type_ == TYPE_CHILD) { if (parent) { WidgetGtk* parent_widget = GetViewForNative(parent); parent_widget->AddChild(widget_); parent_widget->PositionChild(widget_, bounds.x(), bounds.y(), bounds.width(), bounds.height()); } } else { if (bounds.width() > 0 && bounds.height() > 0) gtk_window_resize(GTK_WINDOW(widget_), bounds.width(), bounds.height()); gtk_window_move(GTK_WINDOW(widget_), bounds.x(), bounds.y()); } } void WidgetGtk::SetContentsView(View* view) { root_view_->SetContentsView(view); } void WidgetGtk::GetBounds(gfx::Rect* out, bool including_frame) const { DCHECK(widget_); int x = 0, y = 0, w, h; if (GTK_IS_WINDOW(widget_)) { gtk_window_get_position(GTK_WINDOW(widget_), &x, &y); // NOTE: this doesn't include frame decorations, but it should be good // enough for our uses. gtk_window_get_size(GTK_WINDOW(widget_), &w, &h); } else { GetWidgetPositionOnScreen(widget_, &x, &y); w = widget_->allocation.width; h = widget_->allocation.height; } return out->SetRect(x, y, w, h); } void WidgetGtk::SetBounds(const gfx::Rect& bounds) { if (type_ == TYPE_CHILD) { WidgetGtk* parent_widget = GetViewForNative(gtk_widget_get_parent(widget_)); parent_widget->PositionChild(widget_, bounds.x(), bounds.y(), bounds.width(), bounds.height()); } else if (GTK_WIDGET_MAPPED(widget_)) { // If the widget is mapped (on screen), we can move and resize with one // call, which avoids two separate window manager steps. gdk_window_move_resize(widget_->window, bounds.x(), bounds.y(), bounds.width(), bounds.height()); } else { GtkWindow* gtk_window = GTK_WINDOW(widget_); // TODO: this may need to set an initial size if not showing. // TODO: need to constrain based on screen size. gtk_window_resize(gtk_window, bounds.width(), bounds.height()); gtk_window_move(gtk_window, bounds.x(), bounds.y()); } } void WidgetGtk::SetShape(const gfx::Path& shape) { DCHECK(widget_); DCHECK(widget_->window); gdk_window_shape_combine_region(widget_->window, NULL, 0, 0); GdkRegion* region = shape.CreateGdkRegion(); gdk_window_shape_combine_region(widget_->window, region, 0, 0); gdk_region_destroy(region); } void WidgetGtk::Close() { if (!widget_) return; // No need to do anything. // Hide first. Hide(); if (close_widget_factory_.empty()) { // And we delay the close just in case we're on the stack. MessageLoop::current()->PostTask(FROM_HERE, close_widget_factory_.NewRunnableMethod( &WidgetGtk::CloseNow)); } } void WidgetGtk::CloseNow() { if (widget_) gtk_widget_destroy(widget_); } void WidgetGtk::Show() { if (widget_) gtk_widget_show(widget_); } void WidgetGtk::Hide() { if (widget_) gtk_widget_hide(widget_); } gfx::NativeView WidgetGtk::GetNativeView() const { return widget_; } void WidgetGtk::PaintNow(const gfx::Rect& update_rect) { gtk_widget_queue_draw_area(widget_, update_rect.x(), update_rect.y(), update_rect.width(), update_rect.height()); } void WidgetGtk::SetOpacity(unsigned char opacity) { opacity_ = opacity; if (widget_) { // We can only set the opacity when the widget has been realized. gdk_window_set_opacity(widget_->window, static_cast(opacity) / static_cast(255)); } } RootView* WidgetGtk::GetRootView() { if (!root_view_.get()) { // First time the root view is being asked for, create it now. root_view_.reset(CreateRootView()); } return root_view_.get(); } Widget* WidgetGtk::GetRootWidget() const { GtkWidget* parent = widget_; GtkWidget* last_parent = parent; while (parent) { last_parent = parent; parent = gtk_widget_get_parent(parent); } return last_parent ? GetViewForNative(last_parent) : NULL; } bool WidgetGtk::IsVisible() const { return GTK_WIDGET_VISIBLE(widget_); } bool WidgetGtk::IsActive() const { // If this only applies to windows, it shouldn't be in widget. DCHECK(GTK_IS_WINDOW(widget_)); return gtk_window_is_active(GTK_WINDOW(widget_)); } void WidgetGtk::GenerateMousePressedForView(View* view, const gfx::Point& point) { NOTIMPLEMENTED(); } TooltipManager* WidgetGtk::GetTooltipManager() { return tooltip_manager_.get(); } bool WidgetGtk::GetAccelerator(int cmd_id, Accelerator* accelerator) { NOTIMPLEMENTED(); return false; } Window* WidgetGtk::GetWindow() { return GetWindowImpl(widget_); } const Window* WidgetGtk::GetWindow() const { return GetWindowImpl(widget_); } ThemeProvider* WidgetGtk::GetThemeProvider() const { return default_theme_provider_.get(); } FocusManager* WidgetGtk::GetFocusManager() { return focus_manager_.get(); } void WidgetGtk::ViewHierarchyChanged(bool is_add, View *parent, View *child) { if (drop_target_.get()) drop_target_->ResetTargetViewIfEquals(child); } //////////////////////////////////////////////////////////////////////////////// // WidgetGtk, MessageLoopForUI::Observer implementation: void WidgetGtk::WillProcessEvent(GdkEvent* event) { } void WidgetGtk::DidProcessEvent(GdkEvent* event) { if (root_view_->NeedsPainting(true)) PaintNow(root_view_->GetScheduledPaintRect()); } //////////////////////////////////////////////////////////////////////////////// // TODO(beng): organize into sections: void WidgetGtk::CreateGtkWidget(GtkWidget* parent, const gfx::Rect& bounds) { if (type_ == TYPE_CHILD) { window_contents_ = widget_ = gtk_fixed_new(); gtk_fixed_set_has_window(GTK_FIXED(widget_), true); if (!parent && !null_parent_) { GtkWidget* popup = gtk_window_new(GTK_WINDOW_POPUP); null_parent_ = gtk_fixed_new(); gtk_container_add(GTK_CONTAINER(popup), null_parent_); gtk_widget_realize(null_parent_); } gtk_container_add(GTK_CONTAINER(parent ? parent : null_parent_), widget_); SetViewForNative(widget_, this); } else { widget_ = gtk_window_new( type_ == TYPE_WINDOW ? GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP); if (!bounds.size().IsEmpty()) { // When we realize the window, the window manager is given a size. If we // don't specify a size before then GTK defaults to 200x200. Specify // a size now so that the window manager sees the requested size. GtkAllocation alloc = { 0, 0, bounds.width(), bounds.height() }; gtk_widget_size_allocate(widget_, &alloc); } gtk_window_set_decorated(GTK_WINDOW(widget_), false); // We'll take care of positioning our window. gtk_window_set_position(GTK_WINDOW(widget_), GTK_WIN_POS_NONE); SetWindowForNative(widget_, static_cast(this)); SetViewForNative(widget_, this); window_contents_ = gtk_fixed_new(); gtk_fixed_set_has_window(GTK_FIXED(window_contents_), true); gtk_container_add(GTK_CONTAINER(widget_), window_contents_); gtk_widget_show(window_contents_); SetViewForNative(window_contents_, this); if (transparent_) ConfigureWidgetForTransparentBackground(); } // The widget needs to be realized before handlers like size-allocate can // function properly. gtk_widget_realize(widget_); } void WidgetGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) { root_view_->SetBounds(0, 0, allocation->width, allocation->height); root_view_->SchedulePaint(); } gboolean WidgetGtk::OnMotionNotify(GtkWidget* widget, GdkEventMotion* event) { if (has_capture_ && is_mouse_down_) { last_mouse_event_was_move_ = false; int flags = Event::GetFlagsFromGdkState(event->state); if (event->state & GDK_BUTTON1_MASK) flags |= Event::EF_LEFT_BUTTON_DOWN; if (event->state & GDK_BUTTON2_MASK) flags |= Event::EF_MIDDLE_BUTTON_DOWN; if (event->state & GDK_BUTTON3_MASK) flags |= Event::EF_RIGHT_BUTTON_DOWN; MouseEvent mouse_drag(Event::ET_MOUSE_DRAGGED, event->x, event->y, flags); root_view_->OnMouseDragged(mouse_drag); return true; } gfx::Point screen_loc(event->x_root, event->y_root); if (last_mouse_event_was_move_ && last_mouse_move_x_ == screen_loc.x() && last_mouse_move_y_ == screen_loc.y()) { // Don't generate a mouse event for the same location as the last. return false; } last_mouse_move_x_ = screen_loc.x(); last_mouse_move_y_ = screen_loc.y(); last_mouse_event_was_move_ = true; int flags = Event::GetFlagsFromGdkState(event->state); if (event->state & GDK_BUTTON1_MASK) flags |= Event::EF_LEFT_BUTTON_DOWN; if (event->state & GDK_BUTTON2_MASK) flags |= Event::EF_MIDDLE_BUTTON_DOWN; if (event->state & GDK_BUTTON3_MASK) flags |= Event::EF_RIGHT_BUTTON_DOWN; MouseEvent mouse_move(Event::ET_MOUSE_MOVED, event->x, event->y, flags); root_view_->OnMouseMoved(mouse_move); return true; } gboolean WidgetGtk::OnButtonPress(GtkWidget* widget, GdkEventButton* event) { ProcessMousePressed(event); return true; } gboolean WidgetGtk::OnButtonRelease(GtkWidget* widget, GdkEventButton* event) { ProcessMouseReleased(event); return true; } gboolean WidgetGtk::OnFocusIn(GtkWidget* widget, GdkEventFocus* event) { if (type_ == TYPE_CHILD) return false; // The top-level window got focus, restore the last focused view. focus_manager_->RestoreFocusedView(); return false; } gboolean WidgetGtk::OnFocusOut(GtkWidget* widget, GdkEventFocus* event) { if (type_ == TYPE_CHILD) return false; // The top-level window lost focus, store the focused view. focus_manager_->StoreFocusedView(); return false; } void WidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) { root_view_->OnPaint(event); } void WidgetGtk::OnDragDataGet(GdkDragContext* context, GtkSelectionData* data, guint info, guint time) { if (!drag_data_) { NOTREACHED(); return; } drag_data_->WriteFormatToSelection(info, data); } void WidgetGtk::OnDragDataReceived(GdkDragContext* context, gint x, gint y, GtkSelectionData* data, guint info, guint time) { if (drop_target_.get()) drop_target_->OnDragDataReceived(context, x, y, data, info, time); } gboolean WidgetGtk::OnDragDrop(GdkDragContext* context, gint x, gint y, guint time) { if (drop_target_.get()) { return drop_target_->OnDragDrop(context, x, y, time); } return FALSE; } void WidgetGtk::OnDragEnd(GdkDragContext* context) { if (!drag_data_) { // This indicates we didn't start a drag operation, and should never // happen. NOTREACHED(); return; } // Quit the nested message loop we spawned in DoDrag. MessageLoop::current()->Quit(); } gboolean WidgetGtk::OnDragFailed(GdkDragContext* context, GtkDragResult result) { return FALSE; } void WidgetGtk::OnDragLeave(GdkDragContext* context, guint time) { if (ignore_drag_leave_) { ignore_drag_leave_ = false; return; } if (drop_target_.get()) { drop_target_->OnDragLeave(context, time); drop_target_.reset(NULL); } } gboolean WidgetGtk::OnDragMotion(GdkDragContext* context, gint x, gint y, guint time) { if (!drop_target_.get()) drop_target_.reset(new DropTargetGtk(GetRootView(), context)); return drop_target_->OnDragMotion(context, x, y, time); } gboolean WidgetGtk::OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event) { return false; } gboolean WidgetGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event) { last_mouse_event_was_move_ = false; if (!has_capture_ && !is_mouse_down_) root_view_->ProcessOnMouseExited(); return true; } gboolean WidgetGtk::OnKeyPress(GtkWidget* widget, GdkEventKey* event) { KeyEvent key_event(event, false); return root_view_->ProcessKeyEvent(key_event); } gboolean WidgetGtk::OnKeyRelease(GtkWidget* widget, GdkEventKey* event) { KeyEvent key_event(event, false); return root_view_->ProcessKeyEvent(key_event); } gboolean WidgetGtk::OnGrabBrokeEvent(GtkWidget* widget, GdkEvent* event) { HandleGrabBroke(); return false; // To let other widgets get the event. } void WidgetGtk::OnGrabNotify(GtkWidget* widget, gboolean was_grabbed) { gtk_grab_remove(window_contents_); HandleGrabBroke(); } void WidgetGtk::OnDestroy() { widget_ = window_contents_ = NULL; if (delete_on_destroy_) delete this; } void WidgetGtk::DoGrab() { has_capture_ = true; gtk_grab_add(window_contents_); } void WidgetGtk::ReleaseGrab() { if (has_capture_) { has_capture_ = false; gtk_grab_remove(window_contents_); } } // static WindowGtk* WidgetGtk::GetWindowForNative(GtkWidget* widget) { gpointer user_data = g_object_get_data(G_OBJECT(widget), "chrome-window"); return static_cast(user_data); } // static void WidgetGtk::SetWindowForNative(GtkWidget* widget, WindowGtk* window) { g_object_set_data(G_OBJECT(widget), "chrome-window", window); } RootView* WidgetGtk::CreateRootView() { return new RootView(this); } void WidgetGtk::OnWindowPaint(GtkWidget* widget, GdkEventExpose* event) { // NOTE: for reasons I don't understand this code is never hit. It should // be hit when transparent_, but we never get the expose-event for the // window in this case, even though a stand alone test case triggers it. I'm // leaving it in just in case. // Fill the background totally transparent. We don't need to paint the root // view here as that is done by OnPaint. DCHECK(transparent_); int width, height; gtk_window_get_size(GTK_WINDOW (widget), &width, &height); cairo_t* cr = gdk_cairo_create(widget->window); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 0, 0, 0, 0); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); } bool WidgetGtk::ProcessMousePressed(GdkEventButton* event) { if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) { // The sequence for double clicks is press, release, press, 2press, release. // This means that at the time we get the second 'press' we don't know // whether it corresponds to a double click or not. For now we're completely // ignoring the 2press/3press events as they are duplicate. To make this // work right we need to write our own code that detects if the press is a // double/triple. For now we're completely punting, which means we always // get single clicks. // TODO: fix this. return true; } last_mouse_event_was_move_ = false; MouseEvent mouse_pressed(Event::ET_MOUSE_PRESSED, event->x, event->y, GetFlagsForEventButton(*event)); if (root_view_->OnMousePressed(mouse_pressed)) { is_mouse_down_ = true; if (!has_capture_) DoGrab(); return true; } return false; } void WidgetGtk::ProcessMouseReleased(GdkEventButton* event) { last_mouse_event_was_move_ = false; MouseEvent mouse_up(Event::ET_MOUSE_RELEASED, event->x, event->y, GetFlagsForEventButton(*event)); // Release the capture first, that way we don't get confused if // OnMouseReleased blocks. if (has_capture_ && ReleaseCaptureOnMouseReleased()) ReleaseGrab(); is_mouse_down_ = false; // GTK generates a mouse release at the end of dnd. We need to ignore it. if (!drag_data_) root_view_->OnMouseReleased(mouse_up, false); } // static WidgetGtk* WidgetGtk::GetViewForNative(GtkWidget* widget) { gpointer user_data = g_object_get_data(G_OBJECT(widget), "chrome-views"); return static_cast(user_data); } void WidgetGtk::ResetDropTarget() { ignore_drag_leave_ = false; drop_target_.reset(NULL); } // static void WidgetGtk::SetViewForNative(GtkWidget* widget, WidgetGtk* view) { g_object_set_data(G_OBJECT(widget), "chrome-views", view); } // static RootView* WidgetGtk::GetRootViewForWidget(GtkWidget* widget) { gpointer user_data = g_object_get_data(G_OBJECT(widget), "root-view"); return static_cast(user_data); } // static void WidgetGtk::SetRootViewForWidget(GtkWidget* widget, RootView* root_view) { g_object_set_data(G_OBJECT(widget), "root-view", root_view); } // static void WidgetGtk::CallSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return; widget_gtk->OnSizeAllocate(widget, allocation); } // static gboolean WidgetGtk::CallPaint(GtkWidget* widget, GdkEventExpose* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (widget_gtk) widget_gtk->OnPaint(widget, event); return false; // False indicates other widgets should get the event as well. } // static gboolean WidgetGtk::CallWindowPaint(GtkWidget* widget, GdkEventExpose* event, WidgetGtk* widget_gtk) { widget_gtk->OnWindowPaint(widget, event); return false; // False indicates other widgets should get the event as well. } // static void WidgetGtk::CallDragDataGet(GtkWidget* widget, GdkDragContext* context, GtkSelectionData* data, guint info, guint time, WidgetGtk* host) { host->OnDragDataGet(context, data, info, time); } // static void WidgetGtk::CallDragDataReceived(GtkWidget* widget, GdkDragContext* context, gint x, gint y, GtkSelectionData* data, guint info, guint time, WidgetGtk* host) { return host->OnDragDataReceived(context, x, y, data, info, time); } // static gboolean WidgetGtk::CallDragDrop(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time, WidgetGtk* host) { return host->OnDragDrop(context, x, y, time); } // static void WidgetGtk::CallDragEnd(GtkWidget* widget, GdkDragContext* context, WidgetGtk* host) { host->OnDragEnd(context); } // static gboolean WidgetGtk::CallDragFailed(GtkWidget* widget, GdkDragContext* context, GtkDragResult result, WidgetGtk* host) { return host->OnDragFailed(context, result); } // static void WidgetGtk::CallDragLeave(GtkWidget* widget, GdkDragContext* context, guint time, WidgetGtk* host) { host->OnDragLeave(context, time); } // static gboolean WidgetGtk::CallDragMotion(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time, WidgetGtk* host) { return host->OnDragMotion(context, x, y, time); } gboolean WidgetGtk::CallEnterNotify(GtkWidget* widget, GdkEventCrossing* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnEnterNotify(widget, event); } gboolean WidgetGtk::CallLeaveNotify(GtkWidget* widget, GdkEventCrossing* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnLeaveNotify(widget, event); } gboolean WidgetGtk::CallMotionNotify(GtkWidget* widget, GdkEventMotion* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnMotionNotify(widget, event); } gboolean WidgetGtk::CallButtonPress(GtkWidget* widget, GdkEventButton* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnButtonPress(widget, event); } gboolean WidgetGtk::CallButtonRelease(GtkWidget* widget, GdkEventButton* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnButtonRelease(widget, event); } gboolean WidgetGtk::CallFocusIn(GtkWidget* widget, GdkEventFocus* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnFocusIn(widget, event); } gboolean WidgetGtk::CallFocusOut(GtkWidget* widget, GdkEventFocus* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnFocusOut(widget, event); } gboolean WidgetGtk::CallKeyPress(GtkWidget* widget, GdkEventKey* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnKeyPress(widget, event); } gboolean WidgetGtk::CallKeyRelease(GtkWidget* widget, GdkEventKey* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnKeyRelease(widget, event); } gboolean WidgetGtk::CallScroll(GtkWidget* widget, GdkEventScroll* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnScroll(widget, event); } gboolean WidgetGtk::CallVisibilityNotify(GtkWidget* widget, GdkEventVisibility* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnVisibilityNotify(widget, event); } // static gboolean WidgetGtk::CallGrabBrokeEvent(GtkWidget* widget, GdkEvent* event) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return false; return widget_gtk->OnGrabBrokeEvent(widget, event); } // static void WidgetGtk::CallGrabNotify(GtkWidget* widget, gboolean was_grabbed) { WidgetGtk* widget_gtk = GetViewForNative(widget); if (!widget_gtk) return; return widget_gtk->OnGrabNotify(widget, was_grabbed); } // static void WidgetGtk::CallDestroy(GtkObject* object) { WidgetGtk* widget_gtk = GetViewForNative(GTK_WIDGET(object)); if (widget_gtk) widget_gtk->OnDestroy(); } // static Window* WidgetGtk::GetWindowImpl(GtkWidget* widget) { GtkWidget* parent = widget; while (parent) { WidgetGtk* widget_gtk = GetViewForNative(parent); if (widget_gtk && widget_gtk->is_window_) return static_cast(widget_gtk); parent = gtk_widget_get_parent(parent); } return NULL; } void WidgetGtk::ConfigureWidgetForTransparentBackground() { DCHECK(widget_ && window_contents_ && widget_ != window_contents_); GdkColormap* rgba_colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default()); if (!rgba_colormap) { transparent_ = false; return; } // To make the background transparent we need to install the RGBA colormap // on both the window and fixed. In addition we need to turn off double // buffering and make sure no decorations are drawn. The last bit is to make // sure the widget doesn't attempt to draw a pixmap in it's background. gtk_widget_set_colormap(widget_, rgba_colormap); gtk_widget_set_app_paintable(widget_, true); GTK_WIDGET_UNSET_FLAGS(widget_, GTK_DOUBLE_BUFFERED); gtk_widget_realize(widget_); gdk_window_set_decorations(widget_->window, static_cast(0)); // Widget must be realized before setting pixmap. gdk_window_set_back_pixmap(widget_->window, NULL, FALSE); gtk_widget_set_colormap(window_contents_, rgba_colormap); gtk_widget_set_app_paintable(window_contents_, true); GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED); gtk_widget_realize(window_contents_); // Widget must be realized before setting pixmap. gdk_window_set_back_pixmap(window_contents_->window, NULL, FALSE); } void WidgetGtk::HandleGrabBroke() { if (has_capture_) { if (is_mouse_down_) root_view_->ProcessMouseDragCanceled(); is_mouse_down_ = false; has_capture_ = false; } } //////////////////////////////////////////////////////////////////////////////// // Widget, public: // static Widget* Widget::CreateTransparentPopupWidget(bool delete_on_destroy) { WidgetGtk* popup = new WidgetGtk(WidgetGtk::TYPE_POPUP); popup->set_delete_on_destroy(delete_on_destroy); popup->MakeTransparent(); return popup; } } // namespace views