summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views')
-rw-r--r--chrome/browser/views/about_chrome_view.cc490
-rw-r--r--chrome/browser/views/about_chrome_view.h135
-rw-r--r--chrome/browser/views/bookmark_bar_view.cc2204
-rw-r--r--chrome/browser/views/bookmark_bar_view.h424
-rw-r--r--chrome/browser/views/bookmark_bubble_view.cc390
-rw-r--r--chrome/browser/views/bookmark_bubble_view.h187
-rw-r--r--chrome/browser/views/bookmark_editor_view.cc561
-rw-r--r--chrome/browser/views/bookmark_editor_view.h269
-rw-r--r--chrome/browser/views/bug_report_view.cc510
-rw-r--r--chrome/browser/views/bug_report_view.h154
-rw-r--r--chrome/browser/views/clear_browsing_data.cc403
-rw-r--r--chrome/browser/views/clear_browsing_data.h135
-rw-r--r--chrome/browser/views/constrained_window_animation.cc55
-rw-r--r--chrome/browser/views/constrained_window_animation.h54
-rw-r--r--chrome/browser/views/constrained_window_impl.cc1493
-rw-r--r--chrome/browser/views/constrained_window_impl.h214
-rw-r--r--chrome/browser/views/constrained_window_impl_interactive_uitest.cc112
-rw-r--r--chrome/browser/views/delay_view.cc114
-rw-r--r--chrome/browser/views/delay_view.h73
-rw-r--r--chrome/browser/views/dom_view.h92
-rw-r--r--chrome/browser/views/download_item_view.cc585
-rw-r--r--chrome/browser/views/download_item_view.h222
-rw-r--r--chrome/browser/views/download_shelf_view.cc300
-rw-r--r--chrome/browser/views/download_shelf_view.h139
-rw-r--r--chrome/browser/views/download_started_animation.cc131
-rw-r--r--chrome/browser/views/download_started_animation.h85
-rw-r--r--chrome/browser/views/edit_keyword_controller.cc392
-rw-r--r--chrome/browser/views/edit_keyword_controller.h161
-rw-r--r--chrome/browser/views/event_utils.cc56
-rw-r--r--chrome/browser/views/event_utils.h52
-rw-r--r--chrome/browser/views/first_run_bubble.cc241
-rw-r--r--chrome/browser/views/first_run_bubble.h72
-rw-r--r--chrome/browser/views/first_run_customize_view.cc253
-rw-r--r--chrome/browser/views/first_run_customize_view.h120
-rw-r--r--chrome/browser/views/first_run_view.cc233
-rw-r--r--chrome/browser/views/first_run_view.h94
-rw-r--r--chrome/browser/views/first_run_view_base.cc204
-rw-r--r--chrome/browser/views/first_run_view_base.h115
-rw-r--r--chrome/browser/views/go_button.cc158
-rw-r--r--chrome/browser/views/go_button.h89
-rw-r--r--chrome/browser/views/html_dialog_view.cc201
-rw-r--r--chrome/browser/views/html_dialog_view.h122
-rw-r--r--chrome/browser/views/hung_renderer_view.cc475
-rw-r--r--chrome/browser/views/hung_renderer_view.h56
-rw-r--r--chrome/browser/views/importer_lock_view.cc99
-rw-r--r--chrome/browser/views/importer_lock_view.h78
-rw-r--r--chrome/browser/views/importer_view.cc199
-rw-r--r--chrome/browser/views/importer_view.h105
-rw-r--r--chrome/browser/views/importing_progress_view.cc306
-rw-r--r--chrome/browser/views/importing_progress_view.h114
-rw-r--r--chrome/browser/views/info_bar_alternate_nav_url_view.cc94
-rw-r--r--chrome/browser/views/info_bar_alternate_nav_url_view.h51
-rw-r--r--chrome/browser/views/info_bar_confirm_view.cc124
-rw-r--r--chrome/browser/views/info_bar_confirm_view.h89
-rw-r--r--chrome/browser/views/info_bar_item_view.cc317
-rw-r--r--chrome/browser/views/info_bar_item_view.h163
-rw-r--r--chrome/browser/views/info_bar_message_view.cc67
-rw-r--r--chrome/browser/views/info_bar_message_view.h63
-rw-r--r--chrome/browser/views/info_bar_view.cc245
-rw-r--r--chrome/browser/views/info_bar_view.h111
-rw-r--r--chrome/browser/views/info_bubble.cc445
-rw-r--r--chrome/browser/views/info_bubble.h193
-rw-r--r--chrome/browser/views/input_window.cc194
-rw-r--r--chrome/browser/views/input_window.h68
-rw-r--r--chrome/browser/views/keyword_editor_view.cc694
-rw-r--r--chrome/browser/views/keyword_editor_view.h227
-rw-r--r--chrome/browser/views/location_bar_view.cc1048
-rw-r--r--chrome/browser/views/location_bar_view.h424
-rw-r--r--chrome/browser/views/login_view.cc149
-rw-r--r--chrome/browser/views/login_view.h108
-rw-r--r--chrome/browser/views/options/advanced_contents_view.cc1203
-rw-r--r--chrome/browser/views/options/advanced_contents_view.h63
-rw-r--r--chrome/browser/views/options/advanced_page_view.cc210
-rw-r--r--chrome/browser/views/options/advanced_page_view.h67
-rw-r--r--chrome/browser/views/options/content_page_view.cc464
-rw-r--r--chrome/browser/views/options/content_page_view.h114
-rw-r--r--chrome/browser/views/options/cookies_view.cc791
-rw-r--r--chrome/browser/views/options/cookies_view.h136
-rw-r--r--chrome/browser/views/options/fonts_languages_window_view.cc115
-rw-r--r--chrome/browser/views/options/fonts_languages_window_view.h98
-rw-r--r--chrome/browser/views/options/fonts_page_view.cc509
-rw-r--r--chrome/browser/views/options/fonts_page_view.h148
-rw-r--r--chrome/browser/views/options/general_page_view.cc1094
-rw-r--r--chrome/browser/views/options/general_page_view.h178
-rw-r--r--chrome/browser/views/options/language_combobox_model.cc190
-rw-r--r--chrome/browser/views/options/language_combobox_model.h95
-rw-r--r--chrome/browser/views/options/languages_page_view.cc778
-rw-r--r--chrome/browser/views/options/languages_page_view.h123
-rw-r--r--chrome/browser/views/options/options_group_view.cc161
-rw-r--r--chrome/browser/views/options/options_group_view.h100
-rw-r--r--chrome/browser/views/options/options_page_view.cc82
-rw-r--r--chrome/browser/views/options/options_page_view.h104
-rw-r--r--chrome/browser/views/options/options_window_view.cc254
-rw-r--r--chrome/browser/views/password_manager_view.cc399
-rw-r--r--chrome/browser/views/password_manager_view.h170
-rw-r--r--chrome/browser/views/restart_message_box.cc84
-rw-r--r--chrome/browser/views/restart_message_box.h60
-rw-r--r--chrome/browser/views/sad_tab_view.cc137
-rw-r--r--chrome/browser/views/sad_tab_view.h81
-rw-r--r--chrome/browser/views/shelf_item_dialog.cc511
-rw-r--r--chrome/browser/views/shelf_item_dialog.h155
-rw-r--r--chrome/browser/views/shell_dialogs.cc551
-rw-r--r--chrome/browser/views/star_toggle.cc105
-rw-r--r--chrome/browser/views/star_toggle.h92
-rw-r--r--chrome/browser/views/status_bubble.cc671
-rw-r--r--chrome/browser/views/status_bubble.h110
-rw-r--r--chrome/browser/views/tab_icon_view.cc141
-rw-r--r--chrome/browser/views/tab_icon_view.h89
-rw-r--r--chrome/browser/views/theme_helpers.cc115
-rw-r--r--chrome/browser/views/theme_helpers.h53
-rw-r--r--chrome/browser/views/toolbar_star_toggle.cc116
-rw-r--r--chrome/browser/views/toolbar_star_toggle.h91
-rw-r--r--chrome/browser/views/toolbar_view.cc675
-rw-r--r--chrome/browser/views/toolbar_view.h197
114 files changed, 30080 insertions, 0 deletions
diff --git a/chrome/browser/views/about_chrome_view.cc b/chrome/browser/views/about_chrome_view.cc
new file mode 100644
index 0000000..157ccc6
--- /dev/null
+++ b/chrome/browser/views/about_chrome_view.cc
@@ -0,0 +1,490 @@
+// 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/views/about_chrome_view.h"
+
+#include <commdlg.h>
+
+#include "base/file_version_info.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/common/gfx/color_utils.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/browser/views/restart_message_box.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/throbber.h"
+#include "chrome/views/window.h"
+#include "webkit/glue/webkit_glue.h"
+
+#include "generated_resources.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// AboutChromeView, public:
+
+AboutChromeView::AboutChromeView(Profile* profile)
+ : dialog_(NULL),
+ profile_(profile),
+ about_dlg_background_(NULL),
+ about_title_label_(NULL),
+ version_label_(NULL),
+ main_text_label_(NULL),
+ check_button_status_(CHECKBUTTON_HIDDEN),
+ google_updater_(NULL) {
+ DCHECK(profile);
+ Init();
+
+ google_updater_ = new GoogleUpdate(); // This object deletes itself.
+ google_updater_->AddStatusChangeListener(this);
+}
+
+AboutChromeView::~AboutChromeView() {
+ // The Google Updater will hold a pointer to us until it reports status, so we
+ // need to let it know that we will no longer be listening.
+ if (google_updater_)
+ google_updater_->RemoveStatusChangeListener();
+}
+
+void AboutChromeView::Init() {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule());
+ if (version_info.get() == NULL) {
+ NOTREACHED() << L"Failed to initialize about window";
+ return;
+ }
+
+ current_version_ = version_info->file_version();
+
+ std::wstring official;
+ if (version_info->is_official_build()) {
+ official = l10n_util::GetString(IDS_ABOUT_VERSION_OFFICIAL);
+ } else {
+ official = l10n_util::GetString(IDS_ABOUT_VERSION_UNOFFICIAL);
+ }
+
+ // Views we will add to the *parent* of this dialog, since it will display
+ // next to the buttons which we don't draw ourselves.
+ throbber_.reset(new ChromeViews::Throbber(50, true));
+ throbber_->SetParentOwned(false);
+ throbber_->SetVisible(false);
+
+ SkBitmap* success_image = rb.GetBitmapNamed(IDR_UPDATE_UPTODATE);
+ success_indicator_.SetImage(*success_image);
+ success_indicator_.SetParentOwned(false);
+
+ SkBitmap* update_available_image = rb.GetBitmapNamed(IDR_UPDATE_AVAILABLE);
+ update_available_indicator_.SetImage(*update_available_image);
+ update_available_indicator_.SetParentOwned(false);
+
+ SkBitmap* timeout_image = rb.GetBitmapNamed(IDR_UPDATE_FAIL);
+ timeout_indicator_.SetImage(*timeout_image);
+ timeout_indicator_.SetParentOwned(false);
+
+ update_label_.SetVisible(false);
+ update_label_.SetParentOwned(false);
+
+ // Regular view controls we draw by ourself. First, we add the background
+ // image for the dialog. We have two different background bitmaps, one for
+ // LTR UIs and one for RTL UIs. We load the correct bitmap based on the UI
+ // layout of the view.
+ about_dlg_background_ = new ChromeViews::ImageView();
+ SkBitmap* about_background;
+ if (UILayoutIsRightToLeft())
+ about_background = rb.GetBitmapNamed(IDR_ABOUT_BACKGROUND_RTL);
+ else
+ about_background = rb.GetBitmapNamed(IDR_ABOUT_BACKGROUND);
+
+ about_dlg_background_->SetImage(*about_background);
+ AddChildView(about_dlg_background_);
+
+ // Add the dialog labels.
+ about_title_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_PRODUCT_NAME));
+ about_title_label_->SetFont(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont).DeriveFont(18, BOLD_FONTTYPE));
+ AddChildView(about_title_label_);
+
+ // This is a text field so people can copy the version number from the dialog.
+ version_label_ = new ChromeViews::TextField();
+ version_label_->SetText(current_version_);
+ version_label_->SetReadOnly(true);
+ version_label_->RemoveBorder();
+ version_label_->SetFont(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont).DeriveFont(0, BOLD_FONTTYPE));
+ version_label_->set_default_width_in_chars(
+ static_cast<int>(current_version_.size() + 1));
+ AddChildView(version_label_);
+
+ // Text to display at the bottom of the dialog.
+ std::wstring main_text =
+ l10n_util::GetString(IDS_ABOUT_VERSION_COMPANY_NAME) + L"\n" +
+ l10n_util::GetString(IDS_ABOUT_VERSION_COPYRIGHT) + L"\n" +
+ l10n_util::GetStringF(IDS_ABOUT_VERSION_LICENSE,
+ l10n_util::GetString(IDS_ABOUT_VERSION_LICENSE_URL)) + L"\n\n" +
+ official + L" " + version_info->last_change() + L"\n" +
+ UTF8ToWide(webkit_glue::GetDefaultUserAgent());
+
+ main_text_label_ =
+ new ChromeViews::TextField(ChromeViews::TextField::STYLE_MULTILINE);
+ main_text_label_->SetText(main_text);
+ main_text_label_->SetReadOnly(true);
+ main_text_label_->RemoveBorder();
+ // Background color for the main label TextField.
+ SkColor main_label_background = color_utils::GetSysSkColor(COLOR_3DFACE);
+ main_text_label_->SetBackgroundColor(main_label_background);
+ AddChildView(main_text_label_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AboutChromeView, ChromeViews::View implementation:
+
+void AboutChromeView::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_ABOUT_DIALOG_WIDTH_CHARS,
+ IDS_ABOUT_DIALOG_HEIGHT_LINES).ToSIZE();
+ // We compute the height of the dialog based on the size of the image (it
+ // would be nice to not hard code this), the text in the about dialog and the
+ // margins around the text.
+ out->cy += 145 + (kPanelVertMargin * 2);
+ // TODO(beng): Eventually the image should be positioned such that hard-
+ // coding the width isn't necessary. This breaks with fonts
+ // that are large and cause wrapping.
+ out->cx = 422;
+}
+
+void AboutChromeView::Layout() {
+ CSize panel_size;
+ GetPreferredSize(&panel_size);
+
+ CSize sz;
+
+ // Background image for the dialog.
+ about_dlg_background_->GetPreferredSize(&sz);
+ int background_image_height = sz.cy; // used to position main text below.
+ about_dlg_background_->SetBounds(0, 0, sz.cx, sz.cy);
+
+ // First label goes to the top left corner.
+ about_title_label_->GetPreferredSize(&sz);
+ about_title_label_->SetBounds(kPanelHorizMargin, kPanelVertMargin,
+ sz.cx, sz.cy);
+
+ // Then we have the version number right below it.
+ version_label_->GetPreferredSize(&sz);
+ version_label_->SetBounds(kPanelHorizMargin,
+ about_title_label_->GetY() +
+ about_title_label_->GetHeight() +
+ kRelatedControlVerticalSpacing,
+ sz.cx,
+ sz.cy);
+
+ // For the width of the main text label we want to use up the whole panel
+ // width and remaining height, minus a little margin on each side.
+ int y_pos = background_image_height + kPanelVertMargin;
+ sz.cx = panel_size.cx - 2 * kPanelHorizMargin;
+ sz.cy = panel_size.cy - y_pos;
+ // Draw the text right below the background image.
+ main_text_label_->SetBounds(kPanelHorizMargin,
+ y_pos,
+ sz.cx,
+ sz.cy);
+
+ // Get the y-coordinate of our parent so we can position the text left of the
+ // buttons at the bottom.
+ CRect parent_bounds;
+ GetParent()->GetLocalBounds(&parent_bounds, false);
+
+ throbber_->GetPreferredSize(&sz);
+ int throbber_topleft_x = kPanelHorizMargin;
+ int throbber_topleft_y = parent_bounds.bottom - sz.cy -
+ kButtonVEdgeMargin - 3;
+ throbber_->SetBounds(throbber_topleft_x, throbber_topleft_y, sz.cx, sz.cy);
+
+ // This image is hidden (see ViewHierarchyChanged) and displayed on demand.
+ success_indicator_.GetPreferredSize(&sz);
+ success_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y,
+ sz.cx, sz.cy);
+
+ // This image is hidden (see ViewHierarchyChanged) and displayed on demand.
+ update_available_indicator_.GetPreferredSize(&sz);
+ update_available_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y,
+ sz.cx, sz.cy);
+
+ // This image is hidden (see ViewHierarchyChanged) and displayed on demand.
+ timeout_indicator_.GetPreferredSize(&sz);
+ timeout_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y,
+ sz.cx, sz.cy);
+
+ // The update label should be at the bottom of the screen, to the right of
+ // the throbber. We specify width to the end of the dialog because it contains
+ // variable length messages.
+ update_label_.GetPreferredSize(&sz);
+ int update_label_x = throbber_->GetX() + throbber_->GetWidth() +
+ kRelatedControlHorizontalSpacing;
+ update_label_.SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ update_label_.SetBounds(update_label_x,
+ throbber_topleft_y + 1,
+ parent_bounds.Width() - update_label_x,
+ sz.cy);
+}
+
+void AboutChromeView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ // Since we want the some of the controls to show up in the same visual row
+ // as the buttons, which are provided by the framework, we must add the
+ // buttons to the non-client view, which is the parent of this view.
+ // Similarly, when we're removed from the view hierarchy, we must take care
+ // to remove these items as well.
+ if (child == this) {
+ if (is_add) {
+ parent->AddChildView(&update_label_);
+ parent->AddChildView(throbber_.get());
+ parent->AddChildView(&success_indicator_);
+ success_indicator_.SetVisible(false);
+ parent->AddChildView(&update_available_indicator_);
+ update_available_indicator_.SetVisible(false);
+ parent->AddChildView(&timeout_indicator_);
+ timeout_indicator_.SetVisible(false);
+
+ // On-demand updates for Chrome don't work in Vista when UAC is turned
+ // off. So, in this case we just want the About box to not mention
+ // on-demand updates. Silent updates (in the background) should still
+ // work as before.
+ if (win_util::UserAccountControlIsEnabled()) {
+ UpdateStatus(UPGRADE_CHECK_STARTED, GOOGLE_UPDATE_NO_ERROR);
+ google_updater_->CheckForUpdate(false); // false=don't upgrade yet.
+ }
+ } else {
+ parent->RemoveChildView(&update_label_);
+ parent->RemoveChildView(throbber_.get());
+ parent->RemoveChildView(&success_indicator_);
+ parent->RemoveChildView(&update_available_indicator_);
+ parent->RemoveChildView(&timeout_indicator_);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AboutChromeView, ChromeViews::DialogDelegate implementation:
+
+int AboutChromeView::GetDialogButtons() const {
+ return DIALOGBUTTON_OK | DIALOGBUTTON_CANCEL;
+}
+
+std::wstring AboutChromeView::GetDialogButtonLabel(
+ DialogButton button) const {
+ if (button == DIALOGBUTTON_OK) {
+ return l10n_util::GetString(IDS_ABOUT_CHROME_UPDATE_CHECK);
+ } else if (button == DIALOGBUTTON_CANCEL) {
+ // The OK button (which is the default button) has been re-purposed to be
+ // 'Check for Updates' so we want the Cancel button should have the label
+ // OK but act like a Cancel button in all other ways.
+ return l10n_util::GetString(IDS_OK);
+ }
+
+ NOTREACHED();
+ return L"";
+}
+
+bool AboutChromeView::IsDialogButtonEnabled(DialogButton button) const {
+ if (button == DIALOGBUTTON_OK && check_button_status_ != CHECKBUTTON_ENABLED)
+ return false;
+
+ return true;
+}
+
+bool AboutChromeView::IsDialogButtonVisible(DialogButton button) const {
+ if (button == DIALOGBUTTON_OK && check_button_status_ == CHECKBUTTON_HIDDEN)
+ return false;
+
+ return true;
+}
+
+bool AboutChromeView::CanResize() const {
+ return false;
+}
+
+bool AboutChromeView::CanMaximize() const {
+ return false;
+}
+
+bool AboutChromeView::IsAlwaysOnTop() const {
+ return false;
+}
+
+bool AboutChromeView::HasAlwaysOnTopMenu() const {
+ return false;
+}
+
+bool AboutChromeView::IsModal() const {
+ return true;
+}
+
+std::wstring AboutChromeView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_ABOUT_CHROME_TITLE);
+}
+
+bool AboutChromeView::Accept() {
+ UpdateStatus(UPGRADE_STARTED, GOOGLE_UPDATE_NO_ERROR);
+
+ // The Upgrade button isn't available until we have received notification
+ // that an update is available, at which point this pointer should have been
+ // null-ed out.
+ DCHECK(!google_updater_);
+ google_updater_ = new GoogleUpdate(); // This object deletes itself.
+ google_updater_->AddStatusChangeListener(this);
+ google_updater_->CheckForUpdate(true); // true=upgrade if new version found.
+
+ return false; // We never allow this button to close the window.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AboutChromeView, GoogleUpdateStatusListener implementation:
+
+void AboutChromeView::OnReportResults(GoogleUpdateUpgradeResult result,
+ GoogleUpdateErrorCode error_code,
+ const std::wstring& version) {
+ // The object will free itself after reporting its status, so we should
+ // no longer refer to it.
+ google_updater_ = NULL;
+
+ // Make a note of which version Google Update is reporting is the latest
+ // version.
+ new_version_available_ = version;
+ if (!new_version_available_.empty()) {
+ new_version_available_ = std::wstring(L"(") +
+ new_version_available_ +
+ std::wstring(L")");
+ }
+ UpdateStatus(result, error_code);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AboutChromeView, private:
+
+void AboutChromeView::UpdateStatus(GoogleUpdateUpgradeResult result,
+ GoogleUpdateErrorCode error_code) {
+ bool show_success_indicator = false;
+ bool show_update_available_indicator = false;
+ bool show_timeout_indicator = false;
+ bool show_throbber = false;
+ bool show_update_label = true; // Always visible, except at start.
+
+ switch (result) {
+ case UPGRADE_STARTED:
+ UserMetrics::RecordAction(L"Upgrade_Started", profile_);
+ check_button_status_ = CHECKBUTTON_DISABLED;
+ show_throbber = true;
+ update_label_.SetText(l10n_util::GetString(IDS_UPGRADE_STARTED));
+ break;
+ case UPGRADE_CHECK_STARTED:
+ UserMetrics::RecordAction(L"UpgradeCheck_Started", profile_);
+ check_button_status_ = CHECKBUTTON_HIDDEN;
+ show_throbber = true;
+ update_label_.SetText(l10n_util::GetString(IDS_UPGRADE_CHECK_STARTED));
+ break;
+ case UPGRADE_IS_AVAILABLE:
+ UserMetrics::RecordAction(L"UpgradeCheck_UpgradeIsAvailable", profile_);
+ check_button_status_ = CHECKBUTTON_ENABLED;
+ update_label_.SetText(
+ l10n_util::GetStringF(IDS_UPGRADE_AVAILABLE,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ show_update_available_indicator = true;
+ break;
+ case UPGRADE_ALREADY_UP_TO_DATE: {
+ // Google Update reported that Chrome is up-to-date. Now make sure that we
+ // are running the latest version and if not, notify the user by falling
+ // into the next case of UPGRADE_SUCCESSFUL.
+ scoped_ptr<installer::Version> installed_version(
+ InstallUtil::GetChromeVersion(false));
+ scoped_ptr<installer::Version> running_version(
+ installer::Version::GetVersionFromString(current_version_));
+ if (!installed_version.get() ||
+ !installed_version->IsHigherThan(running_version.get())) {
+ UserMetrics::RecordAction(L"UpgradeCheck_AlreadyUpToDate", profile_);
+ check_button_status_ = CHECKBUTTON_HIDDEN;
+ update_label_.SetText(
+ l10n_util::GetStringF(IDS_UPGRADE_ALREADY_UP_TO_DATE,
+ l10n_util::GetString(IDS_PRODUCT_NAME),
+ current_version_));
+ show_success_indicator = true;
+ break;
+ }
+ // No break here as we want to notify user about upgrade if there is one.
+ }
+ case UPGRADE_SUCCESSFUL:
+ if (result == UPGRADE_ALREADY_UP_TO_DATE)
+ UserMetrics::RecordAction(L"UpgradeCheck_AlreadyUpgraded", profile_);
+ else
+ UserMetrics::RecordAction(L"UpgradeCheck_Upgraded", profile_);
+ check_button_status_ = CHECKBUTTON_HIDDEN;
+ update_label_.SetText(l10n_util::GetStringF(IDS_UPGRADE_SUCCESSFUL,
+ l10n_util::GetString(IDS_PRODUCT_NAME),
+ new_version_available_));
+ show_success_indicator = true;
+ RestartMessageBox::ShowMessageBox(dialog_->GetHWND());
+ break;
+ case UPGRADE_ERROR:
+ UserMetrics::RecordAction(L"UpgradeCheck_Error", profile_);
+ check_button_status_ = CHECKBUTTON_HIDDEN;
+ update_label_.SetText(l10n_util::GetStringF(IDS_UPGRADE_ERROR,
+ IntToWString(error_code)));
+ show_timeout_indicator = true;
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ success_indicator_.SetVisible(show_success_indicator);
+ update_available_indicator_.SetVisible(show_update_available_indicator);
+ timeout_indicator_.SetVisible(show_timeout_indicator);
+ update_label_.SetVisible(show_update_label);
+ throbber_->SetVisible(show_throbber);
+ if (show_throbber)
+ throbber_->Start();
+ else
+ throbber_->Stop();
+
+ // We have updated controls on the parent, so we need to update its layout.
+ View* parent = GetParent();
+ parent->Layout();
+
+ // Check button may have appeared/disappeared. We cannot call this during
+ // ViewHierarchyChanged because the |dialog_| pointer hasn't been set yet.
+ if (dialog_)
+ dialog_->UpdateDialogButtons();
+}
diff --git a/chrome/browser/views/about_chrome_view.h b/chrome/browser/views/about_chrome_view.h
new file mode 100644
index 0000000..b304218
--- /dev/null
+++ b/chrome/browser/views/about_chrome_view.h
@@ -0,0 +1,135 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_ABOUT_CHROME_VIEW_H_
+#define CHROME_BROWSER_VIEWS_ABOUT_CHROME_VIEW_H_
+
+#include "chrome/browser/google_update.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/label.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+ class TextField;
+ class Throbber;
+ class Window;
+}
+
+class Profile;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The AboutChromeView class is responsible for drawing the UI controls of the
+// About Chrome dialog that allows the user to see what version is installed
+// and check for updates.
+//
+////////////////////////////////////////////////////////////////////////////////
+class AboutChromeView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public GoogleUpdateStatusListener {
+ public:
+ explicit AboutChromeView(Profile* profile);
+ virtual ~AboutChromeView();
+
+ // Initialize the controls on the dialog.
+ void Init();
+
+ void SetDialog(ChromeViews::Window* dialog) { dialog_ = dialog; }
+
+ // Overridden from ChromeViews::View:
+ virtual void GetPreferredSize(CSize *out);
+ virtual void Layout();
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ // Overridden from ChromeViews::DialogDelegate:
+ virtual int GetDialogButtons() const;
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual bool IsDialogButtonEnabled(DialogButton button) const;
+ virtual bool IsDialogButtonVisible(DialogButton button) const;
+ virtual bool CanResize() const;
+ virtual bool CanMaximize() const;
+ virtual bool IsAlwaysOnTop() const;
+ virtual bool HasAlwaysOnTopMenu() const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Accept();
+
+ // Overridden from GoogleUpdateStatusListener:
+ virtual void OnReportResults(GoogleUpdateUpgradeResult result,
+ GoogleUpdateErrorCode error_code,
+ const std::wstring& version);
+
+ private:
+ // The visible state of the Check For Updates button.
+ enum CheckButtonStatus {
+ CHECKBUTTON_HIDDEN = 0,
+ CHECKBUTTON_DISABLED,
+ CHECKBUTTON_ENABLED,
+ };
+
+ // Update the UI to show the status of the upgrade.
+ void UpdateStatus(GoogleUpdateUpgradeResult result,
+ GoogleUpdateErrorCode error_code);
+
+ ChromeViews::Window* dialog_;
+
+ Profile* profile_;
+
+ // UI elements on the dialog.
+ ChromeViews::ImageView* about_dlg_background_;
+ ChromeViews::Label* about_title_label_;
+ ChromeViews::TextField* version_label_;
+ ChromeViews::TextField* main_text_label_;
+ // UI elements we add to the parent view.
+ scoped_ptr<ChromeViews::Throbber> throbber_;
+ ChromeViews::ImageView success_indicator_;
+ ChromeViews::ImageView update_available_indicator_;
+ ChromeViews::ImageView timeout_indicator_;
+ ChromeViews::Label update_label_;
+
+ // Keeps track of the visible state of the Check For Updates button.
+ CheckButtonStatus check_button_status_;
+
+ // The class that communicates with Google Update to find out if an update is
+ // available and asks it to start an upgrade.
+ GoogleUpdate* google_updater_;
+
+ // Our current version.
+ std::wstring current_version_;
+
+ // The version Google Update reports is available to us.
+ std::wstring new_version_available_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AboutChromeView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_ABOUT_CHROME_VIEW_H_
diff --git a/chrome/browser/views/bookmark_bar_view.cc b/chrome/browser/views/bookmark_bar_view.cc
new file mode 100644
index 0000000..9f505ed
--- /dev/null
+++ b/chrome/browser/views/bookmark_bar_view.cc
@@ -0,0 +1,2204 @@
+// 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 <limits>
+
+#include "chrome/browser/views/bookmark_bar_view.h"
+
+#include "base/base_drag_source.h"
+#include "base/gfx/skia_utils.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_frame.h"
+#include "chrome/browser/drag_utils.h"
+#include "chrome/browser/download_util.h"
+#include "chrome/browser/history/history_backend.h"
+#include "chrome/browser/history/history_database.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/page_navigator.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/browser/views/bookmark_editor_view.h"
+#include "chrome/browser/views/event_utils.h"
+#include "chrome/browser/views/input_window.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/favicon_size.h"
+#include "chrome/common/gfx/url_elider.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_types.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/common/page_transition_types.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/chrome_menu.h"
+#include "chrome/views/menu_button.h"
+#include "chrome/views/tooltip_manager.h"
+#include "generated_resources.h"
+
+using ChromeViews::BaseButton;
+using ChromeViews::DropTargetEvent;
+using ChromeViews::MenuButton;
+using ChromeViews::MenuItemView;
+using ChromeViews::View;
+
+// Margins around the content.
+static const int kTopMargin = 2;
+static const int kBottomMargin = 3;
+static const int kLeftMargin = 1;
+static const int kRightMargin = 0;
+
+// Preferred height of the bookmarks bar.
+static const int kBarHeight = 29;
+
+// Preferred height of the bookmarks bar when only shown on the new tab page.
+static const int kNewtabBarHeight = 57;
+
+// How inset the bookmarks bar is when displayed on the new tab page. This is
+// in addition to the margins above.
+static const int kNewtabHorizontalPadding = 8;
+static const int kNewtabVerticalPadding = 12;
+
+// Padding between buttons.
+static const int kButtonPadding = 0;
+
+// Command ids used in the menu allowing the user to choose when we're visible.
+static const int kAlwaysShowCommandID = 1;
+
+// Whether the pref height has been calculated.
+static bool calcedSize = false;
+
+// The preferred height is the height needed for one button. As it is a constant
+// it is calculated once.
+static int prefButtonHeight = 0;
+
+// Icon to display when one isn't found for the page.
+static SkBitmap* kDefaultFavIcon = NULL;
+
+// Icon used for folders.
+static SkBitmap* kFolderIcon = NULL;
+
+// Icon used for most other menu.
+static SkBitmap* kBookmarkedOtherIcon = NULL;
+
+// Background color.
+static const SkColor kBackgroundColor = SkColorSetRGB(237, 244, 252);
+
+// Border colors for the BookmarBarView.
+static const SkColor kTopBorderColor = SkColorSetRGB(222, 234, 248);
+static const SkColor kBottomBorderColor = SkColorSetRGB(178, 178, 178);
+
+// Background color for when the bookmarks bar is only being displayed on the
+// new tab page - this color should match the background color of the new tab
+// page (white, most likely).
+static const SkColor kNewtabBackgroundColor = SkColorSetRGB(255, 255, 255);
+
+// Border color for the 'new tab' style bookmarks bar.
+static const SkColor kNewtabBorderColor = SkColorSetRGB(195, 206, 224);
+
+// How round the 'new tab' style bookmarks bar is.
+static const int kNewtabBarRoundness = 5;
+
+// Offset for where the menu is shown relative to the bottom of the
+// BookmarkBarView.
+static const int kMenuOffset = 3;
+
+// Delay during drag and drop before the menu pops up. This is only used if
+// we can't get the value from the OS.
+static const int kShowFolderDropMenuDelay = 400;
+
+// Color of the drop indicator.
+static const SkColor kDropIndicatorColor = SK_ColorBLACK;
+
+// Width of the drop indicator.
+static const int kDropIndicatorWidth = 2;
+
+// Distance between the bottom of the bar and the separator.
+static const int kSeparatorMargin = 1;
+
+// Width of the separator between the recently bookmarked button and the
+// overflow indicator.
+static const int kSeparatorWidth = 4;
+
+// Starting x-coordinate of the separator line within a separator.
+static const int kSeparatorStartX = 2;
+
+// Border color along the left edge of the view representing the most recently
+// view pages.
+static const SkColor kSeparatorColor = SkColorSetRGB(194, 205, 212);
+
+// Left-padding for the instructional text.
+static const int kInstructionsPadding = 6;
+
+// Color of the instructional text.
+static const SkColor kInstructionsColor = SkColorSetRGB(128, 128, 142);
+
+namespace {
+
+// Returns the tooltip text for the specified url and title. The returned
+// text is clipped to fit within the bounds of the monitor.
+//
+// Note that we adjust the direction of both the URL and the title based on the
+// locale so that pure LTR strings are displayed properly in RTL locales.
+static std::wstring CreateToolTipForURLAndTitle(const gfx::Point& screen_loc,
+ const GURL& url,
+ const std::wstring& title,
+ const std::wstring& languages) {
+ const gfx::Rect monitor_bounds = win_util::GetMonitorBoundsForRect(
+ gfx::Rect(screen_loc.x(), screen_loc.y(), 1, 1));
+ ChromeFont tt_font = ChromeViews::TooltipManager::GetDefaultFont();
+ std::wstring result;
+
+ // First the title.
+ if (!title.empty()) {
+ std::wstring localized_title;
+ if (l10n_util::AdjustStringForLocaleDirection(title, &localized_title))
+ result.append(gfx::ElideText(localized_title,
+ tt_font,
+ monitor_bounds.width()));
+ else
+ result.append(gfx::ElideText(title, tt_font, monitor_bounds.width()));
+ }
+
+ // Only show the URL if the url and title differ.
+ if (title != UTF8ToWide(url.spec())) {
+ if (!result.empty())
+ result.append(ChromeViews::TooltipManager::GetLineSeparator());
+
+ // We need to explicitly specify the directionality of the URL's text to
+ // make sure it is treated as an LTR string when the context is RTL. For
+ // example, the URL "http://www.yahoo.com/" appears as
+ // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
+ // the Unicode BiDi algorithm puts certain characters on the left by
+ // default.
+ std::wstring elided_url(gfx::ElideUrl(url,
+ tt_font,
+ monitor_bounds.width(),
+ languages));
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
+ l10n_util::WrapStringWithLTRFormatting(&elided_url);
+ result.append(elided_url);
+ }
+ return result;
+}
+
+// Returns the drag operations for the specified node.
+static int GetDragOperationsForNode(BookmarkBarNode* node) {
+ if (node->GetType() == history::StarredEntry::URL) {
+ return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE |
+ DragDropTypes::DRAG_LINK;
+ }
+ return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
+}
+
+// BookmarkButton -------------------------------------------------------------
+
+// Buttons used for the bookmarks on the bookmark bar.
+
+class BookmarkButton : public ChromeViews::TextButton {
+ public:
+ BookmarkButton(const GURL& url,
+ const std::wstring& title,
+ Profile* profile)
+ : TextButton(title),
+ url_(url),
+ profile_(profile) {
+ show_animation_.reset(new SlideAnimation(this));
+ show_animation_->Show();
+ }
+
+ bool GetTooltipText(int x, int y, std::wstring* tooltip) {
+ CPoint location(x, y);
+ ConvertPointToScreen(this, &location);
+ *tooltip = CreateToolTipForURLAndTitle(
+ gfx::Point(location.x, location.y), url_, GetText(),
+ profile_->GetPrefs()->GetString(prefs::kAcceptLanguages));
+ return !tooltip->empty();
+ }
+
+ virtual bool IsTriggerableEvent(const ChromeViews::MouseEvent& e) {
+ return event_utils::IsPossibleDispositionEvent(e);
+ }
+
+ virtual void Paint(ChromeCanvas *canvas) {
+ ChromeViews::TextButton::Paint(canvas);
+
+ // Since we can't change the alpha of the button (it contains un-alphable
+ // text), we paint the bar background over the front of the button. As the
+ // bar background is a gradient, we have to paint the gradient at the
+ // size of the parent (hence all the margin math below). We can't use
+ // the parent's actual bounds because they differ from what is painted.
+ SkPaint paint;
+ paint.setAlpha(static_cast<int>(
+ (1.0 - show_animation_->GetCurrentValue()) * 255));
+ paint.setShader(gfx::CreateGradientShader(0,
+ GetHeight() + kTopMargin + kBottomMargin,
+ kTopBorderColor,
+ kBackgroundColor))->safeUnref();
+ canvas->FillRectInt(0, -kTopMargin, GetWidth(),
+ GetHeight() + kTopMargin + kBottomMargin, paint);
+ }
+
+ virtual void AnimationProgressed(const Animation* animation) {
+ ChromeViews::TextButton::AnimationProgressed(animation);
+ SchedulePaint();
+ }
+
+ private:
+ const GURL& url_;
+ Profile* profile_;
+ scoped_ptr<SlideAnimation> show_animation_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BookmarkButton);
+};
+
+// DropInfo -------------------------------------------------------------------
+
+// Tracks drops on the BookmarkBarView.
+
+struct DropInfo {
+ DropInfo() : drop_index(-1), is_menu_showing(false), valid(false) {}
+
+ // Whether the data is valid.
+ bool valid;
+
+ // Index into the model the drop is over. This is relative to the root node.
+ int drop_index;
+
+ // If true, the menu is being shown.
+ bool is_menu_showing;
+
+ // If true, the user is dropping on a node. This is only used for group
+ // nodes.
+ bool drop_on;
+
+ // If true, the user is over the overflow button.
+ bool is_over_overflow;
+
+ // If true, the user is over the other button.
+ bool is_over_other;
+
+ // Coordinates of the drag (in terms of the BookmarkBarView).
+ int x;
+ int y;
+
+ // The current drag operation.
+ int drag_operation;
+
+ // DropData for the drop.
+ BookmarkDragData data;
+};
+
+// ModelChangedListener -------------------------------------------------------
+
+// Interface implemented by controllers/views that need to be notified any
+// time the model changes, typically to cancel an operation that is showing
+// data from the model such as a menu. This isn't intended as a general
+// way to be notified of changes, rather for cases where a controller/view is
+// showing data from the model in a modal like setting and needs to cleanly
+// exit the modal loop if the model changes out from under it.
+//
+// A controller/view that needs this notification should install itself as the
+// ModelChangeListener via the SetModelChangedListener method when shown and
+// reset the ModelChangeListener of the BookmarkBarView when it closes by way
+// of either the SetModelChangedListener method or the
+// ClearModelChangedListenerIfEquals method.
+//
+class ModelChangedListener {
+ public:
+ virtual ~ModelChangedListener() {}
+ // Invoked when the model changes. Should cancel the edit and close any
+ // dialogs.
+ virtual void ModelChanged() = 0;
+};
+
+// EditFolderController -------------------------------------------------------
+
+// EditFolderController manages the editing and/or creation of a folder. If the
+// user presses ok, the name change is committed to the database.
+//
+// EditFolderController deletes itself when the window is closed.
+//
+class EditFolderController : public InputWindowDelegate,
+ public ModelChangedListener {
+ public:
+ EditFolderController(BookmarkBarView* view,
+ BookmarkBarNode* node,
+ int visual_order,
+ bool is_new)
+ : view_(view),
+ node_(node),
+ visual_order_(visual_order),
+ is_new_(is_new) {
+ DCHECK(is_new_ || node);
+ window_ = CreateInputWindow(view->GetViewContainer()->GetHWND(), this);
+ view_->SetModelChangedListener(this);
+ }
+
+ void Show() {
+ window_->Show();
+ }
+
+ virtual void ModelChanged() {
+ window_->Close();
+ }
+
+ private:
+ virtual std::wstring GetTextFieldLabel() {
+ return l10n_util::GetString(IDS_BOOMARK_BAR_EDIT_FOLDER_LABEL);
+ }
+
+ virtual std::wstring GetTextFieldContents() {
+ if (is_new_)
+ return l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME);
+ return node_->GetTitle();
+ }
+
+ virtual bool IsValid(const std::wstring& text) {
+ return !text.empty();
+ }
+
+ virtual void InputAccepted(const std::wstring& text) {
+ view_->ClearModelChangedListenerIfEquals(this);
+ BookmarkBarModel* model = view_->GetProfile()->GetBookmarkBarModel();
+ if (is_new_)
+ model->AddGroup(node_, visual_order_, text);
+ else
+ model->SetTitle(node_, text);
+ }
+
+ virtual void InputCanceled() {
+ view_->ClearModelChangedListenerIfEquals(this);
+ }
+
+ virtual void WindowClosing() {
+ view_->ClearModelChangedListenerIfEquals(this);
+ delete this;
+ }
+
+ virtual std::wstring GetWindowTitle() const {
+ return is_new_ ?
+ l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE_NEW) :
+ l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE);
+ }
+
+ BookmarkBarView* view_;
+
+ // If is_new is true, this is the parent to create the new node under.
+ // Otherwise this is the node to change the title of.
+ BookmarkBarNode* node_;
+
+ int visual_order_;
+ bool is_new_;
+ ChromeViews::Window* window_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(EditFolderController);
+};
+
+// BookmarkNodeMenuController -------------------------------------------------
+
+// IDs for the menus we create.
+
+static const int kOpenBookmarkID = 2;
+static const int kOpenBookmarkInNewWindowID = 3;
+static const int kOpenBookmarkInNewTabID = 4;
+static const int kOpenAllBookmarksID = 5;
+static const int kOpenAllBookmarksInNewWindowID = 6;
+static const int kEditBookmarkID = 7;
+static const int kDeleteBookmarkID = 8;
+static const int kAddBookmarkID = 9;
+static const int kNewFolderID = 10;
+
+// BookmarkNodeMenuController manages the context menus shown for the bookmark
+// bar and buttons on the bookmark bar.
+
+class BookmarkNodeMenuController : public ChromeViews::MenuDelegate,
+ public ModelChangedListener {
+ public:
+ BookmarkNodeMenuController(BookmarkBarView* view,
+ BookmarkBarNode* node)
+ : view_(view),
+ node_(node),
+ menu_(this) {
+ if (node->GetType() == history::StarredEntry::URL) {
+ menu_.AppendMenuItemWithLabel(kOpenBookmarkID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN));
+ menu_.AppendMenuItemWithLabel(kOpenBookmarkInNewTabID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB));
+ menu_.AppendMenuItemWithLabel(kOpenBookmarkInNewWindowID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW));
+ } else {
+ menu_.AppendMenuItemWithLabel(kOpenAllBookmarksID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL));
+ menu_.AppendMenuItemWithLabel(kOpenAllBookmarksInNewWindowID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ }
+ menu_.AppendSeparator();
+
+ if (node->GetParent() !=
+ view->GetProfile()->GetBookmarkBarModel()->root_node()) {
+ menu_.AppendMenuItemWithLabel(kEditBookmarkID,
+ l10n_util::GetString(IDS_BOOKMARK_BAR_EDIT));
+ menu_.AppendMenuItemWithLabel(kDeleteBookmarkID,
+ l10n_util::GetString(IDS_BOOKMARK_BAR_REMOVE));
+ }
+
+ menu_.AppendMenuItemWithLabel(kAddBookmarkID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ menu_.AppendMenuItemWithLabel(kNewFolderID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_NEW_FOLDER));
+ menu_.AppendSeparator();
+ menu_.AppendMenuItem(kAlwaysShowCommandID,
+ l10n_util::GetString(IDS_BOOMARK_BAR_ALWAYS_SHOW),
+ MenuItemView::CHECKBOX);
+ }
+
+ void RunMenuAt(int x, int y) {
+ // Record the current ModelChangedListener. It will be non-null when we're
+ // used as the context menu for another menu.
+ ModelChangedListener* last_listener = view_->GetModelChangedListener();
+
+ view_->SetModelChangedListener(this);
+
+ // width/height don't matter here.
+ menu_.RunMenuAt(view_->GetViewContainer()->GetHWND(), gfx::Rect(x, y, 0, 0),
+ MenuItemView::TOPLEFT, false);
+
+ if (view_->GetModelChangedListener() == this)
+ view_->SetModelChangedListener(last_listener);
+ }
+
+ virtual void ModelChanged() {
+ menu_.Cancel();
+ }
+
+ private:
+ // Menu::Delegate method. Does the appropriate operation based on chosen
+ // menu item.
+ virtual void ExecuteCommand(int id) {
+ Profile* profile = view_->GetProfile();
+
+ switch (id) {
+ case kOpenBookmarkID:
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Open", profile);
+
+ view_->GetPageNavigator()->OpenURL(node_->GetURL(), CURRENT_TAB,
+ PageTransition::AUTO_BOOKMARK);
+ break;
+
+ case kOpenBookmarkInNewWindowID:
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenInNewWindow",
+ profile);
+
+ view_->GetPageNavigator()->OpenURL(node_->GetURL(), NEW_WINDOW,
+ PageTransition::AUTO_BOOKMARK);
+ break;
+
+ case kOpenBookmarkInNewTabID:
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenInNewTab",
+ profile);
+
+ view_->GetPageNavigator()->OpenURL(node_->GetURL(), NEW_FOREGROUND_TAB,
+ PageTransition::AUTO_BOOKMARK);
+ break;
+
+ case kOpenAllBookmarksID:
+ case kOpenAllBookmarksInNewWindowID: {
+ if (id == kOpenAllBookmarksID) {
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenAll",
+ profile);
+ } else {
+ UserMetrics::RecordAction(
+ L"BookmarkBar_ContextMenu_OpenAllInNewWindow", profile);
+ }
+
+ BookmarkBarNode* node = node_;
+ PageNavigator* navigator = view_->GetPageNavigator();
+ bool opened_url = false;
+ OpenAll(node, (id == kOpenAllBookmarksInNewWindowID), &navigator,
+ &opened_url);
+ break;
+ }
+
+ case kEditBookmarkID:
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Edit", profile);
+
+ if (node_->GetType() == history::StarredEntry::URL) {
+ BookmarkEditorView::Show(view_->GetViewContainer()->GetHWND(),
+ view_->GetProfile(), node_->GetURL(), node_->GetTitle());
+ } else {
+ // Controller deletes itself when done.
+ EditFolderController* controller = new EditFolderController(
+ view_, node_, -1, false);
+ controller->Show();
+ }
+ break;
+
+ case kDeleteBookmarkID: {
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Remove", profile);
+
+ view_->model_->Remove(node_->GetParent(),
+ node_->GetParent()->IndexOfChild(node_));
+ break;
+ }
+
+ case kAddBookmarkID: {
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Add", profile);
+
+ BookmarkEditorView::Show(view_->GetViewContainer()->GetHWND(),
+ view_->GetProfile(), GURL(), std::wstring());
+ break;
+ }
+
+ case kNewFolderID: {
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_NewFolder",
+ profile);
+
+ int visual_order;
+ BookmarkBarNode* parent =
+ GetParentAndVisualOrderForNewNode(&visual_order);
+ GetParentAndVisualOrderForNewNode(&visual_order);
+ // Controller deletes itself when done.
+ EditFolderController* controller =
+ new EditFolderController(view_, parent, visual_order, true);
+ controller->Show();
+ break;
+ }
+
+ case kAlwaysShowCommandID:
+ view_->ToggleWhenVisible();
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ }
+
+ bool IsItemChecked(int id) const {
+ DCHECK(id == kAlwaysShowCommandID);
+ return view_->GetProfile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
+ }
+
+ // Opens a tab/window for node and recursively opens all descendants.
+ // If open_first_in_new_window is true, the first opened node is opened
+ // in a new window. navigator indicates the PageNavigator to use for
+ // new tabs. It is reset if open_first_in_new_window is true.
+ // opened_url is set to true the first time a new tab is opened.
+ void OpenAll(BookmarkBarNode* node,
+ bool open_first_in_new_window,
+ PageNavigator** navigator,
+ bool* opened_url) {
+ if (node->GetType() == history::StarredEntry::URL) {
+ WindowOpenDisposition disposition;
+ if (*opened_url)
+ disposition = NEW_BACKGROUND_TAB;
+ else if (open_first_in_new_window)
+ disposition = NEW_WINDOW;
+ else // Open in current window.
+ disposition = CURRENT_TAB;
+ (*navigator)->OpenURL(node->GetURL(), disposition,
+ PageTransition::AUTO_BOOKMARK);
+ if (!*opened_url) {
+ *opened_url = true;
+ if (open_first_in_new_window || disposition == CURRENT_TAB) {
+ // We opened the tab in a new window or in the current tab which
+ // likely reset the navigator. Need to reset the page navigator
+ // appropriately.
+ Browser* new_browser = BrowserList::GetLastActive();
+ TabContents* current_tab = new_browser->GetSelectedTabContents();
+ DCHECK(new_browser && current_tab);
+ if (new_browser && current_tab)
+ *navigator = current_tab;
+ }
+ }
+ } else {
+ // Group, recurse through children.
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ OpenAll(node->GetChild(i), open_first_in_new_window, navigator,
+ opened_url);
+ }
+ }
+ }
+
+ virtual bool IsCommandEnabled(int id) const {
+ if (id == kOpenAllBookmarksID || id == kOpenAllBookmarksInNewWindowID)
+ return NodeHasURLs(node_);
+
+ return true;
+ }
+
+ // Returns true if the specified node is of type URL, or has a descendant
+ // of type URL.
+ static bool NodeHasURLs(BookmarkBarNode* node) {
+ if (node->GetType() == history::StarredEntry::URL)
+ return true;
+
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ if (NodeHasURLs(node->GetChild(i)))
+ return true;
+ }
+ return false;
+ }
+
+ // Returns the parent node and visual_order to use when adding new
+ // bookmarks/folders.
+ BookmarkBarNode* GetParentAndVisualOrderForNewNode(int* visual_order) {
+ if (node_->GetType() != history::StarredEntry::URL) {
+ // Adding to a group always adds to the end.
+ *visual_order = node_->GetChildCount();
+ return node_;
+ } else {
+ DCHECK(node_->GetParent());
+ *visual_order = node_->GetParent()->IndexOfChild(node_) + 1;
+ return node_->GetParent();
+ }
+ }
+
+ MenuItemView menu_;
+ BookmarkBarView* view_;
+ BookmarkBarNode* node_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BookmarkNodeMenuController);
+};
+
+// MenuRunner -----------------------------------------------------------------
+
+// MenuRunner manages creation and showing of a menu containing BookmarkNodes.
+// MenuRunner is used to show the contents of bookmark folders on the
+// bookmark bar, other folder, or overflow bookmarks.
+//
+class MenuRunner : public ChromeViews::MenuDelegate,
+ public ModelChangedListener {
+ public:
+ // start_child_index is the index of the first child in node to add to the
+ // menu.
+ MenuRunner(BookmarkBarView* view,
+ BookmarkBarNode* node,
+ int start_child_index)
+ : view_(view),
+ node_(node),
+ menu_(this) {
+ int next_menu_id = 1;
+ menu_id_to_node_map_[menu_.GetCommand()] = node;
+ BuildMenu(node, start_child_index, &menu_, &next_menu_id);
+ }
+
+ // Returns the node the menu is being run for.
+ BookmarkBarNode* GetNode() {
+ return node_;
+ }
+
+ void RunMenuAt(HWND hwnd,
+ const gfx::Rect& bounds,
+ MenuItemView::AnchorPosition position,
+ bool for_drop) {
+ view_->SetModelChangedListener(this);
+ if (for_drop)
+ menu_.RunMenuForDropAt(hwnd, bounds, position);
+ else
+ menu_.RunMenuAt(hwnd, bounds, position, false);
+ view_->ClearModelChangedListenerIfEquals(this);
+ }
+
+ // Notification that the favicon has finished loading. Reset the icon
+ // of the menu item.
+ void FavIconLoaded(BookmarkBarNode* node) {
+ if (node_to_menu_id_map_.find(node) !=
+ node_to_menu_id_map_.end()) {
+ menu_.SetIcon(node->GetFavIcon(), node_to_menu_id_map_[node]);
+ }
+ }
+
+ virtual void ModelChanged() {
+ if (context_menu_.get())
+ context_menu_->ModelChanged();
+ menu_.Cancel();
+ }
+
+ private:
+ // Creates an entry in menu for each child node of parent starting at
+ // start_child_index, recursively invoking this for any star groups.
+ void BuildMenu(BookmarkBarNode* parent,
+ int start_child_index,
+ MenuItemView* menu,
+ int* next_menu_id) {
+ DCHECK(!parent->GetChildCount() ||
+ start_child_index < parent->GetChildCount());
+ for (int i = start_child_index; i < parent->GetChildCount(); ++i) {
+ BookmarkBarNode* node = parent->GetChild(i);
+ int id = *next_menu_id;
+
+ (*next_menu_id)++;
+ if (node->GetType() == history::StarredEntry::URL) {
+ SkBitmap icon = node->GetFavIcon();
+ if (icon.width() == 0)
+ icon = *kDefaultFavIcon;
+ menu->AppendMenuItemWithIcon(id, node->GetTitle(), icon);
+ node_to_menu_id_map_[node] = id;
+ } else {
+ SkBitmap* folder_icon =
+ ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_BOOKMARK_BAR_FOLDER);
+ MenuItemView* submenu = menu->AppendSubMenuWithIcon(
+ id, node->GetTitle(), *folder_icon);
+ BuildMenu(node, 0, submenu, next_menu_id);
+ }
+ menu_id_to_node_map_[id] = node;
+ }
+ }
+
+ // ViewMenuDelegate method. Overridden to forward to the PageNavigator so
+ // that we accept any events that may trigger opening a url.
+ virtual bool IsTriggerableEvent(const ChromeViews::MouseEvent& e) {
+ return event_utils::IsPossibleDispositionEvent(e);
+ }
+
+ // Invoked when a menu item is selected. Uses the PageNavigator set on
+ // the BookmarkBarView to open the URL.
+ virtual void ExecuteCommand(int id, int mouse_event_flags) {
+ DCHECK(view_->GetPageNavigator());
+ GURL url;
+ DCHECK(menu_id_to_node_map_.find(id) != menu_id_to_node_map_.end());
+ url = menu_id_to_node_map_[id]->GetURL();
+ view_->GetPageNavigator()->OpenURL(
+ url, event_utils::DispositionFromEventFlags(mouse_event_flags),
+ PageTransition::AUTO_BOOKMARK);
+ }
+
+ virtual bool CanDrop(MenuItemView* menu, const OSExchangeData& data) {
+ if (!drop_data_.Read(data))
+ return false;
+
+ if (drop_data_.is_url)
+ return true;
+
+ if (drop_data_.profile_id != view_->GetProfile()->GetID()) {
+ // Always accept drags of bookmark groups from other profiles.
+ return true;
+ }
+ // Drag originated from same profile and is not a URL. Only accept it if
+ // the dragged node is not a parent of the node menu represents.
+ BookmarkBarNode* drop_node = menu_id_to_node_map_[menu->GetCommand()];
+ DCHECK(drop_node);
+ BookmarkBarNode* drag_node = drop_data_.GetNode(view_->GetProfile()->
+ GetBookmarkBarModel());
+ if (!drag_node) {
+ // Hmmm, can't find the dragged node. This is generally an error
+ // condition and we won't try and do anything fancy.
+ NOTREACHED();
+ return false;
+ }
+ BookmarkBarNode* node = drop_node;
+ while (drop_node && drop_node != drag_node)
+ drop_node = drop_node->GetParent();
+ return (drop_node == NULL);
+ }
+
+ virtual int GetDropOperation(MenuItemView* item,
+ const ChromeViews::DropTargetEvent& event,
+ DropPosition* position) {
+ DCHECK(drop_data_.is_valid);
+ BookmarkBarNode* node = menu_id_to_node_map_[item->GetCommand()];
+ BookmarkBarNode* drop_parent = node->GetParent();
+ int index_to_drop_at = drop_parent->IndexOfChild(node);
+ if (*position == DROP_AFTER) {
+ index_to_drop_at++;
+ } else if (*position == DROP_ON) {
+ drop_parent = node;
+ index_to_drop_at = node->GetChildCount();
+ }
+ DCHECK(drop_parent);
+ return view_->CalculateDropOperation(drop_data_, drop_parent,
+ index_to_drop_at);
+ }
+
+ virtual int OnPerformDrop(MenuItemView* menu,
+ DropPosition position,
+ const DropTargetEvent& event) {
+ BookmarkBarNode* drop_node = menu_id_to_node_map_[menu->GetCommand()];
+ DCHECK(drop_node);
+ BookmarkBarModel* model = view_->GetModel();
+ DCHECK(model);
+ BookmarkBarNode* drop_parent = drop_node->GetParent();
+ DCHECK(drop_parent);
+ int index_to_drop_at = drop_parent->IndexOfChild(drop_node);
+ if (position == DROP_AFTER) {
+ index_to_drop_at++;
+ } else if (position == DROP_ON) {
+ DCHECK(drop_node->GetType() != history::StarredEntry::URL);
+ drop_parent = drop_node;
+ index_to_drop_at = drop_node->GetChildCount();
+ }
+
+ const int result = view_->PerformDropImpl(drop_data_, drop_parent,
+ index_to_drop_at);
+ if (view_->drop_menu_runner_.get() == this)
+ view_->drop_menu_runner_.reset();
+ // WARNING: we've been deleted!
+ return result;
+ }
+
+ virtual void ShowContextMenu(MenuItemView* source,
+ int id,
+ int x,
+ int y,
+ bool is_mouse_gesture) {
+ DCHECK(menu_id_to_node_map_.find(id) != menu_id_to_node_map_.end());
+ context_menu_.reset(
+ new BookmarkNodeMenuController(view_, menu_id_to_node_map_[id]));
+ context_menu_->RunMenuAt(x, y);
+ context_menu_.reset(NULL);
+ }
+
+ virtual void DropMenuClosed(MenuItemView* menu) {
+ if (view_->drop_menu_runner_.get() == this)
+ view_->drop_menu_runner_.reset();
+ }
+
+ virtual bool CanDrag(MenuItemView* menu) {
+ DCHECK(menu);
+ return true;
+ }
+
+ virtual void WriteDragData(MenuItemView* sender, OSExchangeData* data) {
+ DCHECK(sender && data);
+
+ UserMetrics::RecordAction(L"BookmarkBar_DragFromFolder",
+ view_->GetProfile());
+
+ view_->WriteDragData(menu_id_to_node_map_[sender->GetCommand()], data);
+ }
+
+ virtual int GetDragOperations(MenuItemView* sender) {
+ return GetDragOperationsForNode(
+ menu_id_to_node_map_[sender->GetCommand()]);
+ }
+
+ // The node we're showing the contents of.
+ BookmarkBarNode* node_;
+
+ // The view that created us.
+ BookmarkBarView* view_;
+
+ // The menu.
+ MenuItemView menu_;
+
+ // Mapping from menu id to the BookmarkBarNode.
+ std::map<int, BookmarkBarNode*> menu_id_to_node_map_;
+
+ // Mapping from node to menu id. This only contains entries for nodes of type
+ // URL.
+ std::map<BookmarkBarNode*, int> node_to_menu_id_map_;
+
+ // Data for the drop.
+ BookmarkDragData drop_data_;
+
+ scoped_ptr<BookmarkNodeMenuController> context_menu_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MenuRunner);
+};
+
+// ButtonSeparatorView --------------------------------------------------------
+
+// TODO(sky/glen): this is temporary, need to decide on what this should
+// look like.
+class ButtonSeparatorView : public ChromeViews::View {
+ public:
+ ButtonSeparatorView() {}
+ virtual ~ButtonSeparatorView() {}
+
+ virtual void Paint(ChromeCanvas* canvas) {
+ SkPaint paint;
+ paint.setShader(gfx::CreateGradientShader(0,
+ GetHeight() / 2,
+ kTopBorderColor,
+ kSeparatorColor))->safeUnref();
+ SkRect rc = {SkIntToScalar(kSeparatorStartX), SkIntToScalar(0),
+ SkIntToScalar(1), SkIntToScalar(GetHeight() / 2) };
+ canvas->drawRect(rc, paint);
+
+ SkPaint paint_down;
+ paint_down.setShader(gfx::CreateGradientShader(GetHeight() / 2,
+ GetHeight(),
+ kSeparatorColor,
+ kBackgroundColor))->safeUnref();
+ SkRect rc_down = {
+ SkIntToScalar(kSeparatorStartX), SkIntToScalar(GetHeight() / 2),
+ SkIntToScalar(1), SkIntToScalar(GetHeight() - 1) };
+ canvas->drawRect(rc_down, paint_down);
+ }
+
+ virtual void GetPreferredSize(CSize* out) {
+ // We get the full height of the bookmark bar, so that the height returned
+ // here doesn't matter.
+ out->SetSize(kSeparatorWidth, 1);
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ButtonSeparatorView);
+};
+
+} // namespace
+
+// BookmarkBarView ------------------------------------------------------------
+
+// static
+const int BookmarkBarView::kMaxButtonWidth = 150;
+
+// Returns the bitmap to use for starred groups.
+static const SkBitmap& GetGroupIcon() {
+ if (!kFolderIcon) {
+ kFolderIcon = ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER);
+ }
+ return *kFolderIcon;
+}
+
+// static
+void BookmarkBarView::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterBooleanPref(prefs::kShowBookmarkBar, false);
+}
+
+BookmarkBarView::BookmarkBarView(Profile* profile, Browser* browser)
+ : profile_(NULL),
+ browser_(browser),
+ page_navigator_(NULL),
+ model_(NULL),
+ other_bookmarked_button_(NULL),
+ model_changed_listener_(NULL),
+ show_folder_drop_menu_task_(NULL),
+ overflow_button_(NULL),
+ instructions_(NULL),
+ bookmarks_separator_view_(NULL),
+ throbbing_view_(NULL) {
+ SetID(VIEW_ID_BOOKMARK_BAR);
+ Init();
+ SetProfile(profile);
+
+
+ if (IsAlwaysShown()) {
+ size_animation_->Reset(1);
+ } else {
+ size_animation_->Reset(0);
+ }
+}
+
+BookmarkBarView::~BookmarkBarView() {
+ NotifyModelChanged();
+ RemoveNotificationObservers();
+ if (model_)
+ model_->RemoveObserver(this);
+ StopShowFolderDropMenuTimer();
+}
+
+void BookmarkBarView::SetProfile(Profile* profile) {
+ DCHECK(profile);
+ if (profile_ == profile)
+ return;
+
+ StopThrobbing(true);
+
+ // Cancels the current cancelable.
+ NotifyModelChanged();
+
+ // Remove the current buttons.
+ for (int i = GetBookmarkButtonCount() - 1; i >= 0; --i) {
+ View* child = GetChildViewAt(i);
+ RemoveChildView(child);
+ delete child;
+ }
+
+ profile_ = profile;
+
+ if (model_)
+ model_->RemoveObserver(this);
+
+ // Disable the other bookmarked button, we'll re-enable when the model is
+ // loaded.
+ other_bookmarked_button_->SetEnabled(false);
+
+ NotificationService* ns = NotificationService::current();
+ Source<Profile> ns_source(profile_->GetOriginalProfile());
+ ns->AddObserver(this, NOTIFY_HISTORY_CREATED, ns_source);
+ ns->AddObserver(this, NOTIFY_BOOKMARK_BUBBLE_SHOWN, ns_source);
+ ns->AddObserver(this, NOTIFY_BOOKMARK_BUBBLE_HIDDEN, ns_source);
+ ns->AddObserver(this, NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
+ NotificationService::AllSources());
+
+ if (!profile->HasHistoryService()) {
+ // The history service hasn't been loaded yet. We don't want to trigger
+ // loading it. Instead we install an observer that is notified when the
+ // history service has loaded.
+ model_ = NULL;
+ } else {
+ ProfileHasValidHistoryService();
+ }
+}
+
+void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) {
+ page_navigator_ = navigator;
+}
+
+void BookmarkBarView::GetPreferredSize(CSize *out) {
+ if (!prefButtonHeight) {
+ ChromeViews::TextButton text_button(L"X");
+ CSize text_button_pref;
+ text_button.GetMinimumSize(&text_button_pref);
+ prefButtonHeight = static_cast<int>(text_button_pref.cy);
+ }
+
+ if (IsNewTabPage()) {
+ out->cy = kBarHeight + static_cast<int>(static_cast<double>
+ (kNewtabBarHeight - kBarHeight) *
+ (1 - size_animation_->GetCurrentValue()));
+ } else {
+ out->cy = std::max(static_cast<int>(static_cast<double>(kBarHeight) *
+ size_animation_->GetCurrentValue()), 1);
+ }
+
+ // Width doesn't matter, we're always given a width based on the browser size.
+ out->cx = 1;
+}
+
+void BookmarkBarView::Layout() {
+ if (!GetParent())
+ return;
+
+ // First layout out the buttons. Any buttons that are placed beyond the
+ // visible region and made invisible.
+ int x = kLeftMargin;
+ int y = kTopMargin;
+ int width = GetWidth() - kRightMargin - kLeftMargin;
+ int height = GetHeight() - kTopMargin - kBottomMargin;
+ int separator_margin = kSeparatorMargin;
+
+ if (IsNewTabPage()) {
+ double current_state = 1 - size_animation_->GetCurrentValue();
+ x += static_cast<int>(static_cast<double>
+ (kNewtabHorizontalPadding) * current_state);
+ y += static_cast<int>(static_cast<double>
+ (kNewtabVerticalPadding) * current_state);
+ width -= static_cast<int>(static_cast<double>
+ (kNewtabHorizontalPadding) * current_state);
+ height -= static_cast<int>(static_cast<double>
+ (kNewtabVerticalPadding * 2) * current_state);
+ separator_margin -= static_cast<int>(static_cast<double>
+ (kSeparatorMargin) * current_state);
+ }
+
+ CSize other_bookmarked_pref;
+ other_bookmarked_button_->GetPreferredSize(&other_bookmarked_pref);
+ CSize overflow_pref;
+ overflow_button_->GetPreferredSize(&overflow_pref);
+ CSize bookmarks_separator_pref;
+ bookmarks_separator_view_->GetPreferredSize(&bookmarks_separator_pref);
+ const int max_x = width - other_bookmarked_pref.cx - kButtonPadding -
+ overflow_pref.cx - kButtonPadding -
+ bookmarks_separator_pref.cx;
+
+ if (GetBookmarkButtonCount() == 0 && model_ && model_->IsLoaded()) {
+ CSize pref;
+ instructions_->GetPreferredSize(&pref);
+ instructions_->SetBounds(x + kInstructionsPadding, y,
+ std::min(static_cast<int>(pref.cx), max_x - x),
+ height);
+ instructions_->SetVisible(true);
+ } else {
+ instructions_->SetVisible(false);
+
+ for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
+ ChromeViews::View* child = GetChildViewAt(i);
+ CSize pref;
+ child->GetPreferredSize(&pref);
+ int next_x = x + pref.cx + kButtonPadding;
+ child->SetVisible(next_x < max_x);
+ child->SetBounds(x, y, pref.cx, height);
+ x = next_x;
+ }
+ }
+
+ // Layout the right side of the bar.
+ const bool all_visible =
+ (GetBookmarkButtonCount() == 0 ||
+ GetChildViewAt(GetBookmarkButtonCount() - 1)->IsVisible());
+
+ // Layout the recently bookmarked button.
+ x = max_x + kButtonPadding;
+
+ // The overflow button.
+ overflow_button_->SetBounds(x, y, overflow_pref.cx, height);
+ overflow_button_->SetVisible(!all_visible);
+
+ x += overflow_pref.cx;
+
+ // Separator.
+ bookmarks_separator_view_->SetBounds(x,
+ y - kTopMargin,
+ bookmarks_separator_pref.cx,
+ height + kTopMargin + kBottomMargin -
+ separator_margin);
+ x += bookmarks_separator_pref.cx;
+
+ other_bookmarked_button_->SetBounds(x, y, other_bookmarked_pref.cx, height);
+ x += other_bookmarked_pref.cx + kButtonPadding;
+}
+
+void BookmarkBarView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void BookmarkBarView::ViewHierarchyChanged(bool is_add,
+ View* parent, View* child) {
+ if (is_add && child == this && GetHeight() > 0) {
+ // We only layout while parented. When we become parented, if our bounds
+ // haven't changed, DidChangeBounds won't get invoked and we won't layout.
+ // Therefore we always force a layout when added.
+ Layout();
+ }
+}
+
+void BookmarkBarView::Paint(ChromeCanvas* canvas) {
+ int width = GetWidth();
+ int height = GetHeight();
+
+ if (IsNewTabPage() && (!IsAlwaysShown() || size_animation_->IsAnimating())) {
+ // Draw the background to match the new tab page.
+ canvas->FillRectInt(kNewtabBackgroundColor, 0, 0, width, height);
+
+ // Draw the 'bottom' of the toolbar above our bubble.
+ canvas->FillRectInt(kBottomBorderColor, 0, 0, width, 1);
+
+ SkRect rect;
+
+ // As 'hidden' according to the animation is the full in-tab state,
+ // we invert the value - when current_state is at '0', we expect the
+ // bar to be docked.
+ double current_state = 1 - size_animation_->GetCurrentValue();
+
+ // The 0.5 is to correct for Skia's "draw on pixel boundaries"ness.
+ double h_padding = static_cast<double>
+ (kNewtabHorizontalPadding) * current_state;
+ double v_padding = static_cast<double>
+ (kNewtabVerticalPadding) * current_state;
+ rect.set(SkDoubleToScalar(h_padding - 0.5),
+ SkDoubleToScalar(v_padding - 0.5),
+ SkDoubleToScalar(width - h_padding - 0.5),
+ SkDoubleToScalar(height - v_padding - 0.5));
+
+ double roundness = static_cast<double>
+ (kNewtabBarRoundness) * current_state;
+
+ // Draw our background.
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setShader(gfx::CreateGradientShader(0,
+ height,
+ kTopBorderColor,
+ kBackgroundColor))->safeUnref();
+
+ canvas->drawRoundRect(rect,
+ SkDoubleToScalar(roundness),
+ SkDoubleToScalar(roundness), paint);
+
+ // Draw border
+ SkPaint border_paint;
+ border_paint.setColor(kNewtabBorderColor);
+ border_paint.setStyle(SkPaint::kStroke_Style);
+ border_paint.setAntiAlias(true);
+
+ canvas->drawRoundRect(rect,
+ SkDoubleToScalar(roundness),
+ SkDoubleToScalar(roundness), border_paint);
+ } else {
+ SkPaint paint;
+ paint.setShader(gfx::CreateGradientShader(0,
+ height,
+ kTopBorderColor,
+ kBackgroundColor))->safeUnref();
+ canvas->FillRectInt(0, 0, width, height, paint);
+
+ canvas->FillRectInt(kTopBorderColor, 0, 0, width, 1);
+ canvas->FillRectInt(kBottomBorderColor, 0, height - 1, width, 1);
+ }
+}
+
+void BookmarkBarView::PaintChildren(ChromeCanvas* canvas) {
+ View::PaintChildren(canvas);
+
+ if (drop_info_.get() && drop_info_->valid &&
+ drop_info_->drag_operation != 0 && drop_info_->drop_index != -1 &&
+ !drop_info_->is_over_overflow && !drop_info_->drop_on) {
+ int index = drop_info_->drop_index;
+ DCHECK(index <= GetBookmarkButtonCount());
+ int x = 0;
+ int y = 0;
+ int h = GetHeight();
+ if (index == GetBookmarkButtonCount()) {
+ if (index == 0) {
+ x = kLeftMargin;
+ } else {
+ x = GetBookmarkButton(index - 1)->GetX() +
+ GetBookmarkButton(index - 1)->GetWidth();
+ }
+ } else {
+ x = GetBookmarkButton(index)->GetX();
+ }
+ if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->IsVisible()) {
+ y = GetBookmarkButton(0)->GetY();
+ h = GetBookmarkButton(0)->GetHeight();
+ }
+
+ // Since the drop indicator is painted directly onto the canvas, we must
+ // make sure it is painted in the right location if the locale is RTL.
+ gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2,
+ y,
+ kDropIndicatorWidth,
+ h);
+ indicator_bounds.set_x(MirroredLeftPointForRect(indicator_bounds));
+
+ // TODO(sky/glen): make me pretty!
+ canvas->FillRectInt(kDropIndicatorColor, indicator_bounds.x(),
+ indicator_bounds.y(), indicator_bounds.width(),
+ indicator_bounds.height());
+ }
+}
+
+bool BookmarkBarView::CanDrop(const OSExchangeData& data) {
+ if (!model_ || !model_->IsLoaded())
+ return false;
+
+ if (!drop_info_.get())
+ drop_info_.reset(new DropInfo());
+
+ return drop_info_->data.Read(data);
+}
+
+void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
+}
+
+int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) {
+ if (!drop_info_.get())
+ return 0;
+
+ if (drop_info_->valid &&
+ (drop_info_->x == event.GetX() && drop_info_->y == event.GetY())) {
+ return drop_info_->drag_operation;
+ }
+
+ drop_info_->x = event.GetX();
+ drop_info_->y = event.GetY();
+
+ int drop_index;
+ bool drop_on;
+ bool is_over_overflow;
+ bool is_over_other;
+
+ drop_info_->drag_operation = CalculateDropOperation(
+ event, drop_info_->data, &drop_index, &drop_on, &is_over_overflow,
+ &is_over_other);
+
+ if (drop_info_->valid && drop_info_->drop_index == drop_index &&
+ drop_info_->drop_on == drop_on &&
+ drop_info_->is_over_overflow == is_over_overflow &&
+ drop_info_->is_over_other == is_over_other) {
+ return drop_info_->drag_operation;
+ }
+
+ drop_info_->valid = true;
+
+ StopShowFolderDropMenuTimer();
+
+ // TODO(sky): Optimize paint region.
+ SchedulePaint();
+ drop_info_->drop_index = drop_index;
+ drop_info_->drop_on = drop_on;
+ drop_info_->is_over_overflow = is_over_overflow;
+ drop_info_->is_over_other = is_over_other;
+
+ if (drop_info_->is_menu_showing) {
+ drop_menu_runner_.reset();
+ drop_info_->is_menu_showing = false;
+ }
+
+ if (drop_on || is_over_overflow || is_over_other) {
+ BookmarkBarNode* node;
+ if (is_over_other)
+ node = model_->other_node();
+ else if (is_over_overflow)
+ node = model_->GetBookmarkBarNode();
+ else
+ node = model_->GetBookmarkBarNode()->GetChild(drop_index);
+ StartShowFolderDropMenuTimer(node);
+ }
+
+ return drop_info_->drag_operation;
+}
+
+void BookmarkBarView::OnDragExited() {
+ StopShowFolderDropMenuTimer();
+
+ // NOTE: we don't hide the menu on exit as it's possible the user moved the
+ // mouse over the menu, which triggers an exit on us.
+
+ drop_info_->valid = false;
+
+ if (drop_info_->drop_index != -1) {
+ // TODO(sky): optimize the paint region.
+ SchedulePaint();
+ }
+ drop_info_.reset();
+}
+
+int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
+ StopShowFolderDropMenuTimer();
+
+ drop_menu_runner_.reset();
+
+ if (!drop_info_.get() || !drop_info_->drag_operation)
+ return DragDropTypes::DRAG_NONE;
+
+ BookmarkBarNode* root =
+ drop_info_->is_over_other ? model_->other_node() :
+ model_->GetBookmarkBarNode();
+ int index = drop_info_->drop_index;
+ const bool drop_on = drop_info_->drop_on;
+ const BookmarkDragData data = drop_info_->data;
+ const bool is_over_other = drop_info_->is_over_other;
+ DCHECK(data.is_valid);
+
+ if (drop_info_->drop_index != -1) {
+ // TODO(sky): optimize the SchedulePaint region.
+ SchedulePaint();
+ }
+ drop_info_.reset();
+
+ BookmarkBarNode* parent_node;
+ if (is_over_other) {
+ parent_node = root;
+ index = parent_node->GetChildCount();
+ } else if (drop_on) {
+ parent_node = root->GetChild(index);
+ index = parent_node->GetChildCount();
+ } else {
+ parent_node = root;
+ }
+ return PerformDropImpl(data, parent_node, index);
+}
+
+void BookmarkBarView::ToggleWhenVisible() {
+ PrefService* prefs = profile_->GetPrefs();
+ const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
+
+ // The user changed when the bookmark bar is shown, update the preferences.
+ prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
+ prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
+
+ // And notify the notification service.
+ Source<Profile> source(profile_);
+ NotificationService::current()->Notify(
+ NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, source,
+ NotificationService::NoDetails());
+
+ // May need to redraw the bar with a new style.
+ SchedulePaint();
+}
+
+bool BookmarkBarView::IsAlwaysShown() {
+ return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
+}
+
+bool BookmarkBarView::IsNewTabPage() {
+ DCHECK(browser_);
+
+ if (browser_->GetSelectedTabContents()) {
+ return browser_->GetSelectedTabContents()->IsBookmarkBarAlwaysVisible();
+ }
+
+ return false;
+}
+
+void BookmarkBarView::AnimationProgressed(const Animation* animation) {
+ browser_->ToolbarSizeChanged(NULL, true);
+}
+
+void BookmarkBarView::AnimationEnded(const Animation* animation) {
+ browser_->ToolbarSizeChanged(NULL, false);
+ SchedulePaint();
+}
+
+void BookmarkBarView::Init() {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ if (!calcedSize)
+ calcedSize = false;
+
+ if (!kDefaultFavIcon)
+ kDefaultFavIcon = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
+
+ other_bookmarked_button_ = CreateOtherBookmarkedButton();
+ AddChildView(other_bookmarked_button_);
+
+ overflow_button_ = CreateOverflowButton();
+ AddChildView(overflow_button_);
+
+ bookmarks_separator_view_ = new ButtonSeparatorView();
+ AddChildView(bookmarks_separator_view_);
+
+ instructions_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_BOOKMARKS_NO_ITEMS),
+ rb.GetFont(ResourceBundle::BaseFont));
+ instructions_->SetColor(kInstructionsColor);
+ AddChildView(instructions_);
+
+ SetContextMenuController(this);
+
+ size_animation_.reset(new SlideAnimation(this));
+}
+
+MenuButton* BookmarkBarView::CreateOtherBookmarkedButton() {
+ MenuButton* button = new MenuButton(
+ l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_BOOKMARKED), this, false);
+ button->SetIcon(GetGroupIcon());
+ button->SetContextMenuController(this);
+ return button;
+}
+
+MenuButton* BookmarkBarView::CreateOverflowButton() {
+ MenuButton* button = new MenuButton(std::wstring(), this, false);
+ button->SetIcon(*ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(IDR_BOOKMARK_BAR_CHEVRONS));
+
+ // The overflow button's image contains an arrow and therefore it is a
+ // direction sensitive image and we need to flip it if the UI layout is
+ // right-to-left.
+ //
+ // By default, menu buttons are not flipped because they generally contain
+ // text and flipping the ChromeCanvas object will break text rendering. Since
+ // the overflow button does not contain text, we can safely flip it.
+ button->EnableCanvasFlippingForRTLUI(true);
+
+ // Make visible as necessary.
+ button->SetVisible(false);
+ return button;
+}
+
+int BookmarkBarView::GetBookmarkButtonCount() {
+ // We contain four non-bookmark button views: recently bookmarked,
+ // bookmarks separator, chevrons (for overflow) and the instruction
+ // label.
+ return GetChildViewCount() - 4;
+}
+
+ChromeViews::TextButton* BookmarkBarView::GetBookmarkButton(int index) {
+ DCHECK(index >= 0 && index < GetBookmarkButtonCount());
+ return static_cast<ChromeViews::TextButton*>(GetChildViewAt(index));
+}
+
+void BookmarkBarView::Loaded(BookmarkBarModel* model) {
+ BookmarkBarNode* node = model_->GetBookmarkBarNode();
+ DCHECK(node && model_->other_node());
+ // Create a button for each of the children on the bookmark bar.
+ for (int i = 0; i < node->GetChildCount(); ++i)
+ AddChildView(i, CreateBookmarkButton(node->GetChild(i)));
+ other_bookmarked_button_->SetEnabled(true);
+ Layout();
+ SchedulePaint();
+}
+
+void BookmarkBarView::BookmarkNodeMoved(BookmarkBarModel* model,
+ BookmarkBarNode* old_parent,
+ int old_index,
+ BookmarkBarNode* new_parent,
+ int new_index) {
+ StopThrobbing(true);
+ BookmarkNodeRemovedImpl(model, old_parent, old_index);
+ BookmarkNodeAddedImpl(model, new_parent, new_index);
+ StartThrobbing();
+}
+
+void BookmarkBarView::BookmarkNodeAdded(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index) {
+ StopThrobbing(true);
+ BookmarkNodeAddedImpl(model, parent, index);
+ StartThrobbing();
+}
+
+void BookmarkBarView::BookmarkNodeAddedImpl(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index) {
+ NotifyModelChanged();
+ if (parent != model_->GetBookmarkBarNode()) {
+ // We only care about nodes on the bookmark bar.
+ return;
+ }
+ DCHECK(index >= 0 && index <= GetBookmarkButtonCount());
+ AddChildView(index, CreateBookmarkButton(parent->GetChild(index)));
+ Layout();
+ SchedulePaint();
+}
+
+void BookmarkBarView::BookmarkNodeRemoved(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index) {
+ StopThrobbing(true);
+ BookmarkNodeRemovedImpl(model, parent, index);
+ StartThrobbing();
+}
+
+void BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index) {
+ NotifyModelChanged();
+ if (parent != model_->GetBookmarkBarNode()) {
+ // We only care about nodes on the bookmark bar.
+ return;
+ }
+ DCHECK(index >= 0 && index < GetBookmarkButtonCount());
+ ChromeViews::View* button = GetChildViewAt(index);
+ RemoveChildView(button);
+ MessageLoop::current()->DeleteSoon(FROM_HERE, button);
+ Layout();
+ SchedulePaint();
+}
+
+void BookmarkBarView::BookmarkNodeChanged(BookmarkBarModel* model,
+ BookmarkBarNode* node) {
+ NotifyModelChanged();
+ BookmarkNodeChangedImpl(model, node);
+}
+
+void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkBarModel* model,
+ BookmarkBarNode* node) {
+ if (node->GetParent() != model_->GetBookmarkBarNode()) {
+ // We only care about nodes on the bookmark bar.
+ return;
+ }
+ int index = model_->GetBookmarkBarNode()->IndexOfChild(node);
+ DCHECK(index != -1);
+ ChromeViews::TextButton* button = GetBookmarkButton(index);
+ CSize old_pref;
+ button->GetPreferredSize(&old_pref);
+ ConfigureButton(node, button);
+ CSize new_pref;
+ button->GetPreferredSize(&new_pref);
+ if (old_pref.cx != new_pref.cx) {
+ Layout();
+ SchedulePaint();
+ } else if (button->IsVisible()) {
+ button->SchedulePaint();
+ }
+}
+
+void BookmarkBarView::BookmarkNodeFavIconLoaded(BookmarkBarModel* model,
+ BookmarkBarNode* node) {
+ if (menu_runner_.get())
+ menu_runner_->FavIconLoaded(node);
+ if (drop_menu_runner_.get())
+ drop_menu_runner_->FavIconLoaded(node);
+ BookmarkNodeChangedImpl(model, node);
+}
+
+void BookmarkBarView::WriteDragData(View* sender,
+ int press_x,
+ int press_y,
+ OSExchangeData* data) {
+ UserMetrics::RecordAction(L"BookmarkBar_DragButton", profile_);
+
+ for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
+ if (sender == GetBookmarkButton(i)) {
+ ChromeViews::TextButton* button = GetBookmarkButton(i);
+ ChromeCanvas canvas(button->GetWidth(), button->GetHeight(), false);
+ button->Paint(&canvas, true);
+ drag_utils::SetDragImageOnDataObject(canvas, button->GetWidth(),
+ button->GetHeight(), press_x,
+ press_y, data);
+ WriteDragData(model_->GetBookmarkBarNode()->GetChild(i), data);
+ return;
+ }
+ }
+ NOTREACHED();
+}
+
+void BookmarkBarView::WriteDragData(BookmarkBarNode* node,
+ OSExchangeData* data) {
+ DCHECK(node && data);
+ BookmarkDragData drag_data(node);
+ drag_data.profile_id = GetProfile()->GetID();
+ drag_data.Write(data);
+}
+
+int BookmarkBarView::GetDragOperations(View* sender, int x, int y) {
+ for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
+ if (sender == GetBookmarkButton(i)) {
+ return GetDragOperationsForNode(
+ model_->GetBookmarkBarNode()->GetChild(i));
+ }
+ }
+ NOTREACHED();
+ return DragDropTypes::DRAG_NONE;
+}
+
+void BookmarkBarView::RunMenu(ChromeViews::View* view,
+ const CPoint& pt,
+ HWND hwnd) {
+ BookmarkBarNode* node;
+ MenuItemView::AnchorPosition anchor_point = MenuItemView::TOPLEFT;
+
+ // When we set the menu's position, we must take into account the mirrored
+ // position of the View relative to its parent. This can be easily done by
+ // passing the right flag to View::GetX().
+ int x = view->GetX(APPLY_MIRRORING_TRANSFORMATION);
+ int height = GetHeight() - kMenuOffset;
+
+ if (IsNewTabPage() && !IsAlwaysShown())
+ height -= kNewtabVerticalPadding;
+
+ int start_index = 0;
+ if (view == other_bookmarked_button_) {
+ UserMetrics::RecordAction(L"BookmarkBar_ShowOtherBookmarks", profile_);
+
+ node = model_->other_node();
+ if (UILayoutIsRightToLeft())
+ anchor_point = MenuItemView::TOPLEFT;
+ else
+ anchor_point = MenuItemView::TOPRIGHT;
+ } else if (view == overflow_button_) {
+ node = model_->GetBookmarkBarNode();
+ start_index = GetFirstHiddenNodeIndex();
+ if (UILayoutIsRightToLeft())
+ anchor_point = MenuItemView::TOPLEFT;
+ else
+ anchor_point = MenuItemView::TOPRIGHT;
+ } else {
+ int button_index = GetChildIndex(view);
+ DCHECK(button_index != -1);
+ node = model_->GetBookmarkBarNode()->GetChild(button_index);
+
+ // When the UI layout is RTL, the bookmarks are laid out from right to left
+ // and therefore when we display the menu we want it to be aligned with the
+ // bottom right corner of the bookmark item.
+ if (UILayoutIsRightToLeft())
+ anchor_point = MenuItemView::TOPRIGHT;
+ else
+ anchor_point = MenuItemView::TOPLEFT;
+ }
+ CPoint screen_loc(x, 0);
+ View::ConvertPointToScreen(this, &screen_loc);
+ menu_runner_.reset(new MenuRunner(this, node, start_index));
+ HWND parent_hwnd = reinterpret_cast<HWND>(
+ browser_->frame()->GetPlatformID());
+ menu_runner_->RunMenuAt(parent_hwnd,
+ gfx::Rect(screen_loc.x, screen_loc.y,
+ view->GetWidth(), height),
+ anchor_point,
+ false);
+}
+
+void BookmarkBarView::ButtonPressed(ChromeViews::BaseButton* sender) {
+ int index = GetChildIndex(sender);
+ DCHECK(index != -1);
+ BookmarkBarNode* node = model_->GetBookmarkBarNode()->GetChild(index);
+ DCHECK(page_navigator_);
+ page_navigator_->OpenURL(
+ node->GetURL(),
+ event_utils::DispositionFromEventFlags(sender->mouse_event_flags()),
+ PageTransition::AUTO_BOOKMARK);
+ UserMetrics::RecordAction(L"ClickedBookmarkBarURLButton", profile_);
+}
+
+void BookmarkBarView::ShowContextMenu(View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture) {
+ if (!model_->IsLoaded()) {
+ // Don't do anything if the model isn't loaded.
+ return;
+ }
+
+ BookmarkBarNode* node = model_->GetBookmarkBarNode();
+ if (source == other_bookmarked_button_) {
+ node = model_->other_node();
+ } else if (source != this) {
+ // User clicked on one of the bookmark buttons, find which one they
+ // clicked on.
+ int bookmark_button_index = GetChildIndex(source);
+ DCHECK(bookmark_button_index != -1 &&
+ bookmark_button_index < GetBookmarkButtonCount());
+ node = model_->GetBookmarkBarNode()->GetChild(bookmark_button_index);
+ }
+ BookmarkNodeMenuController controller(this, node);
+ controller.RunMenuAt(x, y);
+}
+
+ChromeViews::View* BookmarkBarView::CreateBookmarkButton(
+ BookmarkBarNode* node) {
+ if (node->GetType() == history::StarredEntry::URL) {
+ BookmarkButton* button = new BookmarkButton(node->GetURL(),
+ node->GetTitle(),
+ GetProfile());
+ button->SetListener(this, 0);
+ ConfigureButton(node, button);
+ return button;
+ } else {
+ ChromeViews::MenuButton* button =
+ new ChromeViews::MenuButton(node->GetTitle(), this, false);
+ button->SetIcon(GetGroupIcon());
+ ConfigureButton(node, button);
+ return button;
+ }
+}
+
+void BookmarkBarView::ConfigureButton(BookmarkBarNode* node,
+ ChromeViews::TextButton* button) {
+ button->SetText(node->GetTitle());
+ button->ClearMaxTextSize();
+ button->SetContextMenuController(this);
+ button->SetDragController(this);
+ if (node->GetType() == history::StarredEntry::URL) {
+ if (node->GetFavIcon().width() != 0)
+ button->SetIcon(node->GetFavIcon());
+ else
+ button->SetIcon(*kDefaultFavIcon);
+ }
+ button->set_max_width(kMaxButtonWidth);
+}
+
+bool BookmarkBarView::IsItemChecked(int id) const {
+ DCHECK(id == kAlwaysShowCommandID);
+ return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
+}
+
+void BookmarkBarView::ExecuteCommand(int id) {
+ ToggleWhenVisible();
+}
+
+void BookmarkBarView::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(profile_);
+ if (type == NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED) {
+ if (IsAlwaysShown()) {
+ size_animation_->Show();
+ } else {
+ size_animation_->Hide();
+ }
+ } else if (type == NOTIFY_HISTORY_CREATED) {
+ ProfileHasValidHistoryService();
+ } else if (type == NOTIFY_BOOKMARK_BUBBLE_SHOWN) {
+ StopThrobbing(true);
+ bubble_url_ = *(Details<GURL>(details).ptr());
+ StartThrobbing();
+ } else if (type == NOTIFY_BOOKMARK_BUBBLE_HIDDEN) {
+ StopThrobbing(false);
+ bubble_url_ = GURL();
+ }
+}
+
+void BookmarkBarView::ProfileHasValidHistoryService() {
+ DCHECK(profile_);
+ model_ = profile_->GetBookmarkBarModel();
+ DCHECK(model_);
+ model_->AddObserver(this);
+ if (model_->IsLoaded())
+ Loaded(model_);
+}
+
+void BookmarkBarView::RemoveNotificationObservers() {
+ NotificationService* ns = NotificationService::current();
+ Source<Profile> ns_source(profile_->GetOriginalProfile());
+ ns->RemoveObserver(this, NOTIFY_HISTORY_CREATED, ns_source);
+ ns->RemoveObserver(this, NOTIFY_BOOKMARK_BUBBLE_SHOWN, ns_source);
+ ns->RemoveObserver(this, NOTIFY_BOOKMARK_BUBBLE_HIDDEN, ns_source);
+ ns->RemoveObserver(this, NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
+ NotificationService::AllSources());
+}
+
+void BookmarkBarView::NotifyModelChanged() {
+ if (model_changed_listener_)
+ model_changed_listener_->ModelChanged();
+}
+
+void BookmarkBarView::ShowDropFolderForNode(BookmarkBarNode* node) {
+ if (drop_menu_runner_.get() && drop_menu_runner_->GetNode() == node) {
+ // Already showing for the specified node.
+ return;
+ }
+
+ int start_index = 0;
+ View* view_to_position_menu_from;
+
+ // Note that both the anchor position and the position of the menu itself
+ // change depending on the locale. Also note that we must apply the
+ // mirroring transformation when querying for the child View bounds
+ // (View::GetX(), specifically) so that we end up with the correct screen
+ // coordinates if the View in question is mirrored.
+ MenuItemView::AnchorPosition anchor = MenuItemView::TOPLEFT;
+ if (node == model_->other_node()) {
+ view_to_position_menu_from = other_bookmarked_button_;
+ if (!UILayoutIsRightToLeft())
+ anchor = MenuItemView::TOPRIGHT;
+ } else if (node == model_->GetBookmarkBarNode()) {
+ DCHECK(overflow_button_->IsVisible());
+ view_to_position_menu_from = overflow_button_;
+ start_index = GetFirstHiddenNodeIndex();
+ if (!UILayoutIsRightToLeft())
+ anchor = MenuItemView::TOPRIGHT;
+ } else {
+ // Make sure node is still valid.
+ int index = -1;
+ for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
+ if (model_->GetBookmarkBarNode()->GetChild(i) == node) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1)
+ return;
+ view_to_position_menu_from = GetBookmarkButton(index);
+ if (UILayoutIsRightToLeft())
+ anchor = MenuItemView::TOPRIGHT;
+ }
+
+ drop_info_->is_menu_showing = true;
+ drop_menu_runner_.reset(new MenuRunner(this, node, start_index));
+ CPoint screen_loc(0, 0);
+ View::ConvertPointToScreen(view_to_position_menu_from, &screen_loc);
+ drop_menu_runner_->RunMenuAt(
+ GetViewContainer()->GetHWND(),
+ gfx::Rect(screen_loc.x, screen_loc.y,
+ view_to_position_menu_from->GetWidth(),
+ view_to_position_menu_from->GetHeight()),
+ anchor, true);
+}
+
+void BookmarkBarView::StopShowFolderDropMenuTimer() {
+ if (show_folder_drop_menu_task_)
+ show_folder_drop_menu_task_->Cancel();
+}
+
+void BookmarkBarView::StartShowFolderDropMenuTimer(BookmarkBarNode* node) {
+ DCHECK(!show_folder_drop_menu_task_);
+ show_folder_drop_menu_task_ = new ShowFolderDropMenuTask(this, node);
+ static DWORD delay = 0;
+ if (!delay && !SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &delay, 0)) {
+ delay = kShowFolderDropMenuDelay;
+ }
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ show_folder_drop_menu_task_, delay);
+}
+
+int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event,
+ const BookmarkDragData& data,
+ int* index,
+ bool* drop_on,
+ bool* is_over_overflow,
+ bool* is_over_other) {
+ DCHECK(model_);
+ DCHECK(model_->IsLoaded());
+ DCHECK(data.is_valid);
+
+ // The drop event uses the screen coordinates while the child Views are
+ // always laid out from left to right (even though they are rendered from
+ // right-to-left on RTL locales). Thus, in order to make sure the drop
+ // coordinates calculation works, we mirror the event's X coordinate if the
+ // locale is RTL.
+ int mirrored_x = MirroredXCoordinateInsideView(event.GetX());
+
+ *index = -1;
+ *drop_on = false;
+ *is_over_other = *is_over_overflow = false;
+
+ if (event.GetY() < other_bookmarked_button_->GetY() ||
+ event.GetY() >= other_bookmarked_button_->GetY() +
+ other_bookmarked_button_->GetHeight()) {
+ // Mouse isn't over a button.
+ return DragDropTypes::DRAG_NONE;
+ }
+
+ bool found = false;
+ const int other_delta_x = mirrored_x - other_bookmarked_button_->GetX();
+ if (other_delta_x >= 0 &&
+ other_delta_x < other_bookmarked_button_->GetWidth()) {
+ // Mouse is over 'other' folder.
+ *is_over_other = true;
+ *drop_on = true;
+ found = true;
+ } else if (!GetBookmarkButtonCount()) {
+ // No bookmarks, accept the drop.
+ *index = 0;
+ return DragDropTypes::DRAG_COPY;
+ }
+
+ for (int i = 0; i < GetBookmarkButtonCount() &&
+ GetBookmarkButton(i)->IsVisible() && !found; i++) {
+ ChromeViews::TextButton* button = GetBookmarkButton(i);
+ int button_x = mirrored_x - button->GetX();
+ int button_w = button->GetWidth();
+ if (button_x < button_w) {
+ found = true;
+ BookmarkBarNode* node = model_->GetBookmarkBarNode()->GetChild(i);
+ if (node->GetType() != history::StarredEntry::URL) {
+ if (button_x <= MenuItemView::kDropBetweenPixels) {
+ *index = i;
+ } else if (button_x < button_w - MenuItemView::kDropBetweenPixels) {
+ *index = i;
+ *drop_on = true;
+ } else {
+ *index = i + 1;
+ }
+ } else if (button_x < button_w / 2) {
+ *index = i;
+ } else {
+ *index = i + 1;
+ }
+ break;
+ }
+ }
+
+ if (!found) {
+ if (overflow_button_->IsVisible()) {
+ // Are we over the overflow button?
+ int overflow_delta_x = mirrored_x - overflow_button_->GetX();
+ if (overflow_delta_x >= 0 &&
+ overflow_delta_x < overflow_button_->GetWidth()) {
+ // Mouse is over overflow button.
+ *index = GetFirstHiddenNodeIndex();
+ *is_over_overflow = true;
+ } else if (overflow_delta_x < 0) {
+ // Mouse is after the last visible button but before overflow button;
+ // use the last visible index.
+ *index = GetFirstHiddenNodeIndex();
+ } else {
+ return DragDropTypes::DRAG_NONE;
+ }
+ } else if (mirrored_x < other_bookmarked_button_->GetX()) {
+ // Mouse is after the last visible button but before more recently
+ // bookmarked; use the last visible index.
+ *index = GetFirstHiddenNodeIndex();
+ } else {
+ return DragDropTypes::DRAG_NONE;
+ }
+ }
+
+ if (*drop_on) {
+ BookmarkBarNode* parent =
+ *is_over_other ? model_->other_node() :
+ model_->GetBookmarkBarNode()->GetChild(*index);
+ int operation =
+ CalculateDropOperation(data, parent, parent->GetChildCount());
+ if (!operation && !data.is_url &&
+ data.profile_id == GetProfile()->GetID()) {
+ if (data.GetNode(model_) == parent) {
+ // Don't open a menu if the node being dragged is the the menu to
+ // open.
+ *drop_on = false;
+ }
+ }
+ return operation;
+ } else {
+ return CalculateDropOperation(data, model_->GetBookmarkBarNode(), *index);
+ }
+}
+
+int BookmarkBarView::CalculateDropOperation(const BookmarkDragData& data,
+ BookmarkBarNode* parent,
+ int index) {
+ if (!CanDropAt(data, parent, index))
+ return DragDropTypes::DRAG_NONE;
+
+ if (data.is_url) {
+ // User is dragging a URL.
+ BookmarkBarNode* node = model_->GetNodeByURL(data.url);
+ if (!node) {
+ // We don't have a node with this url.
+ return DragDropTypes::DRAG_COPY;
+ }
+ // Technically we're going to move, but most sources export as copy so that
+ // if we don't accept copy we won't accept the drop.
+ return DragDropTypes::DRAG_MOVE | DragDropTypes::DRAG_COPY;
+ } else if (data.profile_id == GetProfile()->GetID()) {
+ // Dropping a group from the same profile results in a move.
+ BookmarkBarNode* node = data.GetNode(model_);
+ if (!node) {
+ // Generally shouldn't get here, we originated the drag but couldn't
+ // find the node.
+ return DragDropTypes::DRAG_NONE;
+ }
+ return DragDropTypes::DRAG_MOVE;
+ } else {
+ // Dropping a group from different profile. Always accept.
+ return DragDropTypes::DRAG_COPY;
+ }
+}
+
+bool BookmarkBarView::CanDropAt(const BookmarkDragData& data,
+ BookmarkBarNode* parent,
+ int index) {
+ DCHECK(data.is_valid);
+ if (data.is_url) {
+ BookmarkBarNode* existing_node = model_->GetNodeByURL(data.url);
+ if (existing_node && existing_node->GetParent() == parent) {
+ const int existing_index = parent->IndexOfChild(existing_node);
+ if (index == existing_index || existing_index + 1 == index)
+ return false;
+ }
+ return true;
+ } else if (data.profile_id == profile_->GetID()) {
+ BookmarkBarNode* existing_node = data.GetNode(model_);
+ if (existing_node) {
+ if (existing_node->GetParent() == parent) {
+ const int existing_index = parent->IndexOfChild(existing_node);
+ if (index == existing_index || existing_index + 1 == index)
+ return false;
+ }
+ // Allow the drop only if the node we're going to drop on isn't a
+ // descendant of the dragged node.
+ BookmarkBarNode* test_node = parent;
+ while (test_node && test_node != existing_node)
+ test_node = test_node->GetParent();
+ return (test_node == NULL);
+ }
+ } // else case clones, always allow.
+ return true;
+}
+
+
+int BookmarkBarView::PerformDropImpl(const BookmarkDragData& data,
+ BookmarkBarNode* parent_node,
+ int index) {
+ if (data.is_url) {
+ // User is dragging a URL.
+ BookmarkBarNode* node = model_->GetNodeByURL(data.url);
+ if (!node) {
+ std::wstring title = data.title;
+ if (title.empty()) {
+ // No title, use the host.
+ title = UTF8ToWide(data.url.host());
+ if (title.empty())
+ title = l10n_util::GetString(IDS_BOOMARK_BAR_UNKNOWN_DRAG_TITLE);
+ }
+ model_->AddURL(parent_node, index, title, data.url);
+ return DragDropTypes::DRAG_COPY;
+ }
+ model_->Move(node, parent_node, index);
+ return DragDropTypes::DRAG_MOVE;
+ } else if (data.profile_id == GetProfile()->GetID()) {
+ BookmarkBarNode* node = data.GetNode(model_);
+ if (!node) {
+ // Generally shouldn't get here, we originated the drag but couldn't
+ // find the node. Do nothing.
+ return DragDropTypes::DRAG_COPY;
+ }
+ model_->Move(node, parent_node, index);
+ return DragDropTypes::DRAG_MOVE;
+ } else {
+ // Dropping a group from different profile. Always accept.
+ CloneDragData(data, parent_node, index);
+ return DragDropTypes::DRAG_COPY;
+ }
+}
+
+void BookmarkBarView::CloneDragData(const BookmarkDragData& data,
+ BookmarkBarNode* parent,
+ int index_to_add_at) {
+ DCHECK(data.is_valid && model_);
+ if (data.is_url) {
+ BookmarkBarNode* node = model_->GetNodeByURL(data.url);
+ if (node) {
+ model_->Move(node, parent, index_to_add_at);
+ } else {
+ model_->AddURL(parent, index_to_add_at, data.title, data.url);
+ }
+ } else {
+ BookmarkBarNode* new_folder = model_->AddGroup(parent, index_to_add_at,
+ data.title);
+ for (int i = 0; i < static_cast<int>(data.children.size()); ++i)
+ CloneDragData(data.children[i], new_folder, i);
+ }
+}
+
+int BookmarkBarView::GetFirstHiddenNodeIndex() {
+ const int bb_count = GetBookmarkButtonCount();
+ for (int i = 0; i < bb_count; ++i) {
+ if (!GetBookmarkButton(i)->IsVisible())
+ return i;
+ }
+ return bb_count;
+}
+
+void BookmarkBarView::StartThrobbing() {
+ DCHECK(!throbbing_view_);
+
+ if (bubble_url_.is_empty())
+ return; // Bubble isn't showing; nothing to throb.
+
+ if (!GetViewContainer())
+ return; // We're not showing, don't do anything.
+
+ BookmarkBarNode* node = model_->GetNodeByURL(bubble_url_);
+ if (!node)
+ return; // Generally shouldn't happen.
+
+ // Determine which visible button is showing the url (or is an ancestor of
+ // the url).
+ if (node->HasAncestor(model_->GetBookmarkBarNode())) {
+ BookmarkBarNode* bbn = model_->GetBookmarkBarNode();
+ BookmarkBarNode* parent_on_bb = node;
+ while (parent_on_bb->GetParent() != bbn)
+ parent_on_bb = parent_on_bb->GetParent();
+ int index = bbn->IndexOfChild(parent_on_bb);
+ if (index >= GetFirstHiddenNodeIndex()) {
+ // Node is hidden, animate the overflow button.
+ throbbing_view_ = overflow_button_;
+ } else {
+ throbbing_view_ = static_cast<BaseButton*>(GetChildViewAt(index));
+ }
+ } else {
+ throbbing_view_ = other_bookmarked_button_;
+ }
+
+ // Use a large number so that the button continues to throb.
+ throbbing_view_->StartThrobbing(std::numeric_limits<int>::max());
+}
+
+void BookmarkBarView::StopThrobbing(bool immediate) {
+ if (!throbbing_view_)
+ return;
+
+ // If not immediate, cycle through 2 more complete cycles.
+ throbbing_view_->StartThrobbing(immediate ? 0 : 4);
+ throbbing_view_ = NULL;
+}
diff --git a/chrome/browser/views/bookmark_bar_view.h b/chrome/browser/views/bookmark_bar_view.h
new file mode 100644
index 0000000..b8ffa37
--- /dev/null
+++ b/chrome/browser/views/bookmark_bar_view.h
@@ -0,0 +1,424 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BOOKMARK_BAR_VIEW_H__
+#define CHROME_BROWSER_VIEWS_BOOKMARK_BAR_VIEW_H__
+
+#include "chrome/browser/bookmark_bar_model.h"
+#include "chrome/browser/bookmark_drag_data.h"
+#include "chrome/common/slide_animation.h"
+#include "chrome/views/label.h"
+#include "chrome/views/menu.h"
+#include "chrome/views/menu_button.h"
+#include "chrome/views/view.h"
+#include "chrome/views/view_menu_delegate.h"
+
+class PageNavigator;
+class PrefService;
+class Browser;
+
+namespace {
+class BookmarkNodeMenuController;
+// See description in declaration at bookmark_bar_view.cc.
+class ModelChangedListener;
+class MenuRunner;
+class ButtonSeparatorView;
+struct DropInfo;
+}
+
+// BookmarkBarView renders the BookmarkBarModel. Each starred entry
+// on the BookmarkBar is rendered as a MenuButton. An additional
+// MenuButton aligned to the right allows the user to quickly see
+// recently starred entries.
+//
+// BookmarkBarView shows the bookmarks from a specific Profile. BookmarkBarView
+// waits until the HistoryService for the profile has been loaded before
+// creating the BookmarkBarModel.
+class BookmarkBarView : public ChromeViews::View,
+ public BookmarkBarModelObserver,
+ public ChromeViews::ViewMenuDelegate,
+ public ChromeViews::BaseButton::ButtonListener,
+ public Menu::Delegate,
+ public NotificationObserver,
+ public ChromeViews::ContextMenuController,
+ public ChromeViews::DragController,
+ public AnimationDelegate {
+ friend class BookmarkNodeMenuController;
+ friend class MenuRunner;
+ friend class ShowFolderMenuTask;
+
+ public:
+ explicit BookmarkBarView(Profile* profile, Browser* browser);
+ virtual ~BookmarkBarView();
+
+ static void RegisterUserPrefs(PrefService* prefs);
+
+ // Resets the profile. This removes any buttons for the current profile and
+ // recreates the models.
+ void SetProfile(Profile* profile);
+
+ // Returns the current profile.
+ Profile* GetProfile() { return profile_; }
+
+ // Sets the PageNavigator that is used when the user selects an entry on
+ // the bookmark bar.
+ void SetPageNavigator(PageNavigator* navigator);
+
+ // View methods:
+ virtual void GetPreferredSize(CSize *out);
+ virtual void Layout();
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void PaintChildren(ChromeCanvas* canvas);
+ virtual bool CanDrop(const OSExchangeData& data);
+ virtual void OnDragEntered(const ChromeViews::DropTargetEvent& event);
+ virtual int OnDragUpdated(const ChromeViews::DropTargetEvent& event);
+ virtual void OnDragExited();
+ virtual int OnPerformDrop(const ChromeViews::DropTargetEvent& event);
+
+ // Sets the model change listener to listener.
+ void SetModelChangedListener(ModelChangedListener* listener) {
+ model_changed_listener_ = listener;
+ }
+
+ // If the ModelChangedListener is listener, ModelChangeListener is set to
+ // NULL.
+ void ClearModelChangedListenerIfEquals(ModelChangedListener* listener) {
+ if (model_changed_listener_ == listener)
+ model_changed_listener_ = NULL;
+ }
+
+ // Returns the model change listener.
+ ModelChangedListener* GetModelChangedListener() {
+ return model_changed_listener_;
+ }
+
+ // Returns the page navigator.
+ PageNavigator* GetPageNavigator() { return page_navigator_; }
+
+ // Returns the model.
+ BookmarkBarModel* GetModel() { return model_; }
+
+ // Toggles whether the bookmark bar is shown only on the new tab page or on
+ // all tabs.
+ void ToggleWhenVisible();
+
+ // Returns true if the bookmarks bar preference is set to 'always show', we
+ // use this as a shorthand way of knowing what style of bar to draw (if the
+ // pref is set to false but we're painting, then we must be on the new tab
+ // page).
+ bool IsAlwaysShown();
+
+ // True if we're supposed to draw the bookmarks bar in the new tab style.
+ bool IsNewTabPage();
+
+ // SlideAnimationDelegate implementation.
+ void AnimationProgressed(const Animation* animation);
+ void AnimationEnded(const Animation* animation);
+
+ // Maximum size of buttons on the bookmark bar.
+ static const int kMaxButtonWidth;
+
+ private:
+ // Task that invokes ShowDropFolderForNode when run. ShowFolderDropMenuTask
+ // deletes itself once run.
+ class ShowFolderDropMenuTask : public Task {
+ public:
+ ShowFolderDropMenuTask(BookmarkBarView* view,
+ BookmarkBarNode* node)
+ : view_(view),
+ node_(node) {
+ }
+
+ void Cancel() {
+ view_->show_folder_drop_menu_task_ = NULL;
+ view_ = NULL;
+ }
+
+ virtual void Run() {
+ if (view_) {
+ view_->show_folder_drop_menu_task_ = NULL;
+ view_->ShowDropFolderForNode(node_);
+ }
+ // MessageLoop deletes us.
+ }
+
+ private:
+ BookmarkBarView* view_;
+ BookmarkBarNode* node_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ShowFolderDropMenuTask);
+ };
+
+ // Creates recent bookmark button and when visible button as well as
+ // calculating the preferred height.
+ void Init();
+
+ // Creates the button showing the other bookmarked items.
+ ChromeViews::MenuButton* CreateOtherBookmarkedButton();
+
+ // Creates the button used when not all bookmark buttons fit.
+ ChromeViews::MenuButton* CreateOverflowButton();
+
+ // Returns the number of buttons corresponding to starred urls/groups. This
+ // is equivalent to the number of children the bookmark bar node from the
+ // bookmark bar model has.
+ int GetBookmarkButtonCount();
+
+ // Returns the button at the specified index.
+ ChromeViews::TextButton* GetBookmarkButton(int index);
+
+ // Invoked when the bookmark bar model has finished loading. Creates a button
+ // for each of the children of the root node from the model.
+ virtual void Loaded(BookmarkBarModel* model);
+
+ // Invokes added followed by removed.
+ virtual void BookmarkNodeMoved(BookmarkBarModel* model,
+ BookmarkBarNode* old_parent,
+ int old_index,
+ BookmarkBarNode* new_parent,
+ int new_index);
+
+ // Notifies ModelChangeListener of change.
+ // If the node was added to the root node, a button is created and added to
+ // this bookmark bar view.
+ virtual void BookmarkNodeAdded(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index);
+
+ // Implementation for BookmarkNodeAddedImpl.
+ void BookmarkNodeAddedImpl(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index);
+
+ // Notifies ModelChangeListener of change.
+ // If the node was a child of the root node, the button corresponding to it
+ // is removed.
+ virtual void BookmarkNodeRemoved(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index);
+
+ // Implementation for BookmarkNodeRemoved.
+ void BookmarkNodeRemovedImpl(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index);
+
+ // Notifies ModelChangedListener and invokes BookmarkNodeChangedImpl.
+ virtual void BookmarkNodeChanged(BookmarkBarModel* model,
+ BookmarkBarNode* node);
+
+ // If the node is a child of the root node, the button is updated
+ // appropriately.
+ void BookmarkNodeChangedImpl(BookmarkBarModel* model, BookmarkBarNode* node);
+
+ // Invoked when the favicon is available. If the node is a child of the
+ // root node, the appropriate button is updated. If a menu is showing, the
+ // call is forwarded to the menu to allow for it to update the icon.
+ virtual void BookmarkNodeFavIconLoaded(BookmarkBarModel* model,
+ BookmarkBarNode* node);
+
+ // DragController method. Determines the node representing sender and invokes
+ // WriteDragData to write the actual data.
+ virtual void WriteDragData(ChromeViews::View* sender,
+ int press_x,
+ int press_y,
+ OSExchangeData* data);
+
+ // Writes a BookmarkDragData for node to data.
+ void WriteDragData(BookmarkBarNode* node, OSExchangeData* data);
+
+ // Returns the drag operations for the specified button.
+ virtual int GetDragOperations(ChromeViews::View* sender, int x, int y);
+
+ // ViewMenuDelegate method. 3 types of menus may be shown:
+ // . the menu allowing the user to choose when the bookmark bar is visible.
+ // . most recently bookmarked menu
+ // . menu for star groups.
+ // The latter two are handled by a MenuRunner, which builds the appropriate
+ // menu.
+ virtual void RunMenu(ChromeViews::View* view, const CPoint& pt, HWND hwnd);
+
+ // Invoked when a star entry corresponding to a URL on the bookmark bar is
+ // pressed. Forwards to the PageNavigator to open the URL.
+ virtual void ButtonPressed(ChromeViews::BaseButton* sender);
+
+ // Invoked for this View, one of the buttons or the 'other' button. Shows the
+ // appropriate context menu.
+ virtual void ShowContextMenu(ChromeViews::View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture);
+
+ // Creates the button for rendering the specified bookmark node.
+ ChromeViews::View* CreateBookmarkButton(BookmarkBarNode* node);
+
+ // COnfigures the button from the specified node. This sets the text,
+ // and icon.
+ void ConfigureButton(BookmarkBarNode* node, ChromeViews::TextButton* button);
+
+ // Used when showing the menu allowing the user to choose when the bar is
+ // visible. Return value corresponds to the users preference for when the
+ // bar is visible.
+ virtual bool IsItemChecked(int id) const;
+
+ // Used when showing the menu allowing the user to choose when the bar is
+ // visible. Updates the preferences to match the users choice as appropriate.
+ virtual void ExecuteCommand(int id);
+
+ // Notification that the HistoryService is up an running. Removes us as
+ // a listener on the notification service and invokes
+ // ProfileHasValidHistoryService.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Invoked when the profile has a history service. Recreates the models.
+ void ProfileHasValidHistoryService();
+
+ // If we have registered an observer on the notification service, this
+ // unregisters it. This does nothing if we have not installed ourself as an
+ // observer.
+ void RemoveNotificationObservers();
+
+ // If the ModelChangedListener is non-null, ModelChanged is invoked on it.
+ void NotifyModelChanged();
+
+ // Shows the menu used during drag and drop for the specified node.
+ void ShowDropFolderForNode(BookmarkBarNode* node);
+
+ // Cancels the timer used to show a drop menu.
+ void StopShowFolderDropMenuTimer();
+
+ // Stars the timer used to show a drop menu for node.
+ void StartShowFolderDropMenuTimer(BookmarkBarNode* node);
+
+ // Returns the drop operation and index for the drop based on the event
+ // and data. Returns DragDropTypes::DRAG_NONE if not a valid location.
+ int CalculateDropOperation(const ChromeViews::DropTargetEvent& event,
+ const BookmarkDragData& data,
+ int* index,
+ bool* drop_on,
+ bool* is_over_overflow,
+ bool* is_over_other);
+
+ // Invokes CanDropAt to determine if this is a valid location for the data,
+ // then returns the appropriate drag operation based on the data.
+ int CalculateDropOperation(const BookmarkDragData& data,
+ BookmarkBarNode* parent,
+ int index);
+
+ // Returns true if the specified location is a valid drop location for
+ // the supplied drag data.
+ bool CanDropAt(const BookmarkDragData& data,
+ BookmarkBarNode* parent,
+ int index);
+
+ // Performs a drop of the specified data at the specified location. Returns
+ // the result.
+ int PerformDropImpl(const BookmarkDragData& data,
+ BookmarkBarNode* parent_node,
+ int index);
+
+ // Creates a new group/entry for data, and recursively invokes itself for
+ // all children of data. This is used during drag and drop to clone a
+ // group from another profile.
+ void CloneDragData(const BookmarkDragData& data,
+ BookmarkBarNode* parent,
+ int index_to_add_at);
+
+ // Returns the index of the first hidden bookmark button. If all buttons are
+ // visible, this returns GetBookmarkButtonCount().
+ int GetFirstHiddenNodeIndex();
+
+ // If the bookmark bubble is showing this determines which view should throb
+ // and starts it throbbing. Does nothing if bookmark bubble isn't showing.
+ void StartThrobbing();
+
+ // If a button is currently throbbing, it is stopped. If immediate is true
+ // the throb stops immediately, otherwise it stops after a couple more
+ // throbs.
+ void StopThrobbing(bool immediate);
+
+ Profile* profile_;
+
+ // Used for opening urls.
+ PageNavigator* page_navigator_;
+
+ // Model providing details as to the starred entries/groups that should be
+ // shown. This is owned by the Profile.
+ BookmarkBarModel* model_;
+
+ // Used to manage showing a Menu: either for the most recently bookmarked
+ // entries, or for the a starred group.
+ scoped_ptr<MenuRunner> menu_runner_;
+
+ // Used when showing a menu for drag and drop. That is, if the user drags
+ // over a group this becomes non-null and is the MenuRunner used to manage
+ // the menu showing the contents of the node.
+ scoped_ptr<MenuRunner> drop_menu_runner_;
+
+ // Shows the other bookmark entries.
+ ChromeViews::MenuButton* other_bookmarked_button_;
+
+ // ModelChangeListener.
+ ModelChangedListener* model_changed_listener_;
+
+ // Task used to delay showing of the drop menu.
+ ShowFolderDropMenuTask* show_folder_drop_menu_task_;
+
+ // Used to track drops on the bookmark bar view.
+ scoped_ptr<DropInfo> drop_info_;
+
+ // Visible if not all the bookmark buttons fit.
+ ChromeViews::MenuButton* overflow_button_;
+
+ // If no bookmarks are visible, we show some text explaining the bar.
+ ChromeViews::Label* instructions_;
+
+ ButtonSeparatorView* bookmarks_separator_view_;
+
+ // Owning browser.
+ Browser* browser_;
+
+ // Animation controlling showing and hiding of the bar.
+ scoped_ptr<SlideAnimation> size_animation_;
+
+ // If the bookmark bubble is showing, this is the URL.
+ GURL bubble_url_;
+
+ // If the bookmark bubble is showing, this is the visible ancestor of the URL.
+ // The visible ancestor is either the other_bookmarked_button_,
+ // overflow_button_ or a button on the bar.
+ ChromeViews::BaseButton* throbbing_view_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BookmarkBarView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BOOKMARK_BAR_VIEW_H__
diff --git a/chrome/browser/views/bookmark_bubble_view.cc b/chrome/browser/views/bookmark_bubble_view.cc
new file mode 100644
index 0000000..8d8f34e
--- /dev/null
+++ b/chrome/browser/views/bookmark_bubble_view.cc
@@ -0,0 +1,390 @@
+// 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/views/bookmark_bubble_view.h"
+
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/bookmark_bar_model.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/browser/views/bookmark_editor_view.h"
+#include "chrome/browser/views/info_bubble.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/button.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/text_field.h"
+#include "generated_resources.h"
+
+using ChromeViews::ComboBox;
+using ChromeViews::ColumnSet;
+using ChromeViews::GridLayout;
+using ChromeViews::Label;
+using ChromeViews::Link;
+using ChromeViews::NativeButton;
+using ChromeViews::View;
+
+// Color of the title.
+static const SkColor kTitleColor = SkColorSetRGB(6, 45, 117);
+
+// Padding between "Title:" and the actual title.
+static const int kTitlePadding = 4;
+
+// Minimum width for the fields - they will push out the size of the bubble if
+// necessary. This should be big enough so that the field pushes the right side
+// of the bubble far enough so that the edit button's left edge is to the right
+// of the field's left edge.
+static const int kMinimumFieldSize = 180;
+
+// Max number of most recently used folders.
+static const size_t kMaxMRUFolders = 5;
+
+// Bubble close image.
+static SkBitmap* kCloseImage = NULL;
+
+// RecentlyUsedFoldersModel ---------------------------------------------------
+
+BookmarkBubbleView::RecentlyUsedFoldersModel::RecentlyUsedFoldersModel(
+ BookmarkBarModel* bb_model, BookmarkBarNode* node)
+ // Use + 2 to account for bookmark bar and other node.
+ : nodes_(bb_model->GetMostRecentlyModifiedGroups(kMaxMRUFolders + 2)),
+ node_parent_index_(0) {
+ // TODO(sky): bug 1173415 add a separator in the combobox here.
+
+ // We special case the placement of these, so remove them from the list, then
+ // fix up the order.
+ RemoveNode(bb_model->GetBookmarkBarNode());
+ RemoveNode(bb_model->other_node());
+ RemoveNode(node->GetParent());
+
+ // Make the parent the first item, unless it's the bookmark bar or other node.
+ if (node->GetParent() != bb_model->GetBookmarkBarNode() &&
+ node->GetParent() != bb_model->other_node()) {
+ nodes_.insert(nodes_.begin(), node->GetParent());
+ }
+
+ // Make sure we only have kMaxMRUFolders in the first chunk.
+ if (nodes_.size() > kMaxMRUFolders)
+ nodes_.erase(nodes_.begin() + kMaxMRUFolders, nodes_.end());
+
+ // And put the bookmark bar and other nodes at the end of the list.
+ nodes_.push_back(bb_model->GetBookmarkBarNode());
+ nodes_.push_back(bb_model->other_node());
+
+ node_parent_index_ = static_cast<int>(
+ find(nodes_.begin(), nodes_.end(), node->GetParent()) - nodes_.begin());
+}
+
+int BookmarkBubbleView::RecentlyUsedFoldersModel::GetItemCount(
+ ComboBox* source) {
+ return static_cast<int>(nodes_.size() + 1);
+}
+
+std::wstring BookmarkBubbleView::RecentlyUsedFoldersModel::GetItemAt(
+ ComboBox* source, int index) {
+ if (index == nodes_.size())
+ return l10n_util::GetString(IDS_BOOMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER);
+ return nodes_[index]->GetTitle();
+}
+
+BookmarkBarNode* BookmarkBubbleView::RecentlyUsedFoldersModel::GetNodeAt(
+ int index) {
+ return nodes_[index];
+}
+
+void BookmarkBubbleView::RecentlyUsedFoldersModel::RemoveNode(
+ BookmarkBarNode* node) {
+ std::vector<BookmarkBarNode*>::iterator i =
+ find(nodes_.begin(), nodes_.end(), node);
+ if (i != nodes_.end())
+ nodes_.erase(i);
+}
+
+// BookmarkBubbleView ---------------------------------------------------------
+
+// static
+void BookmarkBubbleView::Show(HWND parent,
+ const gfx::Rect& bounds,
+ InfoBubbleDelegate* delegate,
+ Profile* profile,
+ const GURL& url,
+ bool newly_bookmarked) {
+ BookmarkBubbleView* view = new BookmarkBubbleView(delegate, profile, url,
+ newly_bookmarked);
+ InfoBubble::Show(parent, bounds, view, view);
+ GURL url_ptr(url);
+ NotificationService::current()->Notify(
+ NOTIFY_BOOKMARK_BUBBLE_SHOWN,
+ Source<Profile>(profile->GetOriginalProfile()),
+ Details<GURL>(&url_ptr));
+ view->BubbleShown();
+}
+
+BookmarkBubbleView::~BookmarkBubbleView() {
+ SetNodeTitleFromTextField();
+}
+
+void BookmarkBubbleView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void BookmarkBubbleView::BubbleShown() {
+ DCHECK(GetViewContainer());
+ ChromeViews::FocusManager* focus_manager =
+ ChromeViews::FocusManager::GetFocusManager(GetViewContainer()->GetHWND());
+ focus_manager->RegisterAccelerator(
+ ChromeViews::Accelerator(VK_RETURN, false, false, false), this);
+
+ title_tf_->RequestFocus();
+ title_tf_->SelectAll();
+}
+
+bool BookmarkBubbleView::AcceleratorPressed(
+ const ChromeViews::Accelerator& accelerator) {
+ if (accelerator.GetKeyCode() != VK_RETURN)
+ return false;
+
+ Close();
+ return true;
+}
+
+BookmarkBubbleView::BookmarkBubbleView(InfoBubbleDelegate* delegate,
+ Profile* profile,
+ const GURL& url,
+ bool newly_bookmarked)
+ : delegate_(delegate),
+ profile_(profile),
+ url_(url),
+ newly_bookmarked_(newly_bookmarked),
+ parent_model_(profile_->GetBookmarkBarModel(),
+ profile_->GetBookmarkBarModel()->GetNodeByURL(url)) {
+ Init();
+}
+
+void BookmarkBubbleView::Init() {
+ if (!kCloseImage) {
+ kCloseImage = ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_INFO_BUBBLE_CLOSE);
+ }
+
+ remove_link_ = new Link(l10n_util::GetString(
+ IDS_BOOMARK_BUBBLE_REMOVE_BOOKMARK));
+ remove_link_->SetController(this);
+
+ edit_button_ = new NativeButton(
+ l10n_util::GetString(IDS_BOOMARK_BUBBLE_OPTIONS));
+ edit_button_->SetListener(this);
+
+ close_button_ = new NativeButton(l10n_util::GetString(IDS_CLOSE));
+ close_button_->SetListener(this);
+
+ parent_combobox_ = new ComboBox(&parent_model_);
+ parent_combobox_->SetSelectedItem(parent_model_.node_parent_index());
+ parent_combobox_->SetListener(this);
+
+ Label* title_label = new Label(l10n_util::GetString(
+ newly_bookmarked_ ? IDS_BOOMARK_BUBBLE_PAGE_BOOKMARKED :
+ IDS_BOOMARK_BUBBLE_PAGE_BOOKMARK));
+ title_label->SetFont(
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont));
+ title_label->SetColor(kTitleColor);
+
+ GridLayout* layout = new GridLayout(this);
+ SetLayoutManager(layout);
+
+ ColumnSet* cs = layout->AddColumnSet(0);
+
+ // Top (title) row.
+ cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
+ 0, 0);
+ cs->AddPaddingColumn(1, kUnrelatedControlHorizontalSpacing);
+ cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
+ 0, 0);
+
+ // Middle (input field) rows.
+ cs = layout->AddColumnSet(2);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ cs->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, kMinimumFieldSize);
+
+ // Bottom (buttons) row.
+ cs = layout->AddColumnSet(3);
+ cs->AddPaddingColumn(1, kRelatedControlHorizontalSpacing);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ // We subtract 2 to account for the natural button padding, and
+ // to bring the separation visually in line with the row separation
+ // height.
+ cs->AddPaddingColumn(0, kRelatedButtonHSpacing - 2);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, 0);
+ layout->AddView(title_label);
+ layout->AddView(remove_link_);
+
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, 2);
+ layout->AddView(
+ new Label(l10n_util::GetString(IDS_BOOMARK_BUBBLE_TITLE_TEXT)));
+ title_tf_ = new ChromeViews::TextField();
+ title_tf_->SetText(GetTitle());
+ layout->AddView(title_tf_);
+
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+
+ layout->StartRow(0, 2);
+ layout->AddView(
+ new Label(l10n_util::GetString(IDS_BOOMARK_BUBBLE_FOLDER_TEXT)));
+ layout->AddView(parent_combobox_);
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+
+ layout->StartRow(0, 3);
+ layout->AddView(edit_button_);
+ layout->AddView(close_button_);
+}
+
+std::wstring BookmarkBubbleView::GetTitle() {
+ BookmarkBarModel* bookmark_model= profile_->GetBookmarkBarModel();
+ BookmarkBarNode* node = bookmark_model->GetNodeByURL(url_);
+ if (node)
+ return node->GetTitle();
+ else
+ NOTREACHED();
+ return std::wstring();
+}
+
+void BookmarkBubbleView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == edit_button_) {
+ UserMetrics::RecordAction(L"BookmarkBubble_Edit", profile_);
+ ShowEditor();
+ } else {
+ DCHECK(sender == close_button_);
+ Close();
+ }
+ // WARNING: we've most likely been deleted when CloseWindow returns.
+}
+
+void BookmarkBubbleView::LinkActivated(Link* source, int event_flags) {
+ DCHECK(source == remove_link_);
+ RemoveBookmark();
+}
+
+void BookmarkBubbleView::ItemChanged(ComboBox* combo_box,
+ int prev_index,
+ int new_index) {
+ if (new_index + 1 == parent_model_.GetItemCount(parent_combobox_)) {
+ UserMetrics::RecordAction(L"BookmarkBubble_EditFromCombobox", profile_);
+
+ ShowEditor();
+ return;
+ }
+ BookmarkBarModel* model = profile_->GetBookmarkBarModel();
+ BookmarkBarNode* node = model->GetNodeByURL(url_);
+ if (node) {
+ BookmarkBarNode* new_parent = parent_model_.GetNodeAt(new_index);
+ if (new_parent != node->GetParent()) {
+ UserMetrics::RecordAction(L"BookmarkBubble_ChangeParent", profile_);
+ model->Move(node, new_parent, new_parent->GetChildCount());
+ }
+ }
+}
+
+void BookmarkBubbleView::InfoBubbleClosing(InfoBubble* info_bubble) {
+ if (delegate_)
+ delegate_->InfoBubbleClosing(info_bubble);
+ NotificationService::current()->Notify(
+ NOTIFY_BOOKMARK_BUBBLE_HIDDEN,
+ Source<Profile>(profile_->GetOriginalProfile()),
+ NotificationService::NoDetails());
+}
+
+bool BookmarkBubbleView::CloseOnEscape() {
+ return delegate_ ? delegate_->CloseOnEscape() : true;
+}
+
+void BookmarkBubbleView::Close() {
+ static_cast<InfoBubble*>(GetViewContainer())->Close();
+}
+
+void BookmarkBubbleView::RemoveBookmark() {
+ UserMetrics::RecordAction(L"BookmarkBubble_Unstar", profile_);
+
+ GURL url = url_;
+ BookmarkBarModel* model = profile_->GetBookmarkBarModel();
+ // Close first, then notify the service. That way we know we won't be
+ // visible and don't have to worry about some other window becoming
+ // activated and deleting us before we invoke Close.
+ Close();
+ // WARNING: we've likely been deleted.
+ if (model)
+ model->SetURLStarred(url, std::wstring(), false);
+}
+
+void BookmarkBubbleView::ShowEditor() {
+ // Parent the editor to our root ancestor (not the root we're in, as that
+ // is the info bubble and will close shortly).
+ HWND parent = GetAncestor(GetViewContainer()->GetHWND(), GA_ROOTOWNER);
+
+ // We're about to show the bookmark editor. When the bookmark editor closes
+ // we want the browser to become active. HWNDViewContainer::Hide() does a
+ // hide in a such way that activation isn't changed, which means when we
+ // close Windows gets confused as to who it should give active status to.
+ // We explicitly hide the bookmark bubble window in such a way that
+ // activation status changes. That way, when the editor closes, activation
+ // is properly restored to the browser.
+ ShowWindow(GetViewContainer()->GetHWND(), SW_HIDE);
+
+ // Even though we just hid the window, we need to invoke Close to schedule
+ // the delete and all that.
+ Close();
+
+ BookmarkEditorView::Show(parent, profile_, url_, title_);
+}
+
+void BookmarkBubbleView::SetNodeTitleFromTextField() {
+ BookmarkBarModel* model = profile_->GetBookmarkBarModel();
+ BookmarkBarNode* node = model->GetNodeByURL(url_);
+ if (node) {
+ const std::wstring new_title = title_tf_->GetText();
+ if (new_title != node->GetTitle()) {
+ model->SetTitle(node, new_title);
+ UserMetrics::RecordAction(L"BookmarkBubble_ChangeTitleInBubble",
+ profile_);
+ }
+ }
+} \ No newline at end of file
diff --git a/chrome/browser/views/bookmark_bubble_view.h b/chrome/browser/views/bookmark_bubble_view.h
new file mode 100644
index 0000000..53d95fd
--- /dev/null
+++ b/chrome/browser/views/bookmark_bubble_view.h
@@ -0,0 +1,187 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BOOKMARK_BUBBLE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_BOOKMARK_BUBBLE_VIEW_H__
+
+#include "base/gfx/rect.h"
+#include "chrome/browser/views/info_bubble.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/link.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/view.h"
+#include "googleurl/src/gurl.h"
+
+class Profile;
+
+class BookmarkBarModel;
+class BookmarkBarNode;
+
+namespace ChromeViews {
+class CheckBox;
+class TextField;
+}
+
+// BookmarkBubbleView is a view intended to be used as the content of an
+// InfoBubble. BookmarkBubbleView provides views for unstarring and editting
+// the bookmark it is created with. Don't create a BookmarkBubbleView directly,
+// instead use the static Show method.
+class BookmarkBubbleView : public ChromeViews::View,
+ public ChromeViews::LinkController,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::ComboBox::Listener,
+ public InfoBubbleDelegate {
+ public:
+ static void Show(HWND parent,
+ const gfx::Rect& bounds,
+ InfoBubbleDelegate* delegate,
+ Profile* profile,
+ const GURL& url,
+ bool newly_bookmarked);
+
+ virtual ~BookmarkBubbleView();
+
+ // Overriden to force a layout.
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+
+ // Invoked after the bubble has been shown.
+ virtual void BubbleShown();
+
+ // Override to close on return.
+ virtual bool AcceleratorPressed(const ChromeViews::Accelerator& accelerator);
+
+ private:
+ // Model for the combobox showing the list of folders to choose from. The
+ // list always contains the bookmark bar, other node and parent. The list
+ // also contains an extra item that shows the text 'Choose another folder...'.
+ class RecentlyUsedFoldersModel : public ChromeViews::ComboBox::Model {
+ public:
+ RecentlyUsedFoldersModel(BookmarkBarModel* bb_model, BookmarkBarNode* node);
+
+ // ComboBox::Model methods. Call through to nodes_.
+ virtual int GetItemCount(ChromeViews::ComboBox* source);
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index);
+
+ // Returns the node at the specified index.
+ BookmarkBarNode* GetNodeAt(int index);
+
+ // Returns the index of the original parent folder.
+ int node_parent_index() const { return node_parent_index_; }
+
+ private:
+ // Removes node from nodes_. Does nothing if node is not in nodes_.
+ void RemoveNode(BookmarkBarNode* node);
+
+ std::vector<BookmarkBarNode*> nodes_;
+ int node_parent_index_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(RecentlyUsedFoldersModel);
+ };
+
+ // Creates a BookmarkBubbleView.
+ // |title| is the title of the page. If newly_bookmarked is false, title is
+ // ignored and the title of the bookmark is fetched from the database.
+ BookmarkBubbleView(InfoBubbleDelegate* delegate,
+ Profile* profile,
+ const GURL& url,
+ bool newly_bookmarked);
+ // Creates the child views.
+ void Init();
+
+ // Returns the title to display.
+ std::wstring GetTitle();
+
+ // LinkController method, either unstars the item or shows the bookmark
+ // editor (depending upon which link was clicked).
+ virtual void LinkActivated(ChromeViews::Link* source, int event_flags);
+
+ // ButtonListener method, closes the bubble or opens the edit dialog.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // ComboBox::Listener method. Changes the parent of the bookmark.
+ virtual void ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index,
+ int new_index);
+
+ // InfoBubbleDelegate methods. These forward to the InfoBubbleDelegate
+ // supplied in the constructor as well as sending out the necessary
+ // notification.
+ virtual void InfoBubbleClosing(InfoBubble* info_bubble);
+ virtual bool CloseOnEscape();
+
+ // Closes the bubble.
+ void Close();
+
+ // Removes the bookmark and closes the view.
+ void RemoveBookmark();
+
+ // Shows the BookmarkEditor.
+ void ShowEditor();
+
+ // Sets the title of the bookmark from the editor
+ void SetNodeTitleFromTextField();
+
+ // Delegate for the bubble, may be null.
+ InfoBubbleDelegate* delegate_;
+
+ // The profile.
+ Profile* profile_;
+
+ // The bookmark URL.
+ const GURL url_;
+
+ // Title of the bookmark. This is initially the title supplied to the
+ // constructor, which is typically the title of the page.
+ std::wstring title_;
+
+ // If true, the page was just bookmarked.
+ const bool newly_bookmarked_;
+
+ RecentlyUsedFoldersModel parent_model_;
+
+ // Link for removing/unstarring the bookmark.
+ ChromeViews::Link* remove_link_;
+
+ // Button to bring up the editor.
+ ChromeViews::NativeButton* edit_button_;
+
+ // Button to close the window.
+ ChromeViews::NativeButton* close_button_;
+
+ // TextField showing the title of the bookmark.
+ ChromeViews::TextField* title_tf_;
+
+ // ComboBox showing a handful of folders the user can choose from, including
+ // the current parent.
+ ChromeViews::ComboBox* parent_combobox_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BookmarkBubbleView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BOOKMARK_BUBBLE_VIEW_H__
diff --git a/chrome/browser/views/bookmark_editor_view.cc b/chrome/browser/views/bookmark_editor_view.cc
new file mode 100644
index 0000000..e7a834a
--- /dev/null
+++ b/chrome/browser/views/bookmark_editor_view.cc
@@ -0,0 +1,561 @@
+// 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/views/bookmark_editor_view.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/url_fixer_upper.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/background.h"
+#include "chrome/views/focus_manager.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/window.h"
+#include "generated_resources.h"
+#include "googleurl/src/gurl.h"
+
+using ChromeViews::ColumnSet;
+using ChromeViews::GridLayout;
+using ChromeViews::Label;
+using ChromeViews::NativeButton;
+using ChromeViews::TextField;
+using ChromeViews::TreeNode;
+
+// Background color of text field when URL is invalid.
+static const SkColor kErrorColor = SkColorSetRGB(0xFF, 0xBC, 0xBC);
+
+// Preferred width of the tree.
+static const int kTreeWidth = 300;
+
+// ID for various children.
+static const int kNewGroupButtonID = 1002;
+
+// static
+void BookmarkEditorView::Show(HWND parent_hwnd,
+ Profile* profile,
+ const GURL& url,
+ const std::wstring& title) {
+ DCHECK(profile);
+ BookmarkEditorView* editor = new BookmarkEditorView(profile, url, title);
+ editor->Show(parent_hwnd);
+}
+
+BookmarkEditorView::BookmarkEditorView(Profile* profile,
+ const GURL& url,
+ const std::wstring& title)
+ : profile_(profile),
+#pragma warning(suppress: 4355) // Okay to pass "this" here.
+ new_group_button_(
+ l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_BUTTON)),
+ dialog_(NULL),
+ url_(url),
+ title_(title),
+ running_menu_for_root_(false) {
+ DCHECK(profile);
+ Init();
+}
+
+BookmarkEditorView::~BookmarkEditorView() {
+ bb_model_->RemoveObserver(this);
+}
+
+bool BookmarkEditorView::IsDialogButtonEnabled(DialogButton button) const {
+ if (button == DIALOGBUTTON_OK) {
+ const GURL url(GetInputURL());
+ return bb_model_->IsLoaded() && url.is_valid();
+ }
+ return true;
+}
+bool BookmarkEditorView::IsModal() const {
+ return true;
+}
+
+std::wstring BookmarkEditorView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_BOOMARK_EDITOR_TITLE);
+}
+
+bool BookmarkEditorView::Accept() {
+ if (!IsDialogButtonEnabled(DIALOGBUTTON_OK)) {
+ // The url is invalid, focus the url field.
+ url_tf_.SelectAll();
+ url_tf_.RequestFocus();
+ return false;
+ }
+ // Otherwise save changes and close the dialog box.
+ ApplyEdits();
+ return true;
+}
+
+bool BookmarkEditorView::AreAcceleratorsEnabled(DialogButton button) {
+ return !tree_view_.GetEditingNode();
+}
+
+void BookmarkEditorView::Layout() {
+ // Let the grid layout manager lay out most of the dialog...
+ GetLayoutManager()->Layout(this);
+
+ // Manually lay out the New Folder button in the same row as the OK/Cancel
+ // buttons...
+ CRect parent_bounds;
+ GetParent()->GetLocalBounds(&parent_bounds, false);
+ CSize prefsize;
+ new_group_button_.GetPreferredSize(&prefsize);
+ int button_y = parent_bounds.bottom - prefsize.cy - kButtonVEdgeMargin;
+ new_group_button_.SetBounds(kPanelHorizMargin, button_y, prefsize.cx,
+ prefsize.cy);
+}
+
+void BookmarkEditorView::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_EDITBOOKMARK_DIALOG_WIDTH_CHARS,
+ IDS_EDITBOOKMARK_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void BookmarkEditorView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void BookmarkEditorView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (child == this) {
+ // Add and remove the New Folder button from the ClientView's hierarchy.
+ if (is_add) {
+ parent->AddChildView(&new_group_button_);
+ } else {
+ parent->RemoveChildView(&new_group_button_);
+ }
+ }
+}
+
+void BookmarkEditorView::OnTreeViewSelectionChanged(
+ ChromeViews::TreeView* tree_view) {
+}
+
+bool BookmarkEditorView::CanEdit(ChromeViews::TreeView* tree_view,
+ ChromeViews::TreeModelNode* node) {
+ // Only allow editting of children of the bookmark bar node and other node.
+ BookmarkNode* bb_node = tree_model_->AsNode(node);
+ return (bb_node->GetParent() && bb_node->GetParent()->GetParent());
+}
+
+void BookmarkEditorView::ContentsChanged(TextField* sender,
+ const std::wstring& new_contents) {
+ UserInputChanged();
+}
+
+void BookmarkEditorView::ButtonPressed(NativeButton* sender) {
+ DCHECK(sender);
+ switch (sender->GetID()) {
+ case kNewGroupButtonID:
+ NewGroup();
+ break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void BookmarkEditorView::ExecuteCommand(int id) {
+ DCHECK(tree_view_.GetSelectedNode());
+ if (id == IDS_EDIT) {
+ tree_view_.StartEditing(tree_view_.GetSelectedNode());
+ } else {
+ DCHECK(id == IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
+ NewGroup();
+ }
+}
+
+bool BookmarkEditorView::IsCommandEnabled(int id) const {
+ return (id != IDS_EDIT || !running_menu_for_root_);
+}
+
+void BookmarkEditorView::Show(HWND parent_hwnd) {
+ dialog_ = ChromeViews::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(),
+ this, this);
+ UserInputChanged();
+ if (bb_model_->IsLoaded())
+ ExpandAndSelect();
+ dialog_->Show();
+ // Select all the text in the name textfield.
+ title_tf_.SelectAll();
+ // Give focus to the name textfield.
+ title_tf_.RequestFocus();
+}
+
+void BookmarkEditorView::Close() {
+ DCHECK(dialog_);
+ dialog_->Close();
+}
+
+void BookmarkEditorView::ShowContextMenu(View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture) {
+ DCHECK(source == &tree_view_);
+ if (!tree_view_.GetSelectedNode())
+ return;
+ running_menu_for_root_ =
+ (tree_model_->GetParent(tree_view_.GetSelectedNode()) ==
+ tree_model_->GetRoot());
+ context_menu_.reset(new Menu(this, Menu::TOPLEFT,
+ GetViewContainer()->GetHWND()));
+ context_menu_->AppendMenuItemWithLabel(IDS_EDIT,
+ l10n_util::GetString(IDS_EDIT));
+ context_menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM,
+ l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM));
+ context_menu_->RunMenuAt(x, y);
+}
+
+void BookmarkEditorView::Init() {
+ tree_view_.SetContextMenuController(this);
+ bb_model_ = profile_->GetBookmarkBarModel();
+ DCHECK(bb_model_);
+ bb_model_->AddObserver(this);
+
+ tree_view_.SetRootShown(false);
+ // Tell View not to delete all Views declared by value.
+ tree_view_.SetParentOwned(false);
+ new_group_button_.SetParentOwned(false);
+ url_tf_.SetParentOwned(false);
+ title_tf_.SetParentOwned(false);
+
+ new_group_button_.SetEnabled(false);
+ new_group_button_.SetListener(this);
+ new_group_button_.SetID(kNewGroupButtonID);
+
+ title_tf_.SetText(title_);
+ title_tf_.SetController(this);
+
+ url_tf_.SetText(UTF8ToWide(url_.spec()));
+ url_tf_.SetController(this);
+
+ // Yummy layout code.
+ const int labels_column_set_id = 0;
+ const int single_column_view_set_id = 1;
+ const int buttons_column_set_id = 2;
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+ ColumnSet* column_set = layout->AddColumnSet(labels_column_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::FIXED, kTreeWidth, 0);
+
+ column_set = layout->AddColumnSet(buttons_column_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(1, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->LinkColumnSizes(0, 2, 4, -1);
+
+ layout->StartRow(0, labels_column_set_id);
+ layout->AddView(
+ new Label(l10n_util::GetString(IDS_BOOMARK_EDITOR_NAME_LABEL)));
+ layout->AddView(&title_tf_);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, labels_column_set_id);
+ layout->AddView(
+ new Label(l10n_util::GetString(IDS_BOOMARK_EDITOR_URL_LABEL)));
+ layout->AddView(&url_tf_);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(1, single_column_view_set_id);
+ layout->AddView(&tree_view_);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ if (bb_model_->IsLoaded())
+ Loaded(bb_model_);
+}
+
+void BookmarkEditorView::Loaded(BookmarkBarModel* model) {
+ Reset(true);
+}
+
+void BookmarkEditorView::BookmarkNodeMoved(BookmarkBarModel* model,
+ BookmarkBarNode* old_parent,
+ int old_index,
+ BookmarkBarNode* new_parent,
+ int new_index) {
+ Reset(false);
+}
+
+void BookmarkEditorView::BookmarkNodeAdded(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index) {
+ Reset(false);
+}
+
+void BookmarkEditorView::BookmarkNodeRemoved(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index) {
+ Reset(false);
+}
+
+void BookmarkEditorView::Reset(bool first_time) {
+ BookmarkBarNode* node_editing = bb_model_->GetNodeByURL(url_);
+
+ // If the title is empty we need to fetch it from the node.
+ if (first_time && title_.empty()) {
+ if (node_editing) {
+ title_ = node_editing->GetTitle();
+ title_tf_.SetText(title_);
+ }
+ }
+
+ // Do this first, otherwise when we invoke SetModel with the real one
+ // tree_view will try to invoke something on the model we just deleted.
+ tree_view_.SetModel(NULL);
+
+ BookmarkNode* root_node = CreateRootNode();
+ tree_model_.reset(new BookmarkTreeModel(root_node));
+
+ tree_view_.SetModel(tree_model_.get());
+ tree_view_.SetController(this);
+
+ new_group_button_.SetEnabled(true);
+
+ context_menu_.reset();
+
+ if (GetParent()) {
+ ExpandAndSelect();
+
+ if(!first_time)
+ UserInputChanged();
+ } else if (GetParent()) {
+ tree_view_.ExpandAll();
+ }
+}
+GURL BookmarkEditorView::GetInputURL() const {
+ std::wstring input = URLFixerUpper::FixupURL(url_tf_.GetText(), L"");
+ return GURL(input);
+}
+
+std::wstring BookmarkEditorView::GetInputTitle() const {
+ return title_tf_.GetText();
+}
+
+void BookmarkEditorView::UserInputChanged() {
+ const GURL url(GetInputURL());
+ if (!url.is_valid())
+ url_tf_.SetBackgroundColor(kErrorColor);
+ else
+ url_tf_.SetDefaultBackgroundColor();
+ dialog_->UpdateDialogButtons();
+}
+
+void BookmarkEditorView::NewGroup() {
+ // Create a new entry parented to the selected item, or the bookmark
+ // bar if nothing is selected.
+ BookmarkNode* parent = tree_model_->AsNode(tree_view_.GetSelectedNode());
+ if (!parent) {
+ NOTREACHED();
+ return;
+ }
+
+ BookmarkNode* new_node = new BookmarkNode();
+ new_node->SetTitle(l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME));
+ new_node->value = 0;
+ // new_node is now owned by parent.
+ tree_model_->Add(parent, parent->GetChildCount(), new_node);
+ // Edit the new node.
+ tree_view_.StartEditing(new_node);
+}
+
+void BookmarkEditorView::ExpandAndSelect() {
+ tree_view_.ExpandAll();
+
+ BookmarkBarNode* to_select = bb_model_->GetNodeByURL(url_);
+ history::UIStarID group_id_to_select =
+ to_select ? to_select->GetParent()->GetGroupID() :
+ bb_model_->GetParentForNewNodes()->GetGroupID();
+
+ DCHECK(group_id_to_select); // GetMostRecentParent should never return NULL.
+ BookmarkNode* b_node =
+ FindNodeWithID(tree_model_->GetRoot(), group_id_to_select);
+ if (!b_node)
+ b_node = tree_model_->GetRoot()->GetChild(0); // Bookmark bar node.
+
+ tree_view_.SetSelectedNode(b_node);
+}
+
+BookmarkEditorView::BookmarkNode* BookmarkEditorView::CreateRootNode() {
+ BookmarkNode* root_node = new BookmarkNode(std::wstring(), 0);
+ BookmarkBarNode* bb_root_node = bb_model_->root_node();
+ CreateNodes(bb_root_node, root_node);
+ DCHECK(root_node->GetChildCount() == 2);
+ DCHECK(bb_root_node->GetChild(0)->GetType() ==
+ history::StarredEntry::BOOKMARK_BAR);
+ DCHECK(bb_root_node->GetChild(1)->GetType() == history::StarredEntry::OTHER);
+ return root_node;
+}
+
+void BookmarkEditorView::CreateNodes(BookmarkBarNode* bb_node,
+ BookmarkEditorView::BookmarkNode* b_node) {
+ for (int i = 0; i < bb_node->GetChildCount(); ++i) {
+ BookmarkBarNode* child_bb_node = bb_node->GetChild(i);
+ if (child_bb_node->GetType() != history::StarredEntry::URL) {
+ BookmarkNode* new_b_node = new BookmarkNode(child_bb_node->GetTitle(),
+ child_bb_node->GetGroupID());
+ b_node->Add(b_node->GetChildCount(), new_b_node);
+ CreateNodes(child_bb_node, new_b_node);
+ }
+ }
+}
+
+BookmarkEditorView::BookmarkNode* BookmarkEditorView::FindNodeWithID(
+ BookmarkEditorView::BookmarkNode* node,
+ history::UIStarID id) {
+ if (node->value == id)
+ return node;
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ BookmarkNode* result = FindNodeWithID(node->GetChild(i), id);
+ if (result)
+ return result;
+ }
+ return NULL;
+}
+
+void BookmarkEditorView::ApplyEdits() {
+ DCHECK(bb_model_->IsLoaded());
+
+ if (!tree_view_.GetSelectedNode()) {
+ NOTREACHED();
+ return;
+ }
+
+ // We're going to apply edits to the bookmark bar model, which will call us
+ // back. Normally when a structural edit occurs we reset the tree model.
+ // We don't want to do that here, so we remove ourselves as an observer.
+ bb_model_->RemoveObserver(this);
+
+ GURL new_url(GetInputURL());
+ std::wstring new_title(GetInputTitle());
+
+ BookmarkBarNode* old_node = bb_model_->GetNodeByURL(url_);
+ BookmarkBarNode* old_parent = old_node ? old_node->GetParent() : NULL;
+ const int old_index = old_parent ? old_parent->IndexOfChild(old_node) : -1;
+
+ if (url_ != new_url) {
+ // The URL has changed, unstar the old url.
+ bb_model_->SetURLStarred(url_, std::wstring(), false);
+ }
+
+ // Create the new groups and update the titles.
+ BookmarkBarNode* new_parent = NULL;
+ ApplyNameChangesAndCreateNewGroups(
+ bb_model_->root_node(), tree_model_->GetRoot(),
+ tree_model_->AsNode(tree_view_.GetSelectedNode()), &new_parent);
+
+ if (!new_parent) {
+ // Bookmarks must be parented.
+ NOTREACHED();
+ return;
+ }
+
+ BookmarkBarNode* current_node = bb_model_->GetNodeByURL(new_url);
+
+ if (current_node) {
+ // There's already a node with the URL.
+ bb_model_->SetTitle(current_node, new_title);
+ if (new_parent == old_parent) {
+ // Parent hasn't changed.
+ bb_model_->Move(current_node, new_parent, old_index);
+ } else {
+ // Parent changed, move to end of new parent.
+ bb_model_->Move(current_node, new_parent, new_parent->GetChildCount());
+ }
+ } else {
+ // Adding a new URL.
+ if (new_parent == old_parent) {
+ // Parent hasn't changed. Place newly created bookmark at the same
+ // location as last bookmark.
+ bb_model_->AddURL(new_parent, old_index, new_title, new_url);
+ } else {
+ // Parent changed, put bookmark at end of new parent.
+ bb_model_->AddURL(new_parent, new_parent->GetChildCount(), new_title,
+ new_url);
+ }
+ }
+}
+
+void BookmarkEditorView::ApplyNameChangesAndCreateNewGroups(
+ BookmarkBarNode* bb_node,
+ BookmarkEditorView::BookmarkNode* b_node,
+ BookmarkEditorView::BookmarkNode* parent_b_node,
+ BookmarkBarNode** parent_bb_node) {
+ if (parent_b_node == b_node)
+ *parent_bb_node = bb_node;
+ for (int i = 0; i < b_node->GetChildCount(); ++i) {
+ BookmarkNode* child_b_node = b_node->GetChild(i);
+ BookmarkBarNode* child_bb_node = NULL;
+ if (child_b_node->value == 0) {
+ // New group.
+ child_bb_node = bb_model_->AddGroup(bb_node,
+ bb_node->GetChildCount(), child_b_node->GetTitle());
+ } else {
+ // Existing node, reset the title (BBModel ignores changes if the title
+ // is the same).
+ for (int j = 0; j < bb_node->GetChildCount(); ++j) {
+ BookmarkBarNode* node = bb_node->GetChild(j);
+ if (node->GetType() != history::StarredEntry::URL &&
+ node->GetGroupID() == child_b_node->value) {
+ child_bb_node = node;
+ break;
+ }
+ }
+ DCHECK(child_bb_node);
+ bb_model_->SetTitle(child_bb_node, child_b_node->GetTitle());
+ }
+ ApplyNameChangesAndCreateNewGroups(child_bb_node, child_b_node,
+ parent_b_node, parent_bb_node);
+ }
+}
diff --git a/chrome/browser/views/bookmark_editor_view.h b/chrome/browser/views/bookmark_editor_view.h
new file mode 100644
index 0000000..c12abeb
--- /dev/null
+++ b/chrome/browser/views/bookmark_editor_view.h
@@ -0,0 +1,269 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BOOKMARK_EDITOR_VIEW_H__
+#define CHROME_BROWSER_VIEWS_BOOKMARK_EDITOR_VIEW_H__
+
+#include <set>
+
+#include "chrome/views/tree_node_model.h"
+#include "chrome/browser/bookmark_bar_model.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/menu.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/text_field.h"
+
+namespace ChromeViews {
+class Window;
+}
+
+class GURL;
+class Menu;
+class Profile;
+
+// View that allows the user to edit a bookmark/starred URL. The user can
+// change the URL, title and where the bookmark appears as well as adding
+// new groups and changing the name of other groups.
+//
+// Edits are applied to the BookmarkBarModel when the user presses 'OK'.
+//
+// To use BookmarkEditorView invoke the static show method.
+
+class BookmarkEditorView : public ChromeViews::View,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::TreeViewController,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::TextField::Controller,
+ public ChromeViews::ContextMenuController,
+ public Menu::Delegate,
+ public BookmarkBarModelObserver {
+ public:
+ // Shows the BookmarkEditorView editing the specified entry.
+ static void Show(HWND parent_window,
+ Profile* profile,
+ const GURL& url,
+ const std::wstring& title);
+
+ BookmarkEditorView(Profile* profile,
+ const GURL& url,
+ const std::wstring& title);
+
+ virtual ~BookmarkEditorView();
+
+ // DialogDelegate methods:
+ virtual bool IsDialogButtonEnabled(DialogButton button) const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Accept();
+ virtual bool AreAcceleratorsEnabled(DialogButton button);
+
+ // View methods.
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize *out);
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+ virtual void ViewHierarchyChanged(bool is_add, ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ // TreeViewObserver methods.
+ virtual void OnTreeViewSelectionChanged(ChromeViews::TreeView* tree_view);
+ virtual bool CanEdit(ChromeViews::TreeView* tree_view,
+ ChromeViews::TreeModelNode* node);
+
+ // TextField::Controller methods.
+ virtual void ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key, UINT repeat_count,
+ UINT flags) {}
+
+ // NativeButton/CheckBox.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // Menu::Delegate method.
+ virtual void ExecuteCommand(int id);
+
+ // Menu::Delegate method, return false if id is edit and the bookmark node
+ // was selected, true otherwise.
+ virtual bool IsCommandEnabled(int id) const;
+
+ // Creates a Window and adds the BookmarkEditorView to it. When the window is
+ // closed the BookmarkEditorView is deleted.
+ void Show(HWND parent_hwnd);
+
+ // Closes the dialog.
+ void Close();
+
+ // Shows the context menu.
+ virtual void ShowContextMenu(View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture);
+
+ private:
+ // Type of node in the tree.
+ typedef ChromeViews::TreeNodeWithValue<history::UIStarID> BookmarkNode;
+
+ // Model for the TreeView. Trivial subclass that doesn't allow titles with
+ // empty strings.
+ class BookmarkTreeModel : public ChromeViews::TreeNodeModel<BookmarkNode> {
+ public:
+ explicit BookmarkTreeModel(BookmarkNode* root)
+ : TreeNodeModel<BookmarkNode>(root) {}
+
+ virtual void SetTitle(ChromeViews::TreeModelNode* node,
+ const std::wstring& title) {
+ if (!title.empty())
+ TreeNodeModel::SetTitle(node, title);
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(BookmarkTreeModel);
+ };
+
+ // Creates the necessary sub-views, configures them, adds them to the layout,
+ // and requests the entries to display from the database.
+ void Init();
+
+ // BookmarkBarModel observer methods. Any structural change results in
+ // resetting the tree model.
+ virtual void Loaded(BookmarkBarModel* model);
+ virtual void BookmarkNodeMoved(BookmarkBarModel* model,
+ BookmarkBarNode* old_parent,
+ int old_index,
+ BookmarkBarNode* new_parent,
+ int new_index);
+ virtual void BookmarkNodeAdded(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index);
+ virtual void BookmarkNodeRemoved(BookmarkBarModel* model,
+ BookmarkBarNode* parent,
+ int index);
+ virtual void BookmarkNodeChanged(BookmarkBarModel* model,
+ BookmarkBarNode* node) {}
+ virtual void BookmarkNodeFavIconLoaded(BookmarkBarModel* model,
+ BookmarkBarNode* node) {}
+
+ // Resets the model of the tree and updates the various buttons appropriately.
+ // If first_time is true, Reset is being invoked from the constructor or
+ // once the bookmark bar has finished loading.
+ void Reset(bool first_time);
+
+ // Expands all the nodes in the tree and selects the parent node of the
+ // url we're editing or the most recent parent if the url being editted isn't
+ // starred.
+ void ExpandAndSelect();
+
+ // Creates a returns the new root node. This invokes CreateNodes to do
+ // the real work.
+ BookmarkNode* CreateRootNode();
+
+ // Adds and creates a child node in b_node for all children of bb_node that
+ // are groups.
+ void CreateNodes(BookmarkBarNode* bb_node,
+ BookmarkNode* b_node);
+
+ // Returns the node with the specified id, or NULL if one can't be found.
+ BookmarkNode* FindNodeWithID(BookmarkEditorView::BookmarkNode* node,
+ history::UIStarID id);
+
+ // Applies the edits done by the user.
+ void ApplyEdits();
+
+ // Recursively adds newly created groups and sets the title of nodes to
+ // match the user edited title.
+ //
+ // bb_node gives the BookmarkBarNode the edits are to be applied to,
+ // with b_node the source of the edits.
+ //
+ // If b_node == parent_b_node, parent_bb_node is set to bb_node. This is
+ // used to determine the new BookmarkBarNode parent based on the BookmarkNode
+ // parent.
+ void ApplyNameChangesAndCreateNewGroups(
+ BookmarkBarNode* bb_node,
+ BookmarkEditorView::BookmarkNode* b_node,
+ BookmarkEditorView::BookmarkNode* parent_b_node,
+ BookmarkBarNode** parent_bb_node);
+
+ // Returns the current url the user has input.
+ GURL GetInputURL() const;
+
+ // Returns the title the user has input.
+ std::wstring GetInputTitle() const;
+
+ // Invoked when the url or title has possibly changed. Updates the background
+ // of textfields and ok button appropriately.
+ void UserInputChanged();
+
+ // Creates a new group as a child of the selected node. If no node is
+ // selected, the new group is added as a child of the bookmark node. Starts
+ // editing on the new gorup as well.
+ void NewGroup();
+
+ // Profile the entry is from.
+ Profile* profile_;
+
+ // Model driving the TreeView.
+ scoped_ptr<BookmarkTreeModel> tree_model_;
+
+ // Displays star groups.
+ ChromeViews::TreeView tree_view_;
+
+ // Used to create a new group.
+ ChromeViews::NativeButton new_group_button_;
+
+ // Used for editing the URL.
+ ChromeViews::TextField url_tf_;
+
+ // Used for editing the title.
+ ChromeViews::TextField title_tf_;
+
+ // Dialog we're contained in.
+ ChromeViews::Window* dialog_;
+
+ // URL we were created with.
+ const GURL url_;
+
+ // The context menu.
+ scoped_ptr<Menu> context_menu_;
+
+ // Title of the url to display.
+ std::wstring title_;
+
+ // Mode used to create nodes from.
+ BookmarkBarModel* bb_model_;
+
+ // If true, we're running the menu for the bookmark bar or other bookmarks
+ // nodes.
+ bool running_menu_for_root_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BookmarkEditorView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BOOKMARK_EDITOR_VIEW_H__
diff --git a/chrome/browser/views/bug_report_view.cc b/chrome/browser/views/bug_report_view.cc
new file mode 100644
index 0000000..2977409
--- /dev/null
+++ b/chrome/browser/views/bug_report_view.cc
@@ -0,0 +1,510 @@
+// 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/views/bug_report_view.h"
+
+#include <iostream>
+#include <fstream>
+
+#include "base/string_util.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/navigation_controller.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/safe_browsing/safe_browsing_util.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/url_fetcher.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/client_view.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/Window.h"
+#include "generated_resources.h"
+#include "net/base/escape.h"
+#include "unicode/locid.h"
+
+using ChromeViews::ColumnSet;
+using ChromeViews::GridLayout;
+
+// Report a bug data version
+static const int kBugReportVersion = 1;
+
+// Number of lines description field can display at one time.
+static const int kDescriptionLines = 5;
+
+// Google's phishing reporting URL.
+static const char kReportPhishingUrl[] =
+ "http://www.google.com/safebrowsing/report_phish/";
+
+class BugReportComboBoxModel : public ChromeViews::ComboBox::Model {
+ public:
+ BugReportComboBoxModel() {}
+
+ enum BugType {
+ PAGE_WONT_LOAD = 0,
+ PAGE_LOOKS_ODD,
+ PHISHING_PAGE,
+ CANT_SIGN_IN,
+ CHROME_MISBEHAVES,
+ SOMETHING_MISSING,
+ BROWSER_CRASH,
+ OTHER_PROBLEM
+ };
+
+ // ChromeViews::ComboBox::Model interface.
+ virtual int GetItemCount(ChromeViews::ComboBox* source) {
+ return OTHER_PROBLEM + 1;
+ }
+
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index) {
+ return GetItemAtIndex(index);
+ }
+
+ static std::wstring GetItemAtIndex(int index) {
+ switch (index) {
+ case PAGE_WONT_LOAD:
+ return l10n_util::GetString(IDS_BUGREPORT_PAGE_WONT_LOAD);
+ case PAGE_LOOKS_ODD:
+ return l10n_util::GetString(IDS_BUGREPORT_PAGE_LOOKS_ODD);
+ case PHISHING_PAGE:
+ return l10n_util::GetString(IDS_BUGREPORT_PHISHING_PAGE);
+ case CANT_SIGN_IN:
+ return l10n_util::GetString(IDS_BUGREPORT_CANT_SIGN_IN);
+ case CHROME_MISBEHAVES:
+ return l10n_util::GetString(IDS_BUGREPORT_CHROME_MISBEHAVES);
+ case SOMETHING_MISSING:
+ return l10n_util::GetString(IDS_BUGREPORT_SOMETHING_MISSING);
+ case BROWSER_CRASH:
+ return l10n_util::GetString(IDS_BUGREPORT_BROWSER_CRASH);
+ case OTHER_PROBLEM:
+ return l10n_util::GetString(IDS_BUGREPORT_OTHER_PROBLEM);
+ default:
+ NOTREACHED();
+ return std::wstring();
+ }
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(BugReportComboBoxModel);
+};
+
+// Simple URLFetcher::Delegate to clean up URLFetcher on completion
+// (since the BugReportView will be gone by then)
+class BugReportView::PostCleanup : public URLFetcher::Delegate {
+ public:
+ PostCleanup();
+ // Overridden from URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(PostCleanup);
+};
+
+BugReportView::PostCleanup::PostCleanup() {
+}
+
+void BugReportView::PostCleanup::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ // delete the URLFetcher
+ delete source;
+ // and then delete ourselves
+ delete this;
+}
+
+// BugReportView - create and submit a bug report from the user.
+// This is separate from crash reporting, which is handled by Breakpad.
+//
+BugReportView::BugReportView(Profile* profile, TabContents* tab)
+ : dialog_(NULL),
+ include_page_source_checkbox_(NULL),
+ include_page_image_checkbox_(NULL),
+ profile_(profile),
+ post_url_(l10n_util::GetString(IDS_BUGREPORT_POST_URL)),
+ tab_(tab),
+ problem_type_(0) {
+ DCHECK(profile);
+ SetupControl();
+}
+
+BugReportView::~BugReportView() {
+}
+
+void BugReportView::SetupControl() {
+ bug_type_model_.reset(new BugReportComboBoxModel);
+
+ // Adds all controls.
+ bug_type_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_BUGREPORT_BUG_TYPE));
+ bug_type_combo_ = new ChromeViews::ComboBox(bug_type_model_.get());
+ bug_type_combo_->SetListener(this);
+
+ page_title_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_BUGREPORT_REPORT_PAGE_TITLE));
+ page_title_text_ = new ChromeViews::Label(tab_->GetTitle());
+ page_url_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_BUGREPORT_REPORT_URL_LABEL));
+ // page_url_text_'s text (if any) is filled in after dialog creation
+ page_url_text_ = new ChromeViews::TextField;
+ page_url_text_->SetController(this);
+
+ description_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_BUGREPORT_DESCRIPTION_LABEL));
+ description_text_ =
+ new ChromeViews::TextField(ChromeViews::TextField::STYLE_MULTILINE);
+ description_text_->SetHeightInLines(kDescriptionLines);
+
+ include_page_source_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_BUGREPORT_INCLUDE_PAGE_SOURCE_CHKBOX));
+ include_page_source_checkbox_->SetIsSelected(true);
+ include_page_image_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_BUGREPORT_INCLUDE_PAGE_IMAGE_CHKBOX));
+ include_page_image_checkbox_->SetIsSelected(true);
+
+ // Arranges controls by using GridLayout.
+ const int column_set_id = 0;
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+ ColumnSet* column_set = layout->AddColumnSet(column_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing * 2);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ // Page Title and text.
+ layout->StartRow(0, column_set_id);
+ layout->AddView(page_title_label_);
+ layout->AddView(page_title_text_, 1, 1, GridLayout::LEADING,
+ GridLayout::FILL);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Bug type and combo box.
+ layout->StartRow(0, column_set_id);
+ layout->AddView(bug_type_label_, 1, 1, GridLayout::LEADING, GridLayout::FILL);
+ layout->AddView(bug_type_combo_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Page URL and text field.
+ layout->StartRow(0, column_set_id);
+ layout->AddView(page_url_label_);
+ layout->AddView(page_url_text_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Description label and text field.
+ layout->StartRow(0, column_set_id);
+ layout->AddView(description_label_, 1, 1, GridLayout::LEADING,
+ GridLayout::LEADING);
+ layout->AddView(description_text_, 1, 1, GridLayout::FILL,
+ GridLayout::LEADING);
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+
+ // Checkboxes.
+ // The include page source checkbox is hidden until we can make it work.
+ // layout->StartRow(0, column_set_id);
+ // layout->SkipColumns(1);
+ // layout->AddView(include_page_source_checkbox_);
+ // layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, column_set_id);
+ layout->SkipColumns(1);
+ layout->AddView(include_page_image_checkbox_);
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+}
+
+void BugReportView::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_BUGREPORT_DIALOG_WIDTH_CHARS,
+ IDS_BUGREPORT_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void BugReportView::ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index,
+ int new_index) {
+ if (new_index == prev_index)
+ return;
+
+ problem_type_ = new_index;
+ bool is_phishing_report = new_index == BugReportComboBoxModel::PHISHING_PAGE;
+
+ description_text_->SetEnabled(!is_phishing_report);
+ description_text_->SetReadOnly(is_phishing_report);
+ if (is_phishing_report) {
+ old_report_text_ = description_text_->GetText();
+ description_text_->SetText(std::wstring());
+ } else if (!old_report_text_.empty()) {
+ description_text_->SetText(old_report_text_);
+ old_report_text_.clear();
+ }
+ include_page_source_checkbox_->SetEnabled(!is_phishing_report);
+ include_page_source_checkbox_->SetIsSelected(!is_phishing_report);
+ include_page_image_checkbox_->SetEnabled(!is_phishing_report);
+ include_page_image_checkbox_->SetIsSelected(!is_phishing_report);
+
+ dialog_->UpdateDialogButtons();
+}
+
+void BugReportView::ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents) {
+}
+
+void BugReportView::HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key,
+ UINT repeat_count, UINT flags) {
+}
+
+std::wstring BugReportView::GetDialogButtonLabel(DialogButton button) const {
+ if (button == DIALOGBUTTON_OK) {
+ if (problem_type_ == BugReportComboBoxModel::PHISHING_PAGE)
+ return l10n_util::GetString(IDS_BUGREPORT_SEND_PHISHING_REPORT);
+ else
+ return l10n_util::GetString(IDS_BUGREPORT_SEND_REPORT);
+ } else {
+ return std::wstring();
+ }
+}
+
+int BugReportView::GetDefaultDialogButton() const {
+ return DIALOGBUTTON_NONE;
+}
+
+bool BugReportView::CanResize() const {
+ return false;
+}
+
+bool BugReportView::CanMaximize() const {
+ return false;
+}
+
+bool BugReportView::IsAlwaysOnTop() const {
+ return false;
+}
+
+bool BugReportView::HasAlwaysOnTopMenu() const {
+ return false;
+}
+
+bool BugReportView::IsModal() const {
+ return true;
+}
+
+std::wstring BugReportView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_BUGREPORT_TITLE);
+}
+
+bool BugReportView::Accept() {
+ if (IsDialogButtonEnabled(DIALOGBUTTON_OK)) {
+ if (problem_type_ == BugReportComboBoxModel::PHISHING_PAGE)
+ ReportPhishing();
+ else
+ SendReport();
+ }
+ return true;
+}
+
+void BugReportView::SetUrl(const GURL& url) {
+ page_url_text_->SetText(UTF8ToWide(url.spec()));
+}
+
+// SetOSVersion copies the maj.minor.build + servicePack_string
+// into a string (for Windows only). This should probably be
+// in a util somewhere. We currently have
+// win_util::GetWinVersion returns WinVersion, which is just
+// an enum of 2000, XP, 2003, or VISTA. Not enough detail for
+// bug reports
+// env_util::GetOperatingSystemVersion returns an std::string
+// but doesn't include the build or service pack. That function
+// is probably the right one to extend, but will require changing
+// all the call sites or making it a wrapper around another util.
+void BugReportView::SetOSVersion(std::string *os_version) {
+ OSVERSIONINFO osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ if (GetVersionEx(&osvi)) {
+ *os_version = StringPrintf("%d.%d.%d %S",
+ osvi.dwMajorVersion,
+ osvi.dwMinorVersion,
+ osvi.dwBuildNumber,
+ osvi.szCSDVersion);
+ } else {
+ *os_version = "unknown";
+ }
+}
+
+// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits)
+void BugReportView::CreateMimeBoundary(std::string *out) {
+ int r1 = rand();
+ int r2 = rand();
+ SStringPrintf(out, "---------------------------%08X%08X", r1, r2);
+}
+
+void BugReportView::SendReport() {
+ std::wstring post_url = l10n_util::GetString(IDS_BUGREPORT_POST_URL);
+ std::string mime_boundary;
+ CreateMimeBoundary(&mime_boundary);
+
+ // create a request body and add the mandatory parameters
+ std::string post_body;
+
+ // If this is an internal Google user, include user name for followup
+ // TODO: revisit for public release
+
+ if (!strcmp(getenv("USERDOMAIN"), "GOOGLE") && getenv("USERNAME")) {
+ std::string user_name(getenv("USERNAME"));
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"username\"\r\n\r\n");
+ post_body.append(user_name + "\r\n");
+ }
+
+ // Add the protocol version:
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"data_version\"\r\n\r\n");
+ post_body.append(StringPrintf("%d\r\n", kBugReportVersion));
+
+ // Add the page title
+ post_body.append("--" + mime_boundary + "\r\n");
+ std::string page_title = WideToUTF8(page_title_text_->GetText());
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"title\"\r\n\r\n");
+ post_body.append(page_title + "\r\n");
+
+ // Add the problem type
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"problem\"\r\n\r\n");
+ post_body.append(StringPrintf("%d\r\n", problem_type_));
+
+ // Add in the URL, if we have one
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"url\"\r\n\r\n");
+
+ // convert URL to UTF8
+ std::string report_url = WideToUTF8(page_url_text_->GetText());
+ if (report_url.empty()) {
+ post_body.append("n/a\r\n");
+ } else {
+ post_body.append(report_url + "\r\n");
+ }
+
+ // Add Chrome version
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"chrome_version\"\r\n\r\n");
+
+ std::string version = WideToUTF8(version_);
+ if (version.empty()) {
+ post_body.append("n/a\r\n");
+ } else {
+ post_body.append(version + "\r\n");
+ }
+
+ // Add OS version (eg, for WinXP SP2: "5.1.2600 Service Pack 2")
+ std::string os_version = "";
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"os_version\"\r\n\r\n");
+ SetOSVersion(&os_version);
+ post_body.append(os_version + "\r\n");
+
+ // Add locale
+ Locale locale = Locale::getDefault();
+ const char *lang = locale.getLanguage();
+ std::string chrome_locale = (lang)? lang:"en";
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"chrome_locale\"\r\n\r\n");
+ post_body.append(chrome_locale + "\r\n");
+
+ // Add a description if we have one
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; "
+ "name=\"description\"\r\n\r\n");
+
+ std::string description = WideToUTF8(description_text_->GetText());
+ if (description.empty()) {
+ post_body.append("n/a\r\n");
+ } else {
+ post_body.append(description + "\r\n");
+ }
+
+ // include the page image if we have one
+ if (include_page_image_checkbox_->IsSelected() && png_data_.get()) {
+ post_body.append("--" + mime_boundary + "\r\n");
+ post_body.append("Content-Disposition: form-data; name=\"screenshot\"; "
+ "filename=\"screenshot.png\"\r\n");
+ post_body.append("Content-Type: application/octet-stream\r\n");
+ post_body.append(StringPrintf("Content-Length: %lu\r\n\r\n",
+ png_data_->size()));
+ // the following relies on the fact that STL vectors are guaranteed to
+ // be stored contiguously.
+ post_body.append(reinterpret_cast<const char *>(&((*png_data_)[0])),
+ png_data_->size());
+ post_body.append("\r\n");
+ }
+
+ // TODO(awalker): include the page source if we can get it
+ if (include_page_source_checkbox_->IsSelected()) {
+ }
+
+ // terminate the body
+ post_body.append("--" + mime_boundary + "--\r\n");
+
+ // We have the body of our POST, so send it off to the server
+
+ URLFetcher* fetcher = new URLFetcher(post_url_, URLFetcher::POST,
+ new BugReportView::PostCleanup);
+ fetcher->set_request_context(profile_->GetRequestContext());
+ std::string mime_type("multipart/form-data; boundary=");
+ mime_type += mime_boundary;
+ fetcher->set_upload_data(mime_type, post_body);
+ fetcher->Start();
+}
+
+void BugReportView::ReportPhishing() {
+ tab_->controller()->LoadURL(
+ safe_browsing_util::GeneratePhishingReportUrl(
+ kReportPhishingUrl, WideToNativeMB(page_url_text_->GetText())),
+ PageTransition::LINK);
+}
diff --git a/chrome/browser/views/bug_report_view.h b/chrome/browser/views/bug_report_view.h
new file mode 100644
index 0000000..f50e63c
--- /dev/null
+++ b/chrome/browser/views/bug_report_view.h
@@ -0,0 +1,154 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BUGREPORT_VIEW_H__
+#define CHROME_BROWSER_VIEWS_BUGREPORT_VIEW_H__
+
+#include "chrome/browser/url_fetcher.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+
+class CheckBox;
+class Label;
+class Throbber;
+class Window;
+
+}
+
+class Profile;
+class TabContents;
+class BugReportComboBoxModel;
+
+// BugReportView draws the dialog that allows the user to report a
+// bug in rendering a particular page (note: this is not a crash
+// report, which are handled separately by Breakpad). It packages
+// up the URL, a text description, and optionally a screenshot and/or
+// the HTML page source, and submits them as an HTTP POST to the
+// URL stored in the string resource IDS_BUGREPORT_POST_URL.
+//
+// Note: The UI team hasn't defined yet how the bug report UI will look like.
+// So now use dialog as a placeholder.
+class BugReportView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::ComboBox::Listener,
+ public ChromeViews::TextField::Controller {
+ public:
+ explicit BugReportView(Profile* profile, TabContents* tab);
+ virtual ~BugReportView();
+
+ void set_dialog(ChromeViews::Window* dialog) { dialog_ = dialog; }
+ void set_version(const std::wstring& version) { version_ = version; }
+ // NOTE: set_png_data takes ownership of the vector
+ void set_png_data(std::vector<unsigned char> *png_data) {
+ png_data_.reset(png_data);
+ };
+
+ // Overridden from ChromeViews::View:
+ virtual void GetPreferredSize(CSize *out);
+
+ // ChromeViews::TextField::Controller implementation:
+ virtual void ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key,
+ UINT repeat_count, UINT flags);
+
+ // ChromeViews::ComboBox::Listener implementation:
+ virtual void ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index, int new_index);
+
+ // Overridden from ChromeViews::DialogDelegate:
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual int GetDefaultDialogButton() const;
+ virtual bool CanResize() const;
+ virtual bool CanMaximize() const;
+ virtual bool IsAlwaysOnTop() const;
+ virtual bool HasAlwaysOnTopMenu() const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Accept();
+
+ void SetUrl(const GURL& url);
+
+ private:
+ class PostCleanup;
+
+ // Set OS Version information in a string (maj.minor.build SP)
+ void SetOSVersion(std::string *os_version);
+
+ // Initializes the controls on the dialog.
+ void SetupControl();
+ // helper function to create a MIME part boundary string
+ void CreateMimeBoundary(std::string *out);
+ // Sends the data via an HTTP POST
+ void SendReport();
+
+ // Redirects the user to Google's phishing reporting page.
+ void ReportPhishing();
+
+ ChromeViews::Label* bug_type_label_;
+ ChromeViews::ComboBox* bug_type_combo_;
+ ChromeViews::Label* page_title_label_;
+ ChromeViews::Label* page_title_text_;
+ ChromeViews::Label* page_url_label_;
+ ChromeViews::TextField* page_url_text_;
+ ChromeViews::Label* description_label_;
+ ChromeViews::TextField* description_text_;
+ ChromeViews::CheckBox* include_page_source_checkbox_;
+ ChromeViews::CheckBox* include_page_image_checkbox_;
+
+ scoped_ptr<BugReportComboBoxModel> bug_type_model_;
+ ChromeViews::Window* dialog_;
+
+ Profile* profile_;
+
+ std::wstring version_;
+ scoped_ptr< std::vector<unsigned char> > png_data_;
+
+ GURL post_url_;
+
+ TabContents* tab_;
+
+ // Used to distinguish the report type: Phishing or other.
+ int problem_type_;
+
+ // Save the description the user types in when we clear the dialog for the
+ // phishing option. If the user changes the report type back, we reinstate
+ // their original text so they don't have to type it again.
+ std::wstring old_report_text_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BugReportView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BUGREPORT_VIEW_H__
diff --git a/chrome/browser/views/clear_browsing_data.cc b/chrome/browser/views/clear_browsing_data.cc
new file mode 100644
index 0000000..17341cd
--- /dev/null
+++ b/chrome/browser/views/clear_browsing_data.cc
@@ -0,0 +1,403 @@
+// 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/views/clear_browsing_data.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/background.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/label.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/throbber.h"
+#include "chrome/views/window.h"
+#include "net/url_request/url_request_context.h"
+
+#include "generated_resources.h"
+
+// The combo box is vertically aligned to the 'time-period' label, which makes
+// the combo box look a little too close to the check box above it when we use
+// standard layout to separate them. We therefore add a little extra margin to
+// the label, giving it a little breathing space.
+static const int kExtraMarginForTimePeriodLabel = 3;
+
+////////////////////////////////////////////////////////////////////////////////
+// ClearBrowsingDataView, public:
+
+ClearBrowsingDataView::ClearBrowsingDataView(Profile* profile)
+ : dialog_(NULL),
+ del_history_checkbox_(NULL),
+ del_downloads_checkbox_(NULL),
+ del_cache_checkbox_(NULL),
+ del_cookies_checkbox_(NULL),
+ del_passwords_checkbox_(NULL),
+ time_period_label_(NULL),
+ time_period_combobox_(NULL),
+ delete_in_progress_(false),
+ profile_(profile) {
+ DCHECK(profile);
+ Init();
+}
+
+ClearBrowsingDataView::~ClearBrowsingDataView(void) {
+}
+
+void ClearBrowsingDataView::Init() {
+ // Views we will add to the *parent* of this dialog, since it will display
+ // next to the buttons which we don't draw ourselves.
+ throbber_.reset(new ChromeViews::Throbber(50, true));
+ throbber_->SetParentOwned(false);
+ throbber_->SetVisible(false);
+
+ status_label_.SetText(l10n_util::GetString(IDS_CLEAR_DATA_DELETING));
+ status_label_.SetVisible(false);
+ status_label_.SetParentOwned(false);
+
+ // Regular view controls we draw by ourself. First, we add the dialog label.
+ delete_all_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_LABEL));
+ AddChildView(delete_all_label_);
+
+ // Add all the check-boxes.
+ del_history_checkbox_ =
+ AddCheckbox(l10n_util::GetString(IDS_DEL_BROWSING_HISTORY_CHKBOX), true);
+
+ del_downloads_checkbox_ =
+ AddCheckbox(l10n_util::GetString(IDS_DEL_DOWNLOAD_HISTORY_CHKBOX), true);
+
+ del_cache_checkbox_ =
+ AddCheckbox(l10n_util::GetString(IDS_DEL_CACHE_CHKBOX), true);
+
+ del_cookies_checkbox_ =
+ AddCheckbox(l10n_util::GetString(IDS_DEL_COOKIES_CHKBOX), true);
+
+ del_passwords_checkbox_ =
+ AddCheckbox(l10n_util::GetString(IDS_DEL_PASSWORDS_CHKBOX), false);
+
+ // Add a label which appears before the combo box for the time period.
+ time_period_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_TIME_LABEL));
+ AddChildView(time_period_label_);
+
+ // Add the combo box showing how far back in time we want to delete.
+ time_period_combobox_ = new ChromeViews::ComboBox(this);
+ AddChildView(time_period_combobox_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ClearBrowsingDataView, ChromeViews::View implementation:
+
+void ClearBrowsingDataView::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_CLEARDATA_DIALOG_WIDTH_CHARS,
+ IDS_CLEARDATA_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void ClearBrowsingDataView::Layout() {
+ CSize panel_size;
+ GetPreferredSize(&panel_size);
+
+ CSize sz;
+
+ // Delete All label goes to the top left corner.
+ delete_all_label_->GetPreferredSize(&sz);
+ delete_all_label_->SetBounds(kPanelHorizMargin, kPanelVertMargin,
+ sz.cx, sz.cy);
+
+ // Check-boxes go beneath it (with a little indentation).
+ del_history_checkbox_->GetPreferredSize(&sz);
+ del_history_checkbox_->SetBounds(2 * kPanelHorizMargin,
+ delete_all_label_->GetY() +
+ delete_all_label_->GetHeight() +
+ kRelatedControlVerticalSpacing,
+ sz.cx, sz.cy);
+
+ del_downloads_checkbox_->GetPreferredSize(&sz);
+ del_downloads_checkbox_->SetBounds(2 * kPanelHorizMargin,
+ del_history_checkbox_->GetY() +
+ del_history_checkbox_->GetHeight() +
+ kRelatedControlVerticalSpacing,
+ sz.cx, sz.cy);
+
+ del_cache_checkbox_->GetPreferredSize(&sz);
+ del_cache_checkbox_->SetBounds(2 * kPanelHorizMargin,
+ del_downloads_checkbox_->GetY() +
+ del_downloads_checkbox_->GetHeight() +
+ kRelatedControlVerticalSpacing,
+ sz.cx, sz.cy);
+
+ del_cookies_checkbox_->GetPreferredSize(&sz);
+ del_cookies_checkbox_->SetBounds(2 * kPanelHorizMargin,
+ del_cache_checkbox_->GetY() +
+ del_cache_checkbox_->GetHeight() +
+ kRelatedControlVerticalSpacing,
+ sz.cx, sz.cy);
+
+ del_passwords_checkbox_->GetPreferredSize(&sz);
+ del_passwords_checkbox_->SetBounds(2 * kPanelHorizMargin,
+ del_cookies_checkbox_->GetY() +
+ del_cookies_checkbox_->GetHeight() +
+ kRelatedControlVerticalSpacing,
+ sz.cx, sz.cy);
+
+ // Time period label is next below the combo boxes.
+ time_period_label_->GetPreferredSize(&sz);
+ time_period_label_->SetBounds(kPanelHorizMargin,
+ del_passwords_checkbox_->GetY() +
+ del_passwords_checkbox_->GetHeight() +
+ kRelatedControlVerticalSpacing +
+ kExtraMarginForTimePeriodLabel,
+ sz.cx, sz.cy);
+
+ // Time period combo box goes on the right of the label, and we align it
+ // vertically to the label as well.
+ int label_y_size = sz.cy;
+ time_period_combobox_->GetPreferredSize(&sz);
+ time_period_combobox_->SetBounds(time_period_label_->GetX() +
+ time_period_label_->GetWidth() +
+ kRelatedControlVerticalSpacing,
+ time_period_label_->GetY() -
+ ((sz.cy - label_y_size) / 2),
+ sz.cx, sz.cy);
+
+ // Get the y-coordinate of our parent so we can position the throbber and
+ // status message at the bottom of the panel.
+ CRect parent_bounds;
+ GetParent()->GetLocalBounds(&parent_bounds, false);
+
+ throbber_->GetPreferredSize(&sz);
+ int throbber_topleft_x = kPanelHorizMargin;
+ int throbber_topleft_y = parent_bounds.bottom - sz.cy -
+ kButtonVEdgeMargin - 3;
+ throbber_->SetBounds(throbber_topleft_x, throbber_topleft_y, sz.cx, sz.cy);
+
+ // The status label should be at the bottom of the screen, to the right of
+ // the throbber.
+ status_label_.GetPreferredSize(&sz);
+ int status_label_x = throbber_->GetX() + throbber_->GetWidth() +
+ kRelatedControlHorizontalSpacing;
+ status_label_.SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ status_label_.SetBounds(status_label_x,
+ throbber_topleft_y + 1,
+ sz.cx,
+ sz.cy);
+}
+
+void ClearBrowsingDataView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ // Since we want the some of the controls to show up in the same visual row
+ // as the buttons, which are provided by the framework, we must add the
+ // buttons to the non-client view, which is the parent of this view.
+ // Similarly, when we're removed from the view hierarchy, we must take care
+ // to remove these items as well.
+ if (child == this) {
+ if (is_add) {
+ parent->AddChildView(&status_label_);
+ parent->AddChildView(throbber_.get());
+ } else {
+ parent->RemoveChildView(&status_label_);
+ parent->RemoveChildView(throbber_.get());
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ClearBrowsingDataView, ChromeViews::DialogDelegate implementation:
+
+std::wstring ClearBrowsingDataView::GetDialogButtonLabel(
+ DialogButton button) const {
+ if (button == DIALOGBUTTON_OK) {
+ return l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_COMMIT);
+ } else {
+ return std::wstring();
+ }
+}
+
+bool ClearBrowsingDataView::IsDialogButtonEnabled(DialogButton button) const {
+ if (delete_in_progress_)
+ return false;
+
+ if (button == DIALOGBUTTON_OK) {
+ return del_history_checkbox_->IsSelected() ||
+ del_downloads_checkbox_->IsSelected() ||
+ del_cache_checkbox_->IsSelected() ||
+ del_cookies_checkbox_->IsSelected() ||
+ del_passwords_checkbox_->IsSelected();
+ }
+
+ return true;
+}
+
+bool ClearBrowsingDataView::CanResize() const {
+ return false;
+}
+
+bool ClearBrowsingDataView::CanMaximize() const {
+ return false;
+}
+
+bool ClearBrowsingDataView::IsAlwaysOnTop() const {
+ return false;
+}
+
+bool ClearBrowsingDataView::HasAlwaysOnTopMenu() const {
+ return false;
+}
+
+bool ClearBrowsingDataView::IsModal() const {
+ return true;
+}
+
+std::wstring ClearBrowsingDataView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_TITLE);
+}
+
+bool ClearBrowsingDataView::Accept() {
+ if (!IsDialogButtonEnabled(DIALOGBUTTON_OK)) {
+ return false;
+ }
+
+ OnDelete();
+ return false; // We close the dialog in OnDeletionDone().
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ClearBrowsingDataView, ChromeViews::ComboBox::Model implementation:
+
+int ClearBrowsingDataView::GetItemCount(ChromeViews::ComboBox* source) {
+ DCHECK(source == time_period_combobox_);
+ return 4;
+}
+
+std::wstring ClearBrowsingDataView::GetItemAt(ChromeViews::ComboBox* source,
+ int index) {
+ DCHECK(source == time_period_combobox_);
+ switch (index) {
+ case 0: return l10n_util::GetString(IDS_CLEAR_DATA_DAY);
+ case 1: return l10n_util::GetString(IDS_CLEAR_DATA_WEEK);
+ case 2: return l10n_util::GetString(IDS_CLEAR_DATA_4WEEKS);
+ case 3: return l10n_util::GetString(IDS_CLEAR_DATA_EVERYTHING);
+ default: NOTREACHED() << L"Missing item";
+ return L"?";
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ClearBrowsingDataView, ChromeViews::ButtonListener implementation:
+
+void ClearBrowsingDataView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ // When no checkbox is checked we should not have the action button enabled.
+ // This forces the button to evaluate what state they should be in.
+ dialog_->UpdateDialogButtons();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ClearBrowsingDataView, private:
+
+ChromeViews::CheckBox* ClearBrowsingDataView::AddCheckbox(
+ const std::wstring& text, bool checked) {
+ ChromeViews::CheckBox* checkbox = new ChromeViews::CheckBox(text);
+ checkbox->SetIsSelected(checked);
+ checkbox->SetListener(this);
+ AddChildView(checkbox);
+ return checkbox;
+}
+
+void ClearBrowsingDataView::UpdateControlEnabledState() {
+ dialog_->EnableClose(!delete_in_progress_);
+
+ del_history_checkbox_->SetEnabled(!delete_in_progress_);
+ del_downloads_checkbox_->SetEnabled(!delete_in_progress_);
+ del_cache_checkbox_->SetEnabled(!delete_in_progress_);
+ del_cookies_checkbox_->SetEnabled(!delete_in_progress_);
+ del_passwords_checkbox_->SetEnabled(!delete_in_progress_);
+ time_period_combobox_->SetEnabled(!delete_in_progress_);
+
+ status_label_.SetVisible(delete_in_progress_);
+ throbber_->SetVisible(delete_in_progress_);
+ if (delete_in_progress_)
+ throbber_->Start();
+ else
+ throbber_->Stop();
+
+ // Make sure to update the state for OK and Cancel buttons.
+ dialog_->UpdateDialogButtons();
+}
+
+// Convenience method that returns true if the supplied checkbox is selected
+// and enabled.
+static bool IsCheckBoxEnabledAndSelected(ChromeViews::CheckBox* cb) {
+ return (cb->IsEnabled() && cb->IsSelected());
+}
+
+void ClearBrowsingDataView::OnDelete() {
+ TimeDelta diff;
+ Time delete_begin = Time::Now();
+
+ int period_selected = time_period_combobox_->GetSelectedItem();
+ switch (period_selected) {
+ case 0: diff = TimeDelta::FromHours(24); break; // Last day.
+ case 1: diff = TimeDelta::FromHours(7*24); break; // Last week.
+ case 2: diff = TimeDelta::FromHours(4*7*24); break; // Four weeks.
+ case 3: delete_begin = Time(); break; // Everything.
+ default: NOTREACHED() << L"Missing item"; break;
+ }
+
+ delete_begin = delete_begin - diff;
+
+ int remove_mask = 0;
+ if (IsCheckBoxEnabledAndSelected(del_history_checkbox_))
+ remove_mask |= BrowsingDataRemover::REMOVE_HISTORY;
+ if (IsCheckBoxEnabledAndSelected(del_downloads_checkbox_))
+ remove_mask |= BrowsingDataRemover::REMOVE_DOWNLOADS;
+ if (IsCheckBoxEnabledAndSelected(del_cookies_checkbox_))
+ remove_mask |= BrowsingDataRemover::REMOVE_COOKIES;
+ if (IsCheckBoxEnabledAndSelected(del_passwords_checkbox_))
+ remove_mask |= BrowsingDataRemover::REMOVE_PASSWORDS;
+ if (IsCheckBoxEnabledAndSelected(del_cache_checkbox_))
+ remove_mask |= BrowsingDataRemover::REMOVE_CACHE;
+
+ delete_in_progress_ = true;
+ UpdateControlEnabledState();
+
+ // BrowsingDataRemover deletes itself when done.
+ BrowsingDataRemover* remover =
+ new BrowsingDataRemover(profile_, delete_begin, Time());
+ remover->AddObserver(this);
+ remover->Remove(remove_mask);
+}
+
+void ClearBrowsingDataView::OnBrowsingDataRemoverDone() {
+ dialog_->Close();
+}
diff --git a/chrome/browser/views/clear_browsing_data.h b/chrome/browser/views/clear_browsing_data.h
new file mode 100644
index 0000000..bb41ef5
--- /dev/null
+++ b/chrome/browser/views/clear_browsing_data.h
@@ -0,0 +1,135 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_CLEAR_BROWSING_DATA_H__
+#define CHROME_BROWSER_VIEWS_CLEAR_BROWSING_DATA_H__
+
+#include "base/time.h"
+#include "chrome/browser/browsing_data_remover.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/label.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+ class CheckBox;
+ class Label;
+ class Throbber;
+ class Window;
+}
+
+class Profile;
+class MessageLoop;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The ClearBrowsingData class is responsible for drawing the UI controls of the
+// dialog that allows the user to select what to delete (history, downloads,
+// etc).
+//
+////////////////////////////////////////////////////////////////////////////////
+class ClearBrowsingDataView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::ComboBox::Model,
+ public ChromeViews::NativeButton::Listener,
+ public BrowsingDataRemover::Observer {
+ public:
+ explicit ClearBrowsingDataView(Profile* profile);
+ virtual ~ClearBrowsingDataView(void);
+
+ // Initialize the controls on the dialog.
+ void Init();
+
+ void SetDialog(ChromeViews::Window* dialog) { dialog_ = dialog; }
+
+ // Overridden from ChromeViews::View:
+ virtual void GetPreferredSize(CSize *out);
+ virtual void Layout();
+ void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ // Overridden from ChromeViews::DialogDelegate:
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual bool IsDialogButtonEnabled(DialogButton button) const;
+ virtual bool CanResize() const;
+ virtual bool CanMaximize() const;
+ virtual bool IsAlwaysOnTop() const;
+ virtual bool HasAlwaysOnTopMenu() const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Accept();
+
+ // Overridden from ChromeViews::ComboBox::Model:
+ virtual int GetItemCount(ChromeViews::ComboBox* source);
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index);
+
+ // Overridden from ChromeViews::NativeButton::Listener:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ private:
+ // Adds a new check-box as a child to the view.
+ ChromeViews::CheckBox* AddCheckbox(const std::wstring& text, bool checked);
+
+ // Sets the controls on the UI to be enabled/disabled depending on whether we
+ // have a delete operation in progress or not.
+ void UpdateControlEnabledState();
+
+ // Starts the process of deleting the browsing data depending on what the
+ // user selected.
+ void OnDelete();
+
+ // Callback from BrowsingDataRemover. Closes the dialog.
+ virtual void OnBrowsingDataRemoverDone();
+
+ // UI elements we add to the parent view.
+ scoped_ptr<ChromeViews::Throbber> throbber_;
+ ChromeViews::Label status_label_;
+ // Other UI elements.
+ ChromeViews::Label* delete_all_label_;
+ ChromeViews::CheckBox* del_history_checkbox_;
+ ChromeViews::CheckBox* del_downloads_checkbox_;
+ ChromeViews::CheckBox* del_cache_checkbox_;
+ ChromeViews::CheckBox* del_cookies_checkbox_;
+ ChromeViews::CheckBox* del_passwords_checkbox_;
+ ChromeViews::Label* time_period_label_;
+ ChromeViews::ComboBox* time_period_combobox_;
+
+ // Used to signal enabled/disabled state for controls in the UI.
+ bool delete_in_progress_;
+
+ ChromeViews::Window* dialog_;
+
+ Profile* profile_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ClearBrowsingDataView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_CLEAR_BROWSING_DATA_H__
diff --git a/chrome/browser/views/constrained_window_animation.cc b/chrome/browser/views/constrained_window_animation.cc
new file mode 100644
index 0000000..72d69a0
--- /dev/null
+++ b/chrome/browser/views/constrained_window_animation.cc
@@ -0,0 +1,55 @@
+// 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/views/constrained_window_animation.h"
+#include "chrome/browser/views/constrained_window_impl.h"
+
+// The duration of the animation.
+static const int kDuration = 360;
+
+// The frame-rate for the animation.
+static const int kFrameRate = 60;
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowAnimation, public:
+
+ConstrainedWindowAnimation::ConstrainedWindowAnimation(
+ ConstrainedWindowImpl* window)
+ : Animation(kDuration, kFrameRate, NULL), window_(window) {
+}
+
+ConstrainedWindowAnimation::~ConstrainedWindowAnimation() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowAnimation, Animation implementation:
+
+void ConstrainedWindowAnimation::AnimateToState(double state) {
+ window_->SetTitlebarVisibilityPercentage(state);
+}
diff --git a/chrome/browser/views/constrained_window_animation.h b/chrome/browser/views/constrained_window_animation.h
new file mode 100644
index 0000000..02c3017
--- /dev/null
+++ b/chrome/browser/views/constrained_window_animation.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_CONSTRAINED_WINDOW_ANIMATION_H__
+#define CHROME_BROWSER_VIEWS_CONSTRAINED_WINDOW_ANIMATION_H__
+
+#include "chrome/common/animation.h"
+
+class ConstrainedWindowImpl;
+
+// Animates a titlebar of a suppressed constrained window up from the
+// bottom of the screen.
+class ConstrainedWindowAnimation : public Animation {
+ public:
+ explicit ConstrainedWindowAnimation(ConstrainedWindowImpl* window);
+ virtual ~ConstrainedWindowAnimation();
+
+ // Overridden from Animation:
+ virtual void AnimateToState(double state);
+
+ private:
+ // The constrained window we're displaying.
+ ConstrainedWindowImpl* window_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ConstrainedWindowAnimation);
+};
+
+#endif // CHROME_BROWSER_VIEWS_CONSTRAINED_WINDOW_ANIMATION_H__
diff --git a/chrome/browser/views/constrained_window_impl.cc b/chrome/browser/views/constrained_window_impl.cc
new file mode 100644
index 0000000..2e7f837
--- /dev/null
+++ b/chrome/browser/views/constrained_window_impl.cc
@@ -0,0 +1,1493 @@
+// 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/views/constrained_window_impl.h"
+
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/views/constrained_window_animation.h"
+#include "chrome/browser/views/location_bar_view.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/toolbar_model.h"
+#include "chrome/browser/web_app.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/browser/window_sizer.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/gfx/path.h"
+#include "chrome/common/gfx/url_elider.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/button.h"
+#include "chrome/views/focus_manager.h"
+#include "chrome/views/hwnd_view.h"
+#include "generated_resources.h"
+#include "net/base/net_util.h"
+
+namespace ChromeViews {
+class ClientView;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowResources
+
+// An enumeration of bitmap resources used by this window.
+enum FramePartBitmap {
+ FRAME_PART_BITMAP_FIRST = 0, // Must be first.
+
+ FRAME_BOTTOM_CENTER,
+ FRAME_BOTTOM_LEFT_CORNER,
+ FRAME_BOTTOM_RIGHT_CORNER,
+ FRAME_LEFT_SIDE,
+ FRAME_RIGHT_SIDE,
+ FRAME_TOP_CENTER,
+ FRAME_TOP_LEFT_CORNER,
+ FRAME_TOP_RIGHT_CORNER,
+
+ FRAME_CLOSE_BUTTON_ICON,
+ FRAME_CLOSE_BUTTON_ICON_H,
+ FRAME_CLOSE_BUTTON_ICON_P,
+
+ FRAME_PART_BITMAP_COUNT // Must be last.
+};
+
+static const int kXPFramePartIDs[] = {
+ 0, IDR_CONSTRAINED_BOTTOM_CENTER, IDR_CONSTRAINED_BOTTOM_LEFT_CORNER,
+ IDR_CONSTRAINED_BOTTOM_RIGHT_CORNER, IDR_CONSTRAINED_LEFT_SIDE,
+ IDR_CONSTRAINED_RIGHT_SIDE, IDR_CONSTRAINED_TOP_CENTER,
+ IDR_CONSTRAINED_TOP_LEFT_CORNER, IDR_CONSTRAINED_TOP_RIGHT_CORNER,
+ IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, 0 };
+static const int kVistaFramePartIDs[] = {
+ 0, IDR_CONSTRAINED_BOTTOM_CENTER_V, IDR_CONSTRAINED_BOTTOM_LEFT_CORNER_V,
+ IDR_CONSTRAINED_BOTTOM_RIGHT_CORNER_V, IDR_CONSTRAINED_LEFT_SIDE_V,
+ IDR_CONSTRAINED_RIGHT_SIDE_V, IDR_CONSTRAINED_TOP_CENTER_V,
+ IDR_CONSTRAINED_TOP_LEFT_CORNER_V, IDR_CONSTRAINED_TOP_RIGHT_CORNER_V,
+ IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, 0 };
+static const int kOTRFramePartIDs[] = {
+ 0, IDR_WINDOW_BOTTOM_CENTER_OTR, IDR_WINDOW_BOTTOM_LEFT_CORNER_OTR,
+ IDR_WINDOW_BOTTOM_RIGHT_CORNER_OTR, IDR_WINDOW_LEFT_SIDE_OTR,
+ IDR_WINDOW_RIGHT_SIDE_OTR, IDR_WINDOW_TOP_CENTER_OTR,
+ IDR_WINDOW_TOP_LEFT_CORNER_OTR, IDR_WINDOW_TOP_RIGHT_CORNER_OTR,
+ IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, 0 };
+
+class WindowResources {
+ public:
+ virtual ~WindowResources() {}
+ virtual SkBitmap* GetPartBitmap(FramePartBitmap part_id) const = 0;
+ virtual const ChromeFont& GetTitleFont() const = 0;
+ virtual SkColor GetTitleColor() const = 0;
+};
+
+class XPWindowResources : public WindowResources {
+ public:
+ XPWindowResources() {
+ InitClass();
+ }
+ virtual ~XPWindowResources() {}
+
+ virtual SkBitmap* GetPartBitmap(FramePartBitmap part_id) const {
+ return bitmaps_[part_id];
+ }
+ virtual const ChromeFont& GetTitleFont() const { return title_font_; }
+ virtual SkColor GetTitleColor() const { return SK_ColorWHITE; }
+
+ private:
+ static void InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) {
+ int id = kXPFramePartIDs[i];
+ if (id != 0)
+ bitmaps_[i] = rb.GetBitmapNamed(id);
+ }
+ title_font_ =
+ rb.GetFont(ResourceBundle::BaseFont).DeriveFont(1);
+ initialized = true;
+ }
+ }
+
+ static SkBitmap* bitmaps_[FRAME_PART_BITMAP_COUNT];
+ static ChromeFont title_font_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(XPWindowResources);
+};
+
+class VistaWindowResources : public WindowResources {
+ public:
+ VistaWindowResources() {
+ InitClass();
+ }
+ virtual ~VistaWindowResources() {}
+
+ virtual SkBitmap* GetPartBitmap(FramePartBitmap part_id) const {
+ return bitmaps_[part_id];
+ }
+ virtual const ChromeFont& GetTitleFont() const { return title_font_; }
+ virtual SkColor GetTitleColor() const { return SK_ColorBLACK; }
+
+ private:
+ static void InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) {
+ int id = kVistaFramePartIDs[i];
+ if (id != 0)
+ bitmaps_[i] = rb.GetBitmapNamed(id);
+ }
+ title_font_ =
+ rb.GetFont(ResourceBundle::BaseFont).DeriveFont(1);
+ initialized = true;
+ }
+ }
+
+ static SkBitmap* bitmaps_[FRAME_PART_BITMAP_COUNT];
+ static ChromeFont title_font_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(VistaWindowResources);
+};
+
+class OTRWindowResources : public WindowResources {
+ public:
+ OTRWindowResources() {
+ InitClass();
+ }
+ virtual ~OTRWindowResources() {}
+
+ virtual SkBitmap* GetPartBitmap(FramePartBitmap part_id) const {
+ return bitmaps_[part_id];
+ }
+ virtual const ChromeFont& GetTitleFont() const { return title_font_; }
+ virtual SkColor GetTitleColor() const { return SK_ColorWHITE; }
+
+ private:
+ static void InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) {
+ int id = kOTRFramePartIDs[i];
+ if (id != 0)
+ bitmaps_[i] = rb.GetBitmapNamed(id);
+ }
+ title_font_ =
+ rb.GetFont(ResourceBundle::BaseFont).DeriveFont(1);
+ initialized = true;
+ }
+ }
+
+ static SkBitmap* bitmaps_[FRAME_PART_BITMAP_COUNT];
+ static ChromeFont title_font_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OTRWindowResources);
+};
+
+SkBitmap* XPWindowResources::bitmaps_[];
+ChromeFont XPWindowResources::title_font_;
+SkBitmap* VistaWindowResources::bitmaps_[];
+ChromeFont VistaWindowResources::title_font_;
+SkBitmap* OTRWindowResources::bitmaps_[];
+ChromeFont OTRWindowResources::title_font_;
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowNonClientView
+
+class ConstrainedWindowNonClientView
+ : public ChromeViews::CustomFrameWindow::NonClientView,
+ public ChromeViews::BaseButton::ButtonListener,
+ public LocationBarView::Delegate,
+ public Task {
+ public:
+ ConstrainedWindowNonClientView(ConstrainedWindowImpl* container,
+ TabContents* owner);
+ virtual ~ConstrainedWindowNonClientView();
+
+ // Calculates the pixel height of the titlebar
+ int CalculateTitlebarHeight() const;
+
+ // Calculates the pixel height of all pieces of a window that are
+ // not part of the webcontent display area.
+ int CalculateNonClientHeight(bool with_url_field) const;
+ gfx::Rect CalculateWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds,
+ bool with_url_field) const;
+ void UpdateWindowIcon();
+ void UpdateWindowTitle();
+
+ void set_window_delegate(ChromeViews::WindowDelegate* window_delegate) {
+ window_delegate_ = window_delegate;
+ }
+
+ // Changes whether we display a throbber or the current favicon and
+ // forces a repaint of the titlebar.
+ void SetShowThrobber(bool show_throbber);
+
+ // Overridden from ChromeViews::CustomFrameWindow::NonClientView:
+ virtual void Init(ChromeViews::ClientView* client_view);
+ virtual gfx::Rect CalculateClientAreaBounds(int width, int height) const;
+ virtual gfx::Size CalculateWindowSizeForClientSize(int width,
+ int height) const;
+ virtual CPoint GetSystemMenuPoint() const;
+ virtual int HitTest(const gfx::Point& point);
+ virtual void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask);
+ virtual void EnableClose(bool enable);
+
+ // Overridden from ChromeViews::View:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize* out);
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+
+ // Overridden from ChromeViews::BaseButton::ButtonListener:
+ virtual void ButtonPressed(ChromeViews::BaseButton* sender);
+
+ // Overridden from LocationBarView::Delegate:
+ virtual TabContents* GetTabContents();
+ virtual void OnInputInProgress(bool in_progress);
+
+ // Overridden from Task:
+ virtual void Run();
+
+ // Updates the current throbber animation frame; called from the
+ // overloaded Run() and from SetShowThrobber().
+ void UpdateThrobber();
+
+ // Whether we should display the throbber instead of the favicon.
+ bool should_show_throbber() const {
+ return show_throbber_ && current_throbber_frame_ != -1;
+ }
+
+ // Paints different parts of the window to the incoming canvas.
+ void PaintFrameBorder(ChromeCanvas* canvas);
+ void PaintTitleBar(ChromeCanvas* canvas);
+ void PaintThrobber(ChromeCanvas* canvas);
+ void PaintFavicon(ChromeCanvas* canvas);
+ void PaintWindowTitle(ChromeCanvas* canvas);
+
+ void UpdateLocationBar();
+ bool ShouldDisplayURLField() const;
+
+ // The View that provides the background for the window, and optionally
+ // dialog buttons. Note: the non-client view does _not_ own this view, the
+ // container does.
+ ChromeViews::ClientView* client_view_;
+
+ ConstrainedWindowImpl* container_;
+ ChromeViews::WindowDelegate* window_delegate_;
+
+ scoped_ptr<WindowResources> resources_;
+
+ gfx::Rect title_bounds_;
+ gfx::Rect icon_bounds_;
+ gfx::Rect client_bounds_;
+
+ ChromeViews::Button* close_button_;
+
+ LocationBarView* location_bar_;
+
+ // Specialization of ToolbarModel to obtain selected NavigationController for
+ // a constrained TabContents.
+ class ConstrainedWindowToolbarModel : public ToolbarModel {
+ public:
+ ConstrainedWindowToolbarModel(ConstrainedWindowImpl* constrained_window)
+ : constrained_window_(constrained_window) {
+ }
+ ~ConstrainedWindowToolbarModel() { }
+
+ protected:
+ virtual NavigationController* GetNavigationController() {
+ TabContents* tab = constrained_window_->constrained_contents();
+ return tab ? tab->controller() : NULL;
+ }
+
+ private:
+ ConstrainedWindowImpl* constrained_window_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ConstrainedWindowToolbarModel);
+ };
+
+ // The model used for the states of the location bar.
+ ConstrainedWindowToolbarModel toolbar_model_;
+
+ // Whether we should display the animated throbber instead of the
+ // favicon.
+ bool show_throbber_;
+
+ // The timer used to update frames for the throbber.
+ scoped_ptr<Timer> throbber_animation_timer_;
+
+ // The current index into the throbber image strip.
+ int current_throbber_frame_;
+
+ static void InitClass();
+
+ // The default favicon we render when the page has none.
+ static SkBitmap default_favicon_;
+
+ // The throbber to display while a constrained window is loading.
+ static SkBitmap throbber_frames_;
+
+ // The number of animation frames in throbber_frames_.
+ static int throbber_frame_count_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ConstrainedWindowNonClientView);
+};
+
+SkBitmap ConstrainedWindowNonClientView::default_favicon_;
+SkBitmap ConstrainedWindowNonClientView::throbber_frames_;
+int ConstrainedWindowNonClientView::throbber_frame_count_ = -1;
+static const int kWindowIconLeftOffset = 5;
+static const int kWindowIconTopOffset = 5;
+static const int kWindowControlsTopOffset = 1;
+static const int kWindowControlsRightOffset = 4;
+static const int kTitleTopOffset = 6;
+static const int kWindowIconTitleSpacing = 3;
+static const int kTitleBottomSpacing = 5;
+static const int kNoTitleTopSpacing = 8;
+static const int kResizeAreaSize = 5;
+static const int kResizeAreaNorthSize = 3;
+static const int kResizeAreaCornerSize = 16;
+static const int kWindowHorizontalBorderSize = 5;
+static const int kWindowVerticalBorderSize = 5;
+static const int kWindowIconSize = 16;
+
+// How much wider or shorter the location bar is relative to the client area.
+static const int kLocationBarOffset = 2;
+// Spacing between the location bar and the content area.
+static const int kLocationBarSpacing = 1;
+
+static const SkColor kContentsBorderShadow = SkColorSetARGB(51, 0, 0, 0);
+static const SkColor kContentsBorderColor = SkColorSetRGB(219, 235, 255);
+
+static const int kThrobberFrameTimeMs = 30;
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowNonClientView, public:
+
+ConstrainedWindowNonClientView::ConstrainedWindowNonClientView(
+ ConstrainedWindowImpl* container, TabContents* owner)
+ : container_(container),
+ window_delegate_(NULL),
+ close_button_(new ChromeViews::Button),
+ location_bar_(NULL),
+ show_throbber_(false),
+ current_throbber_frame_(-1),
+ toolbar_model_(container) {
+ InitClass();
+ if (owner->profile()->IsOffTheRecord()) {
+ resources_.reset(new OTRWindowResources);
+ } else {
+ if (win_util::ShouldUseVistaFrame()) {
+ resources_.reset(new VistaWindowResources);
+ } else {
+ resources_.reset(new XPWindowResources);
+ }
+ }
+
+ close_button_->SetImage(ChromeViews::Button::BS_NORMAL,
+ resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON));
+ close_button_->SetImage(ChromeViews::Button::BS_HOT,
+ resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_H));
+ close_button_->SetImage(ChromeViews::Button::BS_PUSHED,
+ resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_P));
+ close_button_->SetImageAlignment(ChromeViews::Button::ALIGN_CENTER,
+ ChromeViews::Button::ALIGN_MIDDLE);
+ close_button_->SetListener(this, 0);
+ AddChildView(close_button_);
+
+ throbber_animation_timer_.reset(
+ new Timer(kThrobberFrameTimeMs, this, true));
+
+ // Note: we don't need for a controller because no input event will be ever
+ // processed from a constrained window.
+ location_bar_ = new LocationBarView(owner->profile(),
+ NULL,
+ &toolbar_model_,
+ this,
+ true);
+ AddChildView(location_bar_);
+}
+
+ConstrainedWindowNonClientView::~ConstrainedWindowNonClientView() {
+ MessageLoop::current()->timer_manager()->StopTimer(
+ throbber_animation_timer_.get());
+}
+
+void ConstrainedWindowNonClientView::UpdateLocationBar() {
+ if (ShouldDisplayURLField()) {
+ std::wstring url_spec;
+ TabContents* tab = container_->constrained_contents();
+ url_spec = gfx::ElideUrl(tab->GetURL(),
+ ChromeFont(),
+ 0,
+ tab->profile()->GetPrefs()->GetString(prefs::kAcceptLanguages));
+ std::wstring ev_text, ev_tooltip_text;
+ tab->GetSSLEVText(&ev_text, &ev_tooltip_text),
+ location_bar_->Update(NULL);
+ }
+}
+
+bool ConstrainedWindowNonClientView::ShouldDisplayURLField() const {
+ // If the dialog is not fully initialized, default to showing the URL field.
+ if (!container_ || !container_->owner() || !container_->owner()->delegate())
+ return true;
+
+ return !container_->is_dialog() &&
+ container_->owner()->delegate()->ShouldDisplayURLField();
+}
+
+int ConstrainedWindowNonClientView::CalculateTitlebarHeight() const {
+ int height;
+ if (window_delegate_ && window_delegate_->ShouldShowWindowTitle()) {
+ height = kTitleTopOffset + resources_->GetTitleFont().height() +
+ kTitleBottomSpacing;
+ } else {
+ height = kNoTitleTopSpacing;
+ }
+
+ return height;
+}
+
+int ConstrainedWindowNonClientView::CalculateNonClientHeight(
+ bool with_url_field) const {
+ int r = CalculateTitlebarHeight();
+
+ if (with_url_field) {
+ CSize s;
+ location_bar_->GetPreferredSize(&s);
+ r += s.cy;
+ }
+ return r;
+}
+
+gfx::Rect ConstrainedWindowNonClientView::CalculateWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds,
+ bool with_url_field) const {
+ int non_client_height = CalculateNonClientHeight(with_url_field);
+ gfx::Rect window_bounds = client_bounds;
+ window_bounds.set_width(
+ window_bounds.width() + 2 * kWindowHorizontalBorderSize);
+ window_bounds.set_height(
+ window_bounds.height() + non_client_height + kWindowVerticalBorderSize);
+ window_bounds.set_x(
+ std::max(0, window_bounds.x() - kWindowHorizontalBorderSize));
+ window_bounds.set_y(std::max(0, window_bounds.y() - non_client_height));
+ return window_bounds;
+}
+
+void ConstrainedWindowNonClientView::UpdateWindowIcon() {
+ SchedulePaint(icon_bounds_.ToRECT(), false);
+}
+
+void ConstrainedWindowNonClientView::UpdateWindowTitle() {
+ SchedulePaint(title_bounds_.ToRECT(), false);
+ UpdateLocationBar();
+}
+
+void ConstrainedWindowNonClientView::SetShowThrobber(bool show_throbber) {
+ show_throbber_ = show_throbber;
+
+ TimerManager* tm = MessageLoop::current()->timer_manager();
+ Timer* timer = throbber_animation_timer_.get();
+ if (show_throbber) {
+ if (!tm->IsTimerRunning(timer))
+ tm->ResetTimer(timer);
+ } else {
+ if (tm->IsTimerRunning(timer)) {
+ UpdateThrobber();
+ tm->StopTimer(timer);
+ }
+ }
+}
+
+void ConstrainedWindowNonClientView::Run() {
+ UpdateThrobber();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowNonClientView,
+// ChromeViews::CustomFrameWindow::NonClientView implementation:
+
+void ConstrainedWindowNonClientView::Init(
+ ChromeViews::ClientView* client_view) {
+ client_view_ = client_view;
+ AddChildView(client_view_);
+}
+
+gfx::Rect ConstrainedWindowNonClientView::CalculateClientAreaBounds(
+ int width,
+ int height) const {
+ int non_client_height = CalculateNonClientHeight(ShouldDisplayURLField());
+ return gfx::Rect(kWindowHorizontalBorderSize, non_client_height,
+ std::max(0, width - (2 * kWindowHorizontalBorderSize)),
+ std::max(0, height - non_client_height - kWindowVerticalBorderSize));
+}
+
+gfx::Size ConstrainedWindowNonClientView::CalculateWindowSizeForClientSize(
+ int width,
+ int height) const {
+ // This is only used for truly constrained windows, which does not include
+ // popups generated from a user gesture since those are detached immediately.
+ gfx::Rect window_bounds =
+ CalculateWindowBoundsForClientBounds(gfx::Rect(0, 0, width, height),
+ ShouldDisplayURLField());
+ return window_bounds.size();
+}
+
+CPoint ConstrainedWindowNonClientView::GetSystemMenuPoint() const {
+ CPoint system_menu_point(icon_bounds_.x(), icon_bounds_.bottom());
+ MapWindowPoints(container_->GetHWND(), HWND_DESKTOP, &system_menu_point, 1);
+ return system_menu_point;
+}
+
+int ConstrainedWindowNonClientView::HitTest(const gfx::Point& point) {
+ CRect bounds;
+ CPoint test_point = point.ToPOINT();
+
+ // First see if it's within the grow box area, since that overlaps the client
+ // bounds.
+ if (client_view_->PointIsInSizeBox(point))
+ return HTBOTTOMRIGHT;
+
+ // Then see if it's within the client area.
+ if (client_view_) {
+ client_view_->GetBounds(&bounds);
+ if (bounds.PtInRect(test_point))
+ return HTCLIENT;
+ }
+
+ // Then see if the point is within any of the window controls.
+ close_button_->GetBounds(&bounds);
+ if (bounds.PtInRect(test_point))
+ return HTCLOSE;
+ bounds = icon_bounds_.ToRECT();
+ if (bounds.PtInRect(test_point))
+ return HTSYSMENU;
+
+ // Then see if the point is within the resize boundaries.
+ int width = GetWidth();
+ int height = GetHeight();
+ int component = HTNOWHERE;
+ if (point.x() < kResizeAreaSize) {
+ if (point.y() < kResizeAreaCornerSize) {
+ component = HTTOPLEFT;
+ } else if (point.y() >= (height - kResizeAreaCornerSize)) {
+ component = HTBOTTOMLEFT;
+ } else {
+ component = HTLEFT;
+ }
+ } else if (point.x() < kResizeAreaCornerSize) {
+ if (point.y() < kResizeAreaNorthSize) {
+ component = HTTOPLEFT;
+ } else if (point.y() >= (height - kResizeAreaSize)) {
+ component = HTBOTTOMLEFT;
+ }
+ } else if (point.x() >= (width - kResizeAreaSize)) {
+ if (point.y() < kResizeAreaCornerSize) {
+ component = HTTOPRIGHT;
+ } else if (point.y() >= (height - kResizeAreaCornerSize)) {
+ component = HTBOTTOMRIGHT;
+ } else if (point.x() >= (width - kResizeAreaSize)) {
+ component = HTRIGHT;
+ }
+ } else if (point.x() >= (width - kResizeAreaCornerSize)) {
+ if (point.y() < kResizeAreaNorthSize) {
+ component = HTTOPRIGHT;
+ } else if (point.y() >= (height - kResizeAreaSize)) {
+ component = HTBOTTOMRIGHT;
+ }
+ } else if (point.y() < kResizeAreaNorthSize) {
+ component = HTTOP;
+ } else if (point.y() >= (height - kResizeAreaSize)) {
+ component = HTBOTTOM;
+ }
+
+ // If the window can't be resized, there are no resize boundaries, just
+ // window borders.
+ if (component != HTNOWHERE) {
+ if (window_delegate_ && !window_delegate_->CanResize()) {
+ return HTBORDER;
+ }
+ return component;
+ }
+
+ // Finally fall back to the caption.
+ GetBounds(&bounds);
+ if (bounds.PtInRect(test_point))
+ return HTCAPTION;
+ // The point is outside the window's bounds.
+ return HTNOWHERE;
+}
+
+void ConstrainedWindowNonClientView::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+ DCHECK(window_mask);
+
+ // 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 ConstrainedWindowNonClientView::EnableClose(bool enable) {
+ close_button_->SetEnabled(enable);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowNonClientView, ChromeViews::View implementation:
+
+void ConstrainedWindowNonClientView::Paint(ChromeCanvas* canvas) {
+ PaintFrameBorder(canvas);
+ PaintTitleBar(canvas);
+}
+
+void ConstrainedWindowNonClientView::Layout() {
+ bool should_display_url_field = false;
+ if (location_bar_) {
+ should_display_url_field = ShouldDisplayURLField();
+ location_bar_->SetVisible(should_display_url_field);
+ }
+
+ int location_bar_height = 0;
+ CSize ps;
+ if (should_display_url_field) {
+ location_bar_->GetPreferredSize(&ps);
+ location_bar_height = ps.cy;
+ }
+
+ close_button_->GetPreferredSize(&ps);
+ close_button_->SetBounds(GetWidth() - ps.cx - kWindowControlsRightOffset,
+ kWindowControlsTopOffset, ps.cx, ps.cy);
+
+ int titlebar_height = CalculateTitlebarHeight();
+ if (window_delegate_) {
+ int icon_y = (titlebar_height - kWindowIconSize) / 2;
+ icon_bounds_.SetRect(kWindowIconLeftOffset, icon_y, 0, 0);
+ if (window_delegate_->ShouldShowWindowIcon()) {
+ icon_bounds_.set_width(kWindowIconSize);
+ icon_bounds_.set_height(kWindowIconSize);
+ }
+
+ if (window_delegate_->ShouldShowWindowTitle()) {
+ int spacing = window_delegate_->ShouldShowWindowIcon() ?
+ kWindowIconTitleSpacing : 0;
+ int title_right = close_button_->GetX() - spacing;
+ int title_left = icon_bounds_.right() + spacing;
+ title_bounds_.SetRect(title_left, kTitleTopOffset,
+ title_right - title_left,
+ resources_->GetTitleFont().height());
+ }
+ }
+
+ client_bounds_ = CalculateClientAreaBounds(GetWidth(), GetHeight());
+ if (should_display_url_field) {
+ location_bar_->SetBounds(client_bounds_.x() - kLocationBarOffset,
+ client_bounds_.y() - location_bar_height -
+ kLocationBarSpacing,
+ client_bounds_.width() + kLocationBarOffset * 2,
+ location_bar_height);
+ location_bar_->Layout();
+ }
+ if (client_view_)
+ client_view_->SetBounds(client_bounds_.ToRECT());
+}
+
+void ConstrainedWindowNonClientView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ if (client_view_) {
+ client_view_->GetPreferredSize(out);
+ out->cx += 2 * kWindowHorizontalBorderSize;
+ out->cy += CalculateNonClientHeight(ShouldDisplayURLField()) +
+ kWindowVerticalBorderSize;
+ }
+}
+
+void ConstrainedWindowNonClientView::ViewHierarchyChanged(bool is_add,
+ View *parent,
+ View *child) {
+ if (is_add && location_bar_ && GetViewContainer() &&
+ !(location_bar_->IsInitialized())) {
+ location_bar_->Init();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowNonClientView, ChromeViews::BaseButton::Button
+// implementation:
+
+void ConstrainedWindowNonClientView::ButtonPressed(
+ ChromeViews::BaseButton* sender) {
+ if (sender == close_button_)
+ container_->ExecuteSystemMenuCommand(SC_CLOSE);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowNonClientView, LocationBarView::Delegate
+// implementation:
+TabContents* ConstrainedWindowNonClientView::GetTabContents() {
+ return container_->owner();
+}
+
+void ConstrainedWindowNonClientView::OnInputInProgress(bool in_progress) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowNonClientView, private:
+
+void ConstrainedWindowNonClientView::UpdateThrobber() {
+ if (show_throbber_)
+ current_throbber_frame_ = ++current_throbber_frame_ % throbber_frame_count_;
+ else
+ current_throbber_frame_ = -1;
+
+ SchedulePaint();
+}
+
+void ConstrainedWindowNonClientView::PaintFrameBorder(ChromeCanvas* canvas) {
+ int width = GetWidth();
+ int height = GetHeight();
+
+ 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_CENTER);
+ SkBitmap* right_edge = resources_->GetPartBitmap(FRAME_RIGHT_SIDE);
+ SkBitmap* left_edge = resources_->GetPartBitmap(FRAME_LEFT_SIDE);
+ 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_CENTER);
+
+ // 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.
+ int top_stack_height = top_right_corner->height();
+ canvas->TileImageInt(*right_edge, width - right_edge->width(),
+ top_stack_height, right_edge->width(),
+ height - top_stack_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.
+ top_stack_height = top_left_corner->height();
+ canvas->TileImageInt(*left_edge, 0, top_stack_height, left_edge->width(),
+ height - top_stack_height -
+ bottom_left_corner->height());
+
+ // Contents Border.
+ gfx::Rect border_bounds = client_bounds_;
+ border_bounds.Inset(-2, -2);
+ canvas->FillRectInt(kContentsBorderShadow, border_bounds.x(),
+ border_bounds.y(), border_bounds.width(),
+ border_bounds.height());
+
+ border_bounds.Inset(1, 1);
+ canvas->FillRectInt(kContentsBorderColor, border_bounds.x(),
+ border_bounds.y(), border_bounds.width(),
+ border_bounds.height());
+}
+
+void ConstrainedWindowNonClientView::PaintTitleBar(ChromeCanvas* canvas) {
+ if (!window_delegate_)
+ return;
+
+ if (window_delegate_->ShouldShowWindowIcon()) {
+ if (should_show_throbber())
+ PaintThrobber(canvas);
+ else
+ PaintFavicon(canvas);
+ }
+ if (window_delegate_->ShouldShowWindowTitle()) {
+ PaintWindowTitle(canvas);
+ }
+}
+
+void ConstrainedWindowNonClientView::PaintThrobber(ChromeCanvas* canvas) {
+ int image_size = throbber_frames_.height();
+ int image_offset = current_throbber_frame_ * image_size;
+ canvas->DrawBitmapInt(throbber_frames_,
+ image_offset, 0, image_size, image_size,
+ icon_bounds_.x(), icon_bounds_.y(),
+ image_size, image_size,
+ false);
+}
+
+void ConstrainedWindowNonClientView::PaintFavicon(ChromeCanvas* canvas) {
+ SkBitmap window_icon = window_delegate_->GetWindowIcon();
+ if (window_icon.isNull())
+ window_icon = default_favicon_;
+ canvas->DrawBitmapInt(window_icon, 0, 0, window_icon.width(),
+ window_icon.height(), icon_bounds_.x(),
+ icon_bounds_.y(), icon_bounds_.width(),
+ icon_bounds_.height(), false);
+}
+
+void ConstrainedWindowNonClientView::PaintWindowTitle(ChromeCanvas* canvas) {
+ canvas->DrawStringInt(container_->GetWindowTitle(),
+ resources_->GetTitleFont(),
+ resources_->GetTitleColor(), title_bounds_.x(),
+ title_bounds_.y(), title_bounds_.width(),
+ title_bounds_.height());
+}
+
+// static
+void ConstrainedWindowNonClientView::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_favicon_ = *rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
+
+ throbber_frames_ = *rb.GetBitmapNamed(IDR_THROBBER);
+ DCHECK(throbber_frames_.width() % throbber_frames_.height() == 0);
+ throbber_frame_count_ =
+ throbber_frames_.width() / throbber_frames_.height();
+
+ initialized = true;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedTabContentsWindowDelegate
+
+class ConstrainedTabContentsWindowDelegate
+ : public ChromeViews::WindowDelegate {
+ public:
+ explicit ConstrainedTabContentsWindowDelegate(
+ ConstrainedWindowImpl* window)
+ : window_(window) {
+ }
+
+ // ChromeViews::WindowDelegate implementation:
+ virtual bool CanResize() const {
+ return true;
+ }
+ virtual std::wstring GetWindowTitle() const {
+ TabContents* constrained_contents = window_->constrained_contents();
+ if (constrained_contents)
+ return constrained_contents->GetTitle();
+
+ return std::wstring();
+ }
+ virtual bool ShouldShowWindowIcon() const {
+ return true;
+ }
+ virtual SkBitmap GetWindowIcon() {
+ TabContents* constrained_contents = window_->constrained_contents();
+ if (constrained_contents)
+ return constrained_contents->GetFavIcon();
+
+ return SkBitmap();
+ }
+
+ private:
+ ConstrainedWindowImpl* window_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ConstrainedTabContentsWindowDelegate);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowImpl, public:
+
+// The space (in pixels) between minimized pop-ups stacked horizontally and
+// vertically.
+static const int kPopupRepositionOffset = 5;
+static const int kConstrainedWindowEdgePadding = 10;
+
+ConstrainedWindowImpl::ConstrainedWindowImpl(TabContents* owner)
+ : CustomFrameWindow(new ConstrainedWindowNonClientView(this, owner)),
+ owner_(owner),
+ constrained_contents_(NULL),
+ focus_restoration_disabled_(false),
+ is_dialog_(false),
+ titlebar_visibility_(0.0),
+ contents_container_(NULL) {
+ set_window_style(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION |
+ WS_THICKFRAME | WS_SYSMENU);
+ set_focus_on_creation(false);
+}
+
+ConstrainedWindowImpl::~ConstrainedWindowImpl() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowImpl, ConstrainedWindow implementation:
+
+ConstrainedWindowNonClientView* ConstrainedWindowImpl::non_client_view() {
+ return static_cast<ConstrainedWindowNonClientView*>(non_client_view_);
+}
+
+void ConstrainedWindowImpl::ActivateConstrainedWindow() {
+ if (CanDetach()) {
+ // Detachable pop-ups are torn out as soon as the window is activated.
+ Detach();
+ return;
+ }
+
+ // Other pop-ups are simply moved to the front of the z-order.
+ SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+ // Store the focus of our parent focus manager so we can restore it when we
+ // close.
+ ChromeViews::FocusManager* focus_manager =
+ ChromeViews::FocusManager::GetFocusManager(GetHWND());
+ DCHECK(focus_manager);
+ focus_manager = focus_manager->GetParentFocusManager();
+ if (focus_manager) {
+ // We could not have a parent focus manager if the ConstrainedWindow is
+ // displayed in a tab that is not currently selected.
+ // TODO(jcampan): we should store the ConstrainedWindow active events in
+ // that case and replay them when the WebContents becomes selected.
+ focus_manager->StoreFocusedView();
+
+ if (constrained_contents_) {
+ // We contain another window, let's assume it knows how to process the
+ // focus and let's focus it.
+ // TODO(jcampan): so far this case is the WebContents case. We need to
+ // better find whether the inner window should get focus.
+ ::SetFocus(constrained_contents_->GetContainerHWND());
+ } else {
+ // Give our window the focus so we get keyboard messages.
+ ::SetFocus(GetHWND());
+ }
+ }
+}
+
+void ConstrainedWindowImpl::CloseConstrainedWindow() {
+ // Broadcast to all observers of NOTIFY_CWINDOW_CLOSED.
+ // One example of such an observer is AutomationCWindowTracker in the
+ // automation component.
+ NotificationService::current()->Notify(NOTIFY_CWINDOW_CLOSED,
+ Source<ConstrainedWindow>(this),
+ NotificationService::NoDetails());
+
+ Close();
+}
+
+void ConstrainedWindowImpl::ResizeConstrainedWindow(int width, int height) {
+ gfx::Size window_size =
+ non_client_view_->CalculateWindowSizeForClientSize(width, height);
+ ::SetWindowPos(GetHWND(), NULL, 0, 0, window_size.width(),
+ window_size.height(),
+ SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+}
+
+void ConstrainedWindowImpl::RepositionConstrainedWindowTo(
+ const gfx::Point& anchor_point) {
+ anchor_point_ = anchor_point;
+ ResizeConstrainedTitlebar();
+}
+
+bool ConstrainedWindowImpl::IsSuppressedConstrainedWindow() const {
+ return !is_dialog_;
+}
+
+void ConstrainedWindowImpl::WasHidden() {
+ if (constrained_contents_)
+ constrained_contents_->WasHidden();
+}
+
+void ConstrainedWindowImpl::DidBecomeSelected() {
+ if (constrained_contents_)
+ constrained_contents_->DidBecomeSelected();
+}
+
+std::wstring ConstrainedWindowImpl::GetWindowTitle() const {
+ // TODO(beng): (http://b/1085485) Need to decide if we want to append the app
+ // name to all constrained windows or just web renderers.
+ std::wstring page_title;
+ if (window_delegate())
+ page_title = window_delegate()->GetWindowTitle();
+
+ std::wstring display_title;
+ if (page_title.empty()) {
+ display_title = l10n_util::GetString(IDS_PRODUCT_NAME);
+ } else {
+ display_title = l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT,
+ page_title);
+ }
+ return display_title;
+}
+
+void ConstrainedWindowImpl::UpdateWindowTitle() {
+ UpdateUI(TabContents::INVALIDATE_TITLE);
+}
+
+const gfx::Rect& ConstrainedWindowImpl::GetCurrentBounds() const {
+ return current_bounds_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowImpl, TabContentsDelegate implementation:
+
+void ConstrainedWindowImpl::NavigationStateChanged(
+ const TabContents* source,
+ unsigned int changed_flags) {
+ UpdateUI(changed_flags);
+}
+
+void ConstrainedWindowImpl::ReplaceContents(TabContents* source,
+ TabContents* new_contents) {
+ source->set_delegate(NULL);
+
+ constrained_contents_ = new_contents;
+ constrained_contents_->set_delegate(this);
+ UpdateUI(TabContents::INVALIDATE_EVERYTHING);
+}
+
+void ConstrainedWindowImpl::AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
+ // Pass this to the delegate, since we can't open new tabs in the Constrained
+ // Window, they are sent up to the browser to open as new tabs.
+ owner_->AddNewContents(
+ this, new_contents, disposition, initial_pos, user_gesture);
+}
+
+void ConstrainedWindowImpl::ActivateContents(TabContents* contents) {
+ // Ask the delegate's (which is a TabContents) own TabContentsDelegate to
+ // activate itself...
+ owner_->delegate()->ActivateContents(owner_);
+
+ // Set as the foreground constrained window.
+ ActivateConstrainedWindow();
+}
+
+void ConstrainedWindowImpl::OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ owner_->OpenURL(this, url, disposition, transition);
+}
+
+void ConstrainedWindowImpl::LoadingStateChanged(TabContents* source) {
+ // TODO(beng): (http://b/1085543) Implement a throbber for the Constrained
+ // Window.
+ UpdateUI(TabContents::INVALIDATE_EVERYTHING);
+ non_client_view()->SetShowThrobber(source->is_loading());
+}
+
+void ConstrainedWindowImpl::NavigateToPage(TabContents* source,
+ const GURL& url,
+ PageTransition::Type transition) {
+ UpdateUI(TabContents::INVALIDATE_EVERYTHING);
+}
+
+void ConstrainedWindowImpl::SetTitlebarVisibilityPercentage(double percentage) {
+ titlebar_visibility_ = percentage;
+ ResizeConstrainedTitlebar();
+}
+
+void ConstrainedWindowImpl::StartSuppressedAnimation() {
+ animation_.reset(new ConstrainedWindowAnimation(this));
+ animation_->Start();
+}
+
+void ConstrainedWindowImpl::CloseContents(TabContents* source) {
+ Close();
+}
+
+void ConstrainedWindowImpl::MoveContents(TabContents* source,
+ const gfx::Rect& pos) {
+ if (!IsSuppressedConstrainedWindow())
+ SetWindowBounds(pos);
+}
+
+bool ConstrainedWindowImpl::IsPopup(TabContents* source) {
+ return true;
+}
+
+TabContents* ConstrainedWindowImpl::GetConstrainingContents(
+ TabContents* source) {
+ return owner_;
+}
+
+void ConstrainedWindowImpl::ToolbarSizeChanged(TabContents* source,
+ bool finished) {
+ // We don't control the layout of anything that could be animating,
+ // so do nothing.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowImpl, private:
+
+void ConstrainedWindowImpl::ResizeConstrainedTitlebar() {
+ DCHECK(constrained_contents_)
+ << "ResizeConstrainedTitlebar() is only valid for web popups";
+ // If we represent a web popup and we were not opened as the result of a
+ // user gesture, we override the position specified in |initial_bounds| to
+ // place ourselves at the bottom right of the parent HWND.
+ CRect this_bounds;
+ GetClientRect(&this_bounds);
+
+ // First determine the height of the title bar of a constrained window, so
+ // that we can offset by that much vertically if necessary...
+ int titlebar_height = non_client_view()->CalculateTitlebarHeight();
+
+ int visible_titlebar_pixels =
+ static_cast<int>(titlebar_height * titlebar_visibility_);
+
+ int x = anchor_point_.x() - this_bounds.Width();
+ int y = anchor_point_.y() - visible_titlebar_pixels;
+ SetWindowPos(NULL, x, y, this_bounds.Width(), visible_titlebar_pixels,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+}
+
+void ConstrainedWindowImpl::InitAsDialog(
+ const gfx::Rect& initial_bounds,
+ ChromeViews::View* contents_view,
+ ChromeViews::WindowDelegate* window_delegate) {
+ is_dialog_ = true;
+ non_client_view()->set_window_delegate(window_delegate);
+ CustomFrameWindow::Init(owner_->GetContainerHWND(), initial_bounds,
+ contents_view, window_delegate);
+ ActivateConstrainedWindow();
+}
+
+void ConstrainedWindowImpl::InitWindowForContents(
+ TabContents* constrained_contents) {
+ constrained_contents_ = constrained_contents;
+ constrained_contents_->set_delegate(this);
+ contents_container_ = new ChromeViews::HWNDView;
+ contents_window_delegate_.reset(
+ new ConstrainedTabContentsWindowDelegate(this));
+
+ non_client_view()->set_window_delegate(contents_window_delegate_.get());
+}
+
+void ConstrainedWindowImpl::InitSizeForContents(
+ const gfx::Rect& initial_bounds) {
+ CustomFrameWindow::Init(owner_->GetContainerHWND(), initial_bounds,
+ contents_container_, contents_window_delegate_.get());
+ contents_container_->Attach(constrained_contents_->GetContainerHWND());
+
+ constrained_contents_->SizeContents(
+ gfx::Size(contents_container_->GetWidth(),
+ contents_container_->GetHeight()));
+ current_bounds_ = initial_bounds;
+
+ // Note that this is HWND_TOP, not HWND_TOPMOST... this is important
+ // because otherwise the window will not be visible on top of the
+ // RenderWidgetHostView!
+ win_util::SetChildBounds(GetHWND(), GetParent(), HWND_TOP, initial_bounds,
+ kConstrainedWindowEdgePadding, 0);
+}
+
+bool ConstrainedWindowImpl::CanDetach() const {
+ // Constrained TabContentses can be detached, dialog boxes can't.
+ return constrained_contents_ ? true : false;
+}
+
+void ConstrainedWindowImpl::Detach() {
+ DCHECK(CanDetach());
+ // Tell the container not to restore focus to whatever view was focused last,
+ // since this will interfere with the new window activation in the case where
+ // a constrained window is destroyed by being detached.
+ focus_restoration_disabled_ = true;
+
+ // Detach the HWND immediately.
+ contents_container_->Detach();
+ contents_container_ = NULL;
+
+ // To try and create as seamless as possible a popup experience, web pop-ups
+ // are automatically detached when the user interacts with them. We can
+ // dial this back if we feel this is too much.
+
+ // The detached contents "should" be re-parented by the delegate's
+ // DetachContents, but we clear the delegate pointing to us just in case.
+ constrained_contents_->set_delegate(NULL);
+
+ CRect bounds;
+ ::GetWindowRect(constrained_contents_->GetContainerHWND(), &bounds);
+ CPoint cursor_pos;
+ ::GetCursorPos(&cursor_pos);
+ gfx::Point screen_point(cursor_pos.x, cursor_pos.y);
+ int frame_component = static_cast<int>(OnNCHitTest(screen_point.ToPOINT()));
+ owner_->DetachContents(this, constrained_contents_, gfx::Rect(bounds),
+ screen_point, frame_component);
+ constrained_contents_ = NULL;
+ Close();
+}
+
+void ConstrainedWindowImpl::SetWindowBounds(const gfx::Rect& bounds) {
+ // Note: SetChildBounds ensures that the constrained window is constrained
+ // to the bounds of its parent, however there remains a bug where the
+ // window is positioned incorrectly when the outer window is opened on
+ // a monitor that has negative coords (e.g. secondary monitor to left
+ // of primary, see http://b/issue?id=967905.)
+ gfx::Size window_size = non_client_view()->CalculateWindowSizeForClientSize(
+ bounds.width(), bounds.height());
+
+ current_bounds_ = bounds;
+ current_bounds_.set_width(window_size.width());
+ current_bounds_.set_height(window_size.height());
+ win_util::SetChildBounds(GetHWND(), GetParent(), NULL, current_bounds_,
+ kConstrainedWindowEdgePadding, 0);
+}
+
+void ConstrainedWindowImpl::UpdateUI(unsigned int changed_flags) {
+ if (changed_flags & TabContents::INVALIDATE_FAVICON)
+ non_client_view()->UpdateWindowIcon();
+ if (changed_flags & TabContents::INVALIDATE_TITLE)
+ non_client_view()->UpdateWindowTitle();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowImpl, ChromeViews::HWNDViewContainer overrides:
+
+void ConstrainedWindowImpl::OnDestroy() {
+ // We do this here, rather than |Close|, since the window may be destroyed in
+ // a way other than by some other component calling Close, e.g. by the native
+ // window hierarchy closing. We are guaranteed to receive a WM_DESTROY
+ // message regardless of how the window is closed.
+ // Note that when we get this message, the focus manager of the
+ // ConstrainedWindow has already been destroyed (by the processing of
+ // WM_DESTROY in FocusManager). So the FocusManager we retrieve here is the
+ // parent one (the one from the top window).
+ ChromeViews::FocusManager* focus_manager =
+ ChromeViews::FocusManager::GetFocusManager(GetHWND());
+ if (focus_manager) {
+ // We may not have a focus manager if:
+ // - we are hidden when closed (the TabContent would be detached).
+ // - the tab has been closed and we are closed as a result.
+ // TODO(jcampan): when hidden, we should modify the stored focus of the tab
+ // so when it becomes visible again we retrieve the focus appropriately.
+ if (!focus_restoration_disabled_)
+ focus_manager->RestoreFocusedView();
+ }
+
+ // If we have a child TabContents, we need to unhook it here so that it is
+ // not automatically WM_DESTROYed by virtue of the fact that it is part of
+ // our Window hierarchy. Rather, it needs to be destroyed just like top level
+ // TabContentses are: from OnMsgCloseACK in RenderWidgetHost. So we hide the
+ // TabContents and sever the parent relationship now. Note the GetParent
+ // check so that we don't hide and re-parent TabContentses that have been
+ // detached and re-attached into a new top level browser window via a user
+ // drag action.
+ if (constrained_contents_ &&
+ ::GetParent(constrained_contents_->GetContainerHWND()) == GetHWND()) {
+ ::ShowWindow(constrained_contents_->GetContainerHWND(), SW_HIDE);
+ ::SetParent(constrained_contents_->GetContainerHWND(), NULL);
+ }
+
+ // Make sure we call super so that it can do its cleanup.
+ Window::OnDestroy();
+}
+
+void ConstrainedWindowImpl::OnFinalMessage(HWND window) {
+ // Tell our constraining TabContents that we've gone so it can update its
+ // list.
+ owner_->WillClose(this);
+ if (constrained_contents_) {
+ constrained_contents_->CloseContents();
+ constrained_contents_ = NULL;
+ }
+
+ HWNDViewContainer::OnFinalMessage(window);
+}
+
+void ConstrainedWindowImpl::OnGetMinMaxInfo(LPMINMAXINFO mm_info) {
+ // Override this function to set the maximize area as the client area of the
+ // containing window.
+ CRect parent_rect;
+ ::GetClientRect(GetParent(), &parent_rect);
+
+ mm_info->ptMaxSize.x = parent_rect.Width();
+ mm_info->ptMaxSize.y = parent_rect.Height();
+ mm_info->ptMaxPosition.x = parent_rect.left;
+ mm_info->ptMaxPosition.y = parent_rect.top;
+}
+
+LRESULT ConstrainedWindowImpl::OnMouseActivate(HWND window,
+ UINT hittest_code,
+ UINT message) {
+ // We need to store this value before we call ActivateConstrainedWindow()
+ // since the window may be detached and so this function will return false
+ // afterwards.
+ bool can_detach = CanDetach();
+
+ // We only detach the window if the user clicked on the title bar. That
+ // way, users can click inside the contents of legitimate popups obtained
+ // with a mouse gesture.
+ if (hittest_code == HTCAPTION) {
+ ActivateConstrainedWindow();
+ } else {
+ // If the user did not click on the title bar, don't stop message
+ // propagation.
+ can_detach = false;
+ }
+
+ // If the popup can be detached, then we tell the parent window not to
+ // activate since we will already have adjusted activation ourselves. We also
+ // do _not_ eat the event otherwise the user will have to click again to
+ // interact with the popup.
+ return can_detach ? MA_NOACTIVATEANDEAT : MA_ACTIVATE;
+}
+
+void ConstrainedWindowImpl::OnWindowPosChanged(WINDOWPOS* window_pos) {
+ // If the window was moved or sized, tell the owner.
+ if (!(window_pos->flags & SWP_NOMOVE) || !(window_pos->flags & SWP_NOSIZE))
+ owner_->DidMoveOrResize(this);
+ SetMsgHandled(FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindow, public:
+
+// static
+void ConstrainedWindow::GenerateInitialBounds(
+ const gfx::Rect& initial_bounds, TabContents* parent,
+ gfx::Rect* window_bounds) {
+ // Calculate desired window bounds. Try to use the bounds of a
+ // non-maximized browser window; this matches other browsers' behavior.
+ //
+ // NOTE: The downside here is that, if we open multiple constrained popups,
+ // they'll all get the same window position, since WindowSizer uses the
+ // "last active browser window"'s bounds. Fixing this properly is hard,
+ // since we'd have to tell the WindowSizer about the window we're opening
+ // here, and figure out how the sizing memory and the clipping/offsetting
+ // behvaiors below interact.
+ std::wstring app_name;
+
+ if (parent->delegate() && parent->delegate()->IsApplication() &&
+ parent->AsWebContents() && parent->AsWebContents()->web_app()) {
+ app_name = parent->AsWebContents()->web_app()->name();
+ }
+ bool maximized = false;
+ gfx::Rect empty_bounds;
+ WindowSizer::GetBrowserWindowBounds(app_name, empty_bounds,
+ window_bounds, &maximized);
+ if (initial_bounds.width() > 0)
+ window_bounds->set_width(initial_bounds.width());
+ if (initial_bounds.height() > 0)
+ window_bounds->set_height(initial_bounds.height());
+
+ // Map desired window bounds from screen coordinates to our parent's
+ // coordinates.
+ CPoint window_origin(window_bounds->origin().ToPOINT());
+ MapWindowPoints(HWND_DESKTOP, parent->GetContainerHWND(), &window_origin,
+ 1);
+ window_bounds->set_origin(gfx::Point(window_origin));
+
+ // Ensure some amount of the page is visible above and to the left of the
+ // popup, so it doesn't cover the whole content area (we use 30 px).
+ if (window_bounds->x() < 30)
+ window_bounds->set_x(30);
+ if (window_bounds->y() < 30)
+ window_bounds->set_y(30);
+
+ // Clip the desired coordinates so they fit within the content area.
+ CRect parent_rect;
+ ::GetClientRect(parent->GetContainerHWND(), &parent_rect);
+ if (window_bounds->right() > parent_rect.right)
+ window_bounds->set_width(parent_rect.Width() - window_bounds->x());
+ if (window_bounds->bottom() > parent_rect.bottom)
+ window_bounds->set_height(parent_rect.Height() - window_bounds->y());
+
+ // Don't let the window become too small (we use a 60x30 minimum size).
+ if (window_bounds->width() < 60)
+ window_bounds->set_width(60);
+ if (window_bounds->height() < 30)
+ window_bounds->set_height(30);
+}
+
+// static
+ConstrainedWindow* ConstrainedWindow::CreateConstrainedDialog(
+ TabContents* parent,
+ const gfx::Rect& initial_bounds,
+ ChromeViews::View* contents_view,
+ ChromeViews::WindowDelegate* window_delegate) {
+ ConstrainedWindowImpl* window = new ConstrainedWindowImpl(parent);
+ window->InitAsDialog(initial_bounds, contents_view, window_delegate);
+ return window;
+}
+
+// static
+ConstrainedWindow* ConstrainedWindow::CreateConstrainedPopup(
+ TabContents* parent,
+ const gfx::Rect& initial_bounds,
+ TabContents* constrained_contents) {
+ ConstrainedWindowImpl* window =
+ new ConstrainedWindowImpl(parent);
+ window->InitWindowForContents(constrained_contents);
+
+ gfx::Rect window_bounds;
+ if (initial_bounds.width() == 0 || initial_bounds.height() == 0) {
+ GenerateInitialBounds(initial_bounds, parent, &window_bounds);
+ } else {
+ window_bounds = window->non_client_view()->
+ CalculateWindowBoundsForClientBounds(
+ initial_bounds,
+ parent->delegate()->ShouldDisplayURLField());
+ }
+
+ window->InitSizeForContents(window_bounds);
+
+ // This is a constrained popup window and thus we need to animate it in.
+ window->StartSuppressedAnimation();
+
+ return window;
+}
diff --git a/chrome/browser/views/constrained_window_impl.h b/chrome/browser/views/constrained_window_impl.h
new file mode 100644
index 0000000..f3916ab
--- /dev/null
+++ b/chrome/browser/views/constrained_window_impl.h
@@ -0,0 +1,214 @@
+// 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.
+
+#ifndef CHROME_BROWSER_CONSTRAINED_WINDOW_IMPL_H__
+#define CHROME_BROWSER_CONSTRAINED_WINDOW_IMPL_H__
+
+#include "chrome/browser/constrained_window.h"
+#include "chrome/browser/tab_contents_delegate.h"
+#include "chrome/views/client_view.h"
+#include "chrome/views/custom_frame_window.h"
+
+class ConstrainedTabContentsWindowDelegate;
+class ConstrainedWindowAnimation;
+class ConstrainedWindowNonClientView;
+
+///////////////////////////////////////////////////////////////////////////////
+// ConstrainedWindowImpl
+//
+// A ConstrainedWindow implementation that implements a Constrained Window as
+// a child HWND with a custom window frame.
+//
+class ConstrainedWindowImpl : public ConstrainedWindow,
+ public ChromeViews::CustomFrameWindow,
+ public TabContentsDelegate {
+ public:
+ virtual ~ConstrainedWindowImpl();
+
+ // Returns the TabContents that constrains this Constrained Window.
+ TabContents* owner() const { return owner_; }
+
+ TabContents* constrained_contents() const { return constrained_contents_; }
+ // Returns the non-client view inside this Constrained Window.
+ // NOTE: Defining the function body here would require pulling in the
+ // declarations of ConstrainedWindowNonClientView, as well as all the classes
+ // it depends on, from the .cc file; the benefit isn't worth it.
+ ConstrainedWindowNonClientView* non_client_view();
+
+ // Overridden from ConstrainedWindow:
+ virtual void CloseConstrainedWindow();
+ virtual void ActivateConstrainedWindow();
+ virtual void ResizeConstrainedWindow(int width, int height);
+ virtual void RepositionConstrainedWindowTo(const gfx::Point& anchor_point);
+ virtual bool IsSuppressedConstrainedWindow() const;
+ virtual void WasHidden();
+ virtual void DidBecomeSelected();
+ virtual std::wstring GetWindowTitle() const;
+ virtual void UpdateWindowTitle();
+ virtual const gfx::Rect& GetCurrentBounds() const;
+
+ // Overridden from TabContentsDelegate:
+ virtual void OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition);
+ virtual void NavigationStateChanged(const TabContents* source,
+ unsigned changed_flags);
+ virtual void ReplaceContents(TabContents* source,
+ TabContents* new_contents);
+ virtual void AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture);
+ virtual void ActivateContents(TabContents* contents);
+ virtual void LoadingStateChanged(TabContents* source);
+ virtual void CloseContents(TabContents* source);
+ virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
+ virtual bool IsPopup(TabContents* source);
+ virtual TabContents* GetConstrainingContents(TabContents* source);
+ virtual void ToolbarSizeChanged(TabContents* source, bool is_animating);
+ virtual void URLStarredChanged(TabContents* source, bool) {}
+ virtual void UpdateTargetURL(TabContents* source, const GURL& url) {}
+ virtual bool CanBlur() const { return false; }
+
+ virtual void NavigateToPage(TabContents* source, const GURL& url,
+ PageTransition::Type transition);
+
+
+ bool is_dialog() { return is_dialog_; }
+
+ // Changes the visibility of the titlebar. |percentage| is a real
+ // number ranged 0,1.
+ void SetTitlebarVisibilityPercentage(double percentage);
+
+ // Starts a ConstrainedWindowAnimation to slide in the titlebar of
+ // this suppressed constrained popup window.
+ void StartSuppressedAnimation();
+
+ protected:
+ // Windows message handlers:
+ virtual void OnDestroy();
+ virtual void OnFinalMessage(HWND window);
+ virtual void OnGetMinMaxInfo(LPMINMAXINFO mm_info);
+ virtual LRESULT OnMouseActivate(HWND window, UINT hittest_code, UINT message);
+ virtual void OnWindowPosChanged(WINDOWPOS* window_pos);
+
+ private:
+ // Use the static factory methods on ConstrainedWindow to construct a
+ // ConstrainedWindow.
+ ConstrainedWindowImpl(TabContents* owner);
+
+ friend class ConstrainedWindow;
+
+ // Called after changing either the anchor point or titlebar
+ // visibility of a suppressed popup. This does the actual resizing.
+ //
+ // @see RepositionConstrainedWindowTo
+ // @see SetTitlebarVisibilityPercentage
+ void ResizeConstrainedTitlebar();
+
+ // Initialize the Constrained Window as a Constrained Dialog containing a
+ // ChromeViews::View client area.
+ void InitAsDialog(const gfx::Rect& initial_bounds,
+ ChromeViews::View* contents_view,
+ ChromeViews::WindowDelegate* window_delegate);
+
+ // Builds the underlying HWND and window delegates for a newly
+ // created popup window.
+ //
+ // We have to split the initialization process for a popup window in
+ // two because we first need to initialize a proper window delegate
+ // so that when we query for desired size, we get accurate data. If
+ // we didn't do this, windows will initialize to being smaller then
+ // the desired content size plus room for browser chrome.
+ void InitWindowForContents(TabContents* constrained_contents);
+
+ // Sets the initial bounds for a newly created popup window.
+ //
+ // This is the second part of the initialization process started
+ // with InitWindowForContents. For the parameter initial_bounds to
+ // have been calculated correctly, InitWindowForContents must have
+ // been run first.
+ void InitSizeForContents(const gfx::Rect& initial_bounds);
+
+ // Returns true if the Constrained Window can be detached from its owner.
+ bool CanDetach() const;
+
+ // Detach the Constrained TabContents from its owner.
+ void Detach();
+
+ // Updates the portions of the UI as specified in |changed_flags|.
+ void UpdateUI(unsigned int changed_flags);
+
+ // Place and size the window, constraining to the bounds of the |owner_|.
+ void SetWindowBounds(const gfx::Rect& bounds);
+
+ // The TabContents that owns and constrains this ConstrainedWindow.
+ TabContents* owner_;
+
+ // The TabContents constrained by |owner_|.
+ TabContents* constrained_contents_;
+
+ // True if focus should not be restored to whatever view was focused last
+ // when this window is destroyed.
+ bool focus_restoration_disabled_;
+
+ // A default ChromeViews::WindowDelegate implementation for this window when
+ // a TabContents is being constrained. (For the Constrained Dialog case, the
+ // caller is required to provide the WindowDelegate).
+ scoped_ptr<ConstrainedTabContentsWindowDelegate> contents_window_delegate_;
+
+ // We keep a reference on the HWNDView so we can properly detach the tab
+ // contents when detaching.
+ ChromeViews::HWNDView* contents_container_;
+
+ // true if this window is really a constrained dialog. This is set by
+ // InitAsDialog().
+ bool is_dialog_;
+
+ // Current "anchor point", the lower right point at which we render
+ // the constrained title bar.
+ gfx::Point anchor_point_;
+
+ // The 0,1 percentage representing what amount of a titlebar of a
+ // suppressed popup window should be visible. Used to animate those
+ // titlebars in.
+ double titlebar_visibility_;
+
+ // The animation class which animates constrained windows onto the page.
+ scoped_ptr<ConstrainedWindowAnimation> animation_;
+
+ // Current display rectangle (relative to owner_'s visible area).
+ gfx::Rect current_bounds_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ConstrainedWindowImpl);
+};
+
+#endif // #ifndef CHROME_BROWSER_CONSTRAINED_WINDOW_IMPL_H__
diff --git a/chrome/browser/views/constrained_window_impl_interactive_uitest.cc b/chrome/browser/views/constrained_window_impl_interactive_uitest.cc
new file mode 100644
index 0000000..b06efd3
--- /dev/null
+++ b/chrome/browser/views/constrained_window_impl_interactive_uitest.cc
@@ -0,0 +1,112 @@
+// 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 <string>
+
+#include "base/file_util.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/test/automation/constrained_window_proxy.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/automation/window_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "chrome/views/event.h"
+#include "net/base/net_util.h"
+
+class InteractiveConstrainedWindowTest : public UITest {
+ protected:
+ InteractiveConstrainedWindowTest() {
+ show_window_ = true;
+ }
+};
+
+TEST_F(InteractiveConstrainedWindowTest, UserActivatedResizeToLeavesSpaceForChrome) {
+ scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser.get());
+
+ scoped_ptr<WindowProxy> window(
+ automation()->GetWindowForBrowser(browser.get()));
+ ASSERT_TRUE(window.get());
+
+ scoped_ptr<TabProxy> tab(browser->GetTab(0));
+ ASSERT_TRUE(tab.get());
+
+ std::wstring filename(test_data_directory_);
+ file_util::AppendToPath(&filename, L"constrained_files");
+ file_util::AppendToPath(&filename,
+ L"constrained_window_onload_resizeto.html");
+ ASSERT_TRUE(tab->NavigateToURL(net_util::FilePathToFileURL(filename)));
+
+ gfx::Rect tab_view_bounds;
+ ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
+ &tab_view_bounds, true));
+
+ // Simulate a click of the actual link to force user_gesture to be
+ // true; if we don't, the resulting popup will be constrained, which
+ // isn't what we want to test.
+ POINT link_point(tab_view_bounds.CenterPoint().ToPOINT());
+ ASSERT_TRUE(window->SimulateOSClick(link_point,
+ ChromeViews::Event::EF_LEFT_BUTTON_DOWN));
+
+ ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2, 1000));
+
+ scoped_ptr<BrowserProxy> popup_browser(automation()->GetBrowserWindow(1));
+ scoped_ptr<WindowProxy> popup_window(
+ automation()->GetWindowForBrowser(popup_browser.get()));
+
+ // Make sure we were created with the correct width and height.
+ gfx::Rect rect;
+ bool is_timeout = false;
+ ASSERT_TRUE(popup_window->GetViewBoundsWithTimeout(
+ VIEW_ID_TAB_CONTAINER, &rect, false, 1000, &is_timeout));
+ ASSERT_FALSE(is_timeout);
+ ASSERT_EQ(rect.width(), 300);
+ ASSERT_EQ(rect.height(), 320);
+
+ // Send a click to the popup window to test resizeTo.
+ ASSERT_TRUE(popup_window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
+ &tab_view_bounds, true));
+ POINT popup_link_point(tab_view_bounds.CenterPoint().ToPOINT());
+ ASSERT_TRUE(popup_window->SimulateOSClick(
+ popup_link_point, ChromeViews::Event::EF_LEFT_BUTTON_DOWN));
+
+ // No idea how to wait here other then sleeping.
+ Sleep(1000);
+
+ // The actual content will be LESS than (200, 200) because resizeTo
+ // deals with a window's outer{Width,Height} instead of its
+ // inner{Width,Height}.
+ is_timeout = false;
+ ASSERT_TRUE(popup_window->GetViewBoundsWithTimeout(
+ VIEW_ID_TAB_CONTAINER, &rect, false, 1000, &is_timeout));
+ ASSERT_FALSE(is_timeout);
+ ASSERT_LT(rect.width(), 200);
+ ASSERT_LT(rect.height(), 200);
+}
diff --git a/chrome/browser/views/delay_view.cc b/chrome/browser/views/delay_view.cc
new file mode 100644
index 0000000..fd964ea
--- /dev/null
+++ b/chrome/browser/views/delay_view.cc
@@ -0,0 +1,114 @@
+// 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/views/delay_view.h"
+
+#include "chrome/common/l10n_util.h"
+
+#include "generated_resources.h"
+
+// The amount of horizontal space between the throbber and the label.
+const int kThrobberLabelSpace = 7;
+
+// The amount of space between controls and the edge of the window.
+const int kWindowMargin = 5;
+
+DelayView::DelayView(const std::wstring& text, CommandController* controller,
+ bool show_cancel)
+ : controller_(controller),
+ label_(NULL),
+ cancel_button_(NULL) {
+ DCHECK(controller);
+
+ label_ = new ChromeViews::Label(text);
+ AddChildView(label_);
+
+ if (show_cancel) {
+ cancel_button_ =
+ new ChromeViews::NativeButton(l10n_util::GetString(IDS_CANCEL));
+ cancel_button_->SetID(ID_CANCEL);
+ cancel_button_->SetListener(this);
+ AddChildView(cancel_button_);
+ }
+
+ throbber_ = new ChromeViews::Throbber(50, true);
+ AddChildView(throbber_);
+ throbber_->Start();
+}
+
+DelayView::~DelayView() {
+}
+
+void DelayView::ButtonPressed(ChromeViews::NativeButton *sender) {
+ if (sender->GetID() == ID_CANCEL) {
+ controller_->ExecuteCommand(IDCANCEL);
+ }
+}
+
+void DelayView::Layout() {
+ if (!GetParent())
+ return;
+
+ CSize available;
+ GetParent()->GetSize(&available);
+
+ if (cancel_button_) {
+ CSize button_size;
+ cancel_button_->GetPreferredSize(&button_size);
+ cancel_button_->SetBounds(available.cx - kWindowMargin - button_size.cx,
+ available.cy - kWindowMargin - button_size.cy,
+ button_size.cx, button_size.cy);
+ }
+
+ DCHECK(label_);
+ CSize label_size;
+ label_->GetPreferredSize(&label_size);
+
+ DCHECK(throbber_);
+ CSize throbber_size;
+ throbber_->GetPreferredSize(&throbber_size);
+
+ CRect main_rect(0, 0,
+ throbber_size.cx + kThrobberLabelSpace + label_size.cx,
+ std::max(throbber_size.cy, label_size.cy));
+
+ main_rect.MoveToXY((available.cx / 2) - (main_rect.Width() / 2),
+ (available.cy / 2) - (main_rect.Height() / 2));
+
+ label_->SetBounds(main_rect.left + throbber_size.cx + kThrobberLabelSpace,
+ main_rect.top + main_rect.Height() / 2 - label_size.cy / 2,
+ label_size.cx,
+ label_size.cy);
+
+ throbber_->SetBounds(
+ main_rect.left,
+ main_rect.top + main_rect.Height() / 2 - throbber_size.cy / 2,
+ throbber_size.cx,
+ throbber_size.cy);
+} \ No newline at end of file
diff --git a/chrome/browser/views/delay_view.h b/chrome/browser/views/delay_view.h
new file mode 100644
index 0000000..b463305
--- /dev/null
+++ b/chrome/browser/views/delay_view.h
@@ -0,0 +1,73 @@
+// 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.
+
+// A simple view that indicates to the user that a time-consuming operation
+// is being performed, using a throbber and some explanatory text.
+
+#ifndef CHROME_BROWSER_VIEWS_DELAY_VIEW_H__
+#define CHROME_BROWSER_VIEWS_DELAY_VIEW_H__
+
+#include "chrome/browser/controller.h"
+#include "base/basictypes.h"
+#include "chrome/views/label.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/throbber.h"
+
+class DelayView : public ChromeViews::View,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ // |text| explains the delay
+ // |controller| receives notifications when the "cancel" button is pressed
+ // |show_cancel| determines whether the cancel button is shown
+ DelayView(const std::wstring& text,
+ CommandController* controller,
+ bool show_cancel);
+ virtual ~DelayView();
+
+ enum ViewID {
+ ID_CANCEL = 10000,
+ };
+
+ // Overridden from ChromeViews::View
+ virtual void Layout();
+
+ // Implemented from ChromeViews::NativeButton::Listener
+ virtual void ButtonPressed(ChromeViews::NativeButton *sender);
+
+ private:
+ CommandController* controller_;
+
+ ChromeViews::Label* label_;
+ ChromeViews::NativeButton* cancel_button_;
+ ChromeViews::Throbber* throbber_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DelayView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_DELAY_VIEW_H__
diff --git a/chrome/browser/views/dom_view.h b/chrome/browser/views/dom_view.h
new file mode 100644
index 0000000..07a5f71
--- /dev/null
+++ b/chrome/browser/views/dom_view.h
@@ -0,0 +1,92 @@
+// 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.
+
+// DOMView is a ChromeView that displays the content of a web DOM.
+// It should be used with data: URLs.
+
+#ifndef CHROME_BROWSER_VIEWS_DOM_VIEW_H__
+#define CHROME_BROWSER_VIEWS_DOM_VIEW_H__
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/browser/render_process_host.h"
+#include "chrome/browser/dom_ui/dom_ui_host.h"
+#include "chrome/views/hwnd_view.h"
+
+class DOMView : public ChromeViews::HWNDView {
+ public:
+ // Construct a DOMView to display the given data: URL.
+ explicit DOMView(const GURL& contents)
+ : contents_(contents), initialized_(false), host_(NULL) {}
+
+ virtual ~DOMView() {
+ if (host_) {
+ Detach();
+ host_->Destroy();
+ host_ = NULL;
+ }
+ }
+
+ // Initialize the view, causing it to load its contents. This should be
+ // called once the view has been added to a container.
+ // If |instance| is not null, then the view will be loaded in the same
+ // process as the given instance.
+ bool Init(Profile* profile, SiteInstance* instance) {
+ if (initialized_)
+ return true;
+ initialized_ = true;
+
+ // TODO(timsteele): This should use a separate factory method; e.g
+ // a DOMUIHostFactory rather than TabContentsFactory, because DOMView's
+ // should only be associated with instances of DOMUIHost.
+ TabContentsType type = TabContents::TypeForURL(&contents_);
+ TabContents* tab_contents = TabContents::CreateWithType(type,
+ GetViewContainer()->GetHWND(), profile, instance);
+ host_ = tab_contents->AsDOMUIHost();
+ DCHECK(host_);
+
+ ChromeViews::HWNDView::Attach(host_->GetContainerHWND());
+ host_->SetupController(profile);
+ host_->controller()->LoadURL(contents_, PageTransition::START_PAGE);
+ return true;
+ }
+
+ protected:
+ DOMUIHost* host_;
+
+ private:
+ GURL contents_;
+ bool initialized_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DOMView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_DOM_VIEW_H__
+
diff --git a/chrome/browser/views/download_item_view.cc b/chrome/browser/views/download_item_view.cc
new file mode 100644
index 0000000..4cd218d
--- /dev/null
+++ b/chrome/browser/views/download_item_view.cc
@@ -0,0 +1,585 @@
+// 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 <vector>
+
+#include "chrome/browser/views/download_item_view.h"
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/timer.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download_util.h"
+#include "chrome/browser/views/download_shelf_view.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/root_view.h"
+#include "chrome/views/view_container.h"
+#include "generated_resources.h"
+
+// TODO(paulg): These may need to be adjusted when download progress
+// animation is added, and also possibly to take into account
+// different screen resolutions.
+static const int kTextWidth = 140; // Pixels
+static const int kHorizontalTextPadding = 2; // Pixels
+static const int kVerticalPadding = 3; // Pixels
+static const int kVerticalTextSpacer = 2; // Pixels
+static const int kVerticalTextPadding = 2; // Pixels
+
+// We add some padding before the left image so that the progress animation icon
+// hides the corners of the left image.
+static const int kLeftPadding = 0; // Pixels.
+
+static const SkColor kFileNameColor = SkColorSetRGB(87, 108, 149);
+static const SkColor kStatusColor = SkColorSetRGB(123, 141, 174);
+
+// How long the 'download complete' animation should last for.
+static const int kCompleteAnimationDurationMs = 2500;
+
+// DownloadItemView ------------------------------------------------------------
+
+DownloadItemView::DownloadItemView(DownloadItem* download,
+ DownloadShelfView* parent,
+ DownloadItemView::BaseDownloadItemModel* model)
+ : download_(download),
+ parent_(parent),
+ model_(model),
+ progress_angle_(download_util::kStartAngleDegrees),
+ progress_timer_(NULL),
+ progress_task_(NULL),
+ body_state_(NORMAL),
+ drop_down_state_(NORMAL),
+ drop_down_pressed_(false),
+ file_name_(download_->file_name()),
+ status_text_(l10n_util::GetString(IDS_DOWNLOAD_STATUS_STARTING)),
+ show_status_text_(true),
+ dragging_(false),
+ starting_drag_(false) {
+ // TODO(idana) Bug# 1163334
+ //
+ // We currently do not mirror each download item on the download shelf (even
+ // though the download shelf itself is mirrored and the items appear from
+ // right to left on RTL UIs).
+ //
+ // We explicitly disable mirroring for the item because the code that draws
+ // the download progress animation relies on the View's UI layout setting
+ // when positioning the animation so we should make sure that code doesn't
+ // treat our View as a mirrored View.
+ EnableUIMirroringForRTLLanguages(false);
+ DCHECK(download_);
+ download_->AddObserver(this);
+
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+
+ BodyImageSet normal_body_image_set = {
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM)
+ };
+ normal_body_image_set_ = normal_body_image_set;
+
+ DropDownImageSet normal_drop_down_image_set = {
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_TOP),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_MIDDLE),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_BOTTOM)
+ };
+ normal_drop_down_image_set_ = normal_drop_down_image_set;
+
+ BodyImageSet hot_body_image_set = {
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_H)
+ };
+ hot_body_image_set_ = hot_body_image_set;
+
+ DropDownImageSet hot_drop_down_image_set = {
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_TOP_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_H),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_H)
+ };
+ hot_drop_down_image_set_ = hot_drop_down_image_set;
+
+ BodyImageSet pushed_body_image_set = {
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_P)
+ };
+ pushed_body_image_set_ = pushed_body_image_set;
+
+ DropDownImageSet pushed_drop_down_image_set = {
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_TOP_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_P),
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_P)
+ };
+ pushed_drop_down_image_set_ = pushed_drop_down_image_set;
+
+ LoadIcon();
+
+ font_ = ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
+ box_height_ = std::max<int>(2 * kVerticalPadding + font_.height() +
+ kVerticalTextPadding + font_.height(),
+ 2 * kVerticalPadding +
+ normal_body_image_set_.top_left->height() +
+ normal_body_image_set_.bottom_left->height());
+
+ int progress_icon_size =
+ download_util::GetProgressIconSize(download_util::SMALL);
+ if (progress_icon_size > box_height_)
+ box_y_ = (progress_icon_size - box_height_) / 2;
+ else
+ box_y_ = kVerticalPadding;
+
+ CSize size;
+ GetPreferredSize(&size);
+ drop_down_x_ = size.cx - normal_drop_down_image_set_.top->width();
+
+ body_hover_animation_.reset(new SlideAnimation(this));
+ drop_hover_animation_.reset(new SlideAnimation(this));
+
+ // Set up our animation
+ StartDownloadProgress();
+}
+
+DownloadItemView::~DownloadItemView() {
+ icon_consumer_.CancelAllRequests();
+ StopDownloadProgress();
+ download_->RemoveObserver(this);
+}
+
+// Progress animation handlers
+
+void DownloadItemView::UpdateDownloadProgress() {
+ progress_angle_ = (progress_angle_ +
+ download_util::kUnknownIncrementDegrees) %
+ download_util::kMaxDegrees;
+ SchedulePaint();
+}
+
+void DownloadItemView::StartDownloadProgress() {
+ if (progress_task_ || progress_timer_)
+ return;
+ progress_task_ =
+ new download_util::DownloadProgressTask<DownloadItemView>(this);
+ progress_timer_ =
+ MessageLoop::current()->timer_manager()->
+ StartTimer(download_util::kProgressRateMs, progress_task_, true);
+}
+
+void DownloadItemView::StopDownloadProgress() {
+ if (progress_timer_) {
+ DCHECK(progress_task_);
+ MessageLoop::current()->timer_manager()->StopTimer(progress_timer_);
+ delete progress_timer_;
+ progress_timer_ = NULL;
+ delete progress_task_;
+ progress_task_ = NULL;
+ }
+}
+
+// DownloadObserver interface
+
+// Update the progress graphic on the icon and our text status label
+// to reflect our current bytes downloaded, time remaining.
+void DownloadItemView::OnDownloadUpdated(DownloadItem* download) {
+ DCHECK(download == download_);
+
+ std::wstring status_text = model_->GetStatusText();
+ switch (download_->state()) {
+ case DownloadItem::IN_PROGRESS:
+ download_->is_paused() ? StopDownloadProgress() : StartDownloadProgress();
+ break;
+ case DownloadItem::COMPLETE:
+ StopDownloadProgress();
+ complete_animation_.reset(new SlideAnimation(this));
+ complete_animation_->SetSlideDuration(kCompleteAnimationDurationMs);
+ complete_animation_->SetTweenType(SlideAnimation::NONE);
+ complete_animation_->Show();
+ if (status_text.empty())
+ show_status_text_ = false;
+ SchedulePaint();
+ LoadIcon();
+ break;
+ case DownloadItem::CANCELLED:
+ StopDownloadProgress();
+ LoadIcon();
+ break;
+ case DownloadItem::REMOVING:
+ parent_->RemoveDownloadView(this); // This will delete us!
+ return;
+ default:
+ NOTREACHED();
+ }
+
+ status_text_ = status_text;
+
+ // We use the parent's (DownloadShelfView's) SchedulePaint, since there
+ // are spaces between each DownloadItemView that the parent is responsible
+ // for painting.
+ GetParent()->SchedulePaint();
+}
+
+// View overrides
+
+// Load an icon for the file type we're downloading, and animate any in progress
+// download state.
+void DownloadItemView::Paint(ChromeCanvas* canvas) {
+ int height = GetHeight();
+ int center_width = GetWidth() - kLeftPadding -
+ normal_body_image_set_.left->width() -
+ normal_body_image_set_.right->width() -
+ normal_drop_down_image_set_.center->width();
+
+ // May be caused by animation.
+ if (center_width <= 0)
+ return;
+
+ BodyImageSet* body_image_set;
+ switch (body_state_) {
+ case NORMAL:
+ case HOT:
+ body_image_set = &normal_body_image_set_;
+ break;
+ case PUSHED:
+ body_image_set = &pushed_body_image_set_;
+ break;
+ default:
+ NOTREACHED();
+ }
+ DropDownImageSet* drop_down_image_set;
+ switch (drop_down_state_) {
+ case NORMAL:
+ case HOT:
+ drop_down_image_set = &normal_drop_down_image_set_;
+ break;
+ case PUSHED:
+ drop_down_image_set = &pushed_drop_down_image_set_;
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // Paint the background images.
+ int x = kLeftPadding;
+ PaintBitmaps(canvas,
+ body_image_set->top_left, body_image_set->left,
+ body_image_set->bottom_left,
+ x, box_y_, box_height_, body_image_set->top_left->width());
+ x += body_image_set->top_left->width();
+ PaintBitmaps(canvas,
+ body_image_set->top, body_image_set->center,
+ body_image_set->bottom,
+ x, box_y_, box_height_, center_width);
+ x += center_width;
+ PaintBitmaps(canvas,
+ body_image_set->top_right, body_image_set->right,
+ body_image_set->bottom_right,
+ x, box_y_, box_height_, body_image_set->top_right->width());
+
+ // Overlay our body hot state.
+ if (body_hover_animation_->GetCurrentValue() > 0) {
+ canvas->saveLayerAlpha(NULL,
+ static_cast<int>(body_hover_animation_->GetCurrentValue() * 255),
+ SkCanvas::kARGB_NoClipLayer_SaveFlag);
+ canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode);
+
+ int x = kLeftPadding;
+ PaintBitmaps(canvas,
+ hot_body_image_set_.top_left, hot_body_image_set_.left,
+ hot_body_image_set_.bottom_left,
+ x, box_y_, box_height_, hot_body_image_set_.top_left->width());
+ x += body_image_set->top_left->width();
+ PaintBitmaps(canvas,
+ hot_body_image_set_.top, hot_body_image_set_.center,
+ hot_body_image_set_.bottom,
+ x, box_y_, box_height_, center_width);
+ x += center_width;
+ PaintBitmaps(canvas,
+ hot_body_image_set_.top_right, hot_body_image_set_.right,
+ hot_body_image_set_.bottom_right,
+ x, box_y_, box_height_, hot_body_image_set_.top_right->width());
+ canvas->restore();
+ }
+
+ x += body_image_set->top_right->width();
+ PaintBitmaps(canvas,
+ drop_down_image_set->top, drop_down_image_set->center,
+ drop_down_image_set->bottom,
+ x, box_y_, box_height_, drop_down_image_set->top->width());
+
+ // Overlay our drop-down hot state.
+ if (drop_hover_animation_->GetCurrentValue() > 0) {
+ canvas->saveLayerAlpha(NULL,
+ static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255),
+ SkCanvas::kARGB_NoClipLayer_SaveFlag);
+ canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode);
+
+ PaintBitmaps(canvas,
+ drop_down_image_set->top, drop_down_image_set->center,
+ drop_down_image_set->bottom,
+ x, box_y_, box_height_, drop_down_image_set->top->width());
+
+ canvas->restore();
+ }
+
+ // Print the text, left aligned.
+ int progress_icon_size =
+ download_util::GetProgressIconSize(download_util::SMALL);
+ // Last value of x was the end of the right image, just before the button.
+ if (show_status_text_) {
+ int y = box_y_ + kVerticalPadding;
+ canvas->DrawStringInt(file_name_, font_, kFileNameColor,
+ progress_icon_size, y, kTextWidth, font_.height());
+ y += font_.height() + kVerticalTextPadding;
+
+ canvas->DrawStringInt(status_text_, font_, kStatusColor,
+ progress_icon_size, y, kTextWidth, font_.height());
+ } else {
+ int y = box_y_ + (box_height_ - font_.height()) / 2;
+ canvas->DrawStringInt(file_name_, font_, kFileNameColor,
+ progress_icon_size, y, kTextWidth, font_.height());
+ }
+
+ // Paint the icon.
+ IconManager* im = g_browser_process->icon_manager();
+ SkBitmap* icon = im->LookupIcon(download_->full_path(), IconLoader::SMALL);
+
+ if (icon) {
+ if (download_->state() == DownloadItem::IN_PROGRESS) {
+ download_util::PaintDownloadProgress(canvas, this, 0, 0,
+ progress_angle_,
+ download_->PercentComplete(),
+ download_util::SMALL);
+ } else if (download_->state() == DownloadItem::COMPLETE &&
+ complete_animation_->IsAnimating()) {
+ download_util::PaintDownloadComplete(canvas, this, 0, 0,
+ complete_animation_->GetCurrentValue(),
+ download_util::SMALL);
+ }
+
+ // Draw the icon image
+ int offset = download_util::GetProgressIconOffset(download_util::SMALL);
+ canvas->DrawBitmapInt(*icon, offset, offset);
+ }
+}
+
+void DownloadItemView::PaintBitmaps(ChromeCanvas* canvas,
+ const SkBitmap* top_bitmap,
+ const SkBitmap* center_bitmap,
+ const SkBitmap* bottom_bitmap,
+ int x, int y, int height, int width) {
+ int middle_height = height - top_bitmap->height() - bottom_bitmap->height();
+ // Draw the top.
+ canvas->DrawBitmapInt(*top_bitmap,
+ 0, 0, top_bitmap->width(), top_bitmap->height(),
+ x, y, width, top_bitmap->height(), false);
+ y += top_bitmap->height();
+ // Draw the center.
+ canvas->DrawBitmapInt(*center_bitmap,
+ 0, 0, center_bitmap->width(), center_bitmap->height(),
+ x, y, width, middle_height, false);
+ y += middle_height;
+ // Draw the bottom.
+ canvas->DrawBitmapInt(*bottom_bitmap,
+ 0, 0, bottom_bitmap->width(), bottom_bitmap->height(),
+ x, y, width, bottom_bitmap->height(), false);
+}
+
+void DownloadItemView::SetState(State body_state, State drop_down_state) {
+ if (body_state_ == body_state && drop_down_state_ == drop_down_state)
+ return;
+
+ body_state_ = body_state;
+ drop_down_state_ = drop_down_state;
+ SchedulePaint();
+}
+
+void DownloadItemView::GetPreferredSize(CSize* out) {
+ int width = kLeftPadding + normal_body_image_set_.top_left->width();
+ width += download_util::GetProgressIconSize(download_util::SMALL);
+ width += kTextWidth;
+ width += normal_body_image_set_.top_right->width();
+ width += normal_drop_down_image_set_.top->width();
+
+ out->cx = width;
+ out->cy = std::max<int>(
+ 2 * kVerticalPadding + 2 * font_.height() + kVerticalTextPadding,
+ download_util::GetProgressIconSize(download_util::SMALL));
+}
+
+void DownloadItemView::OnMouseExited(const ChromeViews::MouseEvent& event) {
+ SetState(NORMAL, drop_down_pressed_ ? PUSHED : NORMAL);
+ body_hover_animation_->Hide();
+ drop_hover_animation_->Hide();
+}
+
+// Display the context menu for this item.
+bool DownloadItemView::OnMousePressed(const ChromeViews::MouseEvent& event) {
+ // Stop any completion animation.
+ if (complete_animation_.get() && complete_animation_->IsAnimating())
+ complete_animation_->End();
+
+ if (event.IsOnlyLeftMouseButton()) {
+ CPoint point(event.GetLocation());
+ if (event.GetX() < drop_down_x_) {
+ SetState(PUSHED, NORMAL);
+ return true;
+ }
+
+ drop_down_pressed_ = true;
+ SetState(NORMAL, PUSHED);
+
+ // Similar hack as in MenuButton.
+ // We're about to show the menu from a mouse press. By showing from the
+ // mouse press event we block RootView in mouse dispatching. This also
+ // appears to cause RootView to get a mouse pressed BEFORE the mouse
+ // release is seen, which means RootView sends us another mouse press no
+ // matter where the user pressed. To force RootView to recalculate the
+ // mouse target during the mouse press we explicitly set the mouse handler
+ // to NULL.
+ GetRootView()->SetMouseHandler(NULL);
+
+ // The menu's position is different depending on the UI layout.
+ // DownloadShelfContextMenu will take care of setting the right anchor for
+ // the menu depending on the locale.
+ //
+ // TODO(idana): when bug# 1163334 is fixed the following check should be
+ // replaced with UILayoutIsRightToLeft().
+ point.y = GetHeight();
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
+ point.x = GetWidth();
+ } else {
+ point.x = drop_down_x_;
+ }
+
+ ChromeViews::View::ConvertPointToScreen(this, &point);
+ download_util::DownloadShelfContextMenu menu(download_,
+ GetViewContainer()->GetHWND(),
+ model_.get(),
+ point);
+ drop_down_pressed_ = false;
+ // Showing the menu blocks. Here we revert the state.
+ SetState(NORMAL, NORMAL);
+ }
+ return true;
+}
+
+void DownloadItemView::OnMouseMoved(const ChromeViews::MouseEvent& event) {
+ bool on_body = event.GetX() < drop_down_x_;
+ SetState(on_body ? HOT : NORMAL, on_body ? NORMAL : HOT);
+ if (on_body) {
+ body_hover_animation_->Show();
+ drop_hover_animation_->Hide();
+ } else {
+ body_hover_animation_->Hide();
+ drop_hover_animation_->Show();
+ }
+}
+
+void DownloadItemView::OnMouseReleased(const ChromeViews::MouseEvent& event,
+ bool canceled) {
+ if (dragging_) {
+ // Starting a drag results in a MouseReleased, we need to ignore it.
+ dragging_ = false;
+ starting_drag_ = false;
+ return;
+ }
+ CPoint point(event.GetLocation());
+ if (event.IsOnlyLeftMouseButton() && event.GetX() < drop_down_x_)
+ OpenDownload();
+
+ SetState(NORMAL, NORMAL);
+}
+
+// Handle drag (file copy) operations.
+bool DownloadItemView::OnMouseDragged(const ChromeViews::MouseEvent& event) {
+ if (!starting_drag_) {
+ starting_drag_ = true;
+ drag_start_point_ = event.location();
+ }
+ if (dragging_) {
+ if (download_->state() == DownloadItem::COMPLETE) {
+ IconManager* im = g_browser_process->icon_manager();
+ SkBitmap* icon = im->LookupIcon(download_->full_path(),
+ IconLoader::SMALL);
+ if (icon)
+ download_util::DragDownload(download_, icon);
+ }
+ } else if (win_util::IsDrag(drag_start_point_.ToPOINT(),
+ event.location().ToPOINT())) {
+ dragging_ = true;
+ }
+ return true;
+}
+
+void DownloadItemView::AnimationProgressed(const Animation* animation) {
+ // We don't care if what animation (body button/drop button/complete),
+ // is calling back, as they all have to go through the same paint call.
+ SchedulePaint();
+}
+
+void DownloadItemView::OpenDownload() {
+ if (download_->state() == DownloadItem::IN_PROGRESS) {
+ download_->set_open_when_complete(!download_->open_when_complete());
+ } else if (download_->state() == DownloadItem::COMPLETE) {
+ download_util::OpenDownload(download_);
+ }
+}
+
+void DownloadItemView::OnExtractIconComplete(IconManager::Handle handle,
+ SkBitmap* icon_bitmap) {
+ GetParent()->SchedulePaint();
+}
+
+void DownloadItemView::LoadIcon() {
+ IconManager* im = g_browser_process->icon_manager();
+ im->LoadIcon(download_->full_path(), IconLoader::SMALL,
+ &icon_consumer_,
+ NewCallback(this, &DownloadItemView::OnExtractIconComplete));
+}
diff --git a/chrome/browser/views/download_item_view.h b/chrome/browser/views/download_item_view.h
new file mode 100644
index 0000000..0a03732
--- /dev/null
+++ b/chrome/browser/views/download_item_view.h
@@ -0,0 +1,222 @@
+// 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.
+//
+// A ChromeView that implements one download on the Download shelf.
+// Each DownloadItemView contains an application icon, a text label
+// indicating the download's file name, a text label indicating the
+// download's status (such as the number of bytes downloaded so far)
+// and a button for canceling an in progress download, or opening
+// the completed download.
+//
+// The DownloadItemView lives in the Browser, and has a corresponding
+// DownloadController that receives / writes data which lives in the
+// Renderer.
+
+#ifndef CHROME_BROWSER_VIEWS_DOWNLOAD_ITEM_VIEW_H__
+#define CHROME_BROWSER_VIEWS_DOWNLOAD_ITEM_VIEW_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/slide_animation.h"
+#include "chrome/browser/cancelable_request.h"
+#include "chrome/browser/download_manager.h"
+#include "chrome/browser/icon_manager.h"
+#include "chrome/views/event.h"
+#include "chrome/views/view.h"
+#include "chrome/views/label.h"
+
+class DownloadShelfView;
+class SkBitmap;
+class Task;
+class Timer;
+
+class DownloadItemView : public ChromeViews::View,
+ public DownloadItem::Observer,
+ public AnimationDelegate {
+ public:
+ // This class provides functions which have different behaviors between
+ // download and saving page.
+ class BaseDownloadItemModel {
+ public:
+ // Cancel the task corresponding to the item.
+ virtual void CancelTask() = 0;
+
+ // Get the status text to display.
+ virtual std::wstring GetStatusText() = 0;
+ };
+
+ DownloadItemView(DownloadItem* download,
+ DownloadShelfView* parent,
+ BaseDownloadItemModel* model);
+ virtual ~DownloadItemView();
+
+ // DownloadObserver method
+ virtual void OnDownloadUpdated(DownloadItem* download);
+
+ // View overrides
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void GetPreferredSize(CSize *out);
+ virtual void OnMouseExited(const ChromeViews::MouseEvent& event);
+ virtual void OnMouseMoved(const ChromeViews::MouseEvent& event);
+ virtual bool OnMousePressed(const ChromeViews::MouseEvent& event);
+ virtual void OnMouseReleased(const ChromeViews::MouseEvent& event,
+ bool canceled);
+ virtual bool OnMouseDragged(const ChromeViews::MouseEvent& event);
+
+ // AnimationDelegate implementation.
+ virtual void AnimationProgressed(const Animation* animation);
+
+ // Timer callback for handling animations
+ void UpdateDownloadProgress();
+ void StartDownloadProgress();
+ void StopDownloadProgress();
+
+ // IconManager::Client interface.
+ void OnExtractIconComplete(IconManager::Handle handle, SkBitmap* icon_bitmap);
+
+ private:
+ enum State {
+ NORMAL = 0,
+ HOT,
+ PUSHED,
+ };
+
+ // The image set associated with the part containing the icon and text.
+ struct BodyImageSet {
+ SkBitmap* top_left;
+ SkBitmap* left;
+ SkBitmap* bottom_left;
+ SkBitmap* top;
+ SkBitmap* center;
+ SkBitmap* bottom;
+ SkBitmap* top_right;
+ SkBitmap* right;
+ SkBitmap* bottom_right;
+ };
+
+ // The image set associated with the drop-down button on the right.
+ struct DropDownImageSet {
+ SkBitmap* top;
+ SkBitmap* center;
+ SkBitmap* bottom;
+ };
+
+ void OpenDownload();
+
+ void LoadIcon();
+
+ // Convenience method to paint the 3 vertical bitmaps (bottom, middle, top)
+ // that form the background.
+ void PaintBitmaps(ChromeCanvas* canvas,
+ const SkBitmap* top_bitmap,
+ const SkBitmap* center_bitmap,
+ const SkBitmap* bottom_bitmap,
+ int x,
+ int y,
+ int height,
+ int width);
+
+ // Sets the state and triggers a repaint.
+ void SetState(State body_state, State drop_down_state);
+
+ // The different images used for the background.
+ BodyImageSet normal_body_image_set_;
+ BodyImageSet hot_body_image_set_;
+ BodyImageSet pushed_body_image_set_;
+ DropDownImageSet normal_drop_down_image_set_;
+ DropDownImageSet hot_drop_down_image_set_;
+ DropDownImageSet pushed_drop_down_image_set_;
+
+ // The model we query for display information
+ DownloadItem* download_;
+
+ // Our parent view that owns us.
+ DownloadShelfView* parent_;
+
+ // Elements of our particular download
+ std::wstring file_name_;
+ std::wstring status_text_;
+ bool show_status_text_;
+
+ // The font used to print the file name and status.
+ ChromeFont font_;
+
+ // The current state (normal, hot or pushed) of the body and drop-down.
+ State body_state_;
+ State drop_down_state_;
+
+ // In degrees, for downloads with no known total size.
+ int progress_angle_;
+
+ // The x coordinate at which the drop-down button starts.
+ int drop_down_x_;
+
+ // Used when we are showing the menu to show the drop-down as pressed.
+ bool drop_down_pressed_;
+
+ // The height of the box formed by the background images and its labels.
+ int box_height_;
+
+ // The y coordinate of the box formed by the background images and its labels.
+ int box_y_;
+
+ // Whether we are dragging the download button.
+ bool dragging_;
+
+ // Whether we are tracking a possible drag.
+ bool starting_drag_;
+
+ // Position that a possible drag started at.
+ gfx::Point drag_start_point_;
+
+ // For canceling an in progress icon request.
+ CancelableRequestConsumerT<int, 0> icon_consumer_;
+
+ // A model class to control the status text we display and the cancel
+ // behavior.
+ // This class owns the pointer.
+ scoped_ptr<BaseDownloadItemModel> model_;
+
+ // Hover animations for our body and drop buttons.
+ scoped_ptr<SlideAnimation> body_hover_animation_;
+ scoped_ptr<SlideAnimation> drop_hover_animation_;
+
+ // Animation for download complete.
+ scoped_ptr<SlideAnimation> complete_animation_;
+
+ // Progress animation
+ Timer* progress_timer_;
+ Task* progress_task_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DownloadItemView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_DOWNLOAD_ITEM_VIEW_H__
diff --git a/chrome/browser/views/download_shelf_view.cc b/chrome/browser/views/download_shelf_view.cc
new file mode 100644
index 0000000..49f7f67
--- /dev/null
+++ b/chrome/browser/views/download_shelf_view.cc
@@ -0,0 +1,300 @@
+// 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/views/download_shelf_view.h"
+
+#include <algorithm>
+
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/download_item_model.h"
+#include "chrome/browser/download_manager.h"
+#include "chrome/browser/download_tab_view.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/views/download_item_view.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "base/logging.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/background.h"
+#include "chrome/views/button.h"
+#include "chrome/views/image_view.h"
+
+#include "generated_resources.h"
+
+// Max number of download views we'll contain. Any time a view is added and
+// we already have this many download views, one is removed.
+static const int kMaxDownloadViews = 15;
+
+// Padding from left edge and first download view.
+static const int kLeftPadding = 2;
+
+// Padding from right edge and close button/show downloads link.
+static const int kRightPadding = 10;
+
+// Padding between the show all link and close button.
+static const int kCloseAndLinkPadding = 14;
+
+// Padding between the download views.
+static const int kDownloadPadding = 10;
+
+// Padding between the top/bottom and the content.
+static const int kTopBottomPadding = 2;
+
+// Padding between the icon and 'show all downloads' link
+static const int kDownloadsTitlePadding = 4;
+
+// Default background color for the shelf.
+static const SkColor kBackgroundColor = SkColorSetRGB(230, 237, 244);
+
+// Border color.
+static const SkColor kBorderColor = SkColorSetRGB(214, 214, 214);
+
+// New download item animation speed in milliseconds.
+static const int kNewItemAnimationDurationMs = 800;
+
+// Shelf show/hide speed.
+static const int kShelfAnimationDurationMs = 120;
+
+namespace {
+
+// Sets size->cx to view's preferred width + size->cx.
+// Sets size->cy to the max of the view's preferred height and size->cy;
+void AdjustSize(ChromeViews::View* view, CSize* size) {
+ CSize view_preferred;
+ view->GetPreferredSize(&view_preferred);
+ size->cx += view_preferred.cx;
+ size->cy = std::max(view_preferred.cy, size->cy);
+}
+
+int CenterPosition(int size, int target_size) {
+ return std::max((target_size - size) / 2, kTopBottomPadding);
+}
+
+} // namespace
+
+DownloadShelfView::DownloadShelfView(TabContents* tab_contents)
+ : tab_contents_(tab_contents) {
+ Init();
+}
+
+void DownloadShelfView::Init() {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ arrow_image_ = new ChromeViews::ImageView();
+ arrow_image_->SetImage(rb.GetBitmapNamed(IDR_DOWNLOADS_FAVICON));
+ AddChildView(arrow_image_);
+
+ show_all_view_ =
+ new ChromeViews::Link(l10n_util::GetString(IDS_SHOW_ALL_DOWNLOADS));
+ show_all_view_->SetController(this);
+ AddChildView(show_all_view_);
+
+ close_button_ = new ChromeViews::Button();
+ close_button_->SetImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR));
+ close_button_->SetImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR_H));
+ close_button_->SetImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR_P));
+ close_button_->SetListener(this, 0);
+ AddChildView(close_button_);
+ SetBackground(
+ ChromeViews::Background::CreateSolidBackground(kBackgroundColor));
+
+ new_item_animation_.reset(new SlideAnimation(this));
+ new_item_animation_->SetSlideDuration(kNewItemAnimationDurationMs);
+
+ shelf_animation_.reset(new SlideAnimation(this));
+ shelf_animation_->SetSlideDuration(kShelfAnimationDurationMs);
+ shelf_animation_->Show();
+}
+
+void DownloadShelfView::AddDownloadView(View* view) {
+ DCHECK(view);
+ download_views_.push_back(view);
+ AddChildView(view);
+ if (download_views_.size() > kMaxDownloadViews)
+ RemoveDownloadView(*download_views_.begin());
+
+ new_item_animation_->Reset();
+ new_item_animation_->Show();
+}
+
+void DownloadShelfView::ChangeTabContents(TabContents* old_contents,
+ TabContents* new_contents) {
+ DCHECK(old_contents == tab_contents_);
+ tab_contents_ = new_contents;
+}
+
+void DownloadShelfView::AddDownload(DownloadItem* download) {
+ shelf_animation_->Show();
+
+ DownloadItemView* view = new DownloadItemView(
+ download, this, new DownloadItemModel(download));
+ AddDownloadView(view);
+}
+
+void DownloadShelfView::RemoveDownloadView(View* view) {
+ DCHECK(view);
+ std::vector<View*>::iterator i =
+ find(download_views_.begin(), download_views_.end(), view);
+ DCHECK(i != download_views_.end());
+ download_views_.erase(i);
+ RemoveChildView(view);
+ delete view;
+ if (download_views_.empty())
+ tab_contents_->SetDownloadShelfVisible(false);
+ Layout();
+ SchedulePaint();
+}
+
+void DownloadShelfView::Paint(ChromeCanvas* canvas) {
+ PaintBackground(canvas);
+ PaintBorder(canvas);
+}
+
+void DownloadShelfView::PaintBorder(ChromeCanvas* canvas) {
+ canvas->FillRectInt(kBorderColor, 0, 0, GetWidth(), 1);
+}
+
+void DownloadShelfView::GetPreferredSize(CSize *out) {
+ out->cx = kRightPadding + kLeftPadding + kCloseAndLinkPadding;
+ out->cy = 0;
+ AdjustSize(close_button_, out);
+ AdjustSize(show_all_view_, out);
+ // Add one download view to the preferred size.
+ if (download_views_.size() > 0) {
+ AdjustSize(*download_views_.begin(), out);
+ out->cx += kDownloadPadding;
+ }
+ out->cy += kTopBottomPadding + kTopBottomPadding;
+ if (shelf_animation_->IsAnimating()) {
+ out->cy = static_cast<int>(static_cast<double>(out->cy) *
+ shelf_animation_->GetCurrentValue());
+ }
+}
+
+void DownloadShelfView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void DownloadShelfView::AnimationProgressed(const Animation *animation) {
+ if (animation == new_item_animation_.get()) {
+ Layout();
+ SchedulePaint();
+ } else if (animation == shelf_animation_.get()) {
+ // Force a re-layout of the parent, which will call back into
+ // GetPreferredSize, where we will do our animation. In the case where the
+ // animation is hiding, we do a full resize - the fast resizing would
+ // otherwise leave blank white areas where the shelf was and where the
+ // user's eye is. Thankfully bottom-resizing is a lot faster than
+ // top-resizing.
+ tab_contents_->ToolbarSizeChanged(shelf_animation_->IsShowing());
+ }
+}
+
+void DownloadShelfView::AnimationEnded(const Animation *animation) {
+ if (animation == shelf_animation_.get()) {
+ if (shelf_animation_->IsShowing() == false)
+ tab_contents_->SetDownloadShelfVisible(false);
+ tab_contents_->ToolbarSizeChanged(false);
+ }
+}
+
+void DownloadShelfView::Layout() {
+ int width = GetWidth();
+ int height = GetHeight();
+ CSize image_size;
+ arrow_image_->GetPreferredSize(&image_size);
+ CSize close_button_size;
+ close_button_->GetPreferredSize(&close_button_size);
+ CSize show_all_size;
+ show_all_view_->GetPreferredSize(&show_all_size);
+ int max_download_x =
+ std::max<int>(0, width - kRightPadding - close_button_size.cx -
+ kCloseAndLinkPadding - show_all_size.cx -
+ image_size.cx - kDownloadPadding);
+ int next_x = max_download_x + kDownloadPadding;
+ // Align vertically with show_all_view_.
+ arrow_image_->SetBounds(next_x, CenterPosition(show_all_size.cy, height),
+ image_size.cx, image_size.cy);
+ next_x += image_size.cx + kDownloadsTitlePadding;
+ show_all_view_->SetBounds(next_x,
+ CenterPosition(show_all_size.cy, height),
+ show_all_size.cx,
+ show_all_size.cy);
+ next_x += show_all_size.cx + kCloseAndLinkPadding;
+ close_button_->SetBounds(next_x,
+ CenterPosition(close_button_size.cy, height),
+ close_button_size.cx,
+ close_button_size.cy);
+
+ next_x = kLeftPadding;
+ std::vector<View*>::reverse_iterator ri;
+ for (ri = download_views_.rbegin(); ri != download_views_.rend(); ++ri) {
+ CSize view_size;
+ (*ri)->GetPreferredSize(&view_size);
+
+ int x = next_x;
+
+ // Figure out width of item.
+ int width = view_size.cx;
+ if (new_item_animation_->IsAnimating() && ri == download_views_.rbegin()) {
+ width = static_cast<int>(static_cast<double>(view_size.cx) *
+ new_item_animation_->GetCurrentValue());
+ }
+
+ next_x += (width + kDownloadPadding);
+
+ // Make sure our item can be contained within the shelf.
+ if (next_x < max_download_x) {
+ (*ri)->SetVisible(true);
+ (*ri)->SetBounds(x, CenterPosition(view_size.cy, height), width,
+ view_size.cy);
+ } else {
+ (*ri)->SetVisible(false);
+ }
+ }
+}
+
+// Open the download page.
+void DownloadShelfView::LinkActivated(ChromeViews::Link* source,
+ int event_flags) {
+ int index;
+ NavigationController* controller = tab_contents_->controller();
+ Browser* browser = Browser::GetBrowserForController(controller, &index);
+ DCHECK(browser);
+ browser->ShowNativeUI(DownloadTabUI::GetURL());
+}
+
+void DownloadShelfView::ButtonPressed(ChromeViews::BaseButton* button) {
+ shelf_animation_->Hide();
+} \ No newline at end of file
diff --git a/chrome/browser/views/download_shelf_view.h b/chrome/browser/views/download_shelf_view.h
new file mode 100644
index 0000000..2c98ec3
--- /dev/null
+++ b/chrome/browser/views/download_shelf_view.h
@@ -0,0 +1,139 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_DOWNLOAD_SHELF_VIEW_H__
+#define CHROME_BROWSER_VIEWS_DOWNLOAD_SHELF_VIEW_H__
+
+#include "chrome/common/slide_animation.h"
+#include "chrome/views/button.h"
+#include "chrome/views/link.h"
+
+namespace ChromeViews {
+ class ImageView;
+}
+
+class DownloadItem;
+class TabContents;
+
+class DownloadAnimation;
+
+// DownloadShelfView is a view that contains individual views for each download,
+// as well as a close button and a link to show all downloads.
+//
+// To add a view representing a download to DownloadShelfView, invoke
+// AddDownloadView. AddDownloadView takes ownership of the passed in View.
+// DownloadShelfView does not hold an infinite number of download views, rather
+// it'll automatically remove views once a certain point is reached. As such,
+// the remove method is private.
+class DownloadShelfView : public ChromeViews::View,
+ public ChromeViews::BaseButton::ButtonListener,
+ public ChromeViews::LinkController,
+ public AnimationDelegate {
+ public:
+ DownloadShelfView(TabContents* tab_contents);
+
+ // A new download has started, so add it to our shelf.
+ void AddDownload(DownloadItem* download);
+
+ virtual void GetPreferredSize(CSize *out);
+
+ virtual void Layout();
+
+ // Invokes the following methods to do painting:
+ // PaintBackground, PaintBorder and PaintSeparators.
+ virtual void Paint(ChromeCanvas* canvas);
+
+ void DidChangeBounds(const CRect& previous, const CRect& current);
+
+ // AnimationDelegate implementations
+ virtual void AnimationProgressed(const Animation* animation);
+ virtual void AnimationEnded(const Animation* animation);
+
+ // Invoked when the user clicks the 'show all downloads' link button.
+ virtual void LinkActivated(ChromeViews::Link* source, int event_flags);
+
+ // Invoked when the user clicks the close button. Asks the browser to
+ // hide the download shelf.
+ virtual void ButtonPressed(ChromeViews::BaseButton* button);
+
+ // Removes a specified download view. The supplied view is deleted after
+ // it's removed.
+ void RemoveDownloadView(ChromeViews::View* view);
+
+ // Adds a View representing a download to this DownloadShelfView.
+ // DownloadShelfView takes ownership of the View, and will delete it as
+ // necessary.
+ void AddDownloadView(ChromeViews::View* view);
+
+ // Invoked when the download shelf is migrated from one tab contents to a new
+ // one.
+ void ChangeTabContents(TabContents* old_contents, TabContents* new_contents);
+
+ private:
+ void Init();
+
+ // Paints the border.
+ void PaintBorder(ChromeCanvas* canvas);
+
+ // Paints the separators. This invokes PaintSeparator to paint a particular
+ // separator.
+ void PaintSeparators(ChromeCanvas* canvas);
+
+ // Paints the separator between the two views.
+ void PaintSeparator(ChromeCanvas* canvas,
+ ChromeViews::View* v1,
+ ChromeViews::View* v2);
+
+ TabContents* tab_contents_;
+
+ // The animation for adding new items to the shelf.
+ scoped_ptr<SlideAnimation> new_item_animation_;
+
+ // The show/hide animation for the shelf itself.
+ scoped_ptr<SlideAnimation> shelf_animation_;
+
+ // The download views. These are also child Views, and deleted when
+ // the DownloadShelfView is deleted.
+ std::vector<View*> download_views_;
+
+ // An image displayed on the right of the "Show all downloads..." link.
+ ChromeViews::ImageView* arrow_image_;
+
+ // Link for showing all downloads. This is contained as a child, and deleted
+ // by View.
+ ChromeViews::Link* show_all_view_;
+
+ // Button for closing the downloads. This is contained as a child, and
+ // deleted by View.
+ ChromeViews::Button* close_button_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DownloadShelfView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_DOWNLOAD_SHELF_VIEW_H__
diff --git a/chrome/browser/views/download_started_animation.cc b/chrome/browser/views/download_started_animation.cc
new file mode 100644
index 0000000..3131a31
--- /dev/null
+++ b/chrome/browser/views/download_started_animation.cc
@@ -0,0 +1,131 @@
+// 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/views/download_started_animation.h"
+
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/hwnd_view_container.h"
+
+// How long to spend moving downwards and fading out after waiting.
+static const int kMoveTimeMs = 600;
+
+// The animation framerate.
+static const int kFrameRateHz = 60;
+
+// What fraction of the frame height to move downward from the frame center.
+// Note that setting this greater than 0.5 will mean moving past the bottom of
+// the frame.
+static const double kMoveFraction = 1.0 / 3.0;
+
+DownloadStartedAnimation::DownloadStartedAnimation(TabContents* tab_contents)
+ : Animation(kMoveTimeMs, kFrameRateHz, NULL),
+ popup_(NULL),
+ tab_contents_(tab_contents) {
+ static SkBitmap* kDownloadImage = NULL;
+ if (!kDownloadImage) {
+ kDownloadImage = ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_DOWNLOAD_ANIMATION_BEGIN);
+ }
+
+ // If we're too small to show the download image, then don't bother -
+ // the shelf will be enough.
+ tab_contents_->GetContainerBounds(&tab_contents_bounds_);
+ if (tab_contents_bounds_.height() < kDownloadImage->height())
+ return;
+
+ NotificationService::current()->AddObserver(this, NOTIFY_TAB_CONTENTS_HIDDEN,
+ Source<TabContents>(tab_contents_));
+ NotificationService::current()->AddObserver(this,
+ NOTIFY_TAB_CONTENTS_DESTROYED, Source<TabContents>(tab_contents_));
+
+ SetImage(kDownloadImage);
+
+ gfx::Rect rc(0, 0, 0, 0);
+ popup_ = new ChromeViews::HWNDViewContainer;
+ popup_->set_window_style(WS_POPUP);
+ popup_->set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW |
+ WS_EX_TRANSPARENT);
+ popup_->SetLayeredAlpha(0x00);
+ popup_->Init(tab_contents_->GetContainerHWND(), rc, this, false);
+ Reposition();
+ popup_->ShowWindow(SW_SHOWNOACTIVATE);
+
+ Start();
+}
+
+void DownloadStartedAnimation::Reposition() {
+ if (!tab_contents_)
+ return;
+
+ // Align the image with the bottom left of the web contents (so that it
+ // points to the newly created download).
+ CSize size;
+ GetPreferredSize(&size);
+ popup_->MoveWindow(tab_contents_bounds_.x(),
+ static_cast<int>(tab_contents_bounds_.bottom() - size.cy -
+ size.cy * (1 - GetCurrentValue())),
+ size.cx, size.cy);
+}
+
+void DownloadStartedAnimation::Close() {
+ if (!tab_contents_)
+ return;
+
+ NotificationService::current()->RemoveObserver(this,
+ NOTIFY_TAB_CONTENTS_HIDDEN, Source<TabContents>(tab_contents_));
+ NotificationService::current()->RemoveObserver(this,
+ NOTIFY_TAB_CONTENTS_DESTROYED, Source<TabContents>(tab_contents_));
+ tab_contents_ = NULL;
+ popup_->Close();
+}
+
+void DownloadStartedAnimation::AnimateToState(double state) {
+ if (state >= 1.0) {
+ Close();
+ } else {
+ Reposition();
+
+ // Start at zero, peak halfway and end at zero.
+ double opacity = std::min(1.0 - pow(GetCurrentValue() - 0.5, 2) * 4.0,
+ static_cast<double>(1.0));
+
+ popup_->SetLayeredAlpha(
+ static_cast<BYTE>(opacity * 255.0));
+ SchedulePaint(); // Reposition() calls MoveWindow() which never picks up
+ // alpha changes, so we need to force a paint.
+ }
+}
+
+void DownloadStartedAnimation::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ Close();
+}
diff --git a/chrome/browser/views/download_started_animation.h b/chrome/browser/views/download_started_animation.h
new file mode 100644
index 0000000..a0f7099
--- /dev/null
+++ b/chrome/browser/views/download_started_animation.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_DOWNLOAD_STARTED_ANIMATION_H__
+#define CHROME_BROWSER_VIEWS_DOWNLOAD_STARTED_ANIMATION_H__
+
+#include "base/gfx/rect.h"
+#include "chrome/common/animation.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/views/image_view.h"
+
+namespace ChromeViews {
+ class HWNDViewContainer;
+};
+class TabContents;
+
+// DownloadStartAnimation creates an animation (which begins running
+// immediately) that animates an image downward from the center of the frame
+// provided on the constructor, while simultaneously fading it out. To use,
+// simply call "new DownloadStartAnimation"; the class cleans itself up when it
+// finishes animating.
+class DownloadStartedAnimation : public Animation,
+ public NotificationObserver,
+ public ChromeViews::ImageView {
+ public:
+ DownloadStartedAnimation(TabContents* tab_contents);
+
+ private:
+ // Move the animation to wherever it should currently be.
+ void Reposition();
+
+ // Shut down the animation cleanly.
+ void Close();
+
+ // Animation
+ virtual void AnimateToState(double state);
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // We use a HWND for the popup so that it may float above any HWNDs in our UI.
+ ChromeViews::HWNDViewContainer* popup_;
+
+ // The content area holding us.
+ TabContents* tab_contents_;
+
+ // The content area at the start of the animation. We store this so that the
+ // download shelf's resizing of the content area doesn't cause the animation
+ // to move around. This means that once started, the animation won't move
+ // with the parent window, but it's so fast that this shouldn't cause too
+ // much heartbreak.
+ gfx::Rect tab_contents_bounds_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DownloadStartedAnimation);
+};
+
+#endif // CHROME_BROWSER_VIEWS_DOWNLOAD_STARTED_ANIMATION_H__
diff --git a/chrome/browser/views/edit_keyword_controller.cc b/chrome/browser/views/edit_keyword_controller.cc
new file mode 100644
index 0000000..413f91c
--- /dev/null
+++ b/chrome/browser/views/edit_keyword_controller.cc
@@ -0,0 +1,392 @@
+// 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/views/edit_keyword_controller.h"
+
+#include "base/string_util.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/browser/url_fixer_upper.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/browser/views/keyword_editor_view.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/label.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/window.h"
+#include "googleurl/src/gurl.h"
+
+#include "generated_resources.h"
+
+using ChromeViews::GridLayout;
+using ChromeViews::ImageView;
+using ChromeViews::TextField;
+
+
+namespace {
+// Converts a URL as understood by TemplateURL to one appropriate for display
+// to the user.
+std::wstring GetDisplayURL(const TemplateURL& turl) {
+ return turl.url() ? turl.url()->DisplayURL() : std::wstring();
+}
+} // namespace
+
+EditKeywordController::EditKeywordController(
+ HWND parent,
+ const TemplateURL* template_url,
+ KeywordEditorView* keyword_editor_view,
+ Profile* profile)
+ : parent_(parent),
+ template_url_(template_url),
+ keyword_editor_view_(keyword_editor_view),
+ profile_(profile) {
+ DCHECK(profile_);
+ Init();
+}
+
+void EditKeywordController::Show() {
+ // Window interprets an empty rectangle as needing to query the content for
+ // the size as well as centering relative to the parent.
+ window_ = ChromeViews::Window::CreateChromeWindow(
+ ::IsWindow(parent_) ? parent_ : NULL,
+ gfx::Rect(),
+ view_,
+ this);
+ window_->Show();
+ window_->UpdateDialogButtons();
+ title_tf_->SelectAll();
+ title_tf_->RequestFocus();
+}
+
+bool EditKeywordController::IsModal() const {
+ // If we were called without a KeywordEditorView, and our associated
+ // window happens to have gone away while the TemplateURLFetcher was
+ // loading, we might not have a valid parent anymore.
+ // ::IsWindow() returns a BOOL, which is a typedef for an int and causes a
+ // warning if we try to return it or cast it as a bool.
+ if (::IsWindow(parent_))
+ return true;
+ return false;
+}
+
+std::wstring EditKeywordController::GetWindowTitle() const {
+ return l10n_util::GetString(template_url_ ?
+ IDS_SEARCH_ENGINES_EDITOR_EDIT_WINDOW_TITLE :
+ IDS_SEARCH_ENGINES_EDITOR_NEW_WINDOW_TITLE);
+}
+
+int EditKeywordController::GetDialogButtons() const {
+ return DIALOGBUTTON_OK | DIALOGBUTTON_CANCEL;
+}
+
+bool EditKeywordController::IsDialogButtonEnabled(DialogButton button) const {
+ if (button == DIALOGBUTTON_OK) {
+ return (IsKeywordValid() && !title_tf_->GetText().empty() && IsURLValid());
+ }
+ return true;
+}
+
+void EditKeywordController::WindowClosing() {
+ // User canceled the save, delete us.
+ delete this;
+}
+
+bool EditKeywordController::Cancel() {
+ CleanUpCancelledAdd();
+ return true;
+}
+
+bool EditKeywordController::Accept() {
+ std::wstring url_string = GetURL();
+ DCHECK(!url_string.empty());
+ const std::wstring& keyword = keyword_tf_->GetText();
+
+ const TemplateURL* existing =
+ profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword);
+ if (existing &&
+ (!keyword_editor_view_ || existing != template_url_)) {
+ // An entry may have been added with the same keyword string while the
+ // user edited the dialog, either automatically or by the user (if we're
+ // confirming a JS addition, they could have the Options dialog open at the
+ // same time). If so, just ignore this add.
+ // TODO(pamg): Really, we should modify the entry so this later one
+ // overwrites it. But we don't expect this case to be common.
+ CleanUpCancelledAdd();
+ return true;
+ }
+
+ if (!keyword_editor_view_) {
+ // Confiming an entry we got from JS. We have a template_url_, but it
+ // hasn't yet been added to the model.
+ DCHECK(template_url_);
+ // const_cast is ugly, but this is the same thing the TemplateURLModel
+ // does in a similar situation (updating an existing TemplateURL with
+ // data from a new one).
+ TemplateURL* modifiable_url = const_cast<TemplateURL*>(template_url_);
+ modifiable_url->set_short_name(title_tf_->GetText());
+ modifiable_url->set_keyword(keyword);
+ modifiable_url->SetURL(url_string, 0, 0);
+ // TemplateURLModel takes ownership of template_url_.
+ profile_->GetTemplateURLModel()->Add(modifiable_url);
+ UserMetrics::RecordAction(L"KeywordEditor_AddKeywordJS", profile_);
+ } else if (!template_url_) {
+ // Adding a new entry via the KeywordEditorView.
+ DCHECK(keyword_editor_view_);
+ if (keyword_editor_view_)
+ keyword_editor_view_->AddTemplateURL(title_tf_->GetText(),
+ keyword_tf_->GetText(),
+ url_string);
+ } else {
+ // Modifying an entry via the KeywordEditorView.
+ DCHECK(keyword_editor_view_);
+ if (keyword_editor_view_) {
+ keyword_editor_view_->ModifyTemplateURL(template_url_,
+ title_tf_->GetText(),
+ keyword_tf_->GetText(),
+ url_string);
+ }
+ }
+ return true;
+}
+
+void EditKeywordController::ContentsChanged(TextField* sender,
+ const std::wstring& new_contents) {
+ window_->UpdateDialogButtons();
+ UpdateImageViews();
+}
+
+void EditKeywordController::HandleKeystroke(TextField* sender,
+ UINT message,
+ TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+}
+
+void EditKeywordController::Init() {
+ // Create the views we'll need.
+ view_ = new ChromeViews::View();
+ if (template_url_) {
+ title_tf_ = CreateTextField(template_url_->short_name());
+ keyword_tf_ = CreateTextField(template_url_->keyword());
+ url_tf_ = CreateTextField(GetDisplayURL(*template_url_));
+ // We don't allow users to edit prepopulate URLs. This is done as
+ // occasionally we need to update the URL of prepopulated TemplateURLs.
+ url_tf_->SetReadOnly(template_url_->prepopulate_id() != 0);
+ } else {
+ title_tf_ = CreateTextField(std::wstring());
+ keyword_tf_ = CreateTextField(std::wstring());
+ url_tf_ = CreateTextField(std::wstring());
+ }
+ title_iv_ = new ImageView();
+ keyword_iv_ = new ImageView();
+ url_iv_ = new ImageView();
+
+ UpdateImageViews();
+
+ const int related_x = kRelatedControlHorizontalSpacing;
+ const int related_y = kRelatedControlVerticalSpacing;
+ const int unrelated_y = kUnrelatedControlVerticalSpacing;
+
+ // View and GridLayout take care of deleting GridLayout for us.
+ GridLayout* layout = CreatePanelGridLayout(view_);
+ view_->SetLayoutManager(layout);
+
+ // Define the structure of the layout.
+
+ // For the buttons.
+ ChromeViews::ColumnSet* column_set = layout->AddColumnSet(0);
+ column_set->AddPaddingColumn(1, 0);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->LinkColumnSizes(1, 3, -1);
+
+ // For the textfields.
+ column_set = layout->AddColumnSet(1);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ // For the description.
+ column_set = layout->AddColumnSet(2);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ // Add the contents.
+ layout->StartRow(0, 1);
+ layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_LABEL));
+ layout->AddView(title_tf_);
+ layout->AddView(title_iv_);
+
+ layout->StartRowWithPadding(0, 1, 0, related_y);
+ layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_LABEL));
+ layout->AddView(keyword_tf_);
+ layout->AddView(keyword_iv_);
+
+ layout->StartRowWithPadding(0, 1, 0, related_y);
+ layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_URL_LABEL));
+ layout->AddView(url_tf_);
+ layout->AddView(url_iv_);
+
+ // On RTL UIs (such as Arabic and Hebrew) the description text is not
+ // displayed correctly since it contains the substring "%s". This substring
+ // is not interpreted by the Unicode BiDi algorithm as an LTR string and
+ // therefore the end result is that the following right to left text is
+ // displayed: ".three two s% one" (where 'one', 'two', etc. are words in
+ // Hebrew).
+ //
+ // In order to fix this problem we transform the substring "%s" so that it
+ // is displayed correctly when rendered in an RTL context.
+ layout->StartRowWithPadding(0, 2, 0, unrelated_y);
+ std::wstring description =
+ l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_URL_DESCRIPTION_LABEL);
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
+ const std::wstring reversed_percent(L"s%");
+ std::wstring::size_type percent_index =
+ description.find(L"%s", static_cast<std::wstring::size_type>(0));
+ if (percent_index != std::wstring::npos)
+ description.replace(percent_index,
+ reversed_percent.length(),
+ reversed_percent);
+ }
+
+ ChromeViews::Label* description_label = new ChromeViews::Label(description);
+ description_label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ layout->AddView(description_label);
+
+ layout->AddPaddingRow(0, related_y);
+}
+
+ChromeViews::Label* EditKeywordController::CreateLabel(int message_id) {
+ ChromeViews::Label* label = new ChromeViews::Label(
+ l10n_util::GetString(message_id));
+ label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ return label;
+}
+
+TextField* EditKeywordController::CreateTextField(const std::wstring& text) {
+ TextField* text_field = new TextField();
+ text_field->SetText(text);
+ text_field->SetController(this);
+ return text_field;
+}
+
+bool EditKeywordController::IsURLValid() const {
+ std::wstring url = GetURL();
+ if (url.empty())
+ return false;
+
+ // Use TemplateURLRef to extract the search placeholder.
+ TemplateURLRef template_ref(url, 0, 0);
+ if (!template_ref.IsValid())
+ return false;
+
+ if (template_ref.SupportsReplacement()) {
+ // If the url has a search term, replace it with a random string and make
+ // sure the resulting URL is valid. We don't check the validity of the url
+ // with the search term as that is not necessarily valid.
+ url = template_ref.ReplaceSearchTerms(TemplateURL(), L"a",
+ TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring());
+ }
+ return GURL(url).is_valid();
+}
+
+std::wstring EditKeywordController::GetURL() const {
+ std::wstring url;
+ TrimWhitespace(TemplateURLRef::DisplayURLToURLRef(url_tf_->GetText()),
+ TRIM_ALL, &url);
+ if (url.empty())
+ return url;
+
+ // Parse the string as a URL to determine the scheme. If a scheme hasn't been
+ // specified, we add it.
+ url_parse::Parsed parts;
+ std::wstring scheme(URLFixerUpper::SegmentURL(url, &parts));
+ if (!parts.scheme.is_valid()) {
+ std::wstring fixed_scheme(scheme);
+ fixed_scheme.append(L"://");
+ url.insert(0, fixed_scheme);
+ }
+
+ return url;
+}
+
+bool EditKeywordController::IsKeywordValid() const {
+ std::wstring keyword = keyword_tf_->GetText();
+ if (keyword.empty())
+ return true; // Always allow no keyword.
+ const TemplateURL* turl_with_keyword =
+ profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword);
+ return (turl_with_keyword == NULL || turl_with_keyword == template_url_);
+}
+
+void EditKeywordController::UpdateImageViews() {
+ UpdateImageView(keyword_iv_, IsKeywordValid(),
+ IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT);
+ UpdateImageView(url_iv_, IsURLValid(), IDS_SEARCH_ENGINES_INVALID_URL_TT);
+ UpdateImageView(title_iv_, !title_tf_->GetText().empty(),
+ IDS_SEARCH_ENGINES_INVALID_TITLE_TT);
+}
+
+void EditKeywordController::UpdateImageView(ImageView* image_view,
+ bool is_valid,
+ int invalid_message_id) {
+ if (is_valid) {
+ image_view->SetTooltipText(std::wstring());
+ image_view->SetImage(
+ ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_INPUT_GOOD));
+ } else {
+ image_view->SetTooltipText(l10n_util::GetString(invalid_message_id));
+ image_view->SetImage(
+ ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_INPUT_NONE));
+ }
+}
+
+void EditKeywordController::CleanUpCancelledAdd() {
+ if (!keyword_editor_view_ && template_url_) {
+ // When we have no KeywordEditorView, we know that the template_url_
+ // hasn't yet been added to the model, so we need to clean it up.
+ delete template_url_;
+ template_url_ = NULL;
+ }
+}
diff --git a/chrome/browser/views/edit_keyword_controller.h b/chrome/browser/views/edit_keyword_controller.h
new file mode 100644
index 0000000..c7e362e
--- /dev/null
+++ b/chrome/browser/views/edit_keyword_controller.h
@@ -0,0 +1,161 @@
+// 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.
+
+// EditKeywordController provides text fields for editing a keyword: the title,
+// url and actual keyword. It is used by the KeywordEditorView of the Options
+// dialog, and also on its own to confirm the addition of a keyword added by
+// the ExternalJSObject via the RenderView.
+
+#ifndef CHROME_BROWSER_VIEWS_EDIT_KEYWORD_CONTROLLER_H__
+#define CHROME_BROWSER_VIEWS_EDIT_KEYWORD_CONTROLLER_H__
+
+#include <Windows.h>
+
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/text_field.h"
+
+namespace ChromeViews {
+ class Label;
+ class ImageView;
+ class Window;
+}
+
+class KeywordEditorView;
+class Profile;
+class TemplateURL;
+class TemplateURLModel;
+
+class EditKeywordController : public ChromeViews::TextField::Controller,
+ public ChromeViews::DialogDelegate {
+ public:
+ // The template_url and/or keyword_editor_view may be NULL.
+ EditKeywordController(HWND parent,
+ const TemplateURL* template_url,
+ KeywordEditorView* keyword_editor_view,
+ Profile* profile);
+
+ virtual ~EditKeywordController() {}
+
+ // Shows the dialog to the user. EditKeywordController takes care of
+ // deleting itself after show has been invoked.
+ void Show();
+
+ // DialogDelegate overrides.
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual int GetDialogButtons() const;
+ virtual bool IsDialogButtonEnabled(DialogButton button) const;
+ virtual void WindowClosing();
+ virtual bool Cancel();
+ virtual bool Accept();
+
+ // ChromeViews::TextField::Controller overrides. Updates whether the user can
+ // accept the dialog as well as updating image views showing whether value is
+ // valid.
+ virtual void ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message,
+ TCHAR key,
+ UINT repeat_count,
+ UINT flags);
+
+ private:
+ void Init();
+
+ // Create a Label containing the text with the specified message id.
+ ChromeViews::Label* CreateLabel(int message_id);
+
+ // Creates a text field with the specified text.
+ ChromeViews::TextField* CreateTextField(const std::wstring& text);
+
+ // Returns true if the currently input URL is valid. The URL is valid if it
+ // contains no search terms and is a valid url, or if it contains a search
+ // term and replacing that search term with a character results in a valid
+ // url.
+ bool IsURLValid() const;
+
+ // Returns the URL the user has input. The returned URL is suitable for use
+ // by TemplateURL.
+ std::wstring GetURL() const;
+
+ // Returns whether the currently entered keyword is valid. The keyword is
+ // valid if it is non-empty and does not conflict with an existing entry.
+ // NOTE: this is just the keyword, not the title and url.
+ bool IsKeywordValid() const;
+
+ // Invokes UpdateImageView for each of the images views.
+ void UpdateImageViews();
+
+ // Updates the tooltip and image of the image view based on is_valid. If
+ // is_valid is false the tooltip of the image view is set to the message with
+ // id invalid_message_id, otherwise the tooltip is set to the empty text.
+ void UpdateImageView(ChromeViews::ImageView* image_view,
+ bool is_valid,
+ int invalid_message_id);
+
+ // Deletes an unused TemplateURL, if its add was cancelled and it's not
+ // already owned by the TemplateURLModel.
+ void CleanUpCancelledAdd();
+
+ // The TemplateURL we're displaying information for. It may be NULL. If we
+ // have a keyword_editor_view, we assume that this TemplateURL is already in
+ // the TemplateURLModel; if not, we assume it isn't.
+ const TemplateURL* template_url_;
+
+ // Used to parent window to. May be NULL or an invalid window.
+ HWND parent_;
+
+ // View containing the buttons, text fields ...
+ ChromeViews::View* view_;
+
+ // We may have been created by this, in which case we will call back to it on
+ // success to add/modify the entry. May be NULL.
+ KeywordEditorView* keyword_editor_view_;
+
+ // Profile whose TemplateURLModel we're modifying.
+ Profile* profile_;
+
+ // Container for displaying the contents.
+ ChromeViews::Window* window_;
+
+ // Text fields.
+ ChromeViews::TextField* title_tf_;
+ ChromeViews::TextField* keyword_tf_;
+ ChromeViews::TextField* url_tf_;
+
+ // Shows error images.
+ ChromeViews::ImageView* title_iv_;
+ ChromeViews::ImageView* keyword_iv_;
+ ChromeViews::ImageView* url_iv_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(EditKeywordController);
+};
+
+#endif // CHROME_BROWSER_VIEWS_EDIT_KEYWORD_CONTROLLER_H__ \ No newline at end of file
diff --git a/chrome/browser/views/event_utils.cc b/chrome/browser/views/event_utils.cc
new file mode 100644
index 0000000..75f65c8
--- /dev/null
+++ b/chrome/browser/views/event_utils.cc
@@ -0,0 +1,56 @@
+// 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/views/event_utils.h"
+
+#include "chrome/views/event.h"
+
+using ChromeViews::Event;
+
+namespace event_utils {
+
+WindowOpenDisposition DispositionFromEventFlags(int event_flags) {
+ if (((event_flags & Event::EF_MIDDLE_BUTTON_DOWN) ==
+ Event::EF_MIDDLE_BUTTON_DOWN) ||
+ ((event_flags & Event::EF_CONTROL_DOWN) ==
+ Event::EF_CONTROL_DOWN)) {
+ return ((event_flags & Event::EF_SHIFT_DOWN) == Event::EF_SHIFT_DOWN) ?
+ NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
+ }
+
+ if ((event_flags & Event::EF_SHIFT_DOWN) == Event::EF_SHIFT_DOWN)
+ return NEW_WINDOW;
+ return false /*event.IsAltDown()*/ ? SAVE_TO_DISK : CURRENT_TAB;
+}
+
+bool IsPossibleDispositionEvent(const ChromeViews::MouseEvent& event) {
+ return event.IsLeftMouseButton() || event.IsMiddleMouseButton();
+}
+
+}
diff --git a/chrome/browser/views/event_utils.h b/chrome/browser/views/event_utils.h
new file mode 100644
index 0000000..710d124
--- /dev/null
+++ b/chrome/browser/views/event_utils.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_EVENT_UTILS_H__
+#define CHROME_BROWSER_VIEWS_EVENT_UTILS_H__
+
+#include "webkit/glue/window_open_disposition.h"
+
+namespace ChromeViews {
+class MouseEvent;
+}
+
+namespace event_utils {
+
+// Translates event flags into what kind of disposition they represents.
+// For example, a middle click would mean to open a background tab.
+// event_flags are the flags as understood by ChromeViews::MouseEvent.
+WindowOpenDisposition DispositionFromEventFlags(int event_flags);
+
+// Returns true if the specified mouse event may have a
+// WindowOptionDisposition.
+bool IsPossibleDispositionEvent(const ChromeViews::MouseEvent& event);
+
+}
+
+#endif // CHROME_BROWSER_VIEWS_BOOKMARK_BAR_VIEW_H__
diff --git a/chrome/browser/views/first_run_bubble.cc b/chrome/browser/views/first_run_bubble.cc
new file mode 100644
index 0000000..86f9fb1
--- /dev/null
+++ b/chrome/browser/views/first_run_bubble.cc
@@ -0,0 +1,241 @@
+// 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/views/first_run_bubble.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/chrome_frame.h"
+#include "chrome/browser/options_window.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/event.h"
+#include "chrome/views/label.h"
+#include "chrome/views/native_button.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// How much extra padding to put around our content over what
+// infobubble provides.
+static const int kBubblePadding = 4;
+
+// TODO(cpu): bug 1187517. It is possible that there is no default provider.
+// we should make sure there is none at first run.
+std::wstring GetDefaultSearchEngineName() {
+ Browser* browser = BrowserList::GetLastActive();
+ DCHECK(browser);
+ const TemplateURL* const default_provider =
+ browser->profile()->GetTemplateURLModel()->GetDefaultSearchProvider();
+ DCHECK(default_provider);
+ return default_provider->short_name();
+}
+
+} // namespace
+
+// Implements the client view inside the first run bubble. It is kind of a
+// dialog-ish view, but is not a true dialog.
+class FirstRunBubbleView : public ChromeViews::View,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ explicit FirstRunBubbleView(FirstRunBubble* bubble_window)
+ : bubble_window_(bubble_window),
+ label1_(NULL),
+ label2_(NULL),
+ label3_(NULL),
+ keep_button_(NULL),
+ change_button_(NULL) {
+ ChromeFont& font =
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont);
+
+ label1_ = new ChromeViews::Label(l10n_util::GetString(IDS_FR_BUBBLE_TITLE));
+ label1_->SetFont(font.DeriveFont(3, ChromeFont::BOLD));
+ label1_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ AddChildView(label1_);
+
+ CSize ps;
+ GetPreferredSize(&ps);
+
+ label2_ =
+ new ChromeViews::Label(l10n_util::GetString(IDS_FR_BUBBLE_SUBTEXT));
+ label2_->SetMultiLine(true);
+ label2_->SetFont(font);
+ label2_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label2_->SizeToFit(ps.cx - kBubblePadding * 2);
+ AddChildView(label2_);
+
+ std::wstring question_str
+ = l10n_util::GetStringF(IDS_FR_BUBBLE_QUESTION,
+ GetDefaultSearchEngineName());
+ label3_ = new ChromeViews::Label(question_str);
+ label3_->SetMultiLine(true);
+ label3_->SetFont(font);
+ label3_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label3_->SizeToFit(ps.cx - kBubblePadding * 2);
+ AddChildView(label3_);
+
+ std::wstring keep_str = l10n_util::GetStringF(IDS_FR_BUBBLE_OK,
+ GetDefaultSearchEngineName());
+ keep_button_ = new ChromeViews::NativeButton(keep_str, true);
+ keep_button_->SetListener(this);
+ AddChildView(keep_button_);
+
+ std::wstring change_str = l10n_util::GetString(IDS_FR_BUBBLE_CHANGE);
+ change_button_ = new ChromeViews::NativeButton(change_str);
+ change_button_->SetListener(this);
+ AddChildView(change_button_);
+ }
+
+ // Overridden from ChromeViews::View.
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current) {
+ Layout();
+ }
+
+ // Overridden from NativeButton::Listener.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender) {
+ bubble_window_->Close();
+ if (change_button_ == sender) {
+ Browser* browser = BrowserList::GetLastActive();
+ if (browser) {
+ ShowOptionsWindow(OPTIONS_PAGE_GENERAL, OPTIONS_GROUP_DEFAULT_SEARCH,
+ browser->profile());
+ }
+ }
+ }
+
+ // Overridden from ChromeViews::View.
+ virtual void Layout() {
+ CSize canvas;
+ GetPreferredSize(&canvas);
+
+ CSize pref_size;
+ label1_->GetPreferredSize(&pref_size);
+ label1_->SetBounds(kBubblePadding, kBubblePadding, pref_size.cx,
+ pref_size.cy);
+
+ int next_v_space = label1_->GetY() + label1_->GetHeight() +
+ kRelatedControlSmallVerticalSpacing;
+
+ label2_->GetPreferredSize(&pref_size);
+ label2_->SetBounds(kBubblePadding, next_v_space,
+ canvas.cx - kBubblePadding * 2,
+ pref_size.cy);
+
+ next_v_space = label2_->GetY() + label2_->GetHeight() +
+ kPanelSubVerticalSpacing;
+
+ label3_->GetPreferredSize(&pref_size);
+ label3_->SetBounds(kBubblePadding, next_v_space,
+ canvas.cx - kBubblePadding * 2,
+ pref_size.cy);
+
+ change_button_->GetPreferredSize(&pref_size);
+ change_button_->SetBounds(canvas.cx - pref_size.cx - kBubblePadding,
+ canvas.cy - pref_size.cy - kButtonVEdgeMargin,
+ pref_size.cx, pref_size.cy);
+
+ keep_button_->GetPreferredSize(&pref_size);
+ keep_button_->SetBounds(change_button_->GetX() - pref_size.cx -
+ kRelatedButtonHSpacing, change_button_->GetY(),
+ pref_size.cx, pref_size.cy);
+ }
+
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+ if (keep_button_)
+ keep_button_->RequestFocus();
+ }
+
+ // Overridden from ChromeViews::View.
+ virtual void GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_FIRSTRUNBUBBLE_DIALOG_WIDTH_CHARS,
+ IDS_FIRSTRUNBUBBLE_DIALOG_HEIGHT_LINES).ToSIZE();
+ }
+
+ private:
+ FirstRunBubble* bubble_window_;
+ ChromeViews::Label* label1_;
+ ChromeViews::Label* label2_;
+ ChromeViews::Label* label3_;
+ ChromeViews::NativeButton* change_button_;
+ ChromeViews::NativeButton* keep_button_;
+ DISALLOW_EVIL_CONSTRUCTORS(FirstRunBubbleView);
+};
+
+// Keep the bubble around for kLingerTime milliseconds, to prevent accidental
+// closure.
+static const int kLingerTime = 1000;
+
+void FirstRunBubble::OnActivate(UINT action, BOOL minimized, HWND window) {
+ // We might get re-enabled right before we are closed (sequence is: we get
+ // deactivated, we call close, before we are actually closed we get
+ // reactivated). Don't do the disabling of the parent in such cases.
+ if (action == WA_ACTIVE && !has_been_activated_) {
+ has_been_activated_ = true;
+
+ // Disable the browser to prevent accidental rapid clicks from closing the
+ // bubble.
+ ::EnableWindow(GetParent(), false);
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ enable_window_method_factory_.NewRunnableMethod(
+ &FirstRunBubble::EnableParent),
+ kLingerTime);
+ }
+ InfoBubble::OnActivate(action, minimized, window);
+}
+
+void FirstRunBubble::InfoBubbleClosing(InfoBubble* info_bubble) {
+ // Make sure our parent window is re-enabled.
+ if (!IsWindowEnabled(GetParent()))
+ ::EnableWindow(GetParent(), true);
+ enable_window_method_factory_.RevokeAll();
+}
+
+FirstRunBubble* FirstRunBubble::Show(HWND parent_hwnd,
+ const gfx::Rect& position_relative_to) {
+ FirstRunBubble* window = new FirstRunBubble();
+ ChromeViews::View* view = new FirstRunBubbleView(window);
+ window->SetDelegate(window);
+ window->Init(parent_hwnd, position_relative_to, view);
+ ChromeFrame* frame = window->GetHostingFrame();
+ DCHECK(frame);
+ frame->InfoBubbleShowing();
+ window->ShowWindow(SW_SHOW);
+ return window;
+}
+
+void FirstRunBubble::EnableParent() {
+ ::EnableWindow(GetParent(), true);
+}
diff --git a/chrome/browser/views/first_run_bubble.h b/chrome/browser/views/first_run_bubble.h
new file mode 100644
index 0000000..b904147
--- /dev/null
+++ b/chrome/browser/views/first_run_bubble.h
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_FIRST_RUN_BUBBLE_H__
+#define CHROME_BROWSER_VIEWS_FIRST_RUN_BUBBLE_H__
+
+#include "base/task.h"
+#include "chrome/browser/views/info_bubble.h"
+
+class FirstRunBubble : public InfoBubble,
+ public InfoBubbleDelegate {
+ public:
+ static FirstRunBubble* Show(HWND parent_hwnd,
+ const gfx::Rect& position_relative_to);
+
+ FirstRunBubble()
+ : enable_window_method_factory_(this),
+ has_been_activated_(false) {
+ }
+
+ virtual ~FirstRunBubble() {
+ // We should have called RevokeAll on the method factory already.
+ DCHECK(enable_window_method_factory_.empty());
+ enable_window_method_factory_.RevokeAll();
+ }
+
+ // Overridden from InfoBubble:
+ virtual void OnActivate(UINT action, BOOL minimized, HWND window);
+
+ // InfoBubbleDelegate.
+ virtual void InfoBubbleClosing(InfoBubble* info_bubble);
+ virtual bool CloseOnEscape() { return true; }
+
+ private:
+ // Re-enable the parent window.
+ void EnableParent();
+
+ // Whether we have already been activated.
+ bool has_been_activated_;
+
+ ScopedRunnableMethodFactory<FirstRunBubble> enable_window_method_factory_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FirstRunBubble);
+};
+
+#endif // CHROME_BROWSER_VIEWS_FIRST_RUN_BUBBLE_H__
diff --git a/chrome/browser/views/first_run_customize_view.cc b/chrome/browser/views/first_run_customize_view.cc
new file mode 100644
index 0000000..6b8263a
--- /dev/null
+++ b/chrome/browser/views/first_run_customize_view.cc
@@ -0,0 +1,253 @@
+// 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/views/first_run_customize_view.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/importer.h"
+#include "chrome/browser/first_run.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/label.h"
+#include "chrome/views/throbber.h"
+#include "chrome/views/window.h"
+
+#include "generated_resources.h"
+
+FirstRunCustomizeView::FirstRunCustomizeView(Profile* profile,
+ ImporterHost* importer_host)
+ : FirstRunViewBase(profile),
+ main_label_(NULL),
+ import_cbox_(NULL),
+ default_browser_cbox_(NULL),
+ import_from_combo_(NULL),
+ shortcuts_label_(NULL),
+ desktop_shortcut_cbox_(NULL),
+ quick_shortcut_cbox_(NULL),
+ customize_observer_(NULL) {
+ importer_host_ = importer_host;
+ DCHECK(importer_host_);
+ SetupControls();
+}
+
+FirstRunCustomizeView::~FirstRunCustomizeView() {
+}
+
+ChromeViews::CheckBox* FirstRunCustomizeView::MakeCheckBox(int label_id) {
+ ChromeViews::CheckBox* cbox =
+ new ChromeViews::CheckBox(l10n_util::GetString(label_id));
+ cbox->SetListener(this);
+ AddChildView(cbox);
+ return cbox;
+}
+
+void FirstRunCustomizeView::SetupControls() {
+ using ChromeViews::Label;
+ using ChromeViews::CheckBox;
+
+ main_label_ = new Label(l10n_util::GetString(IDS_FR_CUSTOMIZE_DLG_TEXT));
+ main_label_->SetMultiLine(true);
+ main_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ AddChildView(main_label_);
+
+ import_cbox_ = MakeCheckBox(IDS_FR_CUSTOMIZE_IMPORT);
+
+ import_from_combo_ = new ChromeViews::ComboBox(this);
+ AddChildView(import_from_combo_);
+
+ default_browser_cbox_ = MakeCheckBox(IDS_FR_CUSTOMIZE_DEFAULT_BROWSER);
+
+ shortcuts_label_ =
+ new Label(l10n_util::GetString(IDS_FR_CUSTOMIZE_SHORTCUTS));
+ shortcuts_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ AddChildView(shortcuts_label_);
+
+ // The two check boxes for the different shortcut creation.
+ desktop_shortcut_cbox_ = MakeCheckBox(IDS_FR_CUSTOM_SHORTCUT_DESKTOP);
+ desktop_shortcut_cbox_->SetIsSelected(true);
+
+ quick_shortcut_cbox_ = MakeCheckBox(IDS_FR_CUSTOM_SHORTCUT_QUICKL);
+ quick_shortcut_cbox_->SetIsSelected(true);
+}
+
+void FirstRunCustomizeView::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_FIRSTRUNCUSTOMIZE_DIALOG_WIDTH_CHARS,
+ IDS_FIRSTRUNCUSTOMIZE_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void FirstRunCustomizeView::Layout() {
+ FirstRunViewBase::Layout();
+
+ const int kVertSpacing = 8;
+ const int kComboExtraPad = 8;
+
+ CSize canvas;
+ GetPreferredSize(&canvas);
+
+ // Welcome label goes in to to the left. It does not go across the
+ // entire window because the background gets busy on the right.
+ CSize pref_size;
+ main_label_->GetPreferredSize(&pref_size);
+ main_label_->SetBounds(kPanelHorizMargin, kPanelVertMargin,
+ canvas.cx - pref_size.cx, pref_size.cy);
+ AdjustDialogWidth(main_label_);
+
+ int next_v_space = background_image()->GetY() +
+ background_image()->GetHeight() + kPanelVertMargin;
+
+ import_cbox_->GetPreferredSize(&pref_size);
+ import_cbox_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+
+ import_cbox_->SetIsSelected(true);
+
+ int x_offset = import_cbox_->GetX() +
+ import_cbox_->GetWidth();
+
+ import_from_combo_->GetPreferredSize(&pref_size);
+ import_from_combo_->SetBounds(x_offset, next_v_space,
+ pref_size.cx + kComboExtraPad, pref_size.cy);
+
+ AdjustDialogWidth(import_from_combo_);
+
+ next_v_space = import_cbox_->GetY() + import_cbox_->GetHeight() +
+ kUnrelatedControlVerticalSpacing;
+
+ default_browser_cbox_->GetPreferredSize(&pref_size);
+ default_browser_cbox_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+
+ AdjustDialogWidth(default_browser_cbox_);
+
+ next_v_space += default_browser_cbox_->GetHeight() +
+ kUnrelatedControlVerticalSpacing;
+
+ shortcuts_label_->GetPreferredSize(&pref_size);
+ shortcuts_label_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+
+ AdjustDialogWidth(shortcuts_label_);
+
+ next_v_space += shortcuts_label_->GetHeight() +
+ kRelatedControlVerticalSpacing;
+
+ desktop_shortcut_cbox_->GetPreferredSize(&pref_size);
+ desktop_shortcut_cbox_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+
+ AdjustDialogWidth(desktop_shortcut_cbox_);
+
+ next_v_space += desktop_shortcut_cbox_->GetHeight() +
+ kRelatedControlVerticalSpacing;
+
+ quick_shortcut_cbox_->GetPreferredSize(&pref_size);
+ quick_shortcut_cbox_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+
+ AdjustDialogWidth(quick_shortcut_cbox_);
+}
+
+void FirstRunCustomizeView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (import_cbox_ == sender) {
+ // Disable the import combobox if the user unchecks the checkbox.
+ import_from_combo_->SetEnabled(import_cbox_->IsSelected());
+ }
+}
+
+int FirstRunCustomizeView::GetItemCount(ChromeViews::ComboBox* source) {
+ return importer_host_->GetAvailableProfileCount();
+}
+
+std::wstring FirstRunCustomizeView::GetItemAt(ChromeViews::ComboBox* source,
+ int index) {
+ return importer_host_->GetSourceProfileNameAt(index);
+}
+
+std::wstring FirstRunCustomizeView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_FR_CUSTOMIZE_DLG_TITLE);
+}
+
+bool FirstRunCustomizeView::Accept() {
+ if (!IsDialogButtonEnabled(DIALOGBUTTON_OK))
+ return false;
+
+ DisableButtons();
+ import_cbox_->SetEnabled(false);
+ import_from_combo_->SetEnabled(false);
+ default_browser_cbox_->SetEnabled(false);
+ desktop_shortcut_cbox_->SetEnabled(false);
+ quick_shortcut_cbox_->SetEnabled(false);
+
+ if (desktop_shortcut_cbox_->IsSelected()) {
+ UserMetrics::RecordAction(L"FirstRunCustom_Do_DesktopShortcut", profile_);
+ CreateDesktopShortcut();
+ }
+ if (quick_shortcut_cbox_->IsSelected()) {
+ UserMetrics::RecordAction(L"FirstRunCustom_Do_QuickLShortcut", profile_);
+ CreateQuickLaunchShortcut();
+ }
+ if (!import_cbox_->IsSelected()) {
+ UserMetrics::RecordAction(L"FirstRunCustom_No_Import", profile_);
+ } else {
+ int browser_selected = import_from_combo_->GetSelectedItem();
+ FirstRun::ImportSettings(profile_, browser_selected,
+ GetDefaultImportItems(), dialog_->GetHWND());
+ }
+ if (default_browser_cbox_->IsSelected()) {
+ UserMetrics::RecordAction(L"FirstRunCustom_Do_DefBrowser", profile_);
+ ShellIntegration::SetAsDefaultBrowser();
+ }
+
+ if (customize_observer_)
+ customize_observer_->CustomizeAccepted();
+
+ // Exit the message loop we were started with so that startup can continue.
+ MessageLoop::current()->Quit();
+
+ return true;
+}
+
+bool FirstRunCustomizeView::Cancel() {
+ if (customize_observer_)
+ customize_observer_->CustomizeCanceled();
+
+ // Don't quit the message loop in this case - we're still showing the main
+ // First run dialog box underneath ourselves.
+
+ return true;
+}
diff --git a/chrome/browser/views/first_run_customize_view.h b/chrome/browser/views/first_run_customize_view.h
new file mode 100644
index 0000000..4a22cb6
--- /dev/null
+++ b/chrome/browser/views/first_run_customize_view.h
@@ -0,0 +1,120 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_FIRST_RUN_CUSTOMIZE_VIEW_H_
+#define CHROME_BROWSER_VIEWS_FIRST_RUN_CUSTOMIZE_VIEW_H_
+
+#include "chrome/browser/views/first_run_view_base.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+
+class Label;
+class Window;
+class ImageView;
+class Separator;
+class CheckBox;
+class ComboBox;
+
+}
+
+class Profile;
+
+// FirstRunCustomizeView implements the dialog that allows the user to do
+// some simple customizations during the first run.
+class FirstRunCustomizeView : public FirstRunViewBase,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::ComboBox::Model {
+ public:
+ class CustomizeViewObserver {
+ public:
+ // Called when the user has accepted the dialog.
+ virtual void CustomizeAccepted() = 0;
+ // Called when the user has canceled the dialog.
+ virtual void CustomizeCanceled() = 0;
+ };
+
+ FirstRunCustomizeView(Profile* profile, ImporterHost* importer_host);
+ virtual ~FirstRunCustomizeView();
+
+ void set_dialog(ChromeViews::Window* dialog) { dialog_ = dialog; }
+
+ // Sets the object that receives UserAccept and UserCancel notifications.
+ void set_observer(CustomizeViewObserver* observer) {
+ customize_observer_ = observer;
+ };
+
+ // Overridden from ChromeViews::View.
+ virtual void GetPreferredSize(CSize *out);
+ virtual void Layout();
+
+ // Overridden from ChromeViews::DialogDelegate.
+ virtual bool Accept();
+ virtual bool Cancel();
+
+ // Overridden form ChromeViews::NativeButton::Listener.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // Overridden form ChromeViews::ComboBox::Model.
+ virtual int GetItemCount(ChromeViews::ComboBox* source);
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index);
+
+ // Overridden from ChromeViews::WindowDelegate.
+ virtual std::wstring GetWindowTitle() const;
+
+ // Yes, we're modal.
+ // NOTE: if you change this you'll need to make sure it isn't possible to
+ // close the window while importing.
+ virtual bool IsModal() const {
+ return true;
+ }
+
+ private:
+ // Initializes the controls on the dialog.
+ void SetupControls();
+
+ ChromeViews::CheckBox* MakeCheckBox(int resource_id);
+
+ ChromeViews::Label* main_label_;
+ ChromeViews::CheckBox* import_cbox_;
+ ChromeViews::CheckBox* default_browser_cbox_;
+ ChromeViews::ComboBox* import_from_combo_;
+ ChromeViews::Label* shortcuts_label_;
+ ChromeViews::CheckBox* desktop_shortcut_cbox_;
+ ChromeViews::CheckBox* quick_shortcut_cbox_;
+
+ CustomizeViewObserver* customize_observer_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FirstRunCustomizeView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_FIRST_RUN_CUSTOMIZE_VIEW_H_
diff --git a/chrome/browser/views/first_run_view.cc b/chrome/browser/views/first_run_view.cc
new file mode 100644
index 0000000..1beed44
--- /dev/null
+++ b/chrome/browser/views/first_run_view.cc
@@ -0,0 +1,233 @@
+// 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/views/first_run_view.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/importer.h"
+#include "chrome/browser/first_run.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/first_run_customize_view.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/label.h"
+#include "chrome/views/throbber.h"
+#include "chrome/views/separator.h"
+#include "chrome/views/window.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// Adds a bullet glyph to a string.
+std::wstring AddBullet(const std::wstring& text) {
+ std::wstring btext(L" " + text);
+ return btext.insert(0, 1, L'\u2022');
+}
+
+} // namespace
+
+FirstRunView::FirstRunView(Profile* profile)
+ : FirstRunViewBase(profile),
+ welcome_label_(NULL),
+ actions_label_(NULL),
+ actions_import_(NULL),
+ actions_shorcuts_(NULL),
+ customize_link_(NULL),
+ customize_selected_(false) {
+ importer_host_ = new ImporterHost();
+ SetupControls();
+}
+
+FirstRunView::~FirstRunView() {
+ FirstRunComplete();
+
+ // Exit the message loop we were started with so that startup can continue.
+ MessageLoop::current()->Quit();
+}
+
+void FirstRunView::SetupControls() {
+ using ChromeViews::Label;
+ using ChromeViews::Link;
+
+ welcome_label_ = new Label(l10n_util::GetString(IDS_FIRSTRUN_DLG_TEXT));
+ welcome_label_->SetMultiLine(true);
+ welcome_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ welcome_label_->SizeToFit(0);
+ AddChildView(welcome_label_);
+
+ actions_label_ = new Label(l10n_util::GetString(IDS_FIRSTRUN_DLG_DETAIL));
+ actions_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ AddChildView(actions_label_);
+
+ // The first action label will tell what we are going to import from which
+ // browser, which we obtain from the ImporterHost. We need that the first
+ // browser profile be the default browser.
+ std::wstring label1;
+ if (importer_host_->GetAvailableProfileCount() > 0) {
+ label1 = l10n_util::GetStringF(IDS_FIRSTRUN_DLG_ACTION1,
+ importer_host_->GetSourceProfileNameAt(0));
+ } else {
+ NOTREACHED();
+ }
+
+ actions_import_ = new Label(AddBullet(label1));
+ actions_import_->SetMultiLine(true);
+ actions_import_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ AddChildView(actions_import_);
+ std::wstring label2 = l10n_util::GetString(IDS_FIRSTRUN_DLG_ACTION2);
+ actions_shorcuts_ = new Label(AddBullet(label2));
+ actions_shorcuts_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ actions_shorcuts_->SetMultiLine(true);
+ AddChildView(actions_shorcuts_);
+
+ customize_link_ = new Link(l10n_util::GetString(IDS_FIRSTRUN_DLG_OVERRIDE));
+ customize_link_->SetController(this);
+ AddChildView(customize_link_);
+}
+
+void FirstRunView::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_FIRSTRUN_DIALOG_WIDTH_CHARS,
+ IDS_FIRSTRUN_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void FirstRunView::Layout() {
+ FirstRunViewBase::Layout();
+
+ const int kVertSpacing = 8;
+
+ CSize pref_size;
+ welcome_label_->GetPreferredSize(&pref_size);
+ welcome_label_->SetBounds(kPanelHorizMargin, kPanelVertMargin,
+ pref_size.cx, pref_size.cy);
+ AdjustDialogWidth(welcome_label_);
+
+ int next_v_space = background_image()->GetY() +
+ background_image()->GetHeight() + kPanelVertMargin;
+
+ actions_label_->GetPreferredSize(&pref_size);
+ actions_label_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+ AdjustDialogWidth(actions_label_);
+
+ next_v_space = actions_label_->GetY() +
+ actions_label_->GetHeight() + kVertSpacing;
+
+
+ // First give the label some width, so that GetPreferredSize can return us a
+ // reasonable height...
+ actions_import_->SetBounds(0, 0, GetWidth() - kPanelHorizMargin, 0);
+ actions_import_->GetPreferredSize(&pref_size);
+ actions_import_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx + 100, pref_size.cy);
+
+ next_v_space = actions_import_->GetY() +
+ actions_import_->GetHeight() + kVertSpacing;
+ AdjustDialogWidth(actions_import_);
+
+ actions_shorcuts_->SetBounds(0, 0, GetWidth() - kPanelHorizMargin, 0);
+ actions_shorcuts_->GetPreferredSize(&pref_size);
+ actions_shorcuts_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+ AdjustDialogWidth(actions_shorcuts_);
+
+ next_v_space = actions_shorcuts_->GetY() +
+ actions_shorcuts_->GetHeight() +
+ kUnrelatedControlVerticalSpacing;
+
+ customize_link_->GetPreferredSize(&pref_size);
+ customize_link_->SetBounds(kPanelHorizMargin, next_v_space,
+ pref_size.cx, pref_size.cy);
+}
+
+std::wstring FirstRunView::GetDialogButtonLabel(DialogButton button) const {
+ if (DIALOGBUTTON_OK == button)
+ return l10n_util::GetString(IDS_FIRSTRUN_DLG_OK);
+ // The other buttons get the default text.
+ return std::wstring();
+}
+
+void FirstRunView::OpenCustomizeDialog() {
+ // The customize dialog now owns the importer host object.
+ FirstRunCustomizeView* customize_view =
+ new FirstRunCustomizeView(profile_, importer_host_);
+
+ ChromeViews::Window* customize_dialog =
+ ChromeViews::Window::CreateChromeWindow(dialog_->GetHWND(),
+ gfx::Rect(), customize_view,
+ customize_view);
+ customize_dialog->Show();
+ customize_view->set_dialog(customize_dialog);
+ customize_view->set_observer(this);
+}
+
+void FirstRunView::LinkActivated(ChromeViews::Link* source, int event_flags) {
+ OpenCustomizeDialog();
+}
+
+std::wstring FirstRunView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_FIRSTRUN_DLG_TITLE);
+}
+
+bool FirstRunView::Accept() {
+ if (!IsDialogButtonEnabled(DIALOGBUTTON_OK))
+ return false;
+
+ DisableButtons();
+ customize_link_->SetEnabled(false);
+ CreateDesktopShortcut();
+ CreateQuickLaunchShortcut();
+ // Index 0 is the default browser.
+ FirstRun::ImportSettings(profile_, 0, GetDefaultImportItems(),
+ dialog_->GetHWND());
+ UserMetrics::RecordAction(L"FirstRunDef_Accept", profile_);
+
+ return true;
+}
+
+bool FirstRunView::Cancel() {
+ UserMetrics::RecordAction(L"FirstRunDef_Cancel", profile_);
+ return true;
+}
+
+// Notification from the customize dialog that the user accepted. Since all
+// the work is done there we got nothing else to do.
+void FirstRunView::CustomizeAccepted() {
+ dialog_->Close();
+}
+
+// Notification from the customize dialog that the user cancelled.
+void FirstRunView::CustomizeCanceled() {
+ UserMetrics::RecordAction(L"FirstRunCustom_Cancel", profile_);
+}
diff --git a/chrome/browser/views/first_run_view.h b/chrome/browser/views/first_run_view.h
new file mode 100644
index 0000000..47e3645
--- /dev/null
+++ b/chrome/browser/views/first_run_view.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_H__
+#define CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_H__
+
+#include "chrome/browser/views/first_run_view_base.h"
+#include "chrome/browser/views/first_run_customize_view.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/link.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+
+class Label;
+class Window;
+
+}
+
+class Profile;
+class ImporterHost;
+
+// FirstRunView implements the dialog that welcomes to user to Chrome after
+// a fresh install.
+class FirstRunView : public FirstRunViewBase,
+ public ChromeViews::LinkController,
+ public FirstRunCustomizeView::CustomizeViewObserver {
+ public:
+ explicit FirstRunView(Profile* profile);
+ virtual ~FirstRunView();
+
+ // Overridden from ChromeViews::View:
+ virtual void GetPreferredSize(CSize *out);
+ virtual void Layout();
+
+ // Overridden from ChromeViews::DialogDelegate:
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual bool Accept();
+ virtual bool Cancel();
+
+ // Overridden from ChromeViews::WindowDelegate:
+ virtual std::wstring GetWindowTitle() const;
+
+ // Overridden from ChromeViews::LinkActivated:
+ virtual void LinkActivated(ChromeViews::Link* source, int event_flags);
+
+ // Overridden from FirstRunCustomizeView:
+ virtual void CustomizeAccepted();
+ virtual void CustomizeCanceled();
+
+ private:
+ // Initializes the controls on the dialog.
+ void SetupControls();
+
+ // Creates the dialog that allows the user to customize work items.
+ void OpenCustomizeDialog();
+
+ ChromeViews::Label* welcome_label_;
+ ChromeViews::Label* actions_label_;
+ ChromeViews::Label* actions_import_;
+ ChromeViews::Label* actions_shorcuts_;
+ ChromeViews::Link* customize_link_;
+ bool customize_selected_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FirstRunView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_H__
diff --git a/chrome/browser/views/first_run_view_base.cc b/chrome/browser/views/first_run_view_base.cc
new file mode 100644
index 0000000..b9c04fe
--- /dev/null
+++ b/chrome/browser/views/first_run_view_base.cc
@@ -0,0 +1,204 @@
+// 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/views/first_run_view_base.h"
+
+#include "base/command_line.h"
+#include "base/path_service.h"
+#include "base/ref_counted.h"
+#include "base/thread.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/first_run.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/background.h"
+#include "chrome/views/client_view.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/label.h"
+#include "chrome/views/throbber.h"
+#include "chrome/views/separator.h"
+#include "chrome/views/window.h"
+
+#include "generated_resources.h"
+
+FirstRunViewBase::FirstRunViewBase(Profile* profile)
+ : dialog_(NULL),
+ preferred_width_(0),
+ background_image_(NULL),
+ separator_1_(NULL),
+ separator_2_(NULL),
+ importer_host_(NULL),
+ profile_(profile) {
+ DCHECK(profile);
+ SetupControls();
+}
+
+FirstRunViewBase::~FirstRunViewBase() {
+ // Register and set the "show first run information bubble" state so that the
+ // browser can read it later.
+ PrefService* local_state = g_browser_process->local_state();
+ if (!local_state->IsPrefRegistered(prefs::kShouldShowFirstRunBubble)) {
+ local_state->RegisterBooleanPref(prefs::kShouldShowFirstRunBubble, false);
+ local_state->SetBoolean(prefs::kShouldShowFirstRunBubble, true);
+ }
+
+ if (!local_state->IsPrefRegistered(prefs::kShouldShowWelcomePage)) {
+ local_state->RegisterBooleanPref(prefs::kShouldShowWelcomePage, false);
+ local_state->SetBoolean(prefs::kShouldShowWelcomePage, true);
+ }
+}
+
+void FirstRunViewBase::SetupControls() {
+ using ChromeViews::Label;
+ using ChromeViews::ImageView;
+ using ChromeViews::Background;
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ background_image_ = new ChromeViews::ImageView();
+ background_image_->SetImage(rb.GetBitmapNamed(IDR_WIZARD_ICON));
+ background_image_->SetHorizontalAlignment(ImageView::TRAILING);
+
+ int color = 0;
+ {
+ SkAutoLockPixels pixel_loc(background_image_->GetImage());
+ uint32_t* pixel = background_image_->GetImage().getAddr32(0, 0);
+ color = (0xff & (*pixel));
+ }
+ Background* bkg = Background::CreateSolidBackground(color, color, color);
+
+ // The bitmap we use as the background contains a clipped logo and therefore
+ // we can not automatically mirror it for RTL UIs by simply flipping it. This
+ // is why we load a different bitmap if the View is using a right-to-left UI
+ // layout.
+ //
+ // Note that we first load the LTR image and then replace it with the RTL
+ // image because the code above derives the background color from the LTR
+ // image so we have to use the LTR logo initially and then replace it with
+ // the RTL logo if we find out that we are running in a right-to-left locale.
+ if (UILayoutIsRightToLeft())
+ background_image_->SetImage(rb.GetBitmapNamed(IDR_WIZARD_ICON_RTL));
+
+ background_image_->SetBackground(bkg);
+ AddChildView(background_image_);
+
+ // The first separator marks the end of the image.
+ separator_1_ = new ChromeViews::Separator;
+ AddChildView(separator_1_);
+
+ // The second separator marks the start of buttons.
+ separator_2_ = new ChromeViews::Separator;
+ AddChildView(separator_2_);
+}
+
+void FirstRunViewBase::AdjustDialogWidth(const ChromeViews::View* sub_view) {
+ CRect bounds;
+ sub_view->GetBounds(&bounds);
+ preferred_width_ =
+ std::max(preferred_width_,
+ static_cast<int>(bounds.right) + kPanelHorizMargin);
+}
+
+void FirstRunViewBase::SetMinimumDialogWidth(int width) {
+ preferred_width_ = std::max(preferred_width_, width);
+}
+
+void FirstRunViewBase::Layout() {
+ const int kVertSpacing = 8;
+
+ CSize canvas;
+ GetPreferredSize(&canvas);
+
+ CSize pref_size;
+ background_image_->GetPreferredSize(&pref_size);
+ background_image_->SetBounds(0, 0, canvas.cx, pref_size.cy);
+
+ int next_v_space = background_image_->GetY() +
+ background_image_->GetHeight() - 2;
+
+ separator_1_->GetPreferredSize(&pref_size);
+ separator_1_->SetBounds(0, next_v_space,
+ canvas.cx + 1,
+ pref_size.cy);
+
+ next_v_space = canvas.cy - kPanelSubVerticalSpacing;
+ separator_2_->GetPreferredSize(&pref_size);
+ separator_2_->SetBounds(kPanelHorizMargin , next_v_space,
+ canvas.cx - 2*kPanelHorizMargin, pref_size.cy);
+}
+
+bool FirstRunViewBase::CanResize() const {
+ return false;
+}
+
+bool FirstRunViewBase::CanMaximize() const {
+ return false;
+}
+
+bool FirstRunViewBase::IsAlwaysOnTop() const {
+ return false;
+}
+
+bool FirstRunViewBase::HasAlwaysOnTopMenu() const {
+ return false;
+}
+
+int FirstRunViewBase::GetDefaultImportItems() const {
+ // It is best to avoid importing cookies because there is a bug that make
+ // the process take way too much time among other issues. So for the time
+ // being we say: TODO(CPU): Bug 1196875
+ return HISTORY | FAVORITES | PASSWORDS | SEARCH_ENGINES | HOME_PAGE;
+};
+
+void FirstRunViewBase::DisableButtons() {
+ dialog_->EnableClose(false);
+ ChromeViews::ClientView* cv =
+ reinterpret_cast<ChromeViews::ClientView*>(GetParent());
+ if (cv) {
+ cv->ok_button()->SetEnabled(false);
+ cv->cancel_button()->SetEnabled(false);
+ }
+}
+
+bool FirstRunViewBase::CreateDesktopShortcut() {
+ return FirstRun::CreateChromeDesktopShortcut();
+}
+
+bool FirstRunViewBase::CreateQuickLaunchShortcut() {
+ return FirstRun::CreateChromeQuickLaunchShortcut();
+}
+
+bool FirstRunViewBase::FirstRunComplete() {
+ return FirstRun::CreateSentinel();
+}
diff --git a/chrome/browser/views/first_run_view_base.h b/chrome/browser/views/first_run_view_base.h
new file mode 100644
index 0000000..9266ce8
--- /dev/null
+++ b/chrome/browser/views/first_run_view_base.h
@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_BASE_H__
+#define CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_BASE_H__
+#include "chrome/browser/importer.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+
+class Window;
+class ImageView;
+class Separator;
+class Throbber;
+
+}
+
+class Profile;
+class ImporterHost;
+
+// This class abstracts the code that creates the dialog look for the two
+// first-run dialogs. This amounts to the bitmap, the two separators, the
+// progress throbber and some common resize code.
+class FirstRunViewBase : public ChromeViews::View,
+ public ChromeViews::DialogDelegate {
+ public:
+ explicit FirstRunViewBase(Profile* profile);
+ virtual ~FirstRunViewBase();
+
+ // Sets the dialog window that host the view.
+ void set_dialog(ChromeViews::Window* dialog) { dialog_ = dialog; }
+
+ // Overridden from ChromeViews::View.
+ virtual void Layout();
+
+ // Overridden from ChromeViews::WindowDelegate.
+ virtual bool CanResize() const;
+ virtual bool CanMaximize() const;
+ virtual bool IsAlwaysOnTop() const;
+ virtual bool HasAlwaysOnTopMenu() const;
+
+ protected:
+ // Returns the items that the first run process is required to import
+ // from other browsers.
+ int GetDefaultImportItems() const;
+
+ // Creates the desktop and quick launch shortcut. Existing shortcut is lost.
+ bool CreateDesktopShortcut();
+ bool CreateQuickLaunchShortcut();
+
+ // Modifies the chrome configuration so that the first-run dialogs are not
+ // shown again.
+ bool FirstRunComplete();
+
+ // Disables the standard buttons of the dialog. Useful when importing.
+ void DisableButtons();
+ // Computes a tight dialog width given a contained UI element.
+ void AdjustDialogWidth(const ChromeViews::View* sub_view);
+
+ // Sets a minimum dialog size.
+ void SetMinimumDialogWidth(int width);
+
+ // Returns the background image. It is useful for getting the metrics.
+ const ChromeViews::ImageView* background_image() const {
+ return background_image_;
+ }
+ // Returns the computed preferred width of the dialog. This value can change
+ // when AdjustDialogWidth() is called during layout.
+ int preferred_width() const {
+ return preferred_width_;
+ }
+
+ scoped_refptr<ImporterHost> importer_host_;
+ Profile* profile_;
+ ChromeViews::Window* dialog_;
+
+ private:
+ // Initializes the controls on the dialog.
+ void SetupControls();
+ ChromeViews::ImageView* background_image_;
+ ChromeViews::Separator* separator_1_;
+ ChromeViews::Separator* separator_2_;
+ int preferred_width_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FirstRunViewBase);
+};
+
+#endif // CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_BASE_H__
diff --git a/chrome/browser/views/go_button.cc b/chrome/browser/views/go_button.cc
new file mode 100644
index 0000000..8319be9
--- /dev/null
+++ b/chrome/browser/views/go_button.cc
@@ -0,0 +1,158 @@
+// 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/views/go_button.h"
+
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/views/location_bar_view.h"
+#include "chrome/common/l10n_util.h"
+
+#include "generated_resources.h"
+
+GoButton::GoButton(LocationBarView* location_bar,
+ CommandController* controller) : ToggleButton(),
+ location_bar_(location_bar),
+ controller_(controller),
+ intended_mode_(MODE_GO),
+ visible_mode_(MODE_GO),
+ button_delay_(NULL),
+ stop_timer_(this) {
+ DCHECK(location_bar_);
+}
+
+GoButton::~GoButton() {
+ stop_timer_.RevokeAll();
+}
+
+void GoButton::NotifyClick(int mouse_event_flags) {
+ if (visible_mode_ == MODE_STOP) {
+ controller_->ExecuteCommand(IDC_STOP);
+
+ // The user has clicked, so we can feel free to update the button,
+ // even if the mouse is still hovering.
+ ChangeMode(MODE_GO);
+ } else if (visible_mode_ == MODE_GO && stop_timer_.empty()) {
+ // If the go button is visible and not within the doubleclick timer, go.
+ controller_->ExecuteCommand(IDC_GO);
+
+ // Figure out the system double-click time.
+ if (button_delay_ == NULL)
+ button_delay_ = GetDoubleClickTime();
+
+ // Stop any existing timers.
+ stop_timer_.RevokeAll();
+
+ // Start a timer - while this timer is running, the go button
+ // cannot be changed to a stop button. We do not set intended_mode_
+ // to MODE_STOP here as we want to wait for the browser to tell
+ // us that it has started loading (and this may occur only after
+ // some delay).
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ stop_timer_.NewRunnableMethod(&GoButton::OnButtonTimer),
+ button_delay_);
+ }
+}
+
+void GoButton::OnMouseExited(const ChromeViews::MouseEvent& e) {
+ using namespace ChromeViews;
+
+ if (visible_mode_ != intended_mode_)
+ ChangeMode(intended_mode_);
+
+ if (GetState() != BS_DISABLED)
+ SetState(BS_NORMAL);
+}
+
+void GoButton::ChangeMode(Mode mode) {
+ stop_timer_.RevokeAll();
+
+ SetToggled(mode == MODE_STOP);
+ intended_mode_ = mode;
+ visible_mode_ = mode;
+}
+
+void GoButton::ScheduleChangeMode(Mode mode) {
+ if (mode == MODE_STOP) {
+ // If we still have a timer running, we can't yet change to a stop sign,
+ // so we'll queue up the change for when the timer expires or for when
+ // the mouse exits the button.
+ if (!stop_timer_.empty() && GetState() == BS_HOT) {
+ intended_mode_ = MODE_STOP;
+ } else {
+ ChangeMode(MODE_STOP);
+ }
+ } else {
+ // If we want to change the button to a go button, but the user's mouse
+ // is hovering, don't change the mode just yet - this prevents the
+ // stop button changing to a go under the user's mouse cursor.
+ if (visible_mode_ == MODE_STOP && GetState() == BS_HOT) {
+ intended_mode_ = MODE_GO;
+ } else {
+ ChangeMode(MODE_GO);
+ }
+ }
+}
+
+void GoButton::OnButtonTimer() {
+ if (intended_mode_ != visible_mode_)
+ ChangeMode(intended_mode_);
+
+ stop_timer_.RevokeAll();
+}
+
+bool GoButton::GetTooltipText(int x, int y, std::wstring* tooltip) {
+ if (visible_mode_ == MODE_STOP) {
+ tooltip->assign(l10n_util::GetString(IDS_TOOLTIP_STOP));
+ return true;
+ }
+
+ std::wstring current_text(location_bar_->location_entry()->GetText());
+ if (current_text.empty())
+ return false;
+
+ // Need to make sure the text direction is adjusted based on the locale so
+ // that pure LTR strings are displayed appropriately on RTL locales. For
+ // example, not adjusting the string will cause the URL
+ // "http://www.google.com/" to be displayed in the tooltip as
+ // "/http://www.google.com".
+ //
+ // Note that we mark the URL's text as LTR (instead of examining the
+ // characters and guessing the text directionality) since URLs are always
+ // treated as left-to-right text, even when they contain RTL characters.
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
+ l10n_util::WrapStringWithLTRFormatting(&current_text);
+
+ // TODO(pkasting): http://b/868940 Use the right strings at the right times by
+ // asking the autocomplete system what to do. Don't hardcode "Google" as the
+ // search provider name.
+ tooltip->assign(true ?
+ l10n_util::GetStringF(IDS_TOOLTIP_GO_SITE, current_text) :
+ l10n_util::GetStringF(IDS_TOOLTIP_GO_SEARCH, L"Google", current_text));
+ return true;
+}
diff --git a/chrome/browser/views/go_button.h b/chrome/browser/views/go_button.h
new file mode 100644
index 0000000..72060f9
--- /dev/null
+++ b/chrome/browser/views/go_button.h
@@ -0,0 +1,89 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_GO_BUTTON_H__
+#define CHROME_BROWSER_VIEWS_GO_BUTTON_H__
+
+#include "chrome/views/button.h"
+#include "chrome/browser/controller.h"
+#include "base/task.h"
+
+class LocationBarView;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// GoButton
+//
+// The go button attached to the toolbar. It shows different tooltips
+// according to the content of the location bar and changes to a stop
+// button when a page load is in progress. Trickiness comes from the
+// desire to have the 'stop' button not change back to 'go' if the user's
+// mouse is hovering over it (to prevent misclicks).
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class GoButton : public ChromeViews::ToggleButton {
+ public:
+ GoButton(LocationBarView* location_bar, CommandController* controller);
+ virtual ~GoButton();
+
+ typedef enum Mode { MODE_GO = 0, MODE_STOP };
+
+ virtual void NotifyClick(int mouse_event_flags);
+ virtual void OnMouseExited(const ChromeViews::MouseEvent& e);
+
+ // Force the button state
+ void ChangeMode(Mode mode);
+
+ // Ask for a specified button state. This is commonly called by the Browser
+ // when page load state changes.
+ void ScheduleChangeMode(Mode mode);
+
+ virtual bool GetTooltipText(int x, int y, std::wstring* tooltip);
+
+ private:
+ void OnButtonTimer();
+
+ int button_delay_;
+ ScopedRunnableMethodFactory<GoButton> stop_timer_;
+
+ LocationBarView* location_bar_;
+ CommandController* controller_;
+ ButtonListener* listener_;
+
+ // The mode we should be in
+ Mode intended_mode_;
+
+ // The currently-visible mode - this may different from the intended mode
+ Mode visible_mode_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(GoButton);
+};
+
+#endif // CHROME_BROWSER_VIEWS_GO_BUTTON_H__
diff --git a/chrome/browser/views/html_dialog_view.cc b/chrome/browser/views/html_dialog_view.cc
new file mode 100644
index 0000000..1be835f
--- /dev/null
+++ b/chrome/browser/views/html_dialog_view.cc
@@ -0,0 +1,201 @@
+// 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/browser.h"
+#include "chrome/browser/views/html_dialog_view.h"
+
+#include "chrome/views/window.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// HtmlDialogView, public:
+
+HtmlDialogView::HtmlDialogView(Browser* parent_browser,
+ Profile* profile,
+ HtmlDialogContentsDelegate* delegate)
+ : DOMView(delegate->GetDialogContentURL()),
+ dialog_(NULL),
+ parent_browser_(parent_browser),
+ profile_(profile),
+ delegate_(delegate) {
+ DCHECK(parent_browser);
+ DCHECK(profile);
+}
+
+HtmlDialogView::~HtmlDialogView() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HtmlDialogView, ChromeViews::View implementation:
+
+void HtmlDialogView::GetPreferredSize(CSize *out) {
+ delegate_->GetDialogSize(out);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HtmlDialogView, ChromeViews::WindowDelegate implementation:
+
+bool HtmlDialogView::CanResize() const {
+ return true;
+}
+
+bool HtmlDialogView::IsModal() const {
+ return delegate_->IsModal();
+}
+
+std::wstring HtmlDialogView::GetWindowTitle() const {
+ return L"Google Gears";
+}
+
+void HtmlDialogView::WindowClosing() {
+ // If we still have a delegate that means we haven't notified it of the
+ // dialog closing. This happens if the user clicks the Close button on the
+ // dialog.
+ if (delegate_)
+ OnDialogClosed("");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HtmlDialogContentsDelegate implementation:
+
+GURL HtmlDialogView::GetDialogContentURL() const {
+ return delegate_->GetDialogContentURL();
+}
+
+void HtmlDialogView::GetDialogSize(CSize* size) const {
+ return delegate_->GetDialogSize(size);
+}
+
+std::string HtmlDialogView::GetDialogArgs() const {
+ return delegate_->GetDialogArgs();
+}
+
+void HtmlDialogView::OnDialogClosed(const std::string& json_retval) {
+ delegate_->OnDialogClosed(json_retval);
+ delegate_ = NULL; // We will not communicate further with the delegate.
+ dialog_->Close();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PageNavigator implementation:
+void HtmlDialogView::OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ // Force all links to open in a new window, ignoring the incoming
+ // disposition. This is a tabless, modal dialog so we can't just
+ // open it in the current frame.
+ parent_browser_->OpenURLFromTab(source, url, NEW_WINDOW, transition);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabContentsDelegate implementation:
+
+void HtmlDialogView::NavigationStateChanged(const TabContents* source,
+ unsigned changed_flags) {
+ // We shouldn't receive any NavigationStateChanged except the first
+ // one, which we ignore because we're a dialog box.
+}
+
+void HtmlDialogView::ReplaceContents(TabContents* source,
+ TabContents* new_contents) {
+}
+
+void HtmlDialogView::AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
+ parent_browser_->AddNewContents(
+ source, new_contents, NEW_WINDOW, initial_pos, user_gesture);
+}
+
+void HtmlDialogView::ActivateContents(TabContents* contents) {
+ // We don't do anything here because there's only one TabContents in
+ // this frame and we don't have a TabStripModel.
+}
+
+void HtmlDialogView::LoadingStateChanged(TabContents* source) {
+ // We don't care about this notification
+}
+
+void HtmlDialogView::CloseContents(TabContents* source) {
+ // We receive this message but don't handle it because we really do the
+ // cleanup in OnDialogClosed().
+}
+
+void HtmlDialogView::MoveContents(TabContents* source, const gfx::Rect& pos) {
+ // The contained web page wishes to resize itself. We let it do this because
+ // if it's a dialog we know about, we trust it not to be mean to the user.
+
+ // Determine the size the window containing the dialog at its requested size.
+ gfx::Size window_size =
+ dialog_->CalculateWindowSizeForClientSize(pos.size());
+
+ // Actually size the window.
+ CRect vc_bounds;
+ GetViewContainer()->GetBounds(&vc_bounds, true);
+ gfx::Rect bounds(vc_bounds.left, vc_bounds.top, window_size.width(),
+ window_size.height());
+ dialog_->SetBounds(bounds);
+}
+
+bool HtmlDialogView::IsPopup(TabContents* source) {
+ // This needs to return true so that we are allowed to be resized by our
+ // contents.
+ return true;
+}
+
+void HtmlDialogView::ToolbarSizeChanged(TabContents* source, bool is_animating) {
+ Layout();
+}
+
+void HtmlDialogView::URLStarredChanged(TabContents* source, bool starred) {
+ // We don't have a visible star to click in the window.
+ NOTREACHED();
+}
+
+void HtmlDialogView::UpdateTargetURL(TabContents* source, const GURL& url) {
+ // Ignored.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HtmlDialogView:
+
+void HtmlDialogView::InitDialog(ChromeViews::Window* dialog) {
+ dialog_ = dialog;
+
+ // Now Init the DOMView. This view runs in its own process to render the html.
+ DOMView::Init(profile_, NULL);
+
+ // Make sure this new TabContents we just created in Init() knows about us.
+ DCHECK(host_->type() == TAB_CONTENTS_HTML_DIALOG);
+ HtmlDialogContents* host = static_cast<HtmlDialogContents*>(host_);
+ host->Init(this);
+ host->set_delegate(this);
+}
diff --git a/chrome/browser/views/html_dialog_view.h b/chrome/browser/views/html_dialog_view.h
new file mode 100644
index 0000000..54f8f15
--- /dev/null
+++ b/chrome/browser/views/html_dialog_view.h
@@ -0,0 +1,122 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_HTML_DIALOG_VIEW_H__
+#define CHROME_BROWSER_VIEWS_HTML_DIALOG_VIEW_H__
+
+#include <string>
+
+#include "chrome/browser/dom_ui/html_dialog_contents.h"
+#include "chrome/browser/tab_contents_delegate.h"
+#include "chrome/browser/views/dom_view.h"
+
+namespace ChromeViews {
+ class Window;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// HtmlDialogView is a view used to display an HTML dialog to the user. The
+// content of the dialogs is determined by the delegate
+// (HtmlDialogContentsDelegate), but is basically a file URL along with a
+// JSON input string. The HTML is supposed to show a UI to the user and is
+// expected to send back a JSON file as a return value.
+//
+////////////////////////////////////////////////////////////////////////////////
+class HtmlDialogView
+ : public DOMView,
+ public HtmlDialogContentsDelegate,
+ public TabContentsDelegate {
+ public:
+ HtmlDialogView(Browser* parent_browser,
+ Profile* profile,
+ HtmlDialogContentsDelegate* delegate);
+ virtual ~HtmlDialogView();
+
+ // Initializes the contents of the dialog (the DOMView and the callbacks).
+ void InitDialog(ChromeViews::Window* dialog);
+
+ // Overridden from ChromeViews::View:
+ virtual void GetPreferredSize(CSize *out);
+
+ // Overridden from ChromeViews::WindowDelegate:
+ virtual bool CanResize() const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual void WindowClosing();
+
+ // Overridden from HtmlDialogContentsDelegate:
+ virtual GURL GetDialogContentURL() const;
+ virtual void GetDialogSize(CSize* size) const;
+ virtual std::string GetDialogArgs() const;
+ virtual void OnDialogClosed(const std::string& json_retval);
+
+ // Overridden from TabContentsDelegate:
+ virtual void OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition);
+ virtual void NavigationStateChanged(const TabContents* source,
+ unsigned changed_flags);
+ virtual void ReplaceContents(TabContents* source,
+ TabContents* new_contents);
+ virtual void AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture);
+ virtual void ActivateContents(TabContents* contents);
+ virtual void LoadingStateChanged(TabContents* source);
+ virtual void CloseContents(TabContents* source);
+ virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
+ virtual bool IsPopup(TabContents* source);
+ virtual void ToolbarSizeChanged(TabContents* source, bool is_animating);
+ virtual void URLStarredChanged(TabContents* source, bool starred);
+ virtual void UpdateTargetURL(TabContents* source, const GURL& url);
+
+ private:
+ // The dialog this view is displayed in.
+ ChromeViews::Window* dialog_;
+
+ // The Browser object which created this html dialog; we send all
+ // window opening/navigations to this object.
+ Browser* parent_browser_;
+
+ Profile* profile_;
+
+ // This view is a delegate to the HTML content since it needs to get notified
+ // about when the dialog is closing. For all other actions (besides dialog
+ // closing) we delegate to the creator of this view, which we keep track of
+ // using this variable.
+ HtmlDialogContentsDelegate* delegate_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HtmlDialogView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_HTML_DIALOG_VIEW_H__
diff --git a/chrome/browser/views/hung_renderer_view.cc b/chrome/browser/views/hung_renderer_view.cc
new file mode 100644
index 0000000..efb03c5
--- /dev/null
+++ b/chrome/browser/views/hung_renderer_view.cc
@@ -0,0 +1,475 @@
+// 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/views/hung_renderer_view.h"
+
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/path.h"
+#include "chrome/common/logging_chrome.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/client_view.h"
+#include "chrome/views/custom_frame_window.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/group_table_view.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/native_button.h"
+#include "generated_resources.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// HungPagesTableModel
+
+class HungPagesTableModel : public ChromeViews::GroupTableModel {
+ public:
+ HungPagesTableModel();
+ virtual ~HungPagesTableModel();
+
+ void InitForWebContents(WebContents* hung_contents);
+
+ // Overridden from ChromeViews::GroupTableModel:
+ virtual int RowCount();
+ virtual std::wstring GetText(int row, int column_id);
+ virtual SkBitmap GetIcon(int row);
+ virtual void SetObserver(ChromeViews::TableModelObserver* observer);
+ virtual void GetGroupRangeForItem(int item, ChromeViews::GroupRange* range);
+
+ private:
+ typedef std::vector<WebContents*> WebContentsVector;
+ WebContentsVector webcontentses_;
+
+ ChromeViews::TableModelObserver* observer_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HungPagesTableModel);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// HungPagesTableModel, public:
+
+HungPagesTableModel::HungPagesTableModel() : observer_(NULL) {
+}
+
+HungPagesTableModel::~HungPagesTableModel() {
+}
+
+void HungPagesTableModel::InitForWebContents(WebContents* hung_contents) {
+ webcontentses_.clear();
+ for (WebContentsIterator it; !it.done(); ++it) {
+ if (it->process() == hung_contents->process())
+ webcontentses_.push_back(*it);
+ }
+ // The world is different.
+ if (observer_)
+ observer_->OnModelChanged();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HungPagesTableModel, ChromeViews::GroupTableModel implementation:
+
+int HungPagesTableModel::RowCount() {
+ return static_cast<int>(webcontentses_.size());
+}
+
+std::wstring HungPagesTableModel::GetText(int row, int column_id) {
+ DCHECK(row >= 0 && row < RowCount());
+ std::wstring title = webcontentses_.at(row)->GetTitle();
+ if (title.empty())
+ title = l10n_util::GetString(IDS_TAB_UNTITLED_TITLE);
+ return title;
+}
+
+SkBitmap HungPagesTableModel::GetIcon(int row) {
+ DCHECK(row >= 0 && row < RowCount());
+ return webcontentses_.at(row)->GetFavIcon();
+}
+
+void HungPagesTableModel::SetObserver(
+ ChromeViews::TableModelObserver* observer) {
+ observer_ = observer;
+}
+
+void HungPagesTableModel::GetGroupRangeForItem(
+ int item,
+ ChromeViews::GroupRange* range) {
+ DCHECK(range);
+ range->start = 0;
+ range->length = RowCount();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HungRendererWarningView
+
+class HungRendererWarningView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ HungRendererWarningView();
+ ~HungRendererWarningView();
+
+ void ShowForWebContents(WebContents* contents);
+ void EndForWebContents(WebContents* contents);
+
+ void set_window(ChromeViews::Window* window) { window_ = window; }
+
+ // ChromeViews::WindowDelegate overrides:
+ virtual std::wstring GetWindowTitle() const;
+ virtual void WindowClosing();
+ virtual int GetDialogButtons() const;
+ virtual std::wstring GetDialogButtonLabel(
+ ChromeViews::DialogDelegate::DialogButton button) const;
+ virtual ChromeViews::View* GetExtraView();
+ virtual bool Accept(bool window_closing);
+
+ // ChromeViews::NativeButton::Listener overrides:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ // Initialize the controls in this dialog.
+ void Init();
+ void CreateKillButtonView();
+
+ // Returns the bounds the dialog should be displayed at to be meaningfully
+ // associated with the specified WebContents.
+ gfx::Rect GetDisplayBounds(WebContents* contents);
+
+ static void InitClass();
+
+ // Controls within the dialog box.
+ ChromeViews::ImageView* frozen_icon_view_;
+ ChromeViews::Label* info_label_;
+ ChromeViews::GroupTableView* hung_pages_table_;
+
+ // The button we insert into the ClientView to kill the errant process. This
+ // is parented to a container view that uses a grid layout to align it
+ // properly.
+ ChromeViews::NativeButton* kill_button_;
+ class ButtonContainer : public ChromeViews::View {
+ public:
+ ButtonContainer() {}
+ virtual ~ButtonContainer() {}
+
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current) {
+ Layout();
+ }
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ButtonContainer);
+ };
+ ButtonContainer* kill_button_container_;
+
+ // The Window that contains this view.
+ ChromeViews::Window* window_;
+
+ // The model that provides the contents of the table that shows a list of
+ // pages affected by the hang.
+ scoped_ptr<HungPagesTableModel> hung_pages_table_model_;
+
+ // The WebContents that we detected had hung in the first place resulting in
+ // the display of this view.
+ WebContents* contents_;
+
+ // Whether or not we've created controls for ourself.
+ bool initialized_;
+
+ // An amusing icon image.
+ static SkBitmap* frozen_icon_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HungRendererWarningView);
+};
+
+// static
+SkBitmap* HungRendererWarningView::frozen_icon_ = NULL;
+
+// The distance in pixels from the top of the relevant contents to place the
+// warning window.
+static const int kOverlayContentsOffsetY = 50;
+
+// The dimensions of the hung pages list table view, in pixels.
+static const int kTableViewWidth = 300;
+static const int kTableViewHeight = 100;
+
+///////////////////////////////////////////////////////////////////////////////
+// HungRendererWarningView, public:
+
+HungRendererWarningView::HungRendererWarningView()
+ : frozen_icon_view_(NULL),
+ info_label_(NULL),
+ hung_pages_table_(NULL),
+ kill_button_(NULL),
+ kill_button_container_(NULL),
+ window_(NULL),
+ contents_(NULL),
+ initialized_(false) {
+ InitClass();
+}
+
+HungRendererWarningView::~HungRendererWarningView() {
+ hung_pages_table_->SetModel(NULL);
+}
+
+void HungRendererWarningView::ShowForWebContents(WebContents* contents) {
+ DCHECK(contents && window_);
+ contents_ = contents;
+
+ // Don't show the warning unless the foreground window is the frame, or this
+ // window (but still invisible). If the user has another window or
+ // application selected, activating ourselves is rude.
+ HWND frame_hwnd = GetAncestor(contents->GetContainerHWND(), GA_ROOT);
+ HWND foreground_window = GetForegroundWindow();
+ if (foreground_window != frame_hwnd &&
+ foreground_window != window_->GetHWND()) {
+ return;
+ }
+
+ if (!window_->IsActive()) {
+ gfx::Rect bounds = GetDisplayBounds(contents);
+ window_->SetBounds(bounds, frame_hwnd);
+
+ // We only do this if the window isn't active (i.e. hasn't been shown yet,
+ // or is currently shown but deactivated for another WebContents). This is
+ // because this window is a singleton, and it's possible another active
+ // renderer may hang while this one is showing, and we don't want to reset
+ // the list of hung pages for a potentially unrelated renderer while this
+ // one is showing.
+ hung_pages_table_model_->InitForWebContents(contents);
+ window_->Show();
+ }
+}
+
+void HungRendererWarningView::EndForWebContents(WebContents* contents) {
+ DCHECK(contents);
+ if (contents_ && contents_->process() == contents->process()) {
+ window_->Close();
+ // Since we're closing, we no longer need this WebContents.
+ contents_ = NULL;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HungRendererWarningView, ChromeViews::DialogDelegate implementation:
+
+std::wstring HungRendererWarningView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_PRODUCT_NAME);
+}
+
+void HungRendererWarningView::WindowClosing() {
+ // We are going to be deleted soon, so make sure our instance is destroyed.
+ HungRendererWarning::instance_ = NULL;
+}
+
+int HungRendererWarningView::GetDialogButtons() const {
+ // We specifically don't want a CANCEL button here because that code path is
+ // also called when the window is closed by the user clicking the X button in
+ // the window's titlebar, and also if we call Window::Close. Rather, we want
+ // the OK button to wait for responsiveness (and close the dialog) and our
+ // additional button (which we create) to kill the process (which will result
+ // in the dialog being destroyed).
+ return DIALOGBUTTON_OK;
+}
+
+std::wstring HungRendererWarningView::GetDialogButtonLabel(
+ ChromeViews::DialogDelegate::DialogButton button) const {
+ if (button == DIALOGBUTTON_OK)
+ return l10n_util::GetString(IDS_BROWSER_HANGMONITOR_RENDERER_WAIT);
+ return std::wstring();
+}
+
+ChromeViews::View* HungRendererWarningView::GetExtraView() {
+ return kill_button_container_;
+}
+
+bool HungRendererWarningView::Accept(bool window_closing) {
+ // Don't do anything if we're being called only because the dialog is being
+ // destroyed and we don't supply a Cancel function...
+ if (window_closing)
+ return true;
+
+ // Start waiting again for responsiveness.
+ if (contents_ && contents_->render_view_host())
+ contents_->render_view_host()->ResetHangMonitorTimeout();
+ return true;
+}
+
+void HungRendererWarningView::ButtonPressed(
+ ChromeViews::NativeButton* sender) {
+ if (sender == kill_button_) {
+ // Kill the process.
+ HANDLE process = contents_->process()->process();
+ TerminateProcess(process, 0);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HungRendererWarningView, ChromeViews::View overrides:
+
+void HungRendererWarningView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (!initialized_ && is_add && child == this && GetViewContainer())
+ Init();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HungRendererWarningView, private:
+
+void HungRendererWarningView::Init() {
+ frozen_icon_view_ = new ChromeViews::ImageView;
+ frozen_icon_view_->SetImage(frozen_icon_);
+
+ info_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_BROWSER_HANGMONITOR_RENDERER));
+ info_label_->SetMultiLine(true);
+ info_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+
+ hung_pages_table_model_.reset(new HungPagesTableModel);
+ std::vector<ChromeViews::TableColumn> columns;
+ columns.push_back(ChromeViews::TableColumn());
+ hung_pages_table_ = new ChromeViews::GroupTableView(
+ hung_pages_table_model_.get(), columns, ChromeViews::ICON_AND_TEXT, true,
+ false, true);
+ hung_pages_table_->SetPreferredSize(
+ CSize(kTableViewWidth, kTableViewHeight));
+
+ CreateKillButtonView();
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ const int double_column_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(double_column_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
+ GridLayout::FIXED, frozen_icon_->width(), 0);
+ column_set->AddPaddingColumn(0, kUnrelatedControlLargeHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, double_column_set_id);
+ layout->AddView(frozen_icon_view_, 1, 3);
+ layout->AddView(info_label_);
+
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+
+ layout->StartRow(0, double_column_set_id);
+ layout->SkipColumns(1);
+ layout->AddView(hung_pages_table_);
+
+ initialized_ = true;
+}
+
+void HungRendererWarningView::CreateKillButtonView() {
+ kill_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_BROWSER_HANGMONITOR_RENDERER_END));
+ kill_button_->SetListener(this);
+
+ kill_button_container_ = new ButtonContainer;
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = new GridLayout(kill_button_container_);
+ kill_button_container_->SetLayoutManager(layout);
+
+ const int single_column_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_set_id);
+ column_set->AddPaddingColumn(0, frozen_icon_->width() +
+ kPanelHorizMargin + kUnrelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_set_id);
+ layout->AddView(kill_button_);
+}
+
+gfx::Rect HungRendererWarningView::GetDisplayBounds(
+ WebContents* contents) {
+ HWND contents_hwnd = contents->GetContainerHWND();
+ CRect contents_bounds;
+ GetWindowRect(contents_hwnd, &contents_bounds);
+
+ CRect window_bounds;
+ window_->GetBounds(&window_bounds, true);
+
+ int window_x = contents_bounds.left +
+ (contents_bounds.Width() - window_bounds.Width()) / 2;
+ int window_y = contents_bounds.top + kOverlayContentsOffsetY;
+ return gfx::Rect(window_x, window_y, window_bounds.Width(),
+ window_bounds.Height());
+}
+
+// static
+void HungRendererWarningView::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ frozen_icon_ = rb.GetBitmapNamed(IDR_FROZEN_TAB_ICON);
+ initialized = true;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HungRendererWarning
+
+// static
+HungRendererWarningView* HungRendererWarning::instance_ = NULL;
+
+static HungRendererWarningView* CreateHungRendererWarningView() {
+ HungRendererWarningView* cv = new HungRendererWarningView;
+ ChromeViews::Window* window =
+ ChromeViews::Window::CreateChromeWindow(NULL, gfx::Rect(), cv, cv);
+ cv->set_window(window);
+ return cv;
+}
+
+// static
+void HungRendererWarning::ShowForWebContents(WebContents* contents) {
+ if (!logging::DialogsAreSuppressed()) {
+ if (!instance_)
+ instance_ = CreateHungRendererWarningView();
+ instance_->ShowForWebContents(contents);
+ }
+}
+
+// static
+void HungRendererWarning::HideForWebContents(WebContents* contents) {
+ if (!logging::DialogsAreSuppressed() && instance_)
+ instance_->EndForWebContents(contents);
+}
diff --git a/chrome/browser/views/hung_renderer_view.h b/chrome/browser/views/hung_renderer_view.h
new file mode 100644
index 0000000..19e410e
--- /dev/null
+++ b/chrome/browser/views/hung_renderer_view.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_HUNG_RENDERER_VIEW_H__
+#define CHROME_BROWSER_VIEWS_HUNG_RENDERER_VIEW_H__
+
+#include "base/logging.h"
+
+class HungRendererWarningView;
+namespace ChromeViews {
+class Window;
+}
+class WebContents;
+
+class HungRendererWarning {
+ public:
+ static void ShowForWebContents(WebContents* contents);
+ static void HideForWebContents(WebContents* contents);
+
+ private:
+ friend HungRendererWarningView;
+
+ // We only support showing one of these at a time per app.
+ static HungRendererWarningView* instance_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HungRendererWarning);
+};
+
+
+#endif // CHROME_BROWSER_VIEWS_HUNG_RENDERER_VIEW_H__
diff --git a/chrome/browser/views/importer_lock_view.cc b/chrome/browser/views/importer_lock_view.cc
new file mode 100644
index 0000000..9e4b00e
--- /dev/null
+++ b/chrome/browser/views/importer_lock_view.cc
@@ -0,0 +1,99 @@
+// 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/views/importer_lock_view.h"
+
+#include "chrome/browser/importer.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/label.h"
+
+#include "generated_resources.h"
+
+using ChromeViews::ColumnSet;
+using ChromeViews::GridLayout;
+
+// Default size of the dialog window.
+static const int kDefaultWindowWidth = 320;
+static const int kDefaultWindowHeight = 100;
+
+ImporterLockView::ImporterLockView(ImporterHost* host)
+ : dialog_(NULL),
+ description_label_(NULL),
+ importer_host_(host) {
+ description_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_IMPORTER_LOCK_TEXT));
+ description_label_->SetMultiLine(true);
+ description_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ AddChildView(description_label_);
+}
+
+ImporterLockView::~ImporterLockView() {
+}
+
+void ImporterLockView::GetPreferredSize(CSize *out) {
+ out->cx = kDefaultWindowWidth;
+ out->cy = kDefaultWindowHeight;
+}
+
+void ImporterLockView::Layout() {
+ description_label_->SetBounds(kPanelHorizMargin, kPanelVertMargin,
+ kDefaultWindowWidth - 2 * kPanelHorizMargin,
+ kDefaultWindowHeight - 2 * kPanelVertMargin);
+}
+
+std::wstring ImporterLockView::GetDialogButtonLabel(
+ DialogButton button) const {
+ if (button == DIALOGBUTTON_OK) {
+ return l10n_util::GetString(IDS_IMPORTER_LOCK_OK);
+ } else if (button == DIALOGBUTTON_CANCEL) {
+ return l10n_util::GetString(IDS_IMPORTER_LOCK_CANCEL);
+ }
+ return std::wstring();
+}
+
+bool ImporterLockView::IsModal() const {
+ return true;
+}
+
+std::wstring ImporterLockView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_IMPORTER_LOCK_TITLE);
+}
+
+bool ImporterLockView::Accept() {
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ importer_host_, &ImporterHost::OnLockViewEnd, true));
+ return true;
+}
+
+bool ImporterLockView::Cancel() {
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ importer_host_, &ImporterHost::OnLockViewEnd, false));
+ return true;
+}
diff --git a/chrome/browser/views/importer_lock_view.h b/chrome/browser/views/importer_lock_view.h
new file mode 100644
index 0000000..876560a
--- /dev/null
+++ b/chrome/browser/views/importer_lock_view.h
@@ -0,0 +1,78 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_IMPORTER_LOCK_VIEW_H__
+#define CHROME_BROWSER_VIEWS_IMPORTER_LOCK_VIEW_H__
+
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+
+class Label;
+class Window;
+
+}
+
+class ImporterHost;
+
+// ImporterLockView draws the dialog, and asks the user to shut Firefox
+// down before starting the import.
+class ImporterLockView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate {
+ public:
+ explicit ImporterLockView(ImporterHost* host);
+ virtual ~ImporterLockView();
+
+ void set_dialog(ChromeViews::Window* dialog) {
+ dialog_ = dialog;
+ }
+
+ // Overridden from ChromeViews::View.
+ virtual void GetPreferredSize(CSize *out);
+ virtual void Layout();
+
+ // Overridden from ChromeViews::DialogDelegate:
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Accept();
+ virtual bool Cancel();
+
+ private:
+ ChromeViews::Label* description_label_;
+
+ ChromeViews::Window* dialog_;
+
+ ImporterHost* importer_host_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ImporterLockView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_IMPORTER_LOCK_VIEW_H__
diff --git a/chrome/browser/views/importer_view.cc b/chrome/browser/views/importer_view.cc
new file mode 100644
index 0000000..bd71637
--- /dev/null
+++ b/chrome/browser/views/importer_view.cc
@@ -0,0 +1,199 @@
+// 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/views/importer_view.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/window.h"
+
+#include "generated_resources.h"
+
+using ChromeViews::ColumnSet;
+using ChromeViews::GridLayout;
+
+ImporterView::ImporterView(Profile* profile)
+ : dialog_(NULL),
+ import_from_label_(NULL),
+ profile_combobox_(NULL),
+ import_items_label_(NULL),
+ history_checkbox_(NULL),
+ favorites_checkbox_(NULL),
+ passwords_checkbox_(NULL),
+ search_engines_checkbox_(NULL),
+ profile_(profile),
+ importer_host_(new ImporterHost()) {
+ DCHECK(profile);
+ SetupControl();
+}
+
+ImporterView::~ImporterView() {
+}
+
+void ImporterView::SetupControl() {
+ // Adds all controls.
+ import_from_label_ =
+ new ChromeViews::Label(l10n_util::GetString(IDS_IMPORT_FROM_LABEL));
+
+ profile_combobox_ = new ChromeViews::ComboBox(this);
+
+ import_items_label_ =
+ new ChromeViews::Label(l10n_util::GetString(IDS_IMPORT_ITEMS_LABEL));
+
+ history_checkbox_ =
+ InitCheckbox(l10n_util::GetString(IDS_IMPORT_HISTORY_CHKBOX), true);
+ favorites_checkbox_ =
+ InitCheckbox(l10n_util::GetString(IDS_IMPORT_FAVORITES_CHKBOX), true);
+ passwords_checkbox_ =
+ InitCheckbox(l10n_util::GetString(IDS_IMPORT_PASSWORDS_CHKBOX), true);
+ search_engines_checkbox_ =
+ InitCheckbox(l10n_util::GetString(IDS_IMPORT_SEARCH_ENGINES_CHKBOX),
+ true);
+
+ // Arranges controls by using GridLayout.
+ const int column_set_id = 0;
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+ ColumnSet* column_set = layout->AddColumnSet(column_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::FIXED, 200, 0);
+
+ layout->StartRow(0, column_set_id);
+ layout->AddView(import_from_label_);
+ layout->AddView(profile_combobox_);
+
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+ layout->StartRow(0, column_set_id);
+ layout->AddView(import_items_label_, 3, 1);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, column_set_id);
+ layout->AddView(favorites_checkbox_, 3, 1);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, column_set_id);
+ layout->AddView(search_engines_checkbox_, 3, 1);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, column_set_id);
+ layout->AddView(passwords_checkbox_, 3, 1);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, column_set_id);
+ layout->AddView(history_checkbox_, 3, 1);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+}
+
+void ImporterView::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_IMPORT_DIALOG_WIDTH_CHARS,
+ IDS_IMPORT_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void ImporterView::Layout() {
+ GetLayoutManager()->Layout(this);
+}
+
+std::wstring ImporterView::GetDialogButtonLabel(
+ DialogButton button) const {
+ if (button == DIALOGBUTTON_OK) {
+ return l10n_util::GetString(IDS_IMPORT_COMMIT);
+ } else {
+ return std::wstring();
+ }
+}
+
+bool ImporterView::IsModal() const {
+ return true;
+}
+
+std::wstring ImporterView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_IMPORT_SETTINGS_TITLE);
+}
+
+bool ImporterView::Accept() {
+ if (!IsDialogButtonEnabled(DIALOGBUTTON_OK)) {
+ return false;
+ }
+
+ uint16 items = NONE;
+ if (history_checkbox_->IsEnabled() && history_checkbox_->IsSelected())
+ items |= HISTORY;
+ if (favorites_checkbox_->IsEnabled() && favorites_checkbox_->IsSelected())
+ items |= FAVORITES;
+ if (passwords_checkbox_->IsEnabled() && passwords_checkbox_->IsSelected())
+ items |= PASSWORDS;
+ if (search_engines_checkbox_->IsEnabled() &&
+ search_engines_checkbox_->IsSelected())
+ items |= SEARCH_ENGINES;
+
+ Browser* browser = BrowserList::GetLastActive();
+ int selected_index = profile_combobox_->GetSelectedItem();
+ StartImportingWithUI(browser->GetTopLevelHWND(), items, importer_host_.get(),
+ importer_host_->GetSourceProfileInfoAt(selected_index),
+ profile_, this, false);
+ // We return false here to prevent the window from being closed. We will be
+ // notified back by our implementation of ImportObserver when the import is
+ // complete so that we can close ourselves.
+ return false;
+}
+
+int ImporterView::GetItemCount(ChromeViews::ComboBox* source) {
+ DCHECK(source == profile_combobox_);
+ DCHECK(importer_host_.get());
+ return importer_host_->GetAvailableProfileCount();
+}
+
+std::wstring ImporterView::GetItemAt(ChromeViews::ComboBox* source,
+ int index) {
+ DCHECK(source == profile_combobox_);
+ DCHECK(importer_host_.get());
+ return importer_host_->GetSourceProfileNameAt(index);
+}
+
+void ImporterView::ImportCanceled() {
+ ImportComplete();
+}
+
+void ImporterView::ImportComplete() {
+ // Now close this window since the import completed or was canceled.
+ dialog_->Close();
+}
+
+ChromeViews::CheckBox* ImporterView::InitCheckbox(
+ const std::wstring& text, bool checked) {
+ ChromeViews::CheckBox* checkbox = new ChromeViews::CheckBox(text);
+ checkbox->SetIsSelected(checked);
+ return checkbox;
+}
diff --git a/chrome/browser/views/importer_view.h b/chrome/browser/views/importer_view.h
new file mode 100644
index 0000000..fe9ab11
--- /dev/null
+++ b/chrome/browser/views/importer_view.h
@@ -0,0 +1,105 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_IMPORTER_VIEW_H__
+#define CHROME_BROWSER_VIEWS_IMPORTER_VIEW_H__
+
+#include "chrome/browser/importer.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+
+class CheckBox;
+class Label;
+class Window;
+
+}
+
+class Profile;
+
+// ImporterView draws the dialog that allows the user to select what to
+// import from other browsers.
+// Note: The UI team hasn't defined yet how the import UI will look like.
+// So now use dialog as a placeholder.
+class ImporterView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::ComboBox::Model,
+ public ImportObserver {
+ public:
+ explicit ImporterView(Profile* profile);
+ virtual ~ImporterView();
+
+ void set_dialog(ChromeViews::Window* dialog) { dialog_ = dialog; }
+
+ // Overridden from ChromeViews::View.
+ virtual void GetPreferredSize(CSize *out);
+ virtual void Layout();
+
+ // Overridden from ChromeViews::DialogDelegate:
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Accept();
+
+ // Overridden from ChromeViews::ComboBox::Model.
+ virtual int GetItemCount(ChromeViews::ComboBox* source);
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index);
+
+ // Overridden from ImportObserver:
+ virtual void ImportCanceled();
+ virtual void ImportComplete();
+
+ private:
+ // Initializes the controls on the dialog.
+ void SetupControl();
+
+ // Creates and initializes a new check-box.
+ ChromeViews::CheckBox* InitCheckbox(const std::wstring& text, bool checked);
+
+ ChromeViews::Label* import_from_label_;
+ ChromeViews::ComboBox* profile_combobox_;
+ ChromeViews::Label* import_items_label_;
+ ChromeViews::CheckBox* history_checkbox_;
+ ChromeViews::CheckBox* favorites_checkbox_;
+ ChromeViews::CheckBox* passwords_checkbox_;
+ ChromeViews::CheckBox* search_engines_checkbox_;
+
+ ChromeViews::Window* dialog_;
+
+ scoped_refptr<ImporterHost> importer_host_;
+
+ Profile* profile_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ImporterView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_IMPORTER_VIEW_H__
diff --git a/chrome/browser/views/importing_progress_view.cc b/chrome/browser/views/importing_progress_view.cc
new file mode 100644
index 0000000..aa99455
--- /dev/null
+++ b/chrome/browser/views/importing_progress_view.cc
@@ -0,0 +1,306 @@
+// 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/views/importing_progress_view.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/throbber.h"
+#include "chrome/views/window.h"
+#include "generated_resources.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// ImportingProgressView, public:
+
+ImportingProgressView::ImportingProgressView(const std::wstring& source_name,
+ int16 items,
+ ImporterHost* coordinator,
+ ImportObserver* observer,
+ HWND parent_window)
+ : state_bookmarks_(new ChromeViews::CheckmarkThrobber),
+ state_searches_(new ChromeViews::CheckmarkThrobber),
+ state_passwords_(new ChromeViews::CheckmarkThrobber),
+ state_history_(new ChromeViews::CheckmarkThrobber),
+ state_cookies_(new ChromeViews::CheckmarkThrobber),
+ label_info_(new ChromeViews::Label(l10n_util::GetStringF(
+ IDS_IMPORT_PROGRESS_INFO, source_name))),
+ label_bookmarks_(new ChromeViews::Label(
+ l10n_util::GetString(IDS_IMPORT_PROGRESS_STATUS_BOOKMARKS))),
+ label_searches_(new ChromeViews::Label(
+ l10n_util::GetString(IDS_IMPORT_PROGRESS_STATUS_SEARCH))),
+ label_passwords_(new ChromeViews::Label(
+ l10n_util::GetString(IDS_IMPORT_PROGRESS_STATUS_PASSWORDS))),
+ label_history_(new ChromeViews::Label(
+ l10n_util::GetString(IDS_IMPORT_PROGRESS_STATUS_HISTORY))),
+ label_cookies_(new ChromeViews::Label(
+ l10n_util::GetString(IDS_IMPORT_PROGRESS_STATUS_COOKIES))),
+ window_(NULL),
+ parent_window_(parent_window),
+ coordinator_(coordinator),
+ import_observer_(observer),
+ items_(items),
+ importing_(false) {
+ coordinator_->SetObserver(this);
+ label_info_->SetMultiLine(true);
+ label_info_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label_bookmarks_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label_searches_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label_passwords_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label_history_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label_cookies_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+
+ // These are scoped pointers, so we don't need the parent to delete them.
+ state_bookmarks_->SetParentOwned(false);
+ state_searches_->SetParentOwned(false);
+ state_passwords_->SetParentOwned(false);
+ state_history_->SetParentOwned(false);
+ state_cookies_->SetParentOwned(false);
+ label_bookmarks_->SetParentOwned(false);
+ label_searches_->SetParentOwned(false);
+ label_passwords_->SetParentOwned(false);
+ label_history_->SetParentOwned(false);
+ label_cookies_->SetParentOwned(false);
+}
+
+ImportingProgressView::~ImportingProgressView() {
+ RemoveChildView(state_bookmarks_.get());
+ RemoveChildView(state_searches_.get());
+ RemoveChildView(state_passwords_.get());
+ RemoveChildView(state_history_.get());
+ RemoveChildView(state_cookies_.get());
+ RemoveChildView(label_bookmarks_.get());
+ RemoveChildView(label_searches_.get());
+ RemoveChildView(label_passwords_.get());
+ RemoveChildView(label_history_.get());
+ RemoveChildView(label_cookies_.get());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImportingProgressView, ImporterObserver implementation:
+
+void ImportingProgressView::ImportItemStarted(ImportItem item) {
+ DCHECK(items_ & item);
+ switch (item) {
+ case FAVORITES:
+ state_bookmarks_->Start();
+ break;
+ case SEARCH_ENGINES:
+ state_searches_->Start();
+ break;
+ case PASSWORDS:
+ state_passwords_->Start();
+ break;
+ case HISTORY:
+ state_history_->Start();
+ break;
+ case COOKIES:
+ state_cookies_->Start();
+ break;
+ }
+}
+
+void ImportingProgressView::ImportItemEnded(ImportItem item) {
+ DCHECK(items_ & item);
+ switch (item) {
+ case FAVORITES:
+ state_bookmarks_->Stop();
+ state_bookmarks_->SetChecked(true);
+ break;
+ case SEARCH_ENGINES:
+ state_searches_->Stop();
+ state_searches_->SetChecked(true);
+ break;
+ case PASSWORDS:
+ state_passwords_->Stop();
+ state_passwords_->SetChecked(true);
+ break;
+ case HISTORY:
+ state_history_->Stop();
+ state_history_->SetChecked(true);
+ break;
+ case COOKIES:
+ state_cookies_->Stop();
+ state_cookies_->SetChecked(true);
+ break;
+ }
+}
+
+void ImportingProgressView::ImportStarted() {
+ importing_ = true;
+}
+
+void ImportingProgressView::ImportEnded() {
+ // This can happen because:
+ // - the import completed successfully.
+ // - the import was canceled by the user.
+ // - the user chose to skip the import because they didn't want to shut down
+ // Firefox.
+ // In every case, we need to close the UI now.
+ importing_ = false;
+ coordinator_->SetObserver(NULL);
+ window_->Close();
+ if (import_observer_)
+ import_observer_->ImportComplete();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImportingProgressView, ChromeViews::View overrides:
+
+void ImportingProgressView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_IMPORTPROGRESS_DIALOG_WIDTH_CHARS,
+ IDS_IMPORTPROGRESS_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void ImportingProgressView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (is_add && child == this)
+ InitControlLayout();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImportingProgressView, ChromeViews::DialogDelegate implementation:
+
+int ImportingProgressView::GetDialogButtons() const {
+ return DIALOGBUTTON_CANCEL;
+}
+
+std::wstring ImportingProgressView::GetDialogButtonLabel(
+ DialogButton button) const {
+ DCHECK(button == DIALOGBUTTON_CANCEL);
+ return l10n_util::GetString(IDS_IMPORT_PROGRESS_STATUS_CANCEL);
+}
+
+bool ImportingProgressView::IsModal() const {
+ return parent_window_ != NULL;
+}
+
+std::wstring ImportingProgressView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_IMPORT_PROGRESS_TITLE);
+}
+
+bool ImportingProgressView::Cancel() {
+ if (!importing_)
+ return true;
+
+ coordinator_->Cancel();
+ // Return false because the window needs to live long enough to receive
+ // ImportEnded, which will close the window.
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImportingProgressView, private:
+
+void ImportingProgressView::InitControlLayout() {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ CSize ps;
+ state_history_->GetPreferredSize(&ps);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ const int double_column_view_set_id = 1;
+ column_set = layout->AddColumnSet(double_column_view_set_id);
+ column_set->AddPaddingColumn(0, kUnrelatedControlLargeHorizontalSpacing);
+ column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
+ GridLayout::FIXED, ps.cx, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kUnrelatedControlLargeHorizontalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(label_info_);
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+
+ if (items_ & FAVORITES) {
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(state_bookmarks_.get());
+ layout->AddView(label_bookmarks_.get());
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ }
+ if (items_ & SEARCH_ENGINES) {
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(state_searches_.get());
+ layout->AddView(label_searches_.get());
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ }
+ if (items_ & PASSWORDS) {
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(state_passwords_.get());
+ layout->AddView(label_passwords_.get());
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ }
+ if (items_ & HISTORY) {
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(state_history_.get());
+ layout->AddView(label_history_.get());
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ }
+ if (items_ & COOKIES) {
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(state_cookies_.get());
+ layout->AddView(label_cookies_.get());
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// StartImportingWithUI
+
+void StartImportingWithUI(HWND parent_window,
+ int16 items,
+ ImporterHost* coordinator,
+ const ProfileInfo& source_profile,
+ Profile* target_profile,
+ ImportObserver* observer,
+ bool first_run) {
+ DCHECK(items != 0);
+ ImportingProgressView* v = new ImportingProgressView(
+ source_profile.description, items, coordinator, observer, parent_window);
+ ChromeViews::Window* window = ChromeViews::Window::CreateChromeWindow(
+ parent_window, gfx::Rect(), v, v);
+ v->set_window(window);
+ window->Show();
+ coordinator->StartImportSettings(source_profile, items,
+ new ProfileWriter(target_profile),
+ first_run);
+}
diff --git a/chrome/browser/views/importing_progress_view.h b/chrome/browser/views/importing_progress_view.h
new file mode 100644
index 0000000..c3a560d
--- /dev/null
+++ b/chrome/browser/views/importing_progress_view.h
@@ -0,0 +1,114 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_IMPORTING_PROGRESS_VIEW_H_
+#define CHROME_BROWSER_VIEWS_IMPORTING_PROGRESS_VIEW_H_
+
+#include "chrome/browser/importer.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/view.h"
+#include "chrome/views/window.h"
+
+namespace ChromeViews {
+class CheckmarkThrobber;
+class Label;
+}
+
+class ImportingProgressView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ImporterHost::Observer {
+ public:
+ ImportingProgressView(const std::wstring& source_name,
+ int16 items,
+ ImporterHost* coordinator,
+ ImportObserver* observer,
+ HWND parent_window);
+ virtual ~ImportingProgressView();
+
+ void set_window(ChromeViews::Window* window) { window_ = window; }
+
+ protected:
+ // Overridden from ImporterHost::Observer:
+ virtual void ImportItemStarted(ImportItem item);
+ virtual void ImportItemEnded(ImportItem item);
+ virtual void ImportStarted();
+ virtual void ImportEnded();
+
+ // Overridden from ChromeViews::DialogDelegate:
+ virtual int GetDialogButtons() const;
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool Cancel();
+
+ // Overridden from ChromeViews::View:
+ virtual void GetPreferredSize(CSize *out);
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ // Set up the control layout within this dialog.
+ void InitControlLayout();
+
+ // Various dialog controls.
+ scoped_ptr<ChromeViews::CheckmarkThrobber> state_bookmarks_;
+ scoped_ptr<ChromeViews::CheckmarkThrobber> state_searches_;
+ scoped_ptr<ChromeViews::CheckmarkThrobber> state_passwords_;
+ scoped_ptr<ChromeViews::CheckmarkThrobber> state_history_;
+ scoped_ptr<ChromeViews::CheckmarkThrobber> state_cookies_;
+ ChromeViews::Label* label_info_;
+ scoped_ptr<ChromeViews::Label> label_bookmarks_;
+ scoped_ptr<ChromeViews::Label> label_searches_;
+ scoped_ptr<ChromeViews::Label> label_passwords_;
+ scoped_ptr<ChromeViews::Label> label_history_;
+ scoped_ptr<ChromeViews::Label> label_cookies_;
+
+ // The window that contains us.
+ ChromeViews::Window* window_;
+
+ // The native window that we are parented to. Can be NULL.
+ HWND parent_window_;
+
+ // The importer host coordinating the import.
+ scoped_refptr<ImporterHost> coordinator_;
+
+ // An object that wants to be notified when the import is complete.
+ ImportObserver* import_observer_;
+
+ // The ImportItems we are importing.
+ int16 items_;
+
+ // True if the import operation is in progress.
+ bool importing_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ImportingProgressView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_IMPORTING_PROGRESS_VIEW_H_
diff --git a/chrome/browser/views/info_bar_alternate_nav_url_view.cc b/chrome/browser/views/info_bar_alternate_nav_url_view.cc
new file mode 100644
index 0000000..74229ed
--- /dev/null
+++ b/chrome/browser/views/info_bar_alternate_nav_url_view.cc
@@ -0,0 +1,94 @@
+// 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/views/info_bar_alternate_nav_url_view.h"
+
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/browser/views/event_utils.h"
+#include "chrome/browser/views/info_bar_view.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/page_transition_types.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/label.h"
+
+#include "generated_resources.h"
+
+InfoBarAlternateNavURLView::InfoBarAlternateNavURLView(
+ const std::wstring& alternate_nav_url)
+ : alternate_nav_url_(alternate_nav_url) {
+ size_t offset;
+ const std::wstring label(l10n_util::GetStringF(
+ IDS_ALTERNATE_NAV_URL_VIEW_LABEL, std::wstring(), &offset));
+ DCHECK(offset != std::wstring::npos);
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ if (offset > 0) {
+ ChromeViews::Label* label_1 =
+ new ChromeViews::Label(label.substr(0, offset));
+ label_1->SetFont(rb.GetFont(ResourceBundle::MediumFont));
+ AddChildViewLeading(label_1, 0);
+ }
+
+ ChromeViews::Link* link = new ChromeViews::Link(alternate_nav_url_);
+ link->SetFont(rb.GetFont(ResourceBundle::MediumFont));
+ link->SetController(this);
+ AddChildViewLeading(link, 0);
+
+ if (offset < label.length()) {
+ ChromeViews::Label* label_2 = new ChromeViews::Label(label.substr(offset));
+ label_2->SetFont(rb.GetFont(ResourceBundle::MediumFont));
+ AddChildViewLeading(label_2, 0);
+ }
+
+ SetIcon(*rb.GetBitmapNamed(IDR_INFOBAR_ALT_NAV_URL));
+}
+
+void InfoBarAlternateNavURLView::LinkActivated(ChromeViews::Link* source,
+ int event_flags) {
+ // Navigating may or may not automatically close the infobar, depending on
+ // whether the desired disposition replaces the current tab. We always want
+ // the bar to close, so we close it ourselves before navigating (doing things
+ // in the other order would be problematic if navigation synchronously closed
+ // the bar, as on return from the call, |this| would not exist, and calling
+ // Close() would corrupt memory). This means we need to save off all members
+ // we need before calling Close(), which destroys us.
+ PageNavigator* const navigator =
+ static_cast<InfoBarView*>(GetParent())->web_contents();
+ const GURL gurl(alternate_nav_url_);
+
+ BeginClose();
+
+ navigator->OpenURL(gurl, event_utils::DispositionFromEventFlags(event_flags),
+ // Pretend the user typed this URL, so that navigating to
+ // it will be the default action when it's typed again in
+ // the future.
+ PageTransition::TYPED);
+}
diff --git a/chrome/browser/views/info_bar_alternate_nav_url_view.h b/chrome/browser/views/info_bar_alternate_nav_url_view.h
new file mode 100644
index 0000000..9dd718d
--- /dev/null
+++ b/chrome/browser/views/info_bar_alternate_nav_url_view.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_INFO_BAR_ALTERNATE_NAV_URL_VIEW_H__
+#define CHROME_BROWSER_VIEWS_INFO_BAR_ALTERNATE_NAV_URL_VIEW_H__
+
+#include "chrome/browser/views/info_bar_item_view.h"
+#include "chrome/views/link.h"
+
+class InfoBarAlternateNavURLView : public InfoBarItemView,
+ public ChromeViews::LinkController {
+ public:
+ explicit InfoBarAlternateNavURLView(const std::wstring& alternate_nav_url);
+ virtual ~InfoBarAlternateNavURLView() { }
+
+ // LinkController
+ virtual void LinkActivated(ChromeViews::Link* source, int event_flags);
+
+ private:
+ std::wstring alternate_nav_url_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(InfoBarAlternateNavURLView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_INFO_BAR_ALTERNATE_NAV_URL_VIEW_H__ \ No newline at end of file
diff --git a/chrome/browser/views/info_bar_confirm_view.cc b/chrome/browser/views/info_bar_confirm_view.cc
new file mode 100644
index 0000000..8af6a1b
--- /dev/null
+++ b/chrome/browser/views/info_bar_confirm_view.cc
@@ -0,0 +1,124 @@
+// 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/standard_layout.h"
+#include "chrome/browser/views/info_bar_confirm_view.h"
+#include "chrome/common/l10n_util.h"
+
+#include "generated_resources.h"
+
+InfoBarConfirmView::InfoBarConfirmView(const std::wstring& message)
+ : ok_button_(NULL),
+ cancel_button_(NULL),
+ InfoBarMessageView(message) {
+ Init();
+}
+
+InfoBarConfirmView::~InfoBarConfirmView() {}
+
+void InfoBarConfirmView::OKButtonPressed() {
+ // Delete and close this view by default.
+ BeginClose();
+}
+
+void InfoBarConfirmView::CancelButtonPressed() {
+ // Delete and close this view by default.
+ BeginClose();
+}
+
+void InfoBarConfirmView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ // If you close the bar from one of these functions, make sure to use
+ // BeginClose() - Close() could delete us and cause the rest of the
+ // function to go bananas.
+ if (sender == ok_button_)
+ OKButtonPressed();
+ else if (sender == cancel_button_)
+ CancelButtonPressed();
+
+ // Disable our buttons - we only want to allow users to press one, and
+ // leaving them enabled could allow further interaction during the close
+ // animation.
+ if (ok_button_)
+ ok_button_->SetEnabled(false);
+ if (cancel_button_)
+ cancel_button_->SetEnabled(false);
+}
+
+void InfoBarConfirmView::SetOKButtonLabel(const std::wstring& label) {
+ if (ok_button_) {
+ ok_button_->SetLabel(label);
+ ok_button_->SetAccessibleName(label);
+ Layout();
+ }
+}
+
+void InfoBarConfirmView::SetCancelButtonLabel(const std::wstring& label) {
+ if (cancel_button_) {
+ cancel_button_->SetLabel(label);
+ cancel_button_->SetAccessibleName(label);
+ Layout();
+ }
+}
+
+void InfoBarConfirmView::RemoveCancelButton() {
+ if (cancel_button_) {
+ RemoveChildView(cancel_button_);
+ delete cancel_button_;
+ cancel_button_ = NULL;
+ Layout();
+ }
+}
+
+void InfoBarConfirmView::RemoveOKButton() {
+ if (ok_button_) {
+ RemoveChildView(ok_button_);
+ delete ok_button_;
+ ok_button_ = NULL;
+ Layout();
+ }
+}
+
+bool InfoBarConfirmView::GetAccessibleRole(VARIANT* role) {
+ DCHECK(role);
+
+ role->vt = VT_I4;
+ role->lVal = ROLE_SYSTEM_GROUPING;
+ return true;
+}
+
+void InfoBarConfirmView::Init() {
+ ok_button_ = new ChromeViews::NativeButton(l10n_util::GetString(IDS_OK));
+ ok_button_->SetListener(this);
+
+ cancel_button_ =
+ new ChromeViews::NativeButton(l10n_util::GetString(IDS_CANCEL));
+ cancel_button_->SetListener(this);
+ AddChildViewTrailing(cancel_button_, kRelatedButtonHSpacing);
+ AddChildViewTrailing(ok_button_);
+} \ No newline at end of file
diff --git a/chrome/browser/views/info_bar_confirm_view.h b/chrome/browser/views/info_bar_confirm_view.h
new file mode 100644
index 0000000..de0a1a8
--- /dev/null
+++ b/chrome/browser/views/info_bar_confirm_view.h
@@ -0,0 +1,89 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_INFO_BAR_CONFIRM_VIEW_H__
+#define CHROME_BROWSER_VIEWS_INFO_BAR_CONFIRM_VIEW_H__
+
+#include "chrome/browser/views/info_bar_message_view.h"
+#include "chrome/views/native_button.h"
+
+// An info bar with a message, two buttons (labeled OK and Cancel by
+// default), and a close button. Can be inherited to override the behavior
+// of button presses.
+class InfoBarConfirmView : public InfoBarMessageView,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ explicit InfoBarConfirmView(const std::wstring& message);
+
+ virtual ~InfoBarConfirmView();
+
+ // Invoked when the OK button is pressed. Closes info bar by default.
+ virtual void OKButtonPressed();
+
+ // Invoked when the Cancel button is pressed. Closes info bar by default.
+ virtual void CancelButtonPressed();
+
+ // ButtonListener Method:
+ // Invokes OKButtonPressed or CancelButtonPressed() when their
+ // respective buttons are pressed.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // Sets the label on the OK button, if it exists.
+ void SetOKButtonLabel(const std::wstring& label);
+
+ // Sets the label on the Cancel button, if it exists.
+ void SetCancelButtonLabel(const std::wstring& label);
+
+ // Removes the cancel button from the info bar.
+ // Can't be re-added.
+ void RemoveCancelButton();
+
+ // Removes the OK button from the info bar.
+ // Can't be re-added.
+ void RemoveOKButton();
+
+ // Returns the MSAA role of the current view. The role is what assistive
+ // technologies (ATs) use to determine what behavior to expect from a given
+ // control.
+ bool GetAccessibleRole(VARIANT* role);
+
+ private:
+ // Creates the ok and cancel buttons. And then calls the InfoBarMessageViews
+ // init to set up the message and close buttons which will
+ // then call AddAllChildViews.
+ void Init();
+
+ ChromeViews::NativeButton* ok_button_;
+
+ ChromeViews::NativeButton* cancel_button_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(InfoBarConfirmView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_INFO_BAR_CONFIRM_VIEW_H__ \ No newline at end of file
diff --git a/chrome/browser/views/info_bar_item_view.cc b/chrome/browser/views/info_bar_item_view.cc
new file mode 100644
index 0000000..99740f7
--- /dev/null
+++ b/chrome/browser/views/info_bar_item_view.cc
@@ -0,0 +1,317 @@
+// 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/app/theme/theme_resources.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/info_bar_item_view.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/external_focus_tracker.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/root_view.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+class HorizontalSpacer : public ChromeViews::View {
+ public:
+ explicit HorizontalSpacer(int width) : width_(width) {}
+
+ void GetPreferredSize(CSize* out) {
+ out->cx = width_;
+ out->cy = 0;
+ }
+
+ private:
+ int width_;
+};
+
+const int kInfoBarVerticalSpacing = 3;
+const int kInfoBarLeftMargin = 3;
+const double kInfoBarHeight = 37.0;
+
+} // namespace
+
+InfoBarItemView::InfoBarItemView()
+ : insert_index_(0),
+ close_button_(NULL),
+ icon_(NULL) {
+ Init();
+}
+
+InfoBarItemView::~InfoBarItemView() {
+}
+
+// static
+int InfoBarItemView::CenterPosition(int size, int target_size) {
+ return (target_size - size) / 2;
+}
+
+void InfoBarItemView::GetPreferredSize(CSize* out) {
+ out->cx = GetParent()->GetWidth();
+ out->cy = static_cast<int>(kInfoBarHeight * animation_->GetCurrentValue());
+}
+
+// The following is an overall note on the underlying implementation. You don't
+// need this in order to use this view. Ignore unless you're editing
+// implementation:
+// Layout() lays out all of its child views, but it uses insert_index_ to
+// decide whether to lay out on the left or right. Whenever a view is added or
+// removed the insert_index_ is updated accordingly to make sure it is directly
+// between left aligned views and right aligned views. Whenever a view is added,
+// a spacer view provides padding to the right of the view if the view is
+// left aligned, or to the left of the view if the view is right aligned.
+// Removing assumes this spacer view exists.
+//
+// For example, below M stands for built in margins, I stands for the icon
+// which is optional and includes padding of its own. L stands for a left
+// aligned view, and R for a right aligned view. P is padding, which can be
+// zero. The insert index is currently 4, separating the right of left views.
+// The numbers represent what index the child views P, R, and L occupy.
+//
+// M I L P L P P R P R M
+// 0 1 2 3 ^4 5 6 7
+// Say we call AddChildViewTrailing(right_view, 10). We end up with:
+// M I L P L P P R P R P R M
+// 0 1 2 3 ^4 5 6 7 8 9
+// First the right view was added, then its padding was added, the insert index
+// did not need to change because it still separates the right and left views.
+// Note that the padding showed up at the lower index, or to the left of the
+// right aligned view.
+// Then we call AddChildViewLeading(left_view, 0). We end up with:
+// M I L P L P L P P R P R P R M
+// 0 1 2 3 4 5 ^6 7 8 9 10 11
+// First the left view was added, then the insert_index_ was incremented, then
+// the padding is added, even though it is zero (It has no effect on layout)
+// and insert_index_ is incremented again to keep it between the right and
+// left views. Note in this case, the padding appears to the right of the view
+// left aligned view. Removing works the same, but in reverse.
+void InfoBarItemView::Layout() {
+ const int width = GetWidth();
+ const int height = GetHeight();
+
+ int next_x = width - kButtonHEdgeMargin;
+ int height_diff = static_cast<int>(kInfoBarHeight) - height;
+ const int child_count = GetChildViewCount();
+ // Anything greater than or equal to insert_index_ is laid out on the right,
+ // with the greatest index (the first one added to the right) being laid out
+ // rightmost.
+ for (int i = child_count - 1; i >= insert_index_ ; i--) {
+ View* v = GetChildViewAt(i);
+ if (v->IsVisible()) {
+ CSize view_size;
+ v->GetPreferredSize(&view_size);
+ next_x = next_x - view_size.cx;
+ v->SetBounds(next_x,
+ CenterPosition(view_size.cy,
+ static_cast<int>(kInfoBarHeight)) - height_diff,
+ view_size.cx,
+ view_size.cy);
+ }
+ }
+ int left_most_x = next_x;
+
+ next_x = kInfoBarLeftMargin;
+
+ // Anything less than insert_index_ is laid out on the left, with the
+ // smallest index (the first one added to the left) being laid out leftmost.
+ for (int i = 0; i < insert_index_ ; i++) {
+ View* v = GetChildViewAt(i);
+ if (v->IsVisible()) {
+ CSize view_size;
+ v->GetPreferredSize(&view_size);
+ int remaining_space = std::max(0, left_most_x - next_x);
+ if (view_size.cx > remaining_space) {
+ view_size.cx = remaining_space;
+ }
+ v->SetBounds(next_x,
+ CenterPosition(view_size.cy,
+ static_cast<int>(kInfoBarHeight)) - height_diff,
+ view_size.cx,
+ view_size.cy);
+ next_x = next_x + view_size.cx;
+ }
+ }
+}
+
+void InfoBarItemView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ if (GetParent() != NULL)
+ Layout();
+}
+
+void InfoBarItemView::BeginClose() {
+ animation_->Hide();
+}
+
+void InfoBarItemView::Close() {
+ ChromeViews::View* parent = GetParent();
+ parent->RemoveChildView(this);
+ if (focus_tracker_.get() != NULL)
+ focus_tracker_->FocusLastFocusedExternalView();
+ delete this;
+}
+
+void InfoBarItemView::CloseButtonPressed() {
+ // Close this view by default.
+ BeginClose();
+}
+
+void InfoBarItemView::AddChildViewTrailing(ChromeViews::View* view,
+ int leading_padding) {
+ ChromeViews::View::AddChildView(insert_index_, view);
+ View* padding = new HorizontalSpacer(leading_padding);
+ ChromeViews::View::AddChildView(insert_index_, padding);
+}
+
+void InfoBarItemView::AddChildViewTrailing(ChromeViews::View* view) {
+ AddChildViewTrailing(view, kUnrelatedControlHorizontalSpacing);
+}
+
+void InfoBarItemView::AddChildViewLeading(ChromeViews::View* view,
+ int trailing_padding) {
+ ChromeViews::View::AddChildView(insert_index_, view);
+ insert_index_++;
+ View* padding = new HorizontalSpacer(trailing_padding);
+ ChromeViews::View::AddChildView(insert_index_, padding);
+ insert_index_++;
+}
+
+void InfoBarItemView::AddChildViewLeading(ChromeViews::View* view) {
+ AddChildViewLeading(view, kRelatedControlSmallHorizontalSpacing);
+}
+
+void InfoBarItemView::SetIcon(const SkBitmap& icon) {
+ if (icon_ == NULL) {
+ // Add the icon and its padding to the far left of the info bar, and adjust
+ // the insert index accordingly.
+ icon_ = new ChromeViews::ImageView();
+ View* padding = new HorizontalSpacer(kRelatedControlHorizontalSpacing);
+ ChromeViews::View::AddChildView(0, padding);
+ ChromeViews::View::AddChildView(0, icon_);
+ insert_index_ += 2;
+ }
+ icon_->SetImage(icon);
+ Layout();
+}
+
+void InfoBarItemView::ViewHierarchyChanged(bool is_add,
+ View *parent,
+ View *child) {
+ if (child == this) {
+ if (is_add) {
+ Layout();
+
+ View* root_view = GetRootView();
+ HWND root_hwnd = NULL;
+ if (root_view)
+ root_hwnd = root_view->GetViewContainer()->GetHWND();
+
+ if (root_hwnd) {
+ focus_tracker_.reset(new ChromeViews::ExternalFocusTracker(
+ this, ChromeViews::FocusManager::GetFocusManager(root_hwnd)));
+ }
+ } else {
+ // When we're removed from the hierarchy our focus manager is no longer
+ // valid.
+ if (focus_tracker_.get() != NULL)
+ focus_tracker_->SetFocusManager(NULL);
+ }
+ }
+}
+
+void InfoBarItemView::AddChildView(ChromeViews::View* view) {
+ AddChildViewTrailing(view, kUnrelatedControlHorizontalSpacing);
+}
+
+void InfoBarItemView::AddChildView(int index, ChromeViews::View* view) {
+ if (index < insert_index_)
+ AddChildViewLeading(view);
+ else
+ AddChildViewTrailing(view);
+}
+
+void InfoBarItemView::RemoveChildView(ChromeViews::View* view) {
+ int index = GetChildIndex(view);
+ if (index >= 0) {
+ if (index < insert_index_) {
+ // We're removing a leading view. So the view at index + 1 (immediately
+ // trailing) is the corresponding spacer view.
+ View* spacer_view = GetChildViewAt(index + 1);
+ ChromeViews::View::RemoveChildView(view);
+ ChromeViews::View::RemoveChildView(spacer_view);
+ delete spacer_view;
+ // Need to change the insert_index_ so it is still pointing at the
+ // "middle" index between left and right aligned views.
+ insert_index_ -= 2;
+ } else {
+ // We're removing a trailing view. So the view at index - 1 (immediately
+ // leading) is the corresponding spacer view.
+ View* spacer_view = GetChildViewAt(index - 1);
+ ChromeViews::View::RemoveChildView(view);
+ ChromeViews::View::RemoveChildView(spacer_view);
+ delete spacer_view;
+ }
+ }
+}
+
+void InfoBarItemView::ButtonPressed(ChromeViews::BaseButton* button) {
+ if (button == close_button_)
+ CloseButtonPressed();
+}
+
+void InfoBarItemView::AnimationProgressed(const Animation* animation) {
+ static_cast<InfoBarView*>(GetParent())->ChildAnimationProgressed();
+}
+
+void InfoBarItemView::AnimationEnded(const Animation* animation) {
+ static_cast<InfoBarView*>(GetParent())->ChildAnimationEnded();
+
+ if (!animation_->IsShowing())
+ Close();
+}
+
+void InfoBarItemView::Init() {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ close_button_ = new ChromeViews::Button();
+ close_button_->SetImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR));
+ close_button_->SetImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR_H));
+ close_button_->SetImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR_P));
+ close_button_->SetListener(this, 0);
+ close_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE));
+ AddChildViewTrailing(close_button_);
+
+ animation_.reset(new SlideAnimation(this));
+ animation_->SetTweenType(SlideAnimation::NONE);
+ animation_->Show();
+}
diff --git a/chrome/browser/views/info_bar_item_view.h b/chrome/browser/views/info_bar_item_view.h
new file mode 100644
index 0000000..e4fa4d2
--- /dev/null
+++ b/chrome/browser/views/info_bar_item_view.h
@@ -0,0 +1,163 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_INFO_BAR_ITEM_VIEW_H__
+#define CHROME_BROWSER_VIEWS_INFO_BAR_ITEM_VIEW_H__
+
+#include "chrome/browser/views/info_bar_view.h"
+#include "chrome/common/slide_animation.h"
+#include "chrome/views/button.h"
+
+namespace ChromeViews {
+ class ExternalFocusTracker;
+ class ImageView;
+}
+// Note: An InfoBarItemView must be added as a child of InfoBarView to be
+// displayed correctly.
+//
+// InfoBarItemView is basically a view container that lays out views in a
+// horizontal row, on either the left or the right, with specified padding. It
+// has a close button on the far right, which can't be removed, and closes the
+// info bar by default. An icon can be set to be displayed leading all other
+// views by calling SetIcon().
+//
+// A view can be added to either the left or the right of the info bar by
+// calling AddChildViewTrailing and AddChildViewLeading.
+//
+// The most recently added views to either side will always be located further
+// towards the center than views added less recently, with the first views added
+// to the left or right being located on the leftmost or rightmost sides of the
+// info bar, respectively. Each view has a default spacing from the next
+// view added to that side, but you can edit that by specifying a padding when
+// you add a view. For example, if you add a view to the left with a padding of
+// 6 specified, it will be placed in the leftmost position, and the next view
+// added to the left will be 6 pixels to the right of the previously added view.
+class InfoBarItemView : public ChromeViews::View,
+ public ChromeViews::BaseButton::ButtonListener,
+ public AnimationDelegate {
+ public:
+ InfoBarItemView();
+
+ virtual ~InfoBarItemView();
+
+ // The preferred height is equal to the maximum height of all views
+ // in the info bar. Preferred width is equal to the parents width.
+ virtual void GetPreferredSize(CSize* out);
+
+ // Lays out all child views of the info bar from trailing to leading.
+ virtual void Layout();
+
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+
+ // Starts the close animation, which will end in the bar closing itself.
+ void BeginClose();
+
+ // Removes this InfoBarItem from its parent view and then deletes it.
+ void Close();
+
+ // ButtonListener Method
+ // Calls CloseButtonPressed() when the close button is pressed
+ virtual void ButtonPressed(ChromeViews::BaseButton* button);
+
+ // Adds |view| to the info bar, directly leading the last trailing view
+ // added, according to that views specified padding. The next trailing view
+ // added will in turn be leading this view by |leading_padding| pixels.
+ // Specify 0 for |leading_padding| if the views should be flush.
+ void AddChildViewTrailing(ChromeViews::View* view, int leading_padding);
+
+ // Calls AddChildViewTrailing with a default amount of padding.
+ void AddChildViewTrailing(ChromeViews::View* view);
+
+ // Adds |view| to the info bar, directly trailing the last leading view
+ // added, according to that views specified padding. The next leading view
+ // added will in turn be trailing this view by |trailing_padding| pixels.
+ // Specify 0 for |trailing_padding| if the views should be flush.
+ void AddChildViewLeading(ChromeViews::View* view, int trailing_padding);
+
+ // Calls AddChildViewLeading with a default amount of padding.
+ void AddChildViewLeading(ChromeViews::View* view);
+
+ // Sets the icon to be displayed leading all other views in the info bar.
+ // The icon will be displayed at its images height and width by default.
+ void SetIcon(const SkBitmap& icon);
+
+ protected:
+ // Returns the desired position for a centered object of size |size| within
+ // a region of size |target_size|.
+ static int CenterPosition(int size, int target_size);
+
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+
+ // Overridden from the basic Views AddChildView. Calls
+ // AddChildViewTrailing(view)
+ virtual void AddChildView(ChromeViews::View* view);
+
+ // Overridden from basic View. Adds the view to the same side as the view
+ // at index. Does *not* insert at the specified index, or even neccesarily
+ // close to it.
+ virtual void AddChildView(int index, ChromeViews::View* view);
+
+ // Overridden from the basic Views AddChildView, removes the specified view
+ // as well as its padding.
+ virtual void RemoveChildView(ChromeViews::View* view);
+
+ // Invoked whenever the close button is pressed. Closes infobar by default.
+ virtual void CloseButtonPressed();
+
+ private:
+ // Creates cancel button.
+ void Init();
+
+ // SlideAnimationDelegate implementation.
+ virtual void AnimationProgressed(const Animation* animation);
+ virtual void AnimationEnded(const Animation* animation);
+
+ scoped_ptr<SlideAnimation> animation_;
+
+ // View index where all new views will be inserted. Any view at an index less
+ // than insert_index will be laid out will be leading views (left aligned in
+ // left to right languages), any view greater than or equal to insert_index_
+ // will be laid out trailing (right aligned in left to right languages).
+ int insert_index_;
+
+ // Dismisses the info bar by default.
+ ChromeViews::Button* close_button_;
+
+ // Optional icon to be displayed at the far left of the infobar.
+ ChromeViews::ImageView* icon_;
+
+ // Tracks and stores the last focused view which is not the InfoBarItemView or
+ // any of its children. Used to restore focus once the InfoBarItemView is
+ // closed.
+ scoped_ptr<ChromeViews::ExternalFocusTracker> focus_tracker_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(InfoBarItemView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_INFO_BAR_ITEM_VIEW_H__
diff --git a/chrome/browser/views/info_bar_message_view.cc b/chrome/browser/views/info_bar_message_view.cc
new file mode 100644
index 0000000..766796a
--- /dev/null
+++ b/chrome/browser/views/info_bar_message_view.cc
@@ -0,0 +1,67 @@
+// 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/views/info_bar_message_view.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/label.h"
+
+InfoBarMessageView::InfoBarMessageView(const std::wstring& message)
+ : message_string_(message),
+ message_label_(NULL) {
+ Init();
+}
+
+InfoBarMessageView::InfoBarMessageView(ChromeViews::Label* message)
+ : message_string_(),
+ message_label_(message) {
+ Init();
+}
+
+InfoBarMessageView::~InfoBarMessageView() {}
+
+void InfoBarMessageView::SetMessageText(const std::wstring& message) {
+ message_label_->SetText(message);
+ Layout();
+}
+
+std::wstring InfoBarMessageView::GetMessageText() {
+ return message_label_->GetText();
+}
+
+void InfoBarMessageView::Init() {
+ if (message_label_ == NULL) {
+ message_label_ = new ChromeViews::Label(message_string_);
+ message_label_->SetFont(
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont));
+ } else {
+ message_string_ = message_label_->GetText();
+ }
+
+ AddChildViewLeading(message_label_);
+} \ No newline at end of file
diff --git a/chrome/browser/views/info_bar_message_view.h b/chrome/browser/views/info_bar_message_view.h
new file mode 100644
index 0000000..bbd3c51
--- /dev/null
+++ b/chrome/browser/views/info_bar_message_view.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_INFO_BAR_MESSAGE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_INFO_BAR_MESSAGE_VIEW_H__
+
+#include "chrome/browser/views/info_bar_item_view.h"
+#include "chrome/views/label.h"
+
+// A generic message for the info bar. Displays a label and a close button.
+// Can be inherited to override the default behavior of the close button, which
+// closes and deletes the info bar by default.
+class InfoBarMessageView : public InfoBarItemView {
+
+ public:
+ explicit InfoBarMessageView(const std::wstring& message);
+
+ explicit InfoBarMessageView(ChromeViews::Label* message);
+
+ virtual ~InfoBarMessageView();
+
+ void SetMessageText(const std::wstring& message);
+
+ std::wstring GetMessageText();
+
+ private:
+ // Creates message label.
+ void Init();
+
+ std::wstring message_string_;
+
+ ChromeViews::Label* message_label_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(InfoBarMessageView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_INFO_BAR_MESSAGE_VIEW_H__ \ No newline at end of file
diff --git a/chrome/browser/views/info_bar_view.cc b/chrome/browser/views/info_bar_view.cc
new file mode 100644
index 0000000..399a120
--- /dev/null
+++ b/chrome/browser/views/info_bar_view.cc
@@ -0,0 +1,245 @@
+// 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/views/info_bar_view.h"
+
+#include "base/logging.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/tab_contents_delegate.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/background.h"
+
+#include "generated_resources.h"
+
+// Color for the separator.
+static const SkColor kSeparatorColor = SkColorSetRGB(165, 165, 165);
+
+// Default background color for the info bar.
+static const SkColor kBackgroundColorTop = SkColorSetRGB(255, 242, 183);
+static const SkColor kBackgroundColorBottom = SkColorSetRGB(250, 230, 145);
+
+static const SkColor kBorderColorTop = SkColorSetRGB(240, 230, 170);
+static const SkColor kBorderColorBottom = SkColorSetRGB(236, 216, 133);
+
+// Height of the separator.
+static const int kSeparatorHeight = 1;
+
+InfoBarView::InfoBarView(WebContents* web_contents)
+ : web_contents_(web_contents) {
+ Init();
+}
+
+InfoBarView::~InfoBarView() {}
+
+void InfoBarView::AppendInfoBarItem(ChromeViews::View* view, bool auto_expire) {
+ // AddChildView adds an entry to expire_map_ for view.
+ AddChildView(view);
+ if (auto_expire)
+ expire_map_[view] = GetActiveID();
+ else
+ expire_map_.erase(expire_map_.find(view));
+}
+
+// Preferred size is equal to the max of the childrens horizontal sizes
+// and the sum of their vertical sizes.
+void InfoBarView::GetPreferredSize(CSize *out) {
+ out->cx = 0;
+ out->cy = 0;
+
+ // We count backwards so the most recently added view is on the top.
+ for (int i = GetChildViewCount() - 1; i >= 0; i--) {
+ View* v = GetChildViewAt(i);
+ if (v->IsVisible()) {
+ CSize view_size;
+ v->GetPreferredSize(&view_size);
+ out->cx = std::max(static_cast<int>(out->cx), v->GetWidth());
+ out->cy += static_cast<int>(view_size.cy) + kSeparatorHeight;
+ }
+ }
+}
+
+void InfoBarView::Layout() {
+ int width = GetWidth();
+ int height = GetHeight();
+
+ int x = 0;
+ int y = height;
+
+ // We lay the bars out from bottom to top.
+ for (int i = 0; i < GetChildViewCount(); ++i) {
+ View* v = GetChildViewAt(i);
+ if (!v->IsVisible())
+ continue;
+
+ CSize view_size;
+ v->GetPreferredSize(&view_size);
+ int view_width = std::max(static_cast<int>(view_size.cx), width);
+ y = y - view_size.cy - kSeparatorHeight;
+ v->SetBounds(x,
+ y,
+ view_width,
+ view_size.cy);
+ }
+}
+
+void InfoBarView::Paint(ChromeCanvas* canvas) {
+ PaintBackground(canvas);
+ PaintBorder(canvas);
+ PaintSeparators(canvas);
+}
+
+void InfoBarView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void InfoBarView::ChildAnimationProgressed() {
+ if (web_contents_)
+ web_contents_->ToolbarSizeChanged(true);
+}
+
+void InfoBarView::ChildAnimationEnded() {
+ if (web_contents_)
+ web_contents_->ToolbarSizeChanged(false);
+}
+
+void InfoBarView::DidNavigate(NavigationEntry* entry) {
+ // Determine the views to remove first.
+ std::vector<ChromeViews::View*> to_remove;
+ NavigationEntry* pending_entry =
+ web_contents_->controller()->GetPendingEntry();
+ const int active_id = pending_entry ? pending_entry->unique_id() :
+ entry->unique_id();
+ for (std::map<View*,int>::iterator i = expire_map_.begin();
+ i != expire_map_.end(); ++i) {
+ if ((i->second) != active_id)
+ to_remove.push_back(i->first);
+ }
+
+ if (to_remove.empty())
+ return;
+
+ // Remove the views.
+ for (std::vector<ChromeViews::View*>::iterator i = to_remove.begin();
+ i != to_remove.end(); ++i) {
+ // RemoveChildView takes care of removing from expire_map for us.
+ RemoveChildView(*i);
+
+ // We own the child and we're removing it, need to delete it.
+ delete *i;
+ }
+
+ if (GetChildViewCount() == 0) {
+ // All our views have been removed, no need to stay visible.
+ web_contents_->SetInfoBarVisible(false);
+ } else if (web_contents_) {
+ // This triggers a layout.
+ web_contents_->ToolbarSizeChanged(false);
+ }
+}
+
+void InfoBarView::ViewHierarchyChanged(bool is_add, View *parent,
+ View *child) {
+ if (parent == this && child->GetParent() == this) {
+ // ViewHierarchyChanged is actually called before a child is removed.
+ // So set child to not be visible so it isn't included in the layout.
+ if (!is_add) {
+ child->SetVisible(false);
+
+ // If there's an entry in the expire map, nuke it.
+ std::map<View*,int>::iterator i = expire_map_.find(child);
+ if (i != expire_map_.end())
+ expire_map_.erase(i);
+ } else {
+ expire_map_[child] = GetActiveID();
+ }
+
+ if (web_contents_->IsInfoBarVisible()) {
+ web_contents_->ToolbarSizeChanged(false);
+ } else {
+ web_contents_->SetInfoBarVisible(true);
+ }
+ }
+}
+
+void InfoBarView::Init() {
+ SetBackground(
+ ChromeViews::Background::CreateVerticalGradientBackground(
+ kBackgroundColorTop, kBackgroundColorBottom));
+}
+
+int InfoBarView::GetActiveID() const {
+ if (!web_contents_)
+ return 0;
+
+ // The WebContents is guaranteed to have a controller.
+ const NavigationEntry* const entry =
+ web_contents_->controller()->GetActiveEntry();
+ return entry ? entry->unique_id() : 0;
+}
+
+void InfoBarView::PaintBorder(ChromeCanvas* canvas) {
+ canvas->FillRectInt(kBorderColorTop, 0, 0, GetWidth(), 1);
+ canvas->FillRectInt(kBorderColorBottom,
+ 0, GetHeight() - kSeparatorHeight - 1,
+ GetWidth(), kSeparatorHeight);
+
+ if (GetChildViewCount() > 0)
+ canvas->FillRectInt(kSeparatorColor, 0, GetHeight() - kSeparatorHeight,
+ GetWidth(), kSeparatorHeight);
+}
+
+void InfoBarView::PaintSeparators(ChromeCanvas* canvas) {
+ ChromeViews::View* last_view = NULL;
+ for (int i = GetChildViewCount() - 1; i >= 0; i--) {
+ ChromeViews::View* view = GetChildViewAt(i);
+ if (last_view != NULL) {
+ if (view->IsVisible()) {
+ PaintSeparator(canvas, last_view, view);
+ } else {
+ // We aren't interested in views we can't see.
+ continue;
+ }
+ }
+ last_view = view;
+ }
+}
+
+void InfoBarView::PaintSeparator(ChromeCanvas* canvas,
+ ChromeViews::View* v1,
+ ChromeViews::View* v2) {
+ canvas->FillRectInt(kSeparatorColor,
+ 0,
+ v2->GetY() - kSeparatorHeight,
+ GetWidth(),
+ kSeparatorHeight);
+} \ No newline at end of file
diff --git a/chrome/browser/views/info_bar_view.h b/chrome/browser/views/info_bar_view.h
new file mode 100644
index 0000000..fd23dc0
--- /dev/null
+++ b/chrome/browser/views/info_bar_view.h
@@ -0,0 +1,111 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_INFO_BAR_VIEW_H__
+#define CHROME_BROWSER_VIEWS_INFO_BAR_VIEW_H__
+
+#include <map>
+
+#include "chrome/views/view.h"
+
+class NavigationEntry;
+class WebContents;
+
+// This view is used to store and display views in the info bar.
+//
+// It will paint all of its children vertically, with the most recently
+// added child displayed at the top of the info bar.
+class InfoBarView : public ChromeViews::View {
+ public:
+ explicit InfoBarView(WebContents* web_contents);
+
+ virtual ~InfoBarView();
+
+ // Adds view as a child. Views adding using AddChildView() are automatically
+ // removed after 1 navigation, which is also the behavior if you set
+ // |auto_expire| to true here. You mainly need this function if you want to
+ // add an infobar that should not expire.
+ void AppendInfoBarItem(ChromeViews::View* view, bool auto_expire);
+
+ virtual void GetPreferredSize(CSize *out);
+
+ virtual void Layout();
+
+ // API to allow infobar children to notify us of size changes.
+ virtual void ChildAnimationProgressed();
+ virtual void ChildAnimationEnded();
+
+ // Invokes the following methods to do painting:
+ // PaintBackground, PaintBorder and PaintSeparators.
+ virtual void Paint(ChromeCanvas* canvas);
+
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+
+ // Notification that the user has done a navigation. Removes child views that
+ // are set to be removed after a certain number of navigations and
+ // potentially hides the info bar. |entry| is the new entry that we are
+ // navigating to.
+ void DidNavigate(NavigationEntry* entry);
+
+ WebContents* web_contents() { return web_contents_; }
+
+ protected:
+ // Overridden to force the frame to re-layout the info bar whenever a view
+ // is added or removed.
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+
+ private:
+ void Init();
+
+ // Returns the unique ID of the active entry on the WebContents'
+ // NavigationController.
+ int GetActiveID() const;
+
+ // Paints the border.
+ void PaintBorder(ChromeCanvas* canvas);
+
+ // Paints the separators. This invokes PaintSeparator to paint a particular
+ // separator.
+ void PaintSeparators(ChromeCanvas* canvas);
+
+ // Paints the separator between views.
+ void PaintSeparator(ChromeCanvas* canvas,
+ ChromeViews::View* v1,
+ ChromeViews::View* v2);
+
+ WebContents* web_contents_;
+
+ // Map from view to number of navigations before it is removed. If a child
+ // doesn't have an entry in here, it is NOT removed on navigations.
+ std::map<View*,int> expire_map_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(InfoBarView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_INFO_BAR_VIEW_H__
diff --git a/chrome/browser/views/info_bubble.cc b/chrome/browser/views/info_bubble.cc
new file mode 100644
index 0000000..f239684
--- /dev/null
+++ b/chrome/browser/views/info_bubble.cc
@@ -0,0 +1,445 @@
+// 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/views/info_bubble.h"
+
+#include "base/win_util.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/chrome_frame.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/path.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/focus_manager.h"
+
+using ChromeViews::View;
+
+// All sizes are in pixels.
+
+// Size of the border, along each edge.
+static const int kBorderSize = 1;
+
+// Size of the arrow.
+static const int kArrowSize = 5;
+
+// Number of pixels to the start of the arrow from the edge of the window.
+static const int kArrowXOffset = 13;
+
+// Number of pixels between the tip of the arrow and the region we're
+// pointing to.
+static const int kArrowToContentPadding = -4;
+
+// Background color of the bubble.
+static const SkColor kBackgroundColor = SK_ColorWHITE;
+
+// Color of the border and arrow.
+static const SkColor kBorderColor1 = SkColorSetRGB(99, 99, 99);
+// Border shadow color.
+static const SkColor kBorderColor2 = SkColorSetRGB(160, 160, 160);
+
+// Intended dimensions of the bubble's corner images. If you update these,
+// make sure that the OnSize code works.
+static const int kInfoBubbleCornerWidth = 3;
+static const int kInfoBubbleCornerHeight = 3;
+
+// Bubble corner images.
+static const SkBitmap* kInfoBubbleCornerTopLeft = NULL;
+static const SkBitmap* kInfoBubbleCornerTopRight = NULL;
+static const SkBitmap* kInfoBubbleCornerBottomLeft = NULL;
+static const SkBitmap* kInfoBubbleCornerBottomRight = NULL;
+
+// Margins around the content.
+static const int kInfoBubbleViewTopMargin = 6;
+static const int kInfoBubbleViewBottomMargin = 9;
+static const int kInfoBubbleViewLeftMargin = 6;
+static const int kInfoBubbleViewRightMargin = 6;
+
+// The minimum alpha the bubble can be - because we're using a simple layered
+// window (in order to get window-level alpha at the same time as using native
+// controls), the window's drop shadow doesn't fade; this means if we went
+// to zero alpha, you'd see a drop shadow outline against nothing.
+static const int kMinimumAlpha = 72;
+
+// InfoBubble -----------------------------------------------------------------
+
+// static
+InfoBubble* InfoBubble::Show(HWND parent_hwnd,
+ const gfx::Rect& position_relative_to,
+ ChromeViews::View* content,
+ InfoBubbleDelegate* delegate) {
+ InfoBubble* window = new InfoBubble();
+ window->Init(parent_hwnd, position_relative_to, content);
+ ChromeFrame* frame = window->GetHostingFrame();
+ if (frame)
+ frame->InfoBubbleShowing();
+ window->ShowWindow(SW_SHOW);
+ window->delegate_ = delegate;
+ return window;
+}
+
+InfoBubble::InfoBubble() : content_view_(NULL) {
+}
+
+InfoBubble::~InfoBubble() {
+}
+
+void InfoBubble::Init(HWND parent_hwnd,
+ const gfx::Rect& position_relative_to,
+ ChromeViews::View* content) {
+ if (kInfoBubbleCornerTopLeft == NULL) {
+ kInfoBubbleCornerTopLeft = ResourceBundle::GetSharedInstance()
+ .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_TOP_LEFT);
+ kInfoBubbleCornerTopRight = ResourceBundle::GetSharedInstance()
+ .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_TOP_RIGHT);
+ kInfoBubbleCornerBottomLeft = ResourceBundle::GetSharedInstance()
+ .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_BOTTOM_LEFT);
+ kInfoBubbleCornerBottomRight = ResourceBundle::GetSharedInstance()
+ .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_BOTTOM_RIGHT);
+ }
+ set_window_style(WS_POPUP | WS_CLIPCHILDREN);
+ set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW);
+ content_view_ = CreateContentView(content);
+ gfx::Rect bounds = content_view_->
+ CalculateWindowBounds(parent_hwnd, position_relative_to);
+ set_initial_class_style(
+ (win_util::GetWinVersion() < win_util::WINVERSION_XP) ?
+ 0 : CS_DROPSHADOW);
+
+ HWNDViewContainer::Init(parent_hwnd, bounds, content_view_, true);
+ // The preferred size may differ when parented. Ask for the bounds again
+ // and if they differ reset the bounds.
+ gfx::Rect parented_bounds = content_view_->
+ CalculateWindowBounds(parent_hwnd, position_relative_to);
+
+ // Set our initial alpha to zero so we don't flicker at the user. This
+ // doesn't trigger UpdateLayeredWindow, which would explode our native
+ // controls.
+ SetLayeredAlpha(kMinimumAlpha);
+
+ if (bounds != parented_bounds) {
+ SetWindowPos(NULL, parented_bounds.x(), parented_bounds.y(),
+ parented_bounds.width(), parented_bounds.height(),
+ SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOZORDER);
+ // Invoke ChangeSize, otherwise layered window isn't updated correctly.
+ ChangeSize(0, CSize(parented_bounds.width(), parented_bounds.height()));
+ }
+
+ // Register the Escape accelerator for closing.
+ ChromeViews::FocusManager* focus_manager =
+ ChromeViews::FocusManager::GetFocusManager(GetHWND());
+ focus_manager->RegisterAccelerator(ChromeViews::Accelerator(VK_ESCAPE,
+ false, false,
+ false),
+ this);
+
+ fade_animation_.reset(new SlideAnimation(this));
+ fade_animation_->Show();
+}
+
+void InfoBubble::Close() {
+ // We don't fade out because it looks terrible.
+ ChromeFrame* frame = GetHostingFrame();
+ if (delegate_)
+ delegate_->InfoBubbleClosing(this);
+ if (frame)
+ frame->InfoBubbleClosing();
+ HWNDViewContainer::Close();
+}
+
+void InfoBubble::AnimationProgressed(const Animation* animation) {
+ int alpha = static_cast<int>(static_cast<double>
+ (fade_animation_->GetCurrentValue() * (255.0 - kMinimumAlpha) +
+ kMinimumAlpha));
+
+ SetLayeredWindowAttributes(GetHWND(),
+ RGB(0xFF, 0xFF, 0xFF),
+ alpha,
+ LWA_ALPHA);
+ content_view_->SchedulePaint();
+}
+
+bool InfoBubble::AcceleratorPressed(
+ const ChromeViews::Accelerator& accelerator) {
+ DCHECK(accelerator.GetKeyCode() == VK_ESCAPE);
+ if (!delegate_ || delegate_->CloseOnEscape()) {
+ Close();
+ return true;
+ }
+ return false;
+}
+
+void InfoBubble::OnSize(UINT param, const CSize& size) {
+ SetWindowRgn(content_view_->GetMask(size), TRUE);
+}
+
+void InfoBubble::OnActivate(UINT action, BOOL minimized, HWND window) {
+ // The popup should close when it is deactivated.
+ if (action == WA_INACTIVE) {
+ Close();
+ } else if (action == WA_ACTIVE) {
+ DCHECK(GetRootView()->GetChildViewCount() > 0);
+ GetRootView()->GetChildViewAt(0)->RequestFocus();
+ }
+}
+
+InfoBubble::ContentView* InfoBubble::CreateContentView(View* content) {
+ return new ContentView(content, this);
+}
+
+ChromeFrame* InfoBubble::GetHostingFrame() {
+ HWND owning_frame_hwnd = GetAncestor(GetHWND(), GA_ROOTOWNER);
+ ChromeFrame* frame = ChromeFrame::GetChromeFrameForWindow(owning_frame_hwnd);
+ if (!frame) {
+ // We should always have a frame, but there was a bug else where that
+ // made it possible for the frame to be NULL, so we have the check. If
+ // you hit this, file a bug.
+ NOTREACHED();
+ }
+ return frame;
+}
+
+// ContentView ----------------------------------------------------------------
+
+InfoBubble::ContentView::ContentView(ChromeViews::View* content,
+ InfoBubble* host)
+ : host_(host) {
+ if (UILayoutIsRightToLeft()) {
+ arrow_edge_ = TOP_RIGHT;
+ } else {
+ arrow_edge_ = TOP_LEFT;
+ }
+ AddChildView(content);
+}
+
+gfx::Rect InfoBubble::ContentView::CalculateWindowBounds(
+ HWND parent_hwnd,
+ const gfx::Rect& position_relative_to) {
+ gfx::Rect monitor_bounds = win_util::GetMonitorBoundsForRect(
+ position_relative_to);
+ // Calculate the bounds using TOP_LEFT (the default).
+ gfx::Rect window_bounds = CalculateWindowBounds(position_relative_to);
+ if (monitor_bounds.IsEmpty() || monitor_bounds.Contains(window_bounds))
+ return window_bounds;
+ // Didn't fit, adjust the edge to fit as much as we can.
+ if (window_bounds.bottom() > monitor_bounds.bottom())
+ SetArrowEdge(BOTTOM_LEFT);
+ if (window_bounds.right() > monitor_bounds.right()) {
+ if (IsTop())
+ SetArrowEdge(TOP_RIGHT);
+ else
+ SetArrowEdge(BOTTOM_RIGHT);
+ }
+ // And return new bounds.
+ return CalculateWindowBounds(position_relative_to);
+}
+
+void InfoBubble::ContentView::GetPreferredSize(CSize* pref) {
+ DCHECK(GetChildViewCount() == 1);
+ View* content = GetChildViewAt(0);
+ content->GetPreferredSize(pref);
+ pref->cx += kBorderSize + kBorderSize + kInfoBubbleViewLeftMargin +
+ kInfoBubbleViewRightMargin;
+ pref->cy += kBorderSize + kBorderSize + kArrowSize +
+ kInfoBubbleViewTopMargin + kInfoBubbleViewBottomMargin;
+}
+
+void InfoBubble::ContentView::Layout() {
+ DCHECK(GetChildViewCount() == 1);
+ View* content = GetChildViewAt(0);
+ int x = kBorderSize;
+ int y = kBorderSize;
+ int width = GetWidth() - kBorderSize - kBorderSize -
+ kInfoBubbleViewLeftMargin - kInfoBubbleViewRightMargin;
+ int height = GetHeight() - kBorderSize - kBorderSize - kArrowSize -
+ kInfoBubbleViewTopMargin - kInfoBubbleViewBottomMargin;
+ if (IsTop())
+ y += kArrowSize;
+ x += kInfoBubbleViewLeftMargin;
+ y += kInfoBubbleViewTopMargin;
+ content->SetBounds(x, y, width, height);
+}
+
+HRGN InfoBubble::ContentView::GetMask(const CSize &size) {
+ gfx::Path mask;
+
+ // Redefine the window visible region so that our dropshadows look right.
+ SkScalar width = SkIntToScalar(size.cx);
+ SkScalar height = SkIntToScalar(size.cy);
+ SkScalar arrow_size = SkIntToScalar(kArrowSize);
+ SkScalar arrow_x = SkIntToScalar(
+ (IsLeft() ? kArrowXOffset : width - kArrowXOffset) - 1);
+ SkScalar corner_size = SkIntToScalar(kInfoBubbleCornerHeight);
+
+ if (IsTop()) {
+ // Top left corner.
+ mask.moveTo(0, arrow_size + corner_size - 1);
+ mask.lineTo(corner_size - 1, arrow_size);
+
+ // Draw the arrow and the notch of the arrow.
+ mask.lineTo(arrow_x - arrow_size, arrow_size);
+ mask.lineTo(arrow_x, 0);
+ mask.lineTo(arrow_x + 3, 0);
+ mask.lineTo(arrow_x + arrow_size + 3, arrow_size);
+
+ // Top right corner.
+ mask.lineTo(width - corner_size + 1, arrow_size);
+ mask.lineTo(width, arrow_size + corner_size - 1);
+
+ // Bottom right corner.
+ mask.lineTo(width, height - corner_size);
+ mask.lineTo(width - corner_size, height);
+
+ // Bottom left corner.
+ mask.lineTo(corner_size, height);
+ mask.lineTo(0, height - corner_size);
+ } else {
+ // Top left corner.
+ mask.moveTo(0, corner_size - 1);
+ mask.lineTo(corner_size - 1, 0);
+
+ // Top right corner.
+ mask.lineTo(width - corner_size + 1, 0);
+ mask.lineTo(width, corner_size - 1);
+
+ // Bottom right corner.
+ mask.lineTo(width, height - corner_size - arrow_size);
+ mask.lineTo(width - corner_size, height - arrow_size);
+
+ // Draw the arrow and the notch of the arrow.
+ mask.lineTo(arrow_x + arrow_size + 2, height - arrow_size);
+ mask.lineTo(arrow_x + 2, height);
+ mask.lineTo(arrow_x + 1, height);
+ mask.lineTo(arrow_x - arrow_size + 1, height - arrow_size);
+
+ // Bottom left corner.
+ mask.lineTo(corner_size, height - arrow_size);
+ mask.lineTo(0, height - corner_size - arrow_size);
+ }
+
+ mask.close();
+
+ return mask.CreateHRGN();
+}
+
+void InfoBubble::ContentView::Paint(ChromeCanvas* canvas) {
+ int bubble_x = 0;
+ int bubble_y = 0;
+ int bubble_w = GetWidth();
+ int bubble_h = GetHeight() - kArrowSize;
+
+ int border_w = bubble_w - 2 * kInfoBubbleCornerWidth;
+ int border_h = bubble_h - 2 * kInfoBubbleCornerHeight;
+
+ if (IsTop())
+ bubble_y += kArrowSize;
+
+ // Fill in the background.
+ // Left side.
+ canvas->FillRectInt(kBackgroundColor,
+ bubble_x, bubble_y + kInfoBubbleCornerHeight,
+ kInfoBubbleCornerWidth, border_h);
+ // Center Column.
+ canvas->FillRectInt(kBackgroundColor,
+ kInfoBubbleCornerWidth, bubble_y,
+ border_w, bubble_h);
+ // Right Column.
+ canvas->FillRectInt(kBackgroundColor,
+ bubble_w - kInfoBubbleCornerWidth,
+ bubble_y + kInfoBubbleCornerHeight,
+ kInfoBubbleCornerWidth, border_h);
+
+ // Draw the border.
+ // Top border.
+ canvas->DrawRectInt(kBorderColor1,
+ kInfoBubbleCornerWidth, bubble_y,
+ border_w,
+ 0);
+ // Bottom border.
+ canvas->DrawRectInt(kBorderColor1,
+ kInfoBubbleCornerWidth, bubble_y + bubble_h - 1,
+ border_w, 0);
+ // Left border.
+ canvas->DrawRectInt(kBorderColor1,
+ bubble_x, bubble_y + kInfoBubbleCornerHeight,
+ 0, border_h);
+
+ // Right border.
+ canvas->DrawRectInt(kBorderColor1,
+ GetWidth() - 1, bubble_y + kInfoBubbleCornerHeight,
+ 0, border_h);
+
+ // Draw the corners.
+ canvas->DrawBitmapInt(*kInfoBubbleCornerTopLeft, 0, bubble_y);
+ canvas->DrawBitmapInt(*kInfoBubbleCornerTopRight,
+ bubble_w - kInfoBubbleCornerWidth, bubble_y);
+ canvas->DrawBitmapInt(*kInfoBubbleCornerBottomLeft, 0,
+ bubble_y + bubble_h - kInfoBubbleCornerHeight);
+ canvas->DrawBitmapInt(*kInfoBubbleCornerBottomRight,
+ bubble_w - kInfoBubbleCornerWidth,
+ bubble_y + bubble_h - kInfoBubbleCornerHeight);
+
+ // Draw the arrow and the notch of the arrow.
+ int arrow_x = IsLeft() ? kArrowXOffset : GetWidth() - kArrowXOffset;
+ int arrow_y = IsTop() ? bubble_y : bubble_y + bubble_h - 1;
+ const int arrow_delta = IsTop() ? -1 : 1;
+ for (int i = 0, y = arrow_y; i <= kArrowSize; ++i, y += arrow_delta) {
+ if (kArrowSize != i) {
+ // Draw the notch formed by the arrow.
+ canvas->FillRectInt(kBackgroundColor, arrow_x - (kArrowSize - i) + 1,
+ y, (kArrowSize - i) * 2 - 1, 1);
+ }
+ // Draw the sides of the arrow.
+ canvas->FillRectInt(kBorderColor1, arrow_x - (kArrowSize - i), y, 1, 1);
+ canvas->FillRectInt(kBorderColor1, arrow_x + (kArrowSize - i), y, 1, 1);
+ if (i != 0) {
+ canvas->FillRectInt(kBorderColor2, arrow_x - (kArrowSize - i) - 1, y, 1,
+ 1);
+ canvas->FillRectInt(kBorderColor2, arrow_x + (kArrowSize - i) + 1, y, 1,
+ 1);
+ }
+ }
+}
+
+gfx::Rect InfoBubble::ContentView::CalculateWindowBounds(
+ const gfx::Rect& position_relative_to) {
+ CSize pref;
+ GetPreferredSize(&pref);
+ int x = position_relative_to.x() + position_relative_to.width() / 2;
+ int y;
+ if (IsLeft())
+ x -= kArrowXOffset;
+ else
+ x = x + kArrowXOffset - pref.cx;
+ if (IsTop()) {
+ y = position_relative_to.bottom() + kArrowToContentPadding;
+ } else {
+ y = position_relative_to.y() - kArrowToContentPadding - pref.cy;
+ }
+ return gfx::Rect(x, y, pref.cx, pref.cy);
+}
diff --git a/chrome/browser/views/info_bubble.h b/chrome/browser/views/info_bubble.h
new file mode 100644
index 0000000..ee4a2b3
--- /dev/null
+++ b/chrome/browser/views/info_bubble.h
@@ -0,0 +1,193 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_INFO_BUBBLE_H__
+#define CHROME_BROWSER_VIEWS_INFO_BUBBLE_H__
+
+#include "chrome/common/slide_animation.h"
+#include "chrome/views/hwnd_view_container.h"
+
+// InfoBubble is used to display an arbitrary view above all other windows.
+// Think of InfoBubble as a tooltip that allows you to embed an arbitrary view
+// in the tooltip. Additionally the InfoBubble renders an arrow pointing at
+// the region the info bubble is providing the information about.
+//
+// To use an InfoBubble invoke Show and it'll take care of the rest. InfoBubble
+// (or rather ContentView) insets the content view for you, so that the
+// content typically shouldn't have any additional margins around the view.
+
+class ChromeFrame;
+class InfoBubble;
+
+class InfoBubbleDelegate {
+ public:
+ // Called when the InfoBubble is closing and is about to be deleted.
+ virtual void InfoBubbleClosing(InfoBubble* info_bubble) = 0;
+
+ // Whether the InfoBubble should be closed when the Esc key is pressed.
+ virtual bool CloseOnEscape() = 0;
+};
+
+class InfoBubble : public ChromeViews::HWNDViewContainer,
+ public ChromeViews::AcceleratorTarget,
+ public AnimationDelegate {
+ public:
+ // Shows the InfoBubble. The InfoBubble is parented to parent_hwnd, contains
+ // the View content and positioned relative to the screen position
+ // position_relative_to. Show takes ownership of content and deletes the
+ // create InfoBubble when another window is activated. You can explicitly
+ // close the bubble by invoking Close. A delegate may optionally be provided
+ // to be notified when the InfoBubble is closed and to prevent the InfoBubble
+ // from being closed when the Escape key is pressed (which is the default
+ // behavior if there is no delegate).
+ static InfoBubble* Show(HWND parent_hwnd,
+ const gfx::Rect& position_relative_to,
+ ChromeViews::View* content,
+ InfoBubbleDelegate* delegate);
+
+ InfoBubble();
+ virtual ~InfoBubble();
+
+ // Creates the InfoBubble.
+ void Init(HWND parent_hwnd,
+ const gfx::Rect& position_relative_to,
+ ChromeViews::View* content);
+
+ // Sets the delegate for that InfoBubble.
+ void SetDelegate(InfoBubbleDelegate* delegate) { delegate_ = delegate; }
+
+ // The InfoBubble is automatically closed when it loses activation status.
+ virtual void OnActivate(UINT action, BOOL minimized, HWND window);
+
+ // Return our rounded window shape.
+ virtual void OnSize(UINT param, const CSize& size);
+
+ // Overridden to notify the owning ChromeFrame the bubble is closing.
+ virtual void Close();
+
+ // AcceleratorTarget method:
+ virtual bool AcceleratorPressed(const ChromeViews::Accelerator& accelerator);
+
+ // AnimationDelegate Implementation
+ virtual void AnimationProgressed(const Animation* animation);
+
+ protected:
+
+ // InfoBubble::CreateContentView() creates one of these. ContentView houses
+ // the supplied content as its only child view, renders the arrow/border of
+ // the bubble and sizes the content.
+ class ContentView : public ChromeViews::View {
+ public:
+ // Possible edges the arrow is aligned along.
+ enum ArrowEdge {
+ TOP_LEFT = 0,
+ TOP_RIGHT = 1,
+ BOTTOM_LEFT = 2,
+ BOTTOM_RIGHT = 3
+ };
+
+ // Creates the ContentView. The supplied view is added as the only child of
+ // the ContentView.
+ ContentView(ChromeViews::View* content, InfoBubble* host);
+
+ virtual ~ContentView() {}
+
+ // Returns the bounds for the window to contain this view.
+ //
+ // This invokes the method of the same name that doesn't take an HWND, if
+ // the returned bounds don't fit on the monitor containing parent_hwnd,
+ // the arrow edge is adjusted.
+ virtual gfx::Rect CalculateWindowBounds(
+ HWND parent_hwnd,
+ const gfx::Rect& position_relative_to);
+
+ // Sets the edge the arrow is rendered at.
+ void SetArrowEdge(ArrowEdge arrow_edge) { arrow_edge_ = arrow_edge; }
+
+ // Returns the preferred size, which is the sum of the preferred size of
+ // the content and the border/arrow.
+ virtual void GetPreferredSize(CSize* pref);
+
+ // Positions the content relative to the border.
+ virtual void Layout();
+
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current) {
+ Layout();
+ }
+
+ // Return the mask for the content view.
+ HRGN GetMask(const CSize& size);
+
+ // Paints the background and arrow appropriately.
+ virtual void Paint(ChromeCanvas* canvas);
+
+ // Returns true if the arrow is positioned along the top edge of the
+ // view. If this returns false the arrow is positioned along the bottom
+ // edge.
+ bool IsTop() { return (arrow_edge_ & 2) == 0; }
+
+ // Returns true if the arrow is positioned along the left edge of the
+ // view. If this returns false the arrow is positioned along the right edge.
+ bool IsLeft() { return (arrow_edge_ & 1) == 0; }
+
+ private:
+
+ // Returns the bounds for the window containing us based on the current
+ // arrow edge.
+ gfx::Rect CalculateWindowBounds(const gfx::Rect& position_relative_to);
+
+ // Edge to draw the arrow at.
+ ArrowEdge arrow_edge_;
+
+ // The bubble we're in.
+ InfoBubble* host_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentView);
+ };
+
+ // Creates and return a new ContentView containing content.
+ virtual ContentView* CreateContentView(ChromeViews::View* content);
+
+ // Returns the ChromeFrame that owns this InfoBubble.
+ ChromeFrame* GetHostingFrame();
+
+ private:
+ // The delegate notified when the InfoBubble is closed.
+ InfoBubbleDelegate* delegate_;
+
+ // The content view contained by the infobubble.
+ ContentView* content_view_;
+
+ // The fade-in animation.
+ scoped_ptr<SlideAnimation> fade_animation_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(InfoBubble);
+};
+
+#endif // CHROME_BROWSER_VIEWS_INFO_BUBBLE_H__
diff --git a/chrome/browser/views/input_window.cc b/chrome/browser/views/input_window.cc
new file mode 100644
index 0000000..058e29d
--- /dev/null
+++ b/chrome/browser/views/input_window.cc
@@ -0,0 +1,194 @@
+// 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/views/input_window.h"
+
+#include "chrome/browser/standard_layout.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/window.h"
+#include "generated_resources.h"
+
+// Width to make the text field, in pixels.
+static const int kTextFieldWidth = 200;
+
+// ContentView ----------------------------------------------------------------
+
+// ContentView, as the name implies, is the content view for the InputWindow.
+// It registers accelerators that accept/cancel the input.
+
+class ContentView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::TextField::Controller {
+ public:
+ explicit ContentView(InputWindowDelegate* delegate)
+ : delegate_(delegate),
+ window_(NULL),
+ focus_grabber_factory_(this) {
+ DCHECK(delegate_);
+ }
+
+ void set_window(ChromeViews::Window* window) { window_ = window; }
+
+ // ChromeViews::DialogDelegate overrides:
+ virtual bool IsDialogButtonEnabled(DialogButton button) const;
+ virtual bool Accept();
+ virtual bool Cancel();
+ virtual void WindowClosing();
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool IsModal() const { return true; }
+
+ // ChromeViews::TextField::Controller overrides:
+ virtual void ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(ChromeViews::TextField*, UINT, TCHAR, UINT,
+ UINT) {}
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add, ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ // Set up dialog controls and layout.
+ void InitControlLayout();
+
+ // Sets focus to the first focusable element within the dialog.
+ void FocusFirstFocusableControl();
+
+ // The TextField that the user can type into.
+ ChromeViews::TextField* text_field_;
+
+ // The delegate that the ContentView uses to communicate changes to the
+ // caller.
+ InputWindowDelegate* delegate_;
+
+ // The Window that owns this view.
+ ChromeViews::Window* window_;
+
+ // Helps us set focus to the first TextField in the window.
+ ScopedRunnableMethodFactory<ContentView> focus_grabber_factory_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentView);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ContentView, ChromeViews::DialogDelegate implementation:
+
+bool ContentView::IsDialogButtonEnabled(DialogButton button) const {
+ if (button == DIALOGBUTTON_OK && !delegate_->IsValid(text_field_->GetText()))
+ return false;
+ return true;
+}
+
+bool ContentView::Accept() {
+ delegate_->InputAccepted(text_field_->GetText());
+ return true;
+}
+
+bool ContentView::Cancel() {
+ delegate_->InputCanceled();
+ return true;
+}
+
+void ContentView::WindowClosing() {
+ delegate_->WindowClosing();
+}
+
+std::wstring ContentView::GetWindowTitle() const {
+ return delegate_->GetWindowTitle();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ContentView, ChromeViews::TextField::Controller implementation:
+
+void ContentView::ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents) {
+ window_->UpdateDialogButtons();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ContentView, protected:
+
+void ContentView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (is_add && child == this)
+ InitControlLayout();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ContentView, private:
+
+void ContentView::InitControlLayout() {
+ text_field_ = new ChromeViews::TextField;
+ text_field_->SetText(delegate_->GetTextFieldContents());
+ text_field_->SetController(this);
+
+ using ChromeViews::ColumnSet;
+ using ChromeViews::GridLayout;
+
+ // TODO(sky): Vertical alignment should be baseline.
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ ColumnSet* c1 = layout->AddColumnSet(0);
+ c1->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ c1->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ c1->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, kTextFieldWidth, kTextFieldWidth);
+
+ layout->StartRow(0, 0);
+ ChromeViews::Label* label =
+ new ChromeViews::Label(delegate_->GetTextFieldLabel());
+ layout->AddView(label);
+ layout->AddView(text_field_);
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ focus_grabber_factory_.NewRunnableMethod(
+ &ContentView::FocusFirstFocusableControl));
+}
+
+void ContentView::FocusFirstFocusableControl() {
+ text_field_->SelectAll();
+ text_field_->RequestFocus();
+}
+
+ChromeViews::Window* CreateInputWindow(HWND parent_hwnd,
+ InputWindowDelegate* delegate) {
+ ContentView* cv = new ContentView(delegate);
+ ChromeViews::Window* window = ChromeViews::Window::CreateChromeWindow(
+ parent_hwnd, gfx::Rect(), cv, cv);
+ cv->set_window(window);
+ window->UpdateDialogButtons();
+ return window;
+}
diff --git a/chrome/browser/views/input_window.h b/chrome/browser/views/input_window.h
new file mode 100644
index 0000000..bc9c162
--- /dev/null
+++ b/chrome/browser/views/input_window.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_INPUT_WINDOW_H__
+#define CHROME_BROWSER_VIEWS_INPUT_WINDOW_H__
+
+#include "chrome/views/dialog_delegate.h"
+
+// InputWindowDelegate --------------------------------------------------------
+
+class InputWindowDelegate : public ChromeViews::DialogDelegate {
+ public:
+ // Returns the text displayed on the label preceding the text field.
+ virtual std::wstring GetTextFieldLabel() = 0;
+
+ // Returns the initial contents of the text field.
+ virtual std::wstring GetTextFieldContents() {
+ return std::wstring();
+ }
+
+ // Returns whether the text is valid. InputAccepted is only invoked if the
+ // text is valid.
+ virtual bool IsValid(const std::wstring& text) {
+ return true;
+ }
+
+ // Invoked when the user presses the ok button and the text is fvalid.
+ virtual void InputAccepted(const std::wstring& text) = 0;
+
+ // Invoked when the user cancels the dialog.
+ virtual void InputCanceled() {}
+};
+
+
+namespace ChromeViews {
+class Window;
+};
+
+ChromeViews::Window* CreateInputWindow(HWND parent_hwnd,
+ InputWindowDelegate* delegate);
+
+#endif // CHROME_BROWSER_VIEWS_INPUT_WINDOW_H__
diff --git a/chrome/browser/views/keyword_editor_view.cc b/chrome/browser/views/keyword_editor_view.cc
new file mode 100644
index 0000000..04acd38
--- /dev/null
+++ b/chrome/browser/views/keyword_editor_view.cc
@@ -0,0 +1,694 @@
+// 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/views/keyword_editor_view.h"
+
+#include <vector>
+
+#include "base/gfx/png_decoder.h"
+#include "base/string_util.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/browser/views/edit_keyword_controller.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/stl_util-inl.h"
+#include "chrome/views/background.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/window.h"
+#include "googleurl/src/gurl.h"
+#include "skia/include/SkBitmap.h"
+
+#include "generated_resources.h"
+
+using ChromeViews::GridLayout;
+using ChromeViews::NativeButton;
+using ChromeViews::TableColumn;
+
+// Group IDs used by TemplateURLTableModel.
+static const int kMainGroupID = 0;
+static const int kOtherGroupID = 1;
+
+// ModelEntry ----------------------------------------------------
+
+// ModelEntry wraps a TemplateURL as returned from the TemplateURL.
+// ModelEntry also tracks state information about the URL.
+
+// Icon used while loading, or if a specific favicon can't be found.
+static SkBitmap* default_icon = NULL;
+
+class ModelEntry {
+ public:
+ explicit ModelEntry(TemplateURLTableModel* model,
+ const TemplateURL& template_url)
+ : model_(model),
+ template_url_(template_url) {
+ load_state_ = NOT_LOADED;
+ if (!default_icon) {
+ default_icon = ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(IDR_DEFAULT_FAVICON);
+ }
+ }
+
+ const TemplateURL& template_url() {
+ return template_url_;
+ }
+
+ SkBitmap GetIcon() {
+ if (load_state_ == NOT_LOADED)
+ LoadFavIcon();
+ if (!fav_icon_.isNull())
+ return fav_icon_;
+ return *default_icon;
+ }
+
+ // Resets internal status so that the next time the icon is asked for its
+ // fetched again. This should be invoked if the url is modified.
+ void ResetIcon() {
+ load_state_ = NOT_LOADED;
+ fav_icon_ = SkBitmap();
+ }
+
+ private:
+ // State of the favicon.
+ enum LoadState {
+ NOT_LOADED,
+ LOADING,
+ LOADED
+ };
+
+ void LoadFavIcon() {
+ load_state_ = LOADED;
+ HistoryService* hs =
+ model_->template_url_model()->profile()->GetHistoryService(
+ Profile::EXPLICIT_ACCESS);
+ if (!hs)
+ return;
+ GURL fav_icon_url = template_url().GetFavIconURL();
+ if (!fav_icon_url.is_valid()) {
+ // The favicon url isn't always set. Guess at one here.
+ if (template_url_.url() && template_url_.url()->IsValid()) {
+ GURL url = GURL(template_url_.url()->url());
+ if (url.is_valid())
+ fav_icon_url = TemplateURL::GenerateFaviconURL(url);
+ }
+ if (!fav_icon_url.is_valid())
+ return;
+ }
+ load_state_ = LOADING;
+ hs->GetFavIcon(fav_icon_url,
+ &request_consumer_,
+ NewCallback(this, &ModelEntry::OnFavIconDataAvailable));
+ }
+
+ void OnFavIconDataAvailable(
+ HistoryService::Handle handle,
+ bool know_favicon,
+ scoped_refptr<RefCountedBytes> data,
+ bool expired,
+ GURL icon_url) {
+ load_state_ = LOADED;
+ if (know_favicon && data.get() &&
+ PNGDecoder::Decode(&data->data, &fav_icon_)) {
+ model_->FavIconAvailable(this);
+ }
+ }
+
+ const TemplateURL& template_url_;
+ SkBitmap fav_icon_;
+ LoadState load_state_;
+ TemplateURLTableModel* model_;
+ CancelableRequestConsumer request_consumer_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ModelEntry);
+};
+
+// TemplateURLTableModel -----------------------------------------
+
+TemplateURLTableModel::TemplateURLTableModel(
+ TemplateURLModel* template_url_model)
+ : observer_(NULL),
+ template_url_model_(template_url_model) {
+ DCHECK(template_url_model);
+ Reload();
+}
+
+TemplateURLTableModel::~TemplateURLTableModel() {
+ STLDeleteElements(&entries_);
+ entries_.clear();
+}
+
+void TemplateURLTableModel::Reload() {
+ STLDeleteElements(&entries_);
+ entries_.clear();
+
+ std::vector<const TemplateURL*> urls = template_url_model_->GetTemplateURLs();
+
+ // Keywords that can be made the default first.
+ for (std::vector<const TemplateURL*>::iterator i = urls.begin();
+ i != urls.end(); ++i) {
+ const TemplateURL& template_url = *(*i);
+ // NOTE: we don't use ShowInDefaultList here to avoid items bouncing around
+ // the lists while editing.
+ if (template_url.show_in_default_list())
+ entries_.push_back(new ModelEntry(this, template_url));
+ }
+
+ last_search_engine_index_ = static_cast<int>(entries_.size());
+
+ // Then the rest.
+ for (std::vector<const TemplateURL*>::iterator i = urls.begin();
+ i != urls.end(); ++i) {
+ const TemplateURL* template_url = *i;
+ // NOTE: we don't use ShowInDefaultList here to avoid things bouncing
+ // the lists while editing.
+ if (!template_url->show_in_default_list())
+ entries_.push_back(new ModelEntry(this, *template_url));
+ }
+
+ if (observer_)
+ observer_->OnModelChanged();
+}
+
+int TemplateURLTableModel::RowCount() {
+ return static_cast<int>(entries_.size());
+}
+
+std::wstring TemplateURLTableModel::GetText(int row, int col_id) {
+ DCHECK(row >= 0 && row < RowCount());
+ const TemplateURL& url = entries_[row]->template_url();
+
+ switch (col_id) {
+ case IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN:
+ return (template_url_model_->GetDefaultSearchProvider() == &url) ?
+ l10n_util::GetStringF(IDS_SEARCH_ENGINES_EDITOR_DEFAULT_ENGINE,
+ url.short_name()) :
+ url.short_name();
+
+ case IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN:
+ return url.keyword();
+ break;
+
+ default:
+ NOTREACHED();
+ return std::wstring();
+ }
+}
+
+SkBitmap TemplateURLTableModel::GetIcon(int row) {
+ DCHECK(row >= 0 && row < RowCount());
+ return entries_[row]->GetIcon();
+}
+
+void TemplateURLTableModel::SetObserver(
+ ChromeViews::TableModelObserver* observer) {
+ observer_ = observer;
+}
+
+bool TemplateURLTableModel::HasGroups() {
+ return true;
+}
+
+TemplateURLTableModel::Groups TemplateURLTableModel::GetGroups() {
+ Groups groups;
+
+ Group search_engine_group;
+ search_engine_group.title =
+ l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_MAIN_SEPARATOR);
+ search_engine_group.id = kMainGroupID;
+ groups.push_back(search_engine_group);
+
+ Group other_group;
+ other_group.title =
+ l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_OTHER_SEPARATOR);
+ other_group.id = kOtherGroupID;
+ groups.push_back(other_group);
+
+ return groups;
+}
+
+int TemplateURLTableModel::GetGroupID(int row) {
+ DCHECK(row >= 0 && row < RowCount());
+ return row < last_search_engine_index_ ? kMainGroupID : kOtherGroupID;
+}
+
+void TemplateURLTableModel::Remove(int index) {
+ scoped_ptr<ModelEntry> entry(entries_[static_cast<int>(index)]);
+ entries_.erase(entries_.begin() + static_cast<int>(index));
+ if (index < last_search_engine_index_)
+ last_search_engine_index_--;
+ if (observer_)
+ observer_->OnItemsRemoved(index, 1);
+}
+
+void TemplateURLTableModel::Add(int index, const TemplateURL* template_url) {
+ DCHECK(index >= 0 && index <= RowCount());
+ ModelEntry* entry = new ModelEntry(this, *template_url);
+ entries_.insert(entries_.begin() + index, entry);
+ if (observer_)
+ observer_->OnItemsAdded(index, 1);
+}
+
+void TemplateURLTableModel::ReloadIcon(int index) {
+ DCHECK(index >= 0 && index < RowCount());
+
+ entries_[index]->ResetIcon();
+
+ NotifyChanged(index);
+}
+
+const TemplateURL& TemplateURLTableModel::GetTemplateURL(int index) {
+ return entries_[index]->template_url();
+}
+
+int TemplateURLTableModel::IndexOfTemplateURL(
+ const TemplateURL* template_url) {
+ for (std::vector<ModelEntry*>::iterator i = entries_.begin();
+ i != entries_.end(); ++i) {
+ ModelEntry* entry = *i;
+ if (&(entry->template_url()) == template_url)
+ return static_cast<int>(i - entries_.begin());
+ }
+ return -1;
+}
+
+void TemplateURLTableModel::MoveToMainGroup(int index) {
+ if (index < last_search_engine_index_)
+ return; // Already in the main group.
+
+ ModelEntry* current_entry = entries_[index];
+ entries_.erase(index + entries_.begin());
+ if (observer_)
+ observer_->OnItemsRemoved(index, 1);
+
+ const int new_index = last_search_engine_index_++;
+ entries_.insert(entries_.begin() + new_index, current_entry);
+ if (observer_)
+ observer_->OnItemsAdded(new_index, 1);
+}
+
+void TemplateURLTableModel::NotifyChanged(int index) {
+ if (observer_)
+ observer_->OnItemsChanged(index, 1);
+}
+
+void TemplateURLTableModel::FavIconAvailable(ModelEntry* entry) {
+ std::vector<ModelEntry*>::iterator i =
+ find(entries_.begin(), entries_.end(), entry);
+ DCHECK(i != entries_.end());
+ NotifyChanged(static_cast<int>(i - entries_.begin()));
+}
+
+// KeywordEditorView ----------------------------------------------------------
+
+// If non-null, there is an open editor and this is the window it is contained
+// in.
+static ChromeViews::Window* open_window = NULL;
+
+// static
+void KeywordEditorView::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterBooleanPref(prefs::kSearchSuggestEnabled, true);
+}
+
+// static
+void KeywordEditorView::Show(Profile* profile) {
+ if (!profile->GetTemplateURLModel())
+ return;
+
+ if (open_window != NULL)
+ open_window->Close();
+ DCHECK(!open_window);
+
+ // Both of these will be deleted when the dialog closes.
+ KeywordEditorView* keyword_editor = new KeywordEditorView(profile);
+
+ // Initialize the UI. By passing in an empty rect KeywordEditorView is
+ // queried for its preferred size.
+ open_window = ChromeViews::Window::CreateChromeWindow(
+ NULL, gfx::Rect(), keyword_editor, keyword_editor);
+
+ open_window->Show();
+}
+
+KeywordEditorView::KeywordEditorView(Profile* profile)
+ : profile_(profile),
+ url_model_(profile->GetTemplateURLModel()) {
+ DCHECK(url_model_);
+ Init();
+}
+
+KeywordEditorView::~KeywordEditorView() {
+ // Only remove the listener if we installed one.
+ if (table_model_.get()) {
+ table_view_->SetModel(NULL);
+ url_model_->RemoveObserver(this);
+ }
+}
+
+void KeywordEditorView::AddTemplateURL(const std::wstring& title,
+ const std::wstring& keyword,
+ const std::wstring& url) {
+ DCHECK(!url.empty());
+
+ UserMetrics::RecordAction(L"KeywordEditor_AddKeyword", profile_);
+
+ TemplateURL* template_url = new TemplateURL();
+ template_url->set_short_name(title);
+ template_url->set_keyword(keyword);
+ template_url->SetURL(url, 0, 0);
+
+ // There's a bug (1090726) in TableView with groups enabled such that newly
+ // added items in groups ALWAYS appear at the end, regardless of the index
+ // passed in. Worse yet, the selected rows get messed up when this happens
+ // causing other problems. As a work around we always add the item to the end
+ // of the list.
+ const int new_index = table_model_->RowCount();
+ url_model_->RemoveObserver(this);
+ table_model_->Add(new_index, template_url);
+ url_model_->Add(template_url);
+ url_model_->AddObserver(this);
+
+ table_view_->Select(new_index);
+}
+
+void KeywordEditorView::ModifyTemplateURL(const TemplateURL* template_url,
+ const std::wstring& title,
+ const std::wstring& keyword,
+ const std::wstring& url) {
+ const int index = table_model_->IndexOfTemplateURL(template_url);
+ if (index == -1) {
+ // Will happen if url was deleted out from under us while the user was
+ // editing it.
+ return;
+ }
+
+ // Don't do anything if the entry didn't change.
+ if (template_url->short_name() == title &&
+ template_url->keyword() == keyword &&
+ ((url.empty() && !template_url->url()) ||
+ (!url.empty() && template_url->url() &&
+ template_url->url()->url() == url))) {
+ return;
+ }
+
+ url_model_->RemoveObserver(this);
+ url_model_->ResetTemplateURL(template_url, title, keyword, url);
+ if (url_model_->GetDefaultSearchProvider() == template_url &&
+ (!template_url->url() || !template_url->url()->SupportsReplacement())) {
+ // The entry was the default search provider, but the url has been modified
+ // so that it no longer supports replacement. Reset the default search
+ // provider so that it doesn't point to a bogus entry.
+ url_model_->SetDefaultSearchProvider(NULL);
+ }
+ url_model_->AddObserver(this);
+ table_model_->ReloadIcon(index); // Also calls NotifyChanged().
+
+ // Force the make default button to update.
+ OnSelectionChanged();
+
+ UserMetrics::RecordAction(L"KeywordEditor_ModifiedKeyword", profile_);
+}
+
+void KeywordEditorView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void KeywordEditorView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_SEARCHENGINES_DIALOG_WIDTH_CHARS,
+ IDS_SEARCHENGINES_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+bool KeywordEditorView::CanResize() const {
+ return true;
+}
+
+std::wstring KeywordEditorView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_WINDOW_TITLE);
+}
+
+int KeywordEditorView::GetDialogButtons() const {
+ return DIALOGBUTTON_CANCEL;
+}
+
+bool KeywordEditorView::Accept() {
+ open_window = NULL;
+ return true;
+}
+
+bool KeywordEditorView::Cancel() {
+ open_window = NULL;
+ return true;
+}
+
+void KeywordEditorView::Init() {
+ DCHECK(!table_model_.get());
+
+ url_model_->Load();
+ url_model_->AddObserver(this);
+
+ table_model_.reset(new TemplateURLTableModel(url_model_));
+
+ std::vector<TableColumn> columns;
+ columns.push_back(
+ TableColumn(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN,
+ TableColumn::LEFT, -1, .75));
+ columns.push_back(
+ TableColumn(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN,
+ TableColumn::LEFT, -1, .25));
+ table_view_ = new ChromeViews::TableView(table_model_.get(), columns,
+ ChromeViews::ICON_AND_TEXT, false, true, true);
+ table_view_->SetObserver(this);
+
+ add_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_NEW_BUTTON));
+ add_button_->SetEnabled(url_model_->loaded());
+ add_button_->SetListener(this);
+
+ edit_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_EDIT_BUTTON));
+ edit_button_->SetEnabled(false);
+ edit_button_->SetListener(this);
+
+ remove_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_REMOVE_BUTTON));
+ remove_button_->SetEnabled(false);
+ remove_button_->SetListener(this);
+
+ make_default_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON));
+ make_default_button_->SetEnabled(false);
+ make_default_button_->SetListener(this);
+
+ enable_suggest_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_SUGGEST_PREF));
+ enable_suggest_checkbox_->SetMultiLine(true);
+ // Enable the Suggest checkbox only if the default provider has Suggest
+ // capability.
+ const TemplateURL* default_provider = url_model_->GetDefaultSearchProvider();
+ enable_suggest_checkbox_->SetEnabled(default_provider &&
+ (default_provider->suggestions_url() != NULL));
+ enable_suggest_checkbox_->SetIsSelected(
+ profile_->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled));
+ enable_suggest_checkbox_->SetListener(this);
+
+ InitLayoutManager();
+}
+
+void KeywordEditorView::InitLayoutManager() {
+ const int related_x = kRelatedControlHorizontalSpacing;
+ const int related_y = kRelatedControlVerticalSpacing;
+ const int unrelated_y = kUnrelatedControlVerticalSpacing;
+
+ GridLayout* contents_layout = CreatePanelGridLayout(this);
+ SetLayoutManager(contents_layout);
+
+ // For the table and buttons.
+ ChromeViews::ColumnSet* column_set = contents_layout->AddColumnSet(0);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ contents_layout->StartRow(0, 0);
+ contents_layout->AddView(table_view_, 1, 8, GridLayout::FILL,
+ GridLayout::FILL);
+ contents_layout->AddView(add_button_);
+
+ contents_layout->StartRowWithPadding(0, 0, 0, related_y);
+ contents_layout->SkipColumns(2);
+ contents_layout->AddView(edit_button_);
+
+ contents_layout->StartRowWithPadding(0, 0, 0, related_y);
+ contents_layout->SkipColumns(2);
+ contents_layout->AddView(remove_button_);
+
+ contents_layout->StartRowWithPadding(0, 0, 0, related_y);
+ contents_layout->SkipColumns(2);
+ contents_layout->AddView(make_default_button_);
+
+ contents_layout->AddPaddingRow(1, 0);
+
+ contents_layout->StartRowWithPadding(0, 0, 0, related_y);
+ contents_layout->AddView(enable_suggest_checkbox_, 3, 1);
+}
+
+void KeywordEditorView::OnSelectionChanged() {
+ const int selected_row_count = table_view_->SelectedRowCount();
+ edit_button_->SetEnabled(selected_row_count == 1);
+ bool can_make_default = false;
+ bool can_remove = false;
+ if (selected_row_count == 1) {
+ const TemplateURL* selected_url =
+ &table_model_->GetTemplateURL(table_view_->FirstSelectedRow());
+ can_make_default =
+ (selected_url != url_model_->GetDefaultSearchProvider() &&
+ selected_url->url() &&
+ selected_url->url()->SupportsReplacement());
+ can_remove = (selected_url != url_model_->GetDefaultSearchProvider());
+ }
+ remove_button_->SetEnabled(can_remove);
+ make_default_button_->SetEnabled(can_make_default);
+}
+
+void KeywordEditorView::OnDoubleClick() {
+ if (edit_button_->IsEnabled())
+ ButtonPressed(edit_button_);
+}
+
+void KeywordEditorView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == add_button_) {
+ EditKeywordController* controller =
+ new EditKeywordController(GetViewContainer()->GetHWND(), NULL, this,
+ profile_);
+ controller->Show();
+ } else if (sender == remove_button_) {
+ DCHECK(table_view_->SelectedRowCount() > 0);
+ // Remove the observer while we modify the model, that way we don't need to
+ // worry about the model calling us back when we mutate it.
+ url_model_->RemoveObserver(this);
+ int last_row = -1;
+ for (ChromeViews::TableView::iterator i = table_view_->SelectionBegin();
+ i != table_view_->SelectionEnd(); ++i) {
+ last_row = *i;
+ const TemplateURL* template_url = &table_model_->GetTemplateURL(last_row);
+ // Make sure to remove from the table model first, otherwise the
+ // TemplateURL would be freed.
+ table_model_->Remove(last_row);
+ url_model_->Remove(template_url);
+ }
+ if (last_row >= table_model_->RowCount())
+ last_row = table_model_->RowCount() - 1;
+ if (last_row >= 0)
+ table_view_->Select(last_row);
+ url_model_->AddObserver(this);
+
+ // We may have removed the default provider. Enable the Suggest checkbox
+ // only if the default provider has Suggest capability.
+ const TemplateURL* default_provider =
+ url_model_->GetDefaultSearchProvider();
+ enable_suggest_checkbox_->SetEnabled(default_provider &&
+ (default_provider->suggestions_url() != NULL));
+ // TODO(pkasting): http://b/1156120 If the template URL model auto-sets a
+ // new default, ensure that the table model is notified of the change on the
+ // relevant row.
+
+ UserMetrics::RecordAction(L"KeywordEditor_RemoveKeyword", profile_);
+ } else if (sender == edit_button_) {
+ const int selected_row = table_view_->FirstSelectedRow();
+ const TemplateURL* template_url =
+ &table_model_->GetTemplateURL(selected_row);
+ EditKeywordController* controller =
+ new EditKeywordController(GetViewContainer()->GetHWND(), template_url,
+ this, profile_);
+ controller->Show();
+ } else if (sender == enable_suggest_checkbox_) {
+ profile_->GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled,
+ enable_suggest_checkbox_->IsSelected());
+ } else if (sender == make_default_button_) {
+ MakeDefaultSearchProvider();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void KeywordEditorView::OnTemplateURLModelChanged() {
+ table_model_->Reload();
+ add_button_->SetEnabled(url_model_->loaded());
+
+ // Enable the Suggest checkbox only if the default provider has Suggest
+ // capability.
+ const TemplateURL* default_provider = url_model_->GetDefaultSearchProvider();
+ enable_suggest_checkbox_->SetEnabled(default_provider &&
+ (default_provider->suggestions_url() != NULL));
+}
+
+void KeywordEditorView::MakeDefaultSearchProvider() {
+ int selected_index = table_view_->FirstSelectedRow();
+ const TemplateURL* keyword = &table_model_->GetTemplateURL(selected_index);
+ const TemplateURL* current_default = url_model_->GetDefaultSearchProvider();
+ if (current_default == keyword)
+ return;
+
+ url_model_->RemoveObserver(this);
+ url_model_->SetDefaultSearchProvider(keyword);
+ url_model_->AddObserver(this);
+
+ // Enable the Suggest checkbox only if this engine has Suggest capability.
+ enable_suggest_checkbox_->SetEnabled(keyword->suggestions_url() != NULL);
+
+ // The formatting of the default engine is different; notify the table that
+ // both old and new entries have changed.
+ if (current_default != NULL) {
+ table_model_->NotifyChanged(table_model_->IndexOfTemplateURL(
+ current_default));
+ }
+ const int new_index = table_model_->IndexOfTemplateURL(keyword);
+ table_model_->NotifyChanged(new_index);
+
+ // Make sure the new default is in the main group.
+ table_model_->MoveToMainGroup(selected_index);
+
+ // And select it.
+ table_view_->Select(new_index);
+} \ No newline at end of file
diff --git a/chrome/browser/views/keyword_editor_view.h b/chrome/browser/views/keyword_editor_view.h
new file mode 100644
index 0000000..3512648
--- /dev/null
+++ b/chrome/browser/views/keyword_editor_view.h
@@ -0,0 +1,227 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_KEYWORD_EDITOR_VIEW_H__
+#define CHROME_BROWSER_VIEWS_KEYWORD_EDITOR_VIEW_H__
+
+#include <Windows.h>
+#include <map>
+
+#include "base/logging.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+class CheckBox;
+class Label;
+}
+
+namespace {
+class BorderView;
+}
+
+class ModelEntry;
+class SkBitmap;
+class TemplateURLModel;
+class TemplateURLTableModel;
+
+// TemplateURLTableModel is the TableModel implementation used by
+// KeywordEditorView to show the keywords in a TableView.
+//
+// TemplateURLTableModel has two columns, the first showing the description,
+// the second the keyword.
+//
+// TemplateURLTableModel maintains a vector of ModelEntrys that correspond to
+// each row in the tableview. Each ModelEntry wraps a TemplateURL, providing
+// the favicon. The entries in the model are sorted such that non-generated
+// appear first (grouped together) and are followed by generated keywords.
+
+class TemplateURLTableModel : public ChromeViews::TableModel {
+ public:
+ explicit TemplateURLTableModel(TemplateURLModel* template_url_model);
+
+ virtual ~TemplateURLTableModel();
+
+ // Reloads the entries from the TemplateURLModel. This should ONLY be invoked
+ // if the TemplateURLModel wasn't initially loaded and has been loaded.
+ void Reload();
+
+ // TableModel overrides.
+ virtual int RowCount();
+ virtual std::wstring GetText(int row, int column);
+ virtual SkBitmap GetIcon(int row);
+ virtual void SetObserver(ChromeViews::TableModelObserver* observer);
+ virtual bool HasGroups();
+ virtual Groups GetGroups();
+ virtual int GetGroupID(int row);
+
+ // Removes the entry at the specified index. This does NOT propagate the
+ // change to the backend.
+ void Remove(int index);
+
+ // Adds a new entry at the specified index. This does not propagate the
+ // change to the backend.
+ void Add(int index, const TemplateURL* template_url);
+
+ // Reloads the icon at the specified index.
+ void ReloadIcon(int index);
+
+ // Returns The TemplateURL at the specified index.
+ const TemplateURL& GetTemplateURL(int index);
+
+ // Returns the index of the TemplateURL, or -1 if it the TemplateURL is not
+ // found.
+ int IndexOfTemplateURL(const TemplateURL* template_url);
+
+ // Moves the keyword at the specified index to be at the end of the main
+ // group. Returns the new index. This does nothing if the entry is already
+ // in the main group.
+ void MoveToMainGroup(int index);
+
+ // If there is an observer, it's notified the selected row has changed.
+ void NotifyChanged(int index);
+
+ TemplateURLModel* template_url_model() const { return template_url_model_; }
+
+ // Returns the index of the last entry shown in the search engines group.
+ int last_search_engine_index() const { return last_search_engine_index_; }
+
+ private:
+ friend class ModelEntry;
+
+ // Notification that a model entry has fetched its icon.
+ void FavIconAvailable(ModelEntry* entry);
+
+ ChromeViews::TableModelObserver* observer_;
+
+ // The entries.
+ std::vector<ModelEntry*> entries_;
+
+ // The model we're displaying entries from.
+ TemplateURLModel* template_url_model_;
+
+ // Index of the last search engine in entries_. This is used to determine the
+ // group boundaries.
+ int last_search_engine_index_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TemplateURLTableModel);
+};
+
+// KeywordEditorView ----------------------------------------------------------
+
+// KeywordEditorView allows the user to edit keywords.
+
+class KeywordEditorView : public ChromeViews::View,
+ public ChromeViews::TableViewObserver,
+ public ChromeViews::NativeButton::Listener,
+ public TemplateURLModelObserver,
+ public ChromeViews::DialogDelegate {
+ public:
+ static void RegisterUserPrefs(PrefService* prefs);
+
+ // Shows the KeywordEditorView for the specified profile. If there is a
+ // KeywordEditorView already open, it is closed and a new one is shown.
+ static void Show(Profile* profile);
+
+ explicit KeywordEditorView(Profile* profile);
+ virtual ~KeywordEditorView();
+
+ // Invoked when the user succesfully fills out the add keyword dialog.
+ // Propagates the change to the TemplateURLModel and updates the table model.
+ void AddTemplateURL(const std::wstring& title,
+ const std::wstring& keyword,
+ const std::wstring& url);
+
+ // Invoked when the user modifies a TemplateURL. Update the TemplateURLModel
+ // and table model appropriatley.
+ void ModifyTemplateURL(const TemplateURL* template_url,
+ const std::wstring& title,
+ const std::wstring& keyword,
+ const std::wstring& url);
+
+ // Overriden to invoke Layout.
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+ virtual void GetPreferredSize(CSize* out);
+
+ // DialogDelegate methods:
+ virtual bool CanResize() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual int GetDialogButtons() const;
+ virtual bool Accept();
+ virtual bool Cancel();
+
+ // Returns the TemplateURLModel we're using.
+ TemplateURLModel* template_url_model() const { return url_model_; }
+
+ private:
+ void Init();
+
+ // Creates the layout and adds the views to it.
+ void InitLayoutManager();
+
+ // TableViewObserver method. Updates buttons contingent on the selection.
+ virtual void OnSelectionChanged();
+ // Edits the selected item.
+ virtual void OnDoubleClick();
+
+ // Button::ButtonListener method.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // TemplateURLModelObserver notification.
+ virtual void OnTemplateURLModelChanged();
+
+ // Toggles whether the selected keyword is the default search provider.
+ void MakeDefaultSearchProvider();
+
+ // The profile.
+ Profile* profile_;
+
+ // Model containing TemplateURLs. We listen for changes on this and propagate
+ // them to the table model.
+ TemplateURLModel* url_model_;
+
+ // Model for the TableView.
+ scoped_ptr<TemplateURLTableModel> table_model_;
+
+ // All the views are added as children, so that we don't need to delete
+ // them directly.
+ ChromeViews::TableView* table_view_;
+ ChromeViews::NativeButton* add_button_;
+ ChromeViews::NativeButton* edit_button_;
+ ChromeViews::NativeButton* remove_button_;
+ ChromeViews::NativeButton* make_default_button_;
+ ChromeViews::CheckBox* enable_suggest_checkbox_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(KeywordEditorView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_KEYWORD_EDITOR_VIEW_H__
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
new file mode 100644
index 0000000..515646d
--- /dev/null
+++ b/chrome/browser/views/location_bar_view.cc
@@ -0,0 +1,1048 @@
+// 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/views/location_bar_view.h"
+
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/alternate_nav_url_fetcher.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/page_info_window.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/ssl_error_info.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/browser/views/info_bubble.h"
+#include "chrome/browser/views/first_run_bubble.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/background.h"
+#include "chrome/views/border.h"
+#include "chrome/views/view_container.h"
+#include "googleurl/src/gurl.h"
+#include "googleurl/src/url_canon.h"
+#include "generated_resources.h"
+
+using ChromeViews::View;
+
+const int LocationBarView::kTextVertMargin = 2;
+
+const COLORREF LocationBarView::kBackgroundColorByLevel[] = {
+ RGB(255, 245, 195), // SecurityLevel SECURE: Yellow.
+ RGB(255, 255, 255), // SecurityLevel NORMAL: White.
+ RGB(255, 255, 255), // SecurityLevel INSECURE: White.
+};
+
+// The margins around the solid color we draw.
+static const int kBackgroundVertMargin = 2;
+static const int kBackgroundHoriMargin = 0;
+
+// Padding on the right and left of the entry field.
+static const int kEntryPadding = 3;
+
+// Padding between the entry and the leading/trailing views.
+static const int kInnerPadding = 3;
+
+static const SkBitmap* kBackground = NULL;
+
+static const SkBitmap* kPopupBackgroundLeft = NULL;
+static const SkBitmap* kPopupBackgroundCenter = NULL;
+static const SkBitmap* kPopupBackgroundRight = NULL;
+static const int kPopupBackgroundVertMargin = 2;
+static const int kPopupBackgroundHorzMargin = 2;
+
+// The delay the mouse has to be hovering over the lock/warning icon before the
+// info bubble is shown.
+static const int kInfoBubbleHoverDelayMs = 500;
+
+// The tab key image.
+static const SkBitmap* kTabButtonBitmap = NULL;
+
+// Returns the description for a keyword.
+static std::wstring GetKeywordDescription(Profile* profile,
+ const std::wstring& keyword) {
+ // Make sure the TemplateURL still exists.
+ // TODO(sky): Once LocationBarView adds a listener to the TemplateURLModel
+ // to track changes to the model, this should become a DCHECK.
+ const TemplateURL* template_url =
+ profile->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword);
+ return template_url ? template_url->short_name() : std::wstring();
+}
+
+LocationBarView::LocationBarView(Profile* profile,
+ CommandController* controller,
+ ToolbarModel* model,
+ Delegate* delegate,
+ bool popup_window_mode)
+ : profile_(profile),
+ controller_(controller),
+ model_(model),
+ delegate_(delegate),
+ disposition_(CURRENT_TAB),
+ location_entry_view_(NULL),
+ selected_keyword_view_(profile),
+ keyword_hint_view_(profile),
+ type_to_search_view_(l10n_util::GetString(IDS_OMNIBOX_EMPTY_TEXT)),
+ security_image_view_(profile, model),
+ popup_window_mode_(popup_window_mode),
+ first_run_bubble_(this) {
+ DCHECK(profile_);
+ SetID(VIEW_ID_LOCATION_BAR);
+ SetFocusable(true);
+
+ if (!kBackground) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ kBackground = rb.GetBitmapNamed(IDR_LOCATIONBG);
+ kPopupBackgroundLeft =
+ rb.GetBitmapNamed(IDR_LOCATIONBG_POPUPMODE_LEFT);
+ kPopupBackgroundCenter =
+ rb.GetBitmapNamed(IDR_LOCATIONBG_POPUPMODE_CENTER);
+ kPopupBackgroundRight =
+ rb.GetBitmapNamed(IDR_LOCATIONBG_POPUPMODE_RIGHT);
+ }
+}
+
+bool LocationBarView::IsInitialized() const {
+ return location_entry_view_ != NULL;
+}
+
+void LocationBarView::Init() {
+ if (popup_window_mode_) {
+ font_ = ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont);
+ } else {
+ // Use a larger version of the system font.
+ font_ = font_.DeriveFont(3);
+ }
+
+ // URL edit field.
+ ChromeViews::ViewContainer* vc = GetViewContainer();
+ DCHECK(vc) << "LocationBarView::Init - vc is NULL!";
+ location_entry_.reset(new AutocompleteEdit(font_, this, model_, this,
+ vc->GetHWND(),
+ profile_, controller_,
+ popup_window_mode_));
+
+ // View container for URL edit field.
+ location_entry_view_ = new ChromeViews::HWNDView;
+ DCHECK(location_entry_view_) << "LocationBarView::Init - OOM!";
+ location_entry_view_->SetID(VIEW_ID_AUTOCOMPLETE);
+ AddChildView(location_entry_view_);
+ location_entry_view_->SetAssociatedFocusView(this);
+ location_entry_view_->Attach(location_entry_->m_hWnd);
+
+ AddChildView(&selected_keyword_view_);
+ selected_keyword_view_.SetFont(font_);
+ selected_keyword_view_.SetVisible(false);
+ selected_keyword_view_.SetParentOwned(false);
+
+ DWORD sys_color = GetSysColor(COLOR_GRAYTEXT);
+ SkColor gray = SkColorSetRGB(GetRValue(sys_color), GetGValue(sys_color),
+ GetBValue(sys_color));
+
+ AddChildView(&type_to_search_view_);
+ type_to_search_view_.SetVisible(false);
+ type_to_search_view_.SetFont(font_);
+ type_to_search_view_.SetColor(gray);
+ type_to_search_view_.SetParentOwned(false);
+
+ AddChildView(&keyword_hint_view_);
+ keyword_hint_view_.SetVisible(false);
+ keyword_hint_view_.SetFont(font_);
+ keyword_hint_view_.SetColor(gray);
+ keyword_hint_view_.SetParentOwned(false);
+
+ AddChildView(&security_image_view_);
+ security_image_view_.SetVisible(false);
+ security_image_view_.SetParentOwned(false);
+
+ AddChildView(&info_label_);
+ info_label_.SetVisible(false);
+ info_label_.SetParentOwned(false);
+
+ // Notify us when any ancestor is resized. In this case we want to tell the
+ // AutocompleteEdit to close its popup.
+ SetNotifyWhenVisibleBoundsInRootChanges(true);
+
+ // Initialize the location entry. We do this to avoid a black flash which is
+ // visible when the location entry has just been initialized.
+ Update(NULL);
+
+ OnChanged();
+}
+
+void LocationBarView::Update(const TabContents* tab_for_state_restoring) {
+ SetSecurityIcon(model_->GetIcon());
+ std::wstring info_text, info_tooltip;
+ SkColor text_color;
+ model_->GetInfoText(&info_text, &text_color, &info_tooltip);
+ SetInfoText(info_text, text_color, info_tooltip);
+ location_entry_->Update(tab_for_state_restoring);
+ Layout();
+ SchedulePaint();
+}
+
+void LocationBarView::Focus() {
+ ::SetFocus(location_entry_->m_hWnd);
+}
+
+void LocationBarView::SetProfile(Profile* profile) {
+ DCHECK(profile);
+ if (profile_ != profile) {
+ profile_ = profile;
+ location_entry_->SetProfile(profile);
+ selected_keyword_view_.set_profile(profile);
+ keyword_hint_view_.set_profile(profile);
+ security_image_view_.set_profile(profile);
+ }
+}
+
+void LocationBarView::GetPreferredSize(CSize *out) {
+ CSize size;
+ security_image_view_.GetPreferredSize(&size);
+ out->cx = 0;
+
+ out->cy = std::max(
+ (popup_window_mode_ ? kPopupBackgroundCenter : kBackground)->height(),
+ static_cast<int>(size.cy));
+}
+
+void LocationBarView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void LocationBarView::Layout() {
+ DoLayout(true);
+}
+
+void LocationBarView::Paint(ChromeCanvas* canvas) {
+ View::Paint(canvas);
+
+ SkColor bg = SkColorSetRGB(
+ GetRValue(kBackgroundColorByLevel[model_->GetSchemeSecurityLevel()]),
+ GetGValue(kBackgroundColorByLevel[model_->GetSchemeSecurityLevel()]),
+ GetBValue(kBackgroundColorByLevel[model_->GetSchemeSecurityLevel()]));
+
+ if (popup_window_mode_ == false) {
+ int bh = kBackground->height();
+
+ canvas->TileImageInt(*kBackground, 0, (GetHeight() - bh) / 2, GetWidth(),
+ bh);
+
+ canvas->FillRectInt(bg, kBackgroundHoriMargin, kBackgroundVertMargin,
+ GetWidth() - 2 * kBackgroundHoriMargin,
+ bh - kBackgroundVertMargin * 2);
+ } else {
+ canvas->TileImageInt(*kPopupBackgroundLeft, 0, 0,
+ kPopupBackgroundLeft->width(),
+ kPopupBackgroundLeft->height());
+ canvas->TileImageInt(*kPopupBackgroundCenter,
+ kPopupBackgroundLeft->width(), 0,
+ GetWidth() -
+ kPopupBackgroundLeft->width() -
+ kPopupBackgroundRight->width(),
+ kPopupBackgroundCenter->height());
+ canvas->TileImageInt(*kPopupBackgroundRight,
+ GetWidth() - kPopupBackgroundRight->width(),
+ 0, kPopupBackgroundRight->width(),
+ kPopupBackgroundRight->height());
+
+ canvas->FillRectInt(bg, kPopupBackgroundHorzMargin,
+ kPopupBackgroundVertMargin,
+ GetWidth() - kPopupBackgroundHorzMargin * 2,
+ kPopupBackgroundCenter->height() -
+ kPopupBackgroundVertMargin * 2);
+ }
+}
+
+bool LocationBarView::CanProcessTabKeyEvents() {
+ // We want to receive tab key events when the hint is showing.
+ return keyword_hint_view_.IsVisible();
+}
+
+void LocationBarView::VisibleBoundsInRootChanged() {
+ location_entry_->ClosePopup();
+}
+
+bool LocationBarView::OnMousePressed(const ChromeViews::MouseEvent& event) {
+ UINT msg;
+ if (event.IsLeftMouseButton()) {
+ msg = (event.GetFlags() & ChromeViews::MouseEvent::EF_IS_DOUBLE_CLICK) ?
+ WM_LBUTTONDBLCLK : WM_LBUTTONDOWN;
+ } else if (event.IsMiddleMouseButton()) {
+ msg = (event.GetFlags() & ChromeViews::MouseEvent::EF_IS_DOUBLE_CLICK) ?
+ WM_MBUTTONDBLCLK : WM_MBUTTONDOWN;
+ } else if (event.IsRightMouseButton()) {
+ msg = (event.GetFlags() & ChromeViews::MouseEvent::EF_IS_DOUBLE_CLICK) ?
+ WM_RBUTTONDBLCLK : WM_RBUTTONDOWN;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+ OnMouseEvent(event, msg);
+ return true;
+}
+
+bool LocationBarView::OnMouseDragged(const ChromeViews::MouseEvent& event) {
+ OnMouseEvent(event, WM_MOUSEMOVE);
+ return true;
+}
+
+void LocationBarView::OnMouseReleased(const ChromeViews::MouseEvent& event,
+ bool canceled) {
+ UINT msg;
+ if (canceled) {
+ msg = WM_CAPTURECHANGED;
+ } else if (event.IsLeftMouseButton()) {
+ msg = WM_LBUTTONUP;
+ } else if (event.IsMiddleMouseButton()) {
+ msg = WM_MBUTTONUP;
+ } else if (event.IsRightMouseButton()) {
+ msg = WM_RBUTTONUP;
+ } else {
+ NOTREACHED();
+ return;
+ }
+ OnMouseEvent(event, msg);
+}
+
+void LocationBarView::OnAutocompleteAccept(
+ const std::wstring& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const std::wstring& alternate_nav_url) {
+ if (url.empty())
+ return;
+
+ location_input_ = url;
+ disposition_ = disposition;
+ transition_ = transition;
+
+ if (controller_) {
+ if (alternate_nav_url.empty()) {
+ controller_->ExecuteCommand(IDC_OPENURL);
+ return;
+ }
+
+ scoped_ptr<AlternateNavURLFetcher> fetcher(
+ new AlternateNavURLFetcher(alternate_nav_url));
+ // The AlternateNavURLFetcher will listen for the next navigation state
+ // update notification (expecting it to be a new page load) and hook
+ // itself in to that loading process.
+ controller_->ExecuteCommand(IDC_OPENURL);
+ if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) {
+ // I'm not sure this should be reachable, but I'm not also sure enough
+ // that it shouldn't to stick in a NOTREACHED(). In any case, this is
+ // harmless; we can simply let the fetcher get deleted here and it will
+ // clean itself up properly.
+ } else {
+ fetcher.release(); // The navigation controller will delete the fetcher.
+ }
+ }
+}
+
+void LocationBarView::OnChanged() {
+ DoLayout(false);
+}
+
+SkBitmap LocationBarView::GetFavIcon() const {
+ DCHECK(delegate_);
+ DCHECK(delegate_->GetTabContents());
+ return delegate_->GetTabContents()->GetFavIcon();
+}
+
+std::wstring LocationBarView::GetTitle() const {
+ DCHECK(delegate_);
+ DCHECK(delegate_->GetTabContents());
+ return delegate_->GetTabContents()->GetTitle();
+}
+
+void LocationBarView::DoLayout(const bool force_layout) {
+ if (!location_entry_.get())
+ return;
+
+ RECT formatting_rect;
+ location_entry_->GetRect(&formatting_rect);
+ RECT edit_bounds;
+ location_entry_->GetClientRect(&edit_bounds);
+
+ int entry_width = GetWidth() - kEntryPadding - kEntryPadding;
+ CSize security_image_size;
+ if (security_image_view_.IsVisible()) {
+ security_image_view_.GetPreferredSize(&security_image_size);
+ entry_width -= security_image_size.cx;
+ }
+ CSize info_label_size;
+ if (info_label_.IsVisible()) {
+ info_label_.GetPreferredSize(&info_label_size);
+ entry_width -= (info_label_size.cx + kInnerPadding);
+ }
+
+ const int max_edit_width = entry_width - formatting_rect.left -
+ (edit_bounds.right - formatting_rect.right);
+ if (max_edit_width < 0)
+ return;
+ const int text_width = TextDisplayWidth();
+ bool needs_layout = force_layout;
+ needs_layout |= AdjustHints(text_width, max_edit_width);
+
+ if (!needs_layout)
+ return;
+
+ // TODO(sky): baseline layout.
+ int bh = kBackground->height();
+ int location_y = ((GetHeight() - bh) / 2) + kTextVertMargin;
+ int location_height = bh - (2 * kTextVertMargin);
+ if (info_label_.IsVisible()) {
+ info_label_.SetBounds(GetWidth() - kEntryPadding - info_label_size.cx,
+ location_y,
+ info_label_size.cx, location_height);
+ }
+ if (security_image_view_.IsVisible()) {
+ const int info_label_width = info_label_size.cx ?
+ info_label_size.cx + kInnerPadding : 0;
+ security_image_view_.SetBounds(GetWidth() - kEntryPadding -
+ info_label_width -
+ security_image_size.cx,
+ location_y,
+ security_image_size.cx, location_height);
+ }
+ gfx::Rect location_bounds(kEntryPadding, location_y, entry_width,
+ location_height);
+ if (selected_keyword_view_.IsVisible()) {
+ LayoutView(true, &selected_keyword_view_, text_width, max_edit_width,
+ &location_bounds);
+ } else if (keyword_hint_view_.IsVisible()) {
+ LayoutView(false, &keyword_hint_view_, text_width, max_edit_width,
+ &location_bounds);
+ } else if (type_to_search_view_.IsVisible()) {
+ LayoutView(false, &type_to_search_view_, text_width, max_edit_width,
+ &location_bounds);
+ }
+
+ location_entry_view_->SetBounds(location_bounds.x(),
+ location_bounds.y(),
+ location_bounds.width(),
+ location_bounds.height());
+ if (!force_layout) {
+ // If force_layout is false and we got this far it means one of the views
+ // was added/removed or changed in size. We need to paint ourselves.
+ SchedulePaint();
+ }
+}
+
+int LocationBarView::TextDisplayWidth() {
+ POINT last_char_position =
+ location_entry_->PosFromChar(location_entry_->GetTextLength());
+ POINT scroll_position;
+ location_entry_->GetScrollPos(&scroll_position);
+ const int position_x = last_char_position.x + scroll_position.x;
+ return UILayoutIsRightToLeft() ? GetWidth() - position_x : position_x;
+}
+
+bool LocationBarView::UsePref(int pref_width, int text_width, int max_width) {
+ return (pref_width + kInnerPadding + text_width <= max_width);
+}
+
+bool LocationBarView::NeedsResize(View* view, int text_width, int max_width) {
+ CSize size;
+ view->GetPreferredSize(&size);
+ if (!UsePref(size.cx, text_width, max_width))
+ view->GetMinimumSize(&size);
+ return (view->GetWidth() != size.cx);
+}
+
+bool LocationBarView::AdjustHints(int text_width, int max_width) {
+ const std::wstring keyword(location_entry_->keyword());
+ const bool is_keyword_hint(location_entry_->is_keyword_hint());
+ const bool show_selected_keyword = !keyword.empty() && !is_keyword_hint;
+ const bool show_keyword_hint = !keyword.empty() && is_keyword_hint;
+ bool show_search_hint(location_entry_->show_search_hint());
+ DCHECK(keyword.empty() || !show_search_hint);
+
+ if (show_search_hint) {
+ // Only show type to search if all the text fits.
+ CSize view_pref;
+ type_to_search_view_.GetPreferredSize(&view_pref);
+ show_search_hint = UsePref(view_pref.cx, text_width, max_width);
+ }
+
+ // NOTE: This isn't just one big || statement as ToggleVisibility MUST be
+ // invoked for each view.
+ bool needs_layout = false;
+ needs_layout |= ToggleVisibility(show_selected_keyword,
+ &selected_keyword_view_);
+ needs_layout |= ToggleVisibility(show_keyword_hint, &keyword_hint_view_);
+ needs_layout |= ToggleVisibility(show_search_hint, &type_to_search_view_);
+ if (show_selected_keyword) {
+ if (selected_keyword_view_.keyword() != keyword) {
+ needs_layout = true;
+ selected_keyword_view_.SetKeyword(keyword);
+ }
+ needs_layout |= NeedsResize(&selected_keyword_view_, text_width, max_width);
+ } else if (show_keyword_hint) {
+ if (keyword_hint_view_.keyword() != keyword) {
+ needs_layout = true;
+ keyword_hint_view_.SetKeyword(keyword);
+ }
+ needs_layout |= NeedsResize(&keyword_hint_view_, text_width, max_width);
+ }
+
+ return needs_layout;
+}
+
+void LocationBarView::LayoutView(bool leading, ChromeViews::View* view,
+ int text_width, int max_width,
+ gfx::Rect* bounds) {
+ DCHECK(view && bounds);
+ CSize view_size(0, 0);
+ view->GetPreferredSize(&view_size);
+ if (!UsePref(view_size.cx, text_width, max_width))
+ view->GetMinimumSize(&view_size);
+ if (view_size.cx + kInnerPadding < bounds->width()) {
+ view->SetVisible(true);
+ if (leading) {
+ view->SetBounds(bounds->x(), bounds->y(), view_size.cx, bounds->height());
+ bounds->Offset(view_size.cx + kInnerPadding, 0);
+ } else {
+ view->SetBounds(bounds->right() - view_size.cx, bounds->y(),
+ view_size.cx, bounds->height());
+ }
+ bounds->set_width(bounds->width() - view_size.cx - kInnerPadding);
+ } else {
+ view->SetVisible(false);
+ }
+}
+
+void LocationBarView::SetSecurityIcon(ToolbarModel::Icon icon) {
+ switch (icon) {
+ case ToolbarModel::LOCK_ICON:
+ security_image_view_.SetImageShown(SecurityImageView::LOCK);
+ security_image_view_.SetVisible(true);
+ break;
+ case ToolbarModel::WARNING_ICON:
+ security_image_view_.SetImageShown(SecurityImageView::WARNING);
+ security_image_view_.SetVisible(true);
+ break;
+ case ToolbarModel::NO_ICON:
+ security_image_view_.SetVisible(false);
+ break;
+ default:
+ NOTREACHED();
+ security_image_view_.SetVisible(false);
+ break;
+ }
+}
+
+void LocationBarView::SetInfoText(const std::wstring& text,
+ SkColor text_color,
+ const std::wstring& tooltip_text) {
+ info_label_.SetVisible(!text.empty());
+ info_label_.SetText(text);
+ info_label_.SetColor(text_color);
+ info_label_.SetTooltipText(tooltip_text);
+}
+
+bool LocationBarView::ToggleVisibility(bool new_vis, View* view) {
+ DCHECK(view);
+ if (view->IsVisible() != new_vis) {
+ view->SetVisible(new_vis);
+ return true;
+ }
+ return false;
+}
+
+void LocationBarView::OnMouseEvent(const ChromeViews::MouseEvent& event,
+ UINT msg) {
+ UINT flags = 0;
+ if (event.IsControlDown())
+ flags |= MK_CONTROL;
+ if (event.IsShiftDown())
+ flags |= MK_SHIFT;
+ if (event.IsLeftMouseButton())
+ flags |= MK_LBUTTON;
+ if (event.IsMiddleMouseButton())
+ flags |= MK_MBUTTON;
+ if (event.IsRightMouseButton())
+ flags |= MK_RBUTTON;
+
+ CPoint screen_point(event.GetLocation());
+ ConvertPointToScreen(this, &screen_point);
+
+ location_entry_->HandleExternalMsg(msg, flags, screen_point);
+}
+
+bool LocationBarView::GetAccessibleRole(VARIANT* role) {
+ DCHECK(role);
+
+ role->vt = VT_I4;
+ role->lVal = ROLE_SYSTEM_GROUPING;
+ return true;
+}
+
+// SelectedKeywordView -------------------------------------------------------
+
+// The background is drawn using ImagePainter3. This is the left/center/right
+// image names.
+static const int kBorderImages[] = {
+ IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_L,
+ IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_C,
+ IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_R };
+
+// Insets around the label.
+static const int kTopInset = 0;
+static const int kBottomInset = 0;
+static const int kLeftInset = 4;
+static const int kRightInset = 4;
+
+// Offset from the top the background is drawn at.
+static const int kBackgroundYOffset = 2;
+
+LocationBarView::SelectedKeywordView::SelectedKeywordView(Profile* profile)
+ : background_painter_(kBorderImages),
+ profile_(profile) {
+ AddChildView(&full_label_);
+ AddChildView(&partial_label_);
+ // Full_label and partial_label are deleted by us, make sure View doesn't
+ // delete them too.
+ full_label_.SetParentOwned(false);
+ partial_label_.SetParentOwned(false);
+ full_label_.SetVisible(false);
+ partial_label_.SetVisible(false);
+ full_label_.SetBorder(
+ ChromeViews::Border::CreateEmptyBorder(kTopInset, kLeftInset,
+ kBottomInset, kRightInset));
+ partial_label_.SetBorder(
+ ChromeViews::Border::CreateEmptyBorder(kTopInset, kLeftInset,
+ kBottomInset, kRightInset));
+}
+
+LocationBarView::SelectedKeywordView::~SelectedKeywordView() {
+}
+
+void LocationBarView::SelectedKeywordView::SetFont(const ChromeFont& font) {
+ full_label_.SetFont(font);
+ partial_label_.SetFont(font);
+}
+
+void LocationBarView::SelectedKeywordView::Paint(ChromeCanvas* canvas) {
+ canvas->TranslateInt(0, kBackgroundYOffset);
+ background_painter_.Paint(GetWidth(), GetHeight() - kTopInset, canvas);
+ canvas->TranslateInt(0, -kBackgroundYOffset);
+}
+
+void LocationBarView::SelectedKeywordView::GetPreferredSize(CSize* size) {
+ full_label_.GetPreferredSize(size);
+}
+
+void LocationBarView::SelectedKeywordView::GetMinimumSize(CSize* size) {
+ partial_label_.GetMinimumSize(size);
+}
+
+void LocationBarView::SelectedKeywordView::DidChangeBounds(
+ const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void LocationBarView::SelectedKeywordView::Layout() {
+ CSize pref;
+ GetPreferredSize(&pref);
+ bool at_pref = (GetWidth() == pref.cx);
+ if (at_pref)
+ full_label_.SetBounds(0, 0, GetWidth(), GetHeight());
+ else
+ partial_label_.SetBounds(0, 0, GetWidth(), GetHeight());
+ full_label_.SetVisible(at_pref);
+ partial_label_.SetVisible(!at_pref);
+}
+
+void LocationBarView::SelectedKeywordView::SetKeyword(
+ const std::wstring& keyword) {
+ keyword_ = keyword;
+ if (keyword.empty())
+ return;
+ DCHECK(profile_);
+ if (!profile_->GetTemplateURLModel())
+ return;
+
+ const std::wstring description = GetKeywordDescription(profile_, keyword);
+ full_label_.SetText(l10n_util::GetStringF(IDS_OMNIBOX_KEYWORD_TEXT,
+ description));
+ const std::wstring min_string = CalculateMinString(description);
+ if (!min_string.empty()) {
+ partial_label_.SetText(
+ l10n_util::GetStringF(IDS_OMNIBOX_KEYWORD_TEXT, min_string));
+ } else {
+ partial_label_.SetText(full_label_.GetText());
+ }
+}
+
+std::wstring LocationBarView::SelectedKeywordView::CalculateMinString(
+ const std::wstring& description) {
+ // Chop at the first '.' or whitespace.
+ const size_t dot_index = description.find(L'.');
+ const size_t ws_index = description.find_first_of(kWhitespaceWide);
+ size_t chop_index = std::min(dot_index, ws_index);
+ if (chop_index == std::wstring::npos) {
+ // No dot or whitespace, truncate to at most 3 chars.
+ return l10n_util::TruncateString(description, 3);
+ }
+ return description.substr(0, chop_index);
+}
+
+// KeywordHintView -------------------------------------------------------------
+
+// Amount of space to offset the tab image from the top of the view by.
+static const int kTabImageYOffset = 4;
+
+LocationBarView::KeywordHintView::KeywordHintView(Profile* profile)
+ : profile_(profile) {
+ AddChildView(&leading_label_);
+ AddChildView(&trailing_label_);
+
+ if (!kTabButtonBitmap) {
+ kTabButtonBitmap = ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(IDR_LOCATION_BAR_KEYWORD_HINT_TAB);
+ }
+}
+
+LocationBarView::KeywordHintView::~KeywordHintView() {
+ // Labels are freed by us. Remove them so that View doesn't
+ // try to free them too.
+ RemoveChildView(&leading_label_);
+ RemoveChildView(&trailing_label_);
+}
+
+void LocationBarView::KeywordHintView::SetFont(const ChromeFont& font) {
+ leading_label_.SetFont(font);
+ trailing_label_.SetFont(font);
+}
+
+void LocationBarView::KeywordHintView::SetColor(const SkColor& color) {
+ leading_label_.SetColor(color);
+ trailing_label_.SetColor(color);
+}
+
+void LocationBarView::KeywordHintView::SetKeyword(const std::wstring& keyword) {
+ keyword_ = keyword;
+ if (keyword_.empty())
+ return;
+ DCHECK(profile_);
+ if (!profile_->GetTemplateURLModel())
+ return;
+
+ std::vector<size_t> content_param_offsets;
+ const std::wstring keyword_hint(l10n_util::GetStringF(
+ IDS_OMNIBOX_KEYWORD_HINT, std::wstring(),
+ GetKeywordDescription(profile_, keyword), &content_param_offsets));
+ if (content_param_offsets.size() == 2) {
+ leading_label_.SetText(keyword_hint.substr(0,
+ content_param_offsets.front()));
+ trailing_label_.SetText(keyword_hint.substr(content_param_offsets.front()));
+ } else {
+ // See comments on an identical NOTREACHED() in search_provider.cc.
+ NOTREACHED();
+ }
+}
+
+void LocationBarView::KeywordHintView::Paint(ChromeCanvas* canvas) {
+ int image_x = leading_label_.IsVisible() ? leading_label_.GetWidth() : 0;
+
+ // Since we paint the button image directly on the canvas (instead of using a
+ // child view), we must mirror the button's position manually if the locale
+ // is right-to-left.
+ gfx::Rect tab_button_bounds(image_x,
+ kTabImageYOffset,
+ kTabButtonBitmap->width(),
+ kTabButtonBitmap->height());
+ tab_button_bounds.set_x(MirroredLeftPointForRect(tab_button_bounds));
+ canvas->DrawBitmapInt(*kTabButtonBitmap,
+ tab_button_bounds.x(),
+ tab_button_bounds.y());
+}
+
+void LocationBarView::KeywordHintView::GetPreferredSize(CSize *out) {
+ // TODO(sky): currently height doesn't matter, once baseline support is
+ // added this should check baselines.
+ leading_label_.GetPreferredSize(out);
+ int width = out->cx;
+ width += kTabButtonBitmap->width();
+ trailing_label_.GetPreferredSize(out);
+ width += out->cx;
+ out->cx = width;
+}
+
+void LocationBarView::KeywordHintView::GetMinimumSize(CSize* out) {
+ // TODO(sky): currently height doesn't matter, once baseline support is
+ // added this should check baselines.
+ out->cx = kTabButtonBitmap->width();
+}
+
+void LocationBarView::KeywordHintView::Layout() {
+ // TODO(sky): baseline layout.
+ bool show_labels = (GetWidth() != kTabButtonBitmap->width());
+
+ leading_label_.SetVisible(show_labels);
+ trailing_label_.SetVisible(show_labels);
+ int height = GetHeight();
+ int x = 0;
+ CSize pref;
+
+ if (show_labels) {
+ leading_label_.GetPreferredSize(&pref);
+ leading_label_.SetBounds(x, 0, pref.cx, height);
+
+ x += pref.cx + kTabButtonBitmap->width();
+ trailing_label_.GetPreferredSize(&pref);
+ trailing_label_.SetBounds(x, 0, pref.cx, height);
+ }
+}
+
+void LocationBarView::KeywordHintView::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+// We don't translate accelerators for ALT + numpad digit, they are used for
+// entering special characters.
+bool LocationBarView::ShouldLookupAccelerators(const ChromeViews::KeyEvent& e) {
+ if (!e.IsAltDown())
+ return true;
+
+ return !win_util::IsNumPadDigit(e.GetCharacter(), e.IsExtendedKey());
+}
+
+// ShowInfoBubbleTask-----------------------------------------------------------
+
+class LocationBarView::ShowInfoBubbleTask : public Task {
+ public:
+ explicit ShowInfoBubbleTask(LocationBarView::SecurityImageView* image_view);
+ virtual void Run();
+ void Cancel();
+
+ private:
+ LocationBarView::SecurityImageView* image_view_;
+ bool cancelled_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ShowInfoBubbleTask);
+};
+
+LocationBarView::ShowInfoBubbleTask::ShowInfoBubbleTask(
+ LocationBarView::SecurityImageView* image_view)
+ : cancelled_(false),
+ image_view_(image_view) {
+}
+
+void LocationBarView::ShowInfoBubbleTask::Run() {
+ if (cancelled_)
+ return;
+
+ if (!image_view_->GetViewContainer()->IsActive()) {
+ // The browser is no longer active. Let's not show the info bubble, this
+ // would make the browser the active window again. Also makes sure we NULL
+ // show_info_bubble_task_ to prevent the SecurityImageView from keeping a
+ // dangling pointer.
+ image_view_->show_info_bubble_task_ = NULL;
+ return;
+ }
+
+ image_view_->ShowInfoBubble();
+}
+
+void LocationBarView::ShowInfoBubbleTask::Cancel() {
+ cancelled_ = true;
+}
+
+// -----------------------------------------------------------------------------
+
+void LocationBarView::ShowFirstRunBubbleInternal() {
+ if (!location_entry_view_)
+ return;
+ if (!location_entry_view_->GetViewContainer()->IsActive()) {
+ // The browser is no longer active. Let's not show the info bubble, this
+ // would make the browser the active window again.
+ return;
+ }
+
+ CPoint location(0, 0);
+
+ // If the UI layout is RTL, the coordinate system is not transformed and
+ // therefore we need to adjust the X coordinate so that bubble appears on the
+ // right hand side of the location bar.
+ if (UILayoutIsRightToLeft())
+ location.x += GetWidth();
+ ChromeViews::View::ConvertPointToScreen(this, &location);
+
+ // We try to guess that 20 pixels offset is a good place for the first
+ // letter in the OmniBox.
+ gfx::Rect bounds(location.x, location.y, 20, GetHeight());
+
+ // Moving the bounds "backwards" so that it appears within the location bar
+ // if the UI layout is RTL.
+ if (UILayoutIsRightToLeft())
+ bounds.set_x(location.x - 20);
+
+ FirstRunBubble::Show(
+ location_entry_view_->GetRootView()->GetViewContainer()->GetHWND(),
+ bounds);
+}
+
+void LocationBarView::ShowFirstRunBubble() {
+ // We wait 30 milliseconds to open. It allows less flicker.
+ Task* task = first_run_bubble_.NewRunnableMethod(
+ &LocationBarView::ShowFirstRunBubbleInternal);
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 30);
+}
+
+// SecurityImageView------------------------------------------------------------
+
+// static
+SkBitmap* LocationBarView::SecurityImageView::lock_icon_ = NULL;
+SkBitmap* LocationBarView::SecurityImageView::warning_icon_ = NULL;
+
+LocationBarView::SecurityImageView::SecurityImageView(Profile* profile,
+ ToolbarModel* model)
+ : profile_(profile),
+ model_(model),
+ show_info_bubble_task_(NULL),
+ info_bubble_(NULL) {
+ if (!lock_icon_) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ lock_icon_ = rb.GetBitmapNamed(IDR_LOCK);
+ warning_icon_ = rb.GetBitmapNamed(IDR_WARNING);
+ }
+ SetImageShown(LOCK);
+}
+
+LocationBarView::SecurityImageView::~SecurityImageView() {
+ if (show_info_bubble_task_)
+ show_info_bubble_task_->Cancel();
+
+ if (info_bubble_) {
+ // We are going to be invalid, make sure the InfoBubble does not keep a
+ // pointer to us.
+ info_bubble_->SetDelegate(NULL);
+ }
+}
+
+void LocationBarView::SecurityImageView::SetImageShown(Image image) {
+ switch (image) {
+ case LOCK:
+ ImageView::SetImage(lock_icon_);
+ break;
+ case WARNING:
+ ImageView::SetImage(warning_icon_);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void LocationBarView::SecurityImageView::ShowInfoBubble() {
+ std::wstring text;
+ SkColor text_color;
+ model_->GetIconHoverText(&text, &text_color);
+
+ CPoint location(0, 0);
+ ChromeViews::View::ConvertPointToScreen(this, &location);
+ gfx::Rect bounds(location.x, location.y, GetWidth(), GetHeight());
+
+ ChromeViews::Label* label = new ChromeViews::Label(text);
+ label->SetMultiLine(true);
+ label->SetColor(text_color);
+ label->SetFont(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont).DeriveFont(2));
+ label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ label->SizeToFit(0);
+ DCHECK(info_bubble_ == NULL);
+ info_bubble_ = InfoBubble::Show(GetRootView()->GetViewContainer()->GetHWND(),
+ bounds, label, this);
+ show_info_bubble_task_ = NULL;
+}
+
+void LocationBarView::SecurityImageView::OnMouseMoved(
+ const ChromeViews::MouseEvent& event) {
+ if (show_info_bubble_task_) {
+ show_info_bubble_task_->Cancel();
+ show_info_bubble_task_ = NULL;
+ }
+
+ if (info_bubble_) {
+ // If an info bubble is currently showing, nothing to do.
+ return;
+ }
+
+ show_info_bubble_task_ = new ShowInfoBubbleTask(this);
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, show_info_bubble_task_,
+ kInfoBubbleHoverDelayMs);
+}
+
+void LocationBarView::SecurityImageView::OnMouseExited(
+ const ChromeViews::MouseEvent& event) {
+ if (show_info_bubble_task_) {
+ show_info_bubble_task_->Cancel();
+ show_info_bubble_task_ = NULL;
+ }
+
+ if (info_bubble_)
+ info_bubble_->Close();
+}
+
+bool LocationBarView::SecurityImageView::OnMousePressed(
+ const ChromeViews::MouseEvent& event) {
+ NavigationEntry* nav_entry =
+ BrowserList::GetLastActive()->GetSelectedTabContents()->
+ controller()->GetActiveEntry();
+ PageInfoWindow::Create(profile_,
+ nav_entry,
+ GetRootView()->GetViewContainer()->GetHWND(),
+ PageInfoWindow::SECURITY);
+ return true;
+}
+
+void LocationBarView::SecurityImageView::InfoBubbleClosing(
+ InfoBubble* info_bubble) {
+ info_bubble_ = NULL;
+}
+
+bool LocationBarView::OverrideAccelerator(
+ const ChromeViews::Accelerator& accelerator) {
+ return location_entry_->OverrideAccelerator(accelerator);
+}
diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h
new file mode 100644
index 0000000..84bfe32
--- /dev/null
+++ b/chrome/browser/views/location_bar_view.h
@@ -0,0 +1,424 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_LOCATION_BAR_VIEW_H__
+#define CHROME_BROWSER_VIEWS_LOCATION_BAR_VIEW_H__
+
+#include <string>
+
+#include "base/gfx/rect.h"
+#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/controller.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/toolbar_model.h"
+#include "chrome/browser/views/info_bubble.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/views/hwnd_view.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/label.h"
+#include "chrome/views/painter.h"
+
+class GURL;
+class Profile;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// LocationBarView class
+//
+// The LocationBarView class is a View subclass that paints the background
+// of the URL bar strip and contains its content.
+//
+/////////////////////////////////////////////////////////////////////////////
+class LocationBarView : public ChromeViews::View,
+ public AutocompleteEdit::Controller {
+ public:
+
+ class Delegate {
+ public:
+ // Should return the current tab contents.
+ virtual TabContents* GetTabContents() = 0;
+
+ // Called by the location bar view when the user starts typing in the edit.
+ // This forces our security style to be UNKNOWN for the duration of the
+ // editing.
+ virtual void OnInputInProgress(bool in_progress) = 0;
+ };
+
+ LocationBarView(Profile* profile,
+ CommandController* controller,
+ ToolbarModel* model_,
+ Delegate* delegate,
+ bool popup_window_mode);
+ virtual ~LocationBarView() { }
+
+ void Init();
+
+ // Returns whether this instance has been initialized by callin Init. Init can
+ // only be called when the receiving instance is attached to a view container.
+ bool IsInitialized() const;
+
+ // Updates the location bar. We also reset the bar's permanent text and
+ // security style, and, if |tab_for_state_restoring| is non-NULL, also restore
+ // saved state that the tab holds.
+ void Update(const TabContents* tab_for_state_restoring);
+
+ void SetProfile(Profile* profile);
+ Profile* profile() { return profile_; }
+
+ // Sizing functions
+ virtual void GetPreferredSize(CSize *out);
+
+ // Layout and Painting functions
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+ virtual void Layout();
+ virtual void Paint(ChromeCanvas* canvas);
+
+ // No focus border for the location bar, the caret is enough.
+ virtual void PaintFocusBorder(ChromeCanvas* canvas) { }
+
+ // Overridden from View so we can use <tab> to go into keyword search mode.
+ virtual bool CanProcessTabKeyEvents();
+
+ // Called when any ancestor changes its size, asks the AutocompleteEdit to
+ // close its popup.
+ virtual void VisibleBoundsInRootChanged();
+
+ // Event Handlers
+ virtual bool OnMousePressed(const ChromeViews::MouseEvent& event);
+ virtual bool OnMouseDragged(const ChromeViews::MouseEvent& event);
+ virtual void OnMouseReleased(const ChromeViews::MouseEvent& event,
+ bool canceled);
+
+ // AutocompleteEdit::Controller
+ virtual void OnAutocompleteAccept(const std::wstring& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const std::wstring& alternate_nav_url);
+ virtual void OnChanged();
+ virtual void OnInputInProgress(bool in_progress) {
+ delegate_->OnInputInProgress(in_progress);
+ }
+ virtual SkBitmap GetFavIcon() const;
+ virtual std::wstring GetTitle() const;
+
+ // Returns the MSAA role
+ bool GetAccessibleRole(VARIANT* role);
+
+ AutocompleteEdit* location_entry() {
+ return location_entry_.get();
+ }
+
+ std::wstring location_input() {
+ return location_input_;
+ }
+
+ WindowOpenDisposition disposition() {
+ return disposition_;
+ }
+
+ PageTransition::Type transition() {
+ return transition_;
+ }
+
+ // Shows a info bubble that tells the user what the omnibox is and allows
+ // to change the search providers.
+ void ShowFirstRunBubble();
+
+ // Overridden from View.
+ virtual bool OverrideAccelerator(const ChromeViews::Accelerator& accelerator) ;
+
+ static const int kTextVertMargin;
+ static const COLORREF kBackgroundColorByLevel[];
+
+ protected:
+ void Focus();
+
+ // Overridden from Chrome::View.
+ virtual bool ShouldLookupAccelerators(const ChromeViews::KeyEvent& e);
+
+ private:
+ // View used when the user has selected a keyword.
+ //
+ // SelectedKeywordView maintains two labels. One label contains the
+ // complete description of the keyword, the second contains a truncated
+ // version of the description. The second is used if there is not enough room
+ // to display the complete description.
+ class SelectedKeywordView : public ChromeViews::View {
+ public:
+ explicit SelectedKeywordView(Profile* profile);
+ virtual ~SelectedKeywordView();
+
+ void SetFont(const ChromeFont& font);
+
+ virtual void Paint(ChromeCanvas* canvas);
+
+ virtual void GetPreferredSize(CSize* out);
+ virtual void GetMinimumSize(CSize* out);
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+ virtual void Layout();
+
+ // The current keyword, or an empty string if no keyword is displayed.
+ void SetKeyword(const std::wstring& keyword);
+ std::wstring keyword() const { return keyword_; }
+
+ void set_profile(Profile* profile) { profile_ = profile; }
+
+ private:
+ // Returns the truncated version of description to use.
+ std::wstring CalculateMinString(const std::wstring& description);
+
+ // The keyword we're showing. If empty, no keyword is selected.
+ // NOTE: we don't cache the TemplateURL as it is possible for it to get
+ // deleted out from under us.
+ std::wstring keyword_;
+
+ // For painting the background.
+ ChromeViews::HorizontalPainter background_painter_;
+
+ // Label containing the complete description.
+ ChromeViews::Label full_label_;
+
+ // Label containing the partial description.
+ ChromeViews::Label partial_label_;
+
+ Profile* profile_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SelectedKeywordView);
+ };
+
+ // KeywordHintView is used to display a hint to the user when the selected
+ // url has a corresponding keyword.
+ //
+ // Internally KeywordHintView uses two labels to render the text, and draws
+ // the tab image itself.
+ //
+ // NOTE: This should really be called LocationBarKeywordHintView, but I
+ // couldn't bring myself to use such a long name.
+ class KeywordHintView : public ChromeViews::View {
+ public:
+ explicit KeywordHintView(Profile* profile);
+ virtual ~KeywordHintView();
+
+ void SetFont(const ChromeFont& font);
+
+ void SetColor(const SkColor& color);
+
+ void SetKeyword(const std::wstring& keyword);
+ std::wstring keyword() const { return keyword_; }
+
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void GetPreferredSize(CSize* out);
+ // The minimum size is just big enough to show the tab.
+ virtual void GetMinimumSize(CSize* out);
+ virtual void Layout();
+ void DidChangeBounds(const CRect& previous, const CRect& current);
+
+ void set_profile(Profile* profile) { profile_ = profile; }
+
+ private:
+ ChromeViews::Label leading_label_;
+ ChromeViews::Label trailing_label_;
+
+ // The keyword.
+ std::wstring keyword_;
+
+ Profile* profile_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(KeywordHintView);
+ };
+
+
+ class ShowInfoBubbleTask;
+ class ShowFirstRunBubbleTask;
+
+ // SecurityImageView is used to display the lock or warning icon when the
+ // current URL's scheme is https.
+ //
+ // If a message has been set with SetInfoBubbleText, it displays an info
+ // bubble when the mouse hovers on the image.
+ class SecurityImageView : public ChromeViews::ImageView,
+ public InfoBubbleDelegate {
+ public:
+ enum Image {
+ LOCK = 0,
+ WARNING
+ };
+
+ SecurityImageView(Profile* profile, ToolbarModel* model_);
+ virtual ~SecurityImageView();
+
+ // Sets the image that should be displayed.
+ void SetImageShown(Image image);
+
+ // Overridden from view for the mouse hovering.
+ virtual void OnMouseMoved(const ChromeViews::MouseEvent& event);
+ virtual void OnMouseExited(const ChromeViews::MouseEvent& event);
+ virtual bool OnMousePressed(const ChromeViews::MouseEvent& event);
+
+ // InfoBubbleDelegate
+ void InfoBubbleClosing(InfoBubble* info_bubble);
+ bool CloseOnEscape() { return true; }
+
+ void set_profile(Profile* profile) { profile_ = profile; }
+
+ private:
+ friend class ShowInfoBubbleTask;
+
+ void ShowInfoBubble();
+
+ // The lock icon shown when using HTTPS.
+ static SkBitmap* lock_icon_;
+
+ // The warning icon shown when HTTPS is broken.
+ static SkBitmap* warning_icon_;
+
+ // The currently shown info bubble if any.
+ InfoBubble* info_bubble_;
+
+ // A task used to display the info bubble when the mouse hovers on the image.
+ ShowInfoBubbleTask* show_info_bubble_task_;
+
+ Profile* profile_;
+
+ ToolbarModel* model_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SecurityImageView);
+ };
+
+ // Both Layout and OnChanged call into this. This updates the contents
+ // of the 3 views: selected_keyword, keyword_hint and type_search_view. If
+ // force_layout is true, or one of these views has changed in such a way as
+ // to necessitate a layout, layout occurs as well.
+ void DoLayout(bool force_layout);
+
+ // Returns the width in pixels of the contents of the edit.
+ int TextDisplayWidth();
+
+ // Returns true if the preferred size should be used for a view whose width
+ // is pref_width, the width of the text in the edit is text_width, and
+ // max_width is the maximum width of the edit. If this returns false, the
+ // minimum size of the view should be used.
+ bool UsePref(int pref_width, int text_width, int max_width);
+
+ // Returns true if the view needs to be resized. This determines whether the
+ // min or pref should be used, and returns true if the view is not at that
+ // size.
+ bool NeedsResize(View* view, int text_width, int max_width);
+
+ // Adjusts the keyword hint, selected keyword and type to search views
+ // based on the contents of the edit. Returns true if something changed that
+ // necessitates a layout.
+ bool AdjustHints(int text_width, int max_width);
+
+ // If View fits in the specified region, it is made visible and the
+ // bounds are adjusted appropriately. If the View does not fit, it is
+ // made invisible.
+ void LayoutView(bool leading, ChromeViews::View* view, int text_width,
+ int max_width, gfx::Rect* bounds);
+
+ // Sets the security icon to display. Note that no repaint is done.
+ void SetSecurityIcon(ToolbarModel::Icon icon);
+
+ // Sets the text that should be displayed in the info label and its associated
+ // tooltip text. Call with an empty string if the info label should be
+ // hidden.
+ void SetInfoText(const std::wstring& text,
+ SkColor text_color,
+ const std::wstring& tooltip_text);
+
+ // Sets the visibility of view to new_vis. Returns whether the visibility
+ // changed.
+ bool ToggleVisibility(bool new_vis, ChromeViews::View* view);
+
+ // Helper for the Mouse event handlers that does all the real work.
+ void OnMouseEvent(const ChromeViews::MouseEvent& event, UINT msg);
+
+ // Helper to show the first run info bubble.
+ void ShowFirstRunBubbleInternal();
+
+ // Current profile. Not owned by us.
+ Profile* profile_;
+
+ // The Autocomplete Edit field.
+ scoped_ptr<AutocompleteEdit> location_entry_;
+
+ // The command controller for this View.
+ CommandController* controller_;
+
+ // The model.
+ ToolbarModel* model_;
+
+ // Our delegate.
+ Delegate* delegate_;
+
+ // This is the string of text from the autocompletion session that the user
+ // entered or selected.
+ std::wstring location_input_;
+
+ // The user's desired disposition for how their input should be opened
+ WindowOpenDisposition disposition_;
+
+ // The transition type to use for the navigation
+ PageTransition::Type transition_;
+
+ // Font used by edit and some of the hints.
+ ChromeFont font_;
+
+ // Location_entry view wrapper
+ ChromeViews::HWNDView* location_entry_view_;
+
+ // The following views are used to provide hints and remind the user as to
+ // what is going in the edit. They are all added a children of the
+ // LocationBarView. At most one is visible at a time. Preference is
+ // given to the keyword_view_, then hint_view_, then type_to_search_view_.
+
+ // Shown if the user has selected a keyword.
+ SelectedKeywordView selected_keyword_view_;
+
+ // Shown if the selected url has a corresponding keyword.
+ KeywordHintView keyword_hint_view_;
+
+ // Shown if the text is not a keyword or url.
+ ChromeViews::Label type_to_search_view_;
+
+ // The view that shows the lock/warning when in HTTPS mode.
+ SecurityImageView security_image_view_;
+
+ // A label displayed after the lock icon to show some extra information.
+ ChromeViews::Label info_label_;
+
+ // When true, the location bar view is read only and also is has a slightly
+ // different presentation (font size / color). This is used for popups.
+ bool popup_window_mode_;
+
+ // Used schedule a task for the first run info bubble.
+ ScopedRunnableMethodFactory<LocationBarView> first_run_bubble_;
+};
+
+#endif // CHROME_BROWSER_VIEWS_LOCATION_BAR_VIEW_H__
diff --git a/chrome/browser/views/login_view.cc b/chrome/browser/views/login_view.cc
new file mode 100644
index 0000000..28cc8d0
--- /dev/null
+++ b/chrome/browser/views/login_view.cc
@@ -0,0 +1,149 @@
+// 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 <string>
+
+#include "chrome/browser/views/login_view.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/root_view.h"
+#include "chrome/views/text_field.h"
+
+#include "generated_resources.h"
+
+namespace ChromeViews {
+
+static const int kMessageWidth = 320;
+static const int kTextFieldStackHorizontalSpacing = 30;
+
+///////////////////////////////////////////////////////////////////////////////
+// LoginView, public:
+
+LoginView::LoginView(const std::wstring& explanation)
+ : username_field_(new TextField),
+ password_field_(new TextField(TextField::STYLE_PASSWORD)),
+ username_label_(new Label(
+ l10n_util::GetString(IDS_LOGIN_DIALOG_USERNAME_FIELD))),
+ password_label_(new Label(
+ l10n_util::GetString(IDS_LOGIN_DIALOG_PASSWORD_FIELD))),
+ message_label_(new Label(explanation)),
+ focus_grabber_factory_(this),
+ login_model_(NULL) {
+ message_label_->SetMultiLine(true);
+ message_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+
+ // Initialize the Grid Layout Manager used for this dialog box.
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ // Add the column set for the information message at the top of the dialog
+ // box.
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::FIXED, kMessageWidth, 0);
+
+ // Add the column set for the user name and password fields and labels.
+ const int labels_column_set_id = 1;
+ column_set = layout->AddColumnSet(labels_column_set_id);
+ column_set->AddPaddingColumn(0, kTextFieldStackHorizontalSpacing);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kTextFieldStackHorizontalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(message_label_);
+
+ layout->AddPaddingRow(0, kUnrelatedControlLargeVerticalSpacing);
+
+ layout->StartRow(0, labels_column_set_id);
+ layout->AddView(username_label_);
+ layout->AddView(username_field_);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, labels_column_set_id);
+ layout->AddView(password_label_);
+ layout->AddView(password_field_);
+
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+}
+
+LoginView::~LoginView() {
+ if (login_model_)
+ login_model_->SetObserver(NULL);
+}
+
+std::wstring LoginView::GetUsername() {
+ return username_field_->GetText();
+}
+
+std::wstring LoginView::GetPassword() {
+ return password_field_->GetText();
+}
+
+void LoginView::SetModel(LoginModel* model) {
+ login_model_ = model;
+ if (login_model_)
+ login_model_->SetObserver(this);
+}
+///////////////////////////////////////////////////////////////////////////////
+// LoginView, ChromeViews::View, ChromeViews::LoginModelObserver overrides:
+
+void LoginView::ViewHierarchyChanged(bool is_add, View *parent, View *child) {
+ if (is_add && child == this) {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ focus_grabber_factory_.NewRunnableMethod(&LoginView::FocusFirstField));
+ }
+}
+
+void LoginView::OnAutofillDataAvailable(const std::wstring& username,
+ const std::wstring& password) {
+ if (username_field_->GetText().empty()) {
+ username_field_->SetText(username);
+ password_field_->SetText(password);
+ username_field_->SelectAll();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// LoginView, private:
+
+void LoginView::FocusFirstField() {
+ username_field_->RequestFocus();
+}
+
+} // namespace
diff --git a/chrome/browser/views/login_view.h b/chrome/browser/views/login_view.h
new file mode 100644
index 0000000..93c3003
--- /dev/null
+++ b/chrome/browser/views/login_view.h
@@ -0,0 +1,108 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_LOGIN_VIEW_H__
+#define CHROME_BROWSER_VIEWS_LOGIN_VIEW_H__
+
+#include "base/task.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+class Label;
+class TextField;
+class LoginModel;
+
+// Simple Model & Observer interfaces for a LoginView to facilitate exchanging
+// information.
+class LoginModelObserver {
+ public:
+ // Called by the model when a username,password pair has been identified
+ // as a match for the pending login prompt.
+ virtual void OnAutofillDataAvailable(const std::wstring& username,
+ const std::wstring& password) = 0;
+};
+
+class LoginModel {
+ public:
+ // Set the observer interested in the data from the model.
+ // observer can be null, signifying there is no longer any observer
+ // interested in the data.
+ virtual void SetObserver(LoginModelObserver* observer) = 0;
+};
+
+// This class is responsible for displaying the contents of a login window
+// for HTTP/FTP authentication.
+class LoginView : public View, public LoginModelObserver {
+ public:
+ explicit LoginView(const std::wstring& explanation);
+ virtual ~LoginView();
+
+ // Access the data in the username/password text fields.
+ std::wstring GetUsername();
+ std::wstring GetPassword();
+
+ // LoginModelObserver implementation.
+ virtual void OnAutofillDataAvailable(const std::wstring& username,
+ const std::wstring& password);
+
+ // Sets the model. This lets the observer notify the model
+ // when it has been closed / freed, so the model should no longer try and
+ // contact it. The view does not own the model, and it is the responsibility
+ // of the caller to inform this view if the model is deleted.
+ void SetModel(LoginModel* model);
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+
+ private:
+ void FocusFirstField();
+
+ // Non-owning refs to the input text fields.
+ TextField* username_field_;
+ TextField* password_field_;
+
+ // Button labels
+ Label* username_label_;
+ Label* password_label_;
+
+ // Authentication message.
+ Label* message_label_;
+
+ // If not null, points to a model we need to notify of our own destruction
+ // so it doesn't try and access this when its too late.
+ LoginModel* login_model_;
+
+ ScopedRunnableMethodFactory<LoginView> focus_grabber_factory_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LoginView);
+};
+
+} // namespace
+#endif // CHROME_BROWSER_VIEWS_LOGIN_VIEW_H__
diff --git a/chrome/browser/views/options/advanced_contents_view.cc b/chrome/browser/views/options/advanced_contents_view.cc
new file mode 100644
index 0000000..32359a9
--- /dev/null
+++ b/chrome/browser/views/options/advanced_contents_view.cc
@@ -0,0 +1,1203 @@
+// 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/views/options/advanced_contents_view.h"
+
+#include <windows.h>
+
+#include <cryptuiapi.h>
+#pragma comment(lib, "cryptui.lib")
+#include <shellapi.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/gfx/native_theme.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download_manager.h"
+#include "chrome/browser/gears_integration.h"
+#include "chrome/browser/metrics_service.h"
+#include "chrome/browser/net/dns_global.h"
+#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/restart_message_box.h"
+#include "chrome/browser/views/options/cookies_view.h"
+#include "chrome/browser/views/options/language_combobox_model.h"
+#include "chrome/common/filter_policy.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/pref_member.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/background.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/scroll_view.h"
+#include "net/base/ssl_config_service.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// A background object that paints the scrollable list background,
+// which may be rendered by the system visual styles system.
+class ListBackground : public ChromeViews::Background {
+ public:
+ explicit ListBackground() {
+ SkColor list_color =
+ gfx::NativeTheme::instance()->GetThemeColorWithDefault(
+ gfx::NativeTheme::LIST, 1, TS_NORMAL, TMT_FILLCOLOR, COLOR_WINDOW);
+ SetNativeControlColor(list_color);
+ }
+ virtual ~ListBackground() {}
+
+ virtual void Paint(ChromeCanvas* canvas, ChromeViews::View* view) const {
+ HDC dc = canvas->beginPlatformPaint();
+ CRect lb;
+ view->GetLocalBounds(&lb, true);
+ gfx::NativeTheme::instance()->PaintListBackground(dc, true, &lb);
+ canvas->endPlatformPaint();
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ListBackground);
+};
+
+} // namespace
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedSection
+// A convenience view for grouping advanced options together into related
+// sections.
+//
+class AdvancedSection : public OptionsPageView {
+ public:
+ AdvancedSection(Profile* profile, const std::wstring& title);
+ virtual ~AdvancedSection() {}
+
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+
+ protected:
+ // Convenience helpers to add different kinds of ColumnSets for specific
+ // types of layout.
+ void AddWrappingColumnSet(ChromeViews::GridLayout* layout, int id);
+ void AddDependentTwoColumnSet(ChromeViews::GridLayout* layout, int id);
+ void AddTwoColumnSet(ChromeViews::GridLayout* layout, int id);
+ void AddIndentedColumnSet(ChromeViews::GridLayout* layout, int id);
+
+ // Convenience helpers for adding controls to specific layouts in an
+ // aesthetically pleasing way.
+ void AddWrappingCheckboxRow(ChromeViews::GridLayout* layout,
+ ChromeViews::CheckBox* checkbox,
+ int id,
+ bool related_follows);
+ void AddWrappingLabelRow(ChromeViews::GridLayout* layout,
+ ChromeViews::Label* label,
+ int id,
+ bool related_follows);
+ void AddTwoColumnRow(ChromeViews::GridLayout* layout,
+ ChromeViews::Label* label,
+ ChromeViews::View* control,
+ bool control_stretches, // Whether or not the control
+ // expands to fill the width.
+ int id,
+ bool related_follows);
+ void AddLeadingControl(ChromeViews::GridLayout* layout,
+ ChromeViews::View* control,
+ int id,
+ bool related_follows);
+ void AddIndentedControl(ChromeViews::GridLayout* layout,
+ ChromeViews::View* control,
+ int id,
+ bool related_follows);
+ void AddSpacing(ChromeViews::GridLayout* layout, bool related_follows);
+
+ // OptionsPageView overrides:
+ virtual void InitControlLayout();
+
+ // The View that contains the contents of the section.
+ ChromeViews::View* contents_;
+
+ private:
+ // The section title.
+ ChromeViews::Label* title_label_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AdvancedSection);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedSection, public:
+
+AdvancedSection::AdvancedSection(Profile* profile,
+ const std::wstring& title)
+ : contents_(NULL),
+ title_label_(new ChromeViews::Label(title)),
+ OptionsPageView(profile) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont title_font =
+ rb.GetFont(ResourceBundle::BaseFont).DeriveFont(0, ChromeFont::BOLD);
+ title_label_->SetFont(title_font);
+
+ SkColor title_color = gfx::NativeTheme::instance()->GetThemeColorWithDefault(
+ gfx::NativeTheme::BUTTON, BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR,
+ COLOR_WINDOWTEXT);
+ title_label_->SetColor(title_color);
+}
+
+void AdvancedSection::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+ contents_->Layout();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedSection, protected:
+
+void AdvancedSection::AddWrappingColumnSet(ChromeViews::GridLayout* layout,
+ int id) {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ ColumnSet* column_set = layout->AddColumnSet(id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+}
+
+void AdvancedSection::AddDependentTwoColumnSet(ChromeViews::GridLayout* layout,
+ int id) {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ ColumnSet* column_set = layout->AddColumnSet(id);
+ column_set->AddPaddingColumn(0, ChromeViews::CheckBox::GetTextIndent());
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing);
+}
+
+void AdvancedSection::AddTwoColumnSet(ChromeViews::GridLayout* layout,
+ int id) {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ ColumnSet* column_set = layout->AddColumnSet(id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+}
+
+void AdvancedSection::AddIndentedColumnSet(ChromeViews::GridLayout* layout,
+ int id) {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ ColumnSet* column_set = layout->AddColumnSet(id);
+ column_set->AddPaddingColumn(0, ChromeViews::CheckBox::GetTextIndent());
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+}
+
+void AdvancedSection::AddWrappingCheckboxRow(ChromeViews::GridLayout* layout,
+ ChromeViews::CheckBox* checkbox,
+ int id,
+ bool related_follows) {
+ checkbox->SetMultiLine(true);
+ layout->StartRow(0, id);
+ layout->AddView(checkbox);
+ AddSpacing(layout, related_follows);
+}
+
+void AdvancedSection::AddWrappingLabelRow(ChromeViews::GridLayout* layout,
+ ChromeViews::Label* label,
+ int id,
+ bool related_follows) {
+ label->SetMultiLine(true);
+ label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ layout->StartRow(0, id);
+ layout->AddView(label);
+ AddSpacing(layout, related_follows);
+}
+
+void AdvancedSection::AddTwoColumnRow(ChromeViews::GridLayout* layout,
+ ChromeViews::Label* label,
+ ChromeViews::View* control,
+ bool control_stretches,
+ int id,
+ bool related_follows) {
+ label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ layout->StartRow(0, id);
+ layout->AddView(label);
+ if (control_stretches) {
+ layout->AddView(control);
+ } else {
+ layout->AddView(control, 1, 1, ChromeViews::GridLayout::LEADING,
+ ChromeViews::GridLayout::CENTER);
+ }
+ AddSpacing(layout, related_follows);
+}
+
+void AdvancedSection::AddLeadingControl(ChromeViews::GridLayout* layout,
+ ChromeViews::View* control,
+ int id,
+ bool related_follows) {
+ using ChromeViews::GridLayout;
+ layout->StartRow(0, id);
+ layout->AddView(control, 1, 1, GridLayout::LEADING, GridLayout::CENTER);
+ AddSpacing(layout, related_follows);
+}
+
+void AdvancedSection::AddSpacing(ChromeViews::GridLayout* layout,
+ bool related_follows) {
+ layout->AddPaddingRow(0, related_follows ? kRelatedControlVerticalSpacing
+ : kUnrelatedControlVerticalSpacing);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedSection, OptionsPageView overrides:
+
+void AdvancedSection::InitControlLayout() {
+ contents_ = new ChromeViews::View;
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = new GridLayout(this);
+ SetLayoutManager(layout);
+
+ const int single_column_layout_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_layout_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ const int inset_column_layout_id = 1;
+ column_set = layout->AddColumnSet(inset_column_layout_id);
+ column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_layout_id);
+ layout->AddView(title_label_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, inset_column_layout_id);
+ layout->AddView(contents_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// GeneralSection
+
+class GeneralSection : public AdvancedSection,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::LinkController {
+ public:
+ explicit GeneralSection(Profile* profile);
+ virtual ~GeneralSection() {}
+
+ // Overridden from ChromeViews::NativeButton::Listener:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // Overridden from ChromeViews::LinkController:
+ virtual void LinkActivated(ChromeViews::Link* source, int event_flags);
+
+ // Overridden from ChromeViews::View:
+ virtual void Layout();
+
+ protected:
+ // OptionsPageView overrides:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+
+ private:
+ // Controls for this section:
+ ChromeViews::Label* reset_file_handlers_label_;
+ ChromeViews::NativeButton* reset_file_handlers_button_;
+ ChromeViews::CheckBox* reporting_enabled_checkbox_;
+ ChromeViews::Link* learn_more_link_;
+
+ // Preferences for this section:
+ StringPrefMember auto_open_files_;
+ BooleanPrefMember enable_metrics_recording_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(GeneralSection);
+};
+
+GeneralSection::GeneralSection(Profile* profile)
+ : reset_file_handlers_label_(NULL),
+ reset_file_handlers_button_(NULL),
+ reporting_enabled_checkbox_(NULL),
+ learn_more_link_(NULL),
+ AdvancedSection(profile,
+ l10n_util::GetString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_GENERAL)) {
+}
+
+void GeneralSection::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == reset_file_handlers_button_) {
+ profile()->GetDownloadManager()->ResetAutoOpenFiles();
+ UserMetricsRecordAction(L"Options_ResetAutoOpenFiles",
+ profile()->GetPrefs());
+ } else if (sender == reporting_enabled_checkbox_) {
+ bool enabled = reporting_enabled_checkbox_->IsSelected();
+ // Do what we can, but we might not be able to get what was asked for.
+ bool done = g_browser_process->metrics_service()->EnableReporting(enabled);
+ if (!done) {
+ enabled = !enabled;
+ done = g_browser_process->metrics_service()->EnableReporting(enabled);
+ DCHECK(done);
+ reporting_enabled_checkbox_->SetIsSelected(enabled);
+ } else {
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_MetricsReportingCheckbox_Enable",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_MetricsReportingCheckbox_Disable",
+ profile()->GetPrefs());
+ }
+ RestartMessageBox::ShowMessageBox(GetRootWindow());
+ }
+ enable_metrics_recording_.SetValue(enabled);
+ }
+}
+
+void GeneralSection::LinkActivated(ChromeViews::Link* source,
+ int event_flags) {
+ if (source == learn_more_link_) {
+ // We open a new browser window so the Options dialog doesn't get lost
+ // behind other windows.
+ Browser* browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile(),
+ BrowserType::TABBED_BROWSER,
+ std::wstring());
+ browser->OpenURL(
+ GURL(l10n_util::GetString(IDS_LEARN_MORE_HELPMAKECHROMEBETTER_URL)),
+ NEW_WINDOW,
+ PageTransition::LINK);
+ }
+}
+
+void GeneralSection::Layout() {
+ // We override this to try and set the width of the enable logging checkbox
+ // to the width of the parent less some fudging since the checkbox's
+ // preferred size calculation code is dependent on its width, and if we don't
+ // do this then it will return 0 as a preferred width when GridLayout (called
+ // from View::Layout) tries to access it.
+ ChromeViews::View* parent = GetParent();
+ if (parent && parent->GetWidth()) {
+ const int parent_width = parent->GetWidth();
+ reporting_enabled_checkbox_->SetBounds(0, 0, parent_width - 20, 0);
+ }
+ View::Layout();
+}
+
+void GeneralSection::InitControlLayout() {
+ AdvancedSection::InitControlLayout();
+
+ reset_file_handlers_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_AUTOOPENFILETYPES_INFO));
+ reset_file_handlers_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_AUTOOPENFILETYPES_RESETTODEFAULT));
+ reset_file_handlers_button_->SetListener(this);
+ reporting_enabled_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_ENABLE_LOGGING));
+ reporting_enabled_checkbox_->SetMultiLine(true);
+ reporting_enabled_checkbox_->SetListener(this);
+ learn_more_link_ =
+ new ChromeViews::Link(l10n_util::GetString(IDS_LEARN_MORE));
+ learn_more_link_->SetController(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ GridLayout* layout = new GridLayout(contents_);
+ contents_->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ AddWrappingColumnSet(layout, single_column_view_set_id);
+ const int dependent_labeled_field_set_id = 1;
+ AddDependentTwoColumnSet(layout, dependent_labeled_field_set_id);
+ const int indented_view_set_id = 2;
+ AddIndentedColumnSet(layout, indented_view_set_id);
+
+ // File Handlers.
+ AddWrappingLabelRow(layout, reset_file_handlers_label_,
+ single_column_view_set_id, true);
+ AddLeadingControl(layout, reset_file_handlers_button_, indented_view_set_id,
+ false);
+ AddLeadingControl(layout, reporting_enabled_checkbox_,
+ single_column_view_set_id, true);
+ AddLeadingControl(layout, learn_more_link_, indented_view_set_id, false);
+
+ // Init member prefs so we can update the controls if prefs change.
+ auto_open_files_.Init(prefs::kDownloadExtensionsToOpen, profile()->GetPrefs(),
+ this);
+ enable_metrics_recording_.Init(prefs::kMetricsReportingEnabled,
+ g_browser_process->local_state(), this);
+}
+
+void GeneralSection::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kDownloadExtensionsToOpen) {
+ bool enabled =
+ profile()->GetDownloadManager()->HasAutoOpenFileTypesRegistered();
+ reset_file_handlers_label_->SetEnabled(enabled);
+ reset_file_handlers_button_->SetEnabled(enabled);
+ }
+ if (!pref_name ||*pref_name == prefs::kMetricsReportingEnabled) {
+ bool enabled = enable_metrics_recording_.GetValue();
+ bool done = g_browser_process->metrics_service()->EnableReporting(enabled);
+ if (!done) {
+ enabled = !enabled;
+ done = g_browser_process->metrics_service()->EnableReporting(enabled);
+ DCHECK(done);
+ }
+ reporting_enabled_checkbox_->SetIsSelected(enabled);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ContentSection
+
+class ContentSection : public AdvancedSection,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ explicit ContentSection(Profile* profile);
+ virtual ~ContentSection() {}
+
+ // Overridden from ChromeViews::NativeButton::Listener:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ protected:
+ // OptionsPageView overrides:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+
+ private:
+ // Controls for this section:
+ ChromeViews::CheckBox* popup_blocked_notification_checkbox_;
+ ChromeViews::Label* gears_label_;
+ ChromeViews::NativeButton* gears_settings_button_;
+
+ BooleanPrefMember disable_popup_blocked_notification_pref_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentSection);
+};
+
+ContentSection::ContentSection(Profile* profile)
+ : popup_blocked_notification_checkbox_(NULL),
+ gears_label_(NULL),
+ gears_settings_button_(NULL),
+ AdvancedSection(profile,
+ l10n_util::GetString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_CONTENT)) {
+}
+
+void ContentSection::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == popup_blocked_notification_checkbox_) {
+ bool notification_disabled =
+ popup_blocked_notification_checkbox_->IsSelected();
+ if (notification_disabled) {
+ UserMetricsRecordAction(L"Options_BlockAllPopups_Disable",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_BlockAllPopups_Enable",
+ profile()->GetPrefs());
+ }
+ disable_popup_blocked_notification_pref_.SetValue(!notification_disabled);
+ } else if (sender == gears_settings_button_) {
+ UserMetricsRecordAction(L"Options_GearsSettings", NULL);
+ GearsSettingsPressed(GetAncestor(GetViewContainer()->GetHWND(), GA_ROOT));
+ }
+}
+
+void ContentSection::InitControlLayout() {
+ AdvancedSection::InitControlLayout();
+
+ popup_blocked_notification_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_SHOWPOPUPBLOCKEDNOTIFICATION));
+ popup_blocked_notification_checkbox_->SetListener(this);
+
+ gears_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_GEARSSETTINGS_GROUP_NAME));
+ gears_settings_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_GEARSSETTINGS_CONFIGUREGEARS_BUTTON));
+ gears_settings_button_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ GridLayout* layout = new GridLayout(contents_);
+ contents_->SetLayoutManager(layout);
+
+ const int col_id = 0;
+ AddWrappingColumnSet(layout, col_id);
+ const int two_col_id = 1;
+ AddTwoColumnSet(layout, two_col_id);
+
+ AddWrappingCheckboxRow(layout, popup_blocked_notification_checkbox_,
+ col_id, true);
+ AddTwoColumnRow(layout, gears_label_, gears_settings_button_, false,
+ two_col_id, false);
+
+ // Init member prefs so we can update the controls if prefs change.
+ disable_popup_blocked_notification_pref_.Init(prefs::kBlockPopups,
+ profile()->GetPrefs(), this);
+}
+
+void ContentSection::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kBlockPopups) {
+ popup_blocked_notification_checkbox_->SetIsSelected(
+ !disable_popup_blocked_notification_pref_.GetValue());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SecuritySection
+
+class MixedContentComboModel : public ChromeViews::ComboBox::Model {
+ public:
+ MixedContentComboModel() {}
+
+ // Return the number of items in the combo box.
+ virtual int GetItemCount(ChromeViews::ComboBox* source) {
+ return 3;
+ }
+
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index) {
+ const int kStringIDs[] = {
+ IDS_OPTIONS_INCLUDE_MIXED_CONTENT,
+ IDS_OPTIONS_INCLUDE_MIXED_CONTENT_IMAGE_ONLY,
+ IDS_OPTIONS_INCLUDE_NO_MIXED_CONTENT
+ };
+ if (index >= 0 && index < arraysize(kStringIDs))
+ return l10n_util::GetString(kStringIDs[index]);
+
+ NOTREACHED();
+ return L"";
+ }
+
+ static int FilterPolicyToIndex(FilterPolicy::Type policy) {
+ return policy;
+ }
+
+ static FilterPolicy::Type IndexToFilterPolicy(int index) {
+ if (FilterPolicy::ValidType(index))
+ return FilterPolicy::FromInt(index);
+
+ NOTREACHED();
+ return FilterPolicy::DONT_FILTER;
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(MixedContentComboModel);
+};
+
+class CookieBehaviorComboModel : public ChromeViews::ComboBox::Model {
+ public:
+ CookieBehaviorComboModel() {}
+
+ // Return the number of items in the combo box.
+ virtual int GetItemCount(ChromeViews::ComboBox* source) {
+ return 3;
+ }
+
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index) {
+ const int kStringIDs[] = {
+ IDS_OPTIONS_COOKIES_ACCEPT_ALL_COOKIES,
+ IDS_OPTIONS_COOKIES_RESTRICT_THIRD_PARTY_COOKIES,
+ IDS_OPTIONS_COOKIES_BLOCK_ALL_COOKIES
+ };
+ if (index >= 0 && index < arraysize(kStringIDs))
+ return l10n_util::GetString(kStringIDs[index]);
+
+ NOTREACHED();
+ return L"";
+ }
+
+ static int CookiePolicyToIndex(CookiePolicy::Type policy) {
+ return policy;
+ }
+
+ static CookiePolicy::Type IndexToCookiePolicy(int index) {
+ if (CookiePolicy::ValidType(index))
+ return CookiePolicy::FromInt(index);
+
+ NOTREACHED();
+ return CookiePolicy::ALLOW_ALL_COOKIES;
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(CookieBehaviorComboModel);
+};
+
+class SecuritySection : public AdvancedSection,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::ComboBox::Listener {
+ public:
+ explicit SecuritySection(Profile* profile);
+ virtual ~SecuritySection() {}
+
+ // Overridden from ChromeViews::NativeButton::Listener:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // Overridden from ChromeViews::ComboBox::Listener:
+ virtual void ItemChanged(ChromeViews::ComboBox* sender,
+ int prev_index,
+ int new_index);
+
+ protected:
+ // OptionsPageView overrides:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+
+ private:
+ // Controls for this section:
+ ChromeViews::CheckBox* enable_safe_browsing_checkbox_;
+ ChromeViews::Label* ssl_info_label_;
+ ChromeViews::CheckBox* enable_ssl2_checkbox_;
+ ChromeViews::CheckBox* check_for_cert_revocation_checkbox_;
+ ChromeViews::Label* mixed_content_info_label_;
+ ChromeViews::ComboBox* mixed_content_combobox_;
+ ChromeViews::Label* manage_certificates_label_;
+ ChromeViews::NativeButton* manage_certificates_button_;
+ ChromeViews::Label* cookie_behavior_label_;
+ ChromeViews::ComboBox* cookie_behavior_combobox_;
+ ChromeViews::NativeButton* show_cookies_button_;
+
+ // The contents of the mixed content combobox.
+ scoped_ptr<MixedContentComboModel> mixed_content_model_;
+
+ // Dummy for now. Used to populate cookies models.
+ scoped_ptr<CookieBehaviorComboModel> allow_cookies_model_;
+
+ BooleanPrefMember safe_browsing_;
+ IntegerPrefMember filter_mixed_content_;
+ IntegerPrefMember cookie_behavior_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SecuritySection);
+};
+
+SecuritySection::SecuritySection(Profile* profile)
+ : enable_safe_browsing_checkbox_(NULL),
+ ssl_info_label_(NULL),
+ enable_ssl2_checkbox_(NULL),
+ check_for_cert_revocation_checkbox_(NULL),
+ mixed_content_info_label_(NULL),
+ mixed_content_combobox_(NULL),
+ manage_certificates_label_(NULL),
+ manage_certificates_button_(NULL),
+ cookie_behavior_label_(NULL),
+ cookie_behavior_combobox_(NULL),
+ show_cookies_button_(NULL),
+ AdvancedSection(profile,
+ l10n_util::GetString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_SECURITY)) {
+}
+
+void SecuritySection::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == enable_safe_browsing_checkbox_) {
+ bool enabled = enable_safe_browsing_checkbox_->IsSelected();
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_SafeBrowsingCheckbox_Enable",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_SafeBrowsingCheckbox_Disable",
+ profile()->GetPrefs());
+ }
+ safe_browsing_.SetValue(enabled);
+ SafeBrowsingService* safe_browsing_service =
+ g_browser_process->resource_dispatcher_host()->safe_browsing_service();
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ safe_browsing_service, &SafeBrowsingService::OnEnable, enabled));
+ } else if (sender == enable_ssl2_checkbox_) {
+ bool enabled = enable_ssl2_checkbox_->IsSelected();
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_SSL2_Enable", NULL);
+ } else {
+ UserMetricsRecordAction(L"Options_SSL2_Disable", NULL);
+ }
+ net::SSLConfigService::SetSSL2Enabled(enabled);
+ } else if (sender == check_for_cert_revocation_checkbox_) {
+ bool enabled = check_for_cert_revocation_checkbox_->IsSelected();
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_CheckCertRevocation_Enable", NULL);
+ } else {
+ UserMetricsRecordAction(L"Options_CheckCertRevocation_Disable", NULL);
+ }
+ net::SSLConfigService::SetRevCheckingEnabled(enabled);
+ } else if (sender == manage_certificates_button_) {
+ UserMetricsRecordAction(L"Options_ManagerCerts", NULL);
+ CRYPTUI_CERT_MGR_STRUCT cert_mgr = { 0 };
+ cert_mgr.dwSize = sizeof(CRYPTUI_CERT_MGR_STRUCT);
+ cert_mgr.hwndParent = GetRootWindow();
+ ::CryptUIDlgCertMgr(&cert_mgr);
+ } else if (sender == show_cookies_button_) {
+ UserMetricsRecordAction(L"Options_ShowCookies", NULL);
+ CookiesView::ShowCookiesWindow(profile());
+ }
+}
+
+void SecuritySection::ItemChanged(ChromeViews::ComboBox* sender,
+ int prev_index,
+ int new_index) {
+ if (sender == mixed_content_combobox_) {
+ // TODO(jcampan): bug #1112812: change this to the real enum once we have
+ // piped the images only filtering.
+ FilterPolicy::Type filter_policy =
+ MixedContentComboModel::IndexToFilterPolicy(new_index);
+ const wchar_t* kUserMetrics[] = {
+ L"Options_FilterNone",
+ L"Options_FilterAllExceptImages",
+ L"Options_FilterAll"
+ };
+ DCHECK(filter_policy >= 0 && filter_policy < arraysize(kUserMetrics));
+ UserMetricsRecordAction(kUserMetrics[filter_policy], profile()->GetPrefs());
+ filter_mixed_content_.SetValue(filter_policy);
+ } else if (sender == cookie_behavior_combobox_) {
+ CookiePolicy::Type cookie_policy =
+ CookieBehaviorComboModel::IndexToCookiePolicy(new_index);
+ const wchar_t* kUserMetrics[] = {
+ L"Options_AllowAllCookies",
+ L"Options_BlockThirdPartyCookies",
+ L"Options_BlockAllCookies"
+ };
+ DCHECK(cookie_policy >= 0 && cookie_policy < arraysize(kUserMetrics));
+ UserMetricsRecordAction(kUserMetrics[cookie_policy], profile()->GetPrefs());
+ this->cookie_behavior_.SetValue(cookie_policy);
+ }
+}
+
+void SecuritySection::InitControlLayout() {
+ AdvancedSection::InitControlLayout();
+
+ enable_safe_browsing_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_SAFEBROWSING_ENABLEPROTECTION));
+ enable_safe_browsing_checkbox_->SetListener(this);
+ ssl_info_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_SSL_GROUP_DESCRIPTION));
+ enable_ssl2_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_SSL_USESSL2));
+ enable_ssl2_checkbox_->SetListener(this);
+ check_for_cert_revocation_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_SSL_CHECKREVOCATION));
+ check_for_cert_revocation_checkbox_->SetListener(this);
+ mixed_content_info_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_MIXED_CONTENT_LABEL));
+ mixed_content_model_.reset(new MixedContentComboModel);
+ mixed_content_combobox_ = new ChromeViews::ComboBox(
+ mixed_content_model_.get());
+ mixed_content_combobox_->SetListener(this);
+ manage_certificates_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_CERTIFICATES_LABEL));
+ manage_certificates_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_CERTIFICATES_MANAGE_BUTTON));
+ manage_certificates_button_->SetListener(this);
+ cookie_behavior_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_COOKIES_ACCEPT_LABEL));
+ allow_cookies_model_.reset(new CookieBehaviorComboModel);
+ cookie_behavior_combobox_ = new ChromeViews::ComboBox(
+ allow_cookies_model_.get());
+ cookie_behavior_combobox_->SetListener(this);
+ show_cookies_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_COOKIES_SHOWCOOKIES));
+ show_cookies_button_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ GridLayout* layout = new GridLayout(contents_);
+ contents_->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ AddWrappingColumnSet(layout, single_column_view_set_id);
+ const int dependent_labeled_field_set_id = 1;
+ AddDependentTwoColumnSet(layout, dependent_labeled_field_set_id);
+ const int double_column_view_set_id = 2;
+ AddTwoColumnSet(layout, double_column_view_set_id);
+ const int indented_column_set_id = 3;
+ AddIndentedColumnSet(layout, indented_column_set_id);
+
+ // Safe browsing controls.
+ AddWrappingCheckboxRow(layout, enable_safe_browsing_checkbox_,
+ single_column_view_set_id, false);
+
+ // SSL connection controls and Certificates.
+ AddWrappingLabelRow(layout, manage_certificates_label_,
+ single_column_view_set_id, true);
+ AddLeadingControl(layout, manage_certificates_button_,
+ indented_column_set_id, false);
+ AddWrappingLabelRow(layout, ssl_info_label_, single_column_view_set_id,
+ true);
+ AddWrappingCheckboxRow(layout, enable_ssl2_checkbox_,
+ indented_column_set_id, true);
+ AddWrappingCheckboxRow(layout, check_for_cert_revocation_checkbox_,
+ indented_column_set_id, false);
+
+ // Mixed content controls.
+ AddWrappingLabelRow(layout, mixed_content_info_label_,
+ single_column_view_set_id, true);
+ AddLeadingControl(layout, mixed_content_combobox_,
+ indented_column_set_id, false);
+
+ // Cookies.
+ AddWrappingLabelRow(layout, cookie_behavior_label_, single_column_view_set_id,
+ true);
+ AddLeadingControl(layout, cookie_behavior_combobox_, indented_column_set_id,
+ true);
+ AddLeadingControl(layout, show_cookies_button_, indented_column_set_id,
+ false);
+
+ // Init member prefs so we can update the controls if prefs change.
+ safe_browsing_.Init(prefs::kSafeBrowsingEnabled, profile()->GetPrefs(), this);
+ filter_mixed_content_.Init(prefs::kMixedContentFiltering,
+ profile()->GetPrefs(), this);
+ cookie_behavior_.Init(prefs::kCookieBehavior, profile()->GetPrefs(), this);
+}
+
+// This method is called with a null pref_name when the dialog is initialized.
+void SecuritySection::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kMixedContentFiltering) {
+ mixed_content_combobox_->SetSelectedItem(
+ MixedContentComboModel::FilterPolicyToIndex(
+ FilterPolicy::FromInt(filter_mixed_content_.GetValue())));
+ }
+ if (!pref_name || *pref_name == prefs::kCookieBehavior) {
+ cookie_behavior_combobox_->SetSelectedItem(
+ CookieBehaviorComboModel::CookiePolicyToIndex(
+ CookiePolicy::FromInt(cookie_behavior_.GetValue())));
+ }
+ if (!pref_name || *pref_name == prefs::kSafeBrowsingEnabled)
+ enable_safe_browsing_checkbox_->SetIsSelected(safe_browsing_.GetValue());
+ // These SSL options are system settings and stored in the OS.
+ if (!pref_name) {
+ net::SSLConfig config;
+ if (net::SSLConfigService::GetSSLConfigNow(&config)) {
+ enable_ssl2_checkbox_->SetIsSelected(config.ssl2_enabled);
+ check_for_cert_revocation_checkbox_->SetIsSelected(
+ config.rev_checking_enabled);
+ } else {
+ enable_ssl2_checkbox_->SetEnabled(false);
+ check_for_cert_revocation_checkbox_->SetEnabled(false);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NetworkSection
+
+namespace {
+
+// A helper method that opens the Internet Options control panel dialog with
+// the Connections tab selected.
+class OpenConnectionDialogTask : public Task {
+ public:
+ OpenConnectionDialogTask() {}
+
+ virtual void Run() {
+ // Using rundll32 seems better than LaunchConnectionDialog which causes a
+ // new dialog to be made for each call. rundll32 uses the same global
+ // dialog and it seems to share with the shortcut in control panel.
+ std::wstring rundll32;
+ PathService::Get(base::DIR_SYSTEM, &rundll32);
+ file_util::AppendToPath(&rundll32, L"rundll32.exe");
+
+ std::wstring shell32dll;
+ PathService::Get(base::DIR_SYSTEM, &shell32dll);
+ file_util::AppendToPath(&shell32dll, L"shell32.dll");
+
+ std::wstring inetcpl;
+ PathService::Get(base::DIR_SYSTEM, &inetcpl);
+ file_util::AppendToPath(&inetcpl, L"inetcpl.cpl,,4");
+
+ std::wstring args(shell32dll);
+ args.append(L",Control_RunDLL ");
+ args.append(inetcpl);
+
+ ShellExecute(NULL, L"open", rundll32.c_str(), args.c_str(), NULL,
+ SW_SHOWNORMAL);
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(OpenConnectionDialogTask);
+};
+
+} // namespace
+
+class NetworkSection : public AdvancedSection,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ explicit NetworkSection(Profile* profile);
+ virtual ~NetworkSection() {}
+
+ // Overridden from ChromeViews::NativeButton::Listener:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ protected:
+ // OptionsPageView overrides:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+
+ private:
+ // Controls for this section:
+ ChromeViews::Label* change_proxies_label_;
+ ChromeViews::NativeButton* change_proxies_button_;
+ ChromeViews::CheckBox* enable_link_doctor_checkbox_;
+ ChromeViews::CheckBox* enable_dns_prefetching_checkbox_;
+
+ BooleanPrefMember alternate_error_pages_;
+ BooleanPrefMember dns_prefetch_enabled_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NetworkSection);
+};
+
+NetworkSection::NetworkSection(Profile* profile)
+ : change_proxies_label_(NULL),
+ change_proxies_button_(NULL),
+ enable_link_doctor_checkbox_(NULL),
+ enable_dns_prefetching_checkbox_(NULL),
+ AdvancedSection(profile,
+ l10n_util::GetString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_NETWORK)) {
+}
+
+void NetworkSection::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == change_proxies_button_) {
+ UserMetricsRecordAction(L"Options_ChangeProxies", NULL);
+ Thread* thread = g_browser_process->file_thread();
+ DCHECK(thread);
+ thread->message_loop()->PostTask(FROM_HERE, new OpenConnectionDialogTask);
+ } else if (sender == enable_link_doctor_checkbox_) {
+ bool enabled = enable_link_doctor_checkbox_->IsSelected();
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_LinkDoctorCheckbox_Enable",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_LinkDoctorCheckbox_Disable",
+ profile()->GetPrefs());
+ }
+ alternate_error_pages_.SetValue(enabled);
+ } else if (sender == enable_dns_prefetching_checkbox_) {
+ bool enabled = enable_dns_prefetching_checkbox_->IsSelected();
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_DnsPrefetchCheckbox_Enable",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_DnsPrefetchCheckbox_Disable",
+ profile()->GetPrefs());
+ }
+ dns_prefetch_enabled_.SetValue(enabled);
+ chrome_browser_net::EnableDnsPrefetch(enabled);
+ }
+}
+
+void NetworkSection::InitControlLayout() {
+ AdvancedSection::InitControlLayout();
+
+ change_proxies_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_PROXIES_LABEL));
+ change_proxies_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON));
+ change_proxies_button_->SetListener(this);
+ enable_link_doctor_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_LINKDOCTOR_PREF));
+ enable_link_doctor_checkbox_->SetListener(this);
+ enable_dns_prefetching_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_NETWORK_DNS_PREFETCH_ENABLED_DESCRIPTION));
+ enable_dns_prefetching_checkbox_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+ GridLayout* layout = new GridLayout(contents_);
+ contents_->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ AddWrappingColumnSet(layout, single_column_view_set_id);
+ const int indented_view_set_id = 1;
+ AddIndentedColumnSet(layout, indented_view_set_id);
+ const int dependent_labeled_field_set_id = 2;
+ AddDependentTwoColumnSet(layout, dependent_labeled_field_set_id);
+ const int dns_set_id = 3;
+ AddDependentTwoColumnSet(layout, dns_set_id);
+
+ // Proxy settings.
+ AddWrappingLabelRow(layout, change_proxies_label_, single_column_view_set_id,
+ true);
+ AddLeadingControl(layout, change_proxies_button_, indented_view_set_id,
+ false);
+
+ // Link doctor.
+ AddWrappingCheckboxRow(layout, enable_link_doctor_checkbox_,
+ single_column_view_set_id, true);
+
+ // DNS pre-fetching.
+ AddWrappingCheckboxRow(layout, enable_dns_prefetching_checkbox_,
+ single_column_view_set_id, false);
+
+ // Init member prefs so we can update the controls if prefs change.
+ alternate_error_pages_.Init(prefs::kAlternateErrorPagesEnabled,
+ profile()->GetPrefs(), this);
+ dns_prefetch_enabled_.Init(prefs::kDnsPrefetchingEnabled,
+ profile()->GetPrefs(), this);
+}
+
+void NetworkSection::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kAlternateErrorPagesEnabled) {
+ enable_link_doctor_checkbox_->SetIsSelected(
+ alternate_error_pages_.GetValue());
+ }
+ if (!pref_name || *pref_name == prefs::kDnsPrefetchingEnabled) {
+ bool enabled = dns_prefetch_enabled_.GetValue();
+ enable_dns_prefetching_checkbox_->SetIsSelected(enabled);
+ chrome_browser_net::EnableDnsPrefetch(enabled);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedContentsView
+
+class AdvancedContentsView : public OptionsPageView {
+ public:
+ explicit AdvancedContentsView(Profile* profile);
+ virtual ~AdvancedContentsView();
+
+ // ChromeViews::View overrides:
+ virtual int GetLineScrollIncrement(ChromeViews::ScrollView* scroll_view,
+ bool is_horizontal, bool is_positive);
+ void Layout();
+
+ protected:
+ // OptionsPageView implementation:
+ virtual void InitControlLayout();
+
+ private:
+ static void InitClass();
+
+ static int line_height_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AdvancedContentsView);
+};
+
+// static
+int AdvancedContentsView::line_height_ = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedContentsView, public:
+
+AdvancedContentsView::AdvancedContentsView(Profile* profile)
+ : OptionsPageView(profile) {
+ InitClass();
+}
+
+AdvancedContentsView::~AdvancedContentsView() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedContentsView, ChromeViews::View overrides:
+
+int AdvancedContentsView::GetLineScrollIncrement(
+ ChromeViews::ScrollView* scroll_view,
+ bool is_horizontal,
+ bool is_positive) {
+
+ if (!is_horizontal)
+ return line_height_;
+ return View::GetPageScrollIncrement(scroll_view, is_horizontal, is_positive);
+}
+
+void AdvancedContentsView::Layout() {
+ ChromeViews::View* parent = GetParent();
+ if (parent && parent->GetWidth()) {
+ const int width = parent->GetWidth();
+ const int height = GetHeightForWidth(width);
+ SetBounds(0, 0, width, height);
+ } else {
+ CSize pref;
+ GetPreferredSize(&pref);
+ SetBounds(0, 0, pref.cx, pref.cy);
+ }
+ View::Layout();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedContentsView, OptionsPageView implementation:
+
+void AdvancedContentsView::InitControlLayout() {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(new GeneralSection(profile()));
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(new NetworkSection(profile()));
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(new ContentSection(profile()));
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(new SecuritySection(profile()));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedContentsView, private:
+
+void AdvancedContentsView::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ line_height_ = rb.GetFont(ResourceBundle::BaseFont).height();
+ initialized = true;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedScrollViewContainer, public:
+
+AdvancedScrollViewContainer::AdvancedScrollViewContainer(Profile* profile)
+ : contents_view_(new AdvancedContentsView(profile)),
+ scroll_view_(new ChromeViews::ScrollView) {
+ AddChildView(scroll_view_);
+ scroll_view_->SetContents(contents_view_);
+ SetBackground(new ListBackground());
+}
+
+AdvancedScrollViewContainer::~AdvancedScrollViewContainer() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AdvancedScrollViewContainer, ChromeViews::View overrides:
+
+void AdvancedScrollViewContainer::Layout() {
+ CRect lb;
+ GetLocalBounds(&lb, false);
+
+ gfx::Size border = gfx::NativeTheme::instance()->GetThemeBorderSize(
+ gfx::NativeTheme::LIST);
+ lb.DeflateRect(border.ToSIZE());
+ scroll_view_->SetBounds(lb);
+ scroll_view_->Layout();
+}
diff --git a/chrome/browser/views/options/advanced_contents_view.h b/chrome/browser/views/options/advanced_contents_view.h
new file mode 100644
index 0000000..c3097e5
--- /dev/null
+++ b/chrome/browser/views/options/advanced_contents_view.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_ADVANCED_CONTENTS_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_ADVANCED_CONTENTS_VIEW_H__
+
+#include "chrome/browser/views/options/options_page_view.h"
+
+class AdvancedContentsView;
+namespace ChromeViews {
+class ScrollView;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AdvancedScrollViewContainer
+//
+// A View that contains a scroll view containing the Advanced options.
+
+class AdvancedScrollViewContainer : public ChromeViews::View {
+ public:
+ explicit AdvancedScrollViewContainer(Profile* profile);
+ virtual ~AdvancedScrollViewContainer();
+
+ // ChromeViews::View overrides:
+ virtual void Layout();
+
+ private:
+ // The contents of the advanced scroll view.
+ AdvancedContentsView* contents_view_;
+
+ // The scroll view that contains the advanced options.
+ ChromeViews::ScrollView* scroll_view_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AdvancedScrollViewContainer);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_ADVANCED_CONTENTS_VIEW_H__
diff --git a/chrome/browser/views/options/advanced_page_view.cc b/chrome/browser/views/options/advanced_page_view.cc
new file mode 100644
index 0000000..f47e5a6
--- /dev/null
+++ b/chrome/browser/views/options/advanced_page_view.cc
@@ -0,0 +1,210 @@
+// 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/views/options/advanced_page_view.h"
+
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/options/advanced_contents_view.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/message_box_view.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/scroll_view.h"
+#include "chrome/views/window.h"
+#include "generated_resources.h"
+
+namespace {
+
+// A dialog box that asks the user to confirm resetting settings.
+class ResetDefaultsConfirmBox : public ChromeViews::DialogDelegate {
+ public:
+ // This box is modal to |parent_hwnd|.
+ static void ShowConfirmBox(HWND parent_hwnd, AdvancedPageView* page_view) {
+ // When the window closes, it will delete itself.
+ new ResetDefaultsConfirmBox(parent_hwnd, page_view);
+ }
+
+ protected:
+ // ChromeViews::DialogDelegate
+ virtual int GetDialogButtons() const {
+ return DIALOGBUTTON_OK | DIALOGBUTTON_CANCEL;
+ }
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const {
+ switch (button) {
+ case DIALOGBUTTON_OK:
+ return l10n_util::GetString(IDS_OPTIONS_RESET_OKLABEL);
+ case DIALOGBUTTON_CANCEL:
+ return l10n_util::GetString(IDS_OPTIONS_RESET_CANCELLABEL);
+ default:
+ break;
+ }
+ NOTREACHED();
+ return std::wstring();
+ }
+ virtual std::wstring GetWindowTitle() const {
+ return l10n_util::GetString(IDS_PRODUCT_NAME);
+ }
+ virtual bool Accept() {
+ advanced_page_view_->ResetToDefaults();
+ return true;
+ }
+ // ChromeViews::WindowDelegate
+ virtual void WindowClosing() { delete this; }
+ virtual bool IsModal() const { return true; }
+
+ private:
+ ResetDefaultsConfirmBox(HWND parent_hwnd, AdvancedPageView* page_view)
+ : advanced_page_view_(page_view) {
+ const int kDialogWidth = 400;
+ // Also deleted when the window closes.
+ MessageBoxView* message_box_view = new MessageBoxView(
+ MessageBoxView::kFlagHasMessage | MessageBoxView::kFlagHasOKButton,
+ l10n_util::GetString(IDS_OPTIONS_RESET_MESSAGE).c_str(),
+ std::wstring(),
+ kDialogWidth);
+ ChromeViews::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(),
+ message_box_view, this)->Show();
+ }
+ virtual ~ResetDefaultsConfirmBox() { }
+
+ AdvancedPageView* advanced_page_view_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ResetDefaultsConfirmBox);
+};
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// AdvancedPageView, public:
+
+AdvancedPageView::AdvancedPageView(Profile* profile)
+ : advanced_scroll_view_(NULL),
+ reset_to_default_button_(NULL),
+ OptionsPageView(profile) {
+}
+
+AdvancedPageView::~AdvancedPageView() {
+}
+
+void AdvancedPageView::ResetToDefaults() {
+ // TODO(tc): It would be nice if we could generate this list automatically so
+ // changes to any of the options pages doesn't require updating this list
+ // manually.
+ PrefService* prefs = profile()->GetPrefs();
+ const wchar_t* kUserPrefs[] = {
+ prefs::kAcceptLanguages,
+ prefs::kAlternateErrorPagesEnabled,
+ prefs::kBlockPopups,
+ prefs::kCookieBehavior,
+ prefs::kDefaultCharset,
+ prefs::kDnsPrefetchingEnabled,
+ prefs::kDownloadDefaultDirectory,
+ prefs::kDownloadExtensionsToOpen,
+ prefs::kHomePage,
+ prefs::kMixedContentFiltering,
+ prefs::kPromptForDownload,
+ prefs::kPasswordManagerEnabled,
+ prefs::kRestoreOnStartup,
+ prefs::kSafeBrowsingEnabled,
+ prefs::kShowHomeButton,
+ prefs::kSpellCheckDictionary,
+ prefs::kURLsToRestoreOnStartup,
+ prefs::kWebKitDefaultFixedFontSize,
+ prefs::kWebKitDefaultFontSize,
+ prefs::kWebKitFixedFontFamily,
+ prefs::kWebKitJavaEnabled,
+ prefs::kWebKitJavascriptEnabled,
+ prefs::kWebKitLoadsImagesAutomatically,
+ prefs::kWebKitPluginsEnabled,
+ prefs::kWebKitSansSerifFontFamily,
+ prefs::kWebKitSerifFontFamily,
+ };
+ for (int i = 0; i < arraysize(kUserPrefs); ++i)
+ prefs->ClearPref(kUserPrefs[i]);
+
+ PrefService* local_state = g_browser_process->local_state();
+ // Note that we don't reset the kMetricsReportingEnabled preference here
+ // because the reset will reset it to the default setting specified in Chrome
+ // source, not the default setting selected by the user on the web page where
+ // they downloaded Chrome. This means that if the user ever resets their
+ // settings they'll either inadvertedly enable this logging or disable it.
+ // One is undesirable for them, one is undesirable for us. For now, we just
+ // don't reset it.
+ const wchar_t* kLocalStatePrefs[] = {
+ prefs::kApplicationLocale,
+ prefs::kOptionsWindowLastTabIndex,
+ };
+ for (int i = 0; i < arraysize(kLocalStatePrefs); ++i)
+ local_state->ClearPref(kLocalStatePrefs[i]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AdvancedPageView, ChromeViews::NativeButton::Listener implementation:
+
+void AdvancedPageView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == reset_to_default_button_) {
+ UserMetricsRecordAction(L"Options_ResetToDefaults", NULL);
+ ResetDefaultsConfirmBox::ShowConfirmBox(GetRootWindow(), this);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AdvancedPageView, OptionsPageView implementation:
+
+void AdvancedPageView::InitControlLayout() {
+ reset_to_default_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_RESET));
+ reset_to_default_button_->SetListener(this);
+ advanced_scroll_view_ = new AdvancedScrollViewContainer(profile());
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ layout->StartRow(1, single_column_view_set_id);
+ layout->AddView(advanced_scroll_view_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(reset_to_default_button_, 1, 1,
+ GridLayout::TRAILING, GridLayout::CENTER);
+}
diff --git a/chrome/browser/views/options/advanced_page_view.h b/chrome/browser/views/options/advanced_page_view.h
new file mode 100644
index 0000000..1c78051
--- /dev/null
+++ b/chrome/browser/views/options/advanced_page_view.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_ADVANCED_PAGE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_ADVANCED_PAGE_VIEW_H__
+
+#include "chrome/browser/views/options/options_page_view.h"
+#include "chrome/views/native_button.h"
+
+class AdvancedOptionsListModel;
+class AdvancedScrollViewContainer;
+class PrefService;
+
+///////////////////////////////////////////////////////////////////////////////
+// AdvancedPageView
+
+class AdvancedPageView : public OptionsPageView,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ explicit AdvancedPageView(Profile* profile);
+ virtual ~AdvancedPageView();
+
+ // Resets all prefs to their default values.
+ void ResetToDefaults();
+
+ // ChromeViews::NativeButton::Listener implementation:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ protected:
+ // OptionsPageView implementation:
+ virtual void InitControlLayout();
+
+ private:
+ // Controls for the Advanced page
+ AdvancedScrollViewContainer* advanced_scroll_view_;
+ ChromeViews::NativeButton* reset_to_default_button_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AdvancedPageView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_ADVANCED_PAGE_VIEW_H__
diff --git a/chrome/browser/views/options/content_page_view.cc b/chrome/browser/views/options/content_page_view.cc
new file mode 100644
index 0000000..6ed056f
--- /dev/null
+++ b/chrome/browser/views/options/content_page_view.cc
@@ -0,0 +1,464 @@
+// 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 <windows.h>
+#include <shlobj.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "chrome/browser/views/options/content_page_view.h"
+
+#include "base/file_util.h"
+#include "base/gfx/native_theme.h"
+#include "base/gfx/skia_utils.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/shell_dialogs.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/options/fonts_languages_window_view.h"
+#include "chrome/browser/views/options/options_group_view.h"
+#include "chrome/browser/views/password_manager_view.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/radio_button.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/view_container.h"
+#include "generated_resources.h"
+#include "skia/include/SkBitmap.h"
+
+namespace {
+
+static const int kPopupBlockingRadioGroup = 1;
+static const int kPasswordSavingRadioGroup = 2;
+static const int kFileIconSize = 16;
+static const int kFileIconVerticalSpacing = 3;
+static const int kFileIconHorizontalSpacing = 3;
+static const int kFileIconTextFieldSpacing = 3;
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// FileDisplayArea
+
+class FileDisplayArea : public ChromeViews::View {
+ public:
+ FileDisplayArea();
+ virtual ~FileDisplayArea();
+
+ void SetFile(const std::wstring& file_path);
+
+ // ChromeViews::View overrides:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize* out);
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ void Init();
+
+ ChromeViews::TextField* text_field_;
+ SkColor text_field_background_color_;
+
+ gfx::Rect icon_bounds_;
+
+ bool initialized_;
+
+ static void InitClass();
+ static SkBitmap default_folder_icon_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FileDisplayArea);
+};
+
+// static
+SkBitmap FileDisplayArea::default_folder_icon_;
+
+FileDisplayArea::FileDisplayArea()
+ : text_field_(new ChromeViews::TextField),
+ text_field_background_color_(0),
+ initialized_(false) {
+ InitClass();
+}
+
+FileDisplayArea::~FileDisplayArea() {
+}
+
+void FileDisplayArea::SetFile(const std::wstring& file_path) {
+ text_field_->SetText(file_path);
+}
+
+void FileDisplayArea::Paint(ChromeCanvas* canvas) {
+ HDC dc = canvas->beginPlatformPaint();
+ RECT rect = { 0, 0, GetWidth(), GetHeight() };
+ gfx::NativeTheme::instance()->PaintTextField(
+ dc, EP_EDITTEXT, ETS_READONLY, 0, &rect,
+ gfx::SkColorToCOLORREF(text_field_background_color_), true, true);
+ canvas->endPlatformPaint();
+ canvas->DrawBitmapInt(default_folder_icon_, icon_bounds_.x(),
+ icon_bounds_.y());
+}
+
+void FileDisplayArea::Layout() {
+ icon_bounds_.SetRect(kFileIconHorizontalSpacing, kFileIconVerticalSpacing,
+ kFileIconSize, kFileIconSize);
+ CSize ps;
+ text_field_->GetPreferredSize(&ps);
+ text_field_->SetBounds(icon_bounds_.right() + kFileIconTextFieldSpacing,
+ (GetHeight() - ps.cy) / 2,
+ GetWidth() - icon_bounds_.right() -
+ kFileIconHorizontalSpacing -
+ kFileIconTextFieldSpacing, ps.cy);
+}
+
+void FileDisplayArea::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ out->cx = kFileIconSize + 2 * kFileIconVerticalSpacing;
+ out->cy = kFileIconSize + 2 * kFileIconHorizontalSpacing;
+}
+
+void FileDisplayArea::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (!initialized_ && is_add && GetViewContainer())
+ Init();
+}
+
+void FileDisplayArea::Init() {
+ initialized_ = true;
+ AddChildView(text_field_);
+ text_field_background_color_ =
+ gfx::NativeTheme::instance()->GetThemeColorWithDefault(
+ gfx::NativeTheme::TEXTFIELD, EP_EDITTEXT, ETS_READONLY,
+ TMT_FILLCOLOR, COLOR_3DFACE);
+ text_field_->SetReadOnly(true);
+ text_field_->RemoveBorder();
+ text_field_->SetBackgroundColor(text_field_background_color_);
+}
+
+void FileDisplayArea::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_folder_icon_ = *rb.GetBitmapNamed(IDR_FOLDER_CLOSED);
+ initialized = true;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ContentPageView, public:
+
+ContentPageView::ContentPageView(Profile* profile)
+ : download_location_group_(NULL),
+ download_default_download_location_display_(NULL),
+ download_browse_button_(NULL),
+ download_ask_for_save_location_checkbox_(NULL),
+ select_file_dialog_(SelectFileDialog::Create(this)),
+ passwords_group_(NULL),
+ passwords_asktosave_radio_(NULL),
+ passwords_neversave_radio_(NULL),
+ passwords_show_passwords_button_(NULL),
+ fonts_lang_group_(NULL),
+ fonts_and_languages_label_(NULL),
+ change_content_fonts_button_(NULL),
+ OptionsPageView(profile) {
+}
+
+ContentPageView::~ContentPageView() {
+ select_file_dialog_->ListenerDestroyed();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ContentPageView, SelectFileDialog::Listener implementation:
+
+void ContentPageView::FileSelected(const std::wstring& path, void* params) {
+ UserMetricsRecordAction(L"Options_SetDownloadDirectory",
+ profile()->GetPrefs());
+ default_download_location_.SetValue(path);
+ // We need to call this manually here since because we're setting the value
+ // through the pref member which avoids notifying the listener that set the
+ // value.
+ UpdateDownloadDirectoryDisplay();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ContentPageView, ChromeViews::NativeButton::Listener implementation:
+
+void ContentPageView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == download_browse_button_) {
+ const std::wstring dialog_title =
+ l10n_util::GetString(IDS_OPTIONS_DOWNLOADLOCATION_BROWSE_TITLE);
+ select_file_dialog_->SelectFile(SelectFileDialog::SELECT_FOLDER,
+ dialog_title, L"", GetRootWindow(), NULL);
+ } else if (sender == download_ask_for_save_location_checkbox_) {
+ bool enabled = download_ask_for_save_location_checkbox_->IsSelected();
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_AskForSaveLocation_Enable",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_AskForSaveLocation_Disable",
+ profile()->GetPrefs());
+ }
+ ask_for_save_location_.SetValue(enabled);
+ } else if (sender == passwords_asktosave_radio_ ||
+ sender == passwords_neversave_radio_) {
+ bool enabled = passwords_asktosave_radio_->IsSelected();
+ if (enabled) {
+ UserMetricsRecordAction(L"Options_PasswordManager_Enable",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_PasswordManager_Disable",
+ profile()->GetPrefs());
+ }
+ ask_to_save_passwords_.SetValue(enabled);
+ } else if (sender == passwords_show_passwords_button_) {
+ UserMetricsRecordAction(L"Options_ShowPasswordManager", NULL);
+ PasswordManagerView::Show(profile());
+ } else if (sender == change_content_fonts_button_) {
+ FontsLanguagesWindowView* flwv = new FontsLanguagesWindowView(profile());
+ ChromeViews::Window* w =
+ ChromeViews::Window::CreateChromeWindow(GetRootWindow(), gfx::Rect(),
+ flwv, flwv);
+ w->Show();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ContentPageView, OptionsPageView implementation:
+
+bool ContentPageView::CanClose() const {
+ return !select_file_dialog_->IsRunning(GetRootWindow());
+}
+
+void ContentPageView::InitControlLayout() {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = new GridLayout(this);
+ layout->SetInsets(5, 5, 5, 5);
+ SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ layout->StartRow(0, single_column_view_set_id);
+ InitDownloadLocation();
+ layout->AddView(download_location_group_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ InitPasswordSavingGroup();
+ layout->AddView(passwords_group_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ InitFontsLangGroup();
+ layout->AddView(fonts_lang_group_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Init member prefs so we can update the controls if prefs change.
+ default_download_location_.Init(prefs::kDownloadDefaultDirectory,
+ profile()->GetPrefs(), this);
+ ask_for_save_location_.Init(prefs::kPromptForDownload,
+ profile()->GetPrefs(), this);
+ ask_to_save_passwords_.Init(prefs::kPasswordManagerEnabled,
+ profile()->GetPrefs(), this);
+}
+
+void ContentPageView::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kDownloadDefaultDirectory)
+ UpdateDownloadDirectoryDisplay();
+
+ if (!pref_name || *pref_name == prefs::kPromptForDownload) {
+ download_ask_for_save_location_checkbox_->SetIsSelected(
+ ask_for_save_location_.GetValue());
+ }
+ if (!pref_name || *pref_name == prefs::kPasswordManagerEnabled) {
+ if (ask_to_save_passwords_.GetValue()) {
+ passwords_asktosave_radio_->SetIsSelected(true);
+ } else {
+ passwords_neversave_radio_->SetIsSelected(true);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ContentsPageView, ChromeViews::View overrides:
+
+void ContentPageView::Layout() {
+ // We need to Layout twice - once to get the width of the contents box...
+ View::Layout();
+ download_ask_for_save_location_checkbox_->SetBounds(
+ 0, 0, download_location_group_->GetContentsWidth(), 0);
+ passwords_asktosave_radio_->SetBounds(
+ 0, 0, passwords_group_->GetContentsWidth(), 0);
+ passwords_neversave_radio_->SetBounds(
+ 0, 0, passwords_group_->GetContentsWidth(), 0);
+ // ... and twice to get the height of multi-line items correct.
+ View::Layout();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ContentPageView, private:
+
+void ContentPageView::InitDownloadLocation() {
+ download_default_download_location_display_ = new FileDisplayArea;
+ download_browse_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_DOWNLOADLOCATION_BROWSE_BUTTON));
+ download_browse_button_->SetListener(this);
+
+ download_ask_for_save_location_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_DOWNLOADLOCATION_ASKFORSAVELOCATION));
+ download_ask_for_save_location_checkbox_->SetListener(this);
+ download_ask_for_save_location_checkbox_->SetMultiLine(true);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ ChromeViews::View* contents = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(contents);
+ contents->SetLayoutManager(layout);
+
+ const int double_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(double_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing);
+
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(download_default_download_location_display_, 1, 1,
+ GridLayout::FILL, GridLayout::CENTER);
+ layout->AddView(download_browse_button_);
+
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+
+ const int single_column_view_set_id = 1;
+ column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(download_ask_for_save_location_checkbox_);
+
+ download_location_group_ = new OptionsGroupView(
+ contents, l10n_util::GetString(IDS_OPTIONS_DOWNLOADLOCATION_GROUP_NAME),
+ std::wstring(),
+ true);
+}
+
+void ContentPageView::InitPasswordSavingGroup() {
+ passwords_asktosave_radio_ = new ChromeViews::RadioButton(
+ l10n_util::GetString(IDS_OPTIONS_PASSWORDS_ASKTOSAVE),
+ kPasswordSavingRadioGroup);
+ passwords_asktosave_radio_->SetListener(this);
+ passwords_asktosave_radio_->SetMultiLine(true);
+ passwords_neversave_radio_ = new ChromeViews::RadioButton(
+ l10n_util::GetString(IDS_OPTIONS_PASSWORDS_NEVERSAVE),
+ kPasswordSavingRadioGroup);
+ passwords_neversave_radio_->SetListener(this);
+ passwords_neversave_radio_->SetMultiLine(true);
+ passwords_show_passwords_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_PASSWORDS_SHOWPASSWORDS));
+ passwords_show_passwords_button_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ ChromeViews::View* contents = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(contents);
+ contents->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 1;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(passwords_asktosave_radio_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(passwords_neversave_radio_);
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(passwords_show_passwords_button_);
+
+ passwords_group_ = new OptionsGroupView(
+ contents, l10n_util::GetString(IDS_OPTIONS_PASSWORDS_GROUP_NAME), L"",
+ true);
+}
+
+void ContentPageView::InitFontsLangGroup() {
+ fonts_and_languages_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_FONTSETTINGS_INFO));
+ fonts_and_languages_label_->SetHorizontalAlignment(
+ ChromeViews::Label::ALIGN_LEFT);
+ change_content_fonts_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_FONTSETTINGS_CONFIGUREFONTS_BUTTON));
+ change_content_fonts_button_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ ChromeViews::View* contents = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(contents);
+ contents->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 1;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(fonts_and_languages_label_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(change_content_fonts_button_);
+
+ fonts_lang_group_ = new OptionsGroupView(
+ contents,
+ l10n_util::GetString(IDS_OPTIONS_FONTSANDLANGUAGES_GROUP_NAME),
+ L"", false);
+}
+
+void ContentPageView::UpdateDownloadDirectoryDisplay() {
+ download_default_download_location_display_->SetFile(
+ default_download_location_.GetValue());
+}
diff --git a/chrome/browser/views/options/content_page_view.h b/chrome/browser/views/options/content_page_view.h
new file mode 100644
index 0000000..2230810
--- /dev/null
+++ b/chrome/browser/views/options/content_page_view.h
@@ -0,0 +1,114 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_CONTENT_PAGE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_CONTENT_PAGE_VIEW_H__
+
+#include "chrome/browser/views/options/options_page_view.h"
+#include "chrome/browser/shell_dialogs.h"
+#include "chrome/common/pref_member.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+class CheckBox;
+class RadioButton;
+}
+class FileDisplayArea;
+class OptionsGroupView;
+class PrefService;
+
+////////////////////////////////////////////////////////////////////////////////
+// ContentPageView
+
+class ContentPageView : public OptionsPageView,
+ public ChromeViews::NativeButton::Listener,
+ public SelectFileDialog::Listener {
+ public:
+ explicit ContentPageView(Profile* profile);
+ virtual ~ContentPageView();
+
+ // ChromeViews::NativeButton::Listener implementation:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // SelectFileDialog::Listener implementation:
+ virtual void FileSelected(const std::wstring& path, void* params);
+
+ // OptionsPageView implementation:
+ virtual bool CanClose() const;
+
+ protected:
+ // OptionsPageView implementation:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+
+ // ChromeViews::View overrides:
+ virtual void Layout();
+
+ private:
+ // Init all the dialog controls.
+ void InitDownloadLocation();
+ void InitPasswordSavingGroup();
+ void InitFontsLangGroup();
+
+ // Updates the directory displayed in the default download location view with
+ // the current value of the pref.
+ void UpdateDownloadDirectoryDisplay();
+
+ // Controls for the Download Location group.
+ OptionsGroupView* download_location_group_;
+ FileDisplayArea* download_default_download_location_display_;
+ ChromeViews::NativeButton* download_browse_button_;
+ ChromeViews::CheckBox* download_ask_for_save_location_checkbox_;
+ scoped_refptr<SelectFileDialog> select_file_dialog_;
+
+ // Controls for the Password Saving group
+ OptionsGroupView* passwords_group_;
+ ChromeViews::RadioButton* passwords_asktosave_radio_;
+ ChromeViews::RadioButton* passwords_neversave_radio_;
+ ChromeViews::NativeButton* passwords_show_passwords_button_;
+
+ // Controls for the Popup Blocking group.
+ OptionsGroupView* popups_group_;
+ ChromeViews::RadioButton* popups_show_minimized_radio_;
+ ChromeViews::RadioButton* popups_block_all_radio_;
+
+ // Controls for the Fonts and Languages group.
+ OptionsGroupView* fonts_lang_group_;
+ ChromeViews::Label* fonts_and_languages_label_;
+ ChromeViews::NativeButton* change_content_fonts_button_;
+
+ StringPrefMember default_download_location_;
+ BooleanPrefMember ask_for_save_location_;
+ BooleanPrefMember ask_to_save_passwords_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentPageView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_CONTENT_PAGE_VIEW_H__
diff --git a/chrome/browser/views/options/cookies_view.cc b/chrome/browser/views/options/cookies_view.cc
new file mode 100644
index 0000000..3b59198
--- /dev/null
+++ b/chrome/browser/views/options/cookies_view.cc
@@ -0,0 +1,791 @@
+// 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 <algorithm>
+
+#include "chrome/browser/views/options/cookies_view.h"
+
+#include "base/string_util.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/gfx/color_utils.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/time_format.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/border.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/table_view.h"
+#include "generated_resources.h"
+#include "net/base/cookie_monster.h"
+#include "net/url_request/url_request_context.h"
+
+// static
+ChromeViews::Window* CookiesView::instance_ = NULL;
+static const int kCookieInfoViewBorderSize = 1;
+static const int kCookieInfoViewInsetSize = 3;
+static const int kSearchFilterDelayMs = 500;
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesTableModel
+
+class CookiesTableModel : public ChromeViews::TableModel {
+ public:
+ explicit CookiesTableModel(Profile* profile);
+ virtual ~CookiesTableModel() {}
+
+ // Returns information about the Cookie at the specified index.
+ std::string GetDomainAt(int index);
+ CookieMonster::CanonicalCookie& GetCookieAt(int index);
+
+ // Remove the specified cookies from the Cookie Monster and update the view.
+ void RemoveCookies(int start_index, int remove_count);
+ void RemoveAllShownCookies();
+
+ // ChromeViews::TableModel implementation:
+ virtual int RowCount();
+ virtual std::wstring GetText(int row, int column_id);
+ virtual SkBitmap GetIcon(int row);
+ virtual void SetObserver(ChromeViews::TableModelObserver* observer);
+
+ // Filter the cookies to only display matched results.
+ void UpdateSearchResults(const std::wstring& filter);
+
+ private:
+ void LoadCookies();
+ void DoFilter();
+
+ std::wstring filter_;
+
+ // The profile from which this model sources cookies.
+ Profile* profile_;
+
+ typedef CookieMonster::CookieList CookieList;
+ typedef std::vector<CookieMonster::CookieListPair*> CookiePtrList;
+ CookieList all_cookies_;
+ CookiePtrList shown_cookies_;
+
+ ChromeViews::TableModelObserver* observer_;
+
+ // Static resources for this object.
+ static SkBitmap cookie_icon_;
+ static void InitClass();
+
+ DISALLOW_EVIL_CONSTRUCTORS(CookiesTableModel);
+};
+
+// static
+SkBitmap CookiesTableModel::cookie_icon_;
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesTableModel, public:
+
+CookiesTableModel::CookiesTableModel(Profile* profile)
+ : profile_(profile) {
+ InitClass();
+ LoadCookies();
+}
+
+std::string CookiesTableModel::GetDomainAt(int index) {
+ DCHECK(index >= 0 && index < RowCount());
+ return shown_cookies_.at(index)->first;
+}
+
+CookieMonster::CanonicalCookie& CookiesTableModel::GetCookieAt(int index) {
+ DCHECK(index >= 0 && index < RowCount());
+ return shown_cookies_.at(index)->second;
+}
+
+void CookiesTableModel::RemoveCookies(int start_index, int remove_count) {
+ if (remove_count <= 0) {
+ NOTREACHED();
+ return;
+ }
+
+ CookieMonster* monster = profile_->GetRequestContext()->cookie_store();
+
+ // We need to update the searched results list, the full cookie list,
+ // and the view. We walk through the search results list (which is what
+ // is displayed) and map these back to the full cookie list. They should
+ // be in the same sort order, and always exist, so we can just walk once.
+ // We can't delete any entries from all_cookies_ without invaliding all of
+ // our pointers after it (which are in shown_cookies), so we go backwards.
+ CookiePtrList::iterator first = shown_cookies_.begin() + start_index;
+ CookiePtrList::iterator last = first + remove_count;
+ CookieList::iterator all_it = all_cookies_.end();
+ while (last != first) {
+ --last;
+ --all_it;
+ // Seek to the corresponding entry in all_cookies_
+ while (&*all_it != *last) --all_it;
+ // Delete the cookie from the monster
+ monster->DeleteCookie(all_it->first, all_it->second, true);
+ all_it = all_cookies_.erase(all_it);
+ }
+
+ // By deleting entries from all_cookies, we just possibly moved stuff around
+ // and have thus invalidated all of our pointers, so rebuild shown_cookies.
+ // We could do this all better if there was a way to mark elements of
+ // all_cookies as dead instead of deleting, but this should be fine for now.
+ DoFilter();
+ observer_->OnItemsRemoved(start_index, remove_count);
+}
+
+void CookiesTableModel::RemoveAllShownCookies() {
+ RemoveCookies(0, RowCount());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesTableModel, ChromeViews::TableModel implementation:
+
+int CookiesTableModel::RowCount() {
+ return static_cast<int>(shown_cookies_.size());
+}
+
+std::wstring CookiesTableModel::GetText(int row, int column_id) {
+ DCHECK(row >= 0 && row < RowCount());
+ switch (column_id) {
+ case IDS_COOKIES_DOMAIN_COLUMN_HEADER:
+ {
+ // Domain cookies start with a trailing dot, but we will show this
+ // in the cookie details, show it without the dot in the list.
+ std::string& domain = shown_cookies_.at(row)->first;
+ if (!domain.empty() && domain[0] == '.')
+ return UTF8ToWide(domain.substr(1));
+ return UTF8ToWide(domain);
+ }
+ break;
+ case IDS_COOKIES_NAME_COLUMN_HEADER:
+ return UTF8ToWide(shown_cookies_.at(row)->second.Name());
+ break;
+ }
+ NOTREACHED();
+ return L"";
+}
+
+SkBitmap CookiesTableModel::GetIcon(int row) {
+ return cookie_icon_;
+}
+
+void CookiesTableModel::SetObserver(ChromeViews::TableModelObserver* observer) {
+ observer_ = observer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesTableModel, private:
+
+// Returns true if |cookie| matches the specified filter, where "match" is
+// defined as the cookie's domain, name and value contains filter text
+// somewhere.
+static bool ContainsFilterText(const std::string& domain,
+ const CookieMonster::CanonicalCookie& cookie,
+ const std::string& filter) {
+ return domain.find(filter) != std::string::npos ||
+ cookie.Name().find(filter) != std::string::npos ||
+ cookie.Value().find(filter) != std::string::npos;
+}
+
+// Sort ignore the '.' prefix for domain cookies.
+static bool CookieSorter(const CookieMonster::CookieListPair& cp1,
+ const CookieMonster::CookieListPair& cp2) {
+ bool is1domain = !cp1.first.empty() && cp1.first[0] == '.';
+ bool is2domain = !cp2.first.empty() && cp2.first[0] == '.';
+
+ // They are both either domain or host cookies, sort them normally.
+ if (is1domain == is2domain)
+ return cp1.first < cp2.first;
+
+ // One (but only one) is a domain cookie, skip the beginning '.'.
+ int comp = is1domain ?
+ cp1.first.compare(1, cp1.first.length() - 1, cp2.first) :
+ -cp2.first.compare(1, cp2.first.length() - 1, cp1.first);
+
+ return comp < 0;
+}
+
+void CookiesTableModel::LoadCookies() {
+ // mmargh mmargh mmargh!
+ CookieMonster* cookie_monster =
+ profile_->GetRequestContext()->cookie_store();
+ all_cookies_ = cookie_monster->GetAllCookies();
+ std::sort(all_cookies_.begin(), all_cookies_.end(), CookieSorter);
+ DoFilter();
+}
+
+void CookiesTableModel::DoFilter() {
+ std::string utf8_filter = WideToUTF8(filter_);
+ bool has_filter = !utf8_filter.empty();
+
+ shown_cookies_.clear();
+
+ CookieList::iterator iter = all_cookies_.begin();
+ for (; iter != all_cookies_.end(); ++iter) {
+ if (!has_filter ||
+ ContainsFilterText(iter->first, iter->second, utf8_filter)) {
+ shown_cookies_.push_back(&*iter);
+ }
+ }
+}
+
+void CookiesTableModel::UpdateSearchResults(const std::wstring& filter) {
+ filter_ = filter;
+ DoFilter();
+ observer_->OnModelChanged();
+}
+
+// static
+void CookiesTableModel::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ cookie_icon_ = *rb.GetBitmapNamed(IDR_COOKIE_ICON);
+ initialized = true;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesTableView
+// Overridden to handle Delete key presses
+
+class CookiesTableView : public ChromeViews::TableView {
+ public:
+ CookiesTableView(CookiesTableModel* cookies_model,
+ std::vector<ChromeViews::TableColumn> columns);
+ virtual ~CookiesTableView() {}
+
+ // Removes the cookies associated with the selected rows in the TableView.
+ void RemoveSelectedCookies();
+
+ protected:
+ // ChromeViews::TableView implementation:
+ virtual void OnKeyDown(unsigned short virtual_keycode);
+
+ private:
+ // Our model, as a CookiesTableModel.
+ CookiesTableModel* cookies_model_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CookiesTableView);
+};
+
+CookiesTableView::CookiesTableView(
+ CookiesTableModel* cookies_model,
+ std::vector<ChromeViews::TableColumn> columns)
+ : ChromeViews::TableView(cookies_model, columns,
+ ChromeViews::ICON_AND_TEXT, false, true, true),
+ cookies_model_(cookies_model) {
+}
+
+void CookiesTableView::RemoveSelectedCookies() {
+ // It's possible that we don't have anything selected.
+ if (SelectedRowCount() <= 0)
+ return;
+
+ // Remove the selected cookies.
+ int selected_row = FirstSelectedRow();
+ cookies_model_->RemoveCookies(selected_row, SelectedRowCount());
+ // Keep an element selected
+ if (RowCount() > 0)
+ Select(std::min(RowCount() - 1, selected_row));
+}
+
+void CookiesTableView::OnKeyDown(unsigned short virtual_keycode) {
+ if (virtual_keycode == VK_DELETE)
+ RemoveSelectedCookies();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookieInfoView
+//
+// Responsible for displaying a tabular grid of Cookie information.
+class CookieInfoView : public ChromeViews::View {
+ public:
+ CookieInfoView();
+ virtual ~CookieInfoView();
+
+ // Update the display from the specified CookieNode.
+ void SetCookie(const std::string& domain,
+ const CookieMonster::CanonicalCookie& cookie_node);
+
+ // Clears the cookie display to indicate that no or multiple cookies are
+ // selected.
+ void ClearCookieDisplay();
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ // Set up the view layout
+ void Init();
+
+ // Individual property labels
+ ChromeViews::Label* name_label_;
+ ChromeViews::TextField* name_value_field_;
+ ChromeViews::Label* content_label_;
+ ChromeViews::TextField* content_value_field_;
+ ChromeViews::Label* domain_label_;
+ ChromeViews::TextField* domain_value_field_;
+ ChromeViews::Label* path_label_;
+ ChromeViews::TextField* path_value_field_;
+ ChromeViews::Label* send_for_label_;
+ ChromeViews::TextField* send_for_value_field_;
+ ChromeViews::Label* created_label_;
+ ChromeViews::TextField* created_value_field_;
+ ChromeViews::Label* expires_label_;
+ ChromeViews::TextField* expires_value_field_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CookieInfoView);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// CookieInfoView, public:
+
+CookieInfoView::CookieInfoView()
+ : name_label_(NULL),
+ name_value_field_(NULL),
+ content_label_(NULL),
+ content_value_field_(NULL),
+ domain_label_(NULL),
+ domain_value_field_(NULL),
+ path_label_(NULL),
+ path_value_field_(NULL),
+ send_for_label_(NULL),
+ send_for_value_field_(NULL),
+ created_label_(NULL),
+ created_value_field_(NULL),
+ expires_label_(NULL),
+ expires_value_field_(NULL) {
+}
+
+CookieInfoView::~CookieInfoView() {
+}
+
+void CookieInfoView::SetCookie(const std::string& domain,
+ const CookieMonster::CanonicalCookie& cookie) {
+ name_value_field_->SetText(UTF8ToWide(cookie.Name()));
+ content_value_field_->SetText(UTF8ToWide(cookie.Value()));
+ domain_value_field_->SetText(UTF8ToWide(domain));
+ path_value_field_->SetText(UTF8ToWide(cookie.Path()));
+ created_value_field_->SetText(
+ TimeFormat::FriendlyDateAndTime(cookie.CreationDate()));
+
+ if (cookie.DoesExpire()) {
+ expires_value_field_->SetText(
+ TimeFormat::FriendlyDateAndTime(cookie.ExpiryDate()));
+ } else {
+ // TODO(deanm) need a string that the average user can understand
+ // "When you quit or restart your browser" ?
+ expires_value_field_->SetText(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_EXPIRES_SESSION));
+ }
+
+ std::wstring sendfor_text;
+ if (cookie.IsSecure()) {
+ sendfor_text = l10n_util::GetString(IDS_COOKIES_COOKIE_SENDFOR_SECURE);
+ } else {
+ sendfor_text = l10n_util::GetString(IDS_COOKIES_COOKIE_SENDFOR_ANY);
+ }
+ send_for_value_field_->SetText(sendfor_text);
+}
+
+void CookieInfoView::ClearCookieDisplay() {
+ std::wstring no_cookie_string =
+ l10n_util::GetString(IDS_COOKIES_COOKIE_NONESELECTED);
+ name_value_field_->SetText(no_cookie_string);
+ name_value_field_->SetEnabled(false);
+ content_value_field_->SetText(no_cookie_string);
+ content_value_field_->SetEnabled(false);
+ domain_value_field_->SetText(no_cookie_string);
+ domain_value_field_->SetEnabled(false);
+ path_value_field_->SetText(no_cookie_string);
+ path_value_field_->SetEnabled(false);
+ send_for_value_field_->SetText(no_cookie_string);
+ send_for_value_field_->SetEnabled(false);
+ created_value_field_->SetText(no_cookie_string);
+ created_value_field_->SetEnabled(false);
+ expires_value_field_->SetText(no_cookie_string);
+ expires_value_field_->SetEnabled(false);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookieInfoView, ChromeViews::View overrides:
+
+void CookieInfoView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (is_add && child == this)
+ Init();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookieInfoView, private:
+
+void CookieInfoView::Init() {
+ SkColor border_color = color_utils::GetSysSkColor(COLOR_3DSHADOW);
+ ChromeViews::Border* border = ChromeViews::Border::CreateSolidBorder(
+ kCookieInfoViewBorderSize, border_color);
+ SetBorder(border);
+
+ name_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_NAME_LABEL));
+ name_value_field_ = new ChromeViews::TextField;
+ content_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_CONTENT_LABEL));
+ content_value_field_ = new ChromeViews::TextField;
+ domain_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_DOMAIN_LABEL));
+ domain_value_field_ = new ChromeViews::TextField;
+ path_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_PATH_LABEL));
+ path_value_field_ = new ChromeViews::TextField;
+ send_for_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_SENDFOR_LABEL));
+ send_for_value_field_ = new ChromeViews::TextField;
+ created_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_CREATED_LABEL));
+ created_value_field_ = new ChromeViews::TextField;
+ expires_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_COOKIE_EXPIRES_LABEL));
+ expires_value_field_ = new ChromeViews::TextField;
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = new GridLayout(this);
+ layout->SetInsets(kCookieInfoViewInsetSize,
+ kCookieInfoViewInsetSize,
+ kCookieInfoViewInsetSize,
+ kCookieInfoViewInsetSize);
+ SetLayoutManager(layout);
+
+ int three_column_layout_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(three_column_layout_id);
+ column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, three_column_layout_id);
+ layout->AddView(name_label_);
+ layout->AddView(name_value_field_);
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, three_column_layout_id);
+ layout->AddView(content_label_);
+ layout->AddView(content_value_field_);
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, three_column_layout_id);
+ layout->AddView(domain_label_);
+ layout->AddView(domain_value_field_);
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, three_column_layout_id);
+ layout->AddView(path_label_);
+ layout->AddView(path_value_field_);
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, three_column_layout_id);
+ layout->AddView(send_for_label_);
+ layout->AddView(send_for_value_field_);
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, three_column_layout_id);
+ layout->AddView(created_label_);
+ layout->AddView(created_value_field_);
+ layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
+ layout->StartRow(0, three_column_layout_id);
+ layout->AddView(expires_label_);
+ layout->AddView(expires_value_field_);
+
+ // Color these borderless text areas the same as the containing dialog.
+ SkColor text_area_background = color_utils::GetSysSkColor(COLOR_3DFACE);
+ // Now that the TextFields are in the view hierarchy, we can initialize them.
+ name_value_field_->SetReadOnly(true);
+ name_value_field_->RemoveBorder();
+ name_value_field_->SetBackgroundColor(text_area_background);
+ content_value_field_->SetReadOnly(true);
+ content_value_field_->RemoveBorder();
+ content_value_field_->SetBackgroundColor(text_area_background);
+ domain_value_field_->SetReadOnly(true);
+ domain_value_field_->RemoveBorder();
+ domain_value_field_->SetBackgroundColor(text_area_background);
+ path_value_field_->SetReadOnly(true);
+ path_value_field_->RemoveBorder();
+ path_value_field_->SetBackgroundColor(text_area_background);
+ send_for_value_field_->SetReadOnly(true);
+ send_for_value_field_->RemoveBorder();
+ send_for_value_field_->SetBackgroundColor(text_area_background);
+ created_value_field_->SetReadOnly(true);
+ created_value_field_->RemoveBorder();
+ created_value_field_->SetBackgroundColor(text_area_background);
+ expires_value_field_->SetReadOnly(true);
+ expires_value_field_->RemoveBorder();
+ expires_value_field_->SetBackgroundColor(text_area_background);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesView, public:
+
+// static
+void CookiesView::ShowCookiesWindow(Profile* profile) {
+ if (!instance_) {
+ CookiesView* cookies_view = new CookiesView(profile);
+ instance_ = ChromeViews::Window::CreateChromeWindow(
+ NULL, gfx::Rect(), cookies_view, cookies_view);
+ }
+ if (!instance_->IsVisible()) {
+ instance_->Show();
+ } else {
+ instance_->Activate();
+ }
+}
+
+CookiesView::~CookiesView() {
+ cookies_table_->SetModel(NULL);
+}
+
+void CookiesView::UpdateSearchResults() {
+ cookies_table_model_->UpdateSearchResults(search_field_->GetText());
+ remove_all_button_->SetEnabled(cookies_table_model_->RowCount() > 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesView, ChromeViews::NativeButton::listener implementation:
+
+void CookiesView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == remove_button_) {
+ cookies_table_->RemoveSelectedCookies();
+ } else if (sender == remove_all_button_) {
+ // Delete all the Cookies shown.
+ cookies_table_model_->RemoveAllShownCookies();
+ UpdateForEmptyState();
+ } else if (sender == clear_search_button_) {
+ ResetSearchQuery();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesView, ChromeViews::TableViewObserver implementation:
+void CookiesView::OnSelectionChanged() {
+ int selected_row_count = cookies_table_->SelectedRowCount();
+ if (selected_row_count == 1) {
+ int selected_index = cookies_table_->FirstSelectedRow();
+ if (selected_index >= 0 &&
+ selected_index < cookies_table_model_->RowCount()) {
+ info_view_->SetCookie(cookies_table_model_->GetDomainAt(selected_index),
+ cookies_table_model_->GetCookieAt(selected_index));
+ }
+ } else {
+ info_view_->ClearCookieDisplay();
+ }
+ remove_button_->SetEnabled(selected_row_count != 0);
+ if (cookies_table_->RowCount() == 0)
+ UpdateForEmptyState();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesView, ChromeViews::TextField::Controller implementation:
+
+void CookiesView::ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents) {
+ search_update_factory_.RevokeAll();
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ search_update_factory_.NewRunnableMethod(
+ &CookiesView::UpdateSearchResults), kSearchFilterDelayMs);
+}
+
+void CookiesView::HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key, UINT repeat_count,
+ UINT flags) {
+ switch (key) {
+ case VK_ESCAPE:
+ ResetSearchQuery();
+ break;
+ case VK_RETURN:
+ search_update_factory_.RevokeAll();
+ UpdateSearchResults();
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesView, ChromeViews::DialogDelegate implementation:
+
+std::wstring CookiesView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_COOKIES_WINDOW_TITLE);
+}
+
+void CookiesView::WindowClosing() {
+ instance_ = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesView, ChromeViews::View overrides:
+
+void CookiesView::Layout() {
+ // Lay out the Remove/Remove All buttons in the parent view.
+ CSize ps;
+ remove_button_->GetPreferredSize(&ps);
+ CRect parent_bounds;
+ GetParent()->GetLocalBounds(&parent_bounds, false);
+ int y_buttons = parent_bounds.bottom - ps.cy - kButtonVEdgeMargin;
+
+ remove_button_->SetBounds(kPanelHorizMargin, y_buttons, ps.cx, ps.cy);
+
+ remove_all_button_->GetPreferredSize(&ps);
+ int remove_all_x = remove_button_->GetX() + remove_button_->GetWidth() +
+ kRelatedControlHorizontalSpacing;
+ remove_all_button_->SetBounds(remove_all_x, y_buttons, ps.cx, ps.cy);
+
+ // Lay out this View
+ View::Layout();
+}
+
+void CookiesView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_COOKIES_DIALOG_WIDTH_CHARS,
+ IDS_COOKIES_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void CookiesView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (is_add && child == this)
+ Init();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookiesView, private:
+
+CookiesView::CookiesView(Profile* profile)
+ : search_label_(NULL),
+ search_field_(NULL),
+ clear_search_button_(NULL),
+ description_label_(NULL),
+ cookies_table_(NULL),
+ info_view_(NULL),
+ remove_button_(NULL),
+ remove_all_button_(NULL),
+ profile_(profile),
+ search_update_factory_(this) {
+}
+
+void CookiesView::Init() {
+ search_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_SEARCH_LABEL));
+ search_field_ = new ChromeViews::TextField;
+ search_field_->SetController(this);
+ clear_search_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_COOKIES_CLEAR_SEARCH_LABEL));
+ clear_search_button_->SetListener(this);
+ description_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_COOKIES_INFO_LABEL));
+ description_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+
+ cookies_table_model_.reset(new CookiesTableModel(profile_));
+ info_view_ = new CookieInfoView;
+ std::vector<ChromeViews::TableColumn> columns;
+ columns.push_back(ChromeViews::TableColumn(IDS_COOKIES_DOMAIN_COLUMN_HEADER,
+ ChromeViews::TableColumn::LEFT,
+ 200, 0.5f));
+ columns.push_back(ChromeViews::TableColumn(IDS_COOKIES_NAME_COLUMN_HEADER,
+ ChromeViews::TableColumn::LEFT,
+ 150, 0.5f));
+ cookies_table_ = new CookiesTableView(cookies_table_model_.get(), columns);
+ cookies_table_->SetObserver(this);
+ remove_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_COOKIES_REMOVE_LABEL));
+ remove_button_->SetListener(this);
+ remove_all_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_COOKIES_REMOVE_ALL_LABEL));
+ remove_all_button_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ const int five_column_layout_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(five_column_layout_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ const int single_column_layout_id = 1;
+ column_set = layout->AddColumnSet(single_column_layout_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, five_column_layout_id);
+ layout->AddView(search_label_);
+ layout->AddView(search_field_);
+ layout->AddView(clear_search_button_);
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_layout_id);
+ layout->AddView(description_label_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(1, single_column_layout_id);
+ layout->AddView(cookies_table_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_layout_id);
+ layout->AddView(info_view_);
+
+ // Add the Remove/Remove All buttons to the ClientView
+ View* parent = GetParent();
+ parent->AddChildView(remove_button_);
+ parent->AddChildView(remove_all_button_);
+
+ if (cookies_table_->RowCount() > 0) {
+ cookies_table_->Select(0);
+ } else {
+ UpdateForEmptyState();
+ }
+}
+
+void CookiesView::ResetSearchQuery() {
+ search_field_->SetText(EmptyWString());
+ UpdateSearchResults();
+}
+
+void CookiesView::UpdateForEmptyState() {
+ info_view_->ClearCookieDisplay();
+ remove_button_->SetEnabled(false);
+ remove_all_button_->SetEnabled(false);
+}
diff --git a/chrome/browser/views/options/cookies_view.h b/chrome/browser/views/options/cookies_view.h
new file mode 100644
index 0000000..9547acc
--- /dev/null
+++ b/chrome/browser/views/options/cookies_view.h
@@ -0,0 +1,136 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_COOKIES_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_COOKIES_VIEW_H__
+
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/view.h"
+#include "chrome/views/window.h"
+
+namespace ChromeViews {
+class Label;
+}
+class CookieInfoView;
+class CookiesTableModel;
+class CookiesTableView;
+class Profile;
+class Timer;
+
+class CookiesView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::TableViewObserver,
+ public ChromeViews::TextField::Controller {
+ public:
+ // Show the Cookies Window, creating one if necessary.
+ static void ShowCookiesWindow(Profile* profile);
+
+ virtual ~CookiesView();
+
+ // Updates the display to show only the search results.
+ void UpdateSearchResults();
+
+ // ChromeViews::NativeButton::Listener implementation:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // ChromeViews::TableViewObserver implementation:
+ virtual void OnSelectionChanged();
+
+ // ChromeViews::TextField::Controller implementation:
+ virtual void ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key, UINT repeat_count,
+ UINT flags);
+
+ // ChromeViews::WindowDelegate implementation:
+ virtual int GetDialogButtons() const { return DIALOGBUTTON_CANCEL; }
+ virtual ChromeViews::View* GetInitiallyFocusedView() const {
+ return search_field_;
+ }
+ virtual bool CanResize() const { return true; }
+ virtual std::wstring GetWindowTitle() const;
+ virtual void WindowClosing();
+
+ // ChromeViews::View overrides:
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize* out);
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ // Use the static factory method to show.
+ explicit CookiesView(Profile* profile);
+
+ // Initialize the dialog contents and layout.
+ void Init();
+
+ // Resets the display to what it would be if there were no search query.
+ void ResetSearchQuery();
+
+ // Update the UI when there are no cookies.
+ void UpdateForEmptyState();
+
+ // Assorted dialog controls
+ ChromeViews::Label* search_label_;
+ ChromeViews::TextField* search_field_;
+ ChromeViews::NativeButton* clear_search_button_;
+ ChromeViews::Label* description_label_;
+ CookiesTableView* cookies_table_;
+ CookieInfoView* info_view_;
+ ChromeViews::NativeButton* remove_button_;
+ ChromeViews::NativeButton* remove_all_button_;
+
+ // The Cookies Table model
+ scoped_ptr<CookiesTableModel> cookies_table_model_;
+ scoped_ptr<CookiesTableModel> search_table_model_;
+
+ // The Profile for which Cookies are displayed
+ Profile* profile_;
+
+ // A factory to construct Runnable Methods so that we can be called back to
+ // re-evaluate the model after the search query string changes.
+ ScopedRunnableMethodFactory<CookiesView> search_update_factory_;
+
+ // Our containing window. If this is non-NULL there is a visible Cookies
+ // window somewhere.
+ static ChromeViews::Window* instance_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CookiesView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_GENERAL_PAGE_VIEW_H__
diff --git a/chrome/browser/views/options/fonts_languages_window_view.cc b/chrome/browser/views/options/fonts_languages_window_view.cc
new file mode 100644
index 0000000..7b7788d
--- /dev/null
+++ b/chrome/browser/views/options/fonts_languages_window_view.cc
@@ -0,0 +1,115 @@
+// 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/views/options/fonts_languages_window_view.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/views/options/fonts_page_view.h"
+#include "chrome/browser/views/options/languages_page_view.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/window.h"
+#include "generated_resources.h"
+
+// static
+static FontsLanguagesWindowView* instance_ = NULL;
+static const int kDialogPadding = 7;
+
+///////////////////////////////////////////////////////////////////////////////
+// FontsLanguagesWindowView, public:
+
+FontsLanguagesWindowView::FontsLanguagesWindowView(Profile* profile)
+ // Always show preferences for the original profile. Most state when off
+ // the record comes from the original profile, but we explicitly use
+ // the original profile to avoid potential problems.
+ : profile_(profile->GetOriginalProfile()),
+ fonts_page_(NULL),
+ languages_page_(NULL) {
+}
+
+FontsLanguagesWindowView::~FontsLanguagesWindowView() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FontsLanguagesWindowView, ChromeViews::DialogDelegate implementation:
+
+std::wstring FontsLanguagesWindowView::GetWindowTitle() const {
+ return l10n_util::GetStringF(IDS_FONT_LANGUAGE_SETTING_WINDOWS_TITLE,
+ l10n_util::GetString(IDS_PRODUCT_NAME));
+}
+
+bool FontsLanguagesWindowView::Accept() {
+ fonts_page_->SaveChanges();
+ languages_page_->SaveChanges();
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FontsLanguagesWindowView, ChromeViews::View overrides:
+
+void FontsLanguagesWindowView::Layout() {
+ tabs_->SetBounds(kDialogPadding, kDialogPadding,
+ GetWidth() - (2 * kDialogPadding),
+ GetHeight() - (2 * kDialogPadding));
+}
+
+void FontsLanguagesWindowView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_FONTSLANG_DIALOG_WIDTH_CHARS,
+ IDS_FONTSLANG_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void FontsLanguagesWindowView::ViewHierarchyChanged(
+ bool is_add, ChromeViews::View* parent, ChromeViews::View* child) {
+ // Can't init before we're inserted into a ViewContainer, because we require
+ // a HWND to parent native child controls to.
+ if (is_add && child == this)
+ Init();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FontsLanguagesWindowView, private:
+
+void FontsLanguagesWindowView::Init() {
+ tabs_ = new ChromeViews::TabbedPane;
+ AddChildView(tabs_);
+
+ fonts_page_ = new FontsPageView(profile_);
+ tabs_->AddTabAtIndex(0, l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_TAB_TITLE), fonts_page_, true);
+
+ languages_page_ = new LanguagesPageView(profile_);
+ tabs_->AddTabAtIndex(1, l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_LANGUAGES_TAB_TITLE), languages_page_, true);
+}
diff --git a/chrome/browser/views/options/fonts_languages_window_view.h b/chrome/browser/views/options/fonts_languages_window_view.h
new file mode 100644
index 0000000..bdae5ea
--- /dev/null
+++ b/chrome/browser/views/options/fonts_languages_window_view.h
@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef CHROME_BROWSER_FONTS_LANGUAGE_WINDOW_H__
+#define CHROME_BROWSER_FONTS_LANGUAGE_WINDOW_H__
+
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/tabbed_pane.h"
+#include "chrome/views/view.h"
+#include "chrome/views/window.h"
+
+class Profile;
+class FontsPageView;
+class LanguagesPageView;
+
+///////////////////////////////////////////////////////////////////////////////
+// FontsLanguagesWindowView
+//
+// The contents of the "Fonts and Languages Preferences" dialog window.
+//
+class FontsLanguagesWindowView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate {
+ public:
+ explicit FontsLanguagesWindowView(Profile* profile);
+ virtual ~FontsLanguagesWindowView();
+
+ ChromeViews::Window* container() const { return container_; }
+ void set_container(ChromeViews::Window* container) {
+ container_ = container;
+ }
+
+ // ChromeViews::DialogDelegate implementation:
+ virtual bool Accept();
+ virtual std::wstring GetWindowTitle() const;
+
+ // ChromeViews::WindowDelegate Methods:
+ virtual bool IsModal() const { return true; }
+
+ // ChromeViews::View overrides:
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize* out);
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+ private:
+ // Init the assorted Tabbed pages
+ void Init();
+
+ // The Tab view that contains all of the options pages.
+ ChromeViews::TabbedPane* tabs_;
+
+ // The Options dialog window.
+ ChromeViews::Window* container_;
+
+ // Fonts Page View handle remembered so that prefs is updated only when
+ // OK is pressed.
+ FontsPageView* fonts_page_;
+
+ // Languages Page View handle remembered so that prefs is updated only when
+ // OK is pressed.
+ LanguagesPageView* languages_page_;
+
+ // The Profile associated with these options.
+ Profile* profile_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FontsLanguagesWindowView);
+};
+
+#endif // #ifndef CHROME_BROWSER_FONTS_LANGUAGE_WINDOW_H__
diff --git a/chrome/browser/views/options/fonts_page_view.cc b/chrome/browser/views/options/fonts_page_view.cc
new file mode 100644
index 0000000..0241cc0
--- /dev/null
+++ b/chrome/browser/views/options/fonts_page_view.cc
@@ -0,0 +1,509 @@
+// 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 <windows.h>
+#include <shlobj.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "chrome/browser/views/options/fonts_page_view.h"
+
+#include "base/file_util.h"
+#include "base/gfx/native_theme.h"
+#include "base/gfx/skia_utils.h"
+#include "base/string_util.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/character_encoding.h"
+#include "chrome/browser/shell_dialogs.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/password_manager_view.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/radio_button.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/view_container.h"
+#include "generated_resources.h"
+#include "skia/include/SkBitmap.h"
+
+class DefaultEncodingComboboxModel : public ChromeViews::ComboBox::Model {
+ public:
+ DefaultEncodingComboboxModel() {
+ canonical_encoding_names_length_ =
+ CharacterEncoding::GetSupportCanonicalEncodingCount();
+ }
+
+ virtual ~DefaultEncodingComboboxModel() {}
+
+ // Overridden from ChromeViews::Combobox::Model.
+ virtual int GetItemCount(ChromeViews::ComboBox* source) {
+ return canonical_encoding_names_length_;
+ }
+
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index) {
+ DCHECK(index >= 0 && canonical_encoding_names_length_ > index);
+ return CharacterEncoding::GetCanonicalEncodingDisplayNameByIndex(index);
+ }
+
+ std::wstring GetEncodingCharsetByIndex(int index) {
+ DCHECK(index >= 0 && canonical_encoding_names_length_ > index);
+ return CharacterEncoding::GetCanonicalEncodingNameByIndex(index);
+ }
+
+ int GetSelectedEncodingIndex(Profile* profile) {
+ StringPrefMember current_encoding_string;
+ current_encoding_string.Init(prefs::kDefaultCharset,
+ profile->GetPrefs(),
+ NULL);
+ const std::wstring current_encoding = current_encoding_string.GetValue();
+ for (int i = 0; i < canonical_encoding_names_length_; i++) {
+ if (CharacterEncoding::GetCanonicalEncodingNameByIndex(i) ==
+ current_encoding)
+ return i;
+ }
+
+ return 0;
+ }
+
+ private:
+ int canonical_encoding_names_length_;
+ DISALLOW_EVIL_CONSTRUCTORS(DefaultEncodingComboboxModel);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// FontDisplayView
+
+class FontDisplayView : public ChromeViews::View {
+ public:
+ FontDisplayView();
+ virtual ~FontDisplayView();
+
+ void SetFontType(const std::wstring& font_name,
+ int font_size);
+
+ void SetFontType(ChromeFont font);
+
+ std::wstring font_name() { return font_name_; }
+ int font_size() { return font_size_; }
+
+ // ChromeViews::View overrides:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize* out);
+
+ private:
+ ChromeViews::Label* font_text_label_;
+ std::wstring font_name_;
+ int font_size_;
+ std::wstring font_text_label_string_;
+
+ static const int kFontDisplayMaxWidthChars = 50;
+ static const int kFontDisplayMaxHeightChars = 1;
+ static const int kFontDisplayLabelPadding = 5;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FontDisplayView);
+};
+
+FontDisplayView::FontDisplayView()
+ : font_text_label_(new ChromeViews::Label) {
+ AddChildView(font_text_label_);
+}
+
+FontDisplayView::~FontDisplayView() {
+}
+
+void FontDisplayView::SetFontType(ChromeFont font) {
+ if (font.FontName().empty())
+ return;
+
+ font_name_ = font.FontName();
+ font_size_ = font.FontSize();
+
+ // Append the font type and size here.
+ std::wstring displayed_text = font_name_;
+ displayed_text += L", ";
+ displayed_text += UTF8ToWide(StringPrintf("%d", font_size_));
+ displayed_text += L"pt";
+
+ // Set Label.
+ font_text_label_->SetText(displayed_text);
+ font_text_label_->SetFont(font);
+}
+
+void FontDisplayView::SetFontType(const std::wstring& font_name,
+ int font_size) {
+ if (font_name.empty())
+ return;
+
+ font_name_ = font_name;
+ font_size_ = font_size;
+ std::wstring displayed_text = font_name_;
+
+ // Append the font type and size.
+ displayed_text += L", ";
+ displayed_text += UTF8ToWide(::StringPrintf("%d", font_size_));
+ displayed_text += L"pt";
+ ChromeFont font = ChromeFont::CreateFont(font_name, font_size);
+ font_text_label_->SetFont(font);
+ font_text_label_->SetText(displayed_text);
+}
+
+void FontDisplayView::Paint(ChromeCanvas* canvas) {
+ HDC dc = canvas->beginPlatformPaint();
+ RECT rect = { 0, 0, GetWidth(), GetHeight() };
+ gfx::NativeTheme::instance()->PaintTextField(
+ dc, EP_BACKGROUND, EBS_NORMAL, 0, &rect, ::GetSysColor(COLOR_3DFACE),
+ true, true);
+ canvas->endPlatformPaint();
+}
+
+void FontDisplayView::Layout() {
+ font_text_label_->SetBounds(0, 0, GetWidth(), GetHeight());
+}
+
+void FontDisplayView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont font = rb.GetFont(ResourceBundle::BaseFont);
+ out->cx = font.ave_char_width() * kFontDisplayMaxWidthChars;
+ out->cy = font.height() * kFontDisplayMaxHeightChars
+ + 2 * kFontDisplayLabelPadding;
+}
+
+void EmbellishTitle(ChromeViews::Label* title_label) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont title_font =
+ rb.GetFont(ResourceBundle::BaseFont).DeriveFont(0, ChromeFont::BOLD);
+ title_label->SetFont(title_font);
+ SkColor title_color =
+ gfx::NativeTheme::instance()->GetThemeColorWithDefault(
+ gfx::NativeTheme::BUTTON, BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR,
+ COLOR_WINDOWTEXT);
+ title_label->SetColor(title_color);
+}
+
+FontsPageView::FontsPageView(Profile* profile)
+ : select_font_dialog_(SelectFontDialog::Create(this)),
+ fonts_group_title_(NULL),
+ encoding_group_title_(NULL),
+ fixed_width_font_change_page_button_(NULL),
+ serif_font_change_page_button_(NULL),
+ sans_serif_font_change_page_button_(NULL),
+ fixed_width_font_label_(NULL),
+ serif_font_label_(NULL),
+ sans_serif_font_label_(NULL),
+ default_encoding_combobox_(NULL),
+ serif_button_pressed_(false),
+ sans_serif_button_pressed_(false),
+ fixed_width_button_pressed_(false),
+ encoding_dropdown_clicked_(false),
+ font_type_being_changed_(NONE),
+ OptionsPageView(profile),
+ font_changed_(false),
+ default_encoding_changed_(false) {
+ serif_name_.Init(prefs::kWebKitSerifFontFamily, profile->GetPrefs(), NULL);
+ serif_size_.Init(prefs::kWebKitDefaultFontSize, profile->GetPrefs(), NULL);
+
+ sans_serif_name_.Init(prefs::kWebKitSansSerifFontFamily, profile->GetPrefs(),
+ NULL);
+ sans_serif_size_.Init(prefs::kWebKitDefaultFontSize, profile->GetPrefs(),
+ NULL);
+
+ fixed_width_name_.Init(prefs::kWebKitFixedFontFamily, profile->GetPrefs(),
+ NULL);
+ fixed_width_size_.Init(prefs::kWebKitDefaultFixedFontSize,
+ profile->GetPrefs(), NULL);
+
+ default_encoding_.Init(prefs::kDefaultCharset, profile->GetPrefs(), NULL);
+}
+
+FontsPageView::~FontsPageView() {
+}
+
+void FontsPageView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ HWND owning_hwnd = GetAncestor(GetViewContainer()->GetHWND(), GA_ROOT);
+ std::wstring font_name;
+ int font_size = 0;
+ if (sender == serif_font_change_page_button_) {
+ font_type_being_changed_ = SERIF;
+ font_name = serif_font_display_view_->font_name();
+ font_size = serif_font_display_view_->font_size();
+ } else if (sender == sans_serif_font_change_page_button_) {
+ font_type_being_changed_ = SANS_SERIF;
+ font_name = sans_serif_font_display_view_->font_name();
+ font_size = sans_serif_font_display_view_->font_size();
+ } else if (sender == fixed_width_font_change_page_button_) {
+ font_type_being_changed_ = FIXED_WIDTH;
+ font_name = fixed_width_font_display_view_->font_name();
+ font_size = fixed_width_font_display_view_->font_size();
+ } else {
+ NOTREACHED();
+ return;
+ }
+
+ select_font_dialog_->SelectFont(owning_hwnd, NULL, font_name, font_size);
+}
+
+void FontsPageView::ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index, int new_index) {
+ if (combo_box == default_encoding_combobox_) {
+ if (prev_index != new_index) { // Default-Encoding has been changed.
+ encoding_dropdown_clicked_ = true;
+ default_encoding_selected_ = default_encoding_combobox_model_->
+ GetEncodingCharsetByIndex(new_index);
+ default_encoding_changed_ = true;
+ }
+ }
+}
+
+void FontsPageView::FontSelected(const ChromeFont& font, void* params) {
+ if (ChromeFont(font).FontName().empty())
+ return;
+ int font_size = ChromeFont(font).FontSize();
+ if (font_type_being_changed_ == SERIF) {
+ serif_font_display_view_->SetFontType(font);
+ sans_serif_font_display_view_->SetFontType(
+ sans_serif_font_display_view_->font_name(), font_size);
+ } else if (font_type_being_changed_ == SANS_SERIF) {
+ sans_serif_font_display_view_->SetFontType(font);
+ serif_font_display_view_->SetFontType(
+ serif_font_display_view_->font_name(), font_size);
+ } else if (font_type_being_changed_ == FIXED_WIDTH) {
+ fixed_width_font_display_view_->SetFontType(font);
+ }
+ font_changed_ = true;
+}
+
+void FontsPageView::SaveChanges() {
+ // Set Fonts.
+ if (font_changed_) {
+ serif_name_.SetValue(serif_font_display_view_->font_name());
+ serif_size_.SetValue(serif_font_display_view_->font_size());
+ sans_serif_name_.SetValue(sans_serif_font_display_view_->font_name());
+ sans_serif_size_.SetValue(sans_serif_font_display_view_->font_size());
+ fixed_width_name_.SetValue(fixed_width_font_display_view_->font_name());
+ fixed_width_size_.SetValue(fixed_width_font_display_view_->font_size());
+ }
+ // Set Encoding.
+ if (default_encoding_changed_)
+ default_encoding_.SetValue(default_encoding_selected_);
+}
+
+void FontsPageView::InitControlLayout() {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+
+ // Fonts group.
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1,
+ GridLayout::USE_PREF, 0, 0);
+ fonts_group_title_ = new ChromeViews::Label(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SUB_DIALOG_FONT_TITLE));
+ EmbellishTitle(fonts_group_title_);
+ fonts_group_title_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(fonts_group_title_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ InitFontLayout();
+ layout->AddView(fonts_contents_);
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+
+ // Encoding group.
+ encoding_group_title_ = new ChromeViews::Label(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SUB_DIALOG_ENCODING_TITLE));
+ EmbellishTitle(encoding_group_title_);
+ encoding_group_title_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(encoding_group_title_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ InitEncodingLayout();
+ layout->AddView(encoding_contents_);
+}
+
+void FontsPageView::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kWebKitFixedFontFamily) {
+ fixed_width_font_display_view_->SetFontType(
+ fixed_width_name_.GetValue(),
+ fixed_width_size_.GetValue());
+ }
+ if (!pref_name || *pref_name == prefs::kWebKitSerifFontFamily) {
+ serif_font_display_view_->SetFontType(
+ serif_name_.GetValue(),
+ serif_size_.GetValue());
+ }
+ if (!pref_name || *pref_name == prefs::kWebKitSansSerifFontFamily) {
+ sans_serif_font_display_view_->SetFontType(
+ sans_serif_name_.GetValue(),
+ sans_serif_size_.GetValue());
+ }
+}
+
+void FontsPageView::InitFontLayout() {
+ // Fixed width.
+ fixed_width_font_display_view_ = new FontDisplayView;
+ fixed_width_font_change_page_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_BUTTON_LABEL));
+ fixed_width_font_change_page_button_->SetListener(this);
+
+ fixed_width_font_label_ = new ChromeViews::Label(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_FIXED_WIDTH_LABEL));
+ fixed_width_font_label_->SetHorizontalAlignment(ChromeViews::Label::
+ ALIGN_LEFT);
+ fixed_width_font_label_->SetMultiLine(true);
+
+ // Serif font.
+ serif_font_display_view_ = new FontDisplayView;
+ serif_font_change_page_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_BUTTON_LABEL));
+ serif_font_change_page_button_->SetListener(this);
+
+ serif_font_label_ = new ChromeViews::Label(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SERIF_LABEL));
+ serif_font_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ serif_font_label_->SetMultiLine(true);
+
+ // Sans Serif font.
+ sans_serif_font_display_view_ = new FontDisplayView;
+ sans_serif_font_change_page_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_BUTTON_LABEL));
+ sans_serif_font_change_page_button_->SetListener(this);
+
+ sans_serif_font_label_ = new ChromeViews::Label(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SANS_SERIF_LABEL));
+ sans_serif_font_label_->SetHorizontalAlignment(ChromeViews::Label::
+ ALIGN_LEFT);
+ sans_serif_font_label_->SetMultiLine(true);
+
+ // Now add the views.
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ fonts_contents_ = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(fonts_contents_);
+ fonts_contents_->SetLayoutManager(layout);
+
+ const int triple_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(triple_column_view_set_id);
+
+ int label_width =
+ _wtoi(l10n_util::GetString(IDS_FONTSLANG_LABEL_WIDTH).c_str()) *
+ ChromeFont().ave_char_width();
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::FIXED, label_width, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ // Serif font controls.
+ layout->StartRow(0, triple_column_view_set_id);
+ layout->AddView(serif_font_label_);
+ layout->AddView(serif_font_display_view_, 1, 1,
+ GridLayout::FILL, GridLayout::CENTER);
+ layout->AddView(serif_font_change_page_button_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Sans serif font controls.
+ layout->StartRow(0, triple_column_view_set_id);
+ layout->AddView(sans_serif_font_label_);
+ layout->AddView(sans_serif_font_display_view_, 1, 1,
+ GridLayout::FILL, GridLayout::CENTER);
+ layout->AddView(sans_serif_font_change_page_button_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Fixed-width font controls.
+ layout->StartRow(0, triple_column_view_set_id);
+ layout->AddView(fixed_width_font_label_);
+ layout->AddView(fixed_width_font_display_view_, 1, 1,
+ GridLayout::FILL, GridLayout::CENTER);
+ layout->AddView(fixed_width_font_change_page_button_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+}
+
+void FontsPageView::InitEncodingLayout() {
+ default_encoding_combobox_label_ = new ChromeViews::Label(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_FONT_DEFAULT_ENCODING_SELECTOR_LABEL));
+ default_encoding_combobox_model_.reset(new DefaultEncodingComboboxModel);
+ default_encoding_combobox_ = new ChromeViews::ComboBox(
+ default_encoding_combobox_model_.get());
+ int selected_encoding_index = default_encoding_combobox_model_->
+ GetSelectedEncodingIndex(profile());
+ default_encoding_combobox_->SetSelectedItem(selected_encoding_index);
+ default_encoding_selected_ = default_encoding_combobox_model_->
+ GetEncodingCharsetByIndex(selected_encoding_index);
+ default_encoding_combobox_->SetListener(this);
+
+ // Now add the views.
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ encoding_contents_ = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(encoding_contents_);
+ encoding_contents_->SetLayoutManager(layout);
+
+ // Double column.
+ const int double_column_view_set_id = 2;
+ ColumnSet* column_set = layout->AddColumnSet(double_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ // Add Encoding ComboBox.
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(default_encoding_combobox_label_);
+ layout->AddView(default_encoding_combobox_, 1, 1, GridLayout::FILL,
+ GridLayout::CENTER);
+}
diff --git a/chrome/browser/views/options/fonts_page_view.h b/chrome/browser/views/options/fonts_page_view.h
new file mode 100644
index 0000000..22a9bf6
--- /dev/null
+++ b/chrome/browser/views/options/fonts_page_view.h
@@ -0,0 +1,148 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_FONTS_PAGE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_FONTS_PAGE_VIEW_H__
+
+#include "chrome/browser/views/options/options_page_view.h"
+#include "chrome/browser/shell_dialogs.h"
+#include "chrome/common/pref_member.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/view.h"
+
+
+namespace ChromeViews {
+class GroupboxView;
+class Label;
+class TableModel;
+class TableView;
+}
+
+class FontDisplayView;
+class DefaultEncodingComboboxModel;
+
+///////////////////////////////////////////////////////////////////////////////
+// FontsPageView
+
+class FontsPageView : public OptionsPageView,
+ public ChromeViews::ComboBox::Listener,
+ public SelectFontDialog::Listener,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ explicit FontsPageView(Profile* profile);
+ virtual ~FontsPageView();
+
+ // ChromeViews::NativeButton::Listener implementation:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // ChromeViews::ComboBox::Listener implementation:
+ virtual void ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index,
+ int new_index);
+
+ // SelectFontDialog::Listener implementation:
+ virtual void FontSelected(const ChromeFont& font, void* params);
+
+ // Save Changes made to relevent pref members associated with this tab.
+ // This is public since it is called by FontsLanguageWindowView in its
+ // Dialog Delegate Accept() method.
+ void SaveChanges();
+
+ protected:
+ // OptionsPageView implementation:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+
+ private:
+ enum FontTypeBeingChanged {
+ NONE,
+ SERIF,
+ SANS_SERIF,
+ FIXED_WIDTH
+ };
+
+ // Init Dialog controls.
+ void InitFontLayout();
+ void InitEncodingLayout();
+
+ bool serif_button_pressed_;
+ bool sans_serif_button_pressed_;
+ bool fixed_width_button_pressed_;
+ bool encoding_dropdown_clicked_;
+
+ ChromeViews::Label* fonts_group_title_;
+ ChromeViews::Label* encoding_group_title_;
+
+ ChromeViews::View* fonts_contents_;
+ ChromeViews::View* encoding_contents_;
+
+ // Fonts settings.
+ // Select Font dialogs.
+ scoped_refptr<SelectFontDialog> select_font_dialog_;
+
+ // Buttons.
+ ChromeViews::NativeButton* fixed_width_font_change_page_button_;
+ ChromeViews::NativeButton* serif_font_change_page_button_;
+ ChromeViews::NativeButton* sans_serif_font_change_page_button_;
+
+ // FontDisplayView objects to display selected font.
+ FontDisplayView* fixed_width_font_display_view_;
+ FontDisplayView* serif_font_display_view_;
+ FontDisplayView* sans_serif_font_display_view_;
+
+ // Labels to describe what is to be changed.
+ ChromeViews::Label* fixed_width_font_label_;
+ ChromeViews::Label* serif_font_label_;
+ ChromeViews::Label* sans_serif_font_label_;
+
+ // Advanced Font names and sizes as PrefMembers.
+ StringPrefMember serif_name_;
+ StringPrefMember sans_serif_name_;
+ StringPrefMember fixed_width_name_;
+ IntegerPrefMember serif_size_;
+ IntegerPrefMember sans_serif_size_;
+ IntegerPrefMember fixed_width_size_;
+ StringPrefMember default_encoding_;
+ bool font_changed_;
+
+ // Windows font picker flag;
+ FontTypeBeingChanged font_type_being_changed_;
+
+ // Default Encoding.
+ scoped_ptr<DefaultEncodingComboboxModel> default_encoding_combobox_model_;
+ ChromeViews::Label* default_encoding_combobox_label_;
+ ChromeViews::ComboBox* default_encoding_combobox_;
+ std::wstring default_encoding_selected_;
+ bool default_encoding_changed_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FontsPageView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_FONTS_PAGE_VIEW_H__
diff --git a/chrome/browser/views/options/general_page_view.cc b/chrome/browser/views/options/general_page_view.cc
new file mode 100644
index 0000000..4544e67
--- /dev/null
+++ b/chrome/browser/views/options/general_page_view.cc
@@ -0,0 +1,1094 @@
+// 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/views/options/general_page_view.h"
+
+#include "base/gfx/png_decoder.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/dom_ui/new_tab_ui.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/session_startup_pref.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/template_url.h"
+#include "chrome/browser/template_url_model.h"
+#include "chrome/browser/url_fixer_upper.h"
+#include "chrome/browser/views/keyword_editor_view.h"
+#include "chrome/browser/views/options/options_group_view.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/radio_button.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/text_field.h"
+#include "generated_resources.h"
+#include "skia/include/SkBitmap.h"
+
+static const int kStartupRadioGroup = 1;
+static const int kHomePageRadioGroup = 2;
+static const SkColor kDefaultBrowserLabelColor = SkColorSetRGB(0, 135, 0);
+static const SkColor kNotDefaultBrowserLabelColor = SkColorSetRGB(135, 0, 0);
+
+namespace {
+std::wstring GetNewTabUIURLString() {
+ return UTF8ToWide(NewTabUIURL().spec());
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView::DefaultBrowserWorker
+//
+// A helper object that handles checking if Chrome is the default browser on
+// Windows and also setting it as the default browser. These operations are
+// performed asynchronously on the file thread since registry access is
+// involved and this can be slow.
+//
+class GeneralPageView::DefaultBrowserWorker
+ : public base::RefCountedThreadSafe<GeneralPageView::DefaultBrowserWorker> {
+ public:
+ explicit DefaultBrowserWorker(GeneralPageView* general_page_view);
+
+ // Checks if Chrome is the default browser.
+ void StartCheckDefaultBrowser();
+
+ // Sets Chrome as the default browser.
+ void StartSetAsDefaultBrowser();
+
+ // Called to notify the worker that the view is gone.
+ void ViewDestroyed();
+
+ private:
+ // Functions that track the process of checking if Chrome is the default
+ // browser.
+ // |ExecuteCheckDefaultBrowser| checks the registry on the file thread.
+ // |CompleteCheckDefaultBrowser| notifies the view to update on the UI thread.
+ void ExecuteCheckDefaultBrowser();
+ void CompleteCheckDefaultBrowser(bool is_default);
+
+ // Functions that track the process of setting Chrome as the default browser.
+ // |ExecuteSetAsDefaultBrowser| updates the registry on the file thread.
+ // |CompleteSetAsDefaultBrowser| notifies the view to update on the UI thread.
+ void ExecuteSetAsDefaultBrowser();
+ void CompleteSetAsDefaultBrowser();
+
+ // Updates the UI in our associated view with the current default browser
+ // state.
+ void UpdateUI(bool is_default);
+
+ GeneralPageView* general_page_view_;
+
+ MessageLoop* ui_loop_;
+ MessageLoop* file_loop_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(GeneralPageView::DefaultBrowserWorker);
+};
+
+GeneralPageView::DefaultBrowserWorker::DefaultBrowserWorker(
+ GeneralPageView* general_page_view)
+ : general_page_view_(general_page_view),
+ ui_loop_(MessageLoop::current()),
+ file_loop_(g_browser_process->file_thread()->message_loop()) {
+}
+
+void GeneralPageView::DefaultBrowserWorker::StartCheckDefaultBrowser() {
+ general_page_view_->SetDefaultBrowserUIState(STATE_PROCESSING);
+ file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &DefaultBrowserWorker::ExecuteCheckDefaultBrowser));
+}
+
+void GeneralPageView::DefaultBrowserWorker::StartSetAsDefaultBrowser() {
+ general_page_view_->SetDefaultBrowserUIState(STATE_PROCESSING);
+ file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &DefaultBrowserWorker::ExecuteSetAsDefaultBrowser));
+}
+
+void GeneralPageView::DefaultBrowserWorker::ViewDestroyed() {
+ // Our associated view has gone away, so we shouldn't call back to it if
+ // our worker thread returns after the view is dead.
+ general_page_view_ = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DefaultBrowserWorker, private:
+
+void GeneralPageView::DefaultBrowserWorker::ExecuteCheckDefaultBrowser() {
+ DCHECK(MessageLoop::current() == file_loop_);
+ bool is_default = ShellIntegration::IsDefaultBrowser();
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &DefaultBrowserWorker::CompleteCheckDefaultBrowser, is_default));
+}
+
+void GeneralPageView::DefaultBrowserWorker::CompleteCheckDefaultBrowser(
+ bool is_default) {
+ DCHECK(MessageLoop::current() == ui_loop_);
+ UpdateUI(is_default);
+}
+
+void GeneralPageView::DefaultBrowserWorker::ExecuteSetAsDefaultBrowser() {
+ DCHECK(MessageLoop::current() == file_loop_);
+ bool result = ShellIntegration::SetAsDefaultBrowser();
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &DefaultBrowserWorker::CompleteSetAsDefaultBrowser));
+}
+
+void GeneralPageView::DefaultBrowserWorker::CompleteSetAsDefaultBrowser() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+ // Set as default completed, check again to make sure it stuck...
+ StartCheckDefaultBrowser();
+}
+
+void GeneralPageView::DefaultBrowserWorker::UpdateUI(bool is_default) {
+ if (general_page_view_) {
+ DefaultBrowserUIState state =
+ is_default ? STATE_DEFAULT : STATE_NOT_DEFAULT;
+ general_page_view_->SetDefaultBrowserUIState(state);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CustomHomePagesTableModel
+
+// CustomHomePagesTableModel is the model for the TableView showing the list
+// of pages the user wants opened on startup.
+
+class CustomHomePagesTableModel : public ChromeViews::TableModel {
+ public:
+ explicit CustomHomePagesTableModel(Profile* profile);
+ virtual ~CustomHomePagesTableModel() {}
+
+ // Sets the set of urls that this model contains.
+ void SetURLs(const std::vector<GURL>& urls);
+
+ // Adds an entry at the specified index.
+ void Add(int index, const GURL& url);
+
+ // Removes the entry at the specified index.
+ void Remove(int index);
+
+ // Returns the set of urls this model contains.
+ std::vector<GURL> GetURLs();
+
+ // ChromeViews::TableModel overrides:
+ virtual int RowCount();
+ virtual std::wstring GetText(int row, int column_id);
+ virtual SkBitmap GetIcon(int row);
+ virtual void SetObserver(ChromeViews::TableModelObserver* observer);
+
+ private:
+ // Each item in the model is represented as an Entry. Entry stores the URL
+ // and favicon of the page.
+ struct Entry {
+ Entry() : fav_icon_handle(0) {}
+
+ // URL of the page.
+ GURL url;
+
+ // Icon for the page.
+ SkBitmap icon;
+
+ // If non-zero, indicates we're loading the favicon for the page.
+ HistoryService::Handle fav_icon_handle;
+ };
+
+ static void InitClass();
+
+ // Loads the favicon for the specified entry.
+ void LoadFavIcon(Entry* entry);
+
+ // Callback from history service. Updates the icon of the Entry whose
+ // fav_icon_handle matches handle and notifies the observer of the change.
+ void OnGotFavIcon(HistoryService::Handle handle,
+ bool know_fav_icon,
+ scoped_refptr<RefCountedBytes> image_data,
+ bool is_expired,
+ GURL icon_url);
+
+ // Returns the entry whose fav_icon_handle matches handle and sets entry_index
+ // to the index of the entry.
+ Entry* GetEntryByLoadHandle(HistoryService::Handle handle, int* entry_index);
+
+ // Set of entries we're showing.
+ std::vector<Entry> entries_;
+
+ // Default icon to show when one can't be found for the URL.
+ static SkBitmap default_favicon_;
+
+ // Profile used to load icons.
+ Profile* profile_;
+
+ ChromeViews::TableModelObserver* observer_;
+
+ // Used in loading favicons.
+ CancelableRequestConsumer fav_icon_consumer_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CustomHomePagesTableModel);
+};
+
+// static
+SkBitmap CustomHomePagesTableModel::default_favicon_;
+
+CustomHomePagesTableModel::CustomHomePagesTableModel(Profile* profile)
+ : profile_(profile),
+ observer_(NULL) {
+ InitClass();
+}
+
+void CustomHomePagesTableModel::SetURLs(const std::vector<GURL>& urls) {
+ entries_.resize(urls.size());
+ for (size_t i = 0; i < urls.size(); ++i) {
+ entries_[i].url = urls[i];
+ LoadFavIcon(&(entries_[i]));
+ }
+ // Complete change, so tell the view to just rebuild itself.
+ if (observer_)
+ observer_->OnModelChanged();
+}
+
+void CustomHomePagesTableModel::Add(int index, const GURL& url) {
+ DCHECK(index >= 0 && index <= RowCount());
+ entries_.insert(entries_.begin() + static_cast<size_t>(index), Entry());
+ entries_[index].url = url;
+ LoadFavIcon(&(entries_[index]));
+ if (observer_)
+ observer_->OnItemsAdded(index, 1);
+}
+
+void CustomHomePagesTableModel::Remove(int index) {
+ DCHECK(index >= 0 && index < RowCount());
+ Entry* entry = &(entries_[index]);
+ if (entry->fav_icon_handle) {
+ // Pending load request, cancel it now so we don't deref a bogus pointer
+ // when we get loaded notification.
+ HistoryService* history =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (history)
+ history->CancelRequest(entry->fav_icon_handle);
+ }
+ entries_.erase(entries_.begin() + static_cast<size_t>(index));
+ if (observer_)
+ observer_->OnItemsRemoved(index, 1);
+}
+
+std::vector<GURL> CustomHomePagesTableModel::GetURLs() {
+ std::vector<GURL> urls(entries_.size());
+ for (size_t i = 0; i < entries_.size(); ++i)
+ urls[i] = entries_[i].url;
+ return urls;
+}
+
+int CustomHomePagesTableModel::RowCount() {
+ return static_cast<int>(entries_.size());
+}
+
+std::wstring CustomHomePagesTableModel::GetText(int row, int column_id) {
+ DCHECK(column_id == 0);
+ DCHECK(row >= 0 && row < RowCount());
+ return UTF8ToWide(entries_[row].url.spec());
+}
+
+SkBitmap CustomHomePagesTableModel::GetIcon(int row) {
+ DCHECK(row >= 0 && row < RowCount());
+ if (!entries_[row].icon.isNull())
+ return entries_[row].icon;
+ return default_favicon_;
+}
+
+void CustomHomePagesTableModel::SetObserver(
+ ChromeViews::TableModelObserver* observer) {
+ observer_ = observer;
+}
+
+void CustomHomePagesTableModel::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_favicon_ = *rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
+ initialized = true;
+ }
+}
+
+void CustomHomePagesTableModel::LoadFavIcon(Entry* entry) {
+ HistoryService* history =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (!history)
+ return;
+ entry->fav_icon_handle = history->GetFavIconForURL(
+ entry->url, &fav_icon_consumer_,
+ NewCallback(this, &CustomHomePagesTableModel::OnGotFavIcon));
+}
+
+void CustomHomePagesTableModel::OnGotFavIcon(
+ HistoryService::Handle handle,
+ bool know_fav_icon,
+ scoped_refptr<RefCountedBytes> image_data,
+ bool is_expired,
+ GURL icon_url) {
+ int entry_index;
+ Entry* entry = GetEntryByLoadHandle(handle, &entry_index);
+ DCHECK(entry);
+ entry->fav_icon_handle = 0;
+ if (know_fav_icon && image_data.get() && !image_data->data.empty()) {
+ int width, height;
+ std::vector<unsigned char> decoded_data;
+ if (PNGDecoder::Decode(&image_data->data.front(), image_data->data.size(),
+ PNGDecoder::FORMAT_BGRA, &decoded_data, &width,
+ &height)) {
+ entry->icon.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ entry->icon.allocPixels();
+ memcpy(entry->icon.getPixels(), &decoded_data.front(),
+ width * height * 4);
+ if (observer_)
+ observer_->OnItemsChanged(static_cast<int>(entry_index), 1);
+ }
+ }
+}
+
+CustomHomePagesTableModel::Entry*
+ CustomHomePagesTableModel::GetEntryByLoadHandle(
+ HistoryService::Handle handle,
+ int* index) {
+ for (size_t i = 0; i < entries_.size(); ++i) {
+ if (entries_[i].fav_icon_handle == handle) {
+ *index = static_cast<int>(i);
+ return &entries_[i];
+ }
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// SearchEngineListModel
+
+class SearchEngineListModel : public ChromeViews::ComboBox::Model,
+ public TemplateURLModelObserver {
+ public:
+ explicit SearchEngineListModel(Profile* profile);
+ virtual ~SearchEngineListModel();
+
+ // Sets the ComboBox. SearchEngineListModel needs a handle to the ComboBox
+ // so that when the TemplateURLModel changes the combobox can be updated.
+ void SetComboBox(ChromeViews::ComboBox* combo_box);
+
+ // ChromeViews::ComboBox::Model overrides:
+ virtual int GetItemCount(ChromeViews::ComboBox* source);
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index);
+
+ // Returns the TemplateURL at the specified index.
+ const TemplateURL* GetTemplateURLAt(int index);
+
+ TemplateURLModel* model() { return template_url_model_; }
+
+ private:
+ // TemplateURLModelObserver methods.
+ virtual void OnTemplateURLModelChanged();
+
+ // Recalculates the TemplateURLs to display and notifies the combobox.
+ void ResetContents();
+
+ // Resets the selection of the combobox based on the users selected search
+ // engine.
+ void ChangeComboBoxSelection();
+
+ TemplateURLModel* template_url_model_;
+
+ // The combobox hosting us.
+ ChromeViews::ComboBox* combo_box_;
+
+ // The TemplateURLs we're showing.
+ typedef std::vector<const TemplateURL*> TemplateURLs;
+ TemplateURLs template_urls_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SearchEngineListModel);
+};
+
+SearchEngineListModel::SearchEngineListModel(Profile* profile)
+ : template_url_model_(profile->GetTemplateURLModel()),
+ combo_box_(NULL) {
+ if (template_url_model_) {
+ template_url_model_->Load();
+ template_url_model_->AddObserver(this);
+ }
+ ResetContents();
+}
+
+SearchEngineListModel::~SearchEngineListModel() {
+ if (template_url_model_)
+ template_url_model_->RemoveObserver(this);
+}
+
+void SearchEngineListModel::SetComboBox(ChromeViews::ComboBox* combo_box) {
+ combo_box_ = combo_box;
+ if (template_url_model_ && template_url_model_->loaded())
+ ChangeComboBoxSelection();
+ else
+ combo_box_->SetEnabled(false);
+}
+
+int SearchEngineListModel::GetItemCount(ChromeViews::ComboBox* source) {
+ return static_cast<int>(template_urls_.size());
+}
+
+std::wstring SearchEngineListModel::GetItemAt(ChromeViews::ComboBox* source,
+ int index) {
+ DCHECK(index < GetItemCount(combo_box_));
+ return template_urls_[index]->short_name();
+}
+
+const TemplateURL* SearchEngineListModel::GetTemplateURLAt(int index) {
+ DCHECK(index >= 0 && index < static_cast<int>(template_urls_.size()));
+ return template_urls_[static_cast<int>(index)];
+}
+
+void SearchEngineListModel::OnTemplateURLModelChanged() {
+ ResetContents();
+}
+
+void SearchEngineListModel::ResetContents() {
+ if (!template_url_model_ || !template_url_model_->loaded())
+ return;
+ template_urls_.clear();
+ TemplateURLs model_urls = template_url_model_->GetTemplateURLs();
+ for (size_t i = 0; i < model_urls.size(); ++i) {
+ if (model_urls[i]->ShowInDefaultList())
+ template_urls_.push_back(model_urls[i]);
+ }
+
+ if (combo_box_) {
+ combo_box_->ModelChanged();
+ ChangeComboBoxSelection();
+ }
+}
+
+void SearchEngineListModel::ChangeComboBoxSelection() {
+ if (template_urls_.size()) {
+ combo_box_->SetEnabled(true);
+
+ const TemplateURL* default_search_provider =
+ template_url_model_->GetDefaultSearchProvider();
+ if (default_search_provider) {
+ TemplateURLs::iterator i =
+ find(template_urls_.begin(), template_urls_.end(),
+ default_search_provider);
+ if (i != template_urls_.end()) {
+ combo_box_->SetSelectedItem(
+ static_cast<int>(i - template_urls_.begin()));
+ }
+ }
+ } else {
+ combo_box_->SetEnabled(false);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView, public:
+
+GeneralPageView::GeneralPageView(Profile* profile)
+ : startup_group_(NULL),
+ startup_homepage_radio_(NULL),
+ startup_last_session_radio_(NULL),
+ startup_custom_radio_(NULL),
+ startup_add_custom_page_button_(NULL),
+ startup_remove_custom_page_button_(NULL),
+ startup_use_current_page_button_(NULL),
+ startup_custom_pages_table_(NULL),
+ homepage_group_(NULL),
+ homepage_use_newtab_radio_(NULL),
+ homepage_use_url_radio_(NULL),
+ homepage_use_url_textfield_(NULL),
+ homepage_show_home_button_checkbox_(NULL),
+ default_search_group_(NULL),
+ default_search_manage_engines_button_(NULL),
+ default_browser_group_(NULL),
+ default_browser_status_label_(NULL),
+ default_browser_use_as_default_button_(NULL),
+ default_browser_worker_(new DefaultBrowserWorker(this)),
+ OptionsPageView(profile) {
+}
+
+GeneralPageView::~GeneralPageView() {
+ profile()->GetPrefs()->RemovePrefObserver(prefs::kRestoreOnStartup, this);
+ profile()->GetPrefs()->RemovePrefObserver(
+ prefs::kURLsToRestoreOnStartup, this);
+ if (startup_custom_pages_table_)
+ startup_custom_pages_table_->SetModel(NULL);
+ default_browser_worker_->ViewDestroyed();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView, ChromeViews::NativeButton::Listener implementation:
+
+void GeneralPageView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == startup_homepage_radio_ ||
+ sender == startup_last_session_radio_ ||
+ sender == startup_custom_radio_) {
+ SaveStartupPref();
+ if (sender == startup_homepage_radio_) {
+ UserMetricsRecordAction(L"Options_Startup_Homepage",
+ profile()->GetPrefs());
+ } else if (sender == startup_last_session_radio_) {
+ UserMetricsRecordAction(L"Options_Startup_LastSession",
+ profile()->GetPrefs());
+ } else if (sender == startup_custom_radio_) {
+ UserMetricsRecordAction(L"Options_Startup_Custom",
+ profile()->GetPrefs());
+ }
+ } else if (sender == startup_add_custom_page_button_) {
+ AddURLToStartupURLs();
+ } else if (sender == startup_remove_custom_page_button_) {
+ RemoveURLsFromStartupURLs();
+ } else if (sender == startup_use_current_page_button_) {
+ SetStartupURLToCurrentPage();
+ } else if (sender == homepage_use_newtab_radio_) {
+ UserMetricsRecordAction(L"Options_Homepage_UseNewTab",
+ profile()->GetPrefs());
+ homepage_.SetValue(GetNewTabUIURLString());
+ EnableHomepageURLField(false);
+ } else if (sender == homepage_use_url_radio_) {
+ UserMetricsRecordAction(L"Options_Homepage_UseURL",
+ profile()->GetPrefs());
+ std::wstring home_page_url = homepage_use_url_textfield_->GetText();
+ if (home_page_url.empty())
+ home_page_url = GetNewTabUIURLString();
+ homepage_.SetValue(home_page_url);
+ EnableHomepageURLField(true);
+ } else if (sender == homepage_show_home_button_checkbox_) {
+ bool show_button = homepage_show_home_button_checkbox_->IsSelected();
+ if (show_button) {
+ UserMetricsRecordAction(L"Options_Homepage_ShowHomeButton",
+ profile()->GetPrefs());
+ } else {
+ UserMetricsRecordAction(L"Options_Homepage_HideHomeButton",
+ profile()->GetPrefs());
+ }
+ show_home_button_.SetValue(show_button);
+ } else if (sender == default_browser_use_as_default_button_) {
+ default_browser_worker_->StartSetAsDefaultBrowser();
+ UserMetricsRecordAction(L"Options_SetAsDefaultBrowser", NULL);
+ } else if (sender == default_search_manage_engines_button_) {
+ UserMetricsRecordAction(L"Options_ManageSearchEngines", NULL);
+ KeywordEditorView::Show(profile());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView, ChromeViews::ComboBox::Listener implementation:
+
+void GeneralPageView::ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index, int new_index) {
+ if (combo_box == default_search_engine_combobox_) {
+ SetDefaultSearchProvider();
+ UserMetricsRecordAction(L"Options_SearchEngineChanged", NULL);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView, ChromeViews::TextField::Controller implementation:
+
+void GeneralPageView::ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents) {
+ if (sender == homepage_use_url_textfield_) {
+ // If the text field contains a valid URL, sync it to prefs. We run it
+ // through the fixer upper to allow input like "google.com" to be converted
+ // to something valid ("http://google.com").
+ std::wstring url_string = URLFixerUpper::FixupURL(
+ homepage_use_url_textfield_->GetText(), std::wstring());
+ if (GURL(url_string).is_valid())
+ homepage_.SetValue(url_string);
+ }
+}
+
+void GeneralPageView::HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key,
+ UINT repeat_count, UINT flags) {
+ // Not necessary.
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView, OptionsPageView implementation:
+
+void GeneralPageView::InitControlLayout() {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = new GridLayout(this);
+ layout->SetInsets(5, 5, 5, 5);
+ SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ layout->StartRow(0, single_column_view_set_id);
+ InitStartupGroup();
+ layout->AddView(startup_group_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ InitHomepageGroup();
+ layout->AddView(homepage_group_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ InitDefaultSearchGroup();
+ layout->AddView(default_search_group_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ InitDefaultBrowserGroup();
+ layout->AddView(default_browser_group_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Register pref observers that update the controls when a pref changes.
+ profile()->GetPrefs()->AddPrefObserver(prefs::kRestoreOnStartup, this);
+ profile()->GetPrefs()->AddPrefObserver(prefs::kURLsToRestoreOnStartup, this);
+
+ homepage_.Init(prefs::kHomePage, profile()->GetPrefs(), this);
+ show_home_button_.Init(prefs::kShowHomeButton, profile()->GetPrefs(), this);
+}
+
+void GeneralPageView::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kRestoreOnStartup) {
+ PrefService* prefs = profile()->GetPrefs();
+ const SessionStartupPref startup_pref =
+ SessionStartupPref::GetStartupPref(prefs);
+ switch (startup_pref.type) {
+ case SessionStartupPref::DEFAULT:
+ startup_homepage_radio_->SetIsSelected(true);
+ EnableCustomHomepagesControls(false);
+ break;
+
+ case SessionStartupPref::LAST:
+ startup_last_session_radio_->SetIsSelected(true);
+ EnableCustomHomepagesControls(false);
+ break;
+
+ case SessionStartupPref::URLS:
+ startup_custom_radio_->SetIsSelected(true);
+ EnableCustomHomepagesControls(true);
+ break;
+ }
+ }
+
+ // TODO(beng): Note that the kURLsToRestoreOnStartup pref is a mutable list,
+ // and changes to mutable lists aren't broadcast through the
+ // observer system, so the second half of this condition will
+ // never match. Once support for broadcasting such updates is
+ // added, this will automagically start to work, and this comment
+ // can be removed.
+ if (!pref_name || *pref_name == prefs::kURLsToRestoreOnStartup) {
+ PrefService* prefs = profile()->GetPrefs();
+ const SessionStartupPref startup_pref =
+ SessionStartupPref::GetStartupPref(prefs);
+ startup_custom_pages_table_model_->SetURLs(startup_pref.urls);
+ }
+
+ if (!pref_name || *pref_name == prefs::kHomePage) {
+ bool enabled = homepage_.GetValue() != GetNewTabUIURLString();
+ if (enabled) {
+ homepage_use_url_radio_->SetIsSelected(true);
+ homepage_use_url_textfield_->SetText(homepage_.GetValue());
+ } else {
+ homepage_use_newtab_radio_->SetIsSelected(true);
+ }
+ EnableHomepageURLField(enabled);
+ }
+
+ if (!pref_name || *pref_name == prefs::kShowHomeButton) {
+ homepage_show_home_button_checkbox_->SetIsSelected(
+ show_home_button_.GetValue());
+ }
+}
+
+void GeneralPageView::HighlightGroup(OptionsGroup highlight_group) {
+ if (highlight_group == OPTIONS_GROUP_DEFAULT_SEARCH)
+ default_search_group_->SetHighlighted(true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView, ChromeViews::View overrides:
+
+void GeneralPageView::Layout() {
+ // We need to Layout twice - once to get the width of the contents box...
+ View::Layout();
+ startup_last_session_radio_->SetBounds(
+ 0, 0, startup_group_->GetContentsWidth(), 0);
+ homepage_use_newtab_radio_->SetBounds(
+ 0, 0, homepage_group_->GetContentsWidth(), 0);
+ homepage_show_home_button_checkbox_->SetBounds(
+ 0, 0, homepage_group_->GetContentsWidth(), 0);
+ default_browser_status_label_->SetBounds(
+ 0, 0, default_browser_group_->GetContentsWidth(), 0);
+ // ... and twice to get the height of multi-line items correct.
+ View::Layout();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView, private:
+
+void GeneralPageView::SetDefaultBrowserUIState(DefaultBrowserUIState state) {
+ bool button_enabled = state == STATE_NOT_DEFAULT;
+ default_browser_use_as_default_button_->SetEnabled(button_enabled);
+ if (state == STATE_DEFAULT) {
+ default_browser_status_label_->SetText(
+ l10n_util::GetStringF(IDS_OPTIONS_DEFAULTBROWSER_DEFAULT,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ default_browser_status_label_->SetColor(kDefaultBrowserLabelColor);
+ Layout();
+ } else if (state == STATE_NOT_DEFAULT) {
+ default_browser_status_label_->SetText(
+ l10n_util::GetStringF(IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ default_browser_status_label_->SetColor(kNotDefaultBrowserLabelColor);
+ Layout();
+ }
+}
+
+void GeneralPageView::InitStartupGroup() {
+ startup_homepage_radio_ = new ChromeViews::RadioButton(
+ l10n_util::GetString(IDS_OPTIONS_STARTUP_SHOW_DEFAULT_AND_NEWTAB),
+ kStartupRadioGroup);
+ startup_homepage_radio_->SetListener(this);
+ startup_last_session_radio_ = new ChromeViews::RadioButton(
+ l10n_util::GetString(IDS_OPTIONS_STARTUP_SHOW_LAST_SESSION),
+ kStartupRadioGroup);
+ startup_last_session_radio_->SetListener(this);
+ startup_last_session_radio_->SetMultiLine(true);
+ startup_custom_radio_ = new ChromeViews::RadioButton(
+ l10n_util::GetString(IDS_OPTIONS_STARTUP_SHOW_PAGES),
+ kStartupRadioGroup);
+ startup_custom_radio_->SetListener(this);
+ startup_add_custom_page_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_STARTUP_ADD_BUTTON));
+ startup_add_custom_page_button_->SetListener(this);
+ startup_remove_custom_page_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_STARTUP_REMOVE_BUTTON));
+ startup_remove_custom_page_button_->SetEnabled(false);
+ startup_remove_custom_page_button_->SetListener(this);
+ startup_use_current_page_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_STARTUP_USE_CURRENT));
+ startup_use_current_page_button_->SetListener(this);
+
+ startup_custom_pages_table_model_.reset(
+ new CustomHomePagesTableModel(profile()));
+ std::vector<ChromeViews::TableColumn> columns;
+ columns.push_back(ChromeViews::TableColumn());
+ startup_custom_pages_table_ = new ChromeViews::TableView(
+ startup_custom_pages_table_model_.get(), columns,
+ ChromeViews::ICON_AND_TEXT, true, false, true);
+ // URLs are inherently left-to-right, so do not mirror the table.
+ startup_custom_pages_table_->EnableUIMirroringForRTLLanguages(false);
+ startup_custom_pages_table_->SetObserver(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ ChromeViews::View* contents = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(contents);
+ contents->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ const int double_column_view_set_id = 1;
+ column_set = layout->AddColumnSet(double_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(startup_homepage_radio_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(startup_last_session_radio_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(startup_custom_radio_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(startup_custom_pages_table_);
+
+ ChromeViews::View* button_stack = new ChromeViews::View;
+ GridLayout* button_stack_layout = new GridLayout(button_stack);
+ button_stack->SetLayoutManager(button_stack_layout);
+
+ column_set = button_stack_layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ button_stack_layout->StartRow(0, single_column_view_set_id);
+ button_stack_layout->AddView(startup_add_custom_page_button_,
+ 1, 1, GridLayout::FILL, GridLayout::CENTER);
+ button_stack_layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ button_stack_layout->StartRow(0, single_column_view_set_id);
+ button_stack_layout->AddView(startup_remove_custom_page_button_,
+ 1, 1, GridLayout::FILL, GridLayout::CENTER);
+ button_stack_layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ button_stack_layout->StartRow(0, single_column_view_set_id);
+ button_stack_layout->AddView(startup_use_current_page_button_,
+ 1, 1, GridLayout::FILL, GridLayout::CENTER);
+ layout->AddView(button_stack);
+
+ startup_group_ = new OptionsGroupView(
+ contents, l10n_util::GetString(IDS_OPTIONS_STARTUP_GROUP_NAME),
+ EmptyWString(), true);
+}
+
+void GeneralPageView::InitHomepageGroup() {
+ homepage_use_newtab_radio_ = new ChromeViews::RadioButton(
+ l10n_util::GetString(IDS_OPTIONS_HOMEPAGE_USE_NEWTAB),
+ kHomePageRadioGroup);
+ homepage_use_newtab_radio_->SetListener(this);
+ homepage_use_newtab_radio_->SetMultiLine(true);
+ homepage_use_url_radio_ = new ChromeViews::RadioButton(
+ l10n_util::GetString(IDS_OPTIONS_HOMEPAGE_USE_URL),
+ kHomePageRadioGroup);
+ homepage_use_url_radio_->SetListener(this);
+ homepage_use_url_textfield_ = new ChromeViews::TextField;
+ homepage_use_url_textfield_->SetController(this);
+ homepage_show_home_button_checkbox_ = new ChromeViews::CheckBox(
+ l10n_util::GetString(IDS_OPTIONS_HOMEPAGE_SHOW_BUTTON));
+ homepage_show_home_button_checkbox_->SetListener(this);
+ homepage_show_home_button_checkbox_->SetMultiLine(true);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ ChromeViews::View* contents = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(contents);
+ contents->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ const int double_column_view_set_id = 1;
+ column_set = layout->AddColumnSet(double_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(homepage_use_newtab_radio_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(homepage_use_url_radio_);
+ layout->AddView(homepage_use_url_textfield_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(homepage_show_home_button_checkbox_);
+
+ homepage_group_ = new OptionsGroupView(
+ contents, l10n_util::GetString(IDS_OPTIONS_HOMEPAGE_GROUP_NAME),
+ EmptyWString(), true);
+}
+
+
+void GeneralPageView::InitDefaultSearchGroup() {
+ default_search_engines_model_.reset(new SearchEngineListModel(profile()));
+ default_search_engine_combobox_ =
+ new ChromeViews::ComboBox(default_search_engines_model_.get());
+ default_search_engines_model_->SetComboBox(default_search_engine_combobox_);
+ default_search_engine_combobox_->SetListener(this);
+
+ default_search_manage_engines_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetString(IDS_OPTIONS_DEFAULTSEARCH_MANAGE_ENGINES_LINK));
+ default_search_manage_engines_button_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ ChromeViews::View* contents = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(contents);
+ contents->SetLayoutManager(layout);
+
+ const int double_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(double_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, double_column_view_set_id);
+ layout->AddView(default_search_engine_combobox_);
+ layout->AddView(default_search_manage_engines_button_);
+
+ default_search_group_ = new OptionsGroupView(
+ contents, l10n_util::GetString(IDS_OPTIONS_DEFAULTSEARCH_GROUP_NAME),
+ EmptyWString(), true);
+}
+
+void GeneralPageView::InitDefaultBrowserGroup() {
+ default_browser_status_label_ = new ChromeViews::Label;
+ default_browser_status_label_->SetMultiLine(true);
+ default_browser_status_label_->SetHorizontalAlignment(
+ ChromeViews::Label::ALIGN_LEFT);
+ default_browser_use_as_default_button_ = new ChromeViews::NativeButton(
+ l10n_util::GetStringF(IDS_OPTIONS_DEFAULTBROWSER_USEASDEFAULT,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ default_browser_use_as_default_button_->SetListener(this);
+
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ ChromeViews::View* contents = new ChromeViews::View;
+ GridLayout* layout = new GridLayout(contents);
+ contents->SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(default_browser_status_label_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(default_browser_use_as_default_button_);
+
+ default_browser_group_ = new OptionsGroupView(
+ contents, l10n_util::GetString(IDS_OPTIONS_DEFAULTBROWSER_GROUP_NAME),
+ EmptyWString(), false);
+
+ default_browser_worker_->StartCheckDefaultBrowser();
+}
+
+void GeneralPageView::SaveStartupPref() {
+ SessionStartupPref pref;
+
+ if (startup_last_session_radio_->IsSelected()) {
+ pref.type = SessionStartupPref::LAST;
+ } else if (startup_custom_radio_->IsSelected()) {
+ pref.type = SessionStartupPref::URLS;
+ }
+
+ pref.urls = startup_custom_pages_table_model_->GetURLs();
+
+ SessionStartupPref::SetStartupPref(profile()->GetPrefs(), pref);
+}
+
+void GeneralPageView::AddURLToStartupURLs() {
+ ShelfItemDialog* dialog = new ShelfItemDialog(this, profile(), false);
+ dialog->Show(GetRootWindow());
+}
+
+void GeneralPageView::RemoveURLsFromStartupURLs() {
+ for (ChromeViews::TableView::iterator i =
+ startup_custom_pages_table_->SelectionBegin();
+ i != startup_custom_pages_table_->SelectionEnd(); ++i) {
+ startup_custom_pages_table_model_->Remove(*i);
+ }
+ SaveStartupPref();
+}
+
+void GeneralPageView::SetStartupURLToCurrentPage() {
+ // Remove the current entries.
+ while (startup_custom_pages_table_model_->RowCount())
+ startup_custom_pages_table_model_->Remove(0);
+
+ // And add all entries for all open browsers with our profile.
+ int add_index = 0;
+ for (BrowserList::const_iterator browser_i = BrowserList::begin();
+ browser_i != BrowserList::end(); ++browser_i) {
+ Browser* browser = *browser_i;
+ if (browser->profile() != profile())
+ continue; // Only want entries for open profile.
+
+ for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
+ TabContents* tab = browser->GetTabContentsAt(tab_index);
+ if (tab->ShouldDisplayURL()) {
+ const GURL url = browser->GetTabContentsAt(tab_index)->GetURL();
+ if (!url.is_empty())
+ startup_custom_pages_table_model_->Add(add_index++, url);
+ }
+ }
+ }
+
+ SaveStartupPref();
+}
+
+void GeneralPageView::EnableCustomHomepagesControls(bool enable) {
+ startup_add_custom_page_button_->SetEnabled(enable);
+ bool has_selected_rows = startup_custom_pages_table_->SelectedRowCount() > 0;
+ startup_remove_custom_page_button_->SetEnabled(enable && has_selected_rows);
+ startup_use_current_page_button_->SetEnabled(enable);
+ startup_custom_pages_table_->SetEnabled(enable);
+}
+
+void GeneralPageView::AddBookmark(ShelfItemDialog* dialog,
+ const std::wstring& title,
+ const GURL& url) {
+ int index = startup_custom_pages_table_->FirstSelectedRow();
+ if (index == -1)
+ index = startup_custom_pages_table_model_->RowCount();
+ else
+ index++;
+ startup_custom_pages_table_model_->Add(index, url);
+
+ SaveStartupPref();
+}
+
+void GeneralPageView::OnSelectionChanged() {
+ startup_remove_custom_page_button_->SetEnabled(
+ startup_custom_pages_table_->SelectedRowCount() > 0);
+}
+
+void GeneralPageView::EnableHomepageURLField(bool enabled) {
+ if (enabled) {
+ homepage_use_url_textfield_->SetEnabled(true);
+ homepage_use_url_textfield_->SetReadOnly(false);
+ } else {
+ homepage_use_url_textfield_->SetEnabled(false);
+ homepage_use_url_textfield_->SetReadOnly(true);
+ }
+}
+
+void GeneralPageView::SetDefaultSearchProvider() {
+ const int index = default_search_engine_combobox_->GetSelectedItem();
+ default_search_engines_model_->model()->SetDefaultSearchProvider(
+ default_search_engines_model_->GetTemplateURLAt(index));
+}
diff --git a/chrome/browser/views/options/general_page_view.h b/chrome/browser/views/options/general_page_view.h
new file mode 100644
index 0000000..597db44
--- /dev/null
+++ b/chrome/browser/views/options/general_page_view.h
@@ -0,0 +1,178 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_GENERAL_PAGE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_GENERAL_PAGE_VIEW_H__
+
+#include "chrome/browser/views/options/options_page_view.h"
+#include "chrome/browser/views/shelf_item_dialog.h"
+#include "chrome/common/pref_member.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+class CheckBox;
+class GroupboxView;
+class Label;
+class RadioButton;
+class TableModel;
+class TableView;
+class TextField;
+}
+class CustomHomePagesTableModel;
+class OptionsGroupView;
+class SearchEngineListModel;
+
+///////////////////////////////////////////////////////////////////////////////
+// GeneralPageView
+
+class GeneralPageView : public OptionsPageView,
+ public ChromeViews::ComboBox::Listener,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::TextField::Controller,
+ public ShelfItemDialogDelegate,
+ public ChromeViews::TableViewObserver {
+ public:
+ explicit GeneralPageView(Profile* profile);
+ virtual ~GeneralPageView();
+
+ protected:
+ // ChromeViews::NativeButton::Listener implementation:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // ChromeViews::ComboBox::Listener implementation:
+ virtual void ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index,
+ int new_index);
+
+ // ChromeViews::TextField::Controller implementation:
+ virtual void ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key, UINT repeat_count,
+ UINT flags);
+
+ // OptionsPageView implementation:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+ virtual void HighlightGroup(OptionsGroup highlight_group);
+
+ // ChromeViews::View overrides:
+ virtual void Layout();
+
+ private:
+ // The current default browser UI state
+ enum DefaultBrowserUIState {
+ STATE_PROCESSING,
+ STATE_DEFAULT,
+ STATE_NOT_DEFAULT
+ };
+ // Updates the UI state to reflect the current default browser state.
+ void SetDefaultBrowserUIState(DefaultBrowserUIState state);
+
+ // Init all the dialog controls
+ void InitStartupGroup();
+ void InitHomepageGroup();
+ void InitDefaultSearchGroup();
+ void InitDefaultBrowserGroup();
+
+ // Saves the startup preference from that of the ui.
+ void SaveStartupPref();
+
+ // Shows a dialog allowing the user to add a new URL to the set of URLs
+ // launched on startup.
+ void AddURLToStartupURLs();
+
+ // Removes the selected URL from the list of startup urls.
+ void RemoveURLsFromStartupURLs();
+
+ // Resets the list of urls to launch on startup from the list of open
+ // browsers.
+ void SetStartupURLToCurrentPage();
+
+ // Enables/Disables the controls associated with the custom start pages
+ // option if that preference is not selected.
+ void EnableCustomHomepagesControls(bool enable);
+
+ // ShelfItemDialogDelegate. Adds the URL to the list of startup urls.
+ virtual void AddBookmark(ShelfItemDialog* dialog,
+ const std::wstring& title,
+ const GURL& url);
+
+ // Invoked when the selection of the table view changes. Updates the enabled
+ // property of the remove button.
+ virtual void OnSelectionChanged();
+
+ // Enables or disables the field for entering a custom homepage URL.
+ void EnableHomepageURLField(bool enabled);
+
+ // Sets the default search provider for the selected item in the combobox.
+ void SetDefaultSearchProvider();
+
+ // Controls for the Startup group
+ OptionsGroupView* startup_group_;
+ ChromeViews::RadioButton* startup_homepage_radio_;
+ ChromeViews::RadioButton* startup_last_session_radio_;
+ ChromeViews::RadioButton* startup_custom_radio_;
+ ChromeViews::NativeButton* startup_add_custom_page_button_;
+ ChromeViews::NativeButton* startup_remove_custom_page_button_;
+ ChromeViews::NativeButton* startup_use_current_page_button_;
+ ChromeViews::TableView* startup_custom_pages_table_;
+ scoped_ptr<CustomHomePagesTableModel> startup_custom_pages_table_model_;
+
+ // Controls for the Home Page group
+ OptionsGroupView* homepage_group_;
+ ChromeViews::RadioButton* homepage_use_newtab_radio_;
+ ChromeViews::RadioButton* homepage_use_url_radio_;
+ ChromeViews::TextField* homepage_use_url_textfield_;
+ ChromeViews::CheckBox* homepage_show_home_button_checkbox_;
+ StringPrefMember homepage_;
+ BooleanPrefMember show_home_button_;
+
+ // Controls for the Default Search group
+ OptionsGroupView* default_search_group_;
+ ChromeViews::ComboBox* default_search_engine_combobox_;
+ ChromeViews::NativeButton* default_search_manage_engines_button_;
+ scoped_ptr<SearchEngineListModel> default_search_engines_model_;
+
+ // Controls for the Default Browser group
+ OptionsGroupView* default_browser_group_;
+ ChromeViews::Label* default_browser_status_label_;
+ ChromeViews::NativeButton* default_browser_use_as_default_button_;
+
+ // The helper object that performs default browser set/check tasks.
+ class DefaultBrowserWorker;
+ friend DefaultBrowserWorker;
+ scoped_refptr<DefaultBrowserWorker> default_browser_worker_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(GeneralPageView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_GENERAL_PAGE_VIEW_H__
diff --git a/chrome/browser/views/options/language_combobox_model.cc b/chrome/browser/views/options/language_combobox_model.cc
new file mode 100644
index 0000000..7743a8c
--- /dev/null
+++ b/chrome/browser/views/options/language_combobox_model.cc
@@ -0,0 +1,190 @@
+// 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/views/options/language_combobox_model.h"
+
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/common/pref_service.h"
+#include "unicode/uloc.h"
+
+#include "generated_resources.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// LanguageComboboxModel used to populate a combobox with native names
+// corresponding to the language code (e.g. English (United States) for en-US)
+//
+LanguageComboboxModel::LanguageComboboxModel()
+ : profile_(NULL) {
+ // Enumerate the languages we know about.
+ const std::vector<std::wstring>& locale_codes =
+ l10n_util::GetAvailableLocales();
+ InitNativeNames(locale_codes);
+}
+
+// Overload using a profile and customized local_codes vector.
+LanguageComboboxModel::LanguageComboboxModel(
+ Profile* profile, const std::vector<std::wstring>& locale_codes)
+ : profile_(profile) {
+ InitNativeNames(locale_codes);
+}
+
+void LanguageComboboxModel::InitNativeNames(const std::vector<std::wstring>&
+ locale_codes) {
+ const std::string app_locale = WideToASCII(
+ g_browser_process->GetApplicationLocale());
+ for (size_t i = 0; i < locale_codes.size(); ++i) {
+ std::string locale_code_str = WideToASCII(locale_codes[i]);
+ const char* locale_code = locale_code_str.c_str();
+
+ // Internally, we use the language code of zh-CN and zh-TW, but we want the
+ // display names to be Chinese (Simplified) and Chinese (Traditional). To
+ // do that, we use zh-hans and zh-hant when using ICU's
+ // uloc_getDisplayName.
+ if (locale_code_str == "zh-CN") {
+ locale_code = "zh-hans";
+ } else if (locale_code_str == "zh-TW") {
+ locale_code = "zh-hant";
+ }
+
+ UErrorCode error = U_ZERO_ERROR;
+ const int buffer_size = 1024;
+ std::wstring name_local;
+ int actual_size = uloc_getDisplayName(locale_code, app_locale.c_str(),
+ WriteInto(&name_local, buffer_size + 1), buffer_size, &error);
+ DCHECK(U_SUCCESS(error));
+ name_local.resize(actual_size);
+
+ std::wstring name_native;
+ actual_size = uloc_getDisplayName(locale_code, locale_code,
+ WriteInto(&name_native, buffer_size + 1), buffer_size, &error);
+ DCHECK(U_SUCCESS(error));
+ name_native.resize(actual_size);
+
+ locale_names_.push_back(name_local);
+ native_names_[name_local] = LocaleData(name_native, locale_codes[i]);
+ }
+
+ // Sort using locale specific sorter.
+ l10n_util::SortStrings(g_browser_process->GetApplicationLocale(),
+ &locale_names_);
+}
+
+// Overridden from ChromeViews::Combobox::Model:
+int LanguageComboboxModel::GetItemCount(ChromeViews::ComboBox* source) {
+ return static_cast<int>(locale_names_.size());
+}
+
+std::wstring LanguageComboboxModel::GetItemAt(ChromeViews::ComboBox* source,
+ int index) {
+ DCHECK(static_cast<int>(locale_names_.size()) > index);
+ LocaleDataMap::const_iterator it =
+ native_names_.find(locale_names_[index]);
+ DCHECK(it != native_names_.end());
+
+ // If the name is the same in the native language and local language,
+ // don't show it twice.
+ if (it->second.native_name == locale_names_[index])
+ return it->second.native_name;
+
+ // We must add directionality formatting to both the native name and the
+ // locale name in order to avoid text rendering problems such as misplaced
+ // parentheses or languages appearing in the wrong order.
+ std::wstring locale_name_localized;
+ std::wstring locale_name;
+ if (l10n_util::AdjustStringForLocaleDirection(locale_names_[index],
+ &locale_name_localized))
+ locale_name.assign(locale_name_localized);
+ else
+ locale_name.assign(locale_names_[index]);
+
+ std::wstring native_name_localized;
+ std::wstring native_name;
+ if (l10n_util::AdjustStringForLocaleDirection(it->second.native_name,
+ &native_name_localized))
+ native_name.assign(native_name_localized);
+ else
+ native_name.assign(it->second.native_name);
+
+ // We used to have a localizable template here, but none of translators
+ // changed the format. We also want to switch the order of locale_name
+ // and native_name without going back to translators.
+ std::wstring formatted_item;
+ SStringPrintf(&formatted_item, L"%s - %s", locale_name.c_str(),
+ native_name.c_str());
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
+ // Somehow combo box (even with LAYOUTRTL flag) doesn't get this
+ // right so we add RTL BDO (U+202E) to set the direction
+ // explicitly.
+ formatted_item.insert(0, L"\x202E");
+ return formatted_item;
+}
+
+// Return the locale for the given index. E.g., may return pt-BR.
+std::wstring LanguageComboboxModel::GetLocaleFromIndex(int index) {
+ DCHECK(static_cast<int>(locale_names_.size()) > index);
+ LocaleDataMap::const_iterator it =
+ native_names_.find(locale_names_[index]);
+ DCHECK(it != native_names_.end());
+
+ return it->second.locale_code;
+}
+
+int LanguageComboboxModel::GetIndexFromLocale(const std::wstring& locale) {
+ for (size_t i = 0; i < locale_names_.size(); ++i) {
+ LocaleDataMap::const_iterator it =
+ native_names_.find(locale_names_[i]);
+ DCHECK(it != native_names_.end());
+ if (it->second.locale_code == locale)
+ return static_cast<int>(i);
+ }
+ return -1;
+}
+
+// Returns the index of the language currently specified in the user's
+// preference file. Note that it's possible for language A to be picked
+// while chrome is currently in language B if the user specified language B
+// via --lang. Since --lang is not a persistent setting, it seems that it
+// shouldn't be reflected in this combo box. We return -1 if the value in
+// the pref doesn't map to a know language (possible if the user edited the
+// prefs file manually).
+int LanguageComboboxModel::GetSelectedLanguageIndex(const std::wstring&
+ prefs) {
+ PrefService* local_state;
+ if (!profile_)
+ local_state = g_browser_process->local_state();
+ else
+ local_state = profile_->GetPrefs();
+
+ DCHECK(local_state);
+ const std::wstring& current_lang = local_state->GetString(prefs.c_str());
+
+ return GetIndexFromLocale(current_lang);
+}
diff --git a/chrome/browser/views/options/language_combobox_model.h b/chrome/browser/views/options/language_combobox_model.h
new file mode 100644
index 0000000..dc03f91
--- /dev/null
+++ b/chrome/browser/views/options/language_combobox_model.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_LANGUAGE_COMBOBOX_MODEL_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_LANGUAGE_COMBOBOX_MODEL_H__
+
+#include "chrome/browser/profile.h"
+#include "chrome/views/combo_box.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// LanguageComboboxModel
+// The model that fills the dropdown of valid UI languages.
+class LanguageComboboxModel : public ChromeViews::ComboBox::Model {
+ public:
+ struct LocaleData {
+ LocaleData() { }
+ LocaleData(const std::wstring& name, const std::wstring& code)
+ : native_name(name), locale_code(code) { }
+
+ std::wstring native_name;
+ std::wstring locale_code; // E.g., en-us.
+ };
+ typedef std::map<std::wstring, LocaleData> LocaleDataMap;
+
+ LanguageComboboxModel();
+
+ // Overload using a profile and customized local_codes vector.
+ LanguageComboboxModel(Profile* profile,
+ const std::vector<std::wstring>& locale_codes);
+
+ virtual ~LanguageComboboxModel() {}
+
+ void InitNativeNames(const std::vector<std::wstring>& locale_codes);
+
+ // Overridden from ChromeViews::Combobox::Model:
+ virtual int GetItemCount(ChromeViews::ComboBox* source);
+
+ virtual std::wstring GetItemAt(ChromeViews::ComboBox* source, int index);
+
+ // Return the locale for the given index. E.g., may return pt-BR.
+ std::wstring GetLocaleFromIndex(int index);
+
+ // Returns the index for the given locale. Returns -1 if the locale is not
+ // in the combobox model.
+ int GetIndexFromLocale(const std::wstring& locale);
+
+ // Returns the index of the language currently specified in the user's
+ // preference file. Note that it's possible for language A to be picked
+ // while chrome is currently in language B if the user specified language B
+ // via --lang. Since --lang is not a persistent setting, it seems that it
+ // shouldn't be reflected in this combo box. We return -1 if the value in
+ // the pref doesn't map to a know language (possible if the user edited the
+ // prefs file manually).
+ int GetSelectedLanguageIndex(const std::wstring& prefs);
+
+ private:
+ // The name all the locales in the current application locale.
+ std::vector<std::wstring> locale_names_;
+
+ // A map of some extra data (LocaleData) keyed off the name of the locale.
+ LocaleDataMap native_names_;
+
+ // Profile.
+ Profile* profile_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LanguageComboboxModel);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_LANGUAGE_COMBOBOX_MODEL_H__
diff --git a/chrome/browser/views/options/languages_page_view.cc b/chrome/browser/views/options/languages_page_view.cc
new file mode 100644
index 0000000..2c9ef19
--- /dev/null
+++ b/chrome/browser/views/options/languages_page_view.cc
@@ -0,0 +1,778 @@
+// 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 <windows.h>
+#include <shlobj.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "chrome/browser/views/options/languages_page_view.h"
+
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "base/gfx/native_theme.h"
+#include "base/gfx/skia_utils.h"
+#include "base/string_util.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/shell_dialogs.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/options/language_combobox_model.h"
+#include "chrome/browser/views/password_manager_view.h"
+#include "chrome/browser/views/restart_message_box.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/radio_button.h"
+#include "chrome/views/tabbed_pane.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/view_container.h"
+#include "generated_resources.h"
+#include "skia/include/SkBitmap.h"
+#include "unicode/uloc.h"
+
+static const wchar_t* const g_supported_spellchecker_languages[] = {
+ L"en-US",
+ L"en-GB",
+ L"fr-FR",
+ L"it-IT",
+ L"de-DE",
+ L"es-ES",
+ L"nl-NL",
+ L"pt-BR",
+ L"ru-RU",
+ L"pl-PL",
+ // L"th-TH", // Not to be included in Spellchecker as per B=1277824
+ L"sv-SE",
+ L"da-DK",
+ L"pt-PT",
+ L"ro-RO",
+ // L"hu-HU", // Not to be included in Spellchecker as per B=1277824
+ // L"he-IL", // Not to be included in Spellchecker as per B=1252241
+ L"id-ID",
+ L"cs-CZ",
+ L"el-GR",
+ L"nb-NO",
+ L"vi-VN",
+ // L"bg-BG", // Not to be included in Spellchecker as per B=1277824
+ L"hr-HR",
+ L"lt-LT",
+ L"sk-SK",
+ L"sl-SI",
+ L"ca-ES"
+ L"lv-LV",
+ // L"uk-UA", // Not to be included in Spellchecker as per B=1277824
+ L"hi-IN",
+ //
+ // TODO(Sidchat): Uncomment/remove languages as and when they get resolved.
+ //
+};
+
+static const wchar_t* const accept_language_list[] = {
+ L"af", // Afrikaans
+ L"am", // Amharic
+ L"ar", // Arabic
+ L"az", // Azerbaijani
+ L"be", // Belarusian
+ L"bg", // Bulgarian
+ L"bh", // Bihari
+ L"bn", // Bengali
+ L"br", // Breton
+ L"bs", // Bosnian
+ L"ca", // Catalan
+ L"co", // Corsican
+ L"cs", // Czech
+ L"cy", // Welsh
+ L"da", // Danish
+ L"de", // German
+ L"el", // Greek
+ L"en", // English
+ L"en-GB", // English (UK)
+ L"en-US", // English (US)
+ L"eo", // Esperanto
+ // TODO(jungshik) : Do we want to list all es-Foo for Latin-American
+ // Spanish speaking countries?
+ L"es", // Spanish
+ L"et", // Estonian
+ L"eu", // Basque
+ L"fa", // Persian
+ L"fi", // Finnish
+ L"fil", // Filipino
+ L"fo", // Faroese
+ L"fr", // French
+ L"fy", // Frisian
+ L"ga", // Irish
+ L"gd", // Scots Gaelic
+ L"gl", // Galician
+ L"gn", // Guarani
+ L"gu", // Gujarati
+ L"he", // Hebrew
+ L"hi", // Hindi
+ L"hr", // Croatian
+ L"hu", // Hungarian
+ L"hy", // Armenian
+ L"ia", // Interlingua
+ L"id", // Indonesian
+ L"is", // Icelandic
+ L"it", // Italian
+ L"ja", // Japanese
+ L"jw", // Javanese
+ L"ka", // Georgian
+ L"kk", // Kazakh
+ L"km", // Cambodian
+ L"kn", // Kannada
+ L"ko", // Korean
+ L"ku", // Kurdish
+ L"ky", // Kyrgyz
+ L"la", // Latin
+ L"ln", // Lingala
+ L"lo", // Laothian
+ L"lt", // Lithuanian
+ L"lv", // Latvian
+ L"mk", // Macedonian
+ L"ml", // Malayalam
+ L"mn", // Mongolian
+ L"mo", // Moldavian
+ L"mr", // Marathi
+ L"ms", // Malay
+ L"mt", // Maltese
+ L"nb", // Norwegian (Bokmal)
+ L"ne", // Nepali
+ L"nl", // Dutch
+ L"nn", // Norwegian (Nynorsk)
+ L"no", // Norwegian
+ L"oc", // Occitan
+ L"or", // Oriya
+ L"pa", // Punjabi
+ L"pl", // Polish
+ L"ps", // Pashto
+ L"pt", // Portuguese
+ L"pt-BR", // Portuguese (Brazil)
+ L"pt-PT", // Portuguese (Portugal)
+ L"qu", // Quechua
+ L"rm", // Romansh
+ L"ro", // Romanian
+ L"ru", // Russian
+ L"sd", // Sindhi
+ L"sh", // Serbo-Croatian
+ L"si", // Sinhalese
+ L"sk", // Slovak
+ L"sl", // Slovenian
+ L"sn", // Shona
+ L"so", // Somali
+ L"sq", // Albanian
+ L"sr", // Serbian
+ L"st", // Sesotho
+ L"su", // Sundanese
+ L"sv", // Swedish
+ L"sw", // Swahili
+ L"ta", // Tamil
+ L"te", // Telugu
+ L"tg", // Tajik
+ L"th", // Thai
+ L"ti", // Tigrinya
+ L"tk", // Turkmen
+ L"to", // Tonga
+ L"tr", // Turkish
+ L"tt", // Tatar
+ L"tw", // Twi
+ L"ug", // Uighur
+ L"uk", // Ukrainian
+ L"ur", // Urdu
+ L"uz", // Uzbek
+ L"vi", // Vietnamese
+ L"xh", // Xhosa
+ L"yi", // Yiddish
+ L"yo", // Yoruba
+ L"zh", // Chinese
+ L"zh-CN", // Chinese&nbsp;(Simplified)
+ L"zh-TW", // Chinese&nbsp;(Traditional)
+ L"zu", // Zulu
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// AddLanguageWindowView
+//
+// This opens another window from where a new accept language can be selected.
+//
+class AddLanguageWindowView : public ChromeViews::View,
+ public ChromeViews::ComboBox::Listener,
+ public ChromeViews::DialogDelegate {
+ public:
+ AddLanguageWindowView(LanguagesPageView* language_delegate, Profile* profile);
+ ChromeViews::Window* container() const { return container_; }
+ void set_container(ChromeViews::Window* container) {
+ container_ = container;
+ }
+
+ // ChromeViews::DialogDelegate methods.
+ virtual bool Accept();
+ virtual std::wstring GetWindowTitle() const;
+
+ // ChromeViews::WindowDelegate method.
+ virtual bool IsModal() const { return true; }
+
+ // ChromeViews::ComboBox::Listener implementation:
+ virtual void ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index,
+ int new_index);
+
+ // ChromeViews::View overrides.
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize *out);
+
+ protected:
+ virtual void ViewHierarchyChanged(bool is_add, ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ void Init();
+
+ // The Options dialog window.
+ ChromeViews::Window* container_;
+
+ // Used for Call back to LanguagePageView that language has been selected.
+ LanguagesPageView* language_delegate_;
+ std::wstring accept_language_selected_;
+
+ // Combobox and its corresponding model.
+ scoped_ptr<LanguageComboboxModel> accept_language_combobox_model_;
+ ChromeViews::ComboBox* accept_language_combobox_;
+
+ // The Profile associated with this window.
+ Profile* profile_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AddLanguageWindowView);
+};
+
+static const int kDialogPadding = 7;
+static int kDefaultWindowWidthChars = 60;
+static int kDefaultWindowHeightLines = 3;
+
+AddLanguageWindowView::AddLanguageWindowView(LanguagesPageView* language_delegate,
+ Profile* profile)
+ : profile_(profile->GetOriginalProfile()),
+ language_delegate_(language_delegate),
+ accept_language_combobox_(NULL) {
+ Init();
+
+ // Initialize accept_language_selected_ to the first index in drop down.
+ accept_language_selected_ = accept_language_combobox_model_->
+ GetLocaleFromIndex(0);
+}
+
+std::wstring AddLanguageWindowView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_FONT_LANGUAGE_SETTING_LANGUAGES_TAB_TITLE);
+}
+
+bool AddLanguageWindowView::Accept() {
+ if (language_delegate_) {
+ language_delegate_->OnAddLanguage(accept_language_selected_);
+ }
+ return true;
+}
+
+void AddLanguageWindowView::ItemChanged(ChromeViews::ComboBox* combo_box,
+ int prev_index,
+ int new_index) {
+ accept_language_selected_ = accept_language_combobox_model_->
+ GetLocaleFromIndex(new_index);
+}
+
+void AddLanguageWindowView::Layout() {
+ CSize sz;
+ accept_language_combobox_->GetPreferredSize(&sz);
+ accept_language_combobox_->SetBounds(kDialogPadding, kDialogPadding,
+ GetWidth() - 2*kDialogPadding, sz.cy);
+}
+
+void AddLanguageWindowView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont font = rb.GetFont(ResourceBundle::BaseFont);
+ out->cx = font.ave_char_width() * kDefaultWindowWidthChars;
+ out->cy = font.height() * kDefaultWindowHeightLines;
+}
+
+void AddLanguageWindowView::ViewHierarchyChanged(
+ bool is_add, ChromeViews::View* parent, ChromeViews::View* child) {
+ // Can't init before we're inserted into a ViewContainer, because we require
+ // a HWND to parent native child controls to.
+ if (is_add && child == this)
+ Init();
+}
+
+void AddLanguageWindowView::Init() {
+ // Determine Locale Codes.
+ std::vector<std::wstring> locale_codes;
+ const std::wstring app_locale = g_browser_process->GetApplicationLocale();
+ for (size_t i = 0; i < arraysize(accept_language_list); ++i) {
+ std::wstring local_name =
+ l10n_util::GetLocalName(accept_language_list[i], app_locale, false);
+ // This is a hack. If ICU doesn't have a translated name for
+ // this language, GetLocalName will just return the language code.
+ // In that case, we skip it.
+ // TODO(jungshik) : Put them at the of the list with language codes
+ // enclosed by brackets.
+ if (local_name != accept_language_list[i])
+ locale_codes.push_back(accept_language_list[i]);
+ }
+ accept_language_combobox_model_.reset(new LanguageComboboxModel(
+ profile_, locale_codes));
+ accept_language_combobox_ = new ChromeViews::ComboBox(
+ accept_language_combobox_model_.get());
+ accept_language_combobox_->SetSelectedItem(0);
+ accept_language_combobox_->SetListener(this);
+ AddChildView(accept_language_combobox_);
+}
+
+class LanguageOrderTableModel : public ChromeViews::TableModel {
+ public:
+ LanguageOrderTableModel();
+
+ // Set Language List.
+ void SetAcceptLanguagesString(const std::wstring& language_list);
+
+ // Add at the end.
+ void Add(const std::wstring& language);
+
+ // Removes the entry at the specified index.
+ void Remove(int index);
+
+ // Move down the entry at the specified index.
+ void MoveDown(int index);
+
+ // Move up the entry at the specified index.
+ void MoveUp(int index);
+
+ // Returns the set of languagess this model contains.
+ std::wstring GetLanguageList() { return VectorToList(languages_); }
+
+ // ChromeViews::TableModel overrides:
+ virtual int RowCount();
+ virtual std::wstring GetText(int row, int column_id);
+ virtual void SetObserver(ChromeViews::TableModelObserver* observer);
+
+ private:
+ // This method converts a comma separated list to a vector of strings.
+ void ListToVector(const std::wstring& list,
+ std::vector<std::wstring>* vector);
+
+ // This method returns a comma separated string given a string vector.
+ std::wstring VectorToList(const std::vector<std::wstring>& vector);
+
+ // Set of entries we're showing.
+ std::vector<std::wstring> languages_;
+ std::wstring comma_separated_language_list_;
+
+ ChromeViews::TableModelObserver* observer_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LanguageOrderTableModel);
+};
+
+LanguageOrderTableModel::LanguageOrderTableModel()
+ : observer_(NULL) {
+}
+
+void LanguageOrderTableModel::SetAcceptLanguagesString(
+ const std::wstring& language_list) {
+ std::vector<std::wstring> languages_vector;
+ ListToVector(language_list, &languages_vector);
+ for (int i = 0; i < static_cast<int>(languages_vector.size()); i++) {
+ Add(languages_vector.at(i));
+ }
+}
+
+void LanguageOrderTableModel::SetObserver(
+ ChromeViews::TableModelObserver* observer) {
+ observer_ = observer;
+}
+
+std::wstring LanguageOrderTableModel::GetText(int row, int column_id) {
+ DCHECK(row >= 0 && row < RowCount());
+ const std::wstring app_locale = g_browser_process->GetApplicationLocale();
+ return l10n_util::GetLocalName(languages_.at(row), app_locale, true);
+}
+
+void LanguageOrderTableModel::Add(const std::wstring& language) {
+ if (language.empty())
+ return;
+ // Check for selecting duplicated language.
+ for (std::vector<std::wstring>::const_iterator cit = languages_.begin();
+ cit != languages_.end(); ++cit)
+ if (*cit == language)
+ return;
+ languages_.push_back(language);
+ if (observer_)
+ observer_->OnItemsAdded(RowCount() - 1, 1);
+}
+
+void LanguageOrderTableModel::Remove(int index) {
+ DCHECK(index >= 0 && index < RowCount());
+ languages_.erase(languages_.begin() + index);
+ if (observer_)
+ observer_->OnItemsRemoved(index, 1);
+}
+
+void LanguageOrderTableModel::MoveDown(int index) {
+ if (index < 0 || index >= RowCount() - 1)
+ return;
+ std::wstring item = languages_.at(index);
+ languages_.erase(languages_.begin() + index);
+ if (index == RowCount() - 1)
+ languages_.push_back(item);
+ else
+ languages_.insert(languages_.begin() + index + 1, item);
+ if (observer_)
+ observer_->OnItemsChanged(0, RowCount());
+}
+
+void LanguageOrderTableModel::MoveUp(int index) {
+ if (index <= 0 || index >= static_cast<int>(languages_.size()))
+ return;
+ std::wstring item = languages_.at(index);
+ languages_.erase(languages_.begin() + index);
+ languages_.insert(languages_.begin() + index - 1, item);
+ if (observer_)
+ observer_->OnItemsChanged(0, RowCount());
+}
+
+int LanguageOrderTableModel::RowCount() {
+ return static_cast<int>(languages_.size());
+}
+
+void LanguageOrderTableModel::ListToVector(const std::wstring& list,
+ std::vector<std::wstring>* vector) {
+ SplitString(list, L',', vector);
+}
+
+std::wstring LanguageOrderTableModel::VectorToList(
+ const std::vector<std::wstring>& vector) {
+ std::wstring list;
+ for (int i = 0 ; i < static_cast<int>(vector.size()) ; i++) {
+ list += vector.at(i);
+ if (i != vector.size() - 1)
+ list += ',';
+ }
+ return list;
+}
+
+LanguagesPageView::LanguagesPageView(Profile* profile)
+ : languages_instructions_(NULL),
+ languages_contents_(NULL),
+ language_order_table_(NULL),
+ add_button_(NULL),
+ remove_button_(NULL),
+ move_up_button_(NULL),
+ move_down_button_(NULL),
+ button_stack_(NULL),
+ language_info_label_(NULL),
+ ui_language_label_(NULL),
+ change_ui_language_combobox_(NULL),
+ change_dictionary_language_combobox_(NULL),
+ dictionary_language_label_(NULL),
+ OptionsPageView(profile),
+ language_table_edited_(false) {
+ accept_languages_.Init(prefs::kAcceptLanguages,
+ profile->GetPrefs(), NULL);
+}
+
+LanguagesPageView::~LanguagesPageView() {
+ if (language_order_table_)
+ language_order_table_->SetModel(NULL);
+}
+
+void LanguagesPageView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ if (sender == move_up_button_) {
+ OnMoveUpLanguage();
+ language_table_edited_ = true;
+ } else if (sender == move_down_button_) {
+ OnMoveDownLanguage();
+ language_table_edited_ = true;
+ } else if (sender == remove_button_) {
+ OnRemoveLanguage();
+ language_table_edited_ = true;
+ } else if (sender == add_button_) {
+ AddLanguageWindowView* instance = new AddLanguageWindowView(this, profile());
+ HWND parent_hwnd = GetViewContainer()->GetHWND();
+ ChromeViews::Window* w =
+ ChromeViews::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(),
+ instance, instance);
+ w->Show();
+ language_table_edited_ = true;
+ }
+}
+
+void LanguagesPageView::OnAddLanguage(const std::wstring& new_language) {
+ language_order_table_model_->Add(new_language);
+ language_order_table_->Select(language_order_table_model_->RowCount() - 1);
+ OnSelectionChanged();
+}
+
+void LanguagesPageView::InitControlLayout() {
+ // Define the buttons.
+ add_button_ = new ChromeViews::NativeButton(l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_LANGUAGES_SELECTOR_ADD_BUTTON_LABEL));
+ add_button_->SetListener(this);
+ remove_button_ = new ChromeViews::NativeButton(l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_LANGUAGES_SELECTOR_REMOVE_BUTTON_LABEL));
+ remove_button_->SetEnabled(false);
+ remove_button_->SetListener(this);
+ move_up_button_ = new ChromeViews::NativeButton(l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_LANGUAGES_SELECTOR_MOVEUP_BUTTON_LABEL));
+ move_up_button_->SetEnabled(false);
+ move_up_button_->SetListener(this);
+ move_down_button_ = new ChromeViews::NativeButton(l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_LANGUAGES_SELECTOR_MOVEDOWN_BUTTON_LABEL));
+ move_down_button_->SetEnabled(false);
+ move_down_button_->SetListener(this);
+
+ languages_contents_ = new ChromeViews::View;
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ const int single_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+
+ // Add the instructions label.
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ languages_instructions_ = new ChromeViews::Label(
+ l10n_util::GetString(
+ IDS_FONT_LANGUAGE_SETTING_LANGUAGES_INSTRUCTIONS));
+ languages_instructions_->SetMultiLine(true);
+ languages_instructions_->SetHorizontalAlignment(
+ ChromeViews::Label::ALIGN_LEFT);
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(languages_instructions_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Add two columns - for table, and for button stack.
+ std::vector<ChromeViews::TableColumn> columns;
+ columns.push_back(ChromeViews::TableColumn());
+ language_order_table_model_.reset(new LanguageOrderTableModel);
+ language_order_table_ = new ChromeViews::TableView(
+ language_order_table_model_.get(), columns,
+ ChromeViews::TEXT_ONLY, false, true, true);
+ language_order_table_->SetObserver(this);
+
+ const int double_column_view_set_id = 1;
+ column_set = layout->AddColumnSet(double_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, double_column_view_set_id);
+
+ // Add the table to the the first column.
+ layout->AddView(language_order_table_);
+
+ // Now add the four buttons to the second column.
+ button_stack_ = new ChromeViews::View;
+ GridLayout* button_stack_layout = new GridLayout(button_stack_);
+ button_stack_->SetLayoutManager(button_stack_layout);
+
+ column_set = button_stack_layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ button_stack_layout->StartRow(0, single_column_view_set_id);
+ button_stack_layout->AddView(move_up_button_, 1, 1, GridLayout::FILL,
+ GridLayout::CENTER);
+ button_stack_layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ button_stack_layout->StartRow(0, single_column_view_set_id);
+ button_stack_layout->AddView(move_down_button_, 1, 1, GridLayout::FILL,
+ GridLayout::CENTER);
+ button_stack_layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ button_stack_layout->StartRow(0, single_column_view_set_id);
+ button_stack_layout->AddView(remove_button_, 1, 1, GridLayout::FILL,
+ GridLayout::CENTER);
+ button_stack_layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ button_stack_layout->StartRow(0, single_column_view_set_id);
+ button_stack_layout->AddView(add_button_, 1, 1, GridLayout::FILL,
+ GridLayout::CENTER);
+
+ layout->AddView(button_stack_);
+
+ layout->AddPaddingRow(0, kUnrelatedControlLargeVerticalSpacing);
+
+ language_info_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_CHROME_LANGUAGE_INFO));
+ language_info_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ ui_language_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_CHROME_UI_LANGUAGE));
+ ui_language_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ ui_language_model_.reset(new LanguageComboboxModel);
+ change_ui_language_combobox_ =
+ new ChromeViews::ComboBox(ui_language_model_.get());
+ change_ui_language_combobox_->SetListener(this);
+ dictionary_language_label_ = new ChromeViews::Label(
+ l10n_util::GetString(IDS_OPTIONS_CHROME_DICTIONARY_LANGUAGE));
+ dictionary_language_label_->SetHorizontalAlignment(
+ ChromeViews::Label::ALIGN_LEFT);
+
+ layout->StartRow(0, single_column_view_set_id);
+ layout->AddView(language_info_label_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ const int double_column_view_set_2_id = 2;
+ column_set = layout->AddColumnSet(double_column_view_set_2_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ // Determine Locale Codes.
+ std::vector<std::wstring> locale_codes;
+ for (size_t i = 0; i < arraysize(g_supported_spellchecker_languages); ++i)
+ locale_codes.push_back(g_supported_spellchecker_languages[i]);
+ dictionary_language_model_.reset(new LanguageComboboxModel(profile(),
+ locale_codes));
+ change_dictionary_language_combobox_ =
+ new ChromeViews::ComboBox(dictionary_language_model_.get());
+ change_dictionary_language_combobox_->SetListener(this);
+
+ layout->StartRow(0, double_column_view_set_2_id);
+ layout->AddView(ui_language_label_);
+ layout->AddView(change_ui_language_combobox_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(0, double_column_view_set_2_id);
+ layout->AddView(dictionary_language_label_);
+ layout->AddView(change_dictionary_language_combobox_);
+
+ // Init member prefs so we can update the controls if prefs change.
+ app_locale_.Init(prefs::kApplicationLocale,
+ g_browser_process->local_state(), this);
+ dictionary_language_.Init(prefs::kSpellCheckDictionary,
+ profile()->GetPrefs(), this);
+}
+
+void LanguagesPageView::NotifyPrefChanged(const std::wstring* pref_name) {
+ if (!pref_name || *pref_name == prefs::kAcceptLanguages) {
+ language_order_table_model_->SetAcceptLanguagesString(
+ accept_languages_.GetValue());
+ }
+ if (!pref_name || *pref_name == prefs::kApplicationLocale) {
+ int index = ui_language_model_->GetSelectedLanguageIndex(
+ prefs::kApplicationLocale);
+ if (-1 == index) {
+ // The pref value for locale isn't valid. Use the current app locale
+ // (which is what we're currently using).
+ index = ui_language_model_->GetIndexFromLocale(
+ g_browser_process->GetApplicationLocale());
+ }
+ DCHECK(-1 != index);
+ change_ui_language_combobox_->SetSelectedItem(index);
+ }
+ if (!pref_name || *pref_name == prefs::kSpellCheckDictionary) {
+ int index = dictionary_language_model_->GetSelectedLanguageIndex(
+ prefs::kSpellCheckDictionary);
+ change_dictionary_language_combobox_->SetSelectedItem(index);
+ }
+}
+
+void LanguagesPageView::ItemChanged(ChromeViews::ComboBox* sender,
+ int prev_index,
+ int new_index) {
+ if (sender == change_ui_language_combobox_) {
+ UserMetricsRecordAction(L"Options_AppLanguage",
+ g_browser_process->local_state());
+ app_locale_.SetValue(ui_language_model_->GetLocaleFromIndex(new_index));
+ RestartMessageBox::ShowMessageBox(GetRootWindow());
+ } else if (sender == change_dictionary_language_combobox_) {
+ UserMetricsRecordAction(L"Options_DictionaryLanguage",
+ profile()->GetPrefs());
+ dictionary_language_.SetValue(dictionary_language_model_->
+ GetLocaleFromIndex(new_index));
+ RestartMessageBox::ShowMessageBox(GetRootWindow());
+ }
+}
+
+void LanguagesPageView::OnSelectionChanged() {
+ move_up_button_->SetEnabled(language_order_table_->FirstSelectedRow() > 0 &&
+ language_order_table_->SelectedRowCount() == 1);
+ move_down_button_->SetEnabled(language_order_table_->FirstSelectedRow() <
+ language_order_table_->RowCount() - 1 &&
+ language_order_table_->SelectedRowCount() ==
+ 1);
+ remove_button_->SetEnabled(language_order_table_->SelectedRowCount() > 0);
+}
+
+void LanguagesPageView::OnRemoveLanguage() {
+ int item_selected = 0;
+ for (ChromeViews::TableView::iterator i =
+ language_order_table_->SelectionBegin();
+ i != language_order_table_->SelectionEnd(); ++i) {
+ language_order_table_model_->Remove(*i);
+ item_selected = *i;
+ }
+
+ move_up_button_->SetEnabled(false);
+ move_down_button_->SetEnabled(false);
+ remove_button_->SetEnabled(false);
+ int items_left = language_order_table_model_->RowCount();
+ if (items_left <= 0)
+ return;
+ if (item_selected > items_left - 1)
+ item_selected = items_left - 1;
+ language_order_table_->Select(item_selected);
+ OnSelectionChanged();
+}
+
+void LanguagesPageView::OnMoveDownLanguage() {
+ int item_selected = language_order_table_->FirstSelectedRow();
+ language_order_table_model_->MoveDown(item_selected);
+ language_order_table_->Select(item_selected + 1);
+ OnSelectionChanged();
+}
+
+void LanguagesPageView::OnMoveUpLanguage() {
+ int item_selected = language_order_table_->FirstSelectedRow();
+ language_order_table_model_->MoveUp(item_selected);
+ language_order_table_->Select(item_selected - 1);
+
+ OnSelectionChanged();
+}
+
+void LanguagesPageView::SaveChanges() {
+ if (language_order_table_model_.get() && language_table_edited_)
+ accept_languages_.SetValue(language_order_table_model_->GetLanguageList());
+}
diff --git a/chrome/browser/views/options/languages_page_view.h b/chrome/browser/views/options/languages_page_view.h
new file mode 100644
index 0000000..8f87ba0
--- /dev/null
+++ b/chrome/browser/views/options/languages_page_view.h
@@ -0,0 +1,123 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_LANGUAGES_PAGE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_LANGUAGES_PAGE_VIEW_H__
+
+#include "chrome/browser/views/options/options_page_view.h"
+#include "chrome/common/pref_member.h"
+#include "chrome/views/combo_box.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+class Label;
+class TableModel;
+class TableView;
+}
+
+class LanguageComboboxModel;
+class LanguageOrderTableModel;
+class AddLanguageView;
+
+///////////////////////////////////////////////////////////////////////////////
+// LanguagesPageView
+
+class LanguagesPageView : public OptionsPageView,
+ public ChromeViews::NativeButton::Listener,
+ public ChromeViews::TableViewObserver,
+ public ChromeViews::ComboBox::Listener {
+ public:
+ explicit LanguagesPageView(Profile* profile);
+ virtual ~LanguagesPageView();
+
+ // ChromeViews::NativeButton::Listener implementation:
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // Save Changes made to relevant pref members associated with this tab.
+ // This is public since it is called by FontsLanguageWindowView in its
+ // Dialog Delegate Accept() method.
+ void SaveChanges();
+
+ // This is public because when user clicks OK in AddLanguageView dialog,
+ // this is called back in the LanguagePageView delegate in order to add
+ // this language to the table model in this tab.
+ void OnAddLanguage(const std::wstring& new_language);
+
+ protected:
+ // OptionsPageView implementation:
+ virtual void InitControlLayout();
+ virtual void NotifyPrefChanged(const std::wstring* pref_name);
+
+ // ChromeViews::ComboBox::Listener implementation:
+ virtual void ItemChanged(ChromeViews::ComboBox* sender,
+ int prev_index,
+ int new_index);
+
+ private:
+ // Invoked when the selection of the table view changes. Updates the enabled
+ // property of the remove button.
+ virtual void OnSelectionChanged();
+ void OnRemoveLanguage();
+ void OnMoveDownLanguage();
+ void OnMoveUpLanguage();
+
+ ChromeViews::Label* languages_instructions_;
+ ChromeViews::View* languages_contents_;
+ ChromeViews::View* button_stack_;
+ ChromeViews::TableView* language_order_table_;
+ ChromeViews::NativeButton* move_up_button_;
+ ChromeViews::NativeButton* move_down_button_;
+ ChromeViews::NativeButton* add_button_;
+ ChromeViews::NativeButton* remove_button_;
+ ChromeViews::Label* language_info_label_;
+ ChromeViews::Label* ui_language_label_;
+ ChromeViews::ComboBox* change_ui_language_combobox_;
+ ChromeViews::ComboBox* change_dictionary_language_combobox_;
+ ChromeViews::Label* dictionary_language_label_;
+
+ scoped_ptr<LanguageOrderTableModel> language_order_table_model_;
+ AddLanguageView* add_language_instance_;
+ StringPrefMember accept_languages_;
+
+ // The contents of the "user interface language" combobox.
+ scoped_ptr<LanguageComboboxModel> ui_language_model_;
+ StringPrefMember app_locale_;
+
+ // The contents of the "dictionary language" combobox.
+ scoped_ptr<LanguageComboboxModel> dictionary_language_model_;
+ StringPrefMember dictionary_language_;
+
+ bool language_table_edited_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LanguagesPageView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_OPTIONS_LANGUAGES_PAGE_VIEW_H__
diff --git a/chrome/browser/views/options/options_group_view.cc b/chrome/browser/views/options/options_group_view.cc
new file mode 100644
index 0000000..80eb0bb
--- /dev/null
+++ b/chrome/browser/views/options/options_group_view.cc
@@ -0,0 +1,161 @@
+// 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 <vsstyle.h>
+#include <vssym32.h>
+
+#include "chrome/browser/views/options/options_group_view.h"
+
+#include "base/gfx/native_theme.h"
+#include "base/gfx/skia_utils.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/separator.h"
+#include "generated_resources.h"
+
+static const int kLeftColumnWidthChars = 20;
+static const int kOptionsGroupViewColumnSpacing = 30;
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsGroupView, public:
+
+OptionsGroupView::OptionsGroupView(ChromeViews::View* contents,
+ const std::wstring& title,
+ const std::wstring& description,
+ bool show_separator)
+ : contents_(contents),
+ title_label_(new ChromeViews::Label(title)),
+ description_label_(new ChromeViews::Label(description)),
+ separator_(NULL),
+ show_separator_(show_separator),
+ highlighted_(false) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont title_font =
+ rb.GetFont(ResourceBundle::BaseFont).DeriveFont(0, ChromeFont::BOLD);
+ title_label_->SetFont(title_font);
+ SkColor title_color = gfx::NativeTheme::instance()->GetThemeColorWithDefault(
+ gfx::NativeTheme::BUTTON, BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, COLOR_WINDOWTEXT);
+ title_label_->SetColor(title_color);
+ title_label_->SetMultiLine(true);
+ title_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+
+ description_label_->SetMultiLine(true);
+ description_label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+}
+
+void OptionsGroupView::SetHighlighted(bool highlighted) {
+ highlighted_ = highlighted;
+ SchedulePaint();
+}
+
+int OptionsGroupView::GetContentsWidth() const {
+ return contents_->GetWidth();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsGroupView, ChromeViews::View overrides:
+
+void OptionsGroupView::Paint(ChromeCanvas* canvas) {
+ if (highlighted_) {
+ COLORREF infocolor = GetSysColor(COLOR_INFOBK);
+ SkColor background_color = SkColorSetRGB(GetRValue(infocolor),
+ GetGValue(infocolor),
+ GetBValue(infocolor));
+ int y_offset = kUnrelatedControlVerticalSpacing / 2;
+ canvas->FillRectInt(background_color, 0, 0, GetWidth(),
+ GetHeight() - y_offset);
+ }
+}
+
+void OptionsGroupView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (is_add && child == this)
+ Init();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsGroupView, private:
+
+void OptionsGroupView::Init() {
+ using ChromeViews::GridLayout;
+ using ChromeViews::ColumnSet;
+
+ GridLayout* layout = new GridLayout(this);
+ SetLayoutManager(layout);
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont font = rb.GetFont(ResourceBundle::BaseFont);
+ std::wstring left_column_chars =
+ l10n_util::GetString(IDS_OPTIONS_DIALOG_LEFT_COLUMN_WIDTH_CHARS);
+ int left_column_width =
+ font.ave_char_width() * _wtoi(left_column_chars.c_str());
+
+ const int two_column_layout_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(two_column_layout_id);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
+ GridLayout::FIXED, left_column_width, 0);
+ column_set->AddPaddingColumn(0, kOptionsGroupViewColumnSpacing);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, two_column_layout_id);
+ // We need to do this so that the label can calculate an appropriate
+ // preferred size (width * height) based on this width constraint. Otherwise
+ // Label::GetPreferredSize will return 0,0 as the preferred size.
+ title_label_->SetBounds(0, 0, left_column_width, 0);
+ layout->AddView(title_label_);
+ layout->AddView(contents_, 1, 3, GridLayout::FILL, GridLayout::FILL);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(1, two_column_layout_id);
+ // See comment above for title_label_.
+ description_label_->SetBounds(0, 0, left_column_width, 0);
+ layout->AddView(description_label_);
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+
+ if (show_separator_) {
+ const int single_column_layout_id = 1;
+ column_set = layout->AddColumnSet(single_column_layout_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ separator_ = new ChromeViews::Separator;
+ layout->StartRow(0, single_column_layout_id);
+ layout->AddView(separator_);
+ }
+}
diff --git a/chrome/browser/views/options/options_group_view.h b/chrome/browser/views/options/options_group_view.h
new file mode 100644
index 0000000..b6c0e3f
--- /dev/null
+++ b/chrome/browser/views/options/options_group_view.h
@@ -0,0 +1,100 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_OPTIONS_GROUP_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_OPTIONS_GROUP_VIEW_H__
+
+#include "chrome/views/view.h"
+
+namespace ChromeViews {
+class Label;
+class Separator;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsGroupView
+//
+// A helper View that gathers related options into groups with a title and
+// optional description.
+//
+class OptionsGroupView : public ChromeViews::View {
+ public:
+ OptionsGroupView(ChromeViews::View* contents,
+ const std::wstring& title,
+ const std::wstring& description,
+ bool show_separator);
+ virtual ~OptionsGroupView() {}
+
+ // Sets the group as being highlighted to attract attention.
+ void SetHighlighted(bool highlighted);
+
+ // Retrieves the width of the ContentsView. Used to help size wrapping items.
+ int GetContentsWidth() const;
+
+ class ContentsView : public ChromeViews::View {
+ public:
+ virtual ~ContentsView() {}
+
+ // ChromeViews::View overrides:
+ virtual void DidChangeBounds(const CRect& prev_bounds,
+ const CRect& next_bounds) {
+ Layout();
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ContentsView);
+ };
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ private:
+ void Init();
+
+ ChromeViews::View* contents_;
+ ChromeViews::Label* title_label_;
+ ChromeViews::Label* description_label_;
+ ChromeViews::Separator* separator_;
+
+ // True if we should show a separator line below the contents of this
+ // section.
+ bool show_separator_;
+
+ // True if this section should have a highlighted treatment to draw the
+ // user's attention.
+ bool highlighted_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OptionsGroupView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_OPTIONS_GROUP_VIEW_H__
diff --git a/chrome/browser/views/options/options_page_view.cc b/chrome/browser/views/options/options_page_view.cc
new file mode 100644
index 0000000..b659249
--- /dev/null
+++ b/chrome/browser/views/options/options_page_view.cc
@@ -0,0 +1,82 @@
+// 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/views/options/options_page_view.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/common/pref_service.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsPageView
+
+OptionsPageView::OptionsPageView(Profile* profile)
+ : profile_(profile),
+ initialized_(false) {
+}
+
+OptionsPageView::~OptionsPageView() {
+}
+
+void OptionsPageView::UserMetricsRecordAction(const wchar_t* action,
+ PrefService* prefs) {
+ UserMetrics::RecordComputedAction(action, profile());
+ if (prefs)
+ prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsPageView, NotificationObserver implementation:
+
+void OptionsPageView::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NOTIFY_PREF_CHANGED)
+ NotifyPrefChanged(Details<std::wstring>(details).ptr());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsPageView, ChromeViews::View overrides:
+
+void OptionsPageView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (!initialized_ && is_add && GetViewContainer()) {
+ // It is important that this only get done _once_ otherwise we end up
+ // duplicating the view hierarchy when tabs are switched.
+ initialized_ = true;
+ InitControlLayout();
+ NotifyPrefChanged(NULL);
+ }
+}
+
+HWND OptionsPageView::GetRootWindow() const {
+ // Our ViewContainer is the TabbedPane content HWND, which is a child HWND.
+ // We need the root HWND for parenting.
+ return GetAncestor(GetViewContainer()->GetHWND(), GA_ROOT);
+} \ No newline at end of file
diff --git a/chrome/browser/views/options/options_page_view.h b/chrome/browser/views/options/options_page_view.h
new file mode 100644
index 0000000..cf107e5
--- /dev/null
+++ b/chrome/browser/views/options/options_page_view.h
@@ -0,0 +1,104 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_OPTIONS_OPTIONS_PAGE_VIEW_H__
+#define CHROME_BROWSER_VIEWS_OPTIONS_OPTIONS_PAGE_VIEW_H__
+
+#include "chrome/browser/options_window.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/views/link.h"
+#include "chrome/views/native_button.h"
+
+class PrefService;
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsPageView
+//
+// A base class for Options dialog pages that handles ensuring control
+// initialization is done just once.
+//
+class OptionsPageView : public ChromeViews::View,
+ public NotificationObserver {
+ public:
+ virtual ~OptionsPageView();
+
+ // Returns true if the window containing this view can be closed, given the
+ // current state of this view. This can be used to prevent the window from
+ // being closed when a modal dialog box is showing, for example.
+ virtual bool CanClose() const { return true; }
+
+ // Highlights the specified group to attract the user's attention.
+ virtual void HighlightGroup(OptionsGroup highlight_group) { }
+
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ protected:
+ // This class cannot be instantiated directly, but its constructor must be
+ // called by derived classes.
+ explicit OptionsPageView(Profile* profile);
+
+ // Returns the Profile associated with this page.
+ Profile* profile() const { return profile_; }
+
+ // Records a user action and schedules the prefs file to be saved.
+ void UserMetricsRecordAction(const wchar_t* action, PrefService* prefs);
+
+ // Initializes the layout of the controls within the panel.
+ virtual void InitControlLayout() = 0;
+
+ // Allows the UI to update when a preference value changes. The parameter is
+ // the specific pref that changed, or NULL if all pref UI should be
+ // validated. This is also called immediately after InitControlLayout()
+ // during setup, but with NULL as the parameter to allow initial state to be
+ // set.
+ virtual void NotifyPrefChanged(const std::wstring* pref_name) { }
+
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ // Returns the HWND on which created windows should be parented.
+ HWND GetRootWindow() const;
+
+ private:
+ // The Profile associated with this page.
+ Profile* profile_;
+
+ // Whether or not the control layout has been initialized for this page.
+ bool initialized_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OptionsPageView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_OPTIONS_OPTIONS_PAGE_VIEW_H__
diff --git a/chrome/browser/views/options/options_window_view.cc b/chrome/browser/views/options/options_window_view.cc
new file mode 100644
index 0000000..72c7029
--- /dev/null
+++ b/chrome/browser/views/options/options_window_view.cc
@@ -0,0 +1,254 @@
+// 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/options_window.h"
+
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/views/options/advanced_page_view.h"
+#include "chrome/browser/views/options/content_page_view.h"
+#include "chrome/browser/views/options/general_page_view.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/tabbed_pane.h"
+#include "chrome/views/window.h"
+#include "generated_resources.h"
+
+static const int kDefaultWindowWidthChars = 85;
+static const int kDefaultWindowHeightLines = 29;
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsWindowView
+//
+// The contents of the Options dialog window.
+//
+class OptionsWindowView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::TabbedPane::Listener {
+ public:
+ explicit OptionsWindowView(Profile* profile);
+ virtual ~OptionsWindowView();
+
+ ChromeViews::Window* container() const { return container_; }
+ void set_container(ChromeViews::Window* container) {
+ container_ = container;
+ }
+
+ // Shows the Tab corresponding to the specified OptionsPage.
+ void ShowOptionsPage(OptionsPage page, OptionsGroup highlight_group);
+
+ // ChromeViews::DialogDelegate implementation:
+ virtual int GetDialogButtons() const { return DIALOGBUTTON_CANCEL; }
+ virtual std::wstring GetWindowTitle() const;
+ virtual void WindowClosing();
+ virtual bool Cancel();
+
+ // ChromeViews::TabbedPane::Listener implementation:
+ virtual void TabSelectedAt(int index);
+
+ // ChromeViews::View overrides:
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize* out);
+
+ protected:
+ // ChromeViews::View overrides:
+ virtual void ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child);
+ private:
+ // Init the assorted Tabbed pages
+ void Init();
+
+ // Returns the currently selected OptionsPageView.
+ OptionsPageView* GetCurrentOptionsPageView() const;
+
+ // The Tab view that contains all of the options pages.
+ ChromeViews::TabbedPane* tabs_;
+
+ // The Options dialog window.
+ ChromeViews::Window* container_;
+
+ // The Profile associated with these options.
+ Profile* profile_;
+
+ // The last page the user was on when they opened the Options window.
+ IntegerPrefMember last_selected_page_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OptionsWindowView);
+};
+
+// static
+static OptionsWindowView* instance_ = NULL;
+static const int kDialogPadding = 7;
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsWindowView, public:
+
+OptionsWindowView::OptionsWindowView(Profile* profile)
+ // Always show preferences for the original profile. Most state when off
+ // the record comes from the original profile, but we explicitly use
+ // the original profile to avoid potential problems.
+ : profile_(profile->GetOriginalProfile()) {
+ // The download manager needs to be initialized before the contents of the
+ // Options Window are created.
+ profile_->GetDownloadManager();
+ // We don't need to observe changes in this value.
+ last_selected_page_.Init(prefs::kOptionsWindowLastTabIndex,
+ g_browser_process->local_state(), NULL);
+}
+
+OptionsWindowView::~OptionsWindowView() {
+}
+
+void OptionsWindowView::ShowOptionsPage(OptionsPage page,
+ OptionsGroup highlight_group) {
+ // If the window is not yet visible, we need to show it (it will become
+ // active), otherwise just bring it to the front.
+ if (!container_->IsVisible()) {
+ container_->Show();
+ } else {
+ container_->Activate();
+ }
+
+ if (page == OPTIONS_PAGE_DEFAULT) {
+ // Remember the last visited page from local state.
+ page = static_cast<OptionsPage>(last_selected_page_.GetValue());
+ if (page == OPTIONS_PAGE_DEFAULT)
+ page = OPTIONS_PAGE_GENERAL;
+ }
+ DCHECK(page > OPTIONS_PAGE_DEFAULT && page < OPTIONS_PAGE_COUNT);
+ tabs_->SelectTabAt(static_cast<int>(page));
+
+ GetCurrentOptionsPageView()->HighlightGroup(highlight_group);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsWindowView, ChromeViews::DialogDelegate implementation:
+
+std::wstring OptionsWindowView::GetWindowTitle() const {
+ return l10n_util::GetStringF(IDS_OPTIONS_DIALOG_TITLE,
+ l10n_util::GetString(IDS_PRODUCT_NAME));
+}
+
+void OptionsWindowView::WindowClosing() {
+ // Clear the static instance so that the next time ShowOptionsWindow() is
+ // called a new window is opened.
+ instance_ = NULL;
+}
+
+bool OptionsWindowView::Cancel() {
+ return GetCurrentOptionsPageView()->CanClose();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsWindowView, ChromeViews::TabbedPane::Listener implementation:
+
+void OptionsWindowView::TabSelectedAt(int index) {
+ DCHECK(index > OPTIONS_PAGE_DEFAULT && index < OPTIONS_PAGE_COUNT);
+ last_selected_page_.SetValue(index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsWindowView, ChromeViews::View overrides:
+
+void OptionsWindowView::Layout() {
+ tabs_->SetBounds(kDialogPadding, kDialogPadding,
+ GetWidth() - (2 * kDialogPadding),
+ GetHeight() - (2 * kDialogPadding));
+}
+
+void OptionsWindowView::GetPreferredSize(CSize* out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_OPTIONS_DIALOG_WIDTH_CHARS,
+ IDS_OPTIONS_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+void OptionsWindowView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ // Can't init before we're inserted into a ViewContainer, because we require
+ // a HWND to parent native child controls to.
+ if (is_add && child == this)
+ Init();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OptionsWindowView, private:
+
+void OptionsWindowView::Init() {
+ tabs_ = new ChromeViews::TabbedPane;
+ tabs_->SetListener(this);
+ AddChildView(tabs_);
+
+ GeneralPageView* general_page = new GeneralPageView(profile_);
+ tabs_->AddTabAtIndex(0, l10n_util::GetString(IDS_OPTIONS_GENERAL_TAB_LABEL),
+ general_page, false);
+
+ ContentPageView* content_page = new ContentPageView(profile_);
+ tabs_->AddTabAtIndex(1, l10n_util::GetString(IDS_OPTIONS_CONTENT_TAB_LABEL),
+ content_page, false);
+
+ AdvancedPageView* advanced_page = new AdvancedPageView(profile_);
+ tabs_->AddTabAtIndex(2, l10n_util::GetString(IDS_OPTIONS_ADVANCED_TAB_LABEL),
+ advanced_page, false);
+
+ DCHECK(tabs_->GetTabCount() == OPTIONS_PAGE_COUNT);
+}
+
+OptionsPageView* OptionsWindowView::GetCurrentOptionsPageView() const {
+ ChromeViews::RootView* contents_root_view = tabs_->GetContentsRootView();
+ DCHECK(contents_root_view->GetChildViewCount() == 1);
+ return static_cast<OptionsPageView*>(contents_root_view->GetChildViewAt(0));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Factory/finder method:
+
+void ShowOptionsWindow(OptionsPage page,
+ OptionsGroup highlight_group,
+ Profile* profile) {
+ DCHECK(profile);
+ // If there's already an existing options window, activate it and switch to
+ // the specified page.
+ // TODO(beng): note this is not multi-simultaneous-profile-safe. When we care
+ // about this case this will have to be fixed.
+ if (!instance_) {
+ instance_ = new OptionsWindowView(profile);
+ instance_->set_container(ChromeViews::Window::CreateChromeWindow(
+ NULL, gfx::Rect(), instance_, instance_));
+ // The window is alive by itself now...
+ }
+ instance_->ShowOptionsPage(page, highlight_group);
+}
diff --git a/chrome/browser/views/password_manager_view.cc b/chrome/browser/views/password_manager_view.cc
new file mode 100644
index 0000000..cf5eb50
--- /dev/null
+++ b/chrome/browser/views/password_manager_view.cc
@@ -0,0 +1,399 @@
+// 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 "base/string_util.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/views/password_manager_view.h"
+#include "chrome/views/background.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/native_button.h"
+#include "webkit/glue/password_form.h"
+
+#include "generated_resources.h"
+
+using ChromeViews::ColumnSet;
+using ChromeViews::GridLayout;
+
+// We can only have one PasswordManagerView at a time.
+static PasswordManagerView* instance_ = NULL;
+
+static const int kDefaultWindowWidth = 530;
+static const int kDefaultWindowHeight = 240;
+
+////////////////////////////////////////////////////////////////////////////////
+// MultiLabelButtons
+//
+MultiLabelButtons::MultiLabelButtons(const std::wstring& label,
+ const std::wstring& alt_label)
+ : NativeButton(label),
+ label_(label),
+ alt_label_(alt_label),
+ pref_size_(-1, -1) {
+}
+
+void MultiLabelButtons::GetPreferredSize(CSize *out) {
+ if (pref_size_.cx == -1 && pref_size_.cy == -1) {
+ // Let's compute our preferred size.
+ std::wstring current_label = GetLabel();
+ SetLabel(label_);
+ NativeButton::GetPreferredSize(&pref_size_);
+ SetLabel(alt_label_);
+ CSize alt_pref_size;
+ NativeButton::GetPreferredSize(&alt_pref_size);
+ // Revert to the original label.
+ SetLabel(current_label);
+ pref_size_.cx = std::max(pref_size_.cx, alt_pref_size.cx);
+ pref_size_.cy = std::max(pref_size_.cy, alt_pref_size.cy);
+ }
+ *out = pref_size_;
+}
+
+////////////////////////////////////////////////////////////////////
+// PasswordManagerTableModel
+PasswordManagerTableModel::PasswordManagerTableModel(
+ WebDataService* profile_web_data_service)
+ : saved_signons_deleter_(&saved_signons_),
+ observer_(NULL),
+ pending_login_query_(NULL),
+ web_data_service_(profile_web_data_service) {
+ DCHECK(web_data_service_);
+}
+
+PasswordManagerTableModel::~PasswordManagerTableModel() {
+ CancelLoginsQuery();
+}
+
+int PasswordManagerTableModel::RowCount() {
+ return static_cast<int>(saved_signons_.size());
+}
+
+std::wstring PasswordManagerTableModel::GetText(int row,
+ int col_id) {
+ PasswordForm* signon = GetPasswordFormAt(row);
+ DCHECK(signon);
+ switch (col_id) {
+ case IDS_PASSWORD_MANAGER_VIEW_SITE_COLUMN: // Site.
+ return UTF8ToWide(signon->origin.spec());
+ case IDS_PASSWORD_MANAGER_VIEW_USERNAME_COLUMN: // Username.
+ return signon->username_value;
+ default:
+ NOTREACHED() << "Invalid column.";
+ return std::wstring();
+ }
+}
+
+void PasswordManagerTableModel::SetObserver(
+ ChromeViews::TableModelObserver* observer) {
+ observer_ = observer;
+}
+
+void PasswordManagerTableModel::GetAllSavedLoginsForProfile() {
+ DCHECK(!pending_login_query_);
+ pending_login_query_ = web_data_service_->GetAllAutofillableLogins(this);
+}
+
+void PasswordManagerTableModel::OnWebDataServiceRequestDone(
+ WebDataService::Handle h,
+ const WDTypedResult* result) {
+ DCHECK_EQ(pending_login_query_, h);
+ pending_login_query_ = NULL;
+
+ if (!result)
+ return;
+
+ DCHECK(result->GetType() == PASSWORD_RESULT);
+
+ // Get the result from the database into a useable form.
+ const WDResult<std::vector<PasswordForm*> >* r =
+ static_cast<const WDResult<std::vector<PasswordForm*> >*>(result);
+ // Copy vector of *pointers* to saved_logins_, thus taking ownership of the
+ // PasswordForm's in the result.
+ saved_signons_ = r->GetValue();
+ if (observer_)
+ observer_->OnModelChanged();
+}
+
+void PasswordManagerTableModel::CancelLoginsQuery() {
+ if (pending_login_query_) {
+ web_data_service_->CancelRequest(pending_login_query_);
+ pending_login_query_ = NULL;
+ }
+}
+
+PasswordForm* PasswordManagerTableModel::GetPasswordFormAt(int row) {
+ DCHECK(row >= 0 && row < RowCount());
+ return saved_signons_[row];
+}
+
+void PasswordManagerTableModel::ForgetAndRemoveSignon(int row) {
+ DCHECK(row >= 0 && row < RowCount());
+ PasswordForms::iterator target_iter = saved_signons_.begin() + row;
+ // Remove from DB, memory, and vector.
+ web_data_service_->RemoveLogin(**target_iter);
+ delete *target_iter;
+ saved_signons_.erase(target_iter);
+ if (observer_)
+ observer_->OnItemsRemoved(row, 1);
+}
+
+void PasswordManagerTableModel::ForgetAndRemoveAllSignons() {
+ PasswordForms::iterator iter = saved_signons_.begin();
+ while (iter != saved_signons_.end()) {
+ web_data_service_->RemoveLogin(**iter);
+ delete *iter;
+ iter = saved_signons_.erase(iter);
+ }
+ if (observer_)
+ observer_->OnModelChanged();
+}
+
+//////////////////////////////////////////////////////////////////////
+// PasswordManagerView
+
+// static
+void PasswordManagerView::Show(Profile* profile) {
+ DCHECK(profile);
+ if (!instance_) {
+ instance_ = new PasswordManagerView(profile);
+
+ // manager is owned by the dialog window, so Close() will delete it.
+ instance_->dialog_ = ChromeViews::Window::CreateChromeWindow(NULL,
+ gfx::Rect(),
+ instance_,
+ instance_);
+ }
+ if (!instance_->dialog_->IsVisible()) {
+ instance_->dialog_->Show();
+ } else {
+ instance_->dialog_->Activate();
+ }
+}
+
+PasswordManagerView::PasswordManagerView(Profile* profile)
+ : show_button_(
+ l10n_util::GetString(IDS_PASSWORD_MANAGER_VIEW_SHOW_BUTTON),
+ l10n_util::GetString(IDS_PASSWORD_MANAGER_VIEW_HIDE_BUTTON)),
+ remove_button_(l10n_util::GetString(
+ IDS_PASSWORD_MANAGER_VIEW_REMOVE_BUTTON)),
+ remove_all_button_(l10n_util::GetString(
+ IDS_PASSWORD_MANAGER_VIEW_REMOVE_ALL_BUTTON)),
+ table_model_(profile->GetWebDataService(Profile::EXPLICIT_ACCESS)) {
+ Init();
+}
+
+void PasswordManagerView::SetupTable() {
+ // Creates the different columns for the table.
+ // The float resize values are the result of much tinkering.
+ std::vector<ChromeViews::TableColumn> columns;
+ columns.push_back(
+ ChromeViews::TableColumn(
+ IDS_PASSWORD_MANAGER_VIEW_SITE_COLUMN,
+ ChromeViews::TableColumn::LEFT, -1, 0.55f));
+ columns.push_back(
+ ChromeViews::TableColumn(
+ IDS_PASSWORD_MANAGER_VIEW_USERNAME_COLUMN,
+ ChromeViews::TableColumn::RIGHT, -1, 0.37f));
+ table_view_ = new ChromeViews::TableView(&table_model_,
+ columns,
+ ChromeViews::TEXT_ONLY,
+ true, true, true);
+ table_view_->SetObserver(this);
+}
+
+void PasswordManagerView::SetupButtonsAndLabels() {
+ // Tell View not to delete class stack allocated views.
+
+ show_button_.SetParentOwned(false);
+ show_button_.SetListener(this);
+ show_button_.SetEnabled(false);
+
+ remove_button_.SetParentOwned(false);
+ remove_button_.SetListener(this);
+ remove_button_.SetEnabled(false);
+
+ remove_all_button_.SetParentOwned(false);
+ remove_all_button_.SetListener(this);
+
+ password_label_.SetParentOwned(false);
+}
+
+void PasswordManagerView::Init() {
+ // Configure the background and view elements (buttons, labels, table).
+ SetupButtonsAndLabels();
+ SetupTable();
+
+ // Do the layout thing.
+ const int top_column_set_id = 0;
+ const int lower_column_set_id = 1;
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+
+ // Design the grid.
+ ColumnSet* column_set = layout->AddColumnSet(top_column_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::FIXED, 300, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ column_set = layout->AddColumnSet(lower_column_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(1, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->LinkColumnSizes(0, 2, -1);
+
+ // Fill the grid.
+ layout->StartRow(0.05f, top_column_set_id);
+ layout->AddView(table_view_, 1, 3);
+ layout->AddView(&remove_button_);
+ layout->StartRow(0.05f, top_column_set_id);
+ layout->SkipColumns(1);
+ layout->AddView(&show_button_);
+ layout->StartRow(0.80f, top_column_set_id);
+ layout->SkipColumns(1);
+ layout->AddView(&password_label_);
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ // Ask the database for saved password data.
+ table_model_.GetAllSavedLoginsForProfile();
+}
+
+PasswordManagerView::~PasswordManagerView() {
+}
+
+void PasswordManagerView::Layout() {
+ GetLayoutManager()->Layout(this);
+
+ // Manually lay out the Remove All button in the same row as
+ // the close button.
+ CRect parent_bounds;
+ GetParent()->GetLocalBounds(&parent_bounds, false);
+ CSize prefsize;
+ remove_all_button_.GetPreferredSize(&prefsize);
+ int button_y = parent_bounds.bottom - prefsize.cy - kButtonVEdgeMargin;
+ remove_all_button_.SetBounds(kPanelHorizMargin, button_y, prefsize.cx,
+ prefsize.cy);
+}
+
+void PasswordManagerView::GetPreferredSize(CSize* out) {
+ out->cx = kDefaultWindowWidth;
+ out->cy = kDefaultWindowHeight;
+}
+
+void PasswordManagerView::ViewHierarchyChanged(bool is_add,
+ ChromeViews::View* parent,
+ ChromeViews::View* child) {
+ if (child == this) {
+ // Add and remove the Remove All button from the ClientView's hierarchy.
+ if (is_add) {
+ parent->AddChildView(&remove_all_button_);
+ } else {
+ parent->RemoveChildView(&remove_all_button_);
+ }
+ }
+}
+
+void PasswordManagerView::OnSelectionChanged() {
+ bool has_selection = table_view_->SelectedRowCount() > 0;
+ remove_button_.SetEnabled(has_selection);
+ // Reset the password related views.
+ show_button_.SetLabel(
+ l10n_util::GetString(IDS_PASSWORD_MANAGER_VIEW_SHOW_BUTTON));
+ show_button_.SetEnabled(has_selection);
+ password_label_.SetText(std::wstring());
+}
+
+int PasswordManagerView::GetDialogButtons() const {
+ return DIALOGBUTTON_CANCEL;
+}
+
+bool PasswordManagerView::CanResize() const {
+ return true;
+}
+
+bool PasswordManagerView::CanMaximize() const {
+ return false;
+}
+
+bool PasswordManagerView::IsAlwaysOnTop() const {
+ return false;
+}
+
+bool PasswordManagerView::HasAlwaysOnTopMenu() const {
+ return false;
+}
+
+std::wstring PasswordManagerView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_PASSWORD_MANAGER_VIEW_TITLE);
+}
+
+void PasswordManagerView::ButtonPressed(ChromeViews::NativeButton* sender) {
+ DCHECK(dialog_);
+ // Close will result in our destruction.
+ if (sender == &remove_all_button_) {
+ table_model_.ForgetAndRemoveAllSignons();
+ return;
+ }
+
+ // The following require a selection (and only one, since table is single-
+ // select only).
+ ChromeViews::TableSelectionIterator iter = table_view_->SelectionBegin();
+ int row = *iter;
+ PasswordForm* selected = table_model_.GetPasswordFormAt(row);
+ DCHECK(++iter == table_view_->SelectionEnd());
+
+ if (sender == &remove_button_) {
+ table_model_.ForgetAndRemoveSignon(row);
+ } else if (sender == &show_button_) {
+ if (password_label_.GetText().length() == 0) {
+ password_label_.SetText(selected->password_value);
+ show_button_.SetLabel(
+ l10n_util::GetString(IDS_PASSWORD_MANAGER_VIEW_HIDE_BUTTON));
+ } else {
+ password_label_.SetText(L"");
+ show_button_.SetLabel(
+ l10n_util::GetString(IDS_PASSWORD_MANAGER_VIEW_SHOW_BUTTON));
+ }
+ } else {
+ NOTREACHED() << "Invalid button.";
+ }
+}
+
+void PasswordManagerView::WindowClosing() {
+ // The table model will be deleted before the table view, so detach it.
+ table_view_->SetModel(NULL);
+
+ // Clear the static instance so the next time Show() is called, a new
+ // instance is created.
+ instance_ = NULL;
+}
diff --git a/chrome/browser/views/password_manager_view.h b/chrome/browser/views/password_manager_view.h
new file mode 100644
index 0000000..19c92ad
--- /dev/null
+++ b/chrome/browser/views/password_manager_view.h
@@ -0,0 +1,170 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_VIEW_H__
+#define CHROME_BROWSER_PASSWORD_MANAGER_VIEW_H__
+
+#include <vector>
+
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/label.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/window.h"
+
+class Profile;
+struct PasswordForm;
+
+class PasswordManagerTableModel : public ChromeViews::TableModel,
+ public WebDataServiceConsumer {
+ public:
+ explicit PasswordManagerTableModel(WebDataService* profile_web_data_service);
+ virtual ~PasswordManagerTableModel();
+
+ // TableModel methods.
+ virtual int RowCount();
+ virtual std::wstring GetText(int row, int column);
+ virtual void SetObserver(ChromeViews::TableModelObserver* observer);
+
+ // Delete the PasswordForm at specified row from the database (and remove
+ // from view).
+ void ForgetAndRemoveSignon(int row);
+
+ // Delete all saved signons for the active profile (via web data service),
+ // and clear the view.
+ void ForgetAndRemoveAllSignons();
+
+ // WebDataServiceConsumer implementation.
+ virtual void OnWebDataServiceRequestDone(WebDataService::Handle h,
+ const WDTypedResult* result);
+ // Request saved logins data.
+ void GetAllSavedLoginsForProfile();
+
+ // Return the PasswordForm at the specified index.
+ PasswordForm* GetPasswordFormAt(int row);
+
+ private:
+ // Cancel any pending login query involving a callback.
+ void CancelLoginsQuery();
+
+ // The TableView observing this model.
+ ChromeViews::TableModelObserver* observer_;
+
+ // Handle to any pending WebDataService::GetLogins query.
+ WebDataService::Handle pending_login_query_;
+
+ // PasswordForms returned by the web data service query.
+ typedef std::vector<PasswordForm*> PasswordForms;
+ PasswordForms saved_signons_;
+
+ // Deleter for saved_logins_.
+ STLElementDeleter<PasswordForms> saved_signons_deleter_;
+
+ // The web data service associated with the currently active profile.
+ WebDataService* web_data_service_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PasswordManagerTableModel);
+};
+
+// A button that can have 2 different labels set on it and for which the
+// preferred size is the size of the widest string.
+class MultiLabelButtons : public ChromeViews::NativeButton {
+ public:
+ MultiLabelButtons(const std::wstring& label, const std::wstring& alt_label);
+
+ virtual void GetPreferredSize(CSize *out);
+
+ private:
+ std::wstring label_;
+ std::wstring alt_label_;
+ CSize pref_size_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MultiLabelButtons);
+};
+
+class PasswordManagerView : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::TableViewObserver,
+ public ChromeViews::NativeButton::Listener {
+ public:
+ explicit PasswordManagerView(Profile* profile);
+ virtual ~PasswordManagerView();
+
+ // Show the PasswordManagerContentView for the given profile.
+ static void Show(Profile* profile);
+
+ // View methods.
+ virtual void Layout();
+ virtual void GetPreferredSize(CSize *out);
+ virtual void ViewHierarchyChanged(bool is_add, ChromeViews::View* parent,
+ ChromeViews::View* child);
+
+ // ChromeViews::TableViewObserver implementation.
+ virtual void OnSelectionChanged();
+
+ // NativeButton::Listener implementation.
+ virtual void ButtonPressed(ChromeViews::NativeButton* sender);
+
+ // ChromeViews::DialogDelegate methods:
+ virtual int GetDialogButtons() const;
+ virtual bool CanResize() const;
+ virtual bool CanMaximize() const;
+ virtual bool IsAlwaysOnTop() const;
+ virtual bool HasAlwaysOnTopMenu() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual void WindowClosing();
+
+ private:
+ // Wire up buttons, the model, and the table view, and query the DB for
+ // saved login data tied to the given profile.
+ void Init();
+
+ // Helper to configure our buttons and labels.
+ void SetupButtonsAndLabels();
+
+ // Helper to configure our table view.
+ void SetupTable();
+
+ // Components in this view.
+ PasswordManagerTableModel table_model_;
+ ChromeViews::TableView* table_view_;
+
+ // The buttons and labels.
+ MultiLabelButtons show_button_;
+ ChromeViews::NativeButton remove_button_;
+ ChromeViews::NativeButton remove_all_button_;
+ ChromeViews::Label password_label_;
+
+ // The window containing this view.
+ ChromeViews::Window* dialog_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PasswordManagerView);
+};
+#endif // CHROME_BROWSER_PASSWORD_MANAGER_VIEW_H__
diff --git a/chrome/browser/views/restart_message_box.cc b/chrome/browser/views/restart_message_box.cc
new file mode 100644
index 0000000..3d940ab
--- /dev/null
+++ b/chrome/browser/views/restart_message_box.cc
@@ -0,0 +1,84 @@
+// 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/views/restart_message_box.h"
+
+#include "chrome/views/message_box_view.h"
+#include "chrome/views/window.h"
+
+#include "generated_resources.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// RestartMessageBox, public:
+
+// static
+void RestartMessageBox::ShowMessageBox(HWND parent_hwnd) {
+ // When the window closes, it will delete itself.
+ new RestartMessageBox(parent_hwnd);
+}
+
+int RestartMessageBox::GetDialogButtons() const {
+ return DialogDelegate::DIALOGBUTTON_OK;
+}
+
+std::wstring RestartMessageBox::GetDialogButtonLabel(DialogButton button)
+ const {
+ DCHECK(button == DIALOGBUTTON_OK);
+ return l10n_util::GetString(IDS_OK);
+}
+
+std::wstring RestartMessageBox::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_PRODUCT_NAME);
+}
+
+void RestartMessageBox::WindowClosing() {
+ delete this;
+}
+
+bool RestartMessageBox::IsModal() const {
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RestartMessageBox, private:
+
+RestartMessageBox::RestartMessageBox(HWND parent_hwnd) {
+ const int kDialogWidth = 400;
+ // Also deleted when the window closes.
+ MessageBoxView* message_box_view = new MessageBoxView(
+ MessageBoxView::kFlagHasMessage | MessageBoxView::kFlagHasOKButton,
+ l10n_util::GetString(IDS_OPTIONS_RESTART_REQUIRED).c_str(),
+ std::wstring(),
+ kDialogWidth);
+ ChromeViews::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(),
+ message_box_view, this)->Show();
+}
+
+RestartMessageBox::~RestartMessageBox() {
+}
diff --git a/chrome/browser/views/restart_message_box.h b/chrome/browser/views/restart_message_box.h
new file mode 100644
index 0000000..5a5da45
--- /dev/null
+++ b/chrome/browser/views/restart_message_box.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_RESTART_MESSAGE_BOX_H_
+#define CHROME_BROWSER_VIEWS_RESTART_MESSAGE_BOX_H_
+
+#include "base/basictypes.h"
+#include "chrome/views/dialog_delegate.h"
+
+// A dialog box that tells the user that s/he needs to restart Chrome
+// for a change to take effect.
+class RestartMessageBox : public ChromeViews::DialogDelegate {
+ public:
+ // This box is modal to |parent_hwnd|.
+ static void ShowMessageBox(HWND parent_hwnd);
+
+ protected:
+ // ChromeViews::DialogDelegate:
+ virtual int GetDialogButtons() const;
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual std::wstring GetWindowTitle() const;
+
+ // ChromeViews::WindowDelegate:
+ virtual void WindowClosing();
+ virtual bool IsModal() const;
+
+ private:
+ explicit RestartMessageBox(HWND parent_hwnd);
+ virtual ~RestartMessageBox();
+
+ DISALLOW_EVIL_CONSTRUCTORS(RestartMessageBox);
+};
+
+#endif // CHROME_BROWSER_VIEWS_RESTART_MESSAGE_BOX_H_
diff --git a/chrome/browser/views/sad_tab_view.cc b/chrome/browser/views/sad_tab_view.cc
new file mode 100644
index 0000000..9800e0b
--- /dev/null
+++ b/chrome/browser/views/sad_tab_view.cc
@@ -0,0 +1,137 @@
+// 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/views/sad_tab_view.h"
+
+#include "base/gfx/size.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "generated_resources.h"
+#include "skia/include/SkGradientShader.h"
+
+static const int kSadTabOffset = -64;
+static const int kIconTitleSpacing = 20;
+static const int kTitleMessageSpacing = 15;
+static const int kMessageBottomMargin = 20;
+static const float kMessageSize = 0.65f;
+static const SkColor kTitleColor = SK_ColorWHITE;
+static const SkColor kMessageColor = SK_ColorWHITE;
+static const SkColor kBackgroundColor = SkColorSetRGB(35, 48, 64);
+static const SkColor kBackgroundEndColor = SkColorSetRGB(35, 48, 64);
+
+// static
+SkBitmap* SadTabView::sad_tab_bitmap_ = NULL;
+ChromeFont SadTabView::title_font_;
+ChromeFont SadTabView::message_font_;
+std::wstring SadTabView::title_;
+std::wstring SadTabView::message_;
+int SadTabView::title_width_;
+
+SadTabView::SadTabView() {
+ InitClass();
+}
+
+static SkShader* CreateGradientShader(int end_point) {
+ SkColor grad_colors[2] = { kBackgroundColor, kBackgroundEndColor };
+ SkPoint grad_points[2];
+ grad_points[0].set(SkIntToScalar(0), SkIntToScalar(0));
+ grad_points[1].set(SkIntToScalar(0), SkIntToScalar(end_point));
+ return SkGradientShader::CreateLinear(
+ grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode);
+}
+
+void SadTabView::Paint(ChromeCanvas* canvas) {
+ SkShader* background_shader = CreateGradientShader(GetHeight());
+ SkPaint paint;
+ paint.setShader(background_shader);
+ background_shader->unref();
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRectCoords(0, 0,
+ SkIntToScalar(GetWidth()), SkIntToScalar(GetHeight()),
+ paint);
+
+ canvas->DrawBitmapInt(*sad_tab_bitmap_, icon_bounds_.x(), icon_bounds_.y());
+
+ canvas->DrawStringInt(title_, title_font_, kTitleColor, title_bounds_.x(),
+ title_bounds_.y(), title_bounds_.width(),
+ title_bounds_.height(),
+ ChromeCanvas::TEXT_ALIGN_CENTER);
+
+ canvas->DrawStringInt(message_, message_font_, kMessageColor,
+ message_bounds_.x(), message_bounds_.y(),
+ message_bounds_.width(), message_bounds_.height(),
+ ChromeCanvas::MULTI_LINE);
+}
+
+void SadTabView::Layout() {
+ int icon_width = sad_tab_bitmap_->width();
+ int icon_height = sad_tab_bitmap_->height();
+ int icon_x = (GetWidth() - icon_width) / 2;
+ int icon_y = ((GetHeight() - icon_height) / 2) + kSadTabOffset;
+ icon_bounds_.SetRect(icon_x, icon_y, icon_width, icon_height);
+
+ int title_x = (GetWidth() - title_width_) / 2;
+ int title_y = icon_bounds_.bottom() + kIconTitleSpacing;
+ int title_height = title_font_.height();
+ title_bounds_.SetRect(title_x, title_y, title_width_, title_height);
+
+ ChromeCanvas cc(0, 0, true);
+ int message_width = static_cast<int>(GetWidth() * kMessageSize);
+ int message_height = 0;
+ cc.SizeStringInt(message_, message_font_, &message_width, &message_height,
+ ChromeCanvas::MULTI_LINE);
+ int message_x = (GetWidth() - message_width) / 2;
+ int message_y = title_bounds_.bottom() + kTitleMessageSpacing;
+ message_bounds_.SetRect(message_x, message_y, message_width, message_height);
+}
+
+void SadTabView::DidChangeBounds(const CRect&, const CRect&) {
+ Layout();
+}
+
+// static
+void SadTabView::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ title_font_ = rb.GetFont(ResourceBundle::BaseFont).
+ DeriveFont(2, ChromeFont::BOLD);
+ message_font_ = rb.GetFont(ResourceBundle::BaseFont).DeriveFont(1);
+ sad_tab_bitmap_ = rb.GetBitmapNamed(IDR_SAD_TAB);
+
+ title_ = l10n_util::GetString(IDS_SAD_TAB_TITLE);
+ title_width_ = title_font_.GetStringWidth(title_);
+ message_ = l10n_util::GetString(IDS_SAD_TAB_MESSAGE);
+
+ initialized = true;
+ }
+}
+
diff --git a/chrome/browser/views/sad_tab_view.h b/chrome/browser/views/sad_tab_view.h
new file mode 100644
index 0000000..231a5b7e
--- /dev/null
+++ b/chrome/browser/views/sad_tab_view.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_SAD_TAB_H__
+#define CHROME_BROWSER_VIEWS_SAD_TAB_H__
+
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/views/view.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// SadTabView
+//
+// A ChromeViews::View subclass used to render the presentation of the crashed
+// "sad tab" in the browser window when a renderer is destroyed unnaturally.
+//
+// Note that since this view is not (currently) part of a ViewContainer or
+// RootView hierarchy, it cannot respond to events or contain controls that
+// do, right now it is used simply to render. Adding an extra ViewContainer to
+// WebContents seemed like a lot of complexity. Ideally, perhaps WebContents'
+// view portion would itself become a ViewContainer in the future, then event
+// processing will work.
+//
+///////////////////////////////////////////////////////////////////////////////
+class SadTabView : public ChromeViews::View {
+ public:
+ SadTabView();
+ virtual ~SadTabView() {}
+
+ // Overridden from ChromeViews::View:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual void DidChangeBounds(const CRect&, const CRect&);
+
+ private:
+ static void InitClass();
+
+ // Assorted resources for display.
+ static SkBitmap* sad_tab_bitmap_;
+ static ChromeFont title_font_;
+ static ChromeFont message_font_;
+ static std::wstring title_;
+ static std::wstring message_;
+ static int title_width_;
+
+ // Regions within the display for different components, populated by
+ // Layout().
+ gfx::Rect icon_bounds_;
+ gfx::Rect title_bounds_;
+ gfx::Rect message_bounds_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SadTabView);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_SAD_TAB_H__
diff --git a/chrome/browser/views/shelf_item_dialog.cc b/chrome/browser/views/shelf_item_dialog.cc
new file mode 100644
index 0000000..bbec8e9
--- /dev/null
+++ b/chrome/browser/views/shelf_item_dialog.cc
@@ -0,0 +1,511 @@
+// 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/views/shelf_item_dialog.h"
+
+#include "base/gfx/png_decoder.h"
+#include "base/string_util.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/standard_layout.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/url_fixer_upper.h"
+#include "chrome/common/gfx/url_elider.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/stl_util-inl.h"
+#include "chrome/views/background.h"
+#include "chrome/views/focus_manager.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/label.h"
+#include "chrome/views/text_field.h"
+#include "generated_resources.h"
+#include "net/base/net_util.h"
+
+using ChromeViews::ColumnSet;
+using ChromeViews::GridLayout;
+
+// Preferred height of the table.
+static const int kTableWidth = 300;
+
+// The default favicon.
+static SkBitmap* default_fav_icon = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A table model to represent the list of URLS that we the user might want to
+// bookmark.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// How long we query entry points for.
+static const int kPossibleURLTimeScope = 30;
+
+class PossibleURLModel : public ChromeViews::TableModel {
+ public:
+ PossibleURLModel() : profile_(NULL) {
+ if (!default_fav_icon) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_fav_icon = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
+ }
+ }
+
+ virtual ~PossibleURLModel() {
+ }
+
+ void Reload(Profile *profile) {
+ profile_ = profile;
+ consumer_.CancelAllRequests();
+ HistoryService* hs =
+ profile->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (hs) {
+ history::QueryOptions options;
+ options.end_time = Time::Now();
+ options.begin_time =
+ options.end_time - TimeDelta::FromDays(kPossibleURLTimeScope);
+ options.most_recent_visit_only = true;
+ options.max_count = 50;
+
+ hs->QueryHistory(std::wstring(), options, &consumer_,
+ NewCallback(this, &PossibleURLModel::OnHistoryQueryComplete));
+ }
+ }
+
+ void OnHistoryQueryComplete(HistoryService::Handle h,
+ history::QueryResults* result) {
+ results_.Swap(result);
+
+ // The old version of this code would filter out all but the most recent
+ // visit to each host, plus all typed URLs and AUTO_BOOKMARK transitions. I
+ // think this dialog has a lot of work, and I'm not sure those old
+ // conditions are correct (the results look about equal quality for my
+ // history with and without those conditions), so I'm not spending time
+ // re-implementing them here. They used to be implemented in the history
+ // service, but I think they should be implemented here because that was
+ // pretty specific behavior that shouldn't be generally exposed.
+
+ fav_icon_map_.clear();
+ if (observer_)
+ observer_->OnModelChanged();
+ }
+
+ virtual int RowCount() {
+ return static_cast<int>(results_.size());
+ }
+
+ const GURL& GetURL(int row) {
+ if (row < 0 || row >= RowCount()) {
+ NOTREACHED();
+ return GURL::EmptyGURL();
+ }
+ return results_[row].url();
+ }
+
+ const std::wstring& GetTitle(int row) {
+ if (row < 0 || row >= RowCount()) {
+ NOTREACHED();
+ return EmptyWString();
+ }
+ return results_[row].title();
+ }
+
+ virtual std::wstring GetText(int row, int col_id) {
+ if (row < 0 || row >= RowCount()) {
+ NOTREACHED();
+ return std::wstring();
+ }
+
+ if (col_id == IDS_ASI_PAGE_COLUMN)
+ return GetTitle(row);
+
+ // TODO(brettw): this should probably pass the GURL up so the URL elider
+ // can be used at a higher level when we know the width.
+ return gfx::ElideUrl(GetURL(row), ChromeFont(), 0, profile_ ?
+ profile_->GetPrefs()->GetString(prefs::kAcceptLanguages) :
+ std::wstring());
+ }
+
+ virtual SkBitmap GetIcon(int row) {
+ if (row < 0 || row >= RowCount()) {
+ NOTREACHED();
+ return *default_fav_icon;
+ }
+
+ const history::URLResult& result = results_[row];
+ FavIconMap::iterator i = fav_icon_map_.find(result.id());
+ if (i != fav_icon_map_.end()) {
+ if (!i->second.isNull())
+ return i->second;
+ } else if (profile_) {
+ HistoryService* hs =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (hs) {
+ CancelableRequestProvider::Handle h =
+ hs->GetFavIconForURL(
+ result.url(), &consumer_,
+ NewCallback(this, &PossibleURLModel::OnFavIconAvailable));
+ consumer_.SetClientData(hs, h, result.id());
+ }
+ }
+ return *default_fav_icon;
+ }
+
+ virtual void OnFavIconAvailable(
+ HistoryService::Handle h,
+ bool fav_icon_available,
+ scoped_refptr<RefCountedBytes> data,
+ bool expired,
+ GURL icon_url) {
+ if (profile_) {
+ HistoryService* hs =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ history::URLID pid = consumer_.GetClientData(hs, h);
+ if (pid) {
+ SkBitmap bm;
+ if (fav_icon_available) {
+ // The decoder will leave our bitmap empty on error.
+ PNGDecoder::Decode(&data->data, &bm);
+ }
+
+ // Store the bitmap. We store it even if it is empty to make sure we
+ // don't query it again.
+ fav_icon_map_[pid] = bm;
+ if (!bm.isNull() && observer_) {
+ for (size_t i = 0; i < results_.size(); ++i) {
+ if (results_[i].id() == pid)
+ observer_->OnItemsChanged(static_cast<int>(i), 1);
+ }
+ }
+ }
+ }
+ }
+
+ virtual void SetObserver(ChromeViews::TableModelObserver* observer) {
+ observer_ = observer;
+ }
+
+ private:
+ // The current profile.
+ Profile* profile_;
+
+ // Our observer.
+ ChromeViews::TableModelObserver* observer_;
+
+ // Our consumer for favicon requests.
+ CancelableRequestConsumerT<history::URLID, NULL> consumer_;
+
+ // The results provided by the history service.
+ history::QueryResults results_;
+
+ // Map URLID -> Favicon. If we queried for a favicon and there is none, that
+ // URL will have an entry, and the bitmap will be empty.
+ typedef std::map<history::URLID, SkBitmap> FavIconMap;
+ FavIconMap fav_icon_map_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PossibleURLModel);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ShelfItemDialog implementation
+//
+////////////////////////////////////////////////////////////////////////////////
+ShelfItemDialog::ShelfItemDialog(ShelfItemDialogDelegate* delegate,
+ Profile* profile,
+ bool show_title)
+ : dialog_(NULL),
+ profile_(profile),
+ expected_title_handle_(0),
+ delegate_(delegate) {
+ DCHECK(profile_);
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ url_table_model_.reset(new PossibleURLModel());
+
+ ChromeViews::TableColumn col1(IDS_ASI_PAGE_COLUMN,
+ ChromeViews::TableColumn::LEFT, -1,
+ 50);
+ ChromeViews::TableColumn col2(IDS_ASI_URL_COLUMN,
+ ChromeViews::TableColumn::LEFT, -1,
+ 50);
+ std::vector<ChromeViews::TableColumn> cols;
+ cols.push_back(col1);
+ cols.push_back(col2);
+
+ url_table_ = new ChromeViews::TableView(url_table_model_.get(),
+ cols,
+ ChromeViews::ICON_AND_TEXT,
+ true,
+ true,
+ true);
+ url_table_->SetObserver(this);
+
+ // Yummy layout code.
+ const int labels_column_set_id = 0;
+ const int single_column_view_set_id = 1;
+ GridLayout* layout = CreatePanelGridLayout(this);
+ SetLayoutManager(layout);
+ ColumnSet* column_set = layout->AddColumnSet(labels_column_set_id);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ column_set = layout->AddColumnSet(single_column_view_set_id);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::FIXED, kTableWidth, 0);
+
+ if (show_title) {
+ layout->StartRow(0, labels_column_set_id);
+ ChromeViews::Label* title_label = new ChromeViews::Label();
+ title_label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ title_label->SetText(l10n_util::GetString(IDS_ASI_TITLE_LABEL));
+ layout->AddView(title_label);
+
+ title_field_ = new ChromeViews::TextField();
+ title_field_->SetController(this);
+ layout->AddView(title_field_);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ } else {
+ title_field_ = NULL;
+ }
+
+ layout->StartRow(0, labels_column_set_id);
+ ChromeViews::Label* url_label = new ChromeViews::Label();
+ url_label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ url_label->SetText(l10n_util::GetString(IDS_ASI_URL));
+ layout->AddView(url_label);
+
+ url_field_ = new ChromeViews::TextField();
+ url_field_->SetController(this);
+ layout->AddView(url_field_);
+
+ layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
+
+ layout->StartRow(0, single_column_view_set_id);
+ ChromeViews::Label* description_label = new ChromeViews::Label();
+ description_label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
+ description_label->SetText(l10n_util::GetString(IDS_ASI_DESCRIPTION));
+ description_label->SetFont(
+ description_label->GetFont().DeriveFont(0, ChromeFont::BOLD));
+ layout->AddView(description_label);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(1, single_column_view_set_id);
+ layout->AddView(url_table_);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ AddAccelerator(ChromeViews::Accelerator(VK_ESCAPE, false, false, false));
+ AddAccelerator(ChromeViews::Accelerator(VK_RETURN, false, false, false));
+}
+
+ShelfItemDialog::~ShelfItemDialog() {
+ url_table_->SetModel(NULL);
+}
+
+void ShelfItemDialog::Show(HWND parent) {
+ DCHECK(!dialog_);
+ dialog_ =
+ ChromeViews::Window::CreateChromeWindow(parent, gfx::Rect(), this, this);
+ dialog_->Show();
+ if (title_field_) {
+ title_field_->SetText(l10n_util::GetString(IDS_ASI_DEFAULT_TITLE));
+ title_field_->SelectAll();
+ title_field_->RequestFocus();
+ } else {
+ url_field_->SelectAll();
+ url_field_->RequestFocus();
+ }
+ url_table_model_->Reload(profile_);
+}
+
+void ShelfItemDialog::Close() {
+ DCHECK(dialog_);
+ dialog_->Close();
+}
+
+std::wstring ShelfItemDialog::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_ASI_ADD_TITLE);
+}
+
+bool ShelfItemDialog::IsModal() const {
+ return true;
+}
+
+void ShelfItemDialog::WindowClosing() {
+}
+
+std::wstring ShelfItemDialog::GetDialogButtonLabel(DialogButton button) const {
+ if (button == DialogDelegate::DIALOGBUTTON_OK)
+ return l10n_util::GetString(IDS_ASI_ADD);
+ return std::wstring();
+}
+
+void ShelfItemDialog::OnURLInfoAvailable(
+ HistoryService::Handle handle,
+ bool success,
+ const history::URLRow* info,
+ history::VisitVector* unused) {
+ if (handle != expected_title_handle_)
+ return;
+
+ std::wstring s;
+ if (success)
+ s = info->title();
+ if (s.empty())
+ s = l10n_util::GetString(IDS_ASI_DEFAULT_TITLE);
+
+ if (title_field_) {
+ // expected_title_handle_ is reset if the title textfield is edited so we
+ // can safely set the value.
+ title_field_->SetText(s);
+ title_field_->SelectAll();
+ }
+ expected_title_handle_ = 0;
+}
+
+void ShelfItemDialog::InitiateTitleAutoFill(const GURL& url) {
+ HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (!hs)
+ return;
+
+ if (expected_title_handle_)
+ hs->CancelRequest(expected_title_handle_);
+
+ expected_title_handle_ = hs->QueryURL(url, false, &history_consumer_,
+ NewCallback(this, &ShelfItemDialog::OnURLInfoAvailable));
+}
+
+void ShelfItemDialog::ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents) {
+ // If the user has edited the title field we no longer want to autofill it
+ // so we reset the expected handle to an impossible value.
+ if (sender == title_field_)
+ expected_title_handle_ = 0;
+ dialog_->UpdateDialogButtons();
+}
+
+bool ShelfItemDialog::Accept() {
+ if (!IsDialogButtonEnabled(DIALOGBUTTON_OK)) {
+ if (!GetInputURL().is_valid())
+ url_field_->RequestFocus();
+ else if (title_field_)
+ title_field_->RequestFocus();
+ return false;
+ }
+ PerformModelChange();
+ return true;
+}
+
+bool ShelfItemDialog::IsDialogButtonEnabled(DialogButton button) const {
+ if (button == DIALOGBUTTON_OK)
+ return GetInputURL().is_valid();
+ return true;
+}
+
+void ShelfItemDialog::PerformModelChange() {
+ DCHECK(delegate_);
+ GURL url(GetInputURL());
+ const std::wstring title =
+ title_field_ ? title_field_->GetText() : std::wstring();
+ delegate_->AddBookmark(this, title, url);
+}
+
+void ShelfItemDialog::GetPreferredSize(CSize *out) {
+ DCHECK(out);
+ *out = ChromeViews::Window::GetLocalizedContentsSize(
+ IDS_SHELFITEM_DIALOG_WIDTH_CHARS,
+ IDS_SHELFITEM_DIALOG_HEIGHT_LINES).ToSIZE();
+}
+
+bool ShelfItemDialog::AcceleratorPressed(
+ const ChromeViews::Accelerator& accelerator) {
+ if (accelerator.GetKeyCode() == VK_ESCAPE) {
+ dialog_->Close();
+ } else if (accelerator.GetKeyCode() == VK_RETURN) {
+ ChromeViews::FocusManager* fm = ChromeViews::FocusManager::GetFocusManager(
+ GetViewContainer()->GetHWND());
+ if (fm->GetFocusedView() == url_table_) {
+ // Return on table behaves like a double click.
+ OnDoubleClick();
+ } else if (fm->GetFocusedView()== url_field_) {
+ // Return on the url field accepts the input if url is valid. If the URL
+ // is invalid, focus is left on the url field.
+ if (GetInputURL().is_valid()) {
+ PerformModelChange();
+ if (dialog_)
+ dialog_->Close();
+ } else {
+ url_field_->SelectAll();
+ }
+ } else if (title_field_ && fm->GetFocusedView() == title_field_) {
+ url_field_->SelectAll();
+ url_field_->RequestFocus();
+ }
+ }
+ return true;
+}
+
+void ShelfItemDialog::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void ShelfItemDialog::OnSelectionChanged() {
+ int selection = url_table_->FirstSelectedRow();
+ if (selection >= 0 && selection < url_table_model_->RowCount()) {
+ url_field_->SetText(
+ UTF8ToWide(url_table_model_->GetURL(selection).spec()));
+ if (title_field_)
+ title_field_->SetText(url_table_model_->GetTitle(selection));
+ dialog_->UpdateDialogButtons();
+ }
+}
+
+void ShelfItemDialog::OnDoubleClick() {
+ int selection = url_table_->FirstSelectedRow();
+ if (selection >= 0 && selection < url_table_model_->RowCount()) {
+ OnSelectionChanged();
+ PerformModelChange();
+ if (dialog_)
+ dialog_->Close();
+ }
+}
+
+GURL ShelfItemDialog::GetInputURL() const {
+ return GURL(URLFixerUpper::FixupURL(url_field_->GetText(), L""));
+}
diff --git a/chrome/browser/views/shelf_item_dialog.h b/chrome/browser/views/shelf_item_dialog.h
new file mode 100644
index 0000000..e5c0b7d
--- /dev/null
+++ b/chrome/browser/views/shelf_item_dialog.h
@@ -0,0 +1,155 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_SHELF_ITEM_DIALOG_H__
+#define CHROME_BROWSER_VIEWS_SHELF_ITEM_DIALOG_H__
+
+#include "chrome/browser/cancelable_request.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/views/dialog_delegate.h"
+#include "chrome/views/native_button.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/view.h"
+#include "chrome/views/window.h"
+
+namespace ChromeViews {
+class Button;
+class Label;
+}
+
+class PossibleURLModel;
+class Profile;
+class ShelfItemDialog;
+
+// TODO(sky): rename this, perhaps to URLPicker.
+
+// ShelfItemDialog delegate. Notified when the user accepts the entry.
+class ShelfItemDialogDelegate {
+ public:
+ virtual void AddBookmark(ShelfItemDialog* dialog,
+ const std::wstring& title,
+ const GURL& url) = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// This class implements the dialog that let the user add a bookmark or page
+// to the list of urls to open on startup.
+// ShelfItemDialog deletes itself when the dialog is closed.
+//
+////////////////////////////////////////////////////////////////////////////////
+class ShelfItemDialog : public ChromeViews::View,
+ public ChromeViews::DialogDelegate,
+ public ChromeViews::TextField::Controller,
+ public ChromeViews::TableViewObserver {
+ public:
+ ShelfItemDialog(ShelfItemDialogDelegate* delegate,
+ Profile* profile,
+ bool show_title);
+ virtual ~ShelfItemDialog();
+
+ // Show the dialog on the provided contents.
+ virtual void Show(HWND parent);
+
+ // Closes the dialog.
+ void Close();
+
+ // DialogDelegate.
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool IsModal() const;
+ virtual void WindowClosing();
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
+ virtual bool Accept();
+ virtual bool IsDialogButtonEnabled(DialogButton button) const;
+
+ // TextField::Controller.
+ virtual void ContentsChanged(ChromeViews::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(ChromeViews::TextField* sender,
+ UINT message, TCHAR key, UINT repeat_count,
+ UINT flags) {}
+
+ // Overridden from View.
+ virtual void DidChangeBounds(const CRect& previous, const CRect& current);
+ virtual void GetPreferredSize(CSize *out);
+ virtual bool AcceleratorPressed(const ChromeViews::Accelerator& accelerator);
+
+ // TableViewObserver.
+ virtual void OnSelectionChanged();
+ virtual void OnDoubleClick();
+
+ private:
+ // Modify the model from the user interface.
+ void PerformModelChange();
+
+ // Fetch the title for the entered URL. If we get the title in time before
+ // the user starts to modify the title field, the title field is changed.
+ void InitiateTitleAutoFill(const GURL& url);
+
+ // Invoked by the history system when a title becomes available.
+ void OnURLInfoAvailable(HistoryService::Handle handle,
+ bool success,
+ const history::URLRow* info,
+ history::VisitVector* unused);
+
+ // Returns the URL the user has typed.
+ GURL GetInputURL() const;
+
+ // The dialog controller for our current dialog.
+ ChromeViews::Window* dialog_;
+
+ // Profile.
+ Profile* profile_;
+
+ // URL Field.
+ ChromeViews::TextField* url_field_;
+
+ // Title field. This is NULL if we're not showing the title.
+ ChromeViews::TextField* title_field_;
+
+ // The table model.
+ scoped_ptr<PossibleURLModel> url_table_model_;
+
+ // The table of visited urls.
+ ChromeViews::TableView* url_table_;
+
+ // Handle of the title request we are expecting.
+ CancelableRequestProvider::Handle expected_title_handle_;
+
+ // The consumer object for the history database.
+ CancelableRequestConsumer history_consumer_;
+
+ // The delegate.
+ ShelfItemDialogDelegate* delegate_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ShelfItemDialog);
+};
+
+#endif // CHROME_BROWSER_VIEWS_SHELF_ITEM_DIALOG_H__
diff --git a/chrome/browser/views/shell_dialogs.cc b/chrome/browser/views/shell_dialogs.cc
new file mode 100644
index 0000000..bd4b19b
--- /dev/null
+++ b/chrome/browser/views/shell_dialogs.cc
@@ -0,0 +1,551 @@
+// 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/shell_dialogs.h"
+
+#include <windows.h>
+#include <Commdlg.h>
+#include <shlobj.h>
+
+#include <algorithm>
+#include <map>
+
+#include "base/file_util.h"
+#include "base/registry.h"
+#include "base/thread.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/win_util.h"
+#include "generated_resources.h"
+
+class ShellDialogThread : public Thread {
+ public:
+ ShellDialogThread() : Thread("Chrome_ShellDialogThread") { }
+
+ protected:
+ void Init() {
+ // Initializes the COM library on the current thread.
+ CoInitialize(NULL);
+ }
+
+ void CleanUp() {
+ // Closes the COM library on the current thread. CoInitialize must
+ // be balanced by a corresponding call to CoUninitialize.
+ CoUninitialize();
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ShellDialogThread);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// A base class for all shell dialog implementations that handles showing a
+// shell dialog modally on its own thread.
+class BaseShellDialogImpl {
+ public:
+ BaseShellDialogImpl();
+ virtual ~BaseShellDialogImpl();
+
+ protected:
+ // Represents a run of a dialog.
+ struct RunState {
+ // Owning HWND, may be null.
+ HWND owner;
+
+ // Thread dialog is run on.
+ Thread* dialog_thread;
+ };
+
+ // Called at the beginning of a modal dialog run. Disables the owner window
+ // and tracks it. Returns the message loop of the thread that the dialog will
+ // be run on.
+ RunState BeginRun(HWND owner);
+
+ // Cleans up after a dialog run. If the run_state has a valid HWND this makes
+ // sure that the window is enabled. This is essential because BeginRun
+ // aggressively guards against multiple modal dialogs per HWND. Must be called
+ // on the UI thread after the result of the dialog has been determined.
+ //
+ // In addition this deletes the Thread in RunState.
+ void EndRun(RunState run_state);
+
+ // Returns true if a modal shell dialog is currently active for the specified
+ // owner. Must be called on the UI thread.
+ bool IsRunningDialogForOwner(HWND owner) const;
+
+ // Disables the window |owner|. Can be run from either the ui or the dialog
+ // thread. Can be called on either the UI or the dialog thread. This function
+ // is called on the dialog thread after the modal Windows Common dialog
+ // functions return because Windows automatically re-enables the owning
+ // window when those functions return, but we don't actually want them to be
+ // re-enabled until the response of the dialog propagates back to the UI
+ // thread, so we disable the owner manually after the Common dialog function
+ // returns.
+ void DisableOwner(HWND owner);
+
+ // The UI thread's message loop.
+ MessageLoop* ui_loop_;
+
+ private:
+ // Creates a thread to run a shell dialog on. Each dialog requires its own
+ // thread otherwise in some situations where a singleton owns a single
+ // instance of this object we can have a situation where a modal dialog in
+ // one window blocks the appearance of a modal dialog in another.
+ static Thread* CreateDialogThread();
+
+ // Enables the window |owner_|. Can only be run from the ui thread.
+ void EnableOwner(HWND owner);
+
+ // A list of windows that currently own active shell dialogs for this
+ // instance. For example, if the DownloadManager owns an instance of this
+ // object and there are two browser windows open both with Save As dialog
+ // boxes active, this list will consist of the two browser windows' HWNDs.
+ // The derived class must call EndRun once the dialog is done showing to
+ // remove the owning HWND from this list.
+ // This object is static since it is maintained for all instances of this
+ // object - i.e. you can't have a font picker and a file picker open for the
+ // same owner, even though they might be represented by different instances
+ // of this object.
+ // This set only contains non-null HWNDs. NULL hwnds are not added to this
+ // list.
+ typedef std::set<HWND> Owners;
+ static Owners owners_;
+ static int instance_count_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BaseShellDialogImpl);
+};
+
+// static
+BaseShellDialogImpl::Owners BaseShellDialogImpl::owners_;
+int BaseShellDialogImpl::instance_count_ = 0;
+
+BaseShellDialogImpl::BaseShellDialogImpl()
+ : ui_loop_(MessageLoop::current()) {
+ ++instance_count_;
+}
+
+BaseShellDialogImpl::~BaseShellDialogImpl() {
+ // All runs should be complete by the time this is called!
+ if (--instance_count_ == 0)
+ DCHECK(owners_.empty());
+}
+
+BaseShellDialogImpl::RunState BaseShellDialogImpl::BeginRun(HWND owner) {
+ // Cannot run a modal shell dialog if one is already running for this owner.
+ DCHECK(!IsRunningDialogForOwner(owner));
+ // The owner must be a top level window, otherwise we could end up with two
+ // entries in our map for the same top level window.
+ DCHECK(!owner || owner == GetAncestor(owner, GA_ROOT));
+ RunState run_state;
+ run_state.dialog_thread = CreateDialogThread();
+ run_state.owner = owner;
+ if (owner) {
+ owners_.insert(owner);
+ DisableOwner(owner);
+ }
+ return run_state;
+}
+
+void BaseShellDialogImpl::EndRun(RunState run_state) {
+ if (run_state.owner) {
+ DCHECK(IsRunningDialogForOwner(run_state.owner));
+ EnableOwner(run_state.owner);
+ DCHECK(owners_.find(run_state.owner) != owners_.end());
+ owners_.erase(run_state.owner);
+ }
+ DCHECK(run_state.dialog_thread);
+ delete run_state.dialog_thread;
+}
+
+bool BaseShellDialogImpl::IsRunningDialogForOwner(HWND owner) const {
+ return (owner && owners_.find(owner) != owners_.end());
+}
+
+void BaseShellDialogImpl::DisableOwner(HWND owner) {
+ if (IsWindow(owner))
+ EnableWindow(owner, FALSE);
+}
+
+// static
+Thread* BaseShellDialogImpl::CreateDialogThread() {
+ Thread* thread = new ShellDialogThread;
+ bool started = thread->Start();
+ DCHECK(started);
+ return thread;
+}
+
+void BaseShellDialogImpl::EnableOwner(HWND owner) {
+ if (IsWindow(owner))
+ EnableWindow(owner, TRUE);
+}
+
+// Implementation of SelectFileDialog that shows a Windows common dialog for
+// choosing a file or folder.
+class SelectFileDialogImpl : public SelectFileDialog,
+ public BaseShellDialogImpl {
+ public:
+ explicit SelectFileDialogImpl(Listener* listener);
+ virtual ~SelectFileDialogImpl();
+
+ // SelectFileDialog implementation:
+ virtual void SelectFile(Type type, const std::wstring& title,
+ const std::wstring& default_path, HWND owning_hwnd,
+ void* params);
+ virtual bool IsRunning(HWND owning_hwnd) const;
+ virtual void ListenerDestroyed();
+
+ private:
+ // Shows the file selection dialog modal to |owner| and calls the result
+ // back on the ui thread. Run on the dialog thread.
+ void ExecuteSelectFile(Type type,
+ const std::wstring& title,
+ const std::wstring& default_path,
+ RunState run_state,
+ void* params);
+
+ // Notifies the listener that a folder was chosen. Run on the ui thread.
+ void FileSelected(const std::wstring& path, void* params, RunState run_state);
+
+ // Notifies the listener that no file was chosen (the action was canceled).
+ // Run on the ui thread.
+ void FileNotSelected(void* params, RunState run_state);
+
+ // Runs a Folder selection dialog box, passes back the selected folder in
+ // |path| and returns true if the user clicks OK. If the user cancels the
+ // dialog box the value in |path| is not modified and returns false. |title|
+ // is the user-supplied title text to show for the dialog box. Run on the
+ // dialog thread.
+ bool RunSelectFolderDialog(const std::wstring& title,
+ HWND owner,
+ std::wstring* path);
+
+ // Runs an Open file dialog box, with similar semantics for input paramaters
+ // as RunSelectFolderDialog.
+ bool RunOpenFileDialog(const std::wstring& title,
+ HWND owner,
+ std::wstring* path);
+
+ // The listener to be notified of selection completion.
+ Listener* listener_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SelectFileDialogImpl);
+};
+
+SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener)
+ : listener_(listener),
+ BaseShellDialogImpl() {
+}
+
+SelectFileDialogImpl::~SelectFileDialogImpl() {
+}
+
+void SelectFileDialogImpl::SelectFile(Type type,
+ const std::wstring& title,
+ const std::wstring& default_path,
+ HWND owner,
+ void* params) {
+ RunState run_state = BeginRun(owner);
+ run_state.dialog_thread->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SelectFileDialogImpl::ExecuteSelectFile, type,
+ title, default_path, run_state, params));
+}
+
+bool SelectFileDialogImpl::IsRunning(HWND owning_hwnd) const {
+ return listener_ && IsRunningDialogForOwner(owning_hwnd);
+}
+
+void SelectFileDialogImpl::ListenerDestroyed() {
+ // Our associated listener has gone away, so we shouldn't call back to it if
+ // our worker thread returns after the listener is dead.
+ listener_ = NULL;
+}
+
+void SelectFileDialogImpl::ExecuteSelectFile(Type type,
+ const std::wstring& title,
+ const std::wstring& default_path,
+ RunState run_state,
+ void* params) {
+ std::wstring path = default_path;
+ bool success = false;
+ if (type == SELECT_FOLDER) {
+ success = RunSelectFolderDialog(title, run_state.owner, &path);
+ } else if (type == SELECT_SAVEAS_FILE) {
+ success = win_util::SaveFileAs(run_state.owner, default_path, &path);
+ DisableOwner(run_state.owner);
+ } else if (type == SELECT_OPEN_FILE) {
+ success = RunOpenFileDialog(title, run_state.owner, &path);
+ }
+
+ if (success) {
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SelectFileDialogImpl::FileSelected, path, params, run_state));
+ } else {
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SelectFileDialogImpl::FileNotSelected, params, run_state));
+ }
+}
+
+void SelectFileDialogImpl::FileSelected(const std::wstring& selected_folder,
+ void* params,
+ RunState run_state) {
+ if (listener_)
+ listener_->FileSelected(selected_folder, params);
+ EndRun(run_state);
+}
+
+void SelectFileDialogImpl::FileNotSelected(void* params, RunState run_state) {
+ if (listener_)
+ listener_->FileSelectionCanceled(params);
+ EndRun(run_state);
+}
+
+bool SelectFileDialogImpl::RunSelectFolderDialog(const std::wstring& title,
+ HWND owner,
+ std::wstring* path) {
+ DCHECK(path);
+
+ wchar_t dir_buffer[MAX_PATH + 1];
+
+ BROWSEINFO browse_info = {0};
+ browse_info.hwndOwner = owner;
+ browse_info.lpszTitle = title.c_str();
+ browse_info.pszDisplayName = dir_buffer;
+ browse_info.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
+ LPITEMIDLIST list = SHBrowseForFolder(&browse_info);
+ DisableOwner(owner);
+ if (list) {
+ wchar_t out_dir_buffer[MAX_PATH + 1];
+ if (SHGetPathFromIDList(list, out_dir_buffer)) {
+ *path = out_dir_buffer;
+
+ // According to MSDN, win2000 will not resolve shortcuts, so we do it
+ // ourself.
+ file_util::ResolveShortcut(path);
+ return true;
+ }
+ CoTaskMemFree(list);
+ }
+ return false;
+}
+
+bool SelectFileDialogImpl::RunOpenFileDialog(const std::wstring& title,
+ HWND owner,
+ std::wstring* path) {
+ OPENFILENAME ofn;
+ // We must do this otherwise the ofn's FlagsEx may be initialized to random
+ // junk in release builds which can cause the Places Bar not to show up!
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = owner;
+
+ wchar_t filename[MAX_PATH];
+ memcpy(filename, path->c_str(), (path->length()+1) * sizeof(wchar_t));
+
+ ofn.lpstrFile = filename;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_FILEMUSTEXIST;
+
+ // TODO(beng): (http://b/issue?id=1126563) edit the filter options in the
+ // dropdown list.
+ bool success = !!GetOpenFileName(&ofn);
+ DisableOwner(owner);
+ if (success)
+ *path = filename;
+ return success;
+}
+
+// static
+SelectFileDialog* SelectFileDialog::Create(Listener* listener) {
+ return new SelectFileDialogImpl(listener);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// SelectFontDialogImpl
+// Implementation of SelectFontDialog that shows a Windows common dialog for
+// choosing a font.
+class SelectFontDialogImpl : public SelectFontDialog,
+ public BaseShellDialogImpl {
+ public:
+ explicit SelectFontDialogImpl(Listener* listener);
+ virtual ~SelectFontDialogImpl();
+
+ // SelectFontDialog implementation:
+ virtual void SelectFont(HWND owning_hwnd, void* params);
+ virtual void SelectFont(HWND owning_hwnd,
+ void* params,
+ const std::wstring& font_name,
+ int font_size);
+ virtual bool IsRunning(HWND owning_hwnd) const;
+ virtual void ListenerDestroyed();
+
+ private:
+ // Shows the font selection dialog modal to |owner| and calls the result
+ // back on the ui thread. Run on the dialog thread.
+ void ExecuteSelectFont(RunState run_state, void* params);
+
+ // Shows the font selection dialog modal to |owner| and calls the result
+ // back on the ui thread. Run on the dialog thread.
+ void ExecuteSelectFontWithNameSize(RunState run_state,
+ void* params,
+ const std::wstring& font_name,
+ int font_size);
+
+ // Notifies the listener that a font was chosen. Run on the ui thread.
+ void FontSelected(LOGFONT logfont, void* params, RunState run_state);
+
+ // Notifies the listener that no font was chosen (the action was canceled).
+ // Run on the ui thread.
+ void FontNotSelected(void* params, RunState run_state);
+
+ // The listener to be notified of selection completion.
+ Listener* listener_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SelectFontDialogImpl);
+};
+
+SelectFontDialogImpl::SelectFontDialogImpl(Listener* listener)
+ : listener_(listener),
+ BaseShellDialogImpl() {
+}
+
+SelectFontDialogImpl::~SelectFontDialogImpl() {
+}
+
+void SelectFontDialogImpl::SelectFont(HWND owner, void* params) {
+ RunState run_state = BeginRun(owner);
+ run_state.dialog_thread->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SelectFontDialogImpl::ExecuteSelectFont,
+ run_state, params));
+}
+
+void SelectFontDialogImpl::SelectFont(HWND owner, void* params,
+ const std::wstring& font_name,
+ int font_size) {
+ RunState run_state = BeginRun(owner);
+ run_state.dialog_thread->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &SelectFontDialogImpl::ExecuteSelectFontWithNameSize, run_state,
+ params, font_name, font_size));
+}
+
+bool SelectFontDialogImpl::IsRunning(HWND owning_hwnd) const {
+ return listener_ && IsRunningDialogForOwner(owning_hwnd);
+}
+
+void SelectFontDialogImpl::ListenerDestroyed() {
+ // Our associated listener has gone away, so we shouldn't call back to it if
+ // our worker thread returns after the listener is dead.
+ listener_ = NULL;
+}
+
+void SelectFontDialogImpl::ExecuteSelectFont(RunState run_state, void* params) {
+ LOGFONT logfont;
+ CHOOSEFONT cf;
+ cf.lStructSize = sizeof(cf);
+ cf.hwndOwner = run_state.owner;
+ cf.lpLogFont = &logfont;
+ cf.Flags = CF_SCREENFONTS;
+ bool success = !!ChooseFont(&cf);
+ DisableOwner(run_state.owner);
+ if (success) {
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SelectFontDialogImpl::FontSelected, logfont, params, run_state));
+ } else {
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SelectFontDialogImpl::FontNotSelected, params, run_state));
+ }
+}
+
+void SelectFontDialogImpl::ExecuteSelectFontWithNameSize(
+ RunState run_state, void* params, const std::wstring& font_name,
+ int font_size) {
+ // Create the HFONT from font name and size.
+ HDC hdc = GetDC(NULL);
+ long lf_height = -MulDiv(font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ ReleaseDC(NULL, hdc);
+ HFONT hf = ::CreateFont(lf_height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ font_name.c_str());
+ LOGFONT logfont;
+ GetObject(hf, sizeof(LOGFONT), &logfont);
+ CHOOSEFONT cf;
+ cf.lStructSize = sizeof(cf);
+ cf.hwndOwner = run_state.owner;
+ cf.lpLogFont = &logfont;
+ // Limit the list to a reasonable subset of fonts.
+ // TODO : get rid of style selector and script selector
+ // 1. List only truetype font
+ // 2. Exclude vertical fonts (whose names begin with '@')
+ // 3. Exclude symbol and OEM fonts
+ // 4. Limit the size to [8, 40].
+ // See http://msdn.microsoft.com/en-us/library/ms646832(VS.85).aspx
+ cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_TTONLY |
+ CF_NOVERTFONTS | CF_SCRIPTSONLY | CF_LIMITSIZE;
+
+ // These limits are arbitrary and needs to be revisited. Is it bad
+ // to clamp the size at 40 from A11Y point of view?
+ cf.nSizeMin = 8;
+ cf.nSizeMax = 40;
+
+ bool success = !!ChooseFont(&cf);
+ DisableOwner(run_state.owner);
+ if (success) {
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SelectFontDialogImpl::FontSelected, logfont, params, run_state));
+ } else {
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SelectFontDialogImpl::FontNotSelected, params, run_state));
+ }
+}
+
+void SelectFontDialogImpl::FontSelected(LOGFONT logfont,
+ void* params,
+ RunState run_state) {
+ if (listener_) {
+ HFONT font = CreateFontIndirect(&logfont);
+ if (font) {
+ listener_->FontSelected(ChromeFont::CreateFont(font), params);
+ DeleteObject(font);
+ } else {
+ listener_->FontSelectionCanceled(params);
+ }
+ }
+ EndRun(run_state);
+}
+
+void SelectFontDialogImpl::FontNotSelected(void* params, RunState run_state) {
+ if (listener_)
+ listener_->FontSelectionCanceled(params);
+ EndRun(run_state);
+}
+
+// static
+SelectFontDialog* SelectFontDialog::Create(Listener* listener) {
+ return new SelectFontDialogImpl(listener);
+}
diff --git a/chrome/browser/views/star_toggle.cc b/chrome/browser/views/star_toggle.cc
new file mode 100644
index 0000000..e480c40
--- /dev/null
+++ b/chrome/browser/views/star_toggle.cc
@@ -0,0 +1,105 @@
+// 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/views/star_toggle.h"
+
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/resource_bundle.h"
+
+StarToggle::StarToggle(Delegate* delegate)
+ : delegate_(delegate),
+ state_(false),
+ change_state_immediately_(false) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ state_off_ = rb.GetBitmapNamed(IDR_CONTENT_STAR_OFF);
+ state_on_ = rb.GetBitmapNamed(IDR_CONTENT_STAR_ON);
+ SetFocusable(true);
+}
+
+StarToggle::~StarToggle() {
+}
+
+void StarToggle::SetState(bool s) {
+ if (s != state_) {
+ state_ = s;
+ SchedulePaint();
+ }
+}
+
+bool StarToggle::GetState() const {
+ return state_;
+}
+
+void StarToggle::Paint(ChromeCanvas* canvas) {
+ PaintFocusBorder(canvas);
+ canvas->DrawBitmapInt(state_ ? *state_on_ : *state_off_,
+ (GetWidth() - state_off_->width()) / 2,
+ (GetHeight() - state_off_->height()) / 2);
+}
+
+void StarToggle::GetPreferredSize(CSize* out) {
+ out->cx = state_off_->width();
+ out->cy = state_off_->height();
+}
+
+bool StarToggle::OnMouseDragged(const ChromeViews::MouseEvent& e) {
+ return e.IsLeftMouseButton();
+}
+
+bool StarToggle::OnMousePressed(const ChromeViews::MouseEvent& e) {
+ if (e.IsLeftMouseButton() && HitTest(e.GetLocation())) {
+ RequestFocus();
+ return true;
+ }
+ return false;
+}
+
+void StarToggle::OnMouseReleased(const ChromeViews::MouseEvent& e,
+ bool canceled) {
+ if (e.IsLeftMouseButton() && HitTest(e.GetLocation()))
+ SwitchState();
+}
+
+bool StarToggle::OnKeyPressed(const ChromeViews::KeyEvent& e) {
+ if ((e.GetCharacter() == L' ') || (e.GetCharacter() == L'\n')) {
+ SwitchState();
+ return true;
+ }
+ return false;
+}
+
+void StarToggle::SwitchState() {
+ const bool new_state = !state_;
+ if (change_state_immediately_)
+ state_ = new_state;
+ SchedulePaint();
+ delegate_->StarStateChanged(new_state);
+} \ No newline at end of file
diff --git a/chrome/browser/views/star_toggle.h b/chrome/browser/views/star_toggle.h
new file mode 100644
index 0000000..0a329fe
--- /dev/null
+++ b/chrome/browser/views/star_toggle.h
@@ -0,0 +1,92 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_STAR_TOGGLE_H__
+#define CHROME_BROWSER_VIEWS_STAR_TOGGLE_H__
+
+#include "chrome/views/view.h"
+#include "chrome/views/event.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A view subclass to implement the star button. The star button notifies its
+// Delegate when the state changes.
+//
+////////////////////////////////////////////////////////////////////////////////
+class StarToggle : public ChromeViews::View {
+ public:
+ class Delegate {
+ public:
+ // Called when the star is toggled.
+ virtual void StarStateChanged(bool state) = 0;
+ };
+
+ explicit StarToggle(Delegate* delegate);
+ virtual ~StarToggle();
+
+ // Set whether the star is checked.
+ void SetState(bool s);
+ bool GetState() const;
+
+ // If true (the default) the state is immediately changed on a mouse release.
+ // If false, on mouse release the delegate is notified, but the state is not
+ // changed.
+ void set_change_state_immediately(bool value) {
+ change_state_immediately_ = value;
+ }
+
+ // Check/uncheck the star.
+ void SwitchState();
+
+ // Overriden from view.
+ void Paint(ChromeCanvas* canvas);
+ void GetPreferredSize(CSize* out);
+ virtual bool OnMousePressed(const ChromeViews::MouseEvent& e);
+ virtual bool OnMouseDragged(const ChromeViews::MouseEvent& event);
+ virtual void OnMouseReleased(const ChromeViews::MouseEvent& e, bool canceled);
+ bool OnKeyPressed(const ChromeViews::KeyEvent& e);
+
+ private:
+ // The state.
+ bool state_;
+
+ // Our bitmap.
+ SkBitmap* state_off_;
+ SkBitmap* state_on_;
+
+ // Parent to be notified.
+ Delegate* delegate_;
+
+ // See note in setter.
+ bool change_state_immediately_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(StarToggle);
+};
+
+#endif // CHROME_BROWSER_VIEWS_STAR_TOGGLE_H__
diff --git a/chrome/browser/views/status_bubble.cc b/chrome/browser/views/status_bubble.cc
new file mode 100644
index 0000000..96398fa
--- /dev/null
+++ b/chrome/browser/views/status_bubble.cc
@@ -0,0 +1,671 @@
+// 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/views/status_bubble.h"
+
+#include <algorithm>
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/common/animation.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/url_elider.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/hwnd_view_container.h"
+#include "chrome/views/label.h"
+#include "chrome/views/view_container.h"
+#include "net/base/net_util.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRect.h"
+
+#include "generated_resources.h"
+
+// The color of the background bubble.
+static const SkColor kBubbleColor = SkColorSetRGB(222, 234, 248);
+
+// The alpha and color of the bubble's shadow.
+static const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0);
+
+// How wide the bubble's shadow is.
+static const int kShadowSize = 1;
+
+// The roundedness of the edges of our bubble.
+static const int kBubbleCornerRadius = 4;
+
+// How close the mouse can get to the infobubble before it starts sliding
+// off-screen.
+static const int kMousePadding = 20;
+
+// The color of the text
+static const SkColor kTextColor = SkColorSetRGB(100, 100, 100);
+
+// The color of the highlight text
+static const SkColor kTextHighlightColor = SkColorSetRGB(242, 250, 255);
+
+static const int kTextPadding = 3;
+static const int kTextPositionX = 4;
+static const int kTextPositionY = 1;
+
+// Delays before we start hiding or showing the bubble after we receive a
+// show or hide request.
+static const int kShowDelay = 80;
+static const int kHideDelay = 250;
+
+// How long each fade should last for.
+static const int kShowFadeDurationMS = 120;
+static const int kHideFadeDurationMS = 200;
+static const int kFramerate = 25;
+
+// View -----------------------------------------------------------------------
+// StatusView manages the display of the bubble, applying text changes and
+// fading in or out the bubble as required.
+class StatusBubble::StatusView : public ChromeViews::Label,
+ public Animation,
+ public AnimationDelegate {
+ public:
+ StatusView(StatusBubble* status_bubble,
+ ChromeViews::HWNDViewContainer* popup)
+ : Animation(kFramerate, this),
+ status_bubble_(status_bubble),
+ popup_(popup),
+ stage_(BUBBLE_HIDDEN),
+ style_(STYLE_STANDARD),
+ timer_factory_(this),
+ opacity_start_(0),
+ opacity_end_(0) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ ChromeFont font(rb.GetFont(ResourceBundle::BaseFont));
+ SetFont(font);
+ }
+
+ ~StatusView() {
+ Stop();
+ CancelTimer();
+ }
+
+ // The bubble can be in one of many stages:
+ typedef enum BubbleStage {
+ BUBBLE_HIDDEN, // Entirely BUBBLE_HIDDEN.
+ BUBBLE_HIDING_FADE, // In a fade-out transition.
+ BUBBLE_HIDING_TIMER, // Waiting before a fade-out.
+ BUBBLE_SHOWING_TIMER, // Waiting before a fade-in.
+ BUBBLE_SHOWING_FADE, // In a fade-in transition.
+ BUBBLE_SHOWN // Fully visible.
+ };
+
+ typedef enum BubbleStyle {
+ STYLE_BOTTOM,
+ STYLE_FLOATING,
+ STYLE_STANDARD
+ };
+
+ // Set the bubble text to a certain value, hides the bubble if text is
+ // an empty string.
+ void SetText(const std::wstring& text);
+
+ BubbleStage GetState() const { return stage_; }
+
+ void SetStyle(BubbleStyle style);
+
+ // Show the bubble instantly.
+ void Show();
+
+ // Hide the bubble instantly.
+ void Hide();
+
+ // Resets any timers we have. Typically called when the user moves a
+ // mouse.
+ void ResetTimer();
+
+ private:
+ class InitialTimer;
+
+ // Manage the timers that control the delay before a fade begins or ends.
+ void StartTimer(int time);
+ void OnTimer();
+ void CancelTimer();
+ void RestartTimer(int delay);
+
+ // Manage the fades and starting and stopping the animations correctly.
+ void StartFade(double start, double end, int duration);
+ void StartHiding();
+ void StartShowing();
+
+ // Animation functions.
+ double GetCurrentOpacity();
+ void SetOpacity(double opacity);
+ void AnimateToState(double state);
+ void AnimationEnded(const Animation* animation);
+
+ virtual void Paint(ChromeCanvas* canvas);
+
+ BubbleStage stage_;
+ BubbleStyle style_;
+
+ ScopedRunnableMethodFactory<StatusBubble::StatusView> timer_factory_;
+
+ // Manager, owns us.
+ StatusBubble* status_bubble_;
+
+ // Handle to the HWND that contains us.
+ ChromeViews::HWNDViewContainer* popup_;
+
+ // The currently-displayed text.
+ std::wstring text_;
+
+ // Start and end opacities for the current transition - note that as a
+ // fade-in can easily turn into a fade out, opacity_start_ is sometimes
+ // a value between 0 and 1.
+ double opacity_start_;
+ double opacity_end_;
+};
+
+void StatusBubble::StatusView::SetText(const std::wstring& text) {
+ if (text.empty()) {
+ // The string was empty.
+ StartHiding();
+ } else {
+ // We want to show the string.
+ text_ = text;
+ StartShowing();
+ }
+
+ SchedulePaint();
+}
+
+void StatusBubble::StatusView::Show() {
+ Stop();
+ CancelTimer();
+ SetOpacity(1.0);
+ stage_ = BUBBLE_SHOWN;
+ PaintNow();
+}
+
+void StatusBubble::StatusView::Hide() {
+ Stop();
+ CancelTimer();
+ SetOpacity(0.0);
+ text_.clear();
+ stage_ = BUBBLE_HIDDEN;
+}
+
+void StatusBubble::StatusView::StartTimer(int time) {
+ if (!timer_factory_.empty())
+ timer_factory_.RevokeAll();
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ timer_factory_.NewRunnableMethod(&StatusBubble::StatusView::OnTimer),
+ time);
+}
+
+void StatusBubble::StatusView::OnTimer() {
+ if (stage_ == BUBBLE_HIDING_TIMER) {
+ stage_ = BUBBLE_HIDING_FADE;
+ StartFade(1.0, 0.0, kHideFadeDurationMS);
+ } else if (stage_ == BUBBLE_SHOWING_TIMER) {
+ stage_ = BUBBLE_SHOWING_FADE;
+ StartFade(0.0, 1.0, kShowFadeDurationMS);
+ }
+}
+
+void StatusBubble::StatusView::CancelTimer() {
+ if (!timer_factory_.empty()) {
+ timer_factory_.RevokeAll();
+ }
+}
+
+void StatusBubble::StatusView::RestartTimer(int delay) {
+ CancelTimer();
+ StartTimer(delay);
+}
+
+void StatusBubble::StatusView::ResetTimer() {
+ if (stage_ == BUBBLE_SHOWING_TIMER) {
+ // We hadn't yet begun showing anything when we received a new request
+ // for something to show, so we start from scratch.
+ RestartTimer(kShowDelay);
+ }
+}
+
+void StatusBubble::StatusView::StartFade(double start,
+ double end,
+ int duration) {
+ opacity_start_ = start;
+ opacity_end_ = end;
+
+ // This will also reset the currently-occuring animation.
+ SetDuration(duration);
+ Start();
+}
+
+void StatusBubble::StatusView::StartHiding() {
+ if (stage_ == BUBBLE_SHOWN) {
+ stage_ = BUBBLE_HIDING_TIMER;
+ StartTimer(kHideDelay);
+ } else if (stage_ == BUBBLE_SHOWING_TIMER) {
+ stage_ = BUBBLE_HIDDEN;
+ CancelTimer();
+ } else if (stage_ == BUBBLE_SHOWING_FADE) {
+ stage_ = BUBBLE_HIDING_FADE;
+ // Figure out where we are in the current fade.
+ double current_opacity = GetCurrentOpacity();
+
+ // Start a fade in the opposite direction.
+ StartFade(current_opacity, 0.0,
+ static_cast<int>(kHideFadeDurationMS * current_opacity));
+ }
+}
+
+void StatusBubble::StatusView::StartShowing() {
+ if (stage_ == BUBBLE_HIDDEN) {
+ stage_ = BUBBLE_SHOWING_TIMER;
+ StartTimer(kShowDelay);
+ } else if (stage_ == BUBBLE_HIDING_TIMER) {
+ stage_ = BUBBLE_SHOWN;
+ CancelTimer();
+ } else if (stage_ == BUBBLE_HIDING_FADE) {
+ // We're partway through a fade.
+ stage_ = BUBBLE_SHOWING_FADE;
+
+ // Figure out where we are in the current fade.
+ double current_opacity = GetCurrentOpacity();
+
+ // Start a fade in the opposite direction.
+ StartFade(current_opacity, 1.0,
+ static_cast<int>(kShowFadeDurationMS * current_opacity));
+ } else if (stage_ == BUBBLE_SHOWING_TIMER) {
+ // We hadn't yet begun showing anything when we received a new request
+ // for something to show, so we start from scratch.
+ ResetTimer();
+ }
+}
+
+// Animation functions.
+double StatusBubble::StatusView::GetCurrentOpacity() {
+ return opacity_start_ + (opacity_end_ - opacity_start_) *
+ Animation::GetCurrentValue();
+}
+
+void StatusBubble::StatusView::SetOpacity(double opacity) {
+ popup_->SetLayeredAlpha(static_cast<BYTE>(opacity * 255));
+ SchedulePaint();
+}
+
+void StatusBubble::StatusView::AnimateToState(double state) {
+ SetOpacity(GetCurrentOpacity());
+}
+
+void StatusBubble::StatusView::AnimationEnded(
+ const Animation* animation) {
+ SetOpacity(opacity_end_);
+
+ if (stage_ == BUBBLE_HIDING_FADE) {
+ stage_ = BUBBLE_HIDDEN;
+ } else if (stage_ == BUBBLE_SHOWING_FADE) {
+ stage_ = BUBBLE_SHOWN;
+ }
+}
+
+void StatusBubble::StatusView::SetStyle(BubbleStyle style) {
+ if (style_ != style) {
+ style_ = style;
+ SchedulePaint();
+ }
+}
+
+void StatusBubble::StatusView::Paint(ChromeCanvas* canvas) {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+ paint.setColor(kBubbleColor);
+
+ RECT parent_rect;
+ ::GetWindowRect(popup_->GetHWND(), &parent_rect);
+
+ // Draw our background.
+ SkRect rect;
+ int width = parent_rect.right - parent_rect.left;
+ int height = parent_rect.bottom - parent_rect.top;
+
+ // Figure out how to round the bubble's four corners.
+ SkScalar rad[8];
+
+ // Top Edges - if the bubble is in its bottom position (sticking downwards),
+ // then we square the top edges. Otherwise, we square the edges based on the
+ // position of the bubble within the window (the bubble is positioned in the
+ // southeast corner in RTL and in the southwest conver in LTR).
+ if (style_ == STYLE_BOTTOM) {
+ // Top Left corner.
+ rad[0] = 0;
+ rad[1] = 0;
+
+ // Top Right corner.
+ rad[2] = 0;
+ rad[3] = 0;
+ } else {
+ if (UILayoutIsRightToLeft()) {
+ // Top Left corner.
+ rad[0] = SkIntToScalar(kBubbleCornerRadius);
+ rad[1] = SkIntToScalar(kBubbleCornerRadius);
+
+ // Top Right corner.
+ rad[2] = 0;
+ rad[3] = 0;
+ } else {
+ // Top Left corner.
+ rad[0] = 0;
+ rad[1] = 0;
+
+ // Top Right corner.
+ rad[2] = SkIntToScalar(kBubbleCornerRadius);
+ rad[3] = SkIntToScalar(kBubbleCornerRadius);
+ }
+ }
+
+ // Bottom edges - square these off if the bubble is in its standard position
+ // (sticking upward).
+ if (style_ == STYLE_STANDARD) {
+ // Bottom Right Corner.
+ rad[4] = 0;
+ rad[5] = 0;
+
+ // Bottom Left Corner.
+ rad[6] = 0;
+ rad[7] = 0;
+ } else {
+ // Bottom Right Corner.
+ rad[4] = SkIntToScalar(kBubbleCornerRadius);
+ rad[5] = SkIntToScalar(kBubbleCornerRadius);
+
+ // Bottom Left Corner.
+ rad[6] = SkIntToScalar(kBubbleCornerRadius);
+ rad[7] = SkIntToScalar(kBubbleCornerRadius);
+ }
+
+ // Draw the bubble's shadow.
+ SkPaint shadow_paint;
+ shadow_paint.setFlags(SkPaint::kAntiAlias_Flag);
+ shadow_paint.setColor(kShadowColor);
+
+ rect.set(0, 0,
+ SkIntToScalar(width),
+ SkIntToScalar(height));
+
+ SkPath shadow_path;
+ shadow_path.addRoundRect(rect, rad, SkPath::kCW_Direction);
+ canvas->drawPath(shadow_path, shadow_paint);
+
+ // Draw the bubble.
+ SkPath path;
+ rect.set(SkIntToScalar(kShadowSize),
+ SkIntToScalar(kShadowSize),
+ SkIntToScalar(width - kShadowSize),
+ SkIntToScalar(height - kShadowSize));
+
+ path.addRoundRect(rect, rad, SkPath::kCW_Direction);
+ canvas->drawPath(path, paint);
+
+
+ int text_width = std::min(static_cast<int>(parent_rect.right -
+ parent_rect.left - kTextPositionX -
+ kTextPadding),
+ static_cast<int>(ChromeViews::Label::GetFont()
+ .GetStringWidth(text_)));
+
+ // Draw highlight text and then the text body. In order to make sure the text
+ // is aligned to the right on RTL UIs, we mirror the text bounds if the
+ // locale is RTL.
+ gfx::Rect body_bounds(kTextPositionX,
+ kTextPositionY,
+ text_width,
+ parent_rect.bottom - parent_rect.top);
+ body_bounds.set_x(MirroredLeftPointForRect(body_bounds));
+ canvas->DrawStringInt(text_,
+ ChromeViews::Label::GetFont(),
+ kTextHighlightColor,
+ body_bounds.x() + 1,
+ body_bounds.y() + 1,
+ body_bounds.width(),
+ body_bounds.height());
+
+ canvas->DrawStringInt(text_,
+ ChromeViews::Label::GetFont(),
+ kTextColor,
+ body_bounds.x(),
+ body_bounds.y(),
+ body_bounds.width(),
+ body_bounds.height());
+}
+
+// StatusBubble ---------------------------------------------------------------
+
+StatusBubble::StatusBubble(ChromeViews::ViewContainer* frame)
+ : popup_(NULL),
+ frame_(frame),
+ view_(NULL),
+ opacity_(0),
+ position_(0, 0),
+ size_(0, 0),
+ offset_(0) {
+}
+
+StatusBubble::~StatusBubble() {
+ if (popup_) {
+ popup_->CloseNow();
+ }
+
+ popup_ = NULL;
+ position_ = NULL;
+ size_ = NULL;
+}
+
+void StatusBubble::Init() {
+ if (!popup_) {
+ popup_ = new ChromeViews::HWNDViewContainer();
+ popup_->set_delete_on_destroy(false);
+
+ if (!view_) {
+ view_ = new StatusView(this, popup_);
+ }
+
+ gfx::Rect rc(0, 0, 0, 0);
+
+ popup_->set_window_style(WS_POPUP);
+ popup_->set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW |
+ WS_EX_TRANSPARENT |
+ l10n_util::GetExtendedTooltipStyles());
+ popup_->SetLayeredAlpha(0x00);
+ popup_->Init(frame_->GetHWND(), rc, view_,
+ false);
+ Reposition();
+ popup_->ShowWindow(SW_SHOWNOACTIVATE);
+ }
+}
+
+void StatusBubble::SetStatus(const std::wstring& status_text) {
+ if (status_text_ == status_text)
+ return;
+
+ Init();
+ status_text_ = status_text;
+ if (!status_text_.empty()) {
+ view_->SetText(status_text);
+ view_->Show();
+ } else if (!url_text_.empty()) {
+ view_->SetText(url_text_);
+ } else {
+ view_->SetText(std::wstring());
+ }
+}
+
+void StatusBubble::SetURL(const GURL& url, const std::wstring& languages) {
+ Init();
+
+ // If we want to clear a displayed URL but there is a status still to
+ // display, display that status instead.
+ if (url.is_empty() && !status_text_.empty()) {
+ url_text_ = std::wstring();
+ view_->SetText(status_text_);
+ return;
+ }
+
+ // Set Elided Text correspoding to the GURL object.
+ RECT parent_rect;
+ ::GetWindowRect(popup_->GetHWND(), &parent_rect);
+ int text_width = static_cast<int>(parent_rect.right -
+ parent_rect.left - kTextPositionX -
+ kTextPadding);
+ url_text_ = gfx::ElideUrl(url, view_->Label::GetFont(), text_width,
+ languages);
+
+ // An URL is always treated as a left-to-right string. On right-to-left UIs
+ // we need to explicitly mark the URL as LTR to make sure it is displayed
+ // correctly.
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT &&
+ !url_text_.empty())
+ l10n_util::WrapStringWithLTRFormatting(&url_text_);
+ view_->SetText(url_text_);
+}
+
+void StatusBubble::ClearURL() {
+ Init();
+ url_text_ = std::wstring();
+ view_->SetText(url_text_);
+}
+
+void StatusBubble::Hide() {
+ status_text_ = std::wstring();
+ url_text_ = std::wstring();
+ if (view_) {
+ view_->Hide();
+ }
+}
+
+void StatusBubble::MouseMoved() {
+ if (view_) {
+ view_->ResetTimer();
+
+ if (view_->GetState() != StatusView::BUBBLE_HIDDEN &&
+ view_->GetState() != StatusView::BUBBLE_HIDING_FADE &&
+ view_->GetState() != StatusView::BUBBLE_HIDING_TIMER) {
+ AvoidMouse();
+ }
+ }
+}
+
+void StatusBubble::AvoidMouse() {
+ // Our status bubble is located in screen coordinates, so we should get
+ // those rather than attempting to reverse decode the web contents
+ // coordinates.
+ CPoint cursor_location;
+ GetCursorPos(&cursor_location);
+
+ // Get the position of the frame.
+ CPoint top_left(0, 0);
+ ChromeViews::View::ConvertPointToScreen(frame_->GetRootView(), &top_left);
+
+ // Get the cursor position relative to the popup.
+ cursor_location.x -= (top_left.x + position_.x);
+ cursor_location.y -= (top_left.y + position_.y);
+
+ // If the mouse is in a position where we think it would move the
+ // status bubble, figure out where and how the bubble should be moved.
+ if (cursor_location.y > -kMousePadding &&
+ cursor_location.x < size_.cx + kMousePadding) {
+ int offset = kMousePadding + cursor_location.y;
+
+ // Make the movement non-linear.
+ offset = offset * offset / kMousePadding;
+
+ // When the mouse is entering from the right, we want the offset to be
+ // scaled by how horizontally far away the cursor is from the bubble.
+ if (cursor_location.x > size_.cx) {
+ offset = static_cast<int>(static_cast<float>(offset) * (
+ static_cast<float>(kMousePadding -
+ (cursor_location.x - size_.cx)) /
+ static_cast<float>(kMousePadding)));
+ }
+
+ // Cap the offset and change the visual presentation of the bubble
+ // depending on where it ends up (so that rounded corners square off
+ // and mate to the edges of the tab content).
+ if (offset >= size_.cy - kShadowSize * 2) {
+ offset = size_.cy - kShadowSize * 2;
+ view_->SetStyle(StatusView::STYLE_BOTTOM);
+ } else if (offset > kBubbleCornerRadius / 2 - kShadowSize) {
+ view_->SetStyle(StatusView::STYLE_FLOATING);
+ } else {
+ view_->SetStyle(StatusView::STYLE_STANDARD);
+ }
+
+ offset_ = offset;
+ popup_->MoveWindow(top_left.x + position_.x,
+ top_left.y + position_.y + offset_,
+ size_.cx,
+ size_.cy);
+ } else if (offset_ != 0) {
+ offset_ = 0;
+ view_->SetStyle(StatusView::STYLE_STANDARD);
+ popup_->MoveWindow(top_left.x + position_.x,
+ top_left.y + position_.y,
+ size_.cx,
+ size_.cy);
+ }
+}
+
+void StatusBubble::Reposition() {
+ if (popup_) {
+ CPoint top_left(0, 0);
+ ChromeViews::View::ConvertPointToScreen(frame_->GetRootView(), &top_left);
+
+ popup_->MoveWindow(top_left.x + position_.x,
+ top_left.y + position_.y,
+ size_.cx,
+ size_.cy);
+ }
+}
+
+void StatusBubble::SetBounds(int x, int y, int w, int h) {
+ // If the UI layout is RTL, we need to mirror the position of the bubble
+ // relative to the parent.
+ if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
+ CRect frame_bounds;
+ frame_->GetBounds(&frame_bounds, false);
+ int mirrored_x = frame_bounds.Width() - x - w;
+ position_.SetPoint(mirrored_x, y);
+ } else {
+ position_.SetPoint(x, y);
+ }
+
+ size_.SetSize(w, h);
+ Reposition();
+}
diff --git a/chrome/browser/views/status_bubble.h b/chrome/browser/views/status_bubble.h
new file mode 100644
index 0000000..ee9edc6
--- /dev/null
+++ b/chrome/browser/views/status_bubble.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_STATUS_BUBBLE_H__
+#define CHROME_BROWSER_VIEWS_STATUS_BUBBLE_H__
+
+#include "base/gfx/rect.h"
+#include "chrome/views/hwnd_view_container.h"
+#include "chrome/views/view_container.h"
+#include "googleurl/src/gurl.h"
+
+// StatusBubble displays a bubble of text that fades in, hovers over the
+// browser chrome and fades away when not needed. It is primarily designed
+// to allow users to see where hovered links point to.
+class StatusBubble {
+ public:
+ explicit StatusBubble(ChromeViews::ViewContainer* frame);
+ ~StatusBubble();
+
+ // Sets the bubble contents to a specific string and causes the bubble
+ // to display immediately. Subsequent empty SetURL calls (typically called
+ // when the cursor exits a link) will set the status bubble back to its
+ // status text. To hide the status bubble again, either call SetStatus
+ // with an empty string, or call Hide().
+ void SetStatus(const std::wstring& status);
+
+ // Sets the bubble text to a URL - if given a non-empty URL, this will cause
+ // the bubble to fade in and remain open until given an empty URL or until
+ // the Hide() method is called. languages is the value of Accept-Language
+ // to determine what characters are understood by a user.
+ void SetURL(const GURL& url, const std::wstring& languages);
+
+ // Clear the URL and begin the fadeout of the bubble if not status text
+ // needs to be displayed.
+ void ClearURL();
+
+ // Skip the fade and instant-hide the bubble.
+ void Hide();
+
+ // Called when the user's mouse has moved over web content.
+ void MouseMoved();
+
+ // Set the bounds of the bubble relative to the browser window.
+ void SetBounds(int x, int y, int w, int h);
+
+ // Reposition the bubble - as we are using a WS_POPUP for the bubble,
+ // we have to manually position it when the browser window moves.
+ void Reposition();
+
+ private:
+ class StatusView;
+
+ // Initializes the popup and view.
+ void Init();
+
+ // Attempt to move the status bubble out of the way of the cursor, allowing
+ // users to see links in the region normally occupied by the status bubble.
+ void AvoidMouse();
+
+ // The status text we want to display when there are no URLs to display.
+ std::wstring status_text_;
+
+ // The url we want to display when there is not status text to display.
+ std::wstring url_text_;
+
+ // Position relative to the parent window.
+ CPoint position_;
+ CSize size_;
+
+ // How vertically offset the bubble is from its root position_.
+ int offset_;
+
+ // We use a HWND for the popup so that it may float above any HWNDs in our
+ // UI (the location bar, for example).
+ ChromeViews::HWNDViewContainer* popup_;
+ double opacity_;
+
+ ChromeViews::ViewContainer* frame_;
+ StatusView* view_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(StatusBubble);
+};
+
+#endif // CHROME_BROWSER_VIEWS_STATUS_BUBBLE_H__
diff --git a/chrome/browser/views/tab_icon_view.cc b/chrome/browser/views/tab_icon_view.cc
new file mode 100644
index 0000000..a6ac4c7
--- /dev/null
+++ b/chrome/browser/views/tab_icon_view.cc
@@ -0,0 +1,141 @@
+// 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/app/theme/theme_resources.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/favicon_size.h"
+#include "chrome/common/gfx/icon_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/views/tab_icon_view.h"
+#include "chrome/app/chrome_dll_resource.h"
+
+static bool g_initialized = false;
+static SkBitmap* g_default_fav_icon = NULL;
+static SkBitmap* g_throbber_frames = NULL;
+static SkBitmap* g_throbber_frames_light = NULL;
+static int g_throbber_frame_count;
+
+// static
+void TabIconView::InitializeIfNeeded() {
+ if (!g_initialized) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ g_default_fav_icon = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
+ g_throbber_frames = rb.GetBitmapNamed(IDR_THROBBER);
+ g_throbber_frames_light = rb.GetBitmapNamed(IDR_THROBBER_LIGHT);
+ g_throbber_frame_count = g_throbber_frames->width() /
+ g_throbber_frames->height();
+
+ // Verify that our light and dark styles have the same number of frames.
+ DCHECK(g_throbber_frame_count ==
+ g_throbber_frames_light->width() / g_throbber_frames_light->height());
+ g_initialized = true;
+ }
+}
+
+TabIconView::TabIconView(TabContentsProvider* provider)
+ : provider_(provider),
+ is_light_(false),
+ throbber_running_(false),
+ throbber_frame_(0) {
+ InitializeIfNeeded();
+}
+
+TabIconView::~TabIconView() {
+}
+
+void TabIconView::Update() {
+ TabContents* contents = provider_->GetCurrentTabContents();
+ if (throbber_running_) {
+ // We think the tab is loading.
+ if (!contents || !contents->is_loading()) {
+ // Woops, tab is invalid or not loading, reset our status and schedule
+ // a paint.
+ throbber_running_ = false;
+ SchedulePaint();
+ } else {
+ // The tab is still loading, increment the frame.
+ throbber_frame_ = (throbber_frame_ + 1) % g_throbber_frame_count;
+ SchedulePaint();
+ }
+ } else if (contents && contents->is_loading()) {
+ // We didn't think we were loading, but the tab is loading. Reset the
+ // frame and status and schedule a paint.
+ throbber_running_ = true;
+ throbber_frame_ = 0;
+ SchedulePaint();
+ }
+}
+
+void TabIconView::PaintThrobber(ChromeCanvas* canvas) {
+ int image_size = g_throbber_frames->height();
+ int image_offset = throbber_frame_ * image_size;
+ canvas->DrawBitmapInt(is_light_ ? *g_throbber_frames_light :
+ *g_throbber_frames,
+ image_offset, 0, image_size, image_size,
+ 0, 0, image_size, image_size, false);
+}
+
+void TabIconView::PaintFavIcon(ChromeCanvas* canvas, const SkBitmap& bitmap) {
+ int bw = bitmap.width();
+ int bh = bitmap.height();
+ if (bw <= kFavIconSize && bh <= kFavIconSize) {
+ canvas->DrawBitmapInt(bitmap, (GetWidth() - kFavIconSize) / 2,
+ (GetHeight() - kFavIconSize) / 2);
+ } else {
+ canvas->DrawBitmapInt(bitmap, 0, 0, bw, bh, 0, 0, GetWidth(), GetHeight(),
+ true);
+ }
+}
+
+void TabIconView::Paint(ChromeCanvas* canvas) {
+ TabContents* contents = provider_->GetCurrentTabContents();
+ bool rendered = false;
+
+ if (contents) {
+ if (throbber_running_) {
+ rendered = true;
+ PaintThrobber(canvas);
+ } else {
+ SkBitmap favicon = provider_->GetFavIcon();
+ if (!favicon.isNull()) {
+ rendered = true;
+ PaintFavIcon(canvas, favicon);
+ }
+ }
+ }
+
+ if (!rendered) {
+ PaintFavIcon(canvas, *g_default_fav_icon);
+ }
+}
+
+void TabIconView::GetPreferredSize(CSize* out) {
+ out->cx = out->cy = kFavIconSize;
+}
diff --git a/chrome/browser/views/tab_icon_view.h b/chrome/browser/views/tab_icon_view.h
new file mode 100644
index 0000000..318d629
--- /dev/null
+++ b/chrome/browser/views/tab_icon_view.h
@@ -0,0 +1,89 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEW_TAB_ICON_VIEW_H__
+#define CHROME_BROWSER_VIEW_TAB_ICON_VIEW_H__
+
+#include "chrome/views/view.h"
+
+class TabContents;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A view to display a tab fav icon or a throbber.
+//
+////////////////////////////////////////////////////////////////////////////////
+class TabIconView : public ChromeViews::View {
+ public:
+ class TabContentsProvider {
+ public:
+ // Should return the current tab contents this TabIconView object is
+ // representing.
+ virtual TabContents* GetCurrentTabContents() = 0;
+
+ // Returns the favicon to display in the icon view
+ virtual SkBitmap GetFavIcon() = 0;
+ };
+
+ static void InitializeIfNeeded();
+
+ explicit TabIconView(TabContentsProvider* provider);
+ virtual ~TabIconView();
+
+ // Invoke whenever the tab state changes or the throbber should update.
+ void Update();
+
+ // Set the throbber to the light style (for use on dark backgrounds).
+ void set_is_light(bool is_light) { is_light_ = is_light; }
+
+ // Overriden from View
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void GetPreferredSize(CSize* out);
+
+ private:
+ void PaintThrobber(ChromeCanvas* canvas);
+ void PaintFavIcon(ChromeCanvas* canvas, const SkBitmap& bitmap);
+
+ // Our provider of current tab contents.
+ TabContentsProvider* provider_;
+
+ // Whether the throbber is running.
+ bool throbber_running_;
+
+ // Whether we should display our light or dark style.
+ bool is_light_;
+
+ // Current frame of the throbber being painted. This is only used if
+ // throbber_running_ is true.
+ int throbber_frame_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TabIconView);
+};
+
+#endif // CHROME_BROWSER_VIEW_TAB_ICON_VIEW_H__
diff --git a/chrome/browser/views/theme_helpers.cc b/chrome/browser/views/theme_helpers.cc
new file mode 100644
index 0000000..fa2820d
--- /dev/null
+++ b/chrome/browser/views/theme_helpers.cc
@@ -0,0 +1,115 @@
+// 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/views/theme_helpers.h"
+
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atltheme.h>
+
+#include "base/gfx/bitmap_platform_device.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "base/logging.h"
+#include "SkGradientShader.h"
+
+void GetRebarGradientColors(int width, int x1, int x2, SkColor* c1, SkColor* c2) {
+ DCHECK(c1 && c2) << "ThemeHelpers::GetRebarGradientColors - c1 or c2 is NULL!";
+
+ // To get the colors we need, we draw a horizontal gradient using
+ // DrawThemeBackground, then extract the pixel values from and return
+ // those so calling code can use them to create gradient brushes for use in
+ // rendering in other directions.
+
+ ChromeCanvas canvas(width, 1, true);
+
+ // Render the Rebar gradient into the DIB
+ CTheme theme;
+ if (theme.IsThemingSupported())
+ theme.OpenThemeData(NULL, L"REBAR");
+ // On Windows XP+, if using a Theme, we can ask the theme to render the
+ // gradient for us.
+ if (!theme.IsThemeNull()) {
+ HDC dc = canvas.beginPlatformPaint();
+ RECT rect = { 0, 0, width, 1 };
+ theme.DrawThemeBackground(dc, 0, 0, &rect, NULL);
+ canvas.endPlatformPaint();
+ } else {
+ // On Windows 2000 or Windows XP+ with the Classic theme selected, we need
+ // to build our own gradient using system colors.
+ SkColor grad_colors[2];
+ COLORREF hl_ref = ::GetSysColor(COLOR_3DHILIGHT);
+ grad_colors[0] = SkColorSetRGB(GetRValue(hl_ref), GetGValue(hl_ref),
+ GetBValue(hl_ref));
+ COLORREF face_ref = ::GetSysColor(COLOR_3DFACE);
+ grad_colors[1] = SkColorSetRGB(GetRValue(face_ref), GetGValue(face_ref),
+ GetBValue(face_ref));
+ SkPoint grad_points[2];
+ grad_points[0].set(SkIntToScalar(0), SkIntToScalar(0));
+ grad_points[1].set(SkIntToScalar(width), SkIntToScalar(0));
+ SkShader* gradient_shader = SkGradientShader::CreateLinear(
+ grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode);
+ SkPaint paint;
+ paint.setShader(gradient_shader);
+ // Shader created with a ref count of 1, release as the paint now owns
+ // the gradient.
+ gradient_shader->unref();
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas.drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
+ SkIntToScalar(width), SkIntToScalar(1), paint);
+ }
+
+ // Extract the color values from the selected pixels
+ // The | in the following operations forces the alpha to 0xFF. This is
+ // needed as windows sets the alpha to 0 when it renders.
+ gfx::BitmapPlatformDevice& device =
+ static_cast<gfx::BitmapPlatformDevice&>(canvas.getTopPlatformDevice());
+ *c1 = 0xFF000000 | device.getColorAt(x1, 0);
+ *c2 = 0xFF000000 | device.getColorAt(x2, 0);
+}
+
+void GetDarkLineColor(SkColor* dark_color) {
+ DCHECK(dark_color) << "ThemeHelpers::DarkColor - dark_color is NULL!";
+
+ CTheme theme;
+ if (theme.IsThemingSupported())
+ theme.OpenThemeData(NULL, L"REBAR");
+
+ // Note: the alpha values were chosen scientifically according to what looked
+ // best to me at the time! --beng
+ if (!theme.IsThemeNull()) {
+ *dark_color = SkColorSetARGB(60, 0, 0, 0);
+ } else {
+ COLORREF shadow_ref = ::GetSysColor(COLOR_3DSHADOW);
+ *dark_color = SkColorSetARGB(175,
+ GetRValue(shadow_ref),
+ GetGValue(shadow_ref),
+ GetBValue(shadow_ref));
+ }
+}
+
diff --git a/chrome/browser/views/theme_helpers.h b/chrome/browser/views/theme_helpers.h
new file mode 100644
index 0000000..9704b28
--- /dev/null
+++ b/chrome/browser/views/theme_helpers.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_THEME_HELPERS_H__
+#define CHROME_BROWSER_VIEWS_THEME_HELPERS_H__
+
+#include <windows.h>
+
+#include "SkColor.h"
+
+// Get the colors at two points on a Rebar background gradient. This is for
+// drawing Rebar like backgrounds in Views. The reason not to just use
+// DrawThemeBackground is that it only draws horizontally, but by extracting
+// the colors at two points on the X axis of a background drawn
+// by DrawThemeBackground, we can construct a LinearGradientBrush and draw
+// such a gradient in any direction.
+//
+// The width parameter is the width of horizontal gradient that will be
+// created to calculate the two colors. x1 and x2 are the two pixel positions
+// along the X axis.
+void GetRebarGradientColors(int width, int x1, int x2, SkColor* c1, SkColor* c2);
+
+
+// Gets the color used to draw dark (inset beveled) lines.
+void GetDarkLineColor(SkColor* dark_color);
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_THEME_HELPERS_H__
diff --git a/chrome/browser/views/toolbar_star_toggle.cc b/chrome/browser/views/toolbar_star_toggle.cc
new file mode 100644
index 0000000..36a4bb2
--- /dev/null
+++ b/chrome/browser/views/toolbar_star_toggle.cc
@@ -0,0 +1,116 @@
+// 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/views/toolbar_star_toggle.h"
+
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/bookmark_bar_model.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/chrome_frame.h"
+#include "chrome/browser/views/bookmark_bubble_view.h"
+#include "chrome/browser/views/toolbar_view.h"
+#include "chrome/common/resource_bundle.h"
+#include "googleurl/src/gurl.h"
+
+// The amount of time (in milliseconds) between when the bubble closes and when
+// pressing on the button again does something. Yes, this is a hackish. I tried
+// many different options, all to no avail:
+// . Keying off mouse activation: this didn't work as there is no way to know
+// which window receives the activation. Additionally once the mouse
+// activation occurs we have no way to tie the next mouse event to the mouse
+// activation.
+// . Watching all events as we dispatch them in the MessageLoop. Mouse
+// activation isn't an observable event though.
+// Ideally we could use mouse capture for this, but we can't use mouse capture
+// with the bubble because it has other native windows.
+static const int64 kDisallowClickMS = 40;
+
+ToolbarStarToggle::ToolbarStarToggle(BrowserToolbarView* host)
+ : host_(host),
+ ignore_click_(false),
+ is_bubble_showing_(false) {
+}
+
+void ToolbarStarToggle::ShowStarBubble(const GURL& url, bool newly_bookmarked) {
+ if (is_bubble_showing_) {
+ // Don't show if we're already showing the bubble.
+ return;
+ }
+
+ CPoint star_location(0, 0);
+ ChromeViews::View::ConvertPointToScreen(this, &star_location);
+ // Shift the x location by 1 as visually the center of the star appears 1
+ // pixel to the right. By doing this bubble arrow points to the center
+ // of the star.
+ gfx::Rect star_bounds(star_location.x + 1, star_location.y, GetWidth(),
+ GetHeight());
+ BookmarkBubbleView::Show(host_->browser()->GetTopLevelHWND(), star_bounds,
+ this, host_->profile(), url, newly_bookmarked);
+ is_bubble_showing_ = true;
+}
+
+bool ToolbarStarToggle::OnMousePressed(const ChromeViews::MouseEvent& e) {
+ ignore_click_ = ((TimeTicks::Now() - bubble_closed_time_).InMilliseconds() <
+ kDisallowClickMS);
+ return ToggleButton::OnMousePressed(e);
+}
+
+void ToolbarStarToggle::OnMouseReleased(const ChromeViews::MouseEvent& e,
+ bool canceled) {
+ ToggleButton::OnMouseReleased(e, canceled);
+ ignore_click_ = false;
+}
+
+void ToolbarStarToggle::OnDragDone() {
+ ToggleButton::OnDragDone();
+ ignore_click_ = false;
+}
+
+void ToolbarStarToggle::NotifyClick(int mouse_event_flags) {
+ if (!ignore_click_ && !is_bubble_showing_)
+ ToggleButton::NotifyClick(mouse_event_flags);
+}
+
+SkBitmap ToolbarStarToggle::GetImageToPaint() {
+ if (is_bubble_showing_) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ return *rb.GetBitmapNamed(IDR_STARRED_P);
+ }
+ return Button::GetImageToPaint();
+}
+
+void ToolbarStarToggle::InfoBubbleClosing(InfoBubble* info_bubble) {
+ is_bubble_showing_ = false;
+ SchedulePaint();
+ bubble_closed_time_ = TimeTicks::Now();
+}
+
+bool ToolbarStarToggle::CloseOnEscape() {
+ return true;
+}
diff --git a/chrome/browser/views/toolbar_star_toggle.h b/chrome/browser/views/toolbar_star_toggle.h
new file mode 100644
index 0000000..f76b5fa
--- /dev/null
+++ b/chrome/browser/views/toolbar_star_toggle.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_TOOLBAR_STAR_TOGGLE_H_
+#define CHROME_BROWSER_VIEWS_TOOLBAR_STAR_TOGGLE_H_
+
+#include "base/time.h"
+#include "chrome/browser/views/info_bubble.h"
+#include "chrome/views/button.h"
+
+class BrowserToolbarView;
+class GURL;
+
+// ToolbarStarToggle is used for the star button on the toolbar, allowing the
+// user to star the current page. ToolbarStarToggle manages showing the
+// InfoBubble and rendering the appropriate state while the bubble is visible.
+
+class ToolbarStarToggle : public ChromeViews::ToggleButton,
+ public InfoBubbleDelegate {
+ public:
+ explicit ToolbarStarToggle(BrowserToolbarView* host);
+
+ // If the bubble isn't showing, shows it.
+ void ShowStarBubble(const GURL& url, bool newly_bookmarked);
+
+ bool is_bubble_showing() const { return is_bubble_showing_; }
+
+ // Overridden to update ignore_click_ based on whether the mouse was clicked
+ // quickly after the bubble was hidden.
+ virtual bool OnMousePressed(const ChromeViews::MouseEvent& e);
+
+ // Overridden to set ignore_click_ to false.
+ virtual void OnMouseReleased(const ChromeViews::MouseEvent& e,
+ bool canceled);
+ virtual void OnDragDone();
+
+ // Only invokes super if ignore_click_ is true and the bubble isn't showing.
+ virtual void NotifyClick(int mouse_event_flags);
+
+ protected:
+ // Overridden to so that we appear pressed while the bubble is showing.
+ virtual SkBitmap GetImageToPaint();
+
+ private:
+ // InfoBubbleDelegate.
+ virtual void InfoBubbleClosing(InfoBubble* info_bubble);
+ virtual bool CloseOnEscape();
+
+ // Contains us.
+ BrowserToolbarView* host_;
+
+ // Time the bubble last closed.
+ TimeTicks bubble_closed_time_;
+
+ // If true NotifyClick does nothing. This is set in OnMousePressed based on
+ // the amount of time between when the bubble clicked and now.
+ bool ignore_click_;
+
+ // Is the bubble showing?
+ bool is_bubble_showing_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ToolbarStarToggle);
+};
+
+#endif // CHROME_BROWSER_VIEWS_TOOLBAR_STAR_TOGGLE_H_
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
new file mode 100644
index 0000000..7b673a3
--- /dev/null
+++ b/chrome/browser/views/toolbar_view.cc
@@ -0,0 +1,675 @@
+// 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/views/toolbar_view.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/character_encoding.h"
+#include "chrome/browser/chrome_frame.h"
+#include "chrome/browser/drag_utils.h"
+#include "chrome/browser/navigation_controller.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/browser/views/dom_view.h"
+#include "chrome/browser/views/go_button.h"
+#include "chrome/browser/views/location_bar_view.h"
+#include "chrome/browser/views/theme_helpers.h"
+#include "chrome/browser/views/toolbar_star_toggle.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/button_dropdown.h"
+#include "chrome/views/hwnd_view.h"
+#include "chrome/views/view_container.h"
+#include "chrome/views/background.h"
+#include "chrome/views/label.h"
+#include "net/base/net_util.h"
+
+#include "generated_resources.h"
+
+static const int kControlHorizOffset = 4;
+static const int kControlVertOffset = 6;
+static const int kControlIndent = 3;
+static const int kStatusBubbleWidth = 480;
+
+// Separation between the location bar and the menus.
+static const int kMenuButtonOffset = 3;
+
+// Padding to the right of the location bar
+static const int kPaddingRight = 2;
+
+BrowserToolbarView::BrowserToolbarView(CommandController* controller,
+ Browser* browser)
+ : EncodingMenuControllerDelegate(browser, controller),
+ controller_(controller),
+ model_(browser->toolbar_model()),
+ back_(NULL),
+ forward_(NULL),
+ reload_(NULL),
+ home_(NULL),
+ star_(NULL),
+ location_bar_(NULL),
+ go_(NULL),
+ profile_(NULL),
+ acc_focused_view_(NULL),
+ browser_(browser),
+ tab_(NULL) {
+ back_menu_model_.reset(new BackForwardMenuModel(
+ browser, BackForwardMenuModel::BACKWARD_MENU_DELEGATE));
+ forward_menu_model_.reset(new BackForwardMenuModel(
+ browser, BackForwardMenuModel::FORWARD_MENU_DELEGATE));
+}
+
+BrowserToolbarView::~BrowserToolbarView() {
+}
+
+void BrowserToolbarView::Init(Profile* profile) {
+ // Create all the individual Views in the Toolbar.
+ CreateLeftSideControls();
+ CreateCenterStack(profile);
+ CreateRightSideControls(profile);
+
+ show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this);
+
+ SetProfile(profile);
+}
+
+void BrowserToolbarView::SetProfile(Profile* profile) {
+ if (profile == profile_)
+ return;
+
+ profile_ = profile;
+ location_bar_->SetProfile(profile);
+}
+
+void BrowserToolbarView::CreateLeftSideControls() {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+
+ back_ = new ChromeViews::ButtonDropDown(back_menu_model_.get());
+ back_->SetImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_BACK));
+ back_->SetImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_BACK_H));
+ back_->SetImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_BACK_P));
+ back_->SetImage(ChromeViews::Button::BS_DISABLED,
+ rb.GetBitmapNamed(IDR_BACK_D));
+ back_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_BACK));
+ back_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_BACK));
+ back_->SetID(VIEW_ID_BACK_BUTTON);
+ AddChildView(back_);
+ controller_->AddManagedButton(back_, IDC_BACK);
+
+ forward_ = new ChromeViews::ButtonDropDown(forward_menu_model_.get());
+ forward_->SetImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_FORWARD));
+ forward_->SetImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_FORWARD_H));
+ forward_->SetImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_FORWARD_P));
+ forward_->SetImage(ChromeViews::Button::BS_DISABLED,
+ rb.GetBitmapNamed(IDR_FORWARD_D));
+ forward_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_FORWARD));
+ forward_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_FORWARD));
+ forward_->SetID(VIEW_ID_FORWARD_BUTTON);
+ AddChildView(forward_);
+ controller_->AddManagedButton(forward_, IDC_FORWARD);
+
+ reload_ = new ChromeViews::Button();
+ reload_->SetImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_RELOAD));
+ reload_->SetImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_RELOAD_H));
+ reload_->SetImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_RELOAD_P));
+ reload_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_RELOAD));
+ reload_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_RELOAD));
+ reload_->SetID(VIEW_ID_RELOAD_BUTTON);
+ AddChildView(reload_);
+ controller_->AddManagedButton(reload_, IDC_RELOAD);
+
+ home_ = new ChromeViews::Button();
+ home_->SetImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_HOME));
+ home_->SetImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_HOME_H));
+ home_->SetImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_HOME_P));
+ home_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_HOME));
+ home_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_HOME));
+ AddChildView(home_);
+ controller_->AddManagedButton(home_, IDC_HOME);
+}
+
+void BrowserToolbarView::CreateCenterStack(Profile *profile) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+
+ star_ = new ToolbarStarToggle(this);
+ star_->SetImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_STAR));
+ star_->SetImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_STAR_H));
+ star_->SetImage(ChromeViews::Button::BS_PUSHED,
+ rb .GetBitmapNamed(IDR_STAR_P));
+ star_->SetImage(ChromeViews::Button::BS_DISABLED,
+ rb .GetBitmapNamed(IDR_STAR_D));
+ star_->SetToggledImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_STARRED));
+ star_->SetToggledImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_STARRED_H));
+ star_->SetToggledImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_STARRED_P));
+ star_->SetDragController(this);
+ star_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_STAR));
+ star_->SetToggledTooltipText(l10n_util::GetString(IDS_TOOLTIP_STARRED));
+ star_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_STAR));
+ star_->SetID(VIEW_ID_STAR_BUTTON);
+ AddChildView(star_);
+ controller_->AddManagedButton(star_, IDC_STAR);
+
+ location_bar_ = new LocationBarView(profile, controller_, model_,
+ this, false);
+ AddChildView(location_bar_);
+ location_bar_->Init();
+
+ // The Go button.
+ go_ = new GoButton(location_bar_, controller_);
+ go_->SetImage(ChromeViews::Button::BS_NORMAL, rb.GetBitmapNamed(IDR_GO));
+ go_->SetImage(ChromeViews::Button::BS_HOT, rb.GetBitmapNamed(IDR_GO_H));
+ go_->SetImage(ChromeViews::Button::BS_PUSHED, rb.GetBitmapNamed(IDR_GO_P));
+ go_->SetToggledImage(ChromeViews::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_STOP));
+ go_->SetToggledImage(ChromeViews::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_STOP_H));
+ go_->SetToggledImage(ChromeViews::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_STOP_P));
+ go_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_GO));
+ go_->SetID(VIEW_ID_GO_BUTTON);
+ AddChildView(go_);
+}
+
+void BrowserToolbarView::Update(TabContents* tab, bool should_restore_state) {
+ tab_ = tab;
+
+ if (!location_bar_)
+ return;
+
+ location_bar_->Update(should_restore_state ? tab : NULL);
+}
+
+void BrowserToolbarView::OnInputInProgress(bool in_progress) {
+ // The edit should make sure we're only notified when something changes.
+ DCHECK(model_->input_in_progress() != in_progress);
+
+ model_->set_input_in_progress(in_progress);
+ location_bar_->Update(NULL);
+}
+
+void BrowserToolbarView::CreateRightSideControls(Profile* profile) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+
+ page_menu_ =
+ new ChromeViews::MenuButton(std::wstring(), this, false);
+
+ // We use different menu button images if the locale is right-to-left.
+ if (UILayoutIsRightToLeft())
+ page_menu_->SetIcon(*rb.GetBitmapNamed(IDR_MENU_PAGE_RTL));
+ else
+ page_menu_->SetIcon(*rb.GetBitmapNamed(IDR_MENU_PAGE));
+
+ page_menu_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_PAGE));
+ page_menu_->SetTooltipText(l10n_util::GetString(IDS_PAGEMENU_TOOLTIP));
+ page_menu_->SetID(VIEW_ID_PAGE_MENU);
+ AddChildView(page_menu_);
+
+ app_menu_ = new ChromeViews::MenuButton(std::wstring(), this, false);
+ if (UILayoutIsRightToLeft())
+ app_menu_->SetIcon(*rb.GetBitmapNamed(IDR_MENU_CHROME_RTL));
+ else
+ app_menu_->SetIcon(*rb.GetBitmapNamed(IDR_MENU_CHROME));
+
+ app_menu_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_APP));
+ app_menu_->SetTooltipText(l10n_util::GetStringF(IDS_APPMENU_TOOLTIP,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ app_menu_->SetID(VIEW_ID_APP_MENU);
+ AddChildView(app_menu_);
+}
+
+void BrowserToolbarView::Layout() {
+ CSize sz;
+
+ // If we have not been initialized yet just do nothing.
+ if (back_ == NULL)
+ return;
+
+ back_->GetPreferredSize(&sz);
+ back_->SetBounds(kControlIndent,
+ kControlVertOffset,
+ sz.cx,
+ sz.cy);
+
+ forward_->GetPreferredSize(&sz);
+ forward_->SetBounds(back_->GetX() + back_->GetWidth(),
+ kControlVertOffset,
+ sz.cx,
+ sz.cy);
+
+ reload_->GetPreferredSize(&sz);
+ reload_->SetBounds(forward_->GetX() + forward_->GetWidth() +
+ kControlHorizOffset,
+ kControlVertOffset,
+ sz.cx,
+ sz.cy);
+
+ int offset = 0;
+ if (show_home_button_.GetValue()) {
+ home_->GetPreferredSize(&sz);
+ offset = kControlHorizOffset;
+ } else {
+ sz = CSize(0, 0);
+ }
+ home_->SetBounds(reload_->GetX() + reload_->GetWidth() + offset,
+ kControlVertOffset, sz.cx, sz.cy);
+
+ star_->GetPreferredSize(&sz);
+ star_->SetBounds(home_->GetX() + home_->GetWidth() + kControlHorizOffset,
+ kControlVertOffset, sz.cx, sz.cy);
+
+ page_menu_->GetPreferredSize(&sz);
+ int right_side_width = sz.cx + kMenuButtonOffset;
+
+ app_menu_->GetPreferredSize(&sz);
+ right_side_width += sz.cx + kPaddingRight;
+
+ go_->GetPreferredSize(&sz);
+ int go_button_height = sz.cy;
+
+ location_bar_->SetBounds(star_->GetX() + star_->GetWidth(),
+ kControlVertOffset,
+ GetWidth() - right_side_width -
+ (star_->GetX() + star_->GetWidth() +
+ sz.cx), // go preferred width
+ sz.cy);
+
+ go_->SetBounds(location_bar_->GetX() + location_bar_->GetWidth(),
+ kControlVertOffset,
+ sz.cx,
+ sz.cy);
+
+ // Make sure the Page menu never overlaps the location bar.
+ int page_x = go_->GetX() + go_->GetWidth() + kMenuButtonOffset;
+ page_menu_->GetPreferredSize(&sz);
+ page_menu_->SetBounds(page_x, kControlVertOffset, sz.cx, go_->GetHeight());
+ app_menu_->GetPreferredSize(&sz);
+ app_menu_->SetBounds(page_menu_->GetX() + page_menu_->GetWidth(),
+ page_menu_->GetY(), sz.cx, go_->GetHeight());
+}
+
+void BrowserToolbarView::DidGainFocus() {
+ // Find first accessible child (-1 for start search at parent).
+ int first_acc_child = GetNextAccessibleViewIndex(-1, false);
+
+ // No buttons enabled or visible.
+ if (first_acc_child == -1)
+ return;
+
+ acc_focused_view_ = GetChildViewAt(first_acc_child);
+
+ // Default focus is on the toolbar.
+ int view_index = VIEW_ID_TOOLBAR;
+
+ // Set hot-tracking for child, and update focused_view for MSAA focus event.
+ if (acc_focused_view_) {
+ acc_focused_view_->SetHotTracked(true);
+ // Update focused_view with MSAA-adjusted child id.
+ view_index = acc_focused_view_->GetID();
+ }
+
+ HWND hwnd = GetViewContainer()->GetHWND();
+
+ // Notify Access Technology that there was a change in keyboard focus.
+ ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT,
+ static_cast<LONG>(view_index));
+}
+
+void BrowserToolbarView::WillLoseFocus() {
+ // Resetting focus state.
+ acc_focused_view_->SetHotTracked(false);
+ acc_focused_view_ = NULL;
+}
+
+bool BrowserToolbarView::OnKeyPressed(const ChromeViews::KeyEvent& e) {
+ // Paranoia check, button should be initialized upon toolbar gaining focus.
+ if (!acc_focused_view_)
+ return false;
+
+ int focused_view = GetChildIndex(acc_focused_view_);
+ int next_view = focused_view;
+
+ switch (e.GetCharacter()) {
+ case VK_LEFT:
+ next_view = GetNextAccessibleViewIndex(focused_view, true);
+ break;
+ case VK_RIGHT:
+ next_view = GetNextAccessibleViewIndex(focused_view, false);
+ break;
+ case VK_DOWN:
+ case VK_RETURN:
+ // VK_SPACE is already handled by the default case.
+ if (acc_focused_view_->GetID() == VIEW_ID_PAGE_MENU ||
+ acc_focused_view_->GetID() == VIEW_ID_APP_MENU) {
+ // Safe to cast, given to above check.
+ static_cast<ChromeViews::MenuButton*>(acc_focused_view_)->Activate();
+ // Re-enable hot-tracking, as Activate() will disable it.
+ acc_focused_view_->SetHotTracked(true);
+ break;
+ }
+ default:
+ // If key is not handled explicitly, pass it on to view.
+ return acc_focused_view_->OnKeyPressed(e);
+ }
+
+ // No buttons enabled or visible.
+ if (next_view == -1)
+ return false;
+
+ // Only send an event if focus moved.
+ if (next_view != focused_view) {
+ // Remove hot-tracking from old focused button.
+ acc_focused_view_->SetHotTracked(false);
+
+ // All is well, update the focused child member variable.
+ acc_focused_view_ = GetChildViewAt(next_view);
+
+ // Hot-track new focused button.
+ acc_focused_view_->SetHotTracked(true);
+
+ // Retrieve information to generate an MSAA focus event.
+ int view_id = acc_focused_view_->GetID();
+ HWND hwnd = GetViewContainer()->GetHWND();
+
+ // Notify Access Technology that there was a change in keyboard focus.
+ ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT,
+ static_cast<LONG>(view_id));
+ return true;
+ }
+ return false;
+}
+
+bool BrowserToolbarView::OnKeyReleased(const ChromeViews::KeyEvent& e) {
+ // Paranoia check, button should be initialized upon toolbar gaining focus.
+ if (!acc_focused_view_)
+ return false;
+
+ // Have keys be handled by the views themselves.
+ return acc_focused_view_->OnKeyReleased(e);
+}
+
+
+void BrowserToolbarView::RunPageMenu(const CPoint& pt, HWND hwnd) {
+ Menu::AnchorPoint anchor = Menu::TOPRIGHT;
+ if (UILayoutIsRightToLeft())
+ anchor = Menu::TOPLEFT;
+
+ Menu menu(this, anchor, hwnd);
+ menu.AppendMenuItemWithLabel(IDC_NEWTAB, l10n_util::GetString(IDS_NEWTAB));
+ menu.AppendMenuItemWithLabel(IDC_NEWWINDOW,
+ l10n_util::GetString(IDS_NEWWINDOW));
+ menu.AppendMenuItemWithLabel(IDC_GOOFFTHERECORD,
+ l10n_util::GetString(IDS_GOOFFTHERECORD));
+ menu.AppendSeparator();
+ // The install menu may be dynamically generated with a contextual label.
+ // See browser_commands.cc.
+ menu.AppendMenuItemWithLabel(IDC_CREATE_SHORTCUT,
+ l10n_util::GetString(IDS_DEFAULT_INSTALL_SITE_LABEL));
+ menu.AppendSeparator();
+ menu.AppendMenuItemWithLabel(IDC_CUT, l10n_util::GetString(IDS_CUT));
+ menu.AppendMenuItemWithLabel(IDC_COPY, l10n_util::GetString(IDS_COPY));
+ menu.AppendMenuItemWithLabel(IDC_PASTE, l10n_util::GetString(IDS_PASTE));
+ menu.AppendSeparator();
+
+ menu.AppendMenuItemWithLabel(IDC_FIND,
+ l10n_util::GetString(IDS_FIND_IN_PAGE));
+ menu.AppendMenuItemWithLabel(IDC_SAVEPAGE,
+ l10n_util::GetString(IDS_SAVEPAGEAS));
+ menu.AppendMenuItemWithLabel(IDC_PRINT, l10n_util::GetString(IDS_PRINT));
+ menu.AppendSeparator();
+
+ Menu* zoom_menu = menu.AppendSubMenu(IDC_ZOOM,
+ l10n_util::GetString(IDS_ZOOM));
+ zoom_menu->AppendMenuItemWithLabel(IDC_ZOOM_PLUS,
+ l10n_util::GetString(IDS_ZOOM_PLUS));
+ zoom_menu->AppendMenuItemWithLabel(IDC_ZOOM_NORMAL,
+ l10n_util::GetString(IDS_ZOOM_NORMAL));
+ zoom_menu->AppendMenuItemWithLabel(IDC_ZOOM_MINUS,
+ l10n_util::GetString(IDS_ZOOM_MINUS));
+
+ // Create encoding menu.
+ Menu* encoding_menu = menu.AppendSubMenu(IDC_ENCODING,
+ l10n_util::GetString(IDS_ENCODING));
+
+ EncodingMenuControllerDelegate::BuildEncodingMenu(profile_, encoding_menu);
+
+ struct MenuCreateMaterial {
+ unsigned int menu_id;
+ unsigned int menu_label_id;
+ };
+ struct MenuCreateMaterial developer_menu_materials[] = {
+ { IDC_VIEWSOURCE, IDS_VIEWPAGESOURCE },
+ { IDC_DEBUGGER, IDS_DEBUGGER },
+ { IDC_SHOW_JS_CONSOLE, IDS_VIEWJSCONSOLE },
+ { IDC_TASKMANAGER, IDS_TASKMANAGER }
+ };
+ // Append developer menu.
+ menu.AppendSeparator();
+ Menu* developer_menu =
+ menu.AppendSubMenu(IDC_DEVELOPER, l10n_util::GetString(IDS_DEVELOPER));
+ for (int i = 0; i < arraysize(developer_menu_materials); ++i) {
+ if (developer_menu_materials[i].menu_id) {
+ developer_menu->AppendMenuItemWithLabel(
+ developer_menu_materials[i].menu_id,
+ l10n_util::GetString(developer_menu_materials[i].menu_label_id));
+ } else {
+ developer_menu->AppendSeparator();
+ }
+ }
+
+ menu.AppendSeparator();
+
+ menu.AppendMenuItemWithLabel(IDS_COMMANDS_REPORTBUG,
+ l10n_util::GetString(IDS_COMMANDS_REPORTBUG));
+ menu.RunMenuAt(pt.x, pt.y);
+}
+
+void BrowserToolbarView::RunAppMenu(const CPoint& pt, HWND hwnd) {
+ Menu::AnchorPoint anchor = Menu::TOPRIGHT;
+ if (UILayoutIsRightToLeft())
+ anchor = Menu::TOPLEFT;
+
+ Menu menu(this, anchor, hwnd);
+ menu.AppendMenuItemWithLabel(IDC_SHOW_BOOKMARKS_BAR,
+ l10n_util::GetString(IDS_SHOW_BOOKMARK_BAR));
+ menu.AppendSeparator();
+ menu.AppendMenuItemWithLabel(IDC_SHOW_HISTORY,
+ l10n_util::GetString(IDS_SHOW_HISTORY));
+ menu.AppendMenuItemWithLabel(IDC_SHOW_DOWNLOADS,
+ l10n_util::GetString(IDS_SHOW_DOWNLOADS));
+ menu.AppendSeparator();
+ menu.AppendMenuItemWithLabel(IDC_CLEAR_BROWSING_DATA,
+ l10n_util::GetString(IDS_CLEAR_BROWSING_DATA));
+ menu.AppendMenuItemWithLabel(IDC_IMPORT_SETTINGS,
+ l10n_util::GetString(IDS_IMPORT_SETTINGS));
+ menu.AppendSeparator();
+ menu.AppendMenuItemWithLabel(IDC_OPTIONS,
+ l10n_util::GetStringF(IDS_OPTIONS,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ menu.AppendMenuItemWithLabel(IDC_ABOUT,
+ l10n_util::GetStringF(IDS_ABOUT,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ menu.AppendMenuItemWithLabel(IDC_HELPMENU, l10n_util::GetString(IDS_HELP));
+ menu.AppendSeparator();
+ menu.AppendMenuItemWithLabel(IDC_EXIT, l10n_util::GetString(IDS_EXIT));
+
+ menu.RunMenuAt(pt.x, pt.y);
+}
+
+bool BrowserToolbarView::IsItemChecked(int id) const {
+ if (!profile_)
+ return false;
+ if (id == IDC_SHOW_BOOKMARKS_BAR)
+ return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
+ else
+ return EncodingMenuControllerDelegate::IsItemChecked(id);
+}
+
+void BrowserToolbarView::RunMenu(ChromeViews::View* source,
+ const CPoint& pt,
+ HWND hwnd) {
+ switch (source->GetID()) {
+ case VIEW_ID_PAGE_MENU:
+ RunPageMenu(pt, hwnd);
+ break;
+ case VIEW_ID_APP_MENU:
+ RunAppMenu(pt, hwnd);
+ break;
+ default:
+ NOTREACHED() << "Invalid source menu.";
+ }
+}
+
+bool BrowserToolbarView::GetAccessibleRole(VARIANT* role) {
+ DCHECK(role);
+
+ role->vt = VT_I4;
+ role->lVal = ROLE_SYSTEM_TOOLBAR;
+ return true;
+}
+
+bool BrowserToolbarView::GetAccessibleName(std::wstring* name) {
+ if (!accessible_name_.empty()) {
+ (*name).assign(accessible_name_);
+ return true;
+ }
+ return false;
+}
+
+void BrowserToolbarView::SetAccessibleName(const std::wstring& name) {
+ accessible_name_.assign(name);
+}
+
+int BrowserToolbarView::GetNextAccessibleViewIndex(int view_index,
+ bool nav_left) {
+ int modifier = 1;
+
+ if (nav_left)
+ modifier = -1;
+
+ int current_view_index = view_index + modifier;
+
+ if ((current_view_index >= 0) && (current_view_index < GetChildViewCount())) {
+ // Skip the location bar, as it has its own keyboard navigation.
+ if (current_view_index == GetChildIndex(location_bar_))
+ current_view_index += modifier;
+
+ view_index = current_view_index;
+ }
+ return view_index;
+}
+
+int BrowserToolbarView::GetDragOperations(ChromeViews::View* sender,
+ int x,
+ int y) {
+ DCHECK(sender == star_);
+ if (model_->input_in_progress() || !tab_ || !tab_->ShouldDisplayURL() ||
+ !tab_->GetURL().is_valid()) {
+ return DragDropTypes::DRAG_NONE;
+ }
+ return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK;
+}
+
+void BrowserToolbarView::WriteDragData(ChromeViews::View* sender,
+ int press_x,
+ int press_y,
+ OSExchangeData* data) {
+ DCHECK(
+ GetDragOperations(sender, press_x, press_y) != DragDropTypes::DRAG_NONE);
+
+ UserMetrics::RecordAction(L"Toolbar_DragStar", profile_);
+
+ drag_utils::SetURLAndDragImage(tab_->GetURL(), tab_->GetTitle(),
+ tab_->GetFavIcon(), data);
+}
+
+TabContents* BrowserToolbarView::GetTabContents() {
+ return tab_;
+}
+
+void BrowserToolbarView::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NOTIFY_PREF_CHANGED) {
+ std::wstring* pref_name = Details<std::wstring>(details).ptr();
+ if (*pref_name == prefs::kShowHomeButton) {
+ Layout();
+ SchedulePaint();
+ }
+ }
+}
+
+bool BrowserToolbarView::GetAcceleratorInfo(int id,
+ ChromeViews::Accelerator* accel) {
+ // The standard Ctrl-X, Ctrl-V and Ctrl-C are not defined as accelerators
+ // anywhere so we need to check for them explicitly here.
+ // TODO(cpu) Bug 1109102. Query WebKit land for the actual bindings.
+ switch (id) {
+ case IDC_CUT:
+ *accel = ChromeViews::Accelerator(L'X', false, true, false);
+ return true;
+ case IDC_COPY:
+ *accel = ChromeViews::Accelerator(L'C', false, true, false);
+ return true;
+ case IDC_PASTE:
+ *accel = ChromeViews::Accelerator(L'V', false, true, false);
+ return true;
+ }
+ // Else, we retrieve the accelerator information from the frame.
+ return GetViewContainer()->GetAccelerator(id, accel);
+}
diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h
new file mode 100644
index 0000000..29d9f19
--- /dev/null
+++ b/chrome/browser/views/toolbar_view.h
@@ -0,0 +1,197 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_TOOLBAR_VIEW_H__
+#define CHROME_BROWSER_VIEWS_TOOLBAR_VIEW_H__
+
+#include <vector>
+
+#include "base/scoped_ptr.h"
+#include "chrome/browser/back_forward_menu_model.h"
+#include "chrome/browser/controller.h"
+#include "chrome/browser/encoding_menu_controller_delegate.h"
+#include "chrome/browser/views/dom_view.h"
+#include "chrome/browser/views/go_button.h"
+#include "chrome/browser/views/location_bar_view.h"
+#include "chrome/common/pref_member.h"
+#include "chrome/views/menu.h"
+#include "chrome/views/menu_button.h"
+#include "chrome/views/view.h"
+#include "chrome/views/view_menu_delegate.h"
+
+class Browser;
+class Profile;
+class ToolbarStarToggle;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BrowserToolbarView class
+//
+// The BrowserToolbarView is responsible for constructing the content of and
+// rendering the Toolbar used in the Browser Window
+//
+///////////////////////////////////////////////////////////////////////////////
+class BrowserToolbarView : public ChromeViews::View,
+ public EncodingMenuControllerDelegate,
+ public ChromeViews::ViewMenuDelegate,
+ public ChromeViews::DragController,
+ public LocationBarView::Delegate,
+ public NotificationObserver {
+ public:
+ BrowserToolbarView(CommandController* controller, Browser* browser);
+ virtual ~BrowserToolbarView();
+
+ // Create the contents of the Browser Toolbar
+ void Init(Profile* profile);
+
+ // ChromeViews::View
+ virtual void Layout();
+ virtual void DidGainFocus();
+ virtual void WillLoseFocus();
+ virtual bool OnKeyPressed(const ChromeViews::KeyEvent& e);
+ virtual bool OnKeyReleased(const ChromeViews::KeyEvent& e);
+
+ // Overridden from EncodingMenuControllerDelegate:
+ virtual bool IsItemChecked(int id) const;
+
+ // Overridden from Menu::BaseControllerDelegate:
+ virtual bool GetAcceleratorInfo(int id, ChromeViews::Accelerator* accel);
+
+ // ChromeViews::MenuDelegate
+ virtual void RunMenu(ChromeViews::View* source, const CPoint& pt, HWND hwnd);
+
+ // Sets the profile which is active on the currently-active tab.
+ void SetProfile(Profile* profile);
+ Profile* profile() { return profile_; }
+
+ ToolbarStarToggle* star_button() { return star_; }
+
+ GoButton* GetGoButton() { return go_; }
+
+ LocationBarView* GetLocationBarView() const { return location_bar_; }
+
+ // Updates the toolbar (and transitively the location bar) with the states of
+ // the specified |tab|. If |should_restore_state| is true, we're switching
+ // (back?) to this tab and should restore any previous location bar state
+ // (such as user editing) as well.
+ void Update(TabContents* tab, bool should_restore_state);
+
+ void OnInputInProgress(bool in_progress);
+
+ // Returns the MSAA role of the current view. The role is what assistive
+ // technologies (ATs) use to determine what behavior to expect from a given
+ // control.
+ bool GetAccessibleRole(VARIANT* role);
+
+ // Returns a brief, identifying string, containing a unique, readable name.
+ bool GetAccessibleName(std::wstring* name);
+
+ // Assigns an accessible string name.
+ void SetAccessibleName(const std::wstring& name);
+
+ // Returns the index of the next view of the toolbar, starting from the given
+ // view index (skipping the location bar), in the given navigation direction
+ // (nav_left true means navigation right to left, and vice versa). -1 finds
+ // first accessible child, based on the above policy.
+ int GetNextAccessibleViewIndex(int view_index, bool nav_left);
+
+ ChromeViews::View* GetAccFocusedChildView() {
+ return acc_focused_view_;
+ }
+
+ // Returns the selected tab.
+ virtual TabContents* GetTabContents();
+
+ Browser* browser() { return browser_; }
+
+ private:
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // DragController methods for the star button. These allow the drag if the
+ // user hasn't edited the text, the url is valid and should be displayed.
+ virtual void WriteDragData(View* sender,
+ int press_x,
+ int press_y,
+ OSExchangeData* data);
+ virtual int GetDragOperations(View* sender, int x, int y);
+
+ // Set up the various Views in the toolbar
+ void CreateLeftSideControls();
+ void CreateCenterStack(Profile* profile);
+ void CreateRightSideControls(Profile* profile);
+
+ // Updates the controls to display the security appropriately (highlighted if
+ // secure).
+ void SetSecurityLevel(ToolbarModel::SecurityLevel security_level);
+
+ // Show the page menu.
+ void RunPageMenu(const CPoint& pt, HWND hwnd);
+
+ // Show the app menu.
+ void RunAppMenu(const CPoint& pt, HWND hwnd);
+
+ // This View's Command Controller
+ CommandController* controller_;
+
+ scoped_ptr<BackForwardMenuModel> back_menu_model_;
+ scoped_ptr<BackForwardMenuModel> forward_menu_model_;
+
+ // The model that contains the security level, text, icon to display...
+ ToolbarModel* model_;
+
+ // Storage of strings needed for accessibility.
+ std::wstring accessible_name_;
+ // Child view currently having MSAA focus (location bar excluded from arrow
+ // navigation).
+ ChromeViews::View* acc_focused_view_;
+
+ // Controls
+ ChromeViews::Button* back_;
+ ChromeViews::Button* forward_;
+ ChromeViews::Button* reload_;
+ ChromeViews::Button* home_;
+ ToolbarStarToggle* star_;
+ LocationBarView* location_bar_;
+ GoButton* go_;
+ ChromeViews::MenuButton* page_menu_;
+ ChromeViews::MenuButton* app_menu_;
+ Profile* profile_;
+ Browser* browser_;
+
+ // Current tab we're showing state for.
+ TabContents* tab_;
+
+ // Controls whether or not a home button should be shown on the toolbar.
+ BooleanPrefMember show_home_button_;
+};
+
+#endif // CHROME_BROWSER_VIEWS_TOOLBAR_VIEW_H__