PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/views/window/window_gtk.cc

https://github.com/akesling/chromium
C++ | 423 lines | 311 code | 69 blank | 43 comment | 14 complexity | 9ecdcdd73d625259709f3fe5c1d15c1b MD5 | raw file
  1. // Copyright (c) 2009 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. #include "views/window/window_gtk.h"
  5. #include "app/gfx/path.h"
  6. #include "app/l10n_util.h"
  7. #include "base/gfx/rect.h"
  8. #include "views/widget/root_view.h"
  9. #include "views/window/custom_frame_view.h"
  10. #include "views/window/hit_test.h"
  11. #include "views/window/non_client_view.h"
  12. #include "views/window/window_delegate.h"
  13. namespace {
  14. // Converts a Windows-style hit test result code into a GDK window edge.
  15. GdkWindowEdge HitTestCodeToGDKWindowEdge(int hittest_code) {
  16. switch (hittest_code) {
  17. case HTBOTTOM:
  18. return GDK_WINDOW_EDGE_SOUTH;
  19. case HTBOTTOMLEFT:
  20. return GDK_WINDOW_EDGE_SOUTH_WEST;
  21. case HTBOTTOMRIGHT:
  22. case HTGROWBOX:
  23. return GDK_WINDOW_EDGE_SOUTH_EAST;
  24. case HTLEFT:
  25. return GDK_WINDOW_EDGE_WEST;
  26. case HTRIGHT:
  27. return GDK_WINDOW_EDGE_EAST;
  28. case HTTOP:
  29. return GDK_WINDOW_EDGE_NORTH;
  30. case HTTOPLEFT:
  31. return GDK_WINDOW_EDGE_NORTH_WEST;
  32. case HTTOPRIGHT:
  33. return GDK_WINDOW_EDGE_NORTH_EAST;
  34. default:
  35. NOTREACHED();
  36. break;
  37. }
  38. // Default to something defaultish.
  39. return HitTestCodeToGDKWindowEdge(HTGROWBOX);
  40. }
  41. // Converts a Windows-style hit test result code into a GDK cursor type.
  42. GdkCursorType HitTestCodeToGdkCursorType(int hittest_code) {
  43. switch (hittest_code) {
  44. case HTBOTTOM:
  45. return GDK_BOTTOM_SIDE;
  46. case HTBOTTOMLEFT:
  47. return GDK_BOTTOM_LEFT_CORNER;
  48. case HTBOTTOMRIGHT:
  49. case HTGROWBOX:
  50. return GDK_BOTTOM_RIGHT_CORNER;
  51. case HTLEFT:
  52. return GDK_LEFT_SIDE;
  53. case HTRIGHT:
  54. return GDK_RIGHT_SIDE;
  55. case HTTOP:
  56. return GDK_TOP_SIDE;
  57. case HTTOPLEFT:
  58. return GDK_TOP_LEFT_CORNER;
  59. case HTTOPRIGHT:
  60. return GDK_TOP_RIGHT_CORNER;
  61. default:
  62. break;
  63. }
  64. // Default to something defaultish.
  65. return GDK_ARROW;
  66. }
  67. } // namespace
  68. namespace views {
  69. WindowGtk::~WindowGtk() {
  70. }
  71. // static
  72. Window* Window::CreateChromeWindow(gfx::NativeWindow parent,
  73. const gfx::Rect& bounds,
  74. WindowDelegate* window_delegate) {
  75. WindowGtk* window = new WindowGtk(window_delegate);
  76. window->GetNonClientView()->SetFrameView(window->CreateFrameViewForWindow());
  77. window->Init(bounds);
  78. return window;
  79. }
  80. // static
  81. void Window::CloseAllSecondaryWindows() {
  82. NOTIMPLEMENTED();
  83. }
  84. gfx::Rect WindowGtk::GetBounds() const {
  85. gfx::Rect bounds;
  86. WidgetGtk::GetBounds(&bounds, true);
  87. return bounds;
  88. }
  89. gfx::Rect WindowGtk::GetNormalBounds() const {
  90. NOTIMPLEMENTED();
  91. return GetBounds();
  92. }
  93. void WindowGtk::SetBounds(const gfx::Rect& bounds,
  94. gfx::NativeWindow other_window) {
  95. // TODO: need to deal with other_window.
  96. WidgetGtk::SetBounds(bounds);
  97. }
  98. void WindowGtk::Show() {
  99. gtk_widget_show_all(GetNativeView());
  100. }
  101. void WindowGtk::HideWindow() {
  102. NOTIMPLEMENTED();
  103. }
  104. void WindowGtk::PushForceHidden() {
  105. NOTIMPLEMENTED();
  106. }
  107. void WindowGtk::PopForceHidden() {
  108. NOTIMPLEMENTED();
  109. }
  110. void WindowGtk::Activate() {
  111. NOTIMPLEMENTED();
  112. }
  113. void WindowGtk::Close() {
  114. if (window_closed_) {
  115. // Don't do anything if we've already been closed.
  116. return;
  117. }
  118. if (non_client_view_->CanClose()) {
  119. WidgetGtk::Close();
  120. window_closed_ = true;
  121. }
  122. }
  123. void WindowGtk::Maximize() {
  124. gtk_window_maximize(GetNativeWindow());
  125. }
  126. void WindowGtk::Minimize() {
  127. gtk_window_iconify(GetNativeWindow());
  128. }
  129. void WindowGtk::Restore() {
  130. NOTIMPLEMENTED();
  131. }
  132. bool WindowGtk::IsActive() const {
  133. return gtk_window_is_active(GetNativeWindow());
  134. }
  135. bool WindowGtk::IsVisible() const {
  136. return GTK_WIDGET_VISIBLE(GetNativeView());
  137. }
  138. bool WindowGtk::IsMaximized() const {
  139. return window_state_ & GDK_WINDOW_STATE_MAXIMIZED;
  140. }
  141. bool WindowGtk::IsMinimized() const {
  142. return window_state_ & GDK_WINDOW_STATE_ICONIFIED;
  143. }
  144. void WindowGtk::SetFullscreen(bool fullscreen) {
  145. if (fullscreen)
  146. gtk_window_fullscreen(GetNativeWindow());
  147. else
  148. gtk_window_unfullscreen(GetNativeWindow());
  149. }
  150. bool WindowGtk::IsFullscreen() const {
  151. return window_state_ & GDK_WINDOW_STATE_FULLSCREEN;
  152. }
  153. void WindowGtk::EnableClose(bool enable) {
  154. gtk_window_set_deletable(GetNativeWindow(), enable);
  155. }
  156. void WindowGtk::DisableInactiveRendering() {
  157. NOTIMPLEMENTED();
  158. }
  159. void WindowGtk::UpdateWindowTitle() {
  160. // If the non-client view is rendering its own title, it'll need to relayout
  161. // now.
  162. non_client_view_->Layout();
  163. // Update the native frame's text. We do this regardless of whether or not
  164. // the native frame is being used, since this also updates the taskbar, etc.
  165. std::wstring window_title = window_delegate_->GetWindowTitle();
  166. std::wstring localized_text;
  167. if (l10n_util::AdjustStringForLocaleDirection(window_title, &localized_text))
  168. window_title.assign(localized_text);
  169. gtk_window_set_title(GetNativeWindow(), WideToUTF8(window_title).c_str());
  170. }
  171. void WindowGtk::UpdateWindowIcon() {
  172. NOTIMPLEMENTED();
  173. }
  174. void WindowGtk::SetIsAlwaysOnTop(bool always_on_top) {
  175. gtk_window_set_keep_above(GetNativeWindow(), always_on_top);
  176. }
  177. NonClientFrameView* WindowGtk::CreateFrameViewForWindow() {
  178. // TODO(erg): Always use a custom frame view? Are there cases where we let
  179. // the window manager deal with the X11 equivalent of the "non-client" area?
  180. return new CustomFrameView(this);
  181. }
  182. void WindowGtk::UpdateFrameAfterFrameChange() {
  183. NOTIMPLEMENTED();
  184. }
  185. WindowDelegate* WindowGtk::GetDelegate() const {
  186. return window_delegate_;
  187. }
  188. NonClientView* WindowGtk::GetNonClientView() const {
  189. return non_client_view_;
  190. }
  191. ClientView* WindowGtk::GetClientView() const {
  192. return non_client_view_->client_view();
  193. }
  194. gfx::NativeWindow WindowGtk::GetNativeWindow() const {
  195. return GTK_WINDOW(GetNativeView());
  196. }
  197. bool WindowGtk::ShouldUseNativeFrame() const {
  198. return false;
  199. }
  200. void WindowGtk::FrameTypeChanged() {
  201. NOTIMPLEMENTED();
  202. }
  203. ////////////////////////////////////////////////////////////////////////////////
  204. // WindowGtk, WidgetGtk overrides:
  205. gboolean WindowGtk::OnButtonPress(GtkWidget* widget, GdkEventButton* event) {
  206. int hittest_code =
  207. non_client_view_->NonClientHitTest(gfx::Point(event->x, event->y));
  208. switch (hittest_code) {
  209. case HTCAPTION: {
  210. gfx::Point screen_point(event->x, event->y);
  211. View::ConvertPointToScreen(GetRootView(), &screen_point);
  212. gtk_window_begin_move_drag(GetNativeWindow(), event->button,
  213. screen_point.x(), screen_point.y(),
  214. event->time);
  215. return TRUE;
  216. }
  217. case HTBOTTOM:
  218. case HTBOTTOMLEFT:
  219. case HTBOTTOMRIGHT:
  220. case HTGROWBOX:
  221. case HTLEFT:
  222. case HTRIGHT:
  223. case HTTOP:
  224. case HTTOPLEFT:
  225. case HTTOPRIGHT: {
  226. gfx::Point screen_point(event->x, event->y);
  227. View::ConvertPointToScreen(GetRootView(), &screen_point);
  228. // TODO(beng): figure out how to get a good minimum size.
  229. gtk_widget_set_size_request(GetNativeView(), 100, 100);
  230. gtk_window_begin_resize_drag(GetNativeWindow(),
  231. HitTestCodeToGDKWindowEdge(hittest_code),
  232. event->button, screen_point.x(),
  233. screen_point.y(), event->time);
  234. return TRUE;
  235. }
  236. default:
  237. // Everything else falls into standard client event handling...
  238. break;
  239. }
  240. return WidgetGtk::OnButtonPress(widget, event);
  241. }
  242. gboolean WindowGtk::OnConfigureEvent(GtkWidget* widget,
  243. GdkEventConfigure* event) {
  244. SaveWindowPosition();
  245. return FALSE;
  246. }
  247. gboolean WindowGtk::OnMotionNotify(GtkWidget* widget, GdkEventMotion* event) {
  248. // Update the cursor for the screen edge.
  249. int hittest_code =
  250. non_client_view_->NonClientHitTest(gfx::Point(event->x, event->y));
  251. GdkCursorType cursor_type = HitTestCodeToGdkCursorType(hittest_code);
  252. GdkCursor* cursor = gdk_cursor_new(cursor_type);
  253. gdk_window_set_cursor(widget->window, cursor);
  254. gdk_cursor_destroy(cursor);
  255. return WidgetGtk::OnMotionNotify(widget, event);
  256. }
  257. void WindowGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) {
  258. WidgetGtk::OnSizeAllocate(widget, allocation);
  259. // The Window's NonClientView may provide a custom shape for the Window.
  260. gfx::Path window_mask;
  261. non_client_view_->GetWindowMask(gfx::Size(allocation->width,
  262. allocation->height),
  263. &window_mask);
  264. GdkRegion* mask_region = window_mask.CreateGdkRegion();
  265. gdk_window_shape_combine_region(GetNativeView()->window, mask_region, 0, 0);
  266. gdk_region_destroy(mask_region);
  267. }
  268. gboolean WindowGtk::OnWindowStateEvent(GtkWidget* widget,
  269. GdkEventWindowState* event) {
  270. window_state_ = event->new_window_state;
  271. if (!(window_state_ & GDK_WINDOW_STATE_WITHDRAWN))
  272. SaveWindowPosition();
  273. return FALSE;
  274. }
  275. ////////////////////////////////////////////////////////////////////////////////
  276. // WindowGtk, protected:
  277. WindowGtk::WindowGtk(WindowDelegate* window_delegate)
  278. : WidgetGtk(TYPE_WINDOW),
  279. is_modal_(false),
  280. window_delegate_(window_delegate),
  281. non_client_view_(new NonClientView(this)),
  282. window_state_(GDK_WINDOW_STATE_WITHDRAWN),
  283. window_closed_(false) {
  284. is_window_ = true;
  285. window_delegate_->window_.reset(this);
  286. }
  287. void WindowGtk::Init(const gfx::Rect& bounds) {
  288. // We call this after initializing our members since our implementations of
  289. // assorted WidgetWin functions may be called during initialization.
  290. is_modal_ = window_delegate_->IsModal();
  291. if (is_modal_) {
  292. // TODO(erg): Fix once modality works.
  293. // BecomeModal();
  294. }
  295. WidgetGtk::Init(NULL, bounds);
  296. g_signal_connect(G_OBJECT(GetNativeWindow()), "configure-event",
  297. G_CALLBACK(CallConfigureEvent), this);
  298. g_signal_connect(G_OBJECT(GetNativeWindow()), "window-state-event",
  299. G_CALLBACK(CallWindowStateEvent), this);
  300. // Create the ClientView, add it to the NonClientView and add the
  301. // NonClientView to the RootView. This will cause everything to be parented.
  302. non_client_view_->set_client_view(window_delegate_->CreateClientView(this));
  303. WidgetGtk::SetContentsView(non_client_view_);
  304. UpdateWindowTitle();
  305. SetInitialBounds(bounds);
  306. // if (!IsAppWindow()) {
  307. // notification_registrar_.Add(
  308. // this,
  309. // NotificationType::ALL_APPWINDOWS_CLOSED,
  310. // NotificationService::AllSources());
  311. // }
  312. // ResetWindowRegion(false);
  313. }
  314. ////////////////////////////////////////////////////////////////////////////////
  315. // WindowGtk, private:
  316. // static
  317. gboolean WindowGtk::CallConfigureEvent(GtkWidget* widget,
  318. GdkEventConfigure* event,
  319. WindowGtk* window_gtk) {
  320. return window_gtk->OnConfigureEvent(widget, event);
  321. }
  322. // static
  323. gboolean WindowGtk::CallWindowStateEvent(GtkWidget* widget,
  324. GdkEventWindowState* event,
  325. WindowGtk* window_gtk) {
  326. return window_gtk->OnWindowStateEvent(widget, event);
  327. }
  328. void WindowGtk::SaveWindowPosition() {
  329. // The delegate may have gone away on us.
  330. if (!window_delegate_)
  331. return;
  332. bool maximized = window_state_ & GDK_WINDOW_STATE_MAXIMIZED;
  333. gfx::Rect bounds;
  334. WidgetGtk::GetBounds(&bounds, true);
  335. window_delegate_->SaveWindowPlacement(bounds, maximized);
  336. }
  337. void WindowGtk::SetInitialBounds(const gfx::Rect& create_bounds) {
  338. gfx::Rect saved_bounds(create_bounds.ToGdkRectangle());
  339. if (window_delegate_->GetSavedWindowBounds(&saved_bounds)) {
  340. WidgetGtk::SetBounds(saved_bounds);
  341. } else {
  342. if (create_bounds.IsEmpty()) {
  343. SizeWindowToDefault();
  344. } else {
  345. SetBounds(create_bounds, NULL);
  346. }
  347. }
  348. }
  349. void WindowGtk::SizeWindowToDefault() {
  350. gfx::Size size = non_client_view_->GetPreferredSize();
  351. gfx::Rect bounds(size.width(), size.height());
  352. SetBounds(bounds, NULL);
  353. }
  354. } // namespace views