summaryrefslogtreecommitdiffstats
path: root/views/window
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
commit2362e4fe2905ab75d3230ebc3e307ae53e2b8362 (patch)
treee6d88357a2021811e0e354f618247217be8bb3da /views/window
parentdb23ac3e713dc17509b2b15d3ee634968da45715 (diff)
downloadchromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.zip
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.gz
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.bz2
Move src/chrome/views to src/views. RS=darin http://crbug.com/11387
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15604 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/window')
-rw-r--r--views/window/client_view.cc61
-rw-r--r--views/window/client_view.h84
-rw-r--r--views/window/custom_frame_view.cc695
-rw-r--r--views/window/custom_frame_view.h122
-rw-r--r--views/window/dialog_client_view.cc440
-rw-r--r--views/window/dialog_client_view.h123
-rw-r--r--views/window/dialog_delegate.cc54
-rw-r--r--views/window/dialog_delegate.h113
-rw-r--r--views/window/native_frame_view.cc60
-rw-r--r--views/window/native_frame_view.h39
-rw-r--r--views/window/non_client_view.cc255
-rw-r--r--views/window/non_client_view.h227
-rw-r--r--views/window/window.h135
-rw-r--r--views/window/window_delegate.cc96
-rw-r--r--views/window/window_delegate.h162
-rw-r--r--views/window/window_resources.h30
-rw-r--r--views/window/window_win.cc1446
-rw-r--r--views/window/window_win.h307
18 files changed, 4449 insertions, 0 deletions
diff --git a/views/window/client_view.cc b/views/window/client_view.cc
new file mode 100644
index 0000000..5b5afb1
--- /dev/null
+++ b/views/window/client_view.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2006-2008 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.
+
+#include "base/logging.h"
+#include "views/window/client_view.h"
+#include "views/window/window.h"
+#include "views/window/window_delegate.h"
+
+namespace views {
+
+///////////////////////////////////////////////////////////////////////////////
+// ClientView, public:
+
+ClientView::ClientView(Window* window, View* contents_view)
+ : window_(window),
+ contents_view_(contents_view) {
+}
+
+int ClientView::NonClientHitTest(const gfx::Point& point) {
+ return bounds().Contains(point) ? HTCLIENT : HTNOWHERE;
+}
+
+void ClientView::WindowClosing() {
+ window_->GetDelegate()->WindowClosing();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ClientView, View overrides:
+
+gfx::Size ClientView::GetPreferredSize() {
+ // |contents_view_| is allowed to be NULL up until the point where this view
+ // is attached to a Container.
+ if (contents_view_)
+ return contents_view_->GetPreferredSize();
+ return gfx::Size();
+}
+
+void ClientView::Layout() {
+ // |contents_view_| is allowed to be NULL up until the point where this view
+ // is attached to a Container.
+ if (contents_view_)
+ contents_view_->SetBounds(0, 0, width(), height());
+}
+
+void ClientView::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+ if (is_add && child == this) {
+ DCHECK(GetWidget());
+ DCHECK(contents_view_); // |contents_view_| must be valid now!
+ AddChildView(contents_view_);
+ }
+}
+
+void ClientView::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+ // Overridden to do nothing. The NonClientView manually calls Layout on the
+ // ClientView when it is itself laid out, see comment in
+ // NonClientView::Layout.
+}
+
+} // namespace views
diff --git a/views/window/client_view.h b/views/window/client_view.h
new file mode 100644
index 0000000..b56fcfd
--- /dev/null
+++ b/views/window/client_view.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2006-2008 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_WINDOW_CLIENT_VIEW_H_
+#define VIEWS_WINDOW_CLIENT_VIEW_H_
+
+#include "views/view.h"
+
+namespace views {
+
+class DialogClientView;
+class Window;
+
+///////////////////////////////////////////////////////////////////////////////
+// ClientView
+//
+// A ClientView is a View subclass that is used to occupy the "client area"
+// of a window. It provides basic information to the window that contains it
+// such as non-client hit testing information, sizing etc. Sub-classes of
+// ClientView are used to create more elaborate contents, e.g.
+// "DialogClientView".
+class ClientView : public View {
+ public:
+ // Constructs a ClientView object for the specified window with the specified
+ // contents. Since this object is created during the process of creating
+ // |window|, |contents_view| must be valid if you want the initial size of
+ // the window to be based on |contents_view|'s preferred size.
+ ClientView(Window* window, View* contents_view);
+ virtual ~ClientView() {}
+
+ // Manual RTTI ftw.
+ virtual DialogClientView* AsDialogClientView() { return NULL; }
+
+ // Returns true to signal that the Window can be closed. Specialized
+ // ClientView subclasses can override this default behavior to allow the
+ // close to be blocked until the user corrects mistakes, accepts a warning
+ // dialog, etc.
+ virtual bool CanClose() const { return true; }
+
+ // Notification that the window is closing. The default implementation
+ // forwards the notification to the delegate.
+ virtual void WindowClosing();
+
+ // Tests to see if the specified point (in view coordinates) is within the
+ // bounds of this view. If so, it returns HTCLIENT in this default
+ // implementation. If it is outside the bounds of this view, this must return
+ // HTNOWHERE to tell the caller to do further processing to determine where
+ // in the non-client area it is (if it is).
+ // Subclasses of ClientView can extend this logic by overriding this method
+ // to detect if regions within the client area count as parts of the "non-
+ // client" area. A good example of this is the size box at the bottom right
+ // corner of resizable dialog boxes.
+ virtual int NonClientHitTest(const gfx::Point& point);
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+
+ protected:
+ // Overridden from View:
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+ virtual void DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current);
+
+ // Accessors for private data members.
+ Window* window() const { return window_; }
+ void set_window(Window* window) { window_ = window; }
+ View* contents_view() const { return contents_view_; }
+ void set_contents_view(View* contents_view) {
+ contents_view_ = contents_view;
+ }
+
+ private:
+ // The Window that hosts this ClientView.
+ Window* window_;
+
+ // The View that this ClientView contains.
+ View* contents_view_;
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_WINDOW_CLIENT_VIEW_H_
diff --git a/views/window/custom_frame_view.cc b/views/window/custom_frame_view.cc
new file mode 100644
index 0000000..cf8173f
--- /dev/null
+++ b/views/window/custom_frame_view.cc
@@ -0,0 +1,695 @@
+// Copyright (c) 2009 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.
+
+#include "views/window/custom_frame_view.h"
+
+#include "app/gfx/chrome_canvas.h"
+#include "app/gfx/chrome_font.h"
+#include "app/gfx/path.h"
+#include "app/resource_bundle.h"
+#include "base/win_util.h"
+#include "chrome/common/win_util.h"
+#include "grit/theme_resources.h"
+#include "views/window/client_view.h"
+#include "views/window/window_delegate.h"
+
+namespace views {
+
+// An enumeration of bitmap resources used by this window.
+enum {
+ FRAME_PART_BITMAP_FIRST = 0, // Must be first.
+
+ // Window Controls.
+ FRAME_CLOSE_BUTTON_ICON,
+ FRAME_CLOSE_BUTTON_ICON_H,
+ FRAME_CLOSE_BUTTON_ICON_P,
+ FRAME_CLOSE_BUTTON_ICON_SA,
+ FRAME_CLOSE_BUTTON_ICON_SA_H,
+ FRAME_CLOSE_BUTTON_ICON_SA_P,
+ FRAME_RESTORE_BUTTON_ICON,
+ FRAME_RESTORE_BUTTON_ICON_H,
+ FRAME_RESTORE_BUTTON_ICON_P,
+ FRAME_MAXIMIZE_BUTTON_ICON,
+ FRAME_MAXIMIZE_BUTTON_ICON_H,
+ FRAME_MAXIMIZE_BUTTON_ICON_P,
+ FRAME_MINIMIZE_BUTTON_ICON,
+ FRAME_MINIMIZE_BUTTON_ICON_H,
+ FRAME_MINIMIZE_BUTTON_ICON_P,
+
+ // Window Frame Border.
+ FRAME_BOTTOM_EDGE,
+ FRAME_BOTTOM_LEFT_CORNER,
+ FRAME_BOTTOM_RIGHT_CORNER,
+ FRAME_LEFT_EDGE,
+ FRAME_RIGHT_EDGE,
+ FRAME_TOP_EDGE,
+ FRAME_TOP_LEFT_CORNER,
+ FRAME_TOP_RIGHT_CORNER,
+
+ // Client Edge Border.
+ FRAME_CLIENT_EDGE_TOP_LEFT,
+ FRAME_CLIENT_EDGE_TOP,
+ FRAME_CLIENT_EDGE_TOP_RIGHT,
+ FRAME_CLIENT_EDGE_RIGHT,
+ FRAME_CLIENT_EDGE_BOTTOM_RIGHT,
+ FRAME_CLIENT_EDGE_BOTTOM,
+ FRAME_CLIENT_EDGE_BOTTOM_LEFT,
+ FRAME_CLIENT_EDGE_LEFT,
+
+ FRAME_PART_BITMAP_COUNT // Must be last.
+};
+
+class ActiveWindowResources : public WindowResources {
+ public:
+ ActiveWindowResources() {
+ InitClass();
+ }
+ virtual ~ActiveWindowResources() {
+ }
+
+ // WindowResources implementation:
+ virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const {
+ return standard_frame_bitmaps_[part];
+ }
+
+ private:
+ static void InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ static const int kFramePartBitmapIds[] = {
+ 0,
+ IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P,
+ IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P,
+ IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P,
+ IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P,
+ IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P,
+ IDR_WINDOW_BOTTOM_CENTER, IDR_WINDOW_BOTTOM_LEFT_CORNER,
+ IDR_WINDOW_BOTTOM_RIGHT_CORNER, IDR_WINDOW_LEFT_SIDE,
+ IDR_WINDOW_RIGHT_SIDE, IDR_WINDOW_TOP_CENTER,
+ IDR_WINDOW_TOP_LEFT_CORNER, IDR_WINDOW_TOP_RIGHT_CORNER,
+ IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT,
+ IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER,
+ IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER,
+ IDR_CONTENT_LEFT_SIDE,
+ 0
+ };
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) {
+ int id = kFramePartBitmapIds[i];
+ if (id != 0)
+ standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id);
+ }
+ initialized = true;
+ }
+ }
+
+ static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT];
+
+ DISALLOW_EVIL_CONSTRUCTORS(ActiveWindowResources);
+};
+
+class InactiveWindowResources : public WindowResources {
+ public:
+ InactiveWindowResources() {
+ InitClass();
+ }
+ virtual ~InactiveWindowResources() {
+ }
+
+ // WindowResources implementation:
+ virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const {
+ return standard_frame_bitmaps_[part];
+ }
+
+ private:
+ static void InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ static const int kFramePartBitmapIds[] = {
+ 0,
+ IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P,
+ IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P,
+ IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P,
+ IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P,
+ IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P,
+ IDR_DEWINDOW_BOTTOM_CENTER, IDR_DEWINDOW_BOTTOM_LEFT_CORNER,
+ IDR_DEWINDOW_BOTTOM_RIGHT_CORNER, IDR_DEWINDOW_LEFT_SIDE,
+ IDR_DEWINDOW_RIGHT_SIDE, IDR_DEWINDOW_TOP_CENTER,
+ IDR_DEWINDOW_TOP_LEFT_CORNER, IDR_DEWINDOW_TOP_RIGHT_CORNER,
+ IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT,
+ IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER,
+ IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER,
+ IDR_CONTENT_LEFT_SIDE,
+ 0
+ };
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) {
+ int id = kFramePartBitmapIds[i];
+ if (id != 0)
+ standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id);
+ }
+ initialized = true;
+ }
+ }
+
+ static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT];
+
+ DISALLOW_EVIL_CONSTRUCTORS(InactiveWindowResources);
+};
+
+// static
+SkBitmap* ActiveWindowResources::standard_frame_bitmaps_[];
+SkBitmap* InactiveWindowResources::standard_frame_bitmaps_[];
+
+// static
+WindowResources* CustomFrameView::active_resources_ = NULL;
+WindowResources* CustomFrameView::inactive_resources_ = NULL;
+ChromeFont* CustomFrameView::title_font_ = NULL;
+
+namespace {
+// The frame border is only visible in restored mode and is hardcoded to 4 px on
+// each side regardless of the system window border size.
+const int kFrameBorderThickness = 4;
+// Various edges of the frame border have a 1 px shadow along their edges; in a
+// few cases we shift elements based on this amount for visual appeal.
+const int kFrameShadowThickness = 1;
+// While resize areas on Windows are normally the same size as the window
+// borders, our top area is shrunk by 1 px to make it easier to move the window
+// around with our thinner top grabbable strip. (Incidentally, our side and
+// bottom resize areas don't match the frame border thickness either -- they
+// span the whole nonclient area, so there's no "dead zone" for the mouse.)
+const int kTopResizeAdjust = 1;
+// In the window corners, the resize areas don't actually expand bigger, but the
+// 16 px at the end of each edge triggers diagonal resizing.
+const int kResizeAreaCornerSize = 16;
+// The titlebar never shrinks to less than 18 px tall, plus the height of the
+// frame border and any bottom edge.
+const int kTitlebarMinimumHeight = 18;
+// The icon is inset 2 px from the left frame border.
+const int kIconLeftSpacing = 2;
+// The icon takes up 16/25th of the available titlebar height. (This is
+// expressed as two ints to avoid precision losses leading to off-by-one pixel
+// errors.)
+const int kIconHeightFractionNumerator = 16;
+const int kIconHeightFractionDenominator = 25;
+// The icon never shrinks below 16 px on a side.
+const int kIconMinimumSize = 16;
+// Because our frame border has a different "3D look" than Windows', with a less
+// cluttered top edge, we need to shift the icon up by 1 px in restored mode so
+// it looks more centered.
+const int kIconRestoredAdjust = 1;
+// There is a 4 px gap between the icon and the title text.
+const int kIconTitleSpacing = 4;
+// The title text starts 2 px below the bottom of the top frame border.
+const int kTitleTopSpacing = 2;
+// There is a 5 px gap between the title text and the caption buttons.
+const int kTitleCaptionSpacing = 5;
+// The caption buttons are always drawn 1 px down from the visible top of the
+// window (the true top in restored mode, or the top of the screen in maximized
+// mode).
+const int kCaptionTopSpacing = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameView, public:
+
+CustomFrameView::CustomFrameView(Window* frame)
+ : NonClientFrameView(),
+ close_button_(new ImageButton(this)),
+ restore_button_(new ImageButton(this)),
+ maximize_button_(new ImageButton(this)),
+ minimize_button_(new ImageButton(this)),
+ system_menu_button_(new ImageButton(this)),
+ should_show_minmax_buttons_(false),
+ frame_(frame) {
+ InitClass();
+ WindowResources* resources = active_resources_;
+
+ // Close button images will be set in LayoutWindowControls().
+ AddChildView(close_button_);
+
+ restore_button_->SetImage(CustomButton::BS_NORMAL,
+ resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON));
+ restore_button_->SetImage(CustomButton::BS_HOT,
+ resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_H));
+ restore_button_->SetImage(CustomButton::BS_PUSHED,
+ resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_P));
+ AddChildView(restore_button_);
+
+ maximize_button_->SetImage(CustomButton::BS_NORMAL,
+ resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON));
+ maximize_button_->SetImage(CustomButton::BS_HOT,
+ resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_H));
+ maximize_button_->SetImage(CustomButton::BS_PUSHED,
+ resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_P));
+ AddChildView(maximize_button_);
+
+ minimize_button_->SetImage(CustomButton::BS_NORMAL,
+ resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON));
+ minimize_button_->SetImage(CustomButton::BS_HOT,
+ resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_H));
+ minimize_button_->SetImage(CustomButton::BS_PUSHED,
+ resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_P));
+ AddChildView(minimize_button_);
+
+ should_show_minmax_buttons_ = frame_->GetDelegate()->CanMaximize();
+
+ AddChildView(system_menu_button_);
+}
+
+CustomFrameView::~CustomFrameView() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameView, NonClientFrameView implementation:
+
+gfx::Rect CustomFrameView::GetBoundsForClientView() const {
+ return client_view_bounds_;
+}
+
+gfx::Rect CustomFrameView::GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const {
+ int top_height = NonClientTopBorderHeight();
+ int border_thickness = NonClientBorderThickness();
+ return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
+ std::max(0, client_bounds.y() - top_height),
+ client_bounds.width() + (2 * border_thickness),
+ client_bounds.height() + top_height + border_thickness);
+}
+
+gfx::Point CustomFrameView::GetSystemMenuPoint() const {
+ gfx::Point system_menu_point(
+ MirroredXCoordinateInsideView(FrameBorderThickness()),
+ NonClientTopBorderHeight() - BottomEdgeThicknessWithinNonClientHeight());
+ ConvertPointToScreen(this, &system_menu_point);
+ return system_menu_point;
+}
+
+int CustomFrameView::NonClientHitTest(const gfx::Point& point) {
+ // Then see if the point is within any of the window controls.
+ if (close_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point))
+ return HTCLOSE;
+ if (restore_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(
+ point))
+ return HTMAXBUTTON;
+ if (maximize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(
+ point))
+ return HTMAXBUTTON;
+ if (minimize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(
+ point))
+ return HTMINBUTTON;
+ if (system_menu_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(
+ point))
+ return HTSYSMENU;
+
+ int window_component = GetHTComponentForFrame(point, FrameBorderThickness(),
+ NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
+ frame_->GetDelegate()->CanResize());
+ // Fall back to the caption if no other component matches.
+ return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
+}
+
+void CustomFrameView::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+ DCHECK(window_mask);
+
+ if (frame_->IsMaximized())
+ return;
+
+ // Redefine the window visible region for the new size.
+ window_mask->moveTo(0, 3);
+ window_mask->lineTo(1, 2);
+ window_mask->lineTo(1, 1);
+ window_mask->lineTo(2, 1);
+ window_mask->lineTo(3, 0);
+
+ window_mask->lineTo(SkIntToScalar(size.width() - 3), 0);
+ window_mask->lineTo(SkIntToScalar(size.width() - 2), 1);
+ window_mask->lineTo(SkIntToScalar(size.width() - 1), 1);
+ window_mask->lineTo(SkIntToScalar(size.width() - 1), 2);
+ window_mask->lineTo(SkIntToScalar(size.width()), 3);
+
+ window_mask->lineTo(SkIntToScalar(size.width()),
+ SkIntToScalar(size.height()));
+ window_mask->lineTo(0, SkIntToScalar(size.height()));
+ window_mask->close();
+}
+
+void CustomFrameView::EnableClose(bool enable) {
+ close_button_->SetEnabled(enable);
+}
+
+void CustomFrameView::ResetWindowControls() {
+ restore_button_->SetState(CustomButton::BS_NORMAL);
+ minimize_button_->SetState(CustomButton::BS_NORMAL);
+ maximize_button_->SetState(CustomButton::BS_NORMAL);
+ // The close button isn't affected by this constraint.
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameView, View overrides:
+
+void CustomFrameView::Paint(ChromeCanvas* canvas) {
+ if (frame_->IsMaximized())
+ PaintMaximizedFrameBorder(canvas);
+ else
+ PaintRestoredFrameBorder(canvas);
+ PaintTitleBar(canvas);
+ if (!frame_->IsMaximized())
+ PaintRestoredClientEdge(canvas);
+}
+
+void CustomFrameView::Layout() {
+ LayoutWindowControls();
+ LayoutTitleBar();
+ LayoutClientView();
+}
+
+gfx::Size CustomFrameView::GetPreferredSize() {
+ gfx::Size pref = frame_->GetClientView()->GetPreferredSize();
+ gfx::Rect bounds(0, 0, pref.width(), pref.height());
+ return frame_->GetNonClientView()->GetWindowBoundsForClientBounds(
+ bounds).size();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameView, ButtonListener implementation:
+
+void CustomFrameView::ButtonPressed(Button* sender) {
+ if (sender == close_button_)
+ frame_->Close();
+ else if (sender == minimize_button_)
+ frame_->Minimize();
+ else if (sender == maximize_button_)
+ frame_->Maximize();
+ else if (sender == restore_button_)
+ frame_->Restore();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameView, private:
+
+int CustomFrameView::FrameBorderThickness() const {
+ return frame_->IsMaximized() ? 0 : kFrameBorderThickness;
+}
+
+int CustomFrameView::NonClientBorderThickness() const {
+ // In maximized mode, we don't show a client edge.
+ return FrameBorderThickness() +
+ (frame_->IsMaximized() ? 0 : kClientEdgeThickness);
+}
+
+int CustomFrameView::NonClientTopBorderHeight() const {
+ int title_top_spacing, title_thickness;
+ return TitleCoordinates(&title_top_spacing, &title_thickness);
+}
+
+int CustomFrameView::BottomEdgeThicknessWithinNonClientHeight() const {
+ return kFrameShadowThickness +
+ (frame_->IsMaximized() ? 0 : kClientEdgeThickness);
+}
+
+int CustomFrameView::TitleCoordinates(int* title_top_spacing,
+ int* title_thickness) const {
+ int frame_thickness = FrameBorderThickness();
+ int min_titlebar_height = kTitlebarMinimumHeight + frame_thickness;
+ *title_top_spacing = frame_thickness + kTitleTopSpacing;
+ // The bottom spacing should be the same apparent height as the top spacing.
+ // Because the actual top spacing height varies based on the system border
+ // thickness, we calculate this based on the restored top spacing and then
+ // adjust for maximized mode. We also don't include the frame shadow here,
+ // since while it's part of the bottom spacing it will be added in at the end.
+ int title_bottom_spacing =
+ kFrameBorderThickness + kTitleTopSpacing - kFrameShadowThickness;
+ if (frame_->IsMaximized()) {
+ // When we maximize, the top border appears to be chopped off; shift the
+ // title down to stay centered within the remaining space.
+ int title_adjust = (kFrameBorderThickness / 2);
+ *title_top_spacing += title_adjust;
+ title_bottom_spacing -= title_adjust;
+ }
+ *title_thickness = std::max(title_font_->height(),
+ min_titlebar_height - *title_top_spacing - title_bottom_spacing);
+ return *title_top_spacing + *title_thickness + title_bottom_spacing +
+ BottomEdgeThicknessWithinNonClientHeight();
+}
+
+void CustomFrameView::PaintRestoredFrameBorder(ChromeCanvas* canvas) {
+ SkBitmap* top_left_corner = resources()->GetPartBitmap(FRAME_TOP_LEFT_CORNER);
+ SkBitmap* top_right_corner =
+ resources()->GetPartBitmap(FRAME_TOP_RIGHT_CORNER);
+ SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE);
+ SkBitmap* right_edge = resources()->GetPartBitmap(FRAME_RIGHT_EDGE);
+ SkBitmap* left_edge = resources()->GetPartBitmap(FRAME_LEFT_EDGE);
+ SkBitmap* bottom_left_corner =
+ resources()->GetPartBitmap(FRAME_BOTTOM_LEFT_CORNER);
+ SkBitmap* bottom_right_corner =
+ resources()->GetPartBitmap(FRAME_BOTTOM_RIGHT_CORNER);
+ SkBitmap* bottom_edge = resources()->GetPartBitmap(FRAME_BOTTOM_EDGE);
+
+ // Top.
+ canvas->DrawBitmapInt(*top_left_corner, 0, 0);
+ canvas->TileImageInt(*top_edge, top_left_corner->width(), 0,
+ width() - top_right_corner->width(), top_edge->height());
+ canvas->DrawBitmapInt(*top_right_corner,
+ width() - top_right_corner->width(), 0);
+
+ // Right.
+ canvas->TileImageInt(*right_edge, width() - right_edge->width(),
+ top_right_corner->height(), right_edge->width(),
+ height() - top_right_corner->height() -
+ bottom_right_corner->height());
+
+ // Bottom.
+ canvas->DrawBitmapInt(*bottom_right_corner,
+ width() - bottom_right_corner->width(),
+ height() - bottom_right_corner->height());
+ canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(),
+ height() - bottom_edge->height(),
+ width() - bottom_left_corner->width() -
+ bottom_right_corner->width(),
+ bottom_edge->height());
+ canvas->DrawBitmapInt(*bottom_left_corner, 0,
+ height() - bottom_left_corner->height());
+
+ // Left.
+ canvas->TileImageInt(*left_edge, 0, top_left_corner->height(),
+ left_edge->width(),
+ height() - top_left_corner->height() - bottom_left_corner->height());
+}
+
+void CustomFrameView::PaintMaximizedFrameBorder(
+ ChromeCanvas* canvas) {
+ SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE);
+ canvas->TileImageInt(*top_edge, 0, FrameBorderThickness(), width(),
+ top_edge->height());
+
+ // The bottom of the titlebar actually comes from the top of the Client Edge
+ // graphic, with the actual client edge clipped off the bottom.
+ SkBitmap* titlebar_bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP);
+ int edge_height = titlebar_bottom->height() - kClientEdgeThickness;
+ canvas->TileImageInt(*titlebar_bottom, 0,
+ frame_->GetClientView()->y() - edge_height, width(), edge_height);
+}
+
+void CustomFrameView::PaintTitleBar(ChromeCanvas* canvas) {
+ WindowDelegate* d = frame_->GetDelegate();
+
+ // It seems like in some conditions we can be asked to paint after the window
+ // that contains us is WM_DESTROYed. At this point, our delegate is NULL. The
+ // correct long term fix may be to shut down the RootView in WM_DESTROY.
+ if (!d)
+ return;
+
+ canvas->DrawStringInt(d->GetWindowTitle(), *title_font_, SK_ColorWHITE,
+ MirroredLeftPointForRect(title_bounds_), title_bounds_.y(),
+ title_bounds_.width(), title_bounds_.height());
+}
+
+void CustomFrameView::PaintRestoredClientEdge(ChromeCanvas* canvas) {
+ gfx::Rect client_area_bounds = frame_->GetClientView()->bounds();
+ int client_area_top = client_area_bounds.y();
+
+ SkBitmap* top_left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT);
+ SkBitmap* top = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP);
+ SkBitmap* top_right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_RIGHT);
+ SkBitmap* right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_RIGHT);
+ SkBitmap* bottom_right =
+ resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_RIGHT);
+ SkBitmap* bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM);
+ SkBitmap* bottom_left =
+ resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_LEFT);
+ SkBitmap* left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_LEFT);
+
+ // Top.
+ // This next calculation is necessary because the top center bitmap is shorter
+ // than the top left and right bitmaps. We need their top edges to line up,
+ // and we need the left and right edges to start below the corners' bottoms.
+ int top_edge_y = client_area_top - top->height();
+ client_area_top = top_edge_y + top_left->height();
+ canvas->DrawBitmapInt(*top_left, client_area_bounds.x() - top_left->width(),
+ top_edge_y);
+ canvas->TileImageInt(*top, client_area_bounds.x(), top_edge_y,
+ client_area_bounds.width(), top->height());
+ canvas->DrawBitmapInt(*top_right, client_area_bounds.right(), top_edge_y);
+
+ // Right.
+ int client_area_bottom =
+ std::max(client_area_top, client_area_bounds.bottom());
+ int client_area_height = client_area_bottom - client_area_top;
+ canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top,
+ right->width(), client_area_height);
+
+ // Bottom.
+ canvas->DrawBitmapInt(*bottom_right, client_area_bounds.right(),
+ client_area_bottom);
+ canvas->TileImageInt(*bottom, client_area_bounds.x(), client_area_bottom,
+ client_area_bounds.width(), bottom_right->height());
+ canvas->DrawBitmapInt(*bottom_left,
+ client_area_bounds.x() - bottom_left->width(), client_area_bottom);
+
+ // Left.
+ canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
+ client_area_top, left->width(), client_area_height);
+}
+
+void CustomFrameView::LayoutWindowControls() {
+ close_button_->SetImageAlignment(ImageButton::ALIGN_LEFT,
+ ImageButton::ALIGN_BOTTOM);
+ // Maximized buttons start at window top so that even if their images aren't
+ // drawn flush with the screen edge, they still obey Fitts' Law.
+ bool is_maximized = frame_->IsMaximized();
+ int frame_thickness = FrameBorderThickness();
+ int caption_y = is_maximized ? frame_thickness : kCaptionTopSpacing;
+ int top_extra_height = is_maximized ? kCaptionTopSpacing : 0;
+ // There should always be the same number of non-shadow pixels visible to the
+ // side of the caption buttons. In maximized mode we extend the rightmost
+ // button to the screen corner to obey Fitts' Law.
+ int right_extra_width = is_maximized ?
+ (kFrameBorderThickness - kFrameShadowThickness) : 0;
+ gfx::Size close_button_size = close_button_->GetPreferredSize();
+ close_button_->SetBounds(width() - close_button_size.width() -
+ right_extra_width - frame_thickness, caption_y,
+ close_button_size.width() + right_extra_width,
+ close_button_size.height() + top_extra_height);
+
+ // When the window is restored, we show a maximized button; otherwise, we show
+ // a restore button.
+ bool is_restored = !is_maximized && !frame_->IsMinimized();
+ views::ImageButton* invisible_button = is_restored ?
+ restore_button_ : maximize_button_;
+ invisible_button->SetVisible(false);
+
+ views::ImageButton* visible_button = is_restored ?
+ maximize_button_ : restore_button_;
+ FramePartBitmap normal_part, hot_part, pushed_part;
+ if (should_show_minmax_buttons_) {
+ visible_button->SetVisible(true);
+ visible_button->SetImageAlignment(ImageButton::ALIGN_LEFT,
+ ImageButton::ALIGN_BOTTOM);
+ gfx::Size visible_button_size = visible_button->GetPreferredSize();
+ visible_button->SetBounds(close_button_->x() - visible_button_size.width(),
+ caption_y, visible_button_size.width(),
+ visible_button_size.height() + top_extra_height);
+
+ minimize_button_->SetVisible(true);
+ minimize_button_->SetImageAlignment(ImageButton::ALIGN_LEFT,
+ ImageButton::ALIGN_BOTTOM);
+ gfx::Size minimize_button_size = minimize_button_->GetPreferredSize();
+ minimize_button_->SetBounds(
+ visible_button->x() - minimize_button_size.width(), caption_y,
+ minimize_button_size.width(),
+ minimize_button_size.height() + top_extra_height);
+
+ normal_part = FRAME_CLOSE_BUTTON_ICON;
+ hot_part = FRAME_CLOSE_BUTTON_ICON_H;
+ pushed_part = FRAME_CLOSE_BUTTON_ICON_P;
+ } else {
+ visible_button->SetVisible(false);
+ minimize_button_->SetVisible(false);
+
+ normal_part = FRAME_CLOSE_BUTTON_ICON_SA;
+ hot_part = FRAME_CLOSE_BUTTON_ICON_SA_H;
+ pushed_part = FRAME_CLOSE_BUTTON_ICON_SA_P;
+ }
+
+ close_button_->SetImage(CustomButton::BS_NORMAL,
+ active_resources_->GetPartBitmap(normal_part));
+ close_button_->SetImage(CustomButton::BS_HOT,
+ active_resources_->GetPartBitmap(hot_part));
+ close_button_->SetImage(CustomButton::BS_PUSHED,
+ active_resources_->GetPartBitmap(pushed_part));
+}
+
+void CustomFrameView::LayoutTitleBar() {
+ // Always lay out the icon, even when it's not present, so we can lay out the
+ // window title based on its position.
+ int frame_thickness = FrameBorderThickness();
+ int icon_x = frame_thickness + kIconLeftSpacing;
+
+ // The usable height of the titlebar area is the total height minus the top
+ // resize border and any edge area we draw at its bottom.
+ int title_top_spacing, title_thickness;
+ int top_height = TitleCoordinates(&title_top_spacing, &title_thickness);
+ int available_height = top_height - frame_thickness -
+ BottomEdgeThicknessWithinNonClientHeight();
+
+ // The icon takes up a constant fraction of the available height, down to a
+ // minimum size, and is always an even number of pixels on a side (presumably
+ // to make scaled icons look better). It's centered within the usable height.
+ int icon_size = std::max((available_height * kIconHeightFractionNumerator /
+ kIconHeightFractionDenominator) / 2 * 2, kIconMinimumSize);
+ int icon_y = ((available_height - icon_size) / 2) + frame_thickness;
+
+ // Hack: Our frame border has a different "3D look" than Windows'. Theirs has
+ // a more complex gradient on the top that they push their icon/title below;
+ // then the maximized window cuts this off and the icon/title are centered in
+ // the remaining space. Because the apparent shape of our border is simpler,
+ // using the same positioning makes things look slightly uncentered with
+ // restored windows, so we come up to compensate.
+ if (!frame_->IsMaximized())
+ icon_y -= kIconRestoredAdjust;
+
+ views::WindowDelegate* d = frame_->GetDelegate();
+ if (!d->ShouldShowWindowIcon())
+ icon_size = 0;
+ system_menu_button_->SetBounds(icon_x, icon_y, icon_size, icon_size);
+
+ // Size the title.
+ int icon_right = icon_x + icon_size;
+ int title_x =
+ icon_right + (d->ShouldShowWindowIcon() ? kIconTitleSpacing : 0);
+ int title_right = (should_show_minmax_buttons_ ?
+ minimize_button_->x() : close_button_->x()) - kTitleCaptionSpacing;
+ title_bounds_.SetRect(title_x,
+ title_top_spacing + ((title_thickness - title_font_->height()) / 2),
+ std::max(0, title_right - title_x), title_font_->height());
+}
+
+void CustomFrameView::LayoutClientView() {
+ int top_height = NonClientTopBorderHeight();
+ int border_thickness = NonClientBorderThickness();
+ client_view_bounds_.SetRect(
+ border_thickness,
+ top_height,
+ std::max(0, width() - (2 * border_thickness)),
+ std::max(0, height() - top_height - border_thickness));
+}
+
+// static
+void CustomFrameView::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ active_resources_ = new ActiveWindowResources;
+ inactive_resources_ = new InactiveWindowResources;
+
+ title_font_ = new ChromeFont(win_util::GetWindowTitleFont());
+
+ initialized = true;
+ }
+}
+
+} // namespace views
diff --git a/views/window/custom_frame_view.h b/views/window/custom_frame_view.h
new file mode 100644
index 0000000..f071692
--- /dev/null
+++ b/views/window/custom_frame_view.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2009 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_WINDOW_CUSTOM_FRAME_VIEW_H_
+#define VIEWS_WINDOW_CUSTOM_FRAME_VIEW_H_
+
+#include "views/controls/button/image_button.h"
+#include "views/window/non_client_view.h"
+#include "views/window/window.h"
+#include "views/window/window_resources.h"
+
+namespace gfx{
+class Size;
+class Path;
+class Point;
+}
+class ChromeCanvas;
+class ChromeFont;
+
+namespace views {
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// CustomFrameView
+//
+// A ChromeView that provides the non client frame for Windows. This means
+// rendering the non-standard window caption, border, and controls.
+//
+////////////////////////////////////////////////////////////////////////////////
+class CustomFrameView : public NonClientFrameView,
+ public ButtonListener {
+ public:
+ explicit CustomFrameView(Window* frame);
+ virtual ~CustomFrameView();
+
+ // Overridden from views::NonClientFrameView:
+ virtual gfx::Rect GetBoundsForClientView() const;
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const;
+ virtual gfx::Point GetSystemMenuPoint() const;
+ virtual int NonClientHitTest(const gfx::Point& point);
+ virtual void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask);
+ virtual void EnableClose(bool enable);
+ virtual void ResetWindowControls();
+
+ // View overrides:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual gfx::Size GetPreferredSize();
+
+ // ButtonListener implementation:
+ virtual void ButtonPressed(Button* sender);
+
+ private:
+ // Returns the thickness of the border that makes up the window frame edges.
+ // This does not include any client edge.
+ int FrameBorderThickness() const;
+
+ // Returns the thickness of the entire nonclient left, right, and bottom
+ // borders, including both the window frame and any client edge.
+ int NonClientBorderThickness() const;
+
+ // Returns the height of the entire nonclient top border, including the window
+ // frame, any title area, and any connected client edge.
+ int NonClientTopBorderHeight() const;
+
+ // A bottom border, and, in restored mode, a client edge are drawn at the
+ // bottom of the titlebar. This returns the total height drawn.
+ int BottomEdgeThicknessWithinNonClientHeight() const;
+
+ // Calculates multiple values related to title layout. Returns the height of
+ // the entire titlebar including any connected client edge.
+ int TitleCoordinates(int* title_top_spacing,
+ int* title_thickness) const;
+
+ // Paint various sub-components of this view.
+ void PaintRestoredFrameBorder(ChromeCanvas* canvas);
+ void PaintMaximizedFrameBorder(ChromeCanvas* canvas);
+ void PaintTitleBar(ChromeCanvas* canvas);
+ void PaintRestoredClientEdge(ChromeCanvas* canvas);
+
+ // Layout various sub-components of this view.
+ void LayoutWindowControls();
+ void LayoutTitleBar();
+ void LayoutClientView();
+
+ // Returns the resource collection to be used when rendering the window.
+ WindowResources* resources() const {
+ return frame_->IsActive() || paint_as_active() ? active_resources_
+ : inactive_resources_;
+ }
+
+ // The bounds of the client view, in this view's coordinates.
+ gfx::Rect client_view_bounds_;
+
+ // The layout rect of the title, if visible.
+ gfx::Rect title_bounds_;
+
+ // Window controls.
+ ImageButton* close_button_;
+ ImageButton* restore_button_;
+ ImageButton* maximize_button_;
+ ImageButton* minimize_button_;
+ ImageButton* system_menu_button_; // Uses the window icon if visible.
+ bool should_show_minmax_buttons_;
+
+ // The window that owns this view.
+ Window* frame_;
+
+ // Initialize various static resources.
+ static void InitClass();
+ static WindowResources* active_resources_;
+ static WindowResources* inactive_resources_;
+ static ChromeFont* title_font_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CustomFrameView);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_WINDOW_CUSTOM_FRAME_VIEW_H_
diff --git a/views/window/dialog_client_view.cc b/views/window/dialog_client_view.cc
new file mode 100644
index 0000000..b7989ac
--- /dev/null
+++ b/views/window/dialog_client_view.cc
@@ -0,0 +1,440 @@
+// Copyright (c) 2006-2008 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.
+
+#include "views/window/dialog_client_view.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+
+#include "app/gfx/chrome_canvas.h"
+#include "app/gfx/chrome_font.h"
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/gfx/native_theme.h"
+#include "chrome/browser/views/standard_layout.h"
+#include "grit/generated_resources.h"
+#include "views/controls/button/native_button.h"
+#include "views/window/dialog_delegate.h"
+#include "views/window/window.h"
+
+namespace views {
+
+namespace {
+
+// Updates any of the standard buttons according to the delegate.
+void UpdateButtonHelper(NativeButton* button_view,
+ DialogDelegate* delegate,
+ MessageBoxFlags::DialogButton button) {
+ std::wstring label = delegate->GetDialogButtonLabel(button);
+ if (!label.empty())
+ button_view->SetLabel(label);
+ button_view->SetEnabled(delegate->IsDialogButtonEnabled(button));
+ button_view->SetVisible(delegate->IsDialogButtonVisible(button));
+}
+
+void FillViewWithSysColor(ChromeCanvas* canvas, View* view, COLORREF color) {
+ SkColor sk_color =
+ SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color));
+ canvas->FillRectInt(sk_color, 0, 0, view->width(), view->height());
+}
+
+// DialogButton ----------------------------------------------------------------
+
+// DialogButtons is used for the ok/cancel buttons of the window. DialogButton
+// forwards AcceleratorPressed to the delegate.
+
+class DialogButton : public NativeButton {
+ public:
+ DialogButton(ButtonListener* listener,
+ Window* owner,
+ MessageBoxFlags::DialogButton type,
+ const std::wstring& title,
+ bool is_default)
+ : NativeButton(listener, title),
+ owner_(owner),
+ type_(type) {
+ SetIsDefault(is_default);
+ }
+
+ // Overridden to forward to the delegate.
+ virtual bool AcceleratorPressed(const Accelerator& accelerator) {
+ if (!owner_->GetDelegate()->AsDialogDelegate()->
+ AreAcceleratorsEnabled(type_)) {
+ return false;
+ }
+ return NativeButton::AcceleratorPressed(accelerator);
+ }
+
+ private:
+ Window* owner_;
+ const MessageBoxFlags::DialogButton type_;
+
+ DISALLOW_COPY_AND_ASSIGN(DialogButton);
+};
+
+} // namespace
+
+// static
+ChromeFont* DialogClientView::dialog_button_font_ = NULL;
+static const int kDialogMinButtonWidth = 75;
+static const int kDialogButtonLabelSpacing = 16;
+static const int kDialogButtonContentSpacing = 5;
+
+// The group used by the buttons. This name is chosen voluntarily big not to
+// conflict with other groups that could be in the dialog content.
+static const int kButtonGroup = 6666;
+
+///////////////////////////////////////////////////////////////////////////////
+// DialogClientView, public:
+
+DialogClientView::DialogClientView(Window* owner, View* contents_view)
+ : ClientView(owner, contents_view),
+ ok_button_(NULL),
+ cancel_button_(NULL),
+ extra_view_(NULL),
+ accepted_(false),
+ default_button_(NULL) {
+ InitClass();
+}
+
+DialogClientView::~DialogClientView() {
+}
+
+void DialogClientView::ShowDialogButtons() {
+ DialogDelegate* dd = GetDialogDelegate();
+ int buttons = dd->GetDialogButtons();
+ if (buttons & MessageBoxFlags::DIALOGBUTTON_OK && !ok_button_) {
+ std::wstring label =
+ dd->GetDialogButtonLabel(MessageBoxFlags::DIALOGBUTTON_OK);
+ if (label.empty())
+ label = l10n_util::GetString(IDS_OK);
+ bool is_default_button =
+ (dd->GetDefaultDialogButton() & MessageBoxFlags::DIALOGBUTTON_OK) != 0;
+ ok_button_ = new DialogButton(this, window(),
+ MessageBoxFlags::DIALOGBUTTON_OK, label,
+ is_default_button);
+ ok_button_->SetGroup(kButtonGroup);
+ if (is_default_button)
+ default_button_ = ok_button_;
+ if (!(buttons & MessageBoxFlags::DIALOGBUTTON_CANCEL))
+ ok_button_->AddAccelerator(Accelerator(VK_ESCAPE, false, false, false));
+ AddChildView(ok_button_);
+ }
+ if (buttons & MessageBoxFlags::DIALOGBUTTON_CANCEL && !cancel_button_) {
+ std::wstring label =
+ dd->GetDialogButtonLabel(MessageBoxFlags::DIALOGBUTTON_CANCEL);
+ if (label.empty()) {
+ if (buttons & MessageBoxFlags::DIALOGBUTTON_OK) {
+ label = l10n_util::GetString(IDS_CANCEL);
+ } else {
+ label = l10n_util::GetString(IDS_CLOSE);
+ }
+ }
+ bool is_default_button =
+ (dd->GetDefaultDialogButton() & MessageBoxFlags::DIALOGBUTTON_CANCEL)
+ != 0;
+ cancel_button_ = new DialogButton(this, window(),
+ MessageBoxFlags::DIALOGBUTTON_CANCEL,
+ label, is_default_button);
+ cancel_button_->SetGroup(kButtonGroup);
+ cancel_button_->AddAccelerator(Accelerator(VK_ESCAPE, false, false, false));
+ if (is_default_button)
+ default_button_ = ok_button_;
+ AddChildView(cancel_button_);
+ }
+ if (!buttons) {
+ // Register the escape key as an accelerator which will close the window
+ // if there are no dialog buttons.
+ AddAccelerator(Accelerator(VK_ESCAPE, false, false, false));
+ }
+}
+
+void DialogClientView::SetDefaultButton(NativeButton* new_default_button) {
+ if (default_button_ && default_button_ != new_default_button) {
+ default_button_->SetIsDefault(false);
+ default_button_ = NULL;
+ }
+
+ if (new_default_button) {
+ default_button_ = new_default_button;
+ default_button_->SetIsDefault(true);
+ }
+}
+
+void DialogClientView::FocusWillChange(View* focused_before,
+ View* focused_now) {
+ NativeButton* new_default_button = NULL;
+ if (focused_now &&
+ focused_now->GetClassName() == NativeButton::kViewClassName) {
+ new_default_button = static_cast<NativeButton*>(focused_now);
+ } else {
+ // The focused view is not a button, get the default button from the
+ // delegate.
+ DialogDelegate* dd = GetDialogDelegate();
+ if ((dd->GetDefaultDialogButton() & MessageBoxFlags::DIALOGBUTTON_OK) != 0)
+ new_default_button = ok_button_;
+ if ((dd->GetDefaultDialogButton() & MessageBoxFlags::DIALOGBUTTON_CANCEL)
+ != 0)
+ new_default_button = cancel_button_;
+ }
+ SetDefaultButton(new_default_button);
+}
+
+// Changing dialog labels will change button widths.
+void DialogClientView::UpdateDialogButtons() {
+ DialogDelegate* dd = GetDialogDelegate();
+ int buttons = dd->GetDialogButtons();
+
+ if (buttons & MessageBoxFlags::DIALOGBUTTON_OK)
+ UpdateButtonHelper(ok_button_, dd, MessageBoxFlags::DIALOGBUTTON_OK);
+
+ if (buttons & MessageBoxFlags::DIALOGBUTTON_CANCEL) {
+ UpdateButtonHelper(cancel_button_, dd,
+ MessageBoxFlags::DIALOGBUTTON_CANCEL);
+ }
+
+ LayoutDialogButtons();
+ SchedulePaint();
+}
+
+void DialogClientView::AcceptWindow() {
+ if (accepted_) {
+ // We should only get into AcceptWindow once.
+ NOTREACHED();
+ return;
+ }
+ if (GetDialogDelegate()->Accept(false)) {
+ accepted_ = true;
+ window()->Close();
+ }
+}
+
+void DialogClientView::CancelWindow() {
+ // Call the standard Close handler, which checks with the delegate before
+ // proceeding. This checking _isn't_ done here, but in the WM_CLOSE handler,
+ // so that the close box on the window also shares this code path.
+ window()->Close();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DialogClientView, ClientView overrides:
+
+bool DialogClientView::CanClose() const {
+ if (!accepted_) {
+ DialogDelegate* dd = GetDialogDelegate();
+ int buttons = dd->GetDialogButtons();
+ if (buttons & MessageBoxFlags::DIALOGBUTTON_CANCEL)
+ return dd->Cancel();
+ if (buttons & MessageBoxFlags::DIALOGBUTTON_OK)
+ return dd->Accept(true);
+ }
+ return true;
+}
+
+void DialogClientView::WindowClosing() {
+ FocusManager* focus_manager = GetFocusManager();
+ DCHECK(focus_manager);
+ if (focus_manager)
+ focus_manager->RemoveFocusChangeListener(this);
+ ClientView::WindowClosing();
+}
+
+int DialogClientView::NonClientHitTest(const gfx::Point& point) {
+ if (size_box_bounds_.Contains(point.x() - x(), point.y() - y()))
+ return HTBOTTOMRIGHT;
+ return ClientView::NonClientHitTest(point);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DialogClientView, View overrides:
+
+void DialogClientView::Paint(ChromeCanvas* canvas) {
+ FillViewWithSysColor(canvas, this, GetSysColor(COLOR_3DFACE));
+}
+
+void DialogClientView::PaintChildren(ChromeCanvas* canvas) {
+ View::PaintChildren(canvas);
+ if (!window()->IsMaximized() && !window()->IsMinimized())
+ PaintSizeBox(canvas);
+}
+
+void DialogClientView::Layout() {
+ if (has_dialog_buttons())
+ LayoutDialogButtons();
+ LayoutContentsView();
+}
+
+void DialogClientView::ViewHierarchyChanged(bool is_add, View* parent,
+ View* child) {
+ if (is_add && child == this) {
+ // Can only add and update the dialog buttons _after_ they are added to the
+ // view hierarchy since they are native controls and require the
+ // Container's HWND.
+ ShowDialogButtons();
+ ClientView::ViewHierarchyChanged(is_add, parent, child);
+
+ FocusManager* focus_manager = GetFocusManager();
+ // Listen for focus change events so we can update the default button.
+ DCHECK(focus_manager); // bug #1291225: crash reports seem to indicate it
+ // can be NULL.
+ if (focus_manager)
+ focus_manager->AddFocusChangeListener(this);
+
+ // The "extra view" must be created and installed after the contents view
+ // has been inserted into the view hierarchy.
+ CreateExtraView();
+ UpdateDialogButtons();
+ Layout();
+ }
+}
+
+gfx::Size DialogClientView::GetPreferredSize() {
+ gfx::Size prefsize = contents_view()->GetPreferredSize();
+ int button_height = 0;
+ if (has_dialog_buttons()) {
+ if (cancel_button_)
+ button_height = cancel_button_->height();
+ else
+ button_height = ok_button_->height();
+ // Account for padding above and below the button.
+ button_height += kDialogButtonContentSpacing + kButtonVEdgeMargin;
+ }
+ prefsize.Enlarge(0, button_height);
+ return prefsize;
+}
+
+bool DialogClientView::AcceleratorPressed(const Accelerator& accelerator) {
+ DCHECK(accelerator.GetKeyCode() == VK_ESCAPE); // We only expect Escape key.
+ window()->Close();
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DialogClientView, ButtonListener implementation:
+
+void DialogClientView::ButtonPressed(Button* sender) {
+ if (sender == ok_button_) {
+ AcceptWindow();
+ } else if (sender == cancel_button_) {
+ CancelWindow();
+ } else {
+ NOTREACHED();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DialogClientView, private:
+
+void DialogClientView::PaintSizeBox(ChromeCanvas* canvas) {
+ if (window()->GetDelegate()->CanResize() ||
+ window()->GetDelegate()->CanMaximize()) {
+ HDC dc = canvas->beginPlatformPaint();
+ SIZE gripper_size = { 0, 0 };
+ gfx::NativeTheme::instance()->GetThemePartSize(
+ gfx::NativeTheme::STATUS, dc, SP_GRIPPER, 1, NULL, TS_TRUE,
+ &gripper_size);
+
+ // TODO(beng): (http://b/1085509) In "classic" rendering mode, there isn't
+ // a theme-supplied gripper. We should probably improvise
+ // something, which would also require changing |gripper_size|
+ // to have different default values, too...
+ size_box_bounds_ = GetLocalBounds(false);
+ size_box_bounds_.set_x(size_box_bounds_.right() - gripper_size.cx);
+ size_box_bounds_.set_y(size_box_bounds_.bottom() - gripper_size.cy);
+ RECT native_bounds = size_box_bounds_.ToRECT();
+ gfx::NativeTheme::instance()->PaintStatusGripper(
+ dc, SP_PANE, 1, 0, &native_bounds);
+ canvas->endPlatformPaint();
+ }
+}
+
+int DialogClientView::GetButtonWidth(int button) const {
+ DialogDelegate* dd = GetDialogDelegate();
+ std::wstring button_label = dd->GetDialogButtonLabel(
+ static_cast<MessageBoxFlags::DialogButton>(button));
+ int string_width = dialog_button_font_->GetStringWidth(button_label);
+ return std::max(string_width + kDialogButtonLabelSpacing,
+ kDialogMinButtonWidth);
+}
+
+int DialogClientView::GetButtonsHeight() const {
+ if (has_dialog_buttons()) {
+ if (cancel_button_)
+ return cancel_button_->height() + kDialogButtonContentSpacing;
+ return ok_button_->height() + kDialogButtonContentSpacing;
+ }
+ return 0;
+}
+
+void DialogClientView::LayoutDialogButtons() {
+ gfx::Rect extra_bounds;
+ if (cancel_button_) {
+ gfx::Size ps = cancel_button_->GetPreferredSize();
+ gfx::Rect lb = GetLocalBounds(false);
+ int button_width = GetButtonWidth(MessageBoxFlags::DIALOGBUTTON_CANCEL);
+ int button_x = lb.right() - button_width - kButtonHEdgeMargin;
+ int button_y = lb.bottom() - ps.height() - kButtonVEdgeMargin;
+ cancel_button_->SetBounds(button_x, button_y, button_width, ps.height());
+ // The extra view bounds are dependent on this button.
+ extra_bounds.set_width(std::max(0, cancel_button_->x()));
+ extra_bounds.set_y(cancel_button_->y());
+ }
+ if (ok_button_) {
+ gfx::Size ps = ok_button_->GetPreferredSize();
+ gfx::Rect lb = GetLocalBounds(false);
+ int button_width = GetButtonWidth(MessageBoxFlags::DIALOGBUTTON_OK);
+ int ok_button_right = lb.right() - kButtonHEdgeMargin;
+ if (cancel_button_)
+ ok_button_right = cancel_button_->x() - kRelatedButtonHSpacing;
+ int button_x = ok_button_right - button_width;
+ int button_y = lb.bottom() - ps.height() - kButtonVEdgeMargin;
+ ok_button_->SetBounds(button_x, button_y, ok_button_right - button_x,
+ ps.height());
+ // The extra view bounds are dependent on this button.
+ extra_bounds.set_width(std::max(0, ok_button_->x()));
+ extra_bounds.set_y(ok_button_->y());
+ }
+ if (extra_view_) {
+ gfx::Size ps = extra_view_->GetPreferredSize();
+ gfx::Rect lb = GetLocalBounds(false);
+ extra_bounds.set_x(lb.x() + kButtonHEdgeMargin);
+ extra_bounds.set_height(ps.height());
+ extra_view_->SetBounds(extra_bounds);
+ }
+}
+
+void DialogClientView::LayoutContentsView() {
+ gfx::Rect lb = GetLocalBounds(false);
+ lb.set_height(std::max(0, lb.height() - GetButtonsHeight()));
+ contents_view()->SetBounds(lb);
+ contents_view()->Layout();
+}
+
+void DialogClientView::CreateExtraView() {
+ View* extra_view = GetDialogDelegate()->GetExtraView();
+ if (extra_view && !extra_view_) {
+ extra_view_ = extra_view;
+ extra_view_->SetGroup(kButtonGroup);
+ AddChildView(extra_view_);
+ }
+}
+
+DialogDelegate* DialogClientView::GetDialogDelegate() const {
+ DialogDelegate* dd = window()->GetDelegate()->AsDialogDelegate();
+ DCHECK(dd);
+ return dd;
+}
+
+// static
+void DialogClientView::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ dialog_button_font_ = new ChromeFont(rb.GetFont(ResourceBundle::BaseFont));
+ initialized = true;
+ }
+}
+
+} // namespace views
diff --git a/views/window/dialog_client_view.h b/views/window/dialog_client_view.h
new file mode 100644
index 0000000..347b61c
--- /dev/null
+++ b/views/window/dialog_client_view.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2006-2008 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_WINDOW_DIALOG_CLIENT_VIEW_H_
+#define VIEWS_WINDOW_DIALOG_CLIENT_VIEW_H_
+
+#include "app/gfx/chrome_font.h"
+#include "views/focus/focus_manager.h"
+#include "views/controls/button/button.h"
+#include "views/window/client_view.h"
+
+namespace views {
+
+class DialogDelegate;
+class NativeButton;
+class Window;
+
+///////////////////////////////////////////////////////////////////////////////
+// DialogClientView
+//
+// This ClientView subclass provides the content of a typical dialog box,
+// including a strip of buttons at the bottom right of the window, default
+// accelerator handlers for accept and cancel, and the ability for the
+// embedded contents view to provide extra UI to be shown in the row of
+// buttons.
+//
+class DialogClientView : public ClientView,
+ public ButtonListener,
+ public FocusChangeListener {
+ public:
+ DialogClientView(Window* window, View* contents_view);
+ virtual ~DialogClientView();
+
+ // Adds the dialog buttons required by the supplied WindowDelegate to the
+ // view.
+ void ShowDialogButtons();
+
+ // Updates the enabled state and label of the buttons required by the
+ // supplied WindowDelegate
+ void UpdateDialogButtons();
+
+ // Accept the changes made in the window that contains this ClientView.
+ void AcceptWindow();
+
+ // Cancel the changes made in the window that contains this ClientView.
+ void CancelWindow();
+
+ // Accessors in case the user wishes to adjust these buttons.
+ NativeButton* ok_button() const { return ok_button_; }
+ NativeButton* cancel_button() const { return cancel_button_; }
+
+ // Overridden from ClientView:
+ virtual bool CanClose() const;
+ virtual void WindowClosing();
+ virtual int NonClientHitTest(const gfx::Point& point);
+ virtual DialogClientView* AsDialogClientView() { return this; }
+
+ // FocusChangeListener implementation:
+ virtual void FocusWillChange(View* focused_before, View* focused_now);
+
+ protected:
+ // View overrides:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void PaintChildren(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+ virtual gfx::Size GetPreferredSize();
+ virtual bool AcceleratorPressed(const Accelerator& accelerator);
+
+ // ButtonListener implementation:
+ virtual void ButtonPressed(Button* sender);
+
+ private:
+ // Paint the size box in the bottom right corner of the window if it is
+ // resizable.
+ void PaintSizeBox(ChromeCanvas* canvas);
+
+ // Returns the width of the specified dialog button using the correct font.
+ int GetButtonWidth(int button) const;
+ int GetButtonsHeight() const;
+
+ // Position and size various sub-views.
+ void LayoutDialogButtons();
+ void LayoutContentsView();
+
+ // Makes the specified button the default button.
+ void SetDefaultButton(NativeButton* button);
+
+ bool has_dialog_buttons() const { return ok_button_ || cancel_button_; }
+
+ // Create and add the extra view, if supplied by the delegate.
+ void CreateExtraView();
+
+ // Returns the DialogDelegate for the window.
+ DialogDelegate* GetDialogDelegate() const;
+
+ // The dialog buttons.
+ NativeButton* ok_button_;
+ NativeButton* cancel_button_;
+
+ // The button that is currently the default button if any.
+ NativeButton* default_button_;
+
+ // The button-level extra view, NULL unless the dialog delegate supplies one.
+ View* extra_view_;
+
+ // The layout rect of the size box, when visible.
+ gfx::Rect size_box_bounds_;
+
+ // True if the window was Accepted by the user using the OK button.
+ bool accepted_;
+
+ // Static resource initialization
+ static void InitClass();
+ static ChromeFont* dialog_button_font_;
+
+ DISALLOW_COPY_AND_ASSIGN(DialogClientView);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_WINDOW_DIALOG_CLIENT_VIEW_H_
diff --git a/views/window/dialog_delegate.cc b/views/window/dialog_delegate.cc
new file mode 100644
index 0000000..4ae9a91
--- /dev/null
+++ b/views/window/dialog_delegate.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2006-2008 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.
+
+#include "views/window/dialog_delegate.h"
+
+#include "base/logging.h"
+#include "views/controls/button/native_button.h"
+#include "views/window/window.h"
+
+namespace views {
+
+// Overridden from WindowDelegate:
+
+int DialogDelegate::GetDefaultDialogButton() const {
+ if (GetDialogButtons() & MessageBoxFlags::DIALOGBUTTON_OK)
+ return MessageBoxFlags::DIALOGBUTTON_OK;
+ if (GetDialogButtons() & MessageBoxFlags::DIALOGBUTTON_CANCEL)
+ return MessageBoxFlags::DIALOGBUTTON_CANCEL;
+ return MessageBoxFlags::DIALOGBUTTON_NONE;
+}
+
+View* DialogDelegate::GetInitiallyFocusedView() {
+ // Focus the default button if any.
+ DialogClientView* dcv = GetDialogClientView();
+ int default_button = GetDefaultDialogButton();
+ if (default_button == MessageBoxFlags::DIALOGBUTTON_NONE)
+ return NULL;
+
+ if ((default_button & GetDialogButtons()) == 0) {
+ // The default button is a button we don't have.
+ NOTREACHED();
+ return NULL;
+ }
+
+ if (default_button & MessageBoxFlags::DIALOGBUTTON_OK)
+ return dcv->ok_button();
+ if (default_button & MessageBoxFlags::DIALOGBUTTON_CANCEL)
+ return dcv->cancel_button();
+ return NULL;
+}
+
+ClientView* DialogDelegate::CreateClientView(Window* window) {
+ return new DialogClientView(window, GetContentsView());
+}
+
+DialogClientView* DialogDelegate::GetDialogClientView() const {
+ ClientView* client_view = window()->GetClientView();
+ DialogClientView* dialog_client_view = client_view->AsDialogClientView();
+ DCHECK(dialog_client_view);
+ return dialog_client_view;
+}
+
+} // namespace views
diff --git a/views/window/dialog_delegate.h b/views/window/dialog_delegate.h
new file mode 100644
index 0000000..4418e61f
--- /dev/null
+++ b/views/window/dialog_delegate.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2006-2008 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_WINDOW_DIALOG_DELEGATE_H_
+#define VIEWS_WINDOW_DIALOG_DELEGATE_H_
+
+#include "app/message_box_flags.h"
+#include "views/window/dialog_client_view.h"
+#include "views/window/window_delegate.h"
+
+namespace views {
+
+class View;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// DialogDelegate
+//
+// DialogDelegate is an interface implemented by objects that wish to show a
+// dialog box Window. The window that is displayed uses this interface to
+// determine how it should be displayed and notify the delegate object of
+// certain events.
+//
+///////////////////////////////////////////////////////////////////////////////
+class DialogDelegate : public WindowDelegate {
+ public:
+ virtual DialogDelegate* AsDialogDelegate() { return this; }
+
+ // Returns a mask specifying which of the available DialogButtons are visible
+ // for the dialog. Note: If an OK button is provided, you should provide a
+ // CANCEL button. A dialog box with just an OK button is frowned upon and
+ // considered a very special case, so if you're planning on including one,
+ // you should reconsider, or beng says there will be stabbings.
+ //
+ // To use the extra button you need to override GetDialogButtons()
+ virtual int GetDialogButtons() const {
+ return MessageBoxFlags::DIALOGBUTTON_OK |
+ MessageBoxFlags::DIALOGBUTTON_CANCEL;
+ }
+
+ // Returns whether accelerators are enabled on the button. This is invoked
+ // when an accelerator is pressed, not at construction time. This
+ // returns true.
+ virtual bool AreAcceleratorsEnabled(MessageBoxFlags::DialogButton button) {
+ return true;
+ }
+
+ // Returns the label of the specified DialogButton.
+ virtual std::wstring GetDialogButtonLabel(
+ MessageBoxFlags::DialogButton button) const {
+ // empty string results in defaults for MessageBoxFlags::DIALOGBUTTON_OK,
+ // MessageBoxFlags::DIALOGBUTTON_CANCEL.
+ return L"";
+ }
+
+ // Override this function if with a view which will be shown in the same
+ // row as the OK and CANCEL buttons but flush to the left and extending
+ // up to the buttons.
+ virtual View* GetExtraView() { return NULL; }
+
+ // Returns the default dialog button. This should not be a mask as only
+ // one button should ever be the default button. Return
+ // MessageBoxFlags::DIALOGBUTTON_NONE if there is no default. Default
+ // behavior is to return MessageBoxFlags::DIALOGBUTTON_OK or
+ // MessageBoxFlags::DIALOGBUTTON_CANCEL (in that order) if they are
+ // present, MessageBoxFlags::DIALOGBUTTON_NONE otherwise.
+ virtual int GetDefaultDialogButton() const;
+
+ // Returns whether the specified dialog button is enabled.
+ virtual bool IsDialogButtonEnabled(
+ MessageBoxFlags::DialogButton button) const {
+ return true;
+ }
+
+ // Returns whether the specified dialog button is visible.
+ virtual bool IsDialogButtonVisible(
+ MessageBoxFlags::DialogButton button) const {
+ return true;
+ }
+
+ // For Dialog boxes, if there is a "Cancel" button, this is called when the
+ // user presses the "Cancel" button or the Close button on the window or
+ // in the system menu, or presses the Esc key. This function should return
+ // true if the window can be closed after it returns, or false if it must
+ // remain open.
+ virtual bool Cancel() { return true; }
+
+ // For Dialog boxes, this is called when the user presses the "OK" button,
+ // or the Enter key. Can also be called on Esc key or close button
+ // presses if there is no "Cancel" button. This function should return
+ // true if the window can be closed after the window can be closed after
+ // it returns, or false if it must remain open. If |window_closing| is
+ // true, it means that this handler is being called because the window is
+ // being closed (e.g. by Window::Close) and there is no Cancel handler,
+ // so Accept is being called instead.
+ virtual bool Accept(bool window_closing) { return Accept(); }
+ virtual bool Accept() { return true; }
+
+ // Overridden from WindowDelegate:
+ virtual View* GetInitiallyFocusedView();
+
+ // Overridden from WindowDelegate:
+ virtual ClientView* CreateClientView(Window* window);
+
+ // A helper for accessing the DialogClientView object contained by this
+ // delegate's Window.
+ DialogClientView* GetDialogClientView() const;
+};
+
+} // namespace views
+
+#endif // VIEWS_WINDOW_DIALOG_DELEGATE_H_
diff --git a/views/window/native_frame_view.cc b/views/window/native_frame_view.cc
new file mode 100644
index 0000000..05a8dcb
--- /dev/null
+++ b/views/window/native_frame_view.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 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.
+
+#include "views/window/native_frame_view.h"
+
+#include "views/window/window_win.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeFrameView, public:
+
+NativeFrameView::NativeFrameView(WindowWin* frame)
+ : NonClientFrameView(),
+ frame_(frame) {
+}
+
+NativeFrameView::~NativeFrameView() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeFrameView, NonClientFrameView overrides:
+
+gfx::Rect NativeFrameView::GetBoundsForClientView() const {
+ return gfx::Rect(0, 0, width(), height());
+}
+
+gfx::Rect NativeFrameView::GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const {
+ RECT rect = client_bounds.ToRECT();
+ AdjustWindowRectEx(&rect, frame_->window_style(), FALSE,
+ frame_->window_ex_style());
+ return gfx::Rect(rect);
+}
+
+gfx::Point NativeFrameView::GetSystemMenuPoint() const {
+ POINT temp = {0, -kFrameShadowThickness };
+ MapWindowPoints(frame_->GetNativeView(), HWND_DESKTOP, &temp, 1);
+ return gfx::Point(temp);
+}
+
+int NativeFrameView::NonClientHitTest(const gfx::Point& point) {
+ return HTNOWHERE;
+}
+
+void NativeFrameView::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+ // Nothing to do, we use the default window mask.
+}
+
+void NativeFrameView::EnableClose(bool enable) {
+ // Nothing to do, handled automatically by Window.
+}
+
+void NativeFrameView::ResetWindowControls() {
+ // Nothing to do.
+}
+
+} // namespace views
diff --git a/views/window/native_frame_view.h b/views/window/native_frame_view.h
new file mode 100644
index 0000000..e2efa9d
--- /dev/null
+++ b/views/window/native_frame_view.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2009 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_WINDOW_NATIVE_FRAME_VIEW_H_
+#define VIEWS_WINDOW_NATIVE_FRAME_VIEW_H_
+
+#include "views/window/non_client_view.h"
+
+namespace views {
+
+class WindowWin;
+
+class NativeFrameView : public NonClientFrameView {
+ public:
+ explicit NativeFrameView(WindowWin* frame);
+ virtual ~NativeFrameView();
+
+ // NonClientFrameView overrides:
+ virtual gfx::Rect GetBoundsForClientView() const;
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const;
+ virtual gfx::Point GetSystemMenuPoint() const;
+ virtual int NonClientHitTest(const gfx::Point& point);
+ virtual void GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask);
+ virtual void EnableClose(bool enable);
+ virtual void ResetWindowControls();
+
+ private:
+ // Our containing frame.
+ WindowWin* frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeFrameView);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_WINDOW_NATIVE_FRAME_VIEW_H_
diff --git a/views/window/non_client_view.cc b/views/window/non_client_view.cc
new file mode 100644
index 0000000..9a06cfc
--- /dev/null
+++ b/views/window/non_client_view.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2006-2008 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.
+
+#include "views/window/non_client_view.h"
+
+#include "chrome/common/win_util.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget.h"
+#include "views/window/window.h"
+
+namespace views {
+
+const int NonClientFrameView::kFrameShadowThickness = 1;
+const int NonClientFrameView::kClientEdgeThickness = 1;
+
+// The frame view and the client view are always at these specific indices,
+// because the RootView message dispatch sends messages to items higher in the
+// z-order first and we always want the client view to have first crack at
+// handling mouse messages.
+static const int kFrameViewIndex = 0;
+static const int kClientViewIndex = 1;
+
+////////////////////////////////////////////////////////////////////////////////
+// NonClientView, public:
+
+NonClientView::NonClientView(Window* frame)
+ : frame_(frame),
+ client_view_(NULL),
+ use_native_frame_(win_util::ShouldUseVistaFrame()) {
+}
+
+NonClientView::~NonClientView() {
+ // This value may have been reset before the window hierarchy shuts down,
+ // so we need to manually remove it.
+ RemoveChildView(frame_view_.get());
+}
+
+void NonClientView::SetFrameView(NonClientFrameView* frame_view) {
+ // See comment in header about ownership.
+ frame_view->SetParentOwned(false);
+ if (frame_view_.get())
+ RemoveChildView(frame_view_.get());
+ frame_view_.reset(frame_view);
+ if (GetParent())
+ AddChildView(kFrameViewIndex, frame_view_.get());
+}
+
+bool NonClientView::CanClose() const {
+ return client_view_->CanClose();
+}
+
+void NonClientView::WindowClosing() {
+ client_view_->WindowClosing();
+}
+
+void NonClientView::SetUseNativeFrame(bool use_native_frame) {
+ use_native_frame_ = use_native_frame;
+ SetFrameView(frame_->CreateFrameViewForWindow());
+ GetRootView()->ThemeChanged();
+ Layout();
+ SchedulePaint();
+ frame_->UpdateFrameAfterFrameChange();
+}
+
+bool NonClientView::UseNativeFrame() const {
+ // The frame view may always require a custom frame, e.g. Constrained Windows.
+ bool always_use_custom_frame =
+ frame_view_.get() && frame_view_->AlwaysUseCustomFrame();
+ return !always_use_custom_frame && use_native_frame_;
+}
+
+void NonClientView::DisableInactiveRendering(bool disable) {
+ frame_view_->DisableInactiveRendering(disable);
+}
+
+gfx::Rect NonClientView::GetWindowBoundsForClientBounds(
+ const gfx::Rect client_bounds) const {
+ return frame_view_->GetWindowBoundsForClientBounds(client_bounds);
+}
+
+gfx::Point NonClientView::GetSystemMenuPoint() const {
+ return frame_view_->GetSystemMenuPoint();
+}
+
+int NonClientView::NonClientHitTest(const gfx::Point& point) {
+ // Sanity check.
+ if (!bounds().Contains(point))
+ return HTNOWHERE;
+
+ // The ClientView gets first crack, since it overlays the NonClientFrameView
+ // in the display stack.
+ int frame_component = client_view_->NonClientHitTest(point);
+ if (frame_component != HTNOWHERE)
+ return frame_component;
+
+ // Finally ask the NonClientFrameView. It's at the back of the display stack
+ // so it gets asked last.
+ return frame_view_->NonClientHitTest(point);
+}
+
+void NonClientView::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+ frame_view_->GetWindowMask(size, window_mask);
+}
+
+void NonClientView::EnableClose(bool enable) {
+ frame_view_->EnableClose(enable);
+}
+
+void NonClientView::ResetWindowControls() {
+ frame_view_->ResetWindowControls();
+}
+
+void NonClientView::LayoutFrameView() {
+ // First layout the NonClientFrameView, which determines the size of the
+ // ClientView...
+ frame_view_->SetBounds(0, 0, width(), height());
+
+ // We need to manually call Layout here because layout for the frame view can
+ // change independently of the bounds changing - e.g. after the initial
+ // display of the window the metrics of the native window controls can change,
+ // which does not change the bounds of the window but requires a re-layout to
+ // trigger a repaint. We override DidChangeBounds for the NonClientFrameView
+ // to do nothing so that SetBounds above doesn't cause Layout to be called
+ // twice.
+ frame_view_->Layout();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NonClientView, View overrides:
+
+gfx::Size NonClientView::GetPreferredSize() {
+ // TODO(pkasting): This should probably be made to look similar to
+ // GetMinimumSize() below. This will require implementing GetPreferredSize()
+ // better in the various frame views.
+ gfx::Rect client_bounds(gfx::Point(), client_view_->GetPreferredSize());
+ return GetWindowBoundsForClientBounds(client_bounds).size();
+}
+
+gfx::Size NonClientView::GetMinimumSize() {
+ return frame_view_->GetMinimumSize();
+}
+
+void NonClientView::Layout() {
+ LayoutFrameView();
+
+ // Then layout the ClientView, using those bounds.
+ client_view_->SetBounds(frame_view_->GetBoundsForClientView());
+
+ // We need to manually call Layout on the ClientView as well for the same
+ // reason as above.
+ client_view_->Layout();
+}
+
+void NonClientView::ViewHierarchyChanged(bool is_add, View* parent,
+ View* child) {
+ // Add our two child views here as we are added to the Widget so that if we
+ // are subsequently resized all the parent-child relationships are
+ // established.
+ if (is_add && GetWidget() && child == this) {
+ AddChildView(kFrameViewIndex, frame_view_.get());
+ AddChildView(kClientViewIndex, client_view_);
+ }
+}
+
+views::View* NonClientView::GetViewForPoint(const gfx::Point& point) {
+ return GetViewForPoint(point, false);
+}
+
+views::View* NonClientView::GetViewForPoint(const gfx::Point& point,
+ bool can_create_floating) {
+ // Because of the z-ordering of our child views (the client view is positioned
+ // over the non-client frame view, if the client view ever overlaps the frame
+ // view visually (as it does for the browser window), then it will eat mouse
+ // events for the window controls. We override this method here so that we can
+ // detect this condition and re-route the events to the non-client frame view.
+ // The assumption is that the frame view's implementation of HitTest will only
+ // return true for area not occupied by the client view.
+ gfx::Point point_in_child_coords(point);
+ View::ConvertPointToView(this, frame_view_.get(), &point_in_child_coords);
+ if (frame_view_->HitTest(point_in_child_coords))
+ return frame_view_->GetViewForPoint(point);
+
+ return View::GetViewForPoint(point, can_create_floating);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NonClientFrameView, View overrides:
+
+bool NonClientFrameView::HitTest(const gfx::Point& l) const {
+ // For the default case, we assume the non-client frame view never overlaps
+ // the client view.
+ return !GetWindow()->GetClientView()->bounds().Contains(l);
+}
+
+void NonClientFrameView::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+ // Overridden to do nothing. The NonClientView manually calls Layout on the
+ // FrameView when it is itself laid out, see comment in NonClientView::Layout.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NonClientFrameView, protected:
+
+int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point,
+ int top_resize_border_height,
+ int resize_border_thickness,
+ int top_resize_corner_height,
+ int resize_corner_width,
+ bool can_resize) {
+ // Tricky: In XP, native behavior is to return HTTOPLEFT and HTTOPRIGHT for
+ // a |resize_corner_size|-length strip of both the side and top borders, but
+ // only to return HTBOTTOMLEFT/HTBOTTOMRIGHT along the bottom border + corner
+ // (not the side border). Vista goes further and doesn't return these on any
+ // of the side borders. We allow callers to match either behavior.
+ int component;
+ if (point.x() < resize_border_thickness) {
+ if (point.y() < top_resize_corner_height)
+ component = HTTOPLEFT;
+ else if (point.y() >= (height() - resize_border_thickness))
+ component = HTBOTTOMLEFT;
+ else
+ component = HTLEFT;
+ } else if (point.x() >= (width() - resize_border_thickness)) {
+ if (point.y() < top_resize_corner_height)
+ component = HTTOPRIGHT;
+ else if (point.y() >= (height() - resize_border_thickness))
+ component = HTBOTTOMRIGHT;
+ else
+ component = HTRIGHT;
+ } else if (point.y() < top_resize_border_height) {
+ if (point.x() < resize_corner_width)
+ component = HTTOPLEFT;
+ else if (point.x() >= (width() - resize_corner_width))
+ component = HTTOPRIGHT;
+ else
+ component = HTTOP;
+ } else if (point.y() >= (height() - resize_border_thickness)) {
+ if (point.x() < resize_corner_width)
+ component = HTBOTTOMLEFT;
+ else if (point.x() >= (width() - resize_corner_width))
+ component = HTBOTTOMRIGHT;
+ else
+ component = HTBOTTOM;
+ } else {
+ return HTNOWHERE;
+ }
+
+ // If the window can't be resized, there are no resize boundaries, just
+ // window borders.
+ return can_resize ? component : HTBORDER;
+}
+
+} // namespace views
diff --git a/views/window/non_client_view.h b/views/window/non_client_view.h
new file mode 100644
index 0000000..d93f423
--- /dev/null
+++ b/views/window/non_client_view.h
@@ -0,0 +1,227 @@
+// Copyright (c) 2006-2008 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_WINDOW_NON_CLIENT_VIEW_H_
+#define VIEWS_WINDOW_NON_CLIENT_VIEW_H_
+
+#include "base/task.h"
+#include "views/view.h"
+#include "views/window/client_view.h"
+
+namespace gfx {
+class Path;
+}
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// NonClientFrameView
+//
+// An object that subclasses NonClientFrameView is a View that renders and
+// responds to events within the frame portions of the non-client area of a
+// window. This view does _not_ contain the ClientView, but rather is a sibling
+// of it.
+class NonClientFrameView : public View {
+ public:
+ // Various edges of the frame border have a 1 px shadow along their edges; in
+ // a few cases we shift elements based on this amount for visual appeal.
+ static const int kFrameShadowThickness;
+ // In restored mode, we draw a 1 px edge around the content area inside the
+ // frame border.
+ static const int kClientEdgeThickness;
+
+ void DisableInactiveRendering(bool disable) {
+ paint_as_active_ = disable;
+ if (!paint_as_active_)
+ SchedulePaint();
+ }
+
+ // Returns the bounds (in this View's parent's coordinates) that the client
+ // view should be laid out within.
+ virtual gfx::Rect GetBoundsForClientView() const = 0;
+
+ // Returns true if this FrameView should always use the custom frame,
+ // regardless of the system settings. An example is the Constrained Window,
+ // which is a child window and must always provide its own frame.
+ virtual bool AlwaysUseCustomFrame() const { return false; }
+
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const = 0;
+ virtual gfx::Point GetSystemMenuPoint() const = 0;
+ virtual int NonClientHitTest(const gfx::Point& point) = 0;
+ virtual void GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) = 0;
+ virtual void EnableClose(bool enable) = 0;
+ virtual void ResetWindowControls() = 0;
+
+ // Overridden from View:
+ virtual bool HitTest(const gfx::Point& l) const;
+
+ protected:
+ virtual void DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current);
+
+ NonClientFrameView() : paint_as_active_(false) {}
+
+
+ // Helper for non-client view implementations to determine which area of the
+ // window border the specified |point| falls within. The other parameters are
+ // the size of the sizing edges, and whether or not the window can be
+ // resized.
+ int GetHTComponentForFrame(const gfx::Point& point,
+ int top_resize_border_height,
+ int resize_border_thickness,
+ int top_resize_corner_height,
+ int resize_corner_width,
+ bool can_resize);
+
+ // Accessor for paint_as_active_.
+ bool paint_as_active() const { return paint_as_active_; }
+
+ private:
+ // True when the non-client view should always be rendered as if the window
+ // were active, regardless of whether or not the top level window actually
+ // is active.
+ bool paint_as_active_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// NonClientView
+//
+// The NonClientView is the logical root of all Views contained within a
+// Window, except for the RootView which is its parent and of which it is the
+// sole child. The NonClientView has two children, the NonClientFrameView which
+// is responsible for painting and responding to events from the non-client
+// portions of the window, and the ClientView, which is responsible for the
+// same for the client area of the window:
+//
+// +- views::Window ------------------------------------+
+// | +- views::RootView ------------------------------+ |
+// | | +- views::NonClientView ---------------------+ | |
+// | | | +- views::NonClientView subclass ---+ | | |
+// | | | | | | | |
+// | | | | << all painting and event receiving >> | | | |
+// | | | | << of the non-client areas of a >> | | | |
+// | | | | << views::Window. >> | | | |
+// | | | | | | | |
+// | | | +----------------------------------------+ | | |
+// | | | +- views::ClientView or subclass --------+ | | |
+// | | | | | | | |
+// | | | | << all painting and event receiving >> | | | |
+// | | | | << of the client areas of a >> | | | |
+// | | | | << views::Window. >> | | | |
+// | | | | | | | |
+// | | | +----------------------------------------+ | | |
+// | | +--------------------------------------------+ | |
+// | +------------------------------------------------+ |
+// +----------------------------------------------------+
+//
+// The NonClientFrameView and ClientView are siblings because due to theme
+// changes the NonClientFrameView may be replaced with different
+// implementations (e.g. during the switch from DWM/Aero-Glass to Vista Basic/
+// Classic rendering).
+//
+class NonClientView : public View {
+ public:
+ explicit NonClientView(Window* frame);
+ virtual ~NonClientView();
+
+ // Replaces the current NonClientFrameView (if any) with the specified one.
+ void SetFrameView(NonClientFrameView* frame_view);
+
+ // Returns true if the ClientView determines that the containing window can be
+ // closed, false otherwise.
+ bool CanClose() const;
+
+ // Called by the containing Window when it is closed.
+ void WindowClosing();
+
+ // Changes the frame from native to custom depending on the value of
+ // |use_native_frame|.
+ void SetUseNativeFrame(bool use_native_frame);
+
+ // Returns true if the native window frame should be used, false if the
+ // NonClientView provides its own frame implementation.
+ bool UseNativeFrame() const;
+
+ // Prevents the window from being rendered as deactivated when |disable| is
+ // true, until called with |disable| false. Used when a sub-window is to be
+ // shown that shouldn't visually de-activate the window.
+ // Subclasses can override this to perform additional actions when this value
+ // changes.
+ void DisableInactiveRendering(bool disable);
+
+ // Returns the bounds of the window required to display the content area at
+ // the specified bounds.
+ gfx::Rect GetWindowBoundsForClientBounds(const gfx::Rect client_bounds) const;
+
+ // Returns the point, in screen coordinates, where the system menu should
+ // be shown so it shows up anchored to the system menu icon.
+ gfx::Point GetSystemMenuPoint() const;
+
+ // Determines the windows HT* code when the mouse cursor is at the
+ // specified point, in window coordinates.
+ int NonClientHitTest(const gfx::Point& point);
+
+ // Returns a mask to be used to clip the top level window for the given
+ // size. This is used to create the non-rectangular window shape.
+ void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask);
+
+ // Toggles the enable state for the Close button (and the Close menu item in
+ // the system menu).
+ void EnableClose(bool enable);
+
+ // Tells the window controls as rendered by the NonClientView to reset
+ // themselves to a normal state. This happens in situations where the
+ // containing window does not receive a normal sequences of messages that
+ // would lead to the controls returning to this normal state naturally, e.g.
+ // when the window is maximized, minimized or restored.
+ void ResetWindowControls();
+
+ // Get/Set client_view property.
+ ClientView* client_view() const { return client_view_; }
+ void set_client_view(ClientView* client_view) {
+ client_view_ = client_view;
+ }
+
+ // Layout just the frame view. This is necessary on Windows when non-client
+ // metrics such as the position of the window controls changes independently
+ // of a window resize message.
+ void LayoutFrameView();
+
+ // NonClientView, View overrides:
+ virtual gfx::Size GetPreferredSize();
+ virtual gfx::Size GetMinimumSize();
+ virtual void Layout();
+
+ protected:
+ // NonClientView, View overrides:
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+ virtual views::View* GetViewForPoint(const gfx::Point& point);
+ virtual views::View* GetViewForPoint(const gfx::Point& point,
+ bool can_create_floating);
+
+ private:
+ // The frame that hosts this NonClientView.
+ Window* frame_;
+
+ // A ClientView object or subclass, responsible for sizing the contents view
+ // of the window, hit testing and perhaps other tasks depending on the
+ // implementation.
+ ClientView* client_view_;
+
+ // The NonClientFrameView that renders the non-client portions of the window.
+ // This object is not owned by the view hierarchy because it can be replaced
+ // dynamically as the system settings change.
+ scoped_ptr<NonClientFrameView> frame_view_;
+
+ // Whether or not we should use the native frame.
+ bool use_native_frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(NonClientView);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_WINDOW_NON_CLIENT_VIEW_H_
diff --git a/views/window/window.h b/views/window/window.h
new file mode 100644
index 0000000..17a2ff5
--- /dev/null
+++ b/views/window/window.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2009 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_WINDOW_WINDOW_H_
+#define VIEWS_WINDOW_WINDOW_H_
+
+#include "base/gfx/native_widget_types.h"
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace views {
+
+class ClientView;
+class NonClientFrameView;
+class NonClientView;
+class WindowDelegate;
+
+// An interface implemented by an object that provides a top level window.
+class Window {
+ public:
+ virtual ~Window() {}
+
+ // Creates an instance of an object implementing this interface.
+ static Window* CreateChromeWindow(gfx::NativeWindow parent,
+ const gfx::Rect& bounds,
+ WindowDelegate* window_delegate);
+
+ // Returns the preferred size of the contents view of this window based on
+ // its localized size data. The width in cols is held in a localized string
+ // resource identified by |col_resource_id|, the height in the same fashion.
+ // TODO(beng): This should eventually live somewhere else, probably closer to
+ // ClientView.
+ static int GetLocalizedContentsWidth(int col_resource_id);
+ static int GetLocalizedContentsHeight(int row_resource_id);
+ static gfx::Size GetLocalizedContentsSize(int col_resource_id,
+ int row_resource_id);
+
+ // Closes all windows that aren't identified as "app windows" via
+ // IsAppWindow. Called during application shutdown when the last "app window"
+ // is closed.
+ static void CloseAllSecondaryWindows();
+
+ // Retrieves the window's bounds, including its frame.
+ virtual gfx::Rect GetBounds() const = 0;
+
+ // Retrieves the restored bounds for the window.
+ virtual gfx::Rect GetNormalBounds() const = 0;
+
+ // Sizes and/or places the window to the specified bounds, size or position.
+ virtual void SetBounds(const gfx::Rect& bounds) = 0;
+
+ // As above, except the window is inserted after |other_window| in the window
+ // Z-order. If this window is not yet visible, other_window's monitor is used
+ // as the constraining rectangle, rather than this window's monitor.
+ virtual void SetBounds(const gfx::Rect& bounds,
+ gfx::NativeWindow other_window) = 0;
+
+ // Makes the window visible.
+ virtual void Show() = 0;
+
+ // Activate the window, assuming it already exists and is visible.
+ virtual void Activate() = 0;
+
+ // Closes the window, ultimately destroying it. This isn't immediate (it
+ // occurs after a return to the message loop. Implementors must also make sure
+ // that invoking Close multiple times doesn't cause bad things to happen,
+ // since it can happen.
+ virtual void Close() = 0;
+
+ // Maximizes/minimizes/restores the window.
+ virtual void Maximize() = 0;
+ virtual void Minimize() = 0;
+ virtual void Restore() = 0;
+
+ // Whether or not the window is currently active.
+ virtual bool IsActive() const = 0;
+
+ // Whether or not the window is currently visible.
+ virtual bool IsVisible() const = 0;
+
+ // Whether or not the window is maximized or minimized.
+ virtual bool IsMaximized() const = 0;
+ virtual bool IsMinimized() const = 0;
+
+ // Accessors for fullscreen state.
+ virtual void SetFullscreen(bool fullscreen) = 0;
+ virtual bool IsFullscreen() const = 0;
+
+ // Returns true if the Window is considered to be an "app window" - i.e.
+ // any window which when it is the last of its type closed causes the
+ // application to exit.
+ virtual bool IsAppWindow() const { return false; }
+
+ // Toggles the enable state for the Close button (and the Close menu item in
+ // the system menu).
+ virtual void EnableClose(bool enable) = 0;
+
+ // Prevents the window from being rendered as deactivated the next time it is.
+ // This state is reset automatically as soon as the window becomes activated
+ // again. There is no ability to control the state through this API as this
+ // leads to sync problems.
+ virtual void DisableInactiveRendering() = 0;
+
+ // Tell the window to update its title from the delegate.
+ virtual void UpdateWindowTitle() = 0;
+
+ // Tell the window to update its icon from the delegate.
+ virtual void UpdateWindowIcon() = 0;
+
+ // Creates an appropriate NonClientFrameView for this window.
+ virtual NonClientFrameView* CreateFrameViewForWindow() = 0;
+
+ // Updates the frame after an event caused it to be changed.
+ virtual void UpdateFrameAfterFrameChange() = 0;
+
+ // Retrieves the Window's delegate.
+ virtual WindowDelegate* GetDelegate() const = 0;
+
+ // Retrieves the Window's non-client view.
+ virtual NonClientView* GetNonClientView() const = 0;
+
+ // Retrieves the Window's client view.
+ virtual ClientView* GetClientView() const = 0;
+
+ // Retrieves the Window's native window handle.
+ virtual gfx::NativeWindow GetNativeWindow() const = 0;
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_WINDOW_WINDOW_H_
diff --git a/views/window/window_delegate.cc b/views/window/window_delegate.cc
new file mode 100644
index 0000000..7b5f251
--- /dev/null
+++ b/views/window/window_delegate.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2006-2008 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.
+
+#include "views/window/window_delegate.h"
+
+// TODO(beng): hrmp. Fix this in http://crbug.com/4406
+#include "chrome/browser/browser_process.h"
+#include "chrome/common/pref_service.h"
+#include "views/window/client_view.h"
+#include "views/window/window.h"
+#include "skia/include/SkBitmap.h"
+
+namespace views {
+
+WindowDelegate::WindowDelegate() {
+}
+
+WindowDelegate::~WindowDelegate() {
+ ReleaseWindow();
+}
+
+// Returns the icon to be displayed in the window.
+SkBitmap WindowDelegate::GetWindowIcon() {
+ return SkBitmap();
+}
+
+void WindowDelegate::SaveWindowPlacement(const gfx::Rect& bounds,
+ bool maximized,
+ bool always_on_top) {
+ std::wstring window_name = GetWindowName();
+ if (window_name.empty() || !g_browser_process->local_state())
+ return;
+
+ DictionaryValue* window_preferences =
+ g_browser_process->local_state()->GetMutableDictionary(
+ window_name.c_str());
+ window_preferences->SetInteger(L"left", bounds.x());
+ window_preferences->SetInteger(L"top", bounds.y());
+ window_preferences->SetInteger(L"right", bounds.right());
+ window_preferences->SetInteger(L"bottom", bounds.bottom());
+ window_preferences->SetBoolean(L"maximized", maximized);
+ window_preferences->SetBoolean(L"always_on_top", always_on_top);
+}
+
+bool WindowDelegate::GetSavedWindowBounds(gfx::Rect* bounds) const {
+ std::wstring window_name = GetWindowName();
+ if (window_name.empty())
+ return false;
+
+ const DictionaryValue* dictionary =
+ g_browser_process->local_state()->GetDictionary(window_name.c_str());
+ int left, top, right, bottom;
+ if (!dictionary || !dictionary->GetInteger(L"left", &left) ||
+ !dictionary->GetInteger(L"top", &top) ||
+ !dictionary->GetInteger(L"right", &right) ||
+ !dictionary->GetInteger(L"bottom", &bottom))
+ return false;
+
+ bounds->SetRect(left, top, right - left, bottom - top);
+ return true;
+}
+
+bool WindowDelegate::GetSavedMaximizedState(bool* maximized) const {
+ std::wstring window_name = GetWindowName();
+ if (window_name.empty())
+ return false;
+
+ const DictionaryValue* dictionary =
+ g_browser_process->local_state()->GetDictionary(window_name.c_str());
+ return dictionary && dictionary->GetBoolean(L"maximized", maximized);
+}
+
+bool WindowDelegate::GetSavedAlwaysOnTopState(bool* always_on_top) const {
+ if (!g_browser_process->local_state())
+ return false;
+
+ std::wstring window_name = GetWindowName();
+ if (window_name.empty())
+ return false;
+
+ const DictionaryValue* dictionary =
+ g_browser_process->local_state()->GetDictionary(window_name.c_str());
+ return dictionary && dictionary->GetBoolean(L"always_on_top", always_on_top);
+}
+
+
+ClientView* WindowDelegate::CreateClientView(Window* window) {
+ return new ClientView(window, GetContentsView());
+}
+
+void WindowDelegate::ReleaseWindow() {
+ window_.release();
+}
+
+} // namespace views
diff --git a/views/window/window_delegate.h b/views/window/window_delegate.h
new file mode 100644
index 0000000..af8285c
--- /dev/null
+++ b/views/window/window_delegate.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2006-2008 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_WINDOW_WINDOW_DELEGATE_H_
+#define VIEWS_WINDOW_WINDOW_DELEGATE_H_
+
+#include <string>
+
+#include "base/scoped_ptr.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Rect;
+}
+// TODO(maruel): Remove once gfx::Rect is used instead.
+namespace WTL {
+class CRect;
+}
+using WTL::CRect;
+
+namespace views {
+
+class ClientView;
+class DialogDelegate;
+class View;
+class Window;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// WindowDelegate
+//
+// WindowDelegate is an interface implemented by objects that wish to show a
+// Window. The window that is displayed uses this interface to determine how
+// it should be displayed and notify the delegate object of certain events.
+//
+///////////////////////////////////////////////////////////////////////////////
+class WindowDelegate {
+ public:
+ WindowDelegate();
+ virtual ~WindowDelegate();
+
+ virtual DialogDelegate* AsDialogDelegate() { return NULL; }
+
+ // Returns true if the window can ever be resized.
+ virtual bool CanResize() const {
+ return false;
+ }
+
+ // Returns true if the window can ever be maximized.
+ virtual bool CanMaximize() const {
+ return false;
+ }
+
+ // Returns true if the window should be placed on top of all other windows on
+ // the system, even when it is not active. If HasAlwaysOnTopMenu() returns
+ // true, then this method is only used the first time the window is opened, it
+ // is stored in the preferences for next runs.
+ virtual bool IsAlwaysOnTop() const {
+ return false;
+ }
+
+ // Returns whether an "always on top" menu should be added to the system menu
+ // of the window.
+ virtual bool HasAlwaysOnTopMenu() const {
+ return false;
+ }
+
+ // Returns true if the dialog should be displayed modally to the window that
+ // opened it. Only windows with WindowType == DIALOG can be modal.
+ virtual bool IsModal() const {
+ return false;
+ }
+
+ // Returns the text to be displayed in the window title.
+ virtual std::wstring GetWindowTitle() const {
+ return L"";
+ }
+
+ // Returns the view that should have the focus when the dialog is opened. If
+ // NULL no view is focused.
+ virtual View* GetInitiallyFocusedView() { return NULL; }
+
+ // Returns true if the window should show a title in the title bar.
+ virtual bool ShouldShowWindowTitle() const {
+ return true;
+ }
+
+ // Returns the icon to be displayed in the window.
+ virtual SkBitmap GetWindowIcon();
+
+ // Returns true if a window icon should be shown.
+ virtual bool ShouldShowWindowIcon() const {
+ return false;
+ }
+
+ // Execute a command in the window's controller. Returns true if the command
+ // was handled, false if it was not.
+ virtual bool ExecuteWindowsCommand(int command_id) { return false; }
+
+ // Returns the window's name identifier. Used to identify this window for
+ // state restoration.
+ virtual std::wstring GetWindowName() const {
+ return std::wstring();
+ }
+
+ // Saves the window's bounds, maximized and always-on-top states. By default
+ // this uses the process' local state keyed by window name (See GetWindowName
+ // above). This behavior can be overridden to provide additional
+ // functionality.
+ virtual void SaveWindowPlacement(const gfx::Rect& bounds,
+ bool maximized,
+ bool always_on_top);
+
+ // Retrieves the window's bounds, maximized and always-on-top states. By
+ // default, this uses the process' local state keyed by window name (See
+ // GetWindowName above). This behavior can be overridden to provide
+ // additional functionality.
+ virtual bool GetSavedWindowBounds(gfx::Rect* bounds) const;
+ virtual bool GetSavedMaximizedState(bool* maximized) const;
+ virtual bool GetSavedAlwaysOnTopState(bool* always_on_top) const;
+
+ // Called when the window closes.
+ virtual void WindowClosing() { }
+
+ // Called when the window is destroyed. No events must be sent or received
+ // after this point. The delegate can use this opportunity to delete itself at
+ // this time if necessary.
+ virtual void DeleteDelegate() { }
+
+ // Returns the View that is contained within this Window.
+ virtual View* GetContentsView() {
+ return NULL;
+ }
+
+ // Called by the Window to create the Client View used to host the contents
+ // of the window.
+ virtual ClientView* CreateClientView(Window* window);
+
+ // An accessor to the Window this delegate is bound to.
+ Window* window() const { return window_.get(); }
+
+ protected:
+ // Releases the Window* we maintain. This should be done by a delegate in its
+ // WindowClosing handler if it intends to be re-cycled to be used on a
+ // different Window.
+ void ReleaseWindow();
+
+ private:
+ friend class WindowWin;
+ // This is a little unusual. We use a scoped_ptr here because it's
+ // initialized to NULL automatically. We do this because we want to allow
+ // people using this helper to not have to call a ctor on this object.
+ // Instead we just release the owning ref this pointer has when we are
+ // destroyed.
+ scoped_ptr<Window> window_;
+};
+
+} // namespace views
+
+#endif // VIEWS_WINDOW_WINDOW_DELEGATE_H_
diff --git a/views/window/window_resources.h b/views/window/window_resources.h
new file mode 100644
index 0000000..e79476e
--- /dev/null
+++ b/views/window/window_resources.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2006-2008 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_WINDOW_WINDOW_RESOURCES_H_
+#define VIEWS_WINDOW_WINDOW_RESOURCES_H_
+
+class SkBitmap;
+
+namespace views {
+
+typedef int FramePartBitmap;
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowResources
+//
+// An interface implemented by an object providing bitmaps to render the
+// contents of a window frame. The Window may swap in different
+// implementations of this interface to render different modes. The definition
+// of FramePartBitmap depends on the implementation.
+//
+class WindowResources {
+ public:
+ virtual ~WindowResources() { }
+ virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const = 0;
+};
+
+} // namespace views
+
+#endif // VIEWS_WINDOW_WINDOW_RESOURCES_H_
diff --git a/views/window/window_win.cc b/views/window/window_win.cc
new file mode 100644
index 0000000..f41ef18
--- /dev/null
+++ b/views/window/window_win.cc
@@ -0,0 +1,1446 @@
+// Copyright (c) 2006-2008 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.
+
+#include "views/window/window_win.h"
+
+#include <shellapi.h>
+
+#include "app/gfx/chrome_canvas.h"
+#include "app/gfx/chrome_font.h"
+#include "app/gfx/icon_util.h"
+#include "app/gfx/path.h"
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/win_util.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/common/win_util.h"
+#include "grit/generated_resources.h"
+#include "views/widget/root_view.h"
+#include "views/window/client_view.h"
+#include "views/window/custom_frame_view.h"
+#include "views/window/native_frame_view.h"
+#include "views/window/non_client_view.h"
+#include "views/window/window_delegate.h"
+
+namespace {
+
+bool GetMonitorAndRects(const RECT& rect,
+ HMONITOR* monitor,
+ gfx::Rect* monitor_rect,
+ gfx::Rect* work_area) {
+ DCHECK(monitor);
+ DCHECK(monitor_rect);
+ DCHECK(work_area);
+ *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
+ if (!*monitor)
+ return false;
+ MONITORINFO monitor_info = { 0 };
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(*monitor, &monitor_info);
+ *monitor_rect = monitor_info.rcMonitor;
+ *work_area = monitor_info.rcWork;
+ return true;
+}
+
+} // namespace
+
+namespace views {
+
+// A scoping class that prevents a window from being able to redraw in response
+// to invalidations that may occur within it for the lifetime of the object.
+//
+// Why would we want such a thing? Well, it turns out Windows has some
+// "unorthodox" behavior when it comes to painting its non-client areas.
+// Occasionally, Windows will paint portions of the default non-client area
+// right over the top of the custom frame. This is not simply fixed by handling
+// WM_NCPAINT/WM_PAINT, with some investigation it turns out that this
+// rendering is being done *inside* the default implementation of some message
+// handlers and functions:
+// . WM_SETTEXT
+// . WM_SETICON
+// . WM_NCLBUTTONDOWN
+// . EnableMenuItem, called from our WM_INITMENU handler
+// The solution is to handle these messages and call DefWindowProc ourselves,
+// but prevent the window from being able to update itself for the duration of
+// the call. We do this with this class, which automatically calls its
+// associated Window's lock and unlock functions as it is created and destroyed.
+// See documentation in those methods for the technique used.
+//
+// IMPORTANT: Do not use this scoping object for large scopes or periods of
+// time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh).
+//
+// I would love to hear Raymond Chen's explanation for all this. And maybe a
+// list of other messages that this applies to ;-)
+class WindowWin::ScopedRedrawLock {
+ public:
+ explicit ScopedRedrawLock(WindowWin* window) : window_(window) {
+ window_->LockUpdates();
+ }
+
+ ~ScopedRedrawLock() {
+ window_->UnlockUpdates();
+ }
+
+ private:
+ // The window having its style changed.
+ WindowWin* window_;
+};
+
+HCURSOR WindowWin::resize_cursors_[6];
+
+// If the hung renderer warning doesn't fit on screen, the amount of padding to
+// be left between the edge of the window and the edge of the nearest monitor,
+// after the window is nudged back on screen. Pixels.
+static const int kMonitorEdgePadding = 10;
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowWin, public:
+
+WindowWin::~WindowWin() {
+}
+
+// static
+Window* Window::CreateChromeWindow(gfx::NativeWindow parent,
+ const gfx::Rect& bounds,
+ WindowDelegate* window_delegate) {
+ WindowWin* window = new WindowWin(window_delegate);
+ window->GetNonClientView()->SetFrameView(window->CreateFrameViewForWindow());
+ window->Init(parent, bounds);
+ return window;
+}
+
+gfx::Rect WindowWin::GetBounds() const {
+ gfx::Rect bounds;
+ WidgetWin::GetBounds(&bounds, true);
+ return bounds;
+}
+
+gfx::Rect WindowWin::GetNormalBounds() const {
+ // If we're in fullscreen mode, we've changed the normal bounds to the monitor
+ // rect, so return the saved bounds instead.
+ if (IsFullscreen())
+ return gfx::Rect(saved_window_info_.window_rect);
+
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ const bool ret = !!GetWindowPlacement(GetNativeView(), &wp);
+ DCHECK(ret);
+ return gfx::Rect(wp.rcNormalPosition);
+}
+
+void WindowWin::SetBounds(const gfx::Rect& bounds) {
+ SetBounds(bounds, NULL);
+}
+
+void WindowWin::SetBounds(const gfx::Rect& bounds,
+ gfx::NativeWindow other_window) {
+ win_util::SetChildBounds(GetNativeView(), GetParent(), other_window, bounds,
+ kMonitorEdgePadding, 0);
+}
+
+void WindowWin::Show(int show_state) {
+ ShowWindow(show_state);
+ // When launched from certain programs like bash and Windows Live Messenger,
+ // show_state is set to SW_HIDE, so we need to correct that condition. We
+ // don't just change show_state to SW_SHOWNORMAL because MSDN says we must
+ // always first call ShowWindow with the specified value from STARTUPINFO,
+ // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead,
+ // we call ShowWindow again in this case.
+ if (show_state == SW_HIDE) {
+ show_state = SW_SHOWNORMAL;
+ ShowWindow(show_state);
+ }
+
+ // We need to explicitly activate the window if we've been shown with a state
+ // that should activate, because if we're opened from a desktop shortcut while
+ // an existing window is already running it doesn't seem to be enough to use
+ // one of these flags to activate the window.
+ if (show_state == SW_SHOWNORMAL)
+ Activate();
+
+ SetInitialFocus();
+}
+
+int WindowWin::GetShowState() const {
+ return SW_SHOWNORMAL;
+}
+
+void WindowWin::ExecuteSystemMenuCommand(int command) {
+ if (command)
+ SendMessage(GetNativeView(), WM_SYSCOMMAND, command, 0);
+}
+
+void WindowWin::PushForceHidden() {
+ if (force_hidden_count_++ == 0)
+ Hide();
+}
+
+void WindowWin::PopForceHidden() {
+ if (--force_hidden_count_ <= 0) {
+ force_hidden_count_ = 0;
+ ShowWindow(SW_SHOW);
+ }
+}
+
+// static
+int Window::GetLocalizedContentsWidth(int col_resource_id) {
+ double chars = _wtof(l10n_util::GetString(col_resource_id).c_str());
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont font = rb.GetFont(ResourceBundle::BaseFont);
+ int width = font.GetExpectedTextWidth(static_cast<int>(chars));
+ DCHECK(width > 0);
+ return width;
+}
+
+// static
+int Window::GetLocalizedContentsHeight(int row_resource_id) {
+ double lines = _wtof(l10n_util::GetString(row_resource_id).c_str());
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont font = rb.GetFont(ResourceBundle::BaseFont);
+ int height = static_cast<int>(font.height() * lines);
+ DCHECK(height > 0);
+ return height;
+}
+
+// static
+gfx::Size Window::GetLocalizedContentsSize(int col_resource_id,
+ int row_resource_id) {
+ return gfx::Size(GetLocalizedContentsWidth(col_resource_id),
+ GetLocalizedContentsHeight(row_resource_id));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowWin, Window implementation:
+
+void WindowWin::Show() {
+ int show_state = GetShowState();
+ if (saved_maximized_state_)
+ show_state = SW_SHOWMAXIMIZED;
+ Show(show_state);
+}
+
+void WindowWin::Activate() {
+ if (IsMinimized())
+ ::ShowWindow(GetNativeView(), SW_RESTORE);
+ ::SetWindowPos(GetNativeView(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ SetForegroundWindow(GetNativeView());
+}
+
+void WindowWin::Close() {
+ if (window_closed_) {
+ // It appears we can hit this code path if you close a modal dialog then
+ // close the last browser before the destructor is hit, which triggers
+ // invoking Close again. I'm short circuiting this code path to avoid
+ // calling into the delegate twice, which is problematic.
+ return;
+ }
+
+ if (non_client_view_->CanClose()) {
+ SaveWindowPosition();
+ RestoreEnabledIfNecessary();
+ WidgetWin::Close();
+ // If the user activates another app after opening us, then comes back and
+ // closes us, we want our owner to gain activation. But only if the owner
+ // is visible. If we don't manually force that here, the other app will
+ // regain activation instead.
+ if (owning_hwnd_ && GetNativeView() == GetForegroundWindow() &&
+ IsWindowVisible(owning_hwnd_)) {
+ SetForegroundWindow(owning_hwnd_);
+ }
+ window_closed_ = true;
+ }
+}
+
+void WindowWin::Maximize() {
+ ExecuteSystemMenuCommand(SC_MAXIMIZE);
+}
+
+void WindowWin::Minimize() {
+ ExecuteSystemMenuCommand(SC_MINIMIZE);
+}
+
+void WindowWin::Restore() {
+ ExecuteSystemMenuCommand(SC_RESTORE);
+}
+
+bool WindowWin::IsActive() const {
+ return is_active_;
+}
+
+bool WindowWin::IsVisible() const {
+ return !!::IsWindowVisible(GetNativeView());
+}
+
+bool WindowWin::IsMaximized() const {
+ return !!::IsZoomed(GetNativeView());
+}
+
+bool WindowWin::IsMinimized() const {
+ return !!::IsIconic(GetNativeView());
+}
+
+void WindowWin::SetFullscreen(bool fullscreen) {
+ if (fullscreen_ == fullscreen)
+ return; // Nothing to do.
+
+ // Reduce jankiness during the following position changes by hiding the window
+ // until it's in the final position.
+ PushForceHidden();
+
+ // Size/position/style window appropriately.
+ if (!fullscreen_) {
+ // Save current window information. We force the window into restored mode
+ // before going fullscreen because Windows doesn't seem to hide the
+ // taskbar if the window is in the maximized state.
+ saved_window_info_.maximized = IsMaximized();
+ if (saved_window_info_.maximized)
+ Restore();
+ saved_window_info_.style = GetWindowLong(GWL_STYLE);
+ saved_window_info_.ex_style = GetWindowLong(GWL_EXSTYLE);
+ GetWindowRect(&saved_window_info_.window_rect);
+ }
+
+ // Toggle fullscreen mode.
+ fullscreen_ = fullscreen;
+
+ if (fullscreen_) {
+ // Set new window style and size.
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(MonitorFromWindow(GetNativeView(), MONITOR_DEFAULTTONEAREST),
+ &monitor_info);
+ gfx::Rect monitor_rect(monitor_info.rcMonitor);
+ SetWindowLong(GWL_STYLE,
+ saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
+ SetWindowLong(GWL_EXSTYLE,
+ saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME |
+ WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+ SetWindowPos(NULL, monitor_rect.x(), monitor_rect.y(),
+ monitor_rect.width(), monitor_rect.height(),
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+ } else {
+ // Reset original window style and size. The multiple window size/moves
+ // here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
+ // repainted. Better-looking methods welcome.
+ gfx::Rect new_rect(saved_window_info_.window_rect);
+ SetWindowLong(GWL_STYLE, saved_window_info_.style);
+ SetWindowLong(GWL_EXSTYLE, saved_window_info_.ex_style);
+ SetWindowPos(NULL, new_rect.x(), new_rect.y(), new_rect.width(),
+ new_rect.height(),
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+ if (saved_window_info_.maximized)
+ Maximize();
+ }
+
+ // Undo our anti-jankiness hacks.
+ PopForceHidden();
+}
+
+bool WindowWin::IsFullscreen() const {
+ return fullscreen_;
+}
+
+void WindowWin::EnableClose(bool enable) {
+ // If the native frame is rendering its own close button, ask it to disable.
+ non_client_view_->EnableClose(enable);
+
+ // Disable the native frame's close button regardless of whether or not the
+ // native frame is in use, since this also affects the system menu.
+ EnableMenuItem(GetSystemMenu(GetNativeView(), false),
+ SC_CLOSE, enable ? MF_ENABLED : MF_GRAYED);
+
+ // Let the window know the frame changed.
+ SetWindowPos(NULL, 0, 0, 0, 0,
+ SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS |
+ SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION |
+ SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER);
+}
+
+void WindowWin::DisableInactiveRendering() {
+ disable_inactive_rendering_ = true;
+ non_client_view_->DisableInactiveRendering(disable_inactive_rendering_);
+}
+
+void WindowWin::UpdateWindowTitle() {
+ // If the non-client view is rendering its own title, it'll need to relayout
+ // now.
+ non_client_view_->Layout();
+
+ // Update the native frame's text. We do this regardless of whether or not
+ // the native frame is being used, since this also updates the taskbar, etc.
+ std::wstring window_title = window_delegate_->GetWindowTitle();
+ std::wstring localized_text;
+ if (l10n_util::AdjustStringForLocaleDirection(window_title, &localized_text))
+ window_title.assign(localized_text);
+ SetWindowText(GetNativeView(), window_title.c_str());
+}
+
+void WindowWin::UpdateWindowIcon() {
+ // If the non-client view is rendering its own icon, we need to tell it to
+ // repaint.
+ non_client_view_->SchedulePaint();
+
+ // Update the native frame's icon. We do this regardless of whether or not
+ // the native frame is being used, since this also updates the taskbar, etc.
+ SkBitmap icon = window_delegate_->GetWindowIcon();
+ if (!icon.isNull()) {
+ HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(icon);
+ // We need to make sure to destroy the previous icon, otherwise we'll leak
+ // these GDI objects until we crash!
+ HICON old_icon = reinterpret_cast<HICON>(
+ SendMessage(GetNativeView(), WM_SETICON, ICON_SMALL,
+ reinterpret_cast<LPARAM>(windows_icon)));
+ if (old_icon)
+ DestroyIcon(old_icon);
+ old_icon = reinterpret_cast<HICON>(
+ SendMessage(GetNativeView(), WM_SETICON, ICON_BIG,
+ reinterpret_cast<LPARAM>(windows_icon)));
+ if (old_icon)
+ DestroyIcon(old_icon);
+ }
+}
+
+NonClientFrameView* WindowWin::CreateFrameViewForWindow() {
+ if (non_client_view_->UseNativeFrame())
+ return new NativeFrameView(this);
+ return new CustomFrameView(this);
+}
+
+void WindowWin::UpdateFrameAfterFrameChange() {
+ // We've either gained or lost a custom window region, so reset it now.
+ ResetWindowRegion(true);
+}
+
+WindowDelegate* WindowWin::GetDelegate() const {
+ return window_delegate_;
+}
+
+NonClientView* WindowWin::GetNonClientView() const {
+ return non_client_view_;
+}
+
+ClientView* WindowWin::GetClientView() const {
+ return non_client_view_->client_view();
+}
+
+gfx::NativeWindow WindowWin::GetNativeWindow() const {
+ return GetNativeView();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowWin, protected:
+
+WindowWin::WindowWin(WindowDelegate* window_delegate)
+ : WidgetWin(),
+ focus_on_creation_(true),
+ window_delegate_(window_delegate),
+ non_client_view_(new NonClientView(this)),
+ owning_hwnd_(NULL),
+ minimum_size_(100, 100),
+ is_modal_(false),
+ restored_enabled_(false),
+ is_always_on_top_(false),
+ fullscreen_(false),
+ window_closed_(false),
+ disable_inactive_rendering_(false),
+ is_active_(false),
+ lock_updates_(false),
+ saved_window_style_(0),
+ saved_maximized_state_(0),
+ ignore_window_pos_changes_(false),
+ ignore_pos_changes_factory_(this),
+ force_hidden_count_(0),
+ last_monitor_(NULL) {
+ is_window_ = true;
+ InitClass();
+ DCHECK(window_delegate_);
+ window_delegate_->window_.reset(this);
+ // Initialize these values to 0 so that subclasses can override the default
+ // behavior before calling Init.
+ set_window_style(0);
+ set_window_ex_style(0);
+}
+
+void WindowWin::Init(HWND parent, const gfx::Rect& bounds) {
+ // We need to save the parent window, since later calls to GetParent() will
+ // return NULL.
+ owning_hwnd_ = parent;
+ // We call this after initializing our members since our implementations of
+ // assorted WidgetWin functions may be called during initialization.
+ is_modal_ = window_delegate_->IsModal();
+ if (is_modal_)
+ BecomeModal();
+ is_always_on_top_ = window_delegate_->IsAlwaysOnTop();
+
+ if (window_style() == 0)
+ set_window_style(CalculateWindowStyle());
+ if (window_ex_style() == 0)
+ set_window_ex_style(CalculateWindowExStyle());
+
+ WidgetWin::Init(parent, bounds, true);
+ win_util::SetWindowUserData(GetNativeView(), this);
+
+ // Create the ClientView, add it to the NonClientView and add the
+ // NonClientView to the RootView. This will cause everything to be parented.
+ non_client_view_->set_client_view(window_delegate_->CreateClientView(this));
+ WidgetWin::SetContentsView(non_client_view_);
+
+ UpdateWindowTitle();
+
+ SetInitialBounds(bounds);
+ InitAlwaysOnTopState();
+
+ GetMonitorAndRects(bounds.ToRECT(), &last_monitor_, &last_monitor_rect_,
+ &last_work_area_);
+ ResetWindowRegion(false);
+}
+
+void WindowWin::SizeWindowToDefault() {
+ win_util::CenterAndSizeWindow(owning_window(), GetNativeView(),
+ non_client_view_->GetPreferredSize().ToSIZE(),
+ false);
+}
+
+void WindowWin::RunSystemMenu(const gfx::Point& point) {
+ // We need to reset and clean up any currently created system menu objects.
+ // We need to call this otherwise there's a small chance that we aren't going
+ // to get a system menu. We also can't take the return value of this
+ // function. We need to call it *again* to get a valid HMENU.
+ //::GetSystemMenu(GetNativeView(), TRUE);
+ UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD;
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
+ flags |= TPM_RIGHTALIGN;
+ HMENU system_menu = ::GetSystemMenu(GetNativeView(), FALSE);
+ int id = ::TrackPopupMenu(system_menu, flags,
+ point.x(), point.y(), 0, GetNativeView(), NULL);
+ ExecuteSystemMenuCommand(id);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowWin, WidgetWin overrides:
+
+void WindowWin::OnActivate(UINT action, BOOL minimized, HWND window) {
+ if (action == WA_INACTIVE)
+ SaveWindowPosition();
+}
+
+void WindowWin::OnActivateApp(BOOL active, DWORD thread_id) {
+ if (!active && thread_id != GetCurrentThreadId()) {
+ // Another application was activated, we should reset any state that
+ // disables inactive rendering now.
+ disable_inactive_rendering_ = false;
+ non_client_view_->DisableInactiveRendering(false);
+ // Update the native frame too, since it could be rendering the non-client
+ // area.
+ CallDefaultNCActivateHandler(FALSE);
+ }
+}
+
+LRESULT WindowWin::OnAppCommand(HWND window, short app_command, WORD device,
+ int keystate) {
+ // We treat APPCOMMAND ids as an extension of our command namespace, and just
+ // let the delegate figure out what to do...
+ if (!window_delegate_->ExecuteWindowsCommand(app_command))
+ return WidgetWin::OnAppCommand(window, app_command, device, keystate);
+ return 0;
+}
+
+void WindowWin::OnCommand(UINT notification_code, int command_id, HWND window) {
+ // If the notification code is > 1 it means it is control specific and we
+ // should ignore it.
+ if (notification_code > 1 ||
+ window_delegate_->ExecuteWindowsCommand(command_id)) {
+ WidgetWin::OnCommand(notification_code, command_id, window);
+ }
+}
+
+void WindowWin::OnDestroy() {
+ non_client_view_->WindowClosing();
+ RestoreEnabledIfNecessary();
+ WidgetWin::OnDestroy();
+}
+
+namespace {
+static BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) {
+ SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0);
+ return TRUE;
+}
+} // namespace
+
+LRESULT WindowWin::OnDwmCompositionChanged(UINT msg, WPARAM w_param,
+ LPARAM l_param) {
+ // The window may try to paint in SetUseNativeFrame, and as a result it can
+ // get into a state where it is very unhappy with itself - rendering black
+ // behind the entire client area. This is because for some reason the
+ // SkPorterDuff::kClear_mode erase done in the RootView thinks the window is
+ // still opaque. So, to work around this we hide the window as soon as we can
+ // (now), saving off its placement so it can be properly restored once
+ // everything has settled down.
+ WINDOWPLACEMENT saved_window_placement;
+ saved_window_placement.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(GetNativeView(), &saved_window_placement);
+ Hide();
+
+ // Important step: restore the window first, since our hiding hack doesn't
+ // work for maximized windows! We tell the frame not to allow itself to be
+ // made visible though, which removes the brief flicker.
+ ++force_hidden_count_;
+ ::ShowWindow(GetNativeView(), SW_RESTORE);
+ --force_hidden_count_;
+
+ // We respond to this in response to WM_DWMCOMPOSITIONCHANGED since that is
+ // the only thing we care about - we don't actually respond to WM_THEMECHANGED
+ // messages.
+ non_client_view_->SetUseNativeFrame(win_util::ShouldUseVistaFrame());
+
+ // Now that we've updated the frame, we'll want to restore our saved placement
+ // since the display should have settled down and we can be properly rendered.
+ SetWindowPlacement(GetNativeView(), &saved_window_placement);
+
+ // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want
+ // to notify our children too, since we can have MDI child windows who need to
+ // update their appearance.
+ EnumChildWindows(GetNativeView(), &SendDwmCompositionChanged, NULL);
+ return 0;
+}
+
+void WindowWin::OnFinalMessage(HWND window) {
+ // Delete and NULL the delegate here once we're guaranteed to get no more
+ // messages.
+ window_delegate_->DeleteDelegate();
+ window_delegate_ = NULL;
+ WidgetWin::OnFinalMessage(window);
+}
+
+void WindowWin::OnGetMinMaxInfo(MINMAXINFO* minmax_info) {
+ gfx::Size min_window_size(GetNonClientView()->GetMinimumSize());
+ minmax_info->ptMinTrackSize.x = min_window_size.width();
+ minmax_info->ptMinTrackSize.y = min_window_size.height();
+ WidgetWin::OnGetMinMaxInfo(minmax_info);
+}
+
+namespace {
+static void EnableMenuItem(HMENU menu, UINT command, bool enabled) {
+ UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(menu, command, flags);
+}
+} // namespace
+
+void WindowWin::OnInitMenu(HMENU menu) {
+ // We only need to manually enable the system menu if we're not using a native
+ // frame.
+ if (non_client_view_->UseNativeFrame())
+ WidgetWin::OnInitMenu(menu);
+
+ bool is_fullscreen = IsFullscreen();
+ bool is_minimized = IsMinimized();
+ bool is_maximized = IsMaximized();
+ bool is_restored = !is_fullscreen && !is_minimized && !is_maximized;
+
+ ScopedRedrawLock lock(this);
+ EnableMenuItem(menu, SC_RESTORE, is_minimized || is_maximized);
+ EnableMenuItem(menu, SC_MOVE, is_restored);
+ EnableMenuItem(menu, SC_SIZE, window_delegate_->CanResize() && is_restored);
+ EnableMenuItem(menu, SC_MAXIMIZE,
+ window_delegate_->CanMaximize() && !is_fullscreen && !is_maximized);
+ EnableMenuItem(menu, SC_MINIMIZE,
+ window_delegate_->CanMaximize() && !is_minimized);
+}
+
+void WindowWin::OnMouseLeave() {
+ // We only need to manually track WM_MOUSELEAVE messages between the client
+ // and non-client area when we're not using the native frame.
+ if (non_client_view_->UseNativeFrame()) {
+ SetMsgHandled(FALSE);
+ return;
+ }
+
+ bool process_mouse_exited = true;
+ POINT pt;
+ if (GetCursorPos(&pt)) {
+ LRESULT ht_component =
+ ::SendMessage(GetNativeView(), WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y));
+ if (ht_component != HTNOWHERE) {
+ // If the mouse moved into a part of the window's non-client area, then
+ // don't send a mouse exited event since the mouse is still within the
+ // bounds of the ChromeView that's rendering the frame. Note that we do
+ // _NOT_ do this for windows with native frames, since in that case the
+ // mouse really will have left the bounds of the RootView.
+ process_mouse_exited = false;
+ }
+ }
+
+ if (process_mouse_exited)
+ ProcessMouseExited();
+}
+
+LRESULT WindowWin::OnNCActivate(BOOL active) {
+ is_active_ = !!active;
+
+ // If we're not using the native frame, we need to force a synchronous repaint
+ // otherwise we'll be left in the wrong activation state until something else
+ // causes a repaint later.
+ if (!non_client_view_->UseNativeFrame()) {
+ // We can get WM_NCACTIVATE before we're actually visible. If we're not
+ // visible, no need to paint.
+ if (IsWindowVisible(GetNativeView())) {
+ non_client_view_->SchedulePaint();
+ // We need to force a paint now, as a user dragging a window will block
+ // painting operations while the move is in progress.
+ PaintNow(root_view_->GetScheduledPaintRect());
+ }
+ }
+
+ // If we're active again, we should be allowed to render as inactive, so
+ // tell the non-client view. This must be done independently of the check for
+ // disable_inactive_rendering_ since that check is valid even if the frame
+ // is not active, but this can only be done if we've become active.
+ if (IsActive())
+ non_client_view_->DisableInactiveRendering(false);
+
+ // Reset the disable inactive rendering state since activation has changed.
+ if (disable_inactive_rendering_) {
+ disable_inactive_rendering_ = false;
+ return CallDefaultNCActivateHandler(TRUE);
+ }
+ return CallDefaultNCActivateHandler(active);
+}
+
+LRESULT WindowWin::OnNCCalcSize(BOOL mode, LPARAM l_param) {
+ // We only need to adjust the client size/paint handling when we're not using
+ // the native frame.
+ if (non_client_view_->UseNativeFrame())
+ return WidgetWin::OnNCCalcSize(mode, l_param);
+
+ RECT* client_rect = mode ?
+ &reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0] :
+ reinterpret_cast<RECT*>(l_param);
+ if (IsMaximized()) {
+ // Make the maximized mode client rect fit the screen exactly, by
+ // subtracting the border Windows automatically adds for maximized mode.
+ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
+ InflateRect(client_rect, -border_thickness, -border_thickness);
+
+ // Find all auto-hide taskbars along the screen edges and adjust in by the
+ // thickness of the auto-hide taskbar on each such edge, so the window isn't
+ // treated as a "fullscreen app", which would cause the taskbars to
+ // disappear.
+ HMONITOR monitor = MonitorFromWindow(GetNativeView(),
+ MONITOR_DEFAULTTONULL);
+ if (win_util::EdgeHasTopmostAutoHideTaskbar(ABE_LEFT, monitor))
+ client_rect->left += win_util::kAutoHideTaskbarThicknessPx;
+ if (win_util::EdgeHasTopmostAutoHideTaskbar(ABE_TOP, monitor))
+ client_rect->top += win_util::kAutoHideTaskbarThicknessPx;
+ if (win_util::EdgeHasTopmostAutoHideTaskbar(ABE_RIGHT, monitor))
+ client_rect->right -= win_util::kAutoHideTaskbarThicknessPx;
+ if (win_util::EdgeHasTopmostAutoHideTaskbar(ABE_BOTTOM, monitor))
+ client_rect->bottom -= win_util::kAutoHideTaskbarThicknessPx;
+
+ // We cannot return WVR_REDRAW when there is nonclient area, or Windows
+ // exhibits bugs where client pixels and child HWNDs are mispositioned by
+ // the width/height of the upper-left nonclient area.
+ return 0;
+ }
+
+ // If the window bounds change, we're going to relayout and repaint anyway.
+ // Returning WVR_REDRAW avoids an extra paint before that of the old client
+ // pixels in the (now wrong) location, and thus makes actions like resizing a
+ // window from the left edge look slightly less broken.
+ return mode ? WVR_REDRAW : 0;
+}
+
+LRESULT WindowWin::OnNCHitTest(const CPoint& point) {
+ // First, give the NonClientView a chance to test the point to see if it
+ // provides any of the non-client area.
+ CPoint temp = point;
+ MapWindowPoints(HWND_DESKTOP, GetNativeView(), &temp, 1);
+ int component = non_client_view_->NonClientHitTest(gfx::Point(temp));
+ if (component != HTNOWHERE)
+ return component;
+
+ // Otherwise, we let Windows do all the native frame non-client handling for
+ // us.
+ return WidgetWin::OnNCHitTest(point);
+}
+
+namespace {
+struct ClipState {
+ // The window being painted.
+ HWND parent;
+
+ // DC painting to.
+ HDC dc;
+
+ // Origin of the window in terms of the screen.
+ int x;
+ int y;
+};
+
+// See comments in OnNCPaint for details of this function.
+static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) {
+ ClipState* clip_state = reinterpret_cast<ClipState*>(param);
+ if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) {
+ RECT bounds;
+ GetWindowRect(window, &bounds);
+ ExcludeClipRect(clip_state->dc,
+ bounds.left - clip_state->x,
+ bounds.top - clip_state->y,
+ bounds.right - clip_state->x,
+ bounds.bottom - clip_state->y);
+ }
+ return TRUE;
+}
+} // namespace
+
+void WindowWin::OnNCPaint(HRGN rgn) {
+ // We only do non-client painting if we're not using the native frame.
+ if (non_client_view_->UseNativeFrame()) {
+ WidgetWin::OnNCPaint(rgn);
+ return;
+ }
+
+ // We have an NC region and need to paint it. We expand the NC region to
+ // include the dirty region of the root view. This is done to minimize
+ // paints.
+ CRect window_rect;
+ GetWindowRect(&window_rect);
+
+ if (window_rect.Width() != root_view_->width() ||
+ window_rect.Height() != root_view_->height()) {
+ // If the size of the window differs from the size of the root view it
+ // means we're being asked to paint before we've gotten a WM_SIZE. This can
+ // happen when the user is interactively resizing the window. To avoid
+ // mass flickering we don't do anything here. Once we get the WM_SIZE we'll
+ // reset the region of the window which triggers another WM_NCPAINT and
+ // all is well.
+ return;
+ }
+
+ CRect dirty_region;
+ // A value of 1 indicates paint all.
+ if (!rgn || rgn == reinterpret_cast<HRGN>(1)) {
+ dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height());
+ } else {
+ RECT rgn_bounding_box;
+ GetRgnBox(rgn, &rgn_bounding_box);
+ if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect))
+ return; // Dirty region doesn't intersect window bounds, bale.
+
+ // rgn_bounding_box is in screen coordinates. Map it to window coordinates.
+ OffsetRect(&dirty_region, -window_rect.left, -window_rect.top);
+ }
+
+ // In theory GetDCEx should do what we want, but I couldn't get it to work.
+ // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell
+ // it doesn't work at all. So, instead we get the DC for the window then
+ // manually clip out the children.
+ HDC dc = GetWindowDC(GetNativeView());
+ ClipState clip_state;
+ clip_state.x = window_rect.left;
+ clip_state.y = window_rect.top;
+ clip_state.parent = GetNativeView();
+ clip_state.dc = dc;
+ EnumChildWindows(GetNativeView(), &ClipDCToChild,
+ reinterpret_cast<LPARAM>(&clip_state));
+
+ RootView* root_view = GetRootView();
+ gfx::Rect old_paint_region =
+ root_view->GetScheduledPaintRectConstrainedToSize();
+
+ if (!old_paint_region.IsEmpty()) {
+ // The root view has a region that needs to be painted. Include it in the
+ // region we're going to paint.
+
+ CRect old_paint_region_crect = old_paint_region.ToRECT();
+ CRect tmp = dirty_region;
+ UnionRect(&dirty_region, &tmp, &old_paint_region_crect);
+ }
+
+ root_view->SchedulePaint(gfx::Rect(dirty_region), false);
+
+ // ChromeCanvasPaints destructor does the actual painting. As such, wrap the
+ // following in a block to force paint to occur so that we can release the dc.
+ {
+ ChromeCanvasPaint canvas(dc, opaque(), dirty_region.left, dirty_region.top,
+ dirty_region.Width(), dirty_region.Height());
+
+ root_view->ProcessPaint(&canvas);
+ }
+
+ ReleaseDC(GetNativeView(), dc);
+}
+
+void WindowWin::OnNCLButtonDown(UINT ht_component, const CPoint& point) {
+ // When we're using a native frame, window controls work without us
+ // interfering.
+ if (!non_client_view_->UseNativeFrame()) {
+ switch (ht_component) {
+ case HTCLOSE:
+ case HTMINBUTTON:
+ case HTMAXBUTTON: {
+ // When the mouse is pressed down in these specific non-client areas,
+ // we need to tell the RootView to send the mouse pressed event (which
+ // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_
+ // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be
+ // sent by the applicable button's ButtonListener. We _have_ to do this
+ // way rather than letting Windows just send the syscommand itself (as
+ // would happen if we never did this dance) because for some insane
+ // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed
+ // window control button appearance, in the Windows classic style, over
+ // our view! Ick! By handling this message we prevent Windows from
+ // doing this undesirable thing, but that means we need to roll the
+ // sys-command handling ourselves.
+ ProcessNCMousePress(point, MK_LBUTTON);
+ return;
+ }
+ }
+ }
+
+ // TODO(beng): figure out why we need to run the system menu manually
+ // ourselves. This is wrong and causes many subtle bugs.
+ // From my initial research, it looks like DefWindowProc tries
+ // to run it but fails before sending the initial WM_MENUSELECT
+ // for the sysmenu.
+ if (ht_component == HTSYSMENU)
+ RunSystemMenu(non_client_view_->GetSystemMenuPoint());
+ else
+ WidgetWin::OnNCLButtonDown(ht_component, point);
+
+ /* TODO(beng): Fix the standard non-client over-painting bug. This code
+ doesn't work but identifies the problem.
+ if (!IsMsgHandled()) {
+ // WindowWin::OnNCLButtonDown set the message as unhandled. This normally
+ // means WidgetWin::ProcessWindowMessage will pass it to
+ // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird
+ // non-client painting, so we need to call it directly here inside a
+ // scoped update lock.
+ ScopedRedrawLock lock(this);
+ DefWindowProc(GetNativeView(), WM_NCLBUTTONDOWN, ht_component,
+ MAKELPARAM(point.x, point.y));
+ SetMsgHandled(TRUE);
+ }
+ */
+}
+
+void WindowWin::OnNCRButtonDown(UINT ht_component, const CPoint& point) {
+ if (ht_component == HTCAPTION || ht_component == HTSYSMENU)
+ RunSystemMenu(gfx::Point(point));
+ else
+ WidgetWin::OnNCRButtonDown(ht_component, point);
+}
+
+LRESULT WindowWin::OnNCUAHDrawCaption(UINT msg, WPARAM w_param,
+ LPARAM l_param) {
+ // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
+ // an explanation about why we need to handle this message.
+ SetMsgHandled(!non_client_view_->UseNativeFrame());
+ return 0;
+}
+
+LRESULT WindowWin::OnNCUAHDrawFrame(UINT msg, WPARAM w_param,
+ LPARAM l_param) {
+ // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
+ // an explanation about why we need to handle this message.
+ SetMsgHandled(!non_client_view_->UseNativeFrame());
+ return 0;
+}
+
+LRESULT WindowWin::OnSetCursor(HWND window, UINT hittest_code, UINT message) {
+ // If the window is disabled, it's because we're showing a modal dialog box.
+ // We need to let DefWindowProc handle the message. That's because
+ // DefWindowProc for WM_SETCURSOR with message = some kind of mouse button
+ // down message sends the top level window a WM_ACTIVATEAPP message, which we
+ // otherwise wouldn't get. The symptom of not doing this is that if the user
+ // has a window in the background with a modal dialog open, they can't click
+ // on the disabled background window to bring the entire stack to the front.
+ // This is annoying because they then have to move all the foreground windows
+ // out of the way to be able to activate said window. I love how on Windows,
+ // the answer isn't always logical.
+ if (!IsWindowEnabled(GetNativeView()))
+ return WidgetWin::OnSetCursor(window, hittest_code, message);
+
+ int index = RC_NORMAL;
+ switch (hittest_code) {
+ case HTTOP:
+ case HTBOTTOM:
+ index = RC_VERTICAL;
+ break;
+ case HTTOPLEFT:
+ case HTBOTTOMRIGHT:
+ index = RC_NWSE;
+ break;
+ case HTTOPRIGHT:
+ case HTBOTTOMLEFT:
+ index = RC_NESW;
+ break;
+ case HTLEFT:
+ case HTRIGHT:
+ index = RC_HORIZONTAL;
+ break;
+ case HTCAPTION:
+ case HTCLIENT:
+ index = RC_NORMAL;
+ break;
+ }
+ SetCursor(resize_cursors_[index]);
+ return 0;
+}
+
+LRESULT WindowWin::OnSetIcon(UINT size_type, HICON new_icon) {
+ // This shouldn't hurt even if we're using the native frame.
+ ScopedRedrawLock lock(this);
+ return DefWindowProc(GetNativeView(), WM_SETICON, size_type,
+ reinterpret_cast<LPARAM>(new_icon));
+}
+
+LRESULT WindowWin::OnSetText(const wchar_t* text) {
+ // This shouldn't hurt even if we're using the native frame.
+ ScopedRedrawLock lock(this);
+ return DefWindowProc(GetNativeView(), WM_SETTEXT, NULL,
+ reinterpret_cast<LPARAM>(text));
+}
+
+void WindowWin::OnSettingChange(UINT flags, const wchar_t* section) {
+ if (!GetParent() && (flags == SPI_SETWORKAREA)) {
+ // Fire a dummy SetWindowPos() call, so we'll trip the code in
+ // OnWindowPosChanging() below that notices work area changes.
+ ::SetWindowPos(GetNativeView(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
+ SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+ SetMsgHandled(TRUE);
+ } else {
+ WidgetWin::OnSettingChange(flags, section);
+ }
+}
+
+void WindowWin::OnSize(UINT size_param, const CSize& new_size) {
+ // Don't no-op if the new_size matches current size. If our normal bounds
+ // and maximized bounds are the same, then we need to layout (because we
+ // layout differently when maximized).
+ SaveWindowPosition();
+ ChangeSize(size_param, new_size);
+ RedrawWindow(GetNativeView(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
+
+ // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've
+ // invoked OnSize we ensure the RootView has been laid out.
+ ResetWindowRegion(false);
+}
+
+void WindowWin::OnSysCommand(UINT notification_code, CPoint click) {
+ // Windows uses the 4 lower order bits of |notification_code| for type-
+ // specific information so we must exclude this when comparing.
+ static const int sc_mask = 0xFFF0;
+ // Ignore size/move/maximize in fullscreen mode.
+ if (IsFullscreen() &&
+ (((notification_code & sc_mask) == SC_SIZE) ||
+ ((notification_code & sc_mask) == SC_MOVE) ||
+ ((notification_code & sc_mask) == SC_MAXIMIZE)))
+ return;
+ if (!non_client_view_->UseNativeFrame()) {
+ if ((notification_code & sc_mask) == SC_MINIMIZE ||
+ (notification_code & sc_mask) == SC_MAXIMIZE ||
+ (notification_code & sc_mask) == SC_RESTORE) {
+ non_client_view_->ResetWindowControls();
+ } else if ((notification_code & sc_mask) == SC_MOVE ||
+ (notification_code & sc_mask) == SC_SIZE) {
+ if (lock_updates_) {
+ // We were locked, before entering a resize or move modal loop. Now that
+ // we've begun to move the window, we need to unlock updates so that the
+ // sizing/moving feedback can be continuous.
+ UnlockUpdates();
+ }
+ }
+ }
+
+ // First see if the delegate can handle it.
+ if (window_delegate_->ExecuteWindowsCommand(notification_code))
+ return;
+
+ if (notification_code == IDC_ALWAYS_ON_TOP) {
+ is_always_on_top_ = !is_always_on_top_;
+
+ // Change the menu check state.
+ HMENU system_menu = GetSystemMenu(GetNativeView(), FALSE);
+ MENUITEMINFO menu_info;
+ memset(&menu_info, 0, sizeof(MENUITEMINFO));
+ menu_info.cbSize = sizeof(MENUITEMINFO);
+ BOOL r = GetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP,
+ FALSE, &menu_info);
+ DCHECK(r);
+ menu_info.fMask = MIIM_STATE;
+ if (is_always_on_top_)
+ menu_info.fState = MFS_CHECKED;
+ r = SetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, FALSE, &menu_info);
+
+ // Now change the actual window's behavior.
+ AlwaysOnTopChanged();
+ } else if ((notification_code == SC_KEYMENU) && (click.x == VK_SPACE)) {
+ // Run the system menu at the NonClientView's desired location.
+ RunSystemMenu(non_client_view_->GetSystemMenuPoint());
+ } else {
+ // Use the default implementation for any other command.
+ DefWindowProc(GetNativeView(), WM_SYSCOMMAND, notification_code,
+ MAKELPARAM(click.y, click.x));
+ }
+}
+
+void WindowWin::OnWindowPosChanging(WINDOWPOS* window_pos) {
+ if (force_hidden_count_) {
+ // Prevent the window from being made visible if we've been asked to do so.
+ // See comment in header as to why we might want this.
+ window_pos->flags &= ~SWP_SHOWWINDOW;
+ }
+
+ if (ignore_window_pos_changes_) {
+ // If somebody's trying to toggle our visibility, change the nonclient area,
+ // change our Z-order, or activate us, we should probably let it go through.
+ if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) |
+ SWP_FRAMECHANGED)) &&
+ (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) {
+ // Just sizing/moving the window; ignore.
+ window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW;
+ window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW);
+ }
+ } else if (!GetParent()) {
+ CRect window_rect;
+ HMONITOR monitor;
+ gfx::Rect monitor_rect, work_area;
+ if (GetWindowRect(&window_rect) &&
+ GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) {
+ if (monitor && (monitor == last_monitor_) &&
+ (IsFullscreen() || ((monitor_rect == last_monitor_rect_) &&
+ (work_area != last_work_area_)))) {
+ // A rect for the monitor we're on changed. Normally Windows notifies
+ // us about this (and thus we're reaching here due to the SetWindowPos()
+ // call in OnSettingChange() above), but with some software (e.g.
+ // nVidia's nView desktop manager) the work area can change asynchronous
+ // to any notification, and we're just sent a SetWindowPos() call with a
+ // new (frequently incorrect) position/size. In either case, the best
+ // response is to throw away the existing position/size information in
+ // |window_pos| and recalculate it based on the new work rect.
+ gfx::Rect new_window_rect;
+ if (IsFullscreen()) {
+ new_window_rect = monitor_rect;
+ } else if (IsZoomed()) {
+ new_window_rect = work_area;
+ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
+ new_window_rect.Inset(-border_thickness, -border_thickness);
+ } else {
+ new_window_rect = gfx::Rect(window_rect).AdjustToFit(work_area);
+ }
+ window_pos->x = new_window_rect.x();
+ window_pos->y = new_window_rect.y();
+ window_pos->cx = new_window_rect.width();
+ window_pos->cy = new_window_rect.height();
+ // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child
+ // HWNDs for some reason.
+ window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW);
+ window_pos->flags |= SWP_NOCOPYBITS;
+
+ // Now ignore all immediately-following SetWindowPos() changes. Windows
+ // likes to (incorrectly) recalculate what our position/size should be
+ // and send us further updates.
+ ignore_window_pos_changes_ = true;
+ DCHECK(ignore_pos_changes_factory_.empty());
+ MessageLoop::current()->PostTask(FROM_HERE,
+ ignore_pos_changes_factory_.NewRunnableMethod(
+ &WindowWin::StopIgnoringPosChanges));
+ }
+ last_monitor_ = monitor;
+ last_monitor_rect_ = monitor_rect;
+ last_work_area_ = work_area;
+ }
+ }
+
+ WidgetWin::OnWindowPosChanging(window_pos);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowWin, private:
+
+void WindowWin::BecomeModal() {
+ // We implement modality by crawling up the hierarchy of windows starting
+ // at the owner, disabling all of them so that they don't receive input
+ // messages.
+ DCHECK(owning_hwnd_) << "Can't create a modal dialog without an owner";
+ HWND start = owning_hwnd_;
+ while (start != NULL) {
+ ::EnableWindow(start, FALSE);
+ start = ::GetParent(start);
+ }
+}
+
+void WindowWin::SetInitialFocus() {
+ if (!focus_on_creation_)
+ return;
+
+ View* v = window_delegate_->GetInitiallyFocusedView();
+ if (v) {
+ v->RequestFocus();
+ } else {
+ // The window does not get keyboard messages unless we focus it, not sure
+ // why.
+ SetFocus(GetNativeView());
+ }
+}
+
+void WindowWin::SetInitialBounds(const gfx::Rect& create_bounds) {
+ // First we obtain the window's saved show-style and store it. We need to do
+ // this here, rather than in Show() because by the time Show() is called,
+ // the window's size will have been reset (below) and the saved maximized
+ // state will have been lost. Sadly there's no way to tell on Windows when
+ // a window is restored from maximized state, so we can't more accurately
+ // track maximized state independently of sizing information.
+ window_delegate_->GetSavedMaximizedState(&saved_maximized_state_);
+
+ // Restore the window's placement from the controller.
+ gfx::Rect saved_bounds(create_bounds.ToRECT());
+ if (window_delegate_->GetSavedWindowBounds(&saved_bounds)) {
+ // Make sure the bounds are at least the minimum size.
+ if (saved_bounds.width() < minimum_size_.cx) {
+ saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(),
+ saved_bounds.right() + minimum_size_.cx -
+ saved_bounds.width(),
+ saved_bounds.bottom());
+ }
+
+ if (saved_bounds.height() < minimum_size_.cy) {
+ saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(),
+ saved_bounds.right(),
+ saved_bounds.bottom() + minimum_size_.cy -
+ saved_bounds.height());
+ }
+
+ // "Show state" (maximized, minimized, etc) is handled by Show().
+ // Don't use SetBounds here. SetBounds constrains to the size of the
+ // monitor, but we don't want that when creating a new window as the result
+ // of dragging out a tab to create a new window.
+ SetWindowPos(NULL, saved_bounds.x(), saved_bounds.y(),
+ saved_bounds.width(), saved_bounds.height(), 0);
+ } else {
+ if (create_bounds.IsEmpty()) {
+ // No initial bounds supplied, so size the window to its content and
+ // center over its parent.
+ SizeWindowToDefault();
+ } else {
+ // Use the supplied initial bounds.
+ SetBounds(create_bounds);
+ }
+ }
+}
+
+void WindowWin::InitAlwaysOnTopState() {
+ is_always_on_top_ = false;
+ if (window_delegate_->GetSavedAlwaysOnTopState(&is_always_on_top_) &&
+ is_always_on_top_ != window_delegate_->IsAlwaysOnTop()) {
+ AlwaysOnTopChanged();
+ }
+
+ if (window_delegate_->HasAlwaysOnTopMenu())
+ AddAlwaysOnTopSystemMenuItem();
+}
+
+void WindowWin::AddAlwaysOnTopSystemMenuItem() {
+ // The Win32 API requires that we own the text.
+ always_on_top_menu_text_ = l10n_util::GetString(IDS_ALWAYS_ON_TOP);
+
+ // Let's insert a menu to the window.
+ HMENU system_menu = ::GetSystemMenu(GetNativeView(), FALSE);
+ int index = ::GetMenuItemCount(system_menu) - 1;
+ if (index < 0) {
+ // Paranoia check.
+ NOTREACHED();
+ index = 0;
+ }
+ // First we add the separator.
+ MENUITEMINFO menu_info;
+ memset(&menu_info, 0, sizeof(MENUITEMINFO));
+ menu_info.cbSize = sizeof(MENUITEMINFO);
+ menu_info.fMask = MIIM_FTYPE;
+ menu_info.fType = MFT_SEPARATOR;
+ ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
+
+ // Then the actual menu.
+ menu_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE;
+ menu_info.fType = MFT_STRING;
+ menu_info.fState = MFS_ENABLED;
+ if (is_always_on_top_)
+ menu_info.fState |= MFS_CHECKED;
+ menu_info.wID = IDC_ALWAYS_ON_TOP;
+ menu_info.dwTypeData = const_cast<wchar_t*>(always_on_top_menu_text_.c_str());
+ ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
+}
+
+void WindowWin::RestoreEnabledIfNecessary() {
+ if (is_modal_ && !restored_enabled_) {
+ restored_enabled_ = true;
+ // If we were run modally, we need to undo the disabled-ness we inflicted on
+ // the owner's parent hierarchy.
+ HWND start = owning_hwnd_;
+ while (start != NULL) {
+ ::EnableWindow(start, TRUE);
+ start = ::GetParent(start);
+ }
+ }
+}
+
+void WindowWin::AlwaysOnTopChanged() {
+ ::SetWindowPos(GetNativeView(),
+ is_always_on_top_ ? HWND_TOPMOST : HWND_NOTOPMOST,
+ 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+}
+
+DWORD WindowWin::CalculateWindowStyle() {
+ DWORD window_styles =
+ WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_SYSMENU | WS_CAPTION;
+ bool can_resize = window_delegate_->CanResize();
+ bool can_maximize = window_delegate_->CanMaximize();
+ if (can_maximize) {
+ window_styles |= WS_OVERLAPPEDWINDOW;
+ } else if (can_resize) {
+ window_styles |= WS_OVERLAPPED | WS_THICKFRAME;
+ }
+ if (window_delegate_->AsDialogDelegate()) {
+ window_styles |= DS_MODALFRAME;
+ // NOTE: Turning this off means we lose the close button, which is bad.
+ // Turning it on though means the user can maximize or size the window
+ // from the system menu, which is worse. We may need to provide our own
+ // menu to get the close button to appear properly.
+ // window_styles &= ~WS_SYSMENU;
+ }
+ return window_styles;
+}
+
+DWORD WindowWin::CalculateWindowExStyle() {
+ DWORD window_ex_styles = 0;
+ if (window_delegate_->AsDialogDelegate())
+ window_ex_styles |= WS_EX_DLGMODALFRAME;
+ if (window_delegate_->IsAlwaysOnTop())
+ window_ex_styles |= WS_EX_TOPMOST;
+ return window_ex_styles;
+}
+
+void WindowWin::SaveWindowPosition() {
+ // The window delegate does the actual saving for us. It seems like (judging
+ // by go/crash) that in some circumstances we can end up here after
+ // WM_DESTROY, at which point the window delegate is likely gone. So just
+ // bail.
+ if (!window_delegate_)
+ return;
+
+ WINDOWPLACEMENT win_placement = { 0 };
+ win_placement.length = sizeof(WINDOWPLACEMENT);
+
+ BOOL r = GetWindowPlacement(GetNativeView(), &win_placement);
+ DCHECK(r);
+
+ bool maximized = (win_placement.showCmd == SW_SHOWMAXIMIZED);
+ CRect window_bounds(win_placement.rcNormalPosition);
+ window_delegate_->SaveWindowPlacement(
+ gfx::Rect(win_placement.rcNormalPosition), maximized, is_always_on_top_);
+}
+
+void WindowWin::LockUpdates() {
+ lock_updates_ = true;
+ saved_window_style_ = GetWindowLong(GWL_STYLE);
+ SetWindowLong(GWL_STYLE, saved_window_style_ & ~WS_VISIBLE);
+}
+
+void WindowWin::UnlockUpdates() {
+ SetWindowLong(GWL_STYLE, saved_window_style_);
+ lock_updates_ = false;
+}
+
+void WindowWin::ResetWindowRegion(bool force) {
+ // A native frame uses the native window region, and we don't want to mess
+ // with it.
+ if (non_client_view_->UseNativeFrame()) {
+ if (force)
+ SetWindowRgn(NULL, TRUE);
+ return;
+ }
+
+ // Changing the window region is going to force a paint. Only change the
+ // window region if the region really differs.
+ HRGN current_rgn = CreateRectRgn(0, 0, 0, 0);
+ int current_rgn_result = GetWindowRgn(GetNativeView(), current_rgn);
+
+ CRect window_rect;
+ GetWindowRect(&window_rect);
+ HRGN new_region;
+ gfx::Path window_mask;
+ non_client_view_->GetWindowMask(
+ gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask);
+ new_region = window_mask.CreateHRGN();
+
+ if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) {
+ // SetWindowRgn takes ownership of the HRGN created by CreateHRGN.
+ SetWindowRgn(new_region, TRUE);
+ } else {
+ DeleteObject(new_region);
+ }
+
+ DeleteObject(current_rgn);
+}
+
+void WindowWin::ProcessNCMousePress(const CPoint& point, int flags) {
+ CPoint temp = point;
+ MapWindowPoints(HWND_DESKTOP, GetNativeView(), &temp, 1);
+ UINT message_flags = 0;
+ if ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)
+ message_flags |= MK_CONTROL;
+ if ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)
+ message_flags |= MK_SHIFT;
+ message_flags |= flags;
+ ProcessMousePressed(temp, message_flags, false, false);
+}
+
+LRESULT WindowWin::CallDefaultNCActivateHandler(BOOL active) {
+ // The DefWindowProc handling for WM_NCACTIVATE renders the classic-look
+ // window title bar directly, so we need to use a redraw lock here to prevent
+ // it from doing so.
+ ScopedRedrawLock lock(this);
+ return DefWindowProc(GetNativeView(), WM_NCACTIVATE, active, 0);
+}
+
+void WindowWin::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ resize_cursors_[RC_NORMAL] = LoadCursor(NULL, IDC_ARROW);
+ resize_cursors_[RC_VERTICAL] = LoadCursor(NULL, IDC_SIZENS);
+ resize_cursors_[RC_HORIZONTAL] = LoadCursor(NULL, IDC_SIZEWE);
+ resize_cursors_[RC_NESW] = LoadCursor(NULL, IDC_SIZENESW);
+ resize_cursors_[RC_NWSE] = LoadCursor(NULL, IDC_SIZENWSE);
+ initialized = true;
+ }
+}
+
+namespace {
+// static
+static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
+ WidgetWin* widget = reinterpret_cast<WidgetWin*>(
+ win_util::GetWindowUserData(hwnd));
+ if (!widget)
+ return TRUE;
+
+ // If the toplevel HWND is a Window, close it if it's identified as a
+ // secondary window.
+ Window* window = widget->GetWindow();
+ if (window) {
+ if (!window->IsAppWindow())
+ window->Close();
+ } else {
+ // If it's not a Window, then close it anyway since it probably is
+ // secondary.
+ widget->Close();
+ }
+ return TRUE;
+}
+} // namespace
+
+void Window::CloseAllSecondaryWindows() {
+ EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0);
+}
+
+} // namespace views
diff --git a/views/window/window_win.h b/views/window/window_win.h
new file mode 100644
index 0000000..76d5196
--- /dev/null
+++ b/views/window/window_win.h
@@ -0,0 +1,307 @@
+// Copyright (c) 2006-2008 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_WINDOW_WINDOW_WIN_H_
+#define VIEWS_WINDOW_WINDOW_WIN_H_
+
+#include "views/widget/widget_win.h"
+#include "views/window/client_view.h"
+#include "views/window/non_client_view.h"
+#include "views/window/window.h"
+
+namespace gfx {
+class Point;
+class Size;
+};
+
+namespace views {
+
+class Client;
+class WindowDelegate;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// WindowWin
+//
+// A WindowWin is a WidgetWin that has a caption and a border. The frame is
+// rendered by the operating system.
+//
+///////////////////////////////////////////////////////////////////////////////
+class WindowWin : public WidgetWin,
+ public Window {
+ public:
+ virtual ~WindowWin();
+
+ // Show the window with the specified show command.
+ void Show(int show_state);
+
+ // Retrieve the show state of the window. This is one of the SW_SHOW* flags
+ // passed into Windows' ShowWindow method. For normal windows this defaults
+ // to SW_SHOWNORMAL, however windows (e.g. the main window) can override this
+ // method to provide different values (e.g. retrieve the user's specified
+ // show state from the shortcut starutp info).
+ virtual int GetShowState() const;
+
+ // Executes the specified SC_command.
+ void ExecuteSystemMenuCommand(int command);
+
+ // Hides the window if it hasn't already been force-hidden, then increments
+ // |force_hidden_count_| to prevent it from being shown again until
+ // PopForceHidden()) is called.
+ void PushForceHidden();
+
+ // Decrements |force_hidden_count_| and, if it is now zero, shows the window.
+ void PopForceHidden();
+
+ // Accessors and setters for various properties.
+ HWND owning_window() const { return owning_hwnd_; }
+ void set_focus_on_creation(bool focus_on_creation) {
+ focus_on_creation_ = focus_on_creation;
+ }
+
+ // Window overrides:
+ virtual gfx::Rect GetBounds() const;
+ virtual gfx::Rect GetNormalBounds() const;
+ virtual void SetBounds(const gfx::Rect& bounds);
+ virtual void SetBounds(const gfx::Rect& bounds,
+ gfx::NativeWindow other_window);
+ virtual void Show();
+ virtual void Activate();
+ virtual void Close();
+ virtual void Maximize();
+ virtual void Minimize();
+ virtual void Restore();
+ virtual bool IsActive() const;
+ virtual bool IsVisible() const;
+ virtual bool IsMaximized() const;
+ virtual bool IsMinimized() const;
+ virtual void SetFullscreen(bool fullscreen);
+ virtual bool IsFullscreen() const;
+ virtual void EnableClose(bool enable);
+ virtual void DisableInactiveRendering();
+ virtual void UpdateWindowTitle();
+ virtual void UpdateWindowIcon();
+ virtual NonClientFrameView* CreateFrameViewForWindow();
+ virtual void UpdateFrameAfterFrameChange();
+ virtual WindowDelegate* GetDelegate() const;
+ virtual NonClientView* GetNonClientView() const;
+ virtual ClientView* GetClientView() const;
+ virtual gfx::NativeWindow GetNativeWindow() const;
+
+ protected:
+ friend Window;
+
+ // Constructs the WindowWin. |window_delegate| cannot be NULL.
+ explicit WindowWin(WindowDelegate* window_delegate);
+
+ // Create the Window.
+ // If parent is NULL, this WindowWin is top level on the desktop.
+ // If |bounds| is empty, the view is queried for its preferred size and
+ // centered on screen.
+ virtual void Init(HWND parent, const gfx::Rect& bounds);
+
+ // Sizes the window to the default size specified by its ClientView.
+ virtual void SizeWindowToDefault();
+
+ // Shows the system menu at the specified screen point.
+ void RunSystemMenu(const gfx::Point& point);
+
+ // Overridden from WidgetWin:
+ virtual void OnActivate(UINT action, BOOL minimized, HWND window);
+ virtual void OnActivateApp(BOOL active, DWORD thread_id);
+ virtual LRESULT OnAppCommand(HWND window, short app_command, WORD device,
+ int keystate);
+ virtual void OnCommand(UINT notification_code, int command_id, HWND window);
+ virtual void OnDestroy();
+ virtual LRESULT OnDwmCompositionChanged(UINT msg, WPARAM w_param,
+ LPARAM l_param);
+ virtual void OnFinalMessage(HWND window);
+ virtual void OnGetMinMaxInfo(MINMAXINFO* minmax_info);
+ virtual void OnInitMenu(HMENU menu);
+ virtual void OnMouseLeave();
+ virtual LRESULT OnNCActivate(BOOL active);
+ virtual LRESULT OnNCCalcSize(BOOL mode, LPARAM l_param);
+ virtual LRESULT OnNCHitTest(const CPoint& point);
+ virtual void OnNCPaint(HRGN rgn);
+ virtual void OnNCLButtonDown(UINT ht_component, const CPoint& point);
+ virtual void OnNCRButtonDown(UINT ht_component, const CPoint& point);
+ virtual LRESULT OnNCUAHDrawCaption(UINT msg, WPARAM w_param, LPARAM l_param);
+ virtual LRESULT OnNCUAHDrawFrame(UINT msg, WPARAM w_param, LPARAM l_param);
+ virtual LRESULT OnSetCursor(HWND window, UINT hittest_code, UINT message);
+ virtual LRESULT OnSetIcon(UINT size_type, HICON new_icon);
+ virtual LRESULT OnSetText(const wchar_t* text);
+ virtual void OnSettingChange(UINT flags, const wchar_t* section);
+ virtual void OnSize(UINT size_param, const CSize& new_size);
+ virtual void OnSysCommand(UINT notification_code, CPoint click);
+ virtual void OnWindowPosChanging(WINDOWPOS* window_pos);
+ virtual Window* GetWindow() { return this; }
+ virtual const Window* GetWindow() const { return this; }
+
+ // Accessor for disable_inactive_rendering_.
+ bool disable_inactive_rendering() const {
+ return disable_inactive_rendering_;
+ }
+
+ private:
+ // Information saved before going into fullscreen mode, used to restore the
+ // window afterwards.
+ struct SavedWindowInfo {
+ bool maximized;
+ LONG style;
+ LONG ex_style;
+ RECT window_rect;
+ };
+
+ // Set the window as modal (by disabling all the other windows).
+ void BecomeModal();
+
+ // Sets-up the focus manager with the view that should have focus when the
+ // window is shown the first time. If NULL is returned, the focus goes to the
+ // button if there is one, otherwise the to the Cancel button.
+ void SetInitialFocus();
+
+ // Place and size the window when it is created. |create_bounds| are the
+ // bounds used when the window was created.
+ void SetInitialBounds(const gfx::Rect& create_bounds);
+
+ // Restore saved always on stop state and add the always on top system menu
+ // if needed.
+ void InitAlwaysOnTopState();
+
+ // Add an item for "Always on Top" to the System Menu.
+ void AddAlwaysOnTopSystemMenuItem();
+
+ // If necessary, enables all ancestors.
+ void RestoreEnabledIfNecessary();
+
+ // Update the window style to reflect the always on top state.
+ void AlwaysOnTopChanged();
+
+ // Calculate the appropriate window styles for this window.
+ DWORD CalculateWindowStyle();
+ DWORD CalculateWindowExStyle();
+
+ // Asks the delegate if any to save the window's location and size.
+ void SaveWindowPosition();
+
+ // Lock or unlock the window from being able to redraw itself in response to
+ // updates to its invalid region.
+ class ScopedRedrawLock;
+ void LockUpdates();
+ void UnlockUpdates();
+
+ // Stops ignoring SetWindowPos() requests (see below).
+ void StopIgnoringPosChanges() { ignore_window_pos_changes_ = false; }
+
+ // Resets the window region for the current window bounds if necessary.
+ // If |force| is true, the window region is reset to NULL even for native
+ // frame windows.
+ void ResetWindowRegion(bool force);
+
+ // Converts a non-client mouse down message to a regular ChromeViews event
+ // and handle it. |point| is the mouse position of the message in screen
+ // coords. |flags| are flags that would be passed with a WM_L/M/RBUTTON*
+ // message and relate to things like which button was pressed. These are
+ // combined with flags relating to the current key state.
+ void ProcessNCMousePress(const CPoint& point, int flags);
+
+ // Calls the default WM_NCACTIVATE handler with the specified activation
+ // value, safely wrapping the call in a ScopedRedrawLock to prevent frame
+ // flicker.
+ LRESULT CallDefaultNCActivateHandler(BOOL active);
+
+ // Static resource initialization.
+ static void InitClass();
+ enum ResizeCursor {
+ RC_NORMAL = 0, RC_VERTICAL, RC_HORIZONTAL, RC_NESW, RC_NWSE
+ };
+ static HCURSOR resize_cursors_[6];
+
+ // Our window delegate (see Init method for documentation).
+ WindowDelegate* window_delegate_;
+
+ // The View that provides the non-client area of the window (title bar,
+ // window controls, sizing borders etc). To use an implementation other than
+ // the default, this class must be subclassed and this value set to the
+ // desired implementation before calling |Init|.
+ NonClientView* non_client_view_;
+
+ // Whether we should SetFocus() on a newly created window after
+ // Init(). Defaults to true.
+ bool focus_on_creation_;
+
+ // We need to save the parent window that spawned us, since GetParent()
+ // returns NULL for dialogs.
+ HWND owning_hwnd_;
+
+ // The smallest size the window can be.
+ CSize minimum_size_;
+
+ // Whether or not the window is modal. This comes from the delegate and is
+ // cached at Init time to avoid calling back to the delegate from the
+ // destructor.
+ bool is_modal_;
+
+ // Whether all ancestors have been enabled. This is only used if is_modal_ is
+ // true.
+ bool restored_enabled_;
+
+ // Whether the window is currently always on top.
+ bool is_always_on_top_;
+
+ // We need to own the text of the menu, the Windows API does not copy it.
+ std::wstring always_on_top_menu_text_;
+
+ // True if we're in fullscreen mode.
+ bool fullscreen_;
+
+ // Saved window information from before entering fullscreen mode.
+ SavedWindowInfo saved_window_info_;
+
+ // Set to true if the window is in the process of closing .
+ bool window_closed_;
+
+ // True when the window should be rendered as active, regardless of whether
+ // or not it actually is.
+ bool disable_inactive_rendering_;
+
+ // True if this window is the active top level window.
+ bool is_active_;
+
+ // True if updates to this window are currently locked.
+ bool lock_updates_;
+
+ // The window styles of the window before updates were locked.
+ DWORD saved_window_style_;
+
+ // The saved maximized state for this window. See note in SetInitialBounds
+ // that explains why we save this.
+ bool saved_maximized_state_;
+
+ // When true, this flag makes us discard incoming SetWindowPos() requests that
+ // only change our position/size. (We still allow changes to Z-order,
+ // activation, etc.)
+ bool ignore_window_pos_changes_;
+
+ // The following factory is used to ignore SetWindowPos() calls for short time
+ // periods.
+ ScopedRunnableMethodFactory<WindowWin> ignore_pos_changes_factory_;
+
+ // If this is greater than zero, we should prevent attempts to make the window
+ // visible when we handle WM_WINDOWPOSCHANGING. Some calls like
+ // ShowWindow(SW_RESTORE) make the window visible in addition to restoring it,
+ // when all we want to do is restore it.
+ int force_hidden_count_;
+
+ // The last-seen monitor containing us, and its rect and work area. These are
+ // used to catch updates to the rect and work area and react accordingly.
+ HMONITOR last_monitor_;
+ gfx::Rect last_monitor_rect_, last_work_area_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowWin);
+};
+
+} // namespace views
+
+#endif // VIEWS_WINDOW_WINDOW_WIN_H_