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.
5 #include "chrome/browser/ui/gtk/panels/panel_stack_window_gtk.h"
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"
14 NativePanelStackWindow
* NativePanelStackWindow::Create(
15 NativePanelStackWindowDelegate
* delegate
) {
16 return new PanelStackWindowGtk(delegate
);
19 PanelStackWindowGtk::PanelStackWindowGtk(
20 NativePanelStackWindowDelegate
* delegate
)
21 : delegate_(delegate
),
24 bounds_updates_started_(false) {
25 ui::ActiveWindowWatcherX::AddObserver(this);
28 PanelStackWindowGtk::~PanelStackWindowGtk() {
29 ui::ActiveWindowWatcherX::RemoveObserver(this);
32 void PanelStackWindowGtk::Close() {
35 gtk_widget_destroy(GTK_WIDGET(window_
));
39 void PanelStackWindowGtk::AddPanel(Panel
* panel
) {
40 panels_
.push_back(panel
);
42 EnsureWindowCreated();
43 SetStackWindowBounds();
45 // The panel being stacked should not appear on the taskbar.
46 gtk_window_set_skip_taskbar_hint(panel
->GetNativeWindow(), true);
49 void PanelStackWindowGtk::RemovePanel(Panel
* panel
) {
50 panels_
.remove(panel
);
52 SetStackWindowBounds();
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
57 GtkWindow
* gtk_window
= panel
->GetNativeWindow();
59 gtk_window_set_skip_taskbar_hint(gtk_window
, false);
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
) {
68 panels_
.push_back(panel
);
70 another_stack
->panels_
.clear();
72 SetStackWindowBounds();
75 bool PanelStackWindowGtk::IsEmpty() const {
76 return panels_
.empty();
79 bool PanelStackWindowGtk::HasPanel(Panel
* panel
) const {
80 return std::find(panels_
.begin(), panels_
.end(), panel
) != panels_
.end();
83 void PanelStackWindowGtk::MovePanelsBy(const gfx::Vector2d
& delta
) {
84 for (Panels::const_iterator iter
= panels_
.begin();
85 iter
!= panels_
.end(); ++iter
) {
87 gfx::Rect bounds
= panel
->GetBounds();
89 panel
->SetPanelBoundsInstantly(bounds
);
92 SetStackWindowBounds();
95 void PanelStackWindowGtk::BeginBatchUpdatePanelBounds(bool animate
) {
96 // Bounds animation is not supported on GTK.
97 bounds_updates_started_
= true;
100 void PanelStackWindowGtk::AddPanelBoundsForBatchUpdate(
101 Panel
* panel
, const gfx::Rect
& new_bounds
) {
102 DCHECK(bounds_updates_started_
);
104 // No need to track it if no change is needed.
105 if (panel
->GetBounds() == new_bounds
)
108 // New bounds are stored as the map value.
109 bounds_updates_
[panel
] = new_bounds
;
112 void PanelStackWindowGtk::EndBatchUpdatePanelBounds() {
113 DCHECK(bounds_updates_started_
);
115 bounds_updates_started_
= false;
117 for (BoundsUpdates::const_iterator iter
= bounds_updates_
.begin();
118 iter
!= bounds_updates_
.end(); ++iter
) {
119 iter
->first
->SetPanelBoundsInstantly(iter
->second
);
121 bounds_updates_
.clear();
123 SetStackWindowBounds();
125 delegate_
->PanelBoundsBatchUpdateCompleted();
128 bool PanelStackWindowGtk::IsAnimatingPanelBounds() const {
129 return bounds_updates_started_
;
132 void PanelStackWindowGtk::Minimize() {
133 gtk_window_iconify(window_
);
136 bool PanelStackWindowGtk::IsMinimized() const {
137 return is_minimized_
;
140 void PanelStackWindowGtk::DrawSystemAttention(bool draw_attention
) {
141 gtk_window_set_urgency_hint(window_
, draw_attention
);
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();
151 GdkWindow
* gdk_window
= gtk_widget_get_window(GTK_WIDGET(gtk_window
));
152 gdk_window_raise(gdk_window
);
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
)
164 if (!window_
|| panels_
.empty())
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();
173 panel_to_focus
->Activate();
177 gboolean
PanelStackWindowGtk::OnWindowDeleteEvent(GtkWidget
* widget
,
179 DCHECK(!panels_
.empty());
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
) {
188 // Return true to prevent the gtk window from being destroyed. Close will
189 // destroy it for us.
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
)
198 is_minimized_
= is_minimized
;
200 for (Panels::const_iterator iter
= panels_
.begin();
201 iter
!= panels_
.end(); ++iter
) {
202 GtkWindow
* gtk_window
= (*iter
)->GetNativeWindow();
204 gtk_window_iconify(gtk_window
);
206 gtk_window_deiconify(gtk_window
);
212 void PanelStackWindowGtk::EnsureWindowCreated() {
216 DCHECK(!panels_
.empty());
217 Panel
* panel
= panels_
.front();
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_
)));
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);
235 // Should appear on the taskbar.
236 gtk_window_set_skip_taskbar_hint(window_
, false);
238 // Set the window icon and title.
239 base::string16 title
= delegate_
->GetTitle();
240 gtk_window_set_title(window_
, base::UTF16ToUTF8(title
).c_str());
242 gfx::Image app_icon
= delegate_
->GetIcon();
243 if (!app_icon
.IsEmpty())
244 gtk_window_set_icon(window_
, app_icon
.ToGdkPixbuf());
247 void PanelStackWindowGtk::SetStackWindowBounds() {
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);