diff options
Diffstat (limited to 'chrome/browser/vista_frame.cc')
-rw-r--r-- | chrome/browser/vista_frame.cc | 1575 |
1 files changed, 1575 insertions, 0 deletions
diff --git a/chrome/browser/vista_frame.cc b/chrome/browser/vista_frame.cc new file mode 100644 index 0000000..c804400 --- /dev/null +++ b/chrome/browser/vista_frame.cc @@ -0,0 +1,1575 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/vista_frame.h" + +#include <windows.h> +#include <atlbase.h> +#include <atlapp.h> +#include <atltheme.h> +#include <commctrl.h> +#include <dwmapi.h> +#include <limits> + +#include "base/gfx/native_theme.h" +#include "base/logging.h" +#include "chrome/app/theme/theme_resources.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/suspend_controller.h" +#include "chrome/browser/tab_contents.h" +#include "chrome/browser/tab_contents_container_view.h" +#include "chrome/browser/tabs/tab_strip.h" +#include "chrome/browser/window_clipping_info.h" +#include "chrome/browser/views/bookmark_bar_view.h" +#include "chrome/browser/views/download_shelf_view.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/win_util.h" +#include "chrome/views/accessibility/view_accessibility.h" +#include "chrome/views/aero_tooltip_manager.h" +#include "chrome/views/background.h" +#include "chrome/views/event.h" +#include "chrome/views/hwnd_view_container.h" +#include "chrome/views/hwnd_notification_source.h" + +#include "generated_resources.h" + +// By how much the toolbar overlaps with the tab strip. +static const int kToolbarOverlapVertOffset = 3; + +// How much space on the right is not used for the tab strip (to provide +// separation between the tabs and the window controls). +static const int kTabStripRightHorizOffset = 30; + +static const int kResizeCornerSize = 12; +static const int kResizeBorder = 5; +static const int kTitlebarHeight = 14; +static const int kTabShadowSize = 2; + +// Status Bubble metrics. +static const int kStatusBubbleHeight = 20; +static const int kStatusBubbleOffset = 2; + +// The line drawn to separate tab end contents. +static const int kSeparationLineHeight = 1; + +// OTR image offsets. +static const int kOTRImageHorizMargin = 2; +static const int kOTRImageVertMargin = 2; + +// The DWM puts a light border around the client area - we need to +// take this border size into account when we reduce its size so that +// we don't draw our content border dropshadow images over the top. +static const int kDwmBorderSize = 1; + +// When laying out the tabstrip, we size it such that it fits to the left of +// the window controls. We get the bounds of the window controls by sending a +// message to the window, but Windows answers the question assuming 96 dpi and +// a fairly conventional screen layout (i.e. not rotated etc). So we need to +// hack around this by making sure the tabstrip is at least this amount inset +// from the right side of the window. +static const int kWindowControlsMinOffset = 100; + +//static +bool VistaFrame::g_initialized = FALSE; + +//static +SkBitmap** VistaFrame::g_bitmaps; + +static const int kImageNames[] = { IDR_CONTENT_BOTTOM_CENTER, + IDR_CONTENT_BOTTOM_LEFT_CORNER, + IDR_CONTENT_BOTTOM_RIGHT_CORNER, + IDR_CONTENT_LEFT_SIDE, + IDR_CONTENT_RIGHT_SIDE, + IDR_CONTENT_TOP_CENTER, + IDR_CONTENT_TOP_LEFT_CORNER, + IDR_CONTENT_TOP_RIGHT_CORNER, +}; + +typedef enum { CT_BOTTOM_CENTER = 0, CT_BOTTOM_LEFT_CORNER, + CT_BOTTOM_RIGHT_CORNER, CT_LEFT_SIDE, CT_RIGHT_SIDE, + CT_TOP_CENTER, CT_TOP_LEFT_CORNER, CT_TOP_RIGHT_CORNER }; + +using ChromeViews::Accelerator; +using ChromeViews::FocusManager; + +//static +VistaFrame* VistaFrame::CreateFrame(const gfx::Rect& bounds, + Browser* browser, + bool is_off_the_record) { + VistaFrame* instance = new VistaFrame(browser); + instance->Create(NULL, bounds.ToRECT(), + l10n_util::GetString(IDS_PRODUCT_NAME).c_str()); + instance->InitAfterHWNDCreated(); + instance->SetIsOffTheRecord(is_off_the_record); + FocusManager::CreateFocusManager(instance->m_hWnd, instance->GetRootView()); + return instance; +} + +VistaFrame::VistaFrame(Browser* browser) + : browser_(browser), + root_view_(this, true), + tabstrip_(NULL), + toolbar_(NULL), + tab_contents_container_(NULL), + custom_window_enabled_(false), + saved_window_placement_(false), + on_mouse_leave_armed_(false), + in_drag_session_(false), + browser_paint_pending_(false), + shelf_view_(NULL), + bookmark_bar_view_(NULL), + info_bar_view_(NULL), + is_off_the_record_(false), + off_the_record_image_(NULL), + ignore_ncactivate_(false), + should_save_window_placement_(browser->GetType() != BrowserType::BROWSER) { + InitializeIfNeeded(); +} + +void VistaFrame::InitializeIfNeeded() { + if (!g_initialized) { + ResourceBundle &rb = ResourceBundle::GetSharedInstance(); + g_bitmaps = new SkBitmap*[arraysize(kImageNames)]; + for (int i = 0; i < arraysize(kImageNames); i++) + g_bitmaps[i] = rb.GetBitmapNamed(kImageNames[i]); + g_initialized = TRUE; + } +} + +VistaFrame::~VistaFrame() { + DestroyBrowser(); +} + +// On Vista (unlike on XP), we let the OS render the Windows decor (close +// button, maximize button, etc.). Since the mirroring infrastructure in +// ChromeViews does not rely on HWND flipping, the Windows decor on Vista are +// not mirrored for RTL locales; that is, they appear on the upper right +// instead of on the upper left (see bug http://b/issue?id=1128173). +// +// Due to the above, we need to be careful when positioning the tabstrip and +// the OTR image. The OTR image and the tabstrip are automatically mirrored for +// RTL locales by the mirroring infrastructure. In order to make sure they are +// not mirrored, we flip them manually so make sure they don't overlap the +// Windows decor. Unfortunately, there is no cleaner way to do this because the +// current ChromeViews mirroring API does not allow mirroring the position of +// a subset of child Views; in other words, once a View is mirrored (in our +// case frame_view_), then the positions of all its child Views (including, in +// our case, the OTR image and the tabstrip) are mirrored. Once bug mentioned +// above is fixed, we won't need to perform any manual mirroring. +void VistaFrame::Layout() { + CRect client_rect; + GetClientRect(&client_rect); + int width = client_rect.Width(); + int height = client_rect.Height(); + + root_view_.SetBounds(0, 0, width, height); + frame_view_->SetBounds(0, 0, width, height); + + if (IsTabStripVisible()) { + tabstrip_->SetVisible(true); + int tabstrip_x = g_bitmaps[CT_LEFT_SIDE]->width(); + if (is_off_the_record_) { + off_the_record_image_->SetVisible(true); + CSize otr_image_size; + off_the_record_image_->GetPreferredSize(&otr_image_size); + tabstrip_x += otr_image_size.cx + (2 * kOTRImageHorizMargin); + gfx::Rect off_the_record_bounds; + if (IsZoomed()) { + off_the_record_bounds.SetRect(g_bitmaps[CT_LEFT_SIDE]->width(), + kResizeBorder, + otr_image_size.cx, + tabstrip_->GetPreferredHeight() - + kToolbarOverlapVertOffset + 1); + } else { + off_the_record_bounds.SetRect(g_bitmaps[CT_LEFT_SIDE]->width(), + kResizeBorder + kTitlebarHeight + + tabstrip_->GetPreferredHeight() - + otr_image_size.cy - + kToolbarOverlapVertOffset + 1, + otr_image_size.cx, + otr_image_size.cy); + } + + if (frame_view_->UILayoutIsRightToLeft()) + off_the_record_bounds.set_x(frame_view_->MirroredLeftPointForRect( + off_the_record_bounds)); + off_the_record_image_->SetBounds(off_the_record_bounds.x(), + off_the_record_bounds.y(), + off_the_record_bounds.width(), + off_the_record_bounds.height()); + + } + + // If we are maxmized, the tab strip will be in line with the window + // controls, so we need to make sure they don't overlap. + int controls_offset = 0; + if(IsZoomed()) { + TITLEBARINFOEX titlebar_info; + titlebar_info.cbSize = sizeof(TITLEBARINFOEX); + SendMessage(WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info); + + // rgrect[2] refers to the minimize button. + controls_offset = client_rect.right - titlebar_info.rgrect[2].left; + controls_offset = std::max(controls_offset, kWindowControlsMinOffset); + } + + gfx::Rect tabstrip_bounds(tabstrip_x, + kResizeBorder + (IsZoomed() ? + kDwmBorderSize : kTitlebarHeight), + width - tabstrip_x - kTabStripRightHorizOffset - + controls_offset, + tabstrip_->GetPreferredHeight()); + if (frame_view_->UILayoutIsRightToLeft() && + (IsZoomed() || is_off_the_record_)) + tabstrip_bounds.set_x( + frame_view_->MirroredLeftPointForRect(tabstrip_bounds)); + tabstrip_->SetBounds(tabstrip_bounds.x(), + tabstrip_bounds.y(), + tabstrip_bounds.width(), + tabstrip_bounds.height()); + + frame_view_->SetContentsOffset(tabstrip_->GetY() + + tabstrip_->GetHeight() - + kToolbarOverlapVertOffset); + } else { + tabstrip_->SetBounds(0, 0, 0, 0); + tabstrip_->SetVisible(false); + if (is_off_the_record_) + off_the_record_image_->SetVisible(false); + } + + int toolbar_bottom; + if (IsToolBarVisible()) { + toolbar_->SetVisible(true); + toolbar_->SetBounds(g_bitmaps[CT_LEFT_SIDE]->width(), + tabstrip_->GetY() + tabstrip_->GetHeight() - + kToolbarOverlapVertOffset, + width - g_bitmaps[CT_LEFT_SIDE]->width() - + g_bitmaps[CT_RIGHT_SIDE]->width(), + g_bitmaps[CT_TOP_CENTER]->height()); + toolbar_->Layout(); + toolbar_bottom = toolbar_->GetY() + toolbar_->GetHeight(); + } else { + toolbar_->SetBounds(0, 0, 0, 0); + toolbar_->SetVisible(false); + toolbar_bottom = tabstrip_->GetY() + tabstrip_->GetHeight(); + } + int browser_x, browser_y; + int browser_w, browser_h; + + if (IsTabStripVisible()) { + browser_x = g_bitmaps[CT_LEFT_SIDE]->width(); + browser_y = toolbar_bottom; + browser_w = width - g_bitmaps[CT_LEFT_SIDE]->width() - + g_bitmaps[CT_RIGHT_SIDE]->width(); + browser_h = height - browser_y - g_bitmaps[CT_BOTTOM_CENTER]->height(); + } else { + browser_x = 0; + browser_y = toolbar_bottom; + browser_w = width; + browser_h = height; + } + + CSize preferred_size; + if (shelf_view_) { + shelf_view_->GetPreferredSize(&preferred_size); + shelf_view_->SetBounds(browser_x, + height - g_bitmaps[CT_BOTTOM_CENTER]->height() - + preferred_size.cy, + browser_w, + preferred_size.cy); + browser_h -= preferred_size.cy; + } + + CSize bookmark_bar_size; + CSize info_bar_size; + + if (bookmark_bar_view_) + bookmark_bar_view_->GetPreferredSize(&bookmark_bar_size); + + if (info_bar_view_) + info_bar_view_->GetPreferredSize(&info_bar_size); + + // If we're showing a bookmarks bar in the new tab page style and we + // have an infobar showing, we need to flip them. + if (info_bar_view_ && + bookmark_bar_view_ && + static_cast<BookmarkBarView*>(bookmark_bar_view_)->IsNewTabPage() && + !static_cast<BookmarkBarView*>(bookmark_bar_view_)->IsAlwaysShown()) { + info_bar_view_->SetBounds(browser_x, + browser_y, + browser_w, + info_bar_size.cy); + browser_h -= info_bar_size.cy; + + browser_y += info_bar_size.cy - kSeparationLineHeight; + + bookmark_bar_view_->SetBounds(browser_x, + browser_y, + browser_w, + bookmark_bar_size.cy); + browser_h -= bookmark_bar_size.cy - kSeparationLineHeight; + browser_y += bookmark_bar_size.cy; + } else { + if (bookmark_bar_view_) { + // We want our bookmarks bar to be responsible for drawing its own + // separator, so we let it overlap ours. + browser_y -= kSeparationLineHeight; + + bookmark_bar_view_->SetBounds(browser_x, + browser_y, + browser_w, + bookmark_bar_size.cy); + browser_h -= bookmark_bar_size.cy - kSeparationLineHeight; + browser_y += bookmark_bar_size.cy; + } + + if (info_bar_view_) { + info_bar_view_->SetBounds(browser_x, + browser_y, + browser_w, + info_bar_size.cy); + browser_h -= info_bar_size.cy; + browser_y += info_bar_size.cy; + } + } + + // While our OnNCCalcSize handler does a good job of covering most of the + // cases where we need to do this, it unfortunately doesn't cover the + // case where we're returning from maximized mode. + ResetDWMFrame(); + + tab_contents_container_->SetBounds(browser_x, + browser_y, + browser_w, + browser_h); + + status_bubble_->SetBounds(browser_x - kStatusBubbleOffset, + browser_y + browser_h - kStatusBubbleHeight + + kStatusBubbleOffset, + width / 3, + kStatusBubbleHeight); + + frame_view_->SchedulePaint(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ChromeFrame implementation +// +//////////////////////////////////////////////////////////////////////////////// + +void VistaFrame::Init() { + ChromeFrame::RegisterChromeFrame(this); + + // Link the HWND with its root view so we can retrieve the RootView from the + // HWND for automation purposes. + ChromeViews::SetRootViewForHWND(m_hWnd, &root_view_); + + frame_view_ = new VistaFrameView(this); + root_view_.AddChildView(frame_view_); + root_view_.SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); + frame_view_->SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); + + toolbar_ = browser_->GetToolbar(); + toolbar_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TOOLBAR)); + frame_view_->AddChildView(toolbar_); + + tabstrip_ = CreateTabStrip(browser_); + tabstrip_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TABSTRIP)); + frame_view_->AddChildView(tabstrip_); + + if (is_off_the_record_) { + ResourceBundle &rb = ResourceBundle::GetSharedInstance(); + off_the_record_image_ = new ChromeViews::ImageView(); + frame_view_->AddViewToDropList(off_the_record_image_); + SkBitmap* otr_icon = rb.GetBitmapNamed(IDR_OTR_ICON); + off_the_record_image_->SetImage(*otr_icon); + off_the_record_image_->SetTooltipText( + l10n_util::GetString(IDS_OFF_THE_RECORD_TOOLTIP)); + off_the_record_image_->SetVerticalAlignment( + ChromeViews::ImageView::LEADING); + frame_view_->AddChildView(off_the_record_image_); + } + + tab_contents_container_ = new TabContentsContainerView(); + frame_view_->AddChildView(tab_contents_container_); + + status_bubble_.reset(new StatusBubble(this)); + + // Add the task manager item to the system menu before the last entry. + task_manager_label_text_ = l10n_util::GetString(IDS_TASKMANAGER); + HMENU system_menu = ::GetSystemMenu(m_hWnd, FALSE); + int index = ::GetMenuItemCount(system_menu) - 1; + if (index < 0) { + NOTREACHED(); + // Paranoia check. + 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; + menu_info.fType = MFT_STRING; + menu_info.fState = MFS_ENABLED; + menu_info.wID = IDC_TASKMANAGER; + menu_info.dwTypeData = const_cast<wchar_t*>(task_manager_label_text_.c_str()); + ::InsertMenuItem(system_menu, index, TRUE, &menu_info); + + // Register accelerators. + HACCEL accelerators_table = AtlLoadAccelerators(IDR_MAINFRAME); + DCHECK(accelerators_table); + ChromeFrame::LoadAccelerators(this, accelerators_table, this); + + ShelfVisibilityChanged(); + root_view_.OnViewContainerCreated(); + Layout(); +} + +TabStrip* VistaFrame::CreateTabStrip(Browser* browser) { + return new TabStrip(browser->tabstrip_model()); +} + +void VistaFrame::Show(int command, bool adjust_to_fit) { + if (adjust_to_fit) + win_util::AdjustWindowToFit(*this); + ::ShowWindow(*this, command); +} + +void VistaFrame::BrowserDidPaint(HRGN region) { + browser_paint_pending_ = false; +} + +// This is called when we receive WM_ENDSESSION. In Vista the we have 5 seconds +// or will be forcefully terminated if we get stuck servicing this message and +// not pump the final messages. +void VistaFrame::OnEndSession(BOOL ending, UINT logoff) { + tabstrip_->AbortActiveDragSession(); + EndSession(); +} + +// Note: called directly by the handler macros to handle WM_CLOSE messages. +void VistaFrame::Close() { + // You cannot close a frame for which there is an active originating drag + // session. + if (tabstrip_->IsDragSessionActive()) + return; + + // Give beforeunload handlers the chance to cancel the close before we hide + // the window below. + if (!browser_->ShouldCloseWindow()) + return; + + // We call this here so that the window position gets saved before moving + // the window into hyperspace. + if (!saved_window_placement_ && should_save_window_placement_ && browser_) { + browser_->SaveWindowPlacement(); + browser_->SaveWindowPlacementToDatabase(); + saved_window_placement_ = true; + } + + if (browser_ && !browser_->tabstrip_model()->empty()) { + // Tab strip isn't empty. Hide the window (so it appears to have closed + // immediately) and close all the tabs, allowing the renderers to shut + // down. When the tab strip is empty we'll be called back recursively. + // NOTE: Don't use ShowWindow(SW_HIDE) here, otherwise end session blocks + // here. + SetWindowPos(NULL, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); + browser_->OnWindowClosing(); + } else { + // Empty tab strip, it's now safe to clean-up. + root_view_.OnViewContainerDestroyed(); + + NotificationService::current()->Notify( + NOTIFY_WINDOW_CLOSED, Source<HWND>(m_hWnd), + NotificationService::NoDetails()); + + DestroyWindow(); + } +} + +void* VistaFrame::GetPlatformID() { + return reinterpret_cast<void*>(m_hWnd); +} + +void VistaFrame::SetAcceleratorTable( + std::map<Accelerator, int>* accelerator_table) { + accelerator_table_.reset(accelerator_table); +} + +bool VistaFrame::GetAccelerator(int cmd_id, + ChromeViews::Accelerator* accelerator) { + std::map<ChromeViews::Accelerator, int>::iterator it = + accelerator_table_->begin(); + for (; it != accelerator_table_->end(); ++it) { + if(it->second == cmd_id) { + *accelerator = it->first; + return true; + } + } + return false; +} + +bool VistaFrame::AcceleratorPressed(const Accelerator& accelerator) { + DCHECK(accelerator_table_.get()); + std::map<Accelerator, int>::const_iterator iter = + accelerator_table_->find(accelerator); + DCHECK(iter != accelerator_table_->end()); + + int command_id = iter->second; + if (browser_->SupportsCommand(command_id) && + browser_->IsCommandEnabled(command_id)) { + browser_->ExecuteCommand(command_id); + return true; + } + return false; +} + +gfx::Rect VistaFrame::GetNormalBounds() { + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + const bool ret = !!GetWindowPlacement(&wp); + DCHECK(ret); + return gfx::Rect(wp.rcNormalPosition); +} + +bool VistaFrame::IsMaximized() { + return !!IsZoomed(); +} + +gfx::Rect VistaFrame::GetBoundsForContentBounds(const gfx::Rect content_rect) { + if (tab_contents_container_->GetX() == 0 && + tab_contents_container_->GetWidth() == 0) { + Layout(); + } + + CPoint p(0, 0); + ChromeViews::View::ConvertPointToViewContainer(tab_contents_container_, &p); + CRect bounds; + GetBounds(&bounds, true); + + gfx::Rect r; + r.set_x(content_rect.x() - p.x); + r.set_y(content_rect.y() - p.y); + r.set_width(p.x + content_rect.width() + + (bounds.Width() - (p.x + tab_contents_container_->GetWidth()))); + r.set_height(p.y + content_rect.height() + + (bounds.Height() - (p.y + + tab_contents_container_->GetHeight()))); + return r; +} + +void VistaFrame::SetBounds(const gfx::Rect& bounds) { + SetWindowPos(NULL, bounds.x(), bounds.y(), bounds.width(), bounds.height(), + SWP_NOZORDER | SWP_NOACTIVATE); +} + +void VistaFrame::DetachFromBrowser() { + browser_->tabstrip_model()->RemoveObserver(tabstrip_); + browser_ = NULL; +} + +void VistaFrame::InfoBubbleShowing() { + ignore_ncactivate_ = true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Events +// +//////////////////////////////////////////////////////////////////////////////// + +LRESULT VistaFrame::OnSettingChange(UINT u_msg, WPARAM w_param, LPARAM l_param, + BOOL& handled) { + // Set this to false, if we actually handle the message, we'll set it to + // true below. + handled = FALSE; + if (w_param != SPI_SETWORKAREA) + return 0; // Return value is effectively ignored in atlwin.h. + + win_util::AdjustWindowToFit(m_hWnd); + handled = TRUE; + return 0; // Return value is effectively ignored in atlwin.h. +} + +LRESULT VistaFrame::OnNCActivate(BOOL param) { + if (ignore_ncactivate_) { + ignore_ncactivate_ = false; + return DefWindowProc(WM_NCACTIVATE, TRUE, 0); + } + SetMsgHandled(false); + return 0; +} + +BOOL VistaFrame::OnPowerBroadcast(DWORD power_event, DWORD data) { + if (PBT_APMSUSPEND == power_event) { + SuspendController::OnSuspend(browser_->profile()); + return TRUE; + } else if (PBT_APMRESUMEAUTOMATIC == power_event) { + SuspendController::OnResume(browser_->profile()); + return TRUE; + } + + SetMsgHandled(FALSE); + return FALSE; +} + +void VistaFrame::OnThemeChanged() { + // Notify NativeTheme. + gfx::NativeTheme::instance()->CloseHandles(); + ChromeFrame::NotifyTabsOfThemeChange(browser_); +} + +void VistaFrame::OnMouseButtonDown(UINT flags, const CPoint& pt) { + if (m_hWnd == NULL) { + return; + } + + if (ProcessMousePressed(pt, flags, FALSE)) { + SetMsgHandled(TRUE); + } else { + SetMsgHandled(FALSE); + } +} + +void VistaFrame::OnMouseButtonUp(UINT flags, const CPoint& pt) { + if (m_hWnd == NULL) { + return; + } + + if (in_drag_session_) { + ProcessMouseReleased(pt, flags); + } +} + +void VistaFrame::OnMouseButtonDblClk(UINT flags, const CPoint& pt) { + if (ProcessMousePressed(pt, flags, TRUE)) { + SetMsgHandled(TRUE); + } else { + SetMsgHandled(FALSE); + } +} + +void VistaFrame::OnLButtonUp(UINT flags, const CPoint& pt) { + OnMouseButtonUp(flags | MK_LBUTTON, pt); +} + +void VistaFrame::OnMButtonUp(UINT flags, const CPoint& pt) { + OnMouseButtonUp(flags | MK_MBUTTON, pt); +} + +void VistaFrame::OnRButtonUp(UINT flags, const CPoint& pt) { + OnMouseButtonUp(flags | MK_RBUTTON, pt); +} + +void VistaFrame::OnNCMButtonDown(UINT flags, const CPoint& pt) { + // The point is in screen coordinate system so we need to convert + CRect window_rect; + GetWindowRect(&window_rect); + CPoint point(pt); + point.x -= window_rect.left; + point.y -= window_rect.top; + + // Yes we need to add MK_MBUTTON. Windows doesn't include it + OnMouseButtonDown(flags | MK_MBUTTON, point); +} + +void VistaFrame::OnNCRButtonDown(UINT flags, const CPoint& pt) { + if (flags == HTCAPTION) { + HMENU system_menu = ::GetSystemMenu(*this, FALSE); + int id = ::TrackPopupMenu(system_menu, + TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD, + pt.x, + pt.y, + 0, + *this, + NULL); + if (id) + ::SendMessage(*this, + WM_SYSCOMMAND, + id, + 0); + } else { + SetMsgHandled(FALSE); + } +} + +void VistaFrame::OnMouseMove(UINT flags, const CPoint& pt) { + if (m_hWnd == NULL) { + return; + } + + if (in_drag_session_) { + ProcessMouseDragged(pt, flags); + } else { + ArmOnMouseLeave(); + ProcessMouseMoved(pt, flags); + } +} + +void VistaFrame::OnMouseLeave() { + if (m_hWnd == NULL) { + return; + } + + ProcessMouseExited(); + on_mouse_leave_armed_ = false; +} + +LRESULT VistaFrame::OnGetObject(UINT uMsg, WPARAM w_param, LPARAM object_id) { + LRESULT reference_result = static_cast<LRESULT>(0L); + + // Accessibility readers will send an OBJID_CLIENT message + if (OBJID_CLIENT == object_id) { + // If our MSAA root is already created, reuse that pointer. Otherwise, + // create a new one. + if (!accessibility_root_) { + CComObject<ViewAccessibility>* instance = NULL; + + HRESULT hr = CComObject<ViewAccessibility>::CreateInstance(&instance); + DCHECK(SUCCEEDED(hr)); + + if (!instance) { + // Return with failure. + return static_cast<LRESULT>(0L); + } + + CComPtr<IAccessible> accessibility_instance(instance); + + if (!SUCCEEDED(instance->Initialize(&root_view_))) { + // Return with failure. + return static_cast<LRESULT>(0L); + } + + // All is well, assign the temp instance to the class smart pointer + accessibility_root_.Attach(accessibility_instance.Detach()); + + if (!accessibility_root_) { + // Return with failure. + return static_cast<LRESULT>(0L); + } + + // Notify that an instance of IAccessible was allocated for m_hWnd + ::NotifyWinEvent(EVENT_OBJECT_CREATE, m_hWnd, OBJID_CLIENT, + CHILDID_SELF); + } + + // Create a reference to ViewAccessibility that MSAA will marshall + // to the client. + reference_result = LresultFromObject(IID_IAccessible, w_param, + static_cast<IAccessible*>(accessibility_root_)); + } + return reference_result; +} + +void VistaFrame::OnKeyDown(TCHAR c, UINT rep_cnt, UINT flags) { + ChromeViews::KeyEvent event(ChromeViews::Event::ET_KEY_PRESSED, c, + rep_cnt, flags); + root_view_.ProcessKeyEvent(event); +} + +void VistaFrame::OnKeyUp(TCHAR c, UINT rep_cnt, UINT flags) { + ChromeViews::KeyEvent event(ChromeViews::Event::ET_KEY_RELEASED, c, + rep_cnt, flags); + root_view_.ProcessKeyEvent(event); +} + +LRESULT VistaFrame::OnAppCommand( + HWND w_param, short app_command, WORD device, int keystate) { + if (browser_ && !browser_->ExecuteWindowsAppCommand(app_command)) + SetMsgHandled(FALSE); + return 0; +} + +void VistaFrame::OnCommand(UINT notification_code, + int command_id, + HWND window) { + if (browser_ && browser_->SupportsCommand(command_id)) { + browser_->ExecuteCommand(command_id); + } else { + SetMsgHandled(FALSE); + } +} + +void VistaFrame::OnSysCommand(UINT notification_code, CPoint click) { + switch (notification_code) { + case IDC_TASKMANAGER: + if (browser_) + browser_->ExecuteCommand(IDC_TASKMANAGER); + break; + default: + // Use the default implementation for any other command. + SetMsgHandled(FALSE); + break; + } +} + +void VistaFrame::OnMove(const CSize& size) { + if (!saved_window_placement_ && should_save_window_placement_ ) + browser_->SaveWindowPlacementToDatabase(); + + browser_->WindowMoved(); +} + +void VistaFrame::OnMoving(UINT param, LPRECT new_bounds) { + // We want to let the browser know that the window moved so that it can + // update the positions of any dependent WS_POPUPs + browser_->WindowMoved(); +} + +void VistaFrame::OnSize(UINT param, const CSize& size) { + Layout(); + + if (root_view_.NeedsPainting(false)) { + PaintNow(root_view_.GetScheduledPaintRect()); + } + + if (!saved_window_placement_ && should_save_window_placement_) + browser_->SaveWindowPlacementToDatabase(); +} + +void VistaFrame::OnFinalMessage(HWND hwnd) { + delete this; +} + +void VistaFrame::OnNCLButtonDown(UINT flags, const CPoint& pt) { + SetMsgHandled(false); +} + +LRESULT VistaFrame::OnNCCalcSize(BOOL w_param, LPARAM l_param) { + // By default the client side is set to the window size which is what + // we want. + if (w_param == TRUE) { + // Calculate new NCCALCSIZE_PARAMS based on custom NCA inset. + NCCALCSIZE_PARAMS *pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param); + + // Hack necessary to stop black background flicker, we cut out + // resizeborder here to save us from having to do too much + // addition and subtraction in Layout(). We don't cut off the + // top + titlebar as that prevents the window controls from + // highlighting. + pncsp->rgrc[0].left = pncsp->rgrc[0].left + kResizeBorder; + pncsp->rgrc[0].right = pncsp->rgrc[0].right - kResizeBorder; + pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - kResizeBorder; + + // We need to reset the frame, as Vista resets it whenever it changes + // composition modes (and NCCALCSIZE is the closest thing we get to + // a reliable message about the change). + ResetDWMFrame(); + + SetMsgHandled(TRUE); + return 0; + } + + SetMsgHandled(FALSE); + return 0; +} + +LRESULT VistaFrame::OnNCHitTest(const CPoint& pt) { + SetMsgHandled(TRUE); + + // Test the caption buttons + LRESULT l_res; + BOOL dwm_processed = DwmDefWindowProc(m_hWnd, + WM_NCHITTEST, + 0, + (LPARAM)MAKELONG(pt.x, pt.y), + &l_res); + + if (dwm_processed) { + return l_res; + } + + CRect cr; + GetBounds(&cr, false); + + CPoint tab_pt(pt); + ChromeViews::View::ConvertPointToView(NULL, tabstrip_, &tab_pt); + + // If we are over the tabstrip + if (tab_pt.x > 0 && tab_pt.y >= kTabShadowSize && + tab_pt.x < tabstrip_->GetWidth() && + tab_pt.y < tabstrip_->GetHeight()) { + ChromeViews::View* v = tabstrip_->GetViewForPoint(tab_pt); + if (v == tabstrip_) + return HTCAPTION; + + // If the view under mouse is a tab, check if the tab strip allows tab + // dragging or not. If not, return caption to get window dragging. + if (v->GetClassName() == Tab::kTabClassName && + !tabstrip_->HasAvailableDragActions()) + return HTCAPTION; + + return HTCLIENT; + } + + CPoint p(pt); + CRect r; + GetWindowRect(&r); + + // Convert from screen to window coordinates + p.x -= r.left; + p.y -= r.top; + + if (p.x < kResizeBorder + g_bitmaps[CT_LEFT_SIDE]->width()) { + if (p.y < kResizeCornerSize) { + return HTTOPLEFT; + } else if (p.y >= (r.Height() - kResizeCornerSize)) { + return HTBOTTOMLEFT; + } else { + return HTLEFT; + } + // BOTTOM_LEFT / TOP_LEFT horizontal extensions + } else if (p.x < kResizeCornerSize) { + if (p.y < kResizeBorder) { + return HTTOPLEFT; + } else if (p.y >= (r.Height() - kResizeBorder)) { + return HTBOTTOMLEFT; + } + // EAST / BOTTOMRIGHT / TOPRIGHT edge + } else if (p.x >= (r.Width() - kResizeBorder - + g_bitmaps[CT_RIGHT_SIDE]->width())) { + if (p.y < kResizeCornerSize) { + return HTTOPRIGHT; + } else if (p.y >= (r.Height() - kResizeCornerSize)) { + return HTBOTTOMRIGHT; + } else { + return HTRIGHT; + } + // EAST / BOTTOMRIGHT / TOPRIGHT horizontal extensions + } else if (p.x >= (r.Width() - kResizeCornerSize)) { + if (p.y < kResizeBorder) { + return HTTOPRIGHT; + } else if (p.y >= (r.Height() - kResizeBorder)) { + return HTBOTTOMRIGHT; + } + // TOP edge + } else if (p.y < kResizeBorder) { + return HTTOP; + // BOTTOM edge + } else if (p.y >= (r.Height() - kResizeBorder - + g_bitmaps[CT_BOTTOM_CENTER]->height())) { + return HTBOTTOM; + } + + if (p.y <= tabstrip_->GetY() + tabstrip_->GetHeight()) { + return HTCAPTION; + } + + SetMsgHandled(FALSE); + return 0; +} + +void VistaFrame::OnActivate(UINT n_state, BOOL is_minimized, HWND other) { + if (ActivateAppModalDialog(browser_)) + return; + + // Enable our custom window if we haven't already (this works in combination + // with our NCCALCSIZE handler). + if (!custom_window_enabled_) { + RECT rcClient; + ::GetWindowRect(m_hWnd, &rcClient); + ::SetWindowPos( + m_hWnd, + NULL, + rcClient.left, rcClient.top, + rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, + SWP_FRAMECHANGED); + custom_window_enabled_ = true; + + // We need to fire this here as well as in OnNCCalcSize, as that function + // does not fire at the right time for this to work when opening the + // window. + ResetDWMFrame(); + } + + SetMsgHandled(FALSE); + if (!is_minimized) + browser_->WindowActivationChanged(n_state != WA_INACTIVE); +} + +int VistaFrame::OnMouseActivate(CWindow wndTopLevel, UINT nHitTest, + UINT message) { + return ActivateAppModalDialog(browser_) ? MA_NOACTIVATEANDEAT : MA_ACTIVATE; +} + +void VistaFrame::OnPaint(HDC dc) { + // Warning: on Vista the ChromeCanvasPaint *must* use an opaque flag of true + // so that ChromeCanvasPaint performs a BitBlt and not an alpha blend. + // + root_view_.OnPaint(*this); +} + +LRESULT VistaFrame::OnEraseBkgnd(HDC dc) { + SetMsgHandled(TRUE); + return 0; +} + +void VistaFrame::ArmOnMouseLeave() { + if (!on_mouse_leave_armed_) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = *this; + tme.dwHoverTime = 0; + TrackMouseEvent(&tme); + on_mouse_leave_armed_ = TRUE; + } +} + +void VistaFrame::OnCaptureChanged(HWND hwnd) { + if (in_drag_session_) + root_view_.ProcessMouseDragCanceled(); + in_drag_session_ = false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// View events propagation +// +//////////////////////////////////////////////////////////////////////////////// + +bool VistaFrame::ProcessMousePressed(const CPoint& pt, UINT flags, + bool dbl_click) { + using namespace ChromeViews; + MouseEvent mouse_pressed(Event::ET_MOUSE_PRESSED, + pt.x, + pt.y, + (dbl_click ? MouseEvent::EF_IS_DOUBLE_CLICK : 0) | + Event::ConvertWindowsFlags(flags)); + if (root_view_.OnMousePressed(mouse_pressed)) { + // If an additional button is pressed during a drag session we don't want + // to call SetCapture() again as it will result in no more events. + if (!in_drag_session_) { + in_drag_session_ = true; + SetCapture(); + } + return TRUE; + } + return FALSE; +} + +void VistaFrame::ProcessMouseDragged(const CPoint& pt, UINT flags) { + using namespace ChromeViews; + MouseEvent drag_event(Event::ET_MOUSE_DRAGGED, + pt.x, + pt.y, + Event::ConvertWindowsFlags(flags)); + root_view_.OnMouseDragged(drag_event); +} + +void VistaFrame::ProcessMouseReleased(const CPoint& pt, UINT flags) { + using namespace ChromeViews; + if (in_drag_session_) { + in_drag_session_ = false; + ReleaseCapture(); + } + MouseEvent mouse_released(Event::ET_MOUSE_RELEASED, + pt.x, + pt.y, + Event::ConvertWindowsFlags(flags)); + root_view_.OnMouseReleased(mouse_released, false); +} + +void VistaFrame::ProcessMouseMoved(const CPoint &pt, UINT flags) { + using namespace ChromeViews; + MouseEvent mouse_move(Event::ET_MOUSE_MOVED, + pt.x, + pt.y, + Event::ConvertWindowsFlags(flags)); + root_view_.OnMouseMoved(mouse_move); +} + +void VistaFrame::ProcessMouseExited() { + root_view_.ProcessOnMouseExited(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ChromeViews::ViewContainer +// +//////////////////////////////////////////////////////////////////////////////// + +void VistaFrame::GetBounds(CRect *out, bool including_frame) const { + if (including_frame) { + GetWindowRect(out); + } else { + GetClientRect(out); + + POINT p = {0, 0}; + ::ClientToScreen(*this, &p); + + out->left += p.x; + out->top += p.y; + out->right += p.x; + out->bottom += p.y; + } +} + +void VistaFrame::MoveToFront(bool should_activate) { + int flags = SWP_NOMOVE | SWP_NOSIZE; + if (!should_activate) { + flags |= SWP_NOACTIVATE; + } + SetWindowPos(HWND_TOP, 0, 0, 0, 0, flags); + SetForegroundWindow(*this); +} + +HWND VistaFrame::GetHWND() const { + return m_hWnd; +} + +void VistaFrame::PaintNow(const CRect& update_rect) { + if (!update_rect.IsRectNull() && IsVisible()) { + RedrawWindow(update_rect, + NULL, + RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_NOERASE); + } +} + +ChromeViews::RootView* VistaFrame::GetRootView() { + return const_cast<ChromeViews::RootView*>(&root_view_); +} + +bool VistaFrame::IsVisible() { + return !!::IsWindowVisible(*this); +} + +bool VistaFrame::IsActive() { + return win_util::IsWindowActive(*this); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// VistaFrameView +// +//////////////////////////////////////////////////////////////////////////////// +void VistaFrame::VistaFrameView::PaintContentsBorder(ChromeCanvas* canvas, + int x, + int y, + int w, + int h) { + if (!parent_->IsTabStripVisible()) + return; + int ro, bo; + canvas->DrawBitmapInt(*g_bitmaps[CT_TOP_LEFT_CORNER], x, y); + canvas->TileImageInt(*g_bitmaps[CT_TOP_CENTER], + x + g_bitmaps[CT_TOP_LEFT_CORNER]->width(), + y, + w - g_bitmaps[CT_TOP_LEFT_CORNER]->width() - + g_bitmaps[CT_TOP_RIGHT_CORNER]->width(), + g_bitmaps[CT_TOP_CENTER]->height()); + ro = x + w - g_bitmaps[CT_TOP_RIGHT_CORNER]->width(); + canvas->DrawBitmapInt(*g_bitmaps[CT_TOP_RIGHT_CORNER], ro, y); + canvas->TileImageInt(*g_bitmaps[CT_RIGHT_SIDE], + ro, + y + g_bitmaps[CT_TOP_RIGHT_CORNER]->height(), + g_bitmaps[CT_RIGHT_SIDE]->width(), + h - (g_bitmaps[CT_TOP_RIGHT_CORNER]->height() + + g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->height())); + bo = y + h - g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->height(); + canvas->DrawBitmapInt(*g_bitmaps[CT_BOTTOM_RIGHT_CORNER], + x + w - g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->width(), + bo); + + canvas->TileImageInt(*g_bitmaps[CT_BOTTOM_CENTER], + x + g_bitmaps[CT_BOTTOM_LEFT_CORNER]->width(), + bo, + w - (g_bitmaps[CT_BOTTOM_LEFT_CORNER]->width() + + g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->width()), + g_bitmaps[CT_BOTTOM_CENTER]->height()); + canvas->DrawBitmapInt(*g_bitmaps[CT_BOTTOM_LEFT_CORNER], x, bo); + canvas->TileImageInt(*g_bitmaps[CT_LEFT_SIDE], + x, + y + g_bitmaps[CT_TOP_LEFT_CORNER]->height(), + g_bitmaps[CT_LEFT_SIDE]->width(), + h - (g_bitmaps[CT_TOP_LEFT_CORNER]->height() + + g_bitmaps[CT_BOTTOM_LEFT_CORNER]->height())); +} + +void VistaFrame::VistaFrameView::Paint(ChromeCanvas* canvas) { + canvas->save(); + + // When painting the border, exclude the contents area. This will prevent the + // border bitmaps (which might be larger than the visible area) from coming + // into the content area when there is no tab painted yet. + int x = parent_->tab_contents_container_->GetX(); + int y = parent_->tab_contents_container_->GetY(); + SkRect clip; + clip.set(SkIntToScalar(x), SkIntToScalar(y), + SkIntToScalar(x + parent_->tab_contents_container_->GetWidth()), + SkIntToScalar(y + parent_->tab_contents_container_->GetHeight())); + canvas->clipRect(clip, SkRegion::kDifference_Op); + + PaintContentsBorder(canvas, + 0, + contents_offset_, + GetWidth(), + GetHeight() - contents_offset_); + + canvas->restore(); +} + +void VistaFrame::VistaFrameView::SetContentsOffset(int o) { + contents_offset_ = o; +} + +bool VistaFrame::VistaFrameView::GetAccessibleRole(VARIANT* role) { + DCHECK(role); + + role->vt = VT_I4; + role->lVal = ROLE_SYSTEM_CLIENT; + return true; +} + +bool VistaFrame::VistaFrameView::GetAccessibleName(std::wstring* name) { + if (!accessible_name_.empty()) { + name->assign(accessible_name_); + return true; + } + return false; +} + +void VistaFrame::VistaFrameView::SetAccessibleName(const std::wstring& name) { + accessible_name_.assign(name); +} + +// Helper function to extract the min/max x-coordinate as well as the max +// y coordinate from the TITLEBARINFOEX struct at the specified index. +static void UpdatePosition(const TITLEBARINFOEX& info, + int element, + int* min_x, + int* max_x, + int* max_y) { + if ((info.rgstate[element] & (STATE_SYSTEM_INVISIBLE | + STATE_SYSTEM_OFFSCREEN | + STATE_SYSTEM_UNAVAILABLE)) == 0) { + *min_x = std::min(*min_x, static_cast<int>(info.rgrect[element].left)); + *max_x = std::max(*max_x, static_cast<int>(info.rgrect[element].right)); + *max_y = std::max(*max_y, static_cast<int>(info.rgrect[element].bottom)); + } +} + +bool VistaFrame::VistaFrameView::ShouldForwardToTabStrip( + const ChromeViews::DropTargetEvent& event) { + if (!FrameView::ShouldForwardToTabStrip(event)) + return false; + + TITLEBARINFOEX titlebar_info; + titlebar_info.cbSize = sizeof(TITLEBARINFOEX); + SendMessage(parent_->m_hWnd, WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info); + + int min_x = std::numeric_limits<int>::max(); + int max_x = std::numeric_limits<int>::min(); + int max_y = std::numeric_limits<int>::min(); + + // 2 is the minimize button. + UpdatePosition(titlebar_info, 2, &min_x, &max_x, &max_y); + // 3 is the maximize button. + UpdatePosition(titlebar_info, 3, &min_x, &max_x, &max_y); + // 5 is the close button. + UpdatePosition(titlebar_info, 5, &min_x, &max_x, &max_y); + + if (min_x != std::numeric_limits<int>::max() && + max_x != std::numeric_limits<int>::min() && + max_y != std::numeric_limits<int>::min()) { + CPoint screen_drag_point(event.GetX(), event.GetY()); + ConvertPointToScreen(this, &screen_drag_point); + if (screen_drag_point.x >= min_x && screen_drag_point.x <= max_x && + screen_drag_point.y <= max_y) { + return false; + } + } + return true; +} + +LRESULT VistaFrame::OnMouseRange(UINT u_msg, WPARAM w_param, LPARAM l_param, + BOOL& handled) { + // Tooltip handling is broken in Vista when using custom frames, so + // we have to implement a lot of this ourselves. + tooltip_manager_->OnMouse(u_msg, w_param, l_param); + handled = FALSE; + + return 0; +} + +LRESULT VistaFrame::OnNotify(int w_param, NMHDR* l_param) { + bool handled; + LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled); + SetMsgHandled(handled); + return result; +} + +ChromeViews::TooltipManager* VistaFrame::GetTooltipManager() { + return tooltip_manager_.get(); +} + +StatusBubble* VistaFrame::GetStatusBubble() { + return status_bubble_.get(); +} + +void VistaFrame::InitAfterHWNDCreated() { + tooltip_manager_.reset(new ChromeViews::AeroTooltipManager(this, m_hWnd)); +} + +void VistaFrame::ResetDWMFrame() { + if (IsTabStripVisible()) { + // Note: we don't use DwmEnableBlurBehindWindow because any region not + // included in the glass region is composited source over. This means + // that anything drawn directly with GDI appears fully transparent. + // + // We want this region to extend past our content border images, as they + // may be partially-transparent. + MARGINS margins = {kDwmBorderSize + + g_bitmaps[CT_TOP_LEFT_CORNER]->width(), + kDwmBorderSize + + g_bitmaps[CT_TOP_RIGHT_CORNER]->width(), + kDwmBorderSize + + IsToolBarVisible() ? + toolbar_->GetY() + kToolbarOverlapVertOffset : + tabstrip_->GetHeight(), + kDwmBorderSize + + g_bitmaps[CT_BOTTOM_CENTER]->height()}; + + DwmExtendFrameIntoClientArea(*this, &margins); + } +} + +void VistaFrame::ShelfVisibilityChanged() { + ShelfVisibilityChangedImpl(browser_->GetSelectedTabContents()); +} + +void VistaFrame::SelectedTabToolbarSizeChanged(bool is_animating) { + if (is_animating) { + tab_contents_container_->set_fast_resize(true); + ShelfVisibilityChanged(); + tab_contents_container_->set_fast_resize(false); + } else { + ShelfVisibilityChanged(); + tab_contents_container_->UpdateHWNDBounds(); + } +} + +bool VistaFrame::UpdateChildViewAndLayout(ChromeViews::View* new_view, + ChromeViews::View** view) { + DCHECK(view); + if (*view == new_view) { + // The views haven't changed, if the views pref changed schedule a layout. + if (new_view) { + CSize pref_size; + new_view->GetPreferredSize(&pref_size); + if (pref_size.cy != new_view->GetHeight()) + return true; + } + return false; + } + + // The views differ, and one may be null (but not both). Remove the old + // view (if it non-null), and add the new one (if it is non-null). If the + // height has changed, schedule a layout, otherwise reuse the existing + // bounds to avoid scheduling a layout. + + int current_height = 0; + if (*view) { + current_height = (*view)->GetHeight(); + root_view_.RemoveChildView(*view); + } + + int new_height = 0; + if (new_view) { + CSize preferred_size; + new_view->GetPreferredSize(&preferred_size); + new_height = preferred_size.cy; + root_view_.AddChildView(new_view); + } + + bool changed = false; + if (new_height != current_height) { + changed = true; + } else if (new_view && *view) { + // The view changed, but the new view wants the same size, give it the + // bounds of the last view and have it repaint. + CRect last_bounds; + (*view)->GetBounds(&last_bounds); + new_view->SetBounds(last_bounds.left, last_bounds.top, + last_bounds.Width(), last_bounds.Height()); + new_view->SchedulePaint(); + } else if (new_view) { + DCHECK(new_height == 0); + // The heights are the same, but the old view is null. This only happens + // when the height is zero. Zero out the bounds. + new_view->SetBounds(0, 0, 0, 0); + } + *view = new_view; + return changed; +} + +void VistaFrame::SetWindowTitle(const std::wstring& title) { + SetWindowText(title.c_str()); +} + +void VistaFrame::Activate() { + if (IsIconic()) + ShowWindow(SW_RESTORE); + MoveToFront(true); +} + +void VistaFrame::FlashFrame() { + FLASHWINFO flash_window_info; + flash_window_info.cbSize = sizeof(FLASHWINFO); + flash_window_info.hwnd = GetHWND(); + flash_window_info.dwFlags = FLASHW_ALL; + flash_window_info.uCount = 4; + flash_window_info.dwTimeout = 0; + ::FlashWindowEx(&flash_window_info); +} + +void VistaFrame::ShowTabContents(TabContents* selected_contents) { + tab_contents_container_->SetTabContents(selected_contents); + + // Force a LoadingStateChanged notification because the TabContents + // could be loading (such as when the user unconstrains a tab. + if (selected_contents && selected_contents->delegate()) + selected_contents->delegate()->LoadingStateChanged(selected_contents); + + ShelfVisibilityChangedImpl(selected_contents); +} + +TabStrip* VistaFrame::GetTabStrip() const { + return tabstrip_; +} + +void VistaFrame::ContinueDetachConstrainedWindowDrag(const gfx::Point& mouse_pt, + int frame_component) { + // Need to force a paint at this point so that the newly created window looks + // correct. (Otherwise parts of the tabstrip are clipped). + CRect cr; + GetClientRect(&cr); + PaintNow(cr); + + // The user's mouse is already moving, and the left button is down, but we + // need to start moving this frame, so we _post_ it a NCLBUTTONDOWN message + // with the HTCAPTION flag to trick windows into believing the user just + // started dragging on the title bar. All the frame moving is then handled + // automatically by windows. Note that we use PostMessage here since we need + // to return to the message loop first otherwise Windows' built in move code + // will not be able to be triggered. + POINTS pts; + pts.x = mouse_pt.x(); + pts.y = mouse_pt.y(); + PostMessage(WM_NCLBUTTONDOWN, frame_component, + reinterpret_cast<LPARAM>(&pts)); +} + +void VistaFrame::SizeToContents(const gfx::Rect& contents_bounds) { + // First we need to ensure everything has an initial size. Currently, the + // window has the wrong size, but that's OK, doing this will allow us to + // figure out how big all the UI bits are. + Layout(); + + // Now figure out the height of the non-client area (the Native windows + // chrome) by comparing the window bounds to the client bounds. + CRect window_bounds, client_bounds; + GetBounds(&window_bounds, true); + GetBounds(&client_bounds, false); + int left_edge_width = client_bounds.left - window_bounds.left; + int top_edge_height = client_bounds.top - window_bounds.top; + int right_edge_width = window_bounds.right - client_bounds.right; + int bottom_edge_height = window_bounds.bottom - client_bounds.bottom; + + // Now resize the window. This will result in Layout() getting called again + // and the contents getting sized to the value specified in |contents_bounds| + SetWindowPos(NULL, contents_bounds.x() - left_edge_width, + contents_bounds.y() - top_edge_height, + contents_bounds.width() + left_edge_width + right_edge_width, + contents_bounds.height() + top_edge_height + bottom_edge_height, + SWP_NOZORDER | SWP_NOACTIVATE); +} + +void VistaFrame::SetIsOffTheRecord(bool value) { + is_off_the_record_ = value; +} + +TabContentsContainerView* VistaFrame::GetTabContentsContainer() { + return tab_contents_container_; +} + +void VistaFrame::DestroyBrowser() { + // TODO(beng): (Cleanup) tidy this up, just fixing the build red for now. + // Need to do this first, before the browser_ is deleted and we can't remove + // the tabstrip from the model's observer list because the model was + // destroyed with browser_. + if (browser_) { + browser_->tabstrip_model()->RemoveObserver(tabstrip_); + delete browser_; + browser_ = NULL; + } +} + +void VistaFrame::ShelfVisibilityChangedImpl(TabContents* current_tab) { + // Coalesce layouts. + bool changed = false; + + ChromeViews::View* new_shelf = NULL; + if (current_tab && current_tab->IsDownloadShelfVisible()) + new_shelf = current_tab->GetDownloadShelfView(); + changed |= UpdateChildViewAndLayout(new_shelf, &shelf_view_); + + ChromeViews::View* new_info_bar = NULL; + if (current_tab && current_tab->IsInfoBarVisible()) + new_info_bar = current_tab->GetInfoBarView(); + changed |= UpdateChildViewAndLayout(new_info_bar, &info_bar_view_); + + ChromeViews::View* new_bookmark_bar_view = NULL; + if (SupportsBookmarkBar() && current_tab) + new_bookmark_bar_view = browser_->GetBookmarkBarView(); + changed |= UpdateChildViewAndLayout(new_bookmark_bar_view, + &bookmark_bar_view_); + + // Only do a layout if the current contents is non-null. We assume that if the + // contents is NULL, we're either being destroyed, or ShowTabContents is going + // to be invoked with a non-null TabContents again so that there is no need + // in doing a layout now (and would result in extra work/invalidation on + // tab switches). + if (changed && current_tab) + Layout(); +} |