// 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.

#ifndef VIEWS_WIDGET_NATIVE_WIDGET_GTK_H_
#define VIEWS_WIDGET_NATIVE_WIDGET_GTK_H_
#pragma once

#include <gtk/gtk.h>

#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
#include "ui/base/gtk/gtk_signal.h"
#include "ui/base/x/active_window_watcher_x.h"
#include "ui/gfx/compositor/compositor.h"
#include "ui/gfx/size.h"
#include "views/focus/focus_manager.h"
#include "views/widget/native_widget_private.h"
#include "views/widget/widget.h"

namespace gfx {
class Rect;
}

namespace ui {
class OSExchangeData;
class OSExchangeDataProviderGtk;
}
using ui::OSExchangeData;
using ui::OSExchangeDataProviderGtk;

namespace views {

class DropTargetGtk;
class InputMethod;
class TooltipManagerGtk;
class View;

namespace internal {
class NativeWidgetDelegate;
}

// Widget implementation for GTK.
class VIEWS_EXPORT NativeWidgetGtk : public internal::NativeWidgetPrivate,
                                     public ui::CompositorDelegate,
                                     public ui::ActiveWindowWatcherX::Observer {
 public:
  explicit NativeWidgetGtk(internal::NativeWidgetDelegate* delegate);
  virtual ~NativeWidgetGtk();

  // 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_; }

  bool is_ignore_events() const { return ignore_events_; }

  // 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 NativeWidgetGtk
  // 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);

  // Invoked when the active status changes.
  virtual void OnActiveChanged();

  // Sets the drop target to NULL. This is invoked by DropTargetGTK when the
  // drop is done.
  void ResetDropTarget();

  // Gets the requested size of the widget.  This can be the size
  // stored in properties for a GtkViewsFixed, or in the requisitioned
  // size of other kinds of widgets.
  void GetRequestedSize(gfx::Size* out) const;

  // Overridden from ui::ActiveWindowWatcherX::Observer.
  virtual void ActiveWindowChanged(GdkWindow* active_window) OVERRIDE;

  // Handles a keyboard event by sending it to our focus manager.
  // Returns true if it's handled by the focus manager.
  bool HandleKeyboardEvent(const KeyEvent& key);

  // Tells widget not to remove FREEZE_UPDATES property when the
  // widget is painted.  This is used if painting the gtk widget
  // is not enough to show the window and has to wait further like
  // keyboard overlay. Returns true if this is called before
  // FREEZE_UPDATES property is removed, or false otherwise.
  bool SuppressFreezeUpdates();

  // Sets and deletes FREEZE_UPDATES property on given |window|.
  // It adds the property when |enable| is true and remove if false.
  // Calling this method will realize the window if it's not realized yet.
  // This property is used to help WindowManager know when the window
  // is fully painted so that WM can map the fully painted window.
  // The property is based on Owen Taylor's proposal at
  // http://mail.gnome.org/archives/wm-spec-list/2009-June/msg00002.html.
  // This is just a hint to WM, and won't change the behavior for WM
  // which does not support this property.
  static void UpdateFreezeUpdatesProperty(GtkWindow* window, bool enable);

  // Registers a expose handler that removes FREEZE_UPDATES property.
  // If you are adding a GtkWidget with its own GdkWindow that may
  // fill the entire area of the NativeWidgetGtk to the view hierachy, you
  // need use this function to tell WM that when the widget is ready
  // to be shown.
  // Caller of this method do not need to disconnect this because the
  // handler will be removed upon the first invocation of the handler,
  // or when the widget is re-attached, and expose won't be emitted on
  // detached widget.
  static void RegisterChildExposeHandler(GtkWidget* widget);

  // Overridden from internal::NativeWidgetPrivate:
  virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE;
  virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE;
  virtual void UpdateFrameAfterFrameChange() OVERRIDE;
  virtual bool ShouldUseNativeFrame() const OVERRIDE;
  virtual void FrameTypeChanged() OVERRIDE;
  virtual Widget* GetWidget() OVERRIDE;
  virtual const Widget* GetWidget() const OVERRIDE;
  virtual gfx::NativeView GetNativeView() const OVERRIDE;
  virtual gfx::NativeWindow GetNativeWindow() const OVERRIDE;
  virtual Widget* GetTopLevelWidget() OVERRIDE;
  virtual const ui::Compositor* GetCompositor() const OVERRIDE;
  virtual ui::Compositor* GetCompositor() OVERRIDE;
  virtual void MarkLayerDirty() OVERRIDE;
  virtual void CalculateOffsetToAncestorWithLayer(gfx::Point* offset,
                                                  View** ancestor) OVERRIDE;
  virtual void ViewRemoved(View* view) OVERRIDE;
  virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE;
  virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE;
  virtual TooltipManager* GetTooltipManager() const OVERRIDE;
  virtual bool IsScreenReaderActive() const OVERRIDE;
  virtual void SendNativeAccessibilityEvent(
      View* view,
      ui::AccessibilityTypes::Event event_type) OVERRIDE;
  virtual void SetMouseCapture() OVERRIDE;
  virtual void ReleaseMouseCapture() OVERRIDE;
  virtual bool HasMouseCapture() const OVERRIDE;
  virtual InputMethod* CreateInputMethod() OVERRIDE;
  virtual void CenterWindow(const gfx::Size& size) OVERRIDE;
  virtual void GetWindowPlacement(
      gfx::Rect* bounds,
      ui::WindowShowState* show_state) const OVERRIDE;
  virtual void SetWindowTitle(const std::wstring& title) OVERRIDE;
  virtual void SetWindowIcons(const SkBitmap& window_icon,
                              const SkBitmap& app_icon) OVERRIDE;
  virtual void SetAccessibleName(const std::wstring& name) OVERRIDE;
  virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) OVERRIDE;
  virtual void SetAccessibleState(ui::AccessibilityTypes::State state) OVERRIDE;
  virtual void BecomeModal() OVERRIDE;
  virtual gfx::Rect GetWindowScreenBounds() const OVERRIDE;
  virtual gfx::Rect GetClientAreaScreenBounds() const OVERRIDE;
  virtual gfx::Rect GetRestoredBounds() const OVERRIDE;
  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
  virtual void SetSize(const gfx::Size& size) OVERRIDE;
  virtual void SetBoundsConstrained(const gfx::Rect& bounds,
                                    Widget* other_widget) OVERRIDE;
  virtual void MoveAbove(gfx::NativeView native_view) OVERRIDE;
  virtual void MoveToTop() OVERRIDE;
  virtual void SetShape(gfx::NativeRegion shape) OVERRIDE;
  virtual void Close() OVERRIDE;
  virtual void CloseNow() OVERRIDE;
  virtual void EnableClose(bool enable) OVERRIDE;
  virtual void Show() OVERRIDE;
  virtual void Hide() OVERRIDE;
  virtual void ShowMaximizedWithBounds(
      const gfx::Rect& restored_bounds) OVERRIDE;
  virtual void ShowWithWindowState(ui::WindowShowState window_state) OVERRIDE;
  virtual bool IsVisible() const OVERRIDE;
  virtual void Activate() OVERRIDE;
  virtual void Deactivate() OVERRIDE;
  virtual bool IsActive() const OVERRIDE;
  virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE;
  virtual void Maximize() OVERRIDE;
  virtual void Minimize() OVERRIDE;
  virtual bool IsMaximized() const OVERRIDE;
  virtual bool IsMinimized() const OVERRIDE;
  virtual void Restore() OVERRIDE;
  virtual void SetFullscreen(bool fullscreen) OVERRIDE;
  virtual bool IsFullscreen() const OVERRIDE;
  virtual void SetOpacity(unsigned char opacity) OVERRIDE;
  virtual void SetUseDragFrame(bool use_drag_frame) OVERRIDE;
  virtual bool IsAccessibleWidget() const OVERRIDE;
  virtual void RunShellDrag(View* view,
                            const ui::OSExchangeData& data,
                            int operation) OVERRIDE;
  virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE;
  virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
  virtual void ClearNativeFocus() OVERRIDE;
  virtual void FocusNativeView(gfx::NativeView native_view) OVERRIDE;
  virtual bool ConvertPointFromAncestor(
      const Widget* ancestor, gfx::Point* point) const OVERRIDE;

 protected:
  // Modifies event coordinates to the targeted widget contained by this widget.
  template<class Event> GdkEvent* TransformEvent(Event* event) {
    GdkWindow* dest = GTK_WIDGET(window_contents_)->window;
    if (event && event->window != dest) {
      gint dest_x, dest_y;
      gdk_window_get_root_origin(dest, &dest_x, &dest_y);
      event->x = event->x_root - dest_x;
      event->y = event->y_root - dest_y;
    }
    return reinterpret_cast<GdkEvent*>(event);
  }

  // Event handlers:
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnButtonPress,
                       GdkEventButton*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, void, OnSizeRequest, GtkRequisition*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, void, OnSizeAllocate, GtkAllocation*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnPaint, GdkEventExpose*);
  CHROMEGTK_CALLBACK_4(NativeWidgetGtk, void, OnDragDataGet,
                       GdkDragContext*, GtkSelectionData*, guint, guint);
  CHROMEGTK_CALLBACK_6(NativeWidgetGtk, void, OnDragDataReceived,
                       GdkDragContext*, gint, gint, GtkSelectionData*,
                       guint, guint);
  CHROMEGTK_CALLBACK_4(NativeWidgetGtk, gboolean, OnDragDrop,
                       GdkDragContext*, gint, gint, guint);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, void, OnDragEnd, GdkDragContext*);
  CHROMEGTK_CALLBACK_2(NativeWidgetGtk, gboolean, OnDragFailed,
                       GdkDragContext*, GtkDragResult);
  CHROMEGTK_CALLBACK_2(NativeWidgetGtk, void, OnDragLeave,
                       GdkDragContext*, guint);
  CHROMEGTK_CALLBACK_4(NativeWidgetGtk, gboolean, OnDragMotion,
                       GdkDragContext*, gint, gint, guint);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnEnterNotify,
                       GdkEventCrossing*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnLeaveNotify,
                       GdkEventCrossing*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnMotionNotify,
                       GdkEventMotion*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnButtonRelease,
                       GdkEventButton*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnFocusIn, GdkEventFocus*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnFocusOut, GdkEventFocus*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnEventKey, GdkEventKey*);
  CHROMEGTK_CALLBACK_4(NativeWidgetGtk, gboolean, OnQueryTooltip,
                       gint, gint, gboolean, GtkTooltip*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnScroll, GdkEventScroll*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnVisibilityNotify,
                       GdkEventVisibility*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnGrabBrokeEvent, GdkEvent*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, void, OnGrabNotify, gboolean);
  CHROMEGTK_CALLBACK_0(NativeWidgetGtk, void, OnDestroy);
  CHROMEGTK_CALLBACK_0(NativeWidgetGtk, void, OnShow);
  CHROMEGTK_CALLBACK_0(NativeWidgetGtk, void, OnMap);
  CHROMEGTK_CALLBACK_0(NativeWidgetGtk, void, OnHide);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnWindowStateEvent,
                       GdkEventWindowState*);
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnConfigureEvent,
                       GdkEventConfigure*);

  // Invoked when the widget is destroyed and right before the object
  // destruction. Useful for overriding.
  virtual void OnDestroyed(GObject *where_the_object_was);
  static void OnDestroyedThunk(gpointer data, GObject *where_the_object_was) {
    reinterpret_cast<NativeWidgetGtk*>(data)->OnDestroyed(where_the_object_was);
  }

  // Invoked when gtk grab is stolen by other GtkWidget in the same
  // application.
  virtual void HandleGtkGrabBroke();

  const internal::NativeWidgetDelegate* delegate() const { return delegate_; }
  internal::NativeWidgetDelegate* delegate() { return delegate_; }

 private:
  class DropObserver;
  friend class DropObserver;

  // Overridden from ui::CompositorDelegate
  virtual void ScheduleCompositorPaint();

  // Overridden from internal::InputMethodDelegate
  virtual void DispatchKeyEventPostIME(const KeyEvent& key) OVERRIDE;

  void SetInitParams(const Widget::InitParams& params);

  // This is called only when the window is transparent.
  CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnWindowPaint,
                       GdkEventExpose*);

  // Callbacks for expose event on child widgets. See the description of
  // RegisterChildChildExposeHandler.
  void OnChildExpose(GtkWidget* child);
  static gboolean ChildExposeHandler(GtkWidget* widget, GdkEventExpose* event);

  // Creates the GtkWidget.
  void CreateGtkWidget(const Widget::InitParams& params);

  // 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.
  void ConfigureWidgetForIgnoreEvents();

  // A utility function to draw a transparent background onto the |widget|.
  static void DrawTransparentBackground(GtkWidget* widget,
                                        GdkEventExpose* event);

  // Asks the delegate if any to save the window's location and size.
  void SaveWindowPosition();

  // A delegate implementation that handles events received here.
  // See class documentation for Widget in widget.h for a note about ownership.
  internal::NativeWidgetDelegate* delegate_;

  // 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_;

  // True if the widget is a child of some other widget.
  bool child_;

  // The TooltipManager.
  // WARNING: RootView's destructor calls into the TooltipManager. As such, this
  // must be destroyed AFTER root_view_.
  scoped_ptr<TooltipManager> tooltip_manager_;

  scoped_ptr<DropTargetGtk> drop_target_;

  // The following factory is used to delay destruction.
  ScopedRunnableMethodFactory<NativeWidgetGtk> close_widget_factory_;

  // See class documentation for Widget in widget.h for a note about ownership.
  Widget::InitParams::Ownership ownership_;

  // See description above make_transparent for details.
  bool transparent_;

  // Makes the window pass all events through to any windows behind it.
  // Set during SetInitParams before the widget is created. The actual work of
  // making the window ignore events is done by ConfigureWidgetForIgnoreEvents.
  bool ignore_events_;

  // 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_;

  // True to enable debug painting. Enabling causes the damaged
  // region to be painted to flash in red.
  static bool debug_paint_enabled_;

  // State of the window, such as fullscreen, hidden...
  GdkWindowState window_state_;

  // 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_;

  // 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_;

  // Indicates if we should handle the upcoming Alt key release event.
  bool should_handle_menu_key_release_;

  // Valid for the lifetime of StartDragForViewFromMouseEvent, indicates the
  // view the drag started from.
  View* dragged_view_;

  // If the widget has ever been painted. This is used to guarantee
  // that window manager shows the window only after the window is painted.
  bool painted_;

  // The compositor for accelerated drawing.
  scoped_refptr<ui::Compositor> compositor_;

  // Have we done a pointer grab?
  bool has_pointer_grab_;

  // Have we done a keyboard grab?
  bool has_keyboard_grab_;

  // ID of the 'grab-notify' signal. If non-zero we're listening for
  // 'grab-notify' events.
  glong grab_notify_signal_id_;

  // If we were created for a menu.
  bool is_menu_;

  DISALLOW_COPY_AND_ASSIGN(NativeWidgetGtk);
};

}  // namespace views

#endif  // VIEWS_WIDGET_NATIVE_WIDGET_GTK_H_