// 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. #ifndef VIEWS_WIDGET_WIDGET_GTK_H_ #define VIEWS_WIDGET_WIDGET_GTK_H_ #include #include "app/active_window_watcher_x.h" #include "app/gtk_signal.h" #include "base/message_loop.h" #include "gfx/size.h" #include "views/focus/focus_manager.h" #include "views/widget/widget.h" class OSExchangeData; class OSExchangeDataProviderGtk; namespace gfx { class Rect; } namespace views { class DefaultThemeProvider; class DropTargetGtk; class TooltipManagerGtk; class View; class WindowGtk; // Widget implementation for GTK. class WidgetGtk : public Widget, public FocusTraversable, public ActiveWindowWatcherX::Observer { public: // Type of widget. enum Type { // Used for popup type windows (bubbles, menus ...). // NOTE: on X windows of this type can NOT get focus. If you need a popup // like widget that can be focused use TYPE_WINDOW and set the window type // to WINDOW_TYPE_CHROME_INFO_BUBBLE. TYPE_POPUP, // A top level window with no title or control buttons. TYPE_WINDOW, // A top level, decorated window. TYPE_DECORATED_WINDOW, // A child widget. TYPE_CHILD }; explicit WidgetGtk(Type type); virtual ~WidgetGtk(); // Marks this window as transient to its parent. A window that is transient // to its parent results in the parent rendering active when the child is // active. // This must be invoked before Init. This is only used for types other than // TYPE_CHILD. The default is false. // See gtk_window_set_transient_for for details. void make_transient_to_parent() { DCHECK(!widget_); transient_to_parent_ = true; } // Returns the transient parent. See make_transient_to_parent for details on // what the transient parent is. GtkWindow* GetTransientParent() const; // Makes the background of the window totally transparent. This must be // invoked before Init. This does a couple of checks and returns true if // the window can be made transparent. The actual work of making the window // transparent is done by ConfigureWidgetForTransparentBackground. // This works for both child and window types. bool MakeTransparent(); bool is_transparent() const { return transparent_; } // Enable/Disable double buffering.This is neccessary to prevent // flickering in ScrollView, which has both native and view // controls. void EnableDoubleBuffer(bool enabled); bool is_double_buffered() const { return is_double_buffered_; } // Makes the window pass all events through to any windows behind it. // This must be invoked before Init. This does a couple of checks and returns // true if the window can be made to ignore events. The actual work of making // the window ignore events is done by ConfigureWidgetForIgnoreEvents. bool MakeIgnoreEvents(); bool is_ignore_events() const { return ignore_events_; } // Sets whether or not we are deleted when the widget is destroyed. The // default is true. void set_delete_on_destroy(bool delete_on_destroy) { delete_on_destroy_ = delete_on_destroy; } // Adds and removes the specified widget as a child of this widget's contents. // These methods make sure to add the widget to the window's contents // container if this widget is a window. void AddChild(GtkWidget* child); void RemoveChild(GtkWidget* child); // A safe way to reparent a child widget to this widget. Calls // gtk_widget_reparent which handles refcounting to avoid destroying the // widget when removing it from its old parent. void ReparentChild(GtkWidget* child); // Positions a child GtkWidget at the specified location and bounds. void PositionChild(GtkWidget* child, int x, int y, int w, int h); // Parent GtkWidget all children are added to. When this WidgetGtk corresponds // to a top level window, this is the GtkFixed within the GtkWindow, not the // GtkWindow itself. For child widgets, this is the same GtkFixed as // |widget_|. GtkWidget* window_contents() const { return window_contents_; } // Starts a drag on this widget. This blocks until the drag is done. void DoDrag(const OSExchangeData& data, int operation); // Are we in PaintNow? See use in root_view_gtk for details on what this is // used for. bool in_paint_now() const { return in_paint_now_; } // Sets the focus traversable parents. void SetFocusTraversableParent(FocusTraversable* parent); void SetFocusTraversableParentView(View* parent_view); // Invoked when the active status changes. virtual void IsActiveChanged(); // Sets initial focus on a new window. On X11/Gtk, window creation // is asynchronous and a focus request has to be made after a window // gets created. This will not be called on a TYPE_CHILD widget. virtual void SetInitialFocus() {} // Gets the WidgetGtk in the userdata section of the widget. static WidgetGtk* GetViewForNative(GtkWidget* widget); // Gets the WindowGtk in the userdata section of the widget. // TODO(beng): move to WindowGtk static WindowGtk* GetWindowForNative(GtkWidget* widget); // Sets the drop target to NULL. This is invoked by DropTargetGTK when the // drop is done. void ResetDropTarget(); // Returns the RootView for |widget|. static RootView* GetRootViewForWidget(GtkWidget* widget); // Overriden from ActiveWindowWatcherX::Observer. virtual void ActiveWindowChanged(GdkWindow* active_window); // Overridden from Widget: virtual void Init(gfx::NativeView parent, const gfx::Rect& bounds); virtual WidgetDelegate* GetWidgetDelegate(); virtual void SetWidgetDelegate(WidgetDelegate* delegate); virtual void SetContentsView(View* view); virtual void GetBounds(gfx::Rect* out, bool including_frame) const; virtual void SetBounds(const gfx::Rect& bounds); virtual void MoveAbove(Widget* other); virtual void SetShape(gfx::NativeRegion region); virtual void Close(); virtual void CloseNow(); virtual void Show(); virtual void Hide(); virtual gfx::NativeView GetNativeView() const; virtual void PaintNow(const gfx::Rect& update_rect); virtual void SetOpacity(unsigned char opacity); virtual void SetAlwaysOnTop(bool on_top); virtual RootView* GetRootView(); virtual Widget* GetRootWidget() const; virtual bool IsVisible() const; virtual bool IsActive() const; virtual void GenerateMousePressedForView(View* view, const gfx::Point& point); virtual TooltipManager* GetTooltipManager(); virtual bool GetAccelerator(int cmd_id, menus::Accelerator* accelerator); virtual Window* GetWindow(); virtual const Window* GetWindow() const; virtual void SetNativeWindowProperty(const std::wstring& name, void* value); virtual void* GetNativeWindowProperty(const std::wstring& name); virtual ThemeProvider* GetThemeProvider() const; virtual ThemeProvider* GetDefaultThemeProvider() const; virtual FocusManager* GetFocusManager(); virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child); virtual bool ContainsNativeView(gfx::NativeView native_view); // Overridden from FocusTraversable: virtual View* FindNextFocusableView(View* starting_view, bool reverse, Direction direction, bool check_starting_view, FocusTraversable** focus_traversable, View** focus_traversable_view); virtual FocusTraversable* GetFocusTraversableParent(); virtual View* GetFocusTraversableParentView(); protected: // If widget containes another widget, translates event coordinates to the // contained widget's coordinates, else returns original event coordinates. template bool GetContainedWidgetEventCoordinates(Event* event, int* x, int* y) { if (event == NULL || x == NULL || y == NULL) return false; *x = event->x; *y = event->y; GdkWindow* dest = GTK_WIDGET(window_contents_)->window; if (event->window != dest) { int dest_x, dest_y; gdk_window_get_root_origin(dest, &dest_x, &dest_y); *x = event->x_root - dest_x; *y = event->y_root - dest_y; return true; } return false; } // Returns the view::Event::flags for a GdkEventButton. static int GetFlagsForEventButton(const GdkEventButton& event); // Event handlers: CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnButtonPress, GdkEventButton*); CHROMEGTK_CALLBACK_1(WidgetGtk, void, OnSizeAllocate, GtkAllocation*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnPaint, GdkEventExpose*); CHROMEGTK_CALLBACK_4(WidgetGtk, void, OnDragDataGet, GdkDragContext*, GtkSelectionData*, guint, guint); CHROMEGTK_CALLBACK_6(WidgetGtk, void, OnDragDataReceived, GdkDragContext*, gint, gint, GtkSelectionData*, guint, guint); CHROMEGTK_CALLBACK_4(WidgetGtk, gboolean, OnDragDrop, GdkDragContext*, gint, gint, guint); CHROMEGTK_CALLBACK_1(WidgetGtk, void, OnDragEnd, GdkDragContext*); CHROMEGTK_CALLBACK_2(WidgetGtk, gboolean, OnDragFailed, GdkDragContext*, GtkDragResult); CHROMEGTK_CALLBACK_2(WidgetGtk, void, OnDragLeave, GdkDragContext*, guint); CHROMEGTK_CALLBACK_4(WidgetGtk, gboolean, OnDragMotion, GdkDragContext*, gint, gint, guint); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnEnterNotify, GdkEventCrossing*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnLeaveNotify, GdkEventCrossing*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnMotionNotify, GdkEventMotion*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnButtonRelease, GdkEventButton*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnFocusIn, GdkEventFocus*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnFocusOut, GdkEventFocus*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyPress, GdkEventKey*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyRelease, GdkEventKey*); CHROMEGTK_CALLBACK_4(WidgetGtk, gboolean, OnQueryTooltip, gint, gint, gboolean, GtkTooltip*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnScroll, GdkEventScroll*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnVisibilityNotify, GdkEventVisibility*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnGrabBrokeEvent, GdkEvent*); CHROMEGTK_CALLBACK_1(WidgetGtk, void, OnGrabNotify, gboolean); CHROMEGTK_CALLBACK_0(WidgetGtk, void, OnDestroy); CHROMEGTK_CALLBACK_0(WidgetGtk, void, OnShow); CHROMEGTK_CALLBACK_0(WidgetGtk, void, OnHide); void set_mouse_down(bool mouse_down) { is_mouse_down_ = mouse_down; } // Do we own the mouse grab? bool has_capture() const { return has_capture_; } // Returns whether capture should be released on mouse release. The default // is true. virtual bool ReleaseCaptureOnMouseReleased() { return true; } // Does a mouse grab on this widget. void DoGrab(); // Releases a grab done by this widget. virtual void ReleaseGrab(); // Sets the WindowGtk in the userdata section of the widget. static void SetWindowForNative(GtkWidget* widget, WindowGtk* window); // Are we a subclass of WindowGtk? bool is_window_; // For test code to provide a customized focus manager. void set_focus_manager(FocusManager* focus_manager) { delete focus_manager_; focus_manager_ = focus_manager; } private: class DropObserver; friend class DropObserver; virtual RootView* CreateRootView(); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnWindowPaint, GdkEventExpose*); // Process a mouse click. bool ProcessMousePressed(GdkEventButton* event); void ProcessMouseReleased(GdkEventButton* event); // Process scroll event. bool ProcessScroll(GdkEventScroll* event); static void SetRootViewForWidget(GtkWidget* widget, RootView* root_view); // Returns the first ancestor of |widget| that is a window. static Window* GetWindowImpl(GtkWidget* widget); // Creates the GtkWidget. void CreateGtkWidget(GtkWidget* parent, const gfx::Rect& bounds); // Invoked from create widget to enable the various bits needed for a // transparent background. This is only invoked if MakeTransparent has been // invoked. void ConfigureWidgetForTransparentBackground(GtkWidget* parent); // Invoked from create widget to enable the various bits needed for a // window which doesn't receive events. This is only invoked if // MakeIgnoreEvents has been invoked. void ConfigureWidgetForIgnoreEvents(); // TODO(sky): documentation void HandleGrabBroke(); // A utility function to draw a transparent background onto the |widget|. static void DrawTransparentBackground(GtkWidget* widget, GdkEventExpose* event); const Type type_; // Our native views. If we're a window/popup, then widget_ is the window and // window_contents_ is a GtkFixed. If we're not a window/popup, then widget_ // and window_contents_ point to the same GtkFixed. GtkWidget* widget_; GtkWidget* window_contents_; // Child GtkWidgets created with no parent need to be parented to a valid top // level window otherwise Gtk throws a fit. |null_parent_| is an invisible // popup that such GtkWidgets are parented to. static GtkWidget* null_parent_; // The TooltipManager. // WARNING: RootView's destructor calls into the TooltipManager. As such, this // must be destroyed AFTER root_view_. scoped_ptr tooltip_manager_; scoped_ptr drop_target_; // The focus manager keeping track of focus for this Widget and any of its // children. NULL for non top-level widgets. // WARNING: RootView's destructor calls into the FocusManager. As such, this // must be destroyed AFTER root_view_. FocusManager* focus_manager_; // The root of the View hierarchy attached to this window. scoped_ptr root_view_; // If true, the mouse is currently down. bool is_mouse_down_; // Have we done a mouse grab? bool has_capture_; // The following are used to detect duplicate mouse move events and not // deliver them. Displaying a window may result in the system generating // duplicate move events even though the mouse hasn't moved. // If true, the last event was a mouse move event. bool last_mouse_event_was_move_; // Coordinates of the last mouse move event, in screen coordinates. int last_mouse_move_x_; int last_mouse_move_y_; // The following factory is used to delay destruction. ScopedRunnableMethodFactory close_widget_factory_; // See description above setter. bool delete_on_destroy_; // See description above make_transparent for details. bool transparent_; // See description above MakeIgnoreEvents for details. bool ignore_events_; scoped_ptr default_theme_provider_; // See note in DropObserver for details on this. bool ignore_drag_leave_; unsigned char opacity_; // This is non-null during the life of DoDrag and contains the actual data // for the drag. const OSExchangeDataProviderGtk* drag_data_; // See description above getter for details. bool in_paint_now_; // Are we active? bool is_active_; // See make_transient_to_parent for a description. bool transient_to_parent_; // Last size supplied to OnSizeAllocate. We cache this as any time the // size of a GtkWidget changes size_allocate is called, even if the size // didn't change. If we didn't cache this and ignore calls when the size // hasn't changed, we can end up getting stuck in a never ending loop. gfx::Size size_; // This is initially false and when the first focus-in event is received this // is set to true and no additional processing is done. Subsequently when // focus-in is received we do the normal focus manager processing. // // This behavior is necessitated by Gtk/X sending focus events // asynchronously. The initial sequence for windows is typically: show, // request focus on some widget. Because of async events on Gtk this becomes // show, request focus, get focus in event which ends up clearing focus // (first request to FocusManager::RestoreFocusedView ends up clearing focus). bool got_initial_focus_in_; // If true, we've received a focus-in event. If false we've received a // focus-out event. We can get multiple focus-out events in a row, we use // this to determine whether we should process the event. bool has_focus_; // Non owned pointer to optional delegate. May be NULL if no delegate is // being used. WidgetDelegate* delegate_; // If true, the window stays on top of the screen. This is only used // for types other than TYPE_CHILD. bool always_on_top_; // If true, we enable the content widget's double buffering. // This is false by default. bool is_double_buffered_; DISALLOW_COPY_AND_ASSIGN(WidgetGtk); }; } // namespace views #endif // VIEWS_WIDGET_WIDGET_GTK_H_