| Index: chrome/browser/gtk/first_run_dialog.cc
|
| diff --git a/chrome/browser/gtk/first_run_dialog.cc b/chrome/browser/gtk/first_run_dialog.cc
|
| index 2af21c1c98ddd55bfb3cbceb2c46a61e192108c2..882786dffa8f22aca3e16d8065ef5d417f50ee5a 100644
|
| --- a/chrome/browser/gtk/first_run_dialog.cc
|
| +++ b/chrome/browser/gtk/first_run_dialog.cc
|
| @@ -5,41 +5,81 @@
|
| #include "chrome/browser/gtk/first_run_dialog.h"
|
|
|
| #include "app/l10n_util.h"
|
| +#include "app/resource_bundle.h"
|
| #include "base/message_loop.h"
|
| #include "base/utf_string_conversions.h"
|
| #include "chrome/browser/gtk/gtk_chrome_link_button.h"
|
| +#include "chrome/browser/gtk/gtk_floating_container.h"
|
| #include "chrome/browser/gtk/gtk_util.h"
|
| #include "chrome/browser/importer/importer_data_types.h"
|
| #include "chrome/browser/platform_util.h"
|
| #include "chrome/browser/process_singleton.h"
|
| +#include "chrome/browser/profile.h"
|
| #include "chrome/browser/shell_integration.h"
|
| #include "chrome/installer/util/google_update_settings.h"
|
| +#include "gfx/gtk_util.h"
|
| #include "grit/chromium_strings.h"
|
| #include "grit/generated_resources.h"
|
| #include "grit/locale_settings.h"
|
| +#include "grit/theme_resources.h"
|
|
|
| #if defined(USE_LINUX_BREAKPAD)
|
| #include "chrome/app/breakpad_linux.h"
|
| #endif
|
|
|
| +namespace {
|
| +
|
| +const gchar* kSearchEngineKey = "template-url-search-engine";
|
| +
|
| +// Height of the label that displays the search engine's logo (in lieu of the
|
| +// actual logo) in chromium.
|
| +const int kLogoLabelHeight = 100;
|
| +
|
| +// The width of the explanatory label. The 180 is the width of the large images.
|
| +const int kExplanationWidth = 3 * 180;
|
| +
|
| +// Horizontal spacing between search engine choices.
|
| +const int kSearchEngineSpacing = 6;
|
| +
|
| +// Set the (x, y) coordinates of the welcome message (which floats on top of
|
| +// the omnibox image at the top of the first run dialog).
|
| +void SetWelcomePosition(GtkFloatingContainer* container,
|
| + GtkAllocation* allocation,
|
| + GtkWidget* label) {
|
| + GValue value = { 0, };
|
| + g_value_init(&value, G_TYPE_INT);
|
| + g_value_set_int(&value, gtk_util::kContentAreaSpacing);
|
| + gtk_container_child_set_property(GTK_CONTAINER(container),
|
| + label, "x", &value);
|
| +
|
| + GtkRequisition req;
|
| + gtk_widget_size_request(label, &req);
|
| + int y = allocation->height / 2 - req.height / 2;
|
| +
|
| + g_value_set_int(&value, y);
|
| + gtk_container_child_set_property(GTK_CONTAINER(container),
|
| + label, "y", &value);
|
| + g_value_unset(&value);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| // static
|
| bool FirstRunDialog::Show(Profile* profile,
|
| ProcessSingleton* process_singleton) {
|
| int response = -1;
|
| // Object deletes itself.
|
| - FirstRunDialog* first_run = new FirstRunDialog(profile, response);
|
| + new FirstRunDialog(profile, response);
|
|
|
| // Prevent further launches of Chrome until First Run UI is done.
|
| - process_singleton->Lock(GTK_WINDOW(first_run->dialog_));
|
| + // We don't actually use the parameter to Lock() on Posix.
|
| + process_singleton->Lock(NULL);
|
|
|
| // TODO(port): it should be sufficient to just run the dialog:
|
| // int response = gtk_dialog_run(GTK_DIALOG(dialog));
|
| // but that spins a nested message loop and hoses us. :(
|
| // http://code.google.com/p/chromium/issues/detail?id=12552
|
| // Instead, run a loop and extract the response manually.
|
| - g_signal_connect(first_run->dialog_, "response",
|
| - G_CALLBACK(OnResponseDialogThunk), first_run);
|
| - gtk_widget_show_all(first_run->dialog_);
|
| MessageLoop::current()->Run();
|
|
|
| process_singleton->Unlock();
|
| @@ -47,9 +87,89 @@ bool FirstRunDialog::Show(Profile* profile,
|
| }
|
|
|
| FirstRunDialog::FirstRunDialog(Profile* profile, int& response)
|
| - : dialog_(NULL), report_crashes_(NULL), make_default_(NULL),
|
| - import_data_(NULL), import_profile_(NULL), profile_(profile),
|
| - response_(response), importer_host_(new ImporterHost()) {
|
| + : search_engine_window_(NULL),
|
| + dialog_(NULL),
|
| + report_crashes_(NULL),
|
| + make_default_(NULL),
|
| + profile_(profile),
|
| + chosen_search_engine_(NULL),
|
| + response_(response),
|
| + importer_host_(new ImporterHost()) {
|
| + search_engines_model_ = profile_->GetTemplateURLModel();
|
| + DCHECK(!search_engines_model_->loaded());
|
| + search_engines_model_->AddObserver(this);
|
| + search_engines_model_->Load();
|
| +
|
| + ShowSearchEngineWindow();
|
| +}
|
| +
|
| +FirstRunDialog::~FirstRunDialog() {
|
| +}
|
| +
|
| +void FirstRunDialog::ShowSearchEngineWindow() {
|
| + search_engine_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
| + gtk_window_set_title(
|
| + GTK_WINDOW(search_engine_window_),
|
| + l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_TITLE).c_str());
|
| + gtk_window_set_resizable(GTK_WINDOW(search_engine_window_), FALSE);
|
| + g_signal_connect(search_engine_window_, "destroy",
|
| + G_CALLBACK(OnSearchEngineWindowDestroyThunk), this);
|
| + GtkWidget* content_area = gtk_vbox_new(FALSE, 0);
|
| + gtk_container_add(GTK_CONTAINER(search_engine_window_), content_area);
|
| +
|
| + GdkPixbuf* pixbuf =
|
| + ResourceBundle::GetSharedInstance().GetPixbufNamed(
|
| + IDR_SEARCH_ENGINE_DIALOG_TOP);
|
| + GtkWidget* top_image = gtk_image_new_from_pixbuf(pixbuf);
|
| + // Right align the image.
|
| + gtk_misc_set_alignment(GTK_MISC(top_image), 1, 0);
|
| + gtk_widget_set_size_request(top_image, 0, -1);
|
| +
|
| + GtkWidget* welcome_message = gtk_util::CreateBoldLabel(
|
| + l10n_util::GetStringUTF8(IDS_FR_SEARCH_MAIN_LABEL));
|
| +
|
| + GtkWidget* top_area = gtk_floating_container_new();
|
| + gtk_container_add(GTK_CONTAINER(top_area), top_image);
|
| + gtk_floating_container_add_floating(GTK_FLOATING_CONTAINER(top_area),
|
| + welcome_message);
|
| + g_signal_connect(top_area, "set-floating-position",
|
| + G_CALLBACK(SetWelcomePosition), welcome_message);
|
| +
|
| + gtk_box_pack_start(GTK_BOX(content_area), top_area,
|
| + FALSE, FALSE, 0);
|
| +
|
| + GtkWidget* bubble_area_background = gtk_event_box_new();
|
| + gtk_widget_modify_bg(bubble_area_background,
|
| + GTK_STATE_NORMAL, &gfx::kGdkWhite);
|
| +
|
| + GtkWidget* bubble_area_box = gtk_vbox_new(FALSE, 0);
|
| + gtk_container_set_border_width(GTK_CONTAINER(bubble_area_box),
|
| + gtk_util::kContentAreaSpacing);
|
| + gtk_container_add(GTK_CONTAINER(bubble_area_background),
|
| + bubble_area_box);
|
| +
|
| + GtkWidget* explanation = gtk_label_new(
|
| + l10n_util::GetStringFUTF8(IDS_FR_SEARCH_TEXT,
|
| + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str());
|
| + gtk_util::SetLabelColor(explanation, &gfx::kGdkBlack);
|
| + gtk_label_set_line_wrap(GTK_LABEL(explanation), TRUE);
|
| + gtk_widget_set_size_request(explanation, kExplanationWidth, -1);
|
| + gtk_box_pack_start(GTK_BOX(bubble_area_box), explanation, FALSE, FALSE, 0);
|
| +
|
| + // We will fill this in after the TemplateURLModel has loaded.
|
| + search_engine_hbox_ = gtk_hbox_new(FALSE, kSearchEngineSpacing);
|
| + gtk_box_pack_start(GTK_BOX(bubble_area_box), search_engine_hbox_,
|
| + FALSE, FALSE, 0);
|
| +
|
| + gtk_box_pack_start(GTK_BOX(content_area), bubble_area_background,
|
| + TRUE, TRUE, 0);
|
| +
|
| + gtk_widget_show_all(content_area);
|
| + gtk_window_present(GTK_WINDOW(search_engine_window_));
|
| +}
|
| +
|
| +void FirstRunDialog::ShowDialog() {
|
| +#if defined(GOOGLE_CHROME_BUILD)
|
| dialog_ = gtk_dialog_new_with_buttons(
|
| l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_TITLE).c_str(),
|
| NULL, // No parent
|
| @@ -61,27 +181,13 @@ FirstRunDialog::FirstRunDialog(Profile* profile, int& response)
|
| l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_OK).c_str(),
|
| GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT);
|
|
|
| - // Normally we would do the following:
|
| - // gtk_widget_realize(dialog_);
|
| - // gtk_util::SetWindowSizeFromResources(GTK_WINDOW(dialog_),
|
| - // IDS_FIRSTRUN_DIALOG_WIDTH_CHARS,
|
| - // -1,
|
| - // false); // resizable
|
| - // But because the first run dialog has extra widgets in Windows, the
|
| - // resources specify a dialog that is way too big. So instead in just this
|
| - // one case we let GTK size the dialog itself and just mark it non-resizable
|
| - // manually:
|
| gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE);
|
|
|
| g_signal_connect(dialog_, "delete-event",
|
| G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
|
| GtkWidget* content_area = GTK_DIALOG(dialog_)->vbox;
|
| - gtk_box_set_spacing(GTK_BOX(content_area), 18);
|
|
|
| - GtkWidget* vbox = gtk_vbox_new(FALSE, 12);
|
| -
|
| -#if defined(GOOGLE_CHROME_BUILD)
|
| GtkWidget* check_label = gtk_label_new(
|
| l10n_util::GetStringUTF8(IDS_OPTIONS_ENABLE_LOGGING).c_str());
|
| gtk_label_set_line_wrap(GTK_LABEL(check_label), TRUE);
|
| @@ -99,51 +205,107 @@ FirstRunDialog::FirstRunDialog(Profile* profile, int& response)
|
| report_crashes_ = gtk_check_button_new();
|
| gtk_container_add(GTK_CONTAINER(report_crashes_), check_label);
|
|
|
| - GtkWidget* report_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
|
| - gtk_box_pack_start(GTK_BOX(report_vbox), report_crashes_, FALSE, FALSE, 0);
|
| - gtk_box_pack_start(GTK_BOX(report_vbox), learn_more_hbox, FALSE, FALSE, 0);
|
| - gtk_box_pack_start(GTK_BOX(vbox), report_vbox, FALSE, FALSE, 0);
|
| -#endif
|
| + gtk_box_pack_start(GTK_BOX(content_area), report_crashes_, FALSE, FALSE, 0);
|
| + gtk_box_pack_start(GTK_BOX(content_area), learn_more_hbox, FALSE, FALSE, 0);
|
|
|
| make_default_ = gtk_check_button_new_with_label(
|
| l10n_util::GetStringUTF8(IDS_FR_CUSTOMIZE_DEFAULT_BROWSER).c_str());
|
| - gtk_box_pack_start(GTK_BOX(vbox), make_default_, FALSE, FALSE, 0);
|
| -
|
| - GtkWidget* combo_hbox = gtk_hbox_new(FALSE, gtk_util::kLabelSpacing);
|
| - import_data_ = gtk_check_button_new_with_label(
|
| - l10n_util::GetStringUTF8(IDS_FR_CUSTOMIZE_IMPORT).c_str());
|
| - gtk_box_pack_start(GTK_BOX(combo_hbox), import_data_, FALSE, FALSE, 0);
|
| - import_profile_ = gtk_combo_box_new_text();
|
| - gtk_box_pack_start(GTK_BOX(combo_hbox), import_profile_, TRUE, TRUE, 0);
|
| - gtk_box_pack_start(GTK_BOX(vbox), combo_hbox, FALSE, FALSE, 0);
|
| -
|
| - // Detect any supported browsers that we can import from and fill
|
| - // up the combo box. If none found, disable import data checkbox.
|
| - int profiles_count = importer_host_->GetAvailableProfileCount();
|
| - if (profiles_count > 0) {
|
| - for (int i = 0; i < profiles_count; i++) {
|
| - std::wstring profile = importer_host_->GetSourceProfileNameAt(i);
|
| - gtk_combo_box_append_text(GTK_COMBO_BOX(import_profile_),
|
| - WideToUTF8(profile).c_str());
|
| + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(make_default_), TRUE);
|
| + gtk_box_pack_start(GTK_BOX(content_area), make_default_, FALSE, FALSE, 0);
|
| +
|
| + g_signal_connect(dialog_, "response",
|
| + G_CALLBACK(OnResponseDialogThunk), this);
|
| + gtk_widget_show_all(dialog_);
|
| +#else // !defined(GOOGLE_CHROME_BUILD)
|
| + // We don't show the dialog in chromium. Pretend the user accepted.
|
| + OnResponseDialog(NULL, GTK_RESPONSE_ACCEPT);
|
| +#endif // !defined(GOOGLE_CHROME_BUILD)
|
| +}
|
| +
|
| +void FirstRunDialog::OnTemplateURLModelChanged() {
|
| + // We only watch the search engine model change once, on load. Remove
|
| + // observer so we don't try to redraw if engines change under us.
|
| + search_engines_model_->RemoveObserver(this);
|
| +
|
| + // Add search engines in search_engines_model_ to buttons list. The
|
| + // first three will always be from prepopulated data.
|
| + std::vector<const TemplateURL*> template_urls =
|
| + search_engines_model_->GetTemplateURLs();
|
| +
|
| + std::vector<const TemplateURL*>::iterator search_engine_iter;
|
| +
|
| + std::string choose_text = l10n_util::GetStringUTF8(IDS_FR_SEARCH_CHOOSE);
|
| + for (std::vector<const TemplateURL*>::iterator search_engine_iter =
|
| + template_urls.begin();
|
| + search_engine_iter < template_urls.end() &&
|
| + search_engine_iter < template_urls.begin() + 3;
|
| + ++search_engine_iter) {
|
| + // Create a container for the search engine widgets.
|
| + GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
|
| +
|
| + // We show text on Chromium and images on Google Chrome.
|
| + bool show_images = false;
|
| +#if defined(GOOGLE_CHROME_BUILD)
|
| + show_images = true;
|
| +#endif
|
| +
|
| + // Create the image (maybe).
|
| + int logo_id = (*search_engine_iter)->logo_id();
|
| + if (show_images && logo_id > 0) {
|
| + GdkPixbuf* pixbuf =
|
| + ResourceBundle::GetSharedInstance().GetPixbufNamed(logo_id);
|
| + GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
|
| + gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 0);
|
| + } else {
|
| + GtkWidget* logo_label = gtk_label_new(NULL);
|
| + char* markup = g_markup_printf_escaped(
|
| + "<span weight='bold' size='x-large' color='black'>%s</span>",
|
| + WideToUTF8((*search_engine_iter)->short_name()).c_str());
|
| + gtk_label_set_markup(GTK_LABEL(logo_label), markup);
|
| + g_free(markup);
|
| + gtk_widget_set_size_request(logo_label, -1, kLogoLabelHeight);
|
| + gtk_box_pack_start(GTK_BOX(vbox), logo_label, FALSE, FALSE, 0);
|
| }
|
| - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(import_data_), TRUE);
|
| - gtk_combo_box_set_active(GTK_COMBO_BOX(import_profile_), 0);
|
| - } else {
|
| - gtk_combo_box_append_text(GTK_COMBO_BOX(import_profile_),
|
| - l10n_util::GetStringUTF8(IDS_IMPORT_NO_PROFILE_FOUND).c_str());
|
| - gtk_combo_box_set_active(GTK_COMBO_BOX(import_profile_), 0);
|
| - gtk_widget_set_sensitive(import_data_, FALSE);
|
| - gtk_widget_set_sensitive(import_profile_, FALSE);
|
| +
|
| + // Create the button.
|
| + GtkWidget* button = gtk_button_new_with_label(choose_text.c_str());
|
| + g_signal_connect(button, "clicked",
|
| + G_CALLBACK(OnSearchEngineButtonClickedThunk), this);
|
| + g_object_set_data(G_OBJECT(button), kSearchEngineKey,
|
| + const_cast<TemplateURL*>(*search_engine_iter));
|
| +
|
| + GtkWidget* button_centerer = gtk_hbox_new(FALSE, 0);
|
| + gtk_box_pack_start(GTK_BOX(button_centerer), button, TRUE, FALSE, 0);
|
| + gtk_box_pack_start(GTK_BOX(vbox), button_centerer, FALSE, FALSE, 0);
|
| +
|
| + gtk_box_pack_start(GTK_BOX(search_engine_hbox_), vbox, TRUE, TRUE, 0);
|
| + gtk_widget_show_all(search_engine_hbox_);
|
| }
|
| +}
|
|
|
| - gtk_box_pack_start(GTK_BOX(content_area), vbox, FALSE, FALSE, 0);
|
| +void FirstRunDialog::OnSearchEngineButtonClicked(GtkWidget* sender) {
|
| + chosen_search_engine_ = static_cast<TemplateURL*>(
|
| + g_object_get_data(G_OBJECT(sender), kSearchEngineKey));
|
| + gtk_widget_destroy(search_engine_window_);
|
| +}
|
| +
|
| +void FirstRunDialog::OnSearchEngineWindowDestroy(GtkWidget* sender) {
|
| + search_engine_window_ = NULL;
|
| + if (chosen_search_engine_) {
|
| + search_engines_model_->SetDefaultSearchProvider(chosen_search_engine_);
|
| + ShowDialog();
|
| + } else {
|
| + FirstRunDone();
|
| + }
|
| }
|
|
|
| void FirstRunDialog::OnResponseDialog(GtkWidget* widget, int response) {
|
| - bool import_started = false;
|
| - gtk_widget_hide_all(dialog_);
|
| + if (dialog_)
|
| + gtk_widget_hide_all(dialog_);
|
| response_ = response;
|
|
|
| + bool do_import = false;
|
| +
|
| if (response == GTK_RESPONSE_ACCEPT) {
|
| // Mark that first run has ran.
|
| FirstRun::CreateSentinel();
|
| @@ -161,38 +323,47 @@ void FirstRunDialog::OnResponseDialog(GtkWidget* widget, int response) {
|
| }
|
|
|
| // If selected set as default browser.
|
| - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(make_default_)))
|
| + if (make_default_ &&
|
| + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(make_default_))) {
|
| ShellIntegration::SetAsDefaultBrowser();
|
| + }
|
|
|
| - // Import data if selected.
|
| - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(import_data_))) {
|
| + do_import = importer_host_->GetAvailableProfileCount() > 0;
|
| + if (do_import) {
|
| + // Import data.
|
| const ProfileInfo& source_profile =
|
| - importer_host_->GetSourceProfileInfoAt(
|
| - gtk_combo_box_get_active(GTK_COMBO_BOX(import_profile_)));
|
| + importer_host_->GetSourceProfileInfoAt(0);
|
| int items = importer::SEARCH_ENGINES + importer::HISTORY +
|
| - importer::FAVORITES + importer::HOME_PAGE + importer::PASSWORDS;
|
| + importer::HOME_PAGE + importer::PASSWORDS;
|
| + importer_host_->SetObserver(this);
|
| // TODO(port): Should we do the actual import in a new process like
|
| // Windows?
|
| - StartImportingWithUI(GTK_WINDOW(dialog_), items, importer_host_.get(),
|
| - source_profile, profile_, this, true);
|
| - import_started = true;
|
| + importer_host_->StartImportSettings(source_profile,
|
| + profile_,
|
| + items,
|
| + new ProfileWriter(profile_),
|
| + true);
|
| }
|
| }
|
| - if (!import_started)
|
| +
|
| + if (!do_import)
|
| FirstRunDone();
|
| }
|
|
|
| +void FirstRunDialog::ImportEnded() {
|
| + FirstRunDone();
|
| +}
|
| +
|
| void FirstRunDialog::OnLearnMoreLinkClicked(GtkButton* button) {
|
| platform_util::OpenExternal(GURL(
|
| l10n_util::GetStringUTF8(IDS_LEARN_MORE_REPORTING_URL)));
|
| }
|
|
|
| void FirstRunDialog::FirstRunDone() {
|
| - // Set preference to show first run bubble and welcome page.
|
| - FirstRun::SetShowFirstRunBubblePref(true);
|
| FirstRun::SetShowWelcomePagePref();
|
|
|
| - gtk_widget_destroy(dialog_);
|
| + if (dialog_)
|
| + gtk_widget_destroy(dialog_);
|
| MessageLoop::current()->Quit();
|
| delete this;
|
| }
|
|
|