- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / panels / panel_stack_window_gtk.cc
1 // Copyright (c) 2013 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/panels/panel_stack_window_gtk.h"
6
7 #include <gdk/gdkkeysyms.h>
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/ui/panels/panel.h"
10 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
11 #include "ui/base/x/active_window_watcher_x.h"
12
13 // static
14 NativePanelStackWindow* NativePanelStackWindow::Create(
15     NativePanelStackWindowDelegate* delegate) {
16   return new PanelStackWindowGtk(delegate);
17 }
18
19 PanelStackWindowGtk::PanelStackWindowGtk(
20    NativePanelStackWindowDelegate* delegate)
21    : delegate_(delegate),
22      window_(NULL),
23      is_minimized_(false),
24      bounds_updates_started_(false) {
25   ui::ActiveWindowWatcherX::AddObserver(this);
26 }
27
28 PanelStackWindowGtk::~PanelStackWindowGtk() {
29   ui::ActiveWindowWatcherX::RemoveObserver(this);
30 }
31
32 void PanelStackWindowGtk::Close() {
33   if (!window_)
34     return;
35   gtk_widget_destroy(GTK_WIDGET(window_));
36   window_ = NULL;
37 }
38
39 void PanelStackWindowGtk::AddPanel(Panel* panel) {
40   panels_.push_back(panel);
41
42   EnsureWindowCreated();
43   SetStackWindowBounds();
44
45   // The panel being stacked should not appear on the taskbar.
46   gtk_window_set_skip_taskbar_hint(panel->GetNativeWindow(), true);
47 }
48
49 void PanelStackWindowGtk::RemovePanel(Panel* panel) {
50   panels_.remove(panel);
51
52   SetStackWindowBounds();
53
54   // The panel being unstacked should re-appear on the taskbar.
55   // Note that the underlying gtk window is gone when the panel is being
56   // closed.
57   GtkWindow* gtk_window = panel->GetNativeWindow();
58   if (gtk_window)
59     gtk_window_set_skip_taskbar_hint(gtk_window, false);
60 }
61
62 void PanelStackWindowGtk::MergeWith(NativePanelStackWindow* another) {
63   PanelStackWindowGtk* another_stack =
64       static_cast<PanelStackWindowGtk*>(another);
65   for (Panels::const_iterator iter = another_stack->panels_.begin();
66        iter != another_stack->panels_.end(); ++iter) {
67     Panel* panel = *iter;
68     panels_.push_back(panel);
69   }
70   another_stack->panels_.clear();
71
72   SetStackWindowBounds();
73 }
74
75 bool PanelStackWindowGtk::IsEmpty() const {
76   return panels_.empty();
77 }
78
79 bool PanelStackWindowGtk::HasPanel(Panel* panel) const {
80   return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
81 }
82
83 void PanelStackWindowGtk::MovePanelsBy(const gfx::Vector2d& delta) {
84   for (Panels::const_iterator iter = panels_.begin();
85        iter != panels_.end(); ++iter) {
86     Panel* panel = *iter;
87     gfx::Rect bounds = panel->GetBounds();
88     bounds.Offset(delta);
89     panel->SetPanelBoundsInstantly(bounds);
90   }
91
92   SetStackWindowBounds();
93 }
94
95 void PanelStackWindowGtk::BeginBatchUpdatePanelBounds(bool animate) {
96   // Bounds animation is not supported on GTK.
97   bounds_updates_started_ = true;
98 }
99
100 void PanelStackWindowGtk::AddPanelBoundsForBatchUpdate(
101     Panel* panel, const gfx::Rect& new_bounds) {
102   DCHECK(bounds_updates_started_);
103
104   // No need to track it if no change is needed.
105   if (panel->GetBounds() == new_bounds)
106     return;
107
108   // New bounds are stored as the map value.
109   bounds_updates_[panel] = new_bounds;
110 }
111
112 void PanelStackWindowGtk::EndBatchUpdatePanelBounds() {
113   DCHECK(bounds_updates_started_);
114
115   bounds_updates_started_ = false;
116
117   for (BoundsUpdates::const_iterator iter = bounds_updates_.begin();
118        iter != bounds_updates_.end(); ++iter) {
119     iter->first->SetPanelBoundsInstantly(iter->second);
120   }
121   bounds_updates_.clear();
122
123   SetStackWindowBounds();
124
125   delegate_->PanelBoundsBatchUpdateCompleted();
126 }
127
128 bool PanelStackWindowGtk::IsAnimatingPanelBounds() const {
129   return bounds_updates_started_;
130 }
131
132 void PanelStackWindowGtk::Minimize() {
133   gtk_window_iconify(window_);
134 }
135
136 bool PanelStackWindowGtk::IsMinimized() const {
137   return is_minimized_;
138 }
139
140 void PanelStackWindowGtk::DrawSystemAttention(bool draw_attention) {
141   gtk_window_set_urgency_hint(window_, draw_attention);
142 }
143
144 void PanelStackWindowGtk::OnPanelActivated(Panel* panel) {
145   // If a panel in a stack is activated, make sure all other panels in the stack
146   // are brought to the top in the z-order.
147   for (Panels::const_iterator iter = panels_.begin();
148        iter != panels_.end(); ++iter) {
149     GtkWindow* gtk_window = (*iter)->GetNativeWindow();
150     if (gtk_window) {
151       GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(gtk_window));
152       gdk_window_raise(gdk_window);
153     }
154   }
155 }
156
157 void PanelStackWindowGtk::ActiveWindowChanged(GdkWindow* active_window) {
158   // Bail out if icewm is detected. This is because icewm always creates a
159   // window as active and we do not want to perform the logic here to
160   // activate a panel window when the background window is being created.
161   if (ui::GuessWindowManager() == ui::WM_ICE_WM)
162     return;
163
164   if (!window_ || panels_.empty())
165     return;
166
167   // The background stack window is activated when its taskbar icon is clicked.
168   // When this occurs, we need to activate the most recently active panel.
169   if (gtk_widget_get_window(GTK_WIDGET(window_)) == active_window) {
170     Panel* panel_to_focus =
171         panels_.front()->stack()->most_recently_active_panel();
172     if (panel_to_focus)
173       panel_to_focus->Activate();
174   }
175 }
176
177 gboolean PanelStackWindowGtk::OnWindowDeleteEvent(GtkWidget* widget,
178                                                   GdkEvent* event) {
179   DCHECK(!panels_.empty());
180
181   // Make a copy since closing a panel could modify the list.
182   Panels panels_copy = panels_;
183   for (Panels::const_iterator iter = panels_copy.begin();
184        iter != panels_copy.end(); ++iter) {
185     (*iter)->Close();
186   }
187
188   // Return true to prevent the gtk window from being destroyed.  Close will
189   // destroy it for us.
190   return TRUE;
191 }
192
193 gboolean PanelStackWindowGtk::OnWindowState(GtkWidget* widget,
194                                             GdkEventWindowState* event) {
195   bool is_minimized = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED;
196   if (is_minimized_ == is_minimized)
197     return FALSE;
198   is_minimized_ = is_minimized;
199
200   for (Panels::const_iterator iter = panels_.begin();
201        iter != panels_.end(); ++iter) {
202     GtkWindow* gtk_window = (*iter)->GetNativeWindow();
203     if (is_minimized_)
204       gtk_window_iconify(gtk_window);
205     else
206       gtk_window_deiconify(gtk_window);
207   }
208
209   return FALSE;
210 }
211
212 void PanelStackWindowGtk::EnsureWindowCreated() {
213   if (window_)
214     return;
215
216   DCHECK(!panels_.empty());
217   Panel* panel = panels_.front();
218
219   // Create a small window that stays behinds the panels.
220   window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
221   gtk_window_set_decorated(window_, false);
222   gtk_window_set_resizable(window_, false);
223   gtk_window_set_focus_on_map(window_, false);
224   gtk_widget_show(GTK_WIDGET(window_));
225   gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(window_)),
226       panel->GetBounds().x(), panel->GetBounds().y(), 1, 1);
227   gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
228
229   // Connect signal handlers to the window.
230   g_signal_connect(window_, "delete-event",
231                    G_CALLBACK(OnWindowDeleteEventThunk), this);
232   g_signal_connect(window_, "window-state-event",
233                    G_CALLBACK(OnWindowStateThunk), this);
234
235   // Should appear on the taskbar.
236   gtk_window_set_skip_taskbar_hint(window_, false);
237
238   // Set the window icon and title.
239   string16 title = delegate_->GetTitle();
240   gtk_window_set_title(window_, UTF16ToUTF8(title).c_str());
241
242   gfx::Image app_icon = delegate_->GetIcon();
243   if (!app_icon.IsEmpty())
244     gtk_window_set_icon(window_, app_icon.ToGdkPixbuf());
245 }
246
247 void PanelStackWindowGtk::SetStackWindowBounds() {
248   if (panels_.empty())
249     return;
250   Panel* panel = panels_.front();
251   // Position the small background window a bit away from the left-top corner
252   // such that it will be completely invisible.
253   gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(window_)),
254       panel->GetBounds().x() + 5, panel->GetBounds().y() + 5, 1, 1);
255 }