1// Copyright (c) 2011 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/constrained_window_gtk.h"
6
7#include <gdk/gdkkeysyms.h>
8
9#include "chrome/browser/ui/browser_list.h"
10#include "chrome/browser/ui/gtk/gtk_util.h"
11#include "content/browser/browser_thread.h"
12#include "content/browser/tab_contents/tab_contents.h"
13
14#if defined(TOUCH_UI)
15#include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
16#else
17#include "chrome/browser/tab_contents/tab_contents_view_gtk.h"
18#endif
19
20ConstrainedWindowGtkDelegate::~ConstrainedWindowGtkDelegate() {
21}
22
23bool ConstrainedWindowGtkDelegate::GetBackgroundColor(GdkColor* color) {
24  return false;
25}
26
27bool ConstrainedWindowGtkDelegate::ShouldHaveBorderPadding() const {
28  return true;
29}
30
31ConstrainedWindowGtk::ConstrainedWindowGtk(
32    TabContents* owner, ConstrainedWindowGtkDelegate* delegate)
33    : owner_(owner),
34      delegate_(delegate),
35      visible_(false),
36      factory_(this) {
37  DCHECK(owner);
38  DCHECK(delegate);
39  GtkWidget* dialog = delegate->GetWidgetRoot();
40
41  // Unlike other users of CreateBorderBin, we need a dedicated frame around
42  // our "window".
43  GtkWidget* ebox = gtk_event_box_new();
44  GtkWidget* frame = gtk_frame_new(NULL);
45  gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
46
47  GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
48  if (delegate->ShouldHaveBorderPadding()) {
49    gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
50        gtk_util::kContentAreaBorder, gtk_util::kContentAreaBorder,
51        gtk_util::kContentAreaBorder, gtk_util::kContentAreaBorder);
52  }
53
54  GdkColor background;
55  if (delegate->GetBackgroundColor(&background)) {
56    gtk_widget_modify_base(ebox, GTK_STATE_NORMAL, &background);
57    gtk_widget_modify_fg(ebox, GTK_STATE_NORMAL, &background);
58    gtk_widget_modify_bg(ebox, GTK_STATE_NORMAL, &background);
59  }
60
61  if (gtk_widget_get_parent(dialog))
62    gtk_widget_reparent(dialog, alignment);
63  else
64    gtk_container_add(GTK_CONTAINER(alignment), dialog);
65
66  gtk_container_add(GTK_CONTAINER(frame), alignment);
67  gtk_container_add(GTK_CONTAINER(ebox), frame);
68  border_.Own(ebox);
69
70  gtk_widget_add_events(widget(), GDK_KEY_PRESS_MASK);
71  g_signal_connect(widget(), "key-press-event", G_CALLBACK(OnKeyPressThunk),
72                   this);
73  g_signal_connect(widget(), "hierarchy-changed",
74                   G_CALLBACK(OnHierarchyChangedThunk), this);
75}
76
77ConstrainedWindowGtk::~ConstrainedWindowGtk() {
78  border_.Destroy();
79}
80
81void ConstrainedWindowGtk::ShowConstrainedWindow() {
82  gtk_widget_show_all(border_.get());
83
84  // We collaborate with TabContentsView and stick ourselves in the
85  // TabContentsView's floating container.
86  ContainingView()->AttachConstrainedWindow(this);
87
88  visible_ = true;
89}
90
91void ConstrainedWindowGtk::CloseConstrainedWindow() {
92  if (visible_)
93    ContainingView()->RemoveConstrainedWindow(this);
94  delegate_->DeleteDelegate();
95  owner_->WillClose(this);
96
97  delete this;
98}
99
100void ConstrainedWindowGtk::FocusConstrainedWindow() {
101  GtkWidget* focus_widget = delegate_->GetFocusWidget();
102  if (!focus_widget)
103    return;
104
105  // The user may have focused another tab. In this case do not grab focus
106  // until this tab is refocused.
107  if ((!owner_->delegate() ||
108          owner_->delegate()->ShouldFocusConstrainedWindow()) &&
109      gtk_util::IsWidgetAncestryVisible(focus_widget)) {
110    gtk_widget_grab_focus(focus_widget);
111  } else {
112  // TODO(estade): this define should not need to be here because this class
113  // should not be used on linux/views.
114#if defined(TOOLKIT_GTK)
115    static_cast<TabContentsViewGtk*>(owner_->view())->
116        SetFocusedWidget(focus_widget);
117#endif
118  }
119}
120
121ConstrainedWindowGtk::TabContentsViewType*
122    ConstrainedWindowGtk::ContainingView() {
123  return static_cast<TabContentsViewType*>(owner_->view());
124}
125
126gboolean ConstrainedWindowGtk::OnKeyPress(GtkWidget* sender,
127                                          GdkEventKey* key) {
128  if (key->keyval == GDK_Escape) {
129    // Let the stack unwind so the event handler can release its ref
130    // on widget().
131    MessageLoop::current()->PostTask(FROM_HERE,
132        factory_.NewRunnableMethod(
133            &ConstrainedWindowGtk::CloseConstrainedWindow));
134    return TRUE;
135  }
136
137  return FALSE;
138}
139
140void ConstrainedWindowGtk::OnHierarchyChanged(GtkWidget* sender,
141                                              GtkWidget* previous_toplevel) {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
143  if (!GTK_WIDGET_TOPLEVEL(gtk_widget_get_toplevel(widget())))
144    return;
145
146  FocusConstrainedWindow();
147}
148
149// static
150ConstrainedWindow* ConstrainedWindow::CreateConstrainedDialog(
151    TabContents* parent,
152    ConstrainedWindowGtkDelegate* delegate) {
153  return new ConstrainedWindowGtk(parent, delegate);
154}
155