text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/ui/gtk/javascript_app_modal_dialog_gtk.h"
  
  #include <gtk/gtk.h>
  
  #include "base/logging.h"
  #include "base/strings/utf_string_conversions.h"
  #include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
  #include "chrome/browser/ui/gtk/gtk_util.h"
  #include "grit/generated_resources.h"
  #include "grit/locale_settings.h"
  #include "ui/base/l10n/l10n_util.h"
  #include "ui/base/ui_base_types.h"
  
  namespace {
  
  // We stash pointers to widgets on the gtk_dialog so we can refer to them
  // after dialog creation.
  const char kPromptTextId[] = "chrome_prompt_text";
  const char kSuppressCheckboxId[] = "chrome_suppress_checkbox";
  
  // If there's a text entry in the dialog, get the text from the first one and
  // return it.
  base::string16 GetPromptText(GtkDialog* dialog) {
    GtkWidget* widget = static_cast<GtkWidget*>(
        g_object_get_data(G_OBJECT(dialog), kPromptTextId));
    if (widget)
      return base::UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(widget)));
    return base::string16();
  }
  
  // If there's a toggle button in the dialog, return the toggled state.
  // Otherwise, return false.
  bool ShouldSuppressJSDialogs(GtkDialog* dialog) {
    GtkWidget* widget = static_cast<GtkWidget*>(
        g_object_get_data(G_OBJECT(dialog), kSuppressCheckboxId));
    if (widget)
      return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
    return false;
  }
  
  }  // namespace
  
  ////////////////////////////////////////////////////////////////////////////////
  // JavaScriptAppModalDialogGtk, public:
  
  JavaScriptAppModalDialogGtk::JavaScriptAppModalDialogGtk(
      JavaScriptAppModalDialog* dialog,
      gfx::NativeWindow parent_window)
      : dialog_(dialog) {
    GtkButtonsType buttons = GTK_BUTTONS_NONE;
    GtkMessageType message_type = GTK_MESSAGE_OTHER;
  
    // We add in the OK button manually later because we want to focus it
    // explicitly.
    switch (dialog_->javascript_message_type()) {
      case content::JAVASCRIPT_MESSAGE_TYPE_ALERT:
        buttons = GTK_BUTTONS_NONE;
        message_type = GTK_MESSAGE_WARNING;
        break;
  
      case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
        if (dialog_->is_before_unload_dialog()) {
          // onbeforeunload also uses a confirm prompt, it just has custom
          // buttons.  We add the buttons using gtk_dialog_add_button below.
          buttons = GTK_BUTTONS_NONE;
        } else {
          buttons = GTK_BUTTONS_CANCEL;
        }
        message_type = GTK_MESSAGE_QUESTION;
        break;
  
      case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT:
        buttons = GTK_BUTTONS_CANCEL;
        message_type = GTK_MESSAGE_QUESTION;
        break;
  
      default:
        NOTREACHED();
    }
  
    // We want the alert to be app modal so put all the browser windows into the
    // same window group.
    gtk_util::MakeAppModalWindowGroup();
  
    gtk_dialog_ = gtk_message_dialog_new(parent_window,
        GTK_DIALOG_MODAL, message_type, buttons, "%s",
        base::UTF16ToUTF8(dialog_->message_text()).c_str());
    g_signal_connect(gtk_dialog_, "delete-event",
                     G_CALLBACK(gtk_widget_hide_on_delete), NULL);
    gtk_util::ApplyMessageDialogQuirks(gtk_dialog_);
    gtk_window_set_title(GTK_WINDOW(gtk_dialog_),
                         base::UTF16ToUTF8(dialog_->title()).c_str());
  
    // Adjust content area as needed.  Set up the prompt text entry or
    // suppression check box.
    if (dialog_->javascript_message_type() ==
            content::JAVASCRIPT_MESSAGE_TYPE_PROMPT) {
      GtkWidget* content_area =
          gtk_dialog_get_content_area(GTK_DIALOG(gtk_dialog_));
      GtkWidget* text_box = gtk_entry_new();
      gtk_entry_set_text(GTK_ENTRY(text_box),
          base::UTF16ToUTF8(dialog_->default_prompt_text()).c_str());
      gtk_box_pack_start(GTK_BOX(content_area), text_box, TRUE, TRUE, 0);
      g_object_set_data(G_OBJECT(gtk_dialog_), kPromptTextId, text_box);
      gtk_entry_set_activates_default(GTK_ENTRY(text_box), TRUE);
    }
  
    if (dialog_->display_suppress_checkbox()) {
      GtkWidget* content_area =
          gtk_dialog_get_content_area(GTK_DIALOG(gtk_dialog_));
      GtkWidget* check_box = gtk_check_button_new_with_label(
          l10n_util::GetStringUTF8(
          IDS_JAVASCRIPT_MESSAGEBOX_SUPPRESS_OPTION).c_str());
      gtk_box_pack_start(GTK_BOX(content_area), check_box, TRUE, TRUE, 0);
      g_object_set_data(G_OBJECT(gtk_dialog_), kSuppressCheckboxId, check_box);
    }
  
    // Adjust buttons/action area as needed.
    if (dialog_->is_before_unload_dialog()) {
      std::string button_text = l10n_util::GetStringUTF8(
          dialog_->is_reload() ?
          IDS_BEFORERELOAD_MESSAGEBOX_OK_BUTTON_LABEL :
          IDS_BEFOREUNLOAD_MESSAGEBOX_OK_BUTTON_LABEL);
      gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), button_text.c_str(),
          GTK_RESPONSE_OK);
  
      button_text = l10n_util::GetStringUTF8(
          dialog_->is_reload() ?
          IDS_BEFORERELOAD_MESSAGEBOX_CANCEL_BUTTON_LABEL :
          IDS_BEFOREUNLOAD_MESSAGEBOX_CANCEL_BUTTON_LABEL);
      gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), button_text.c_str(),
          GTK_RESPONSE_CANCEL);
    } else {
      // Add the OK button and focus it.
      GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_),
          GTK_STOCK_OK, GTK_RESPONSE_OK);
      if (dialog_->javascript_message_type() !=
              content::JAVASCRIPT_MESSAGE_TYPE_PROMPT)
        gtk_widget_grab_focus(ok_button);
    }
  
    gtk_dialog_set_default_response(GTK_DIALOG(gtk_dialog_), GTK_RESPONSE_OK);
    g_signal_connect(gtk_dialog_, "response", G_CALLBACK(OnResponseThunk), this);
  }
  
  JavaScriptAppModalDialogGtk::~JavaScriptAppModalDialogGtk() {
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  // JavaScriptAppModalDialogGtk, NativeAppModalDialog implementation:
  
  int JavaScriptAppModalDialogGtk::GetAppModalDialogButtons() const {
    switch (dialog_->javascript_message_type()) {
      case content::JAVASCRIPT_MESSAGE_TYPE_ALERT:
        return ui::DIALOG_BUTTON_OK;
  
      case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
        return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
  
      case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT:
        return ui::DIALOG_BUTTON_OK;
  
      default:
        NOTREACHED();
        return 0;
    }
  }
  
  void JavaScriptAppModalDialogGtk::ShowAppModalDialog() {
    gtk_util::ShowDialogWithMinLocalizedWidth(GTK_WIDGET(gtk_dialog_),
        IDS_ALERT_DIALOG_WIDTH_CHARS);
  }
  
  void JavaScriptAppModalDialogGtk::ActivateAppModalDialog() {
    DCHECK(gtk_dialog_);
    gtk_window_present(GTK_WINDOW(gtk_dialog_));
  }
  
  void JavaScriptAppModalDialogGtk::CloseAppModalDialog() {
    DCHECK(gtk_dialog_);
    OnResponse(gtk_dialog_, GTK_RESPONSE_DELETE_EVENT);
  }
  
  void JavaScriptAppModalDialogGtk::AcceptAppModalDialog() {
    OnResponse(gtk_dialog_, GTK_RESPONSE_OK);
  }
  
  void JavaScriptAppModalDialogGtk::CancelAppModalDialog() {
    OnResponse(gtk_dialog_, GTK_RESPONSE_CANCEL);
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  // JavaScriptAppModalDialogGtk, private:
  
  void JavaScriptAppModalDialogGtk::OnResponse(GtkWidget* dialog,
                                               int response_id) {
    switch (response_id) {
      case GTK_RESPONSE_OK:
        // The first arg is the prompt text and the second is true if we want to
        // suppress additional popups from the page.
        dialog_->OnAccept(GetPromptText(GTK_DIALOG(dialog)),
                          ShouldSuppressJSDialogs(GTK_DIALOG(dialog)));
        break;
  
      case GTK_RESPONSE_CANCEL:
      case GTK_RESPONSE_DELETE_EVENT:   // User hit the X on the dialog.
        dialog_->OnCancel(ShouldSuppressJSDialogs(GTK_DIALOG(dialog)));
        break;
  
      default:
        NOTREACHED();
    }
    gtk_widget_destroy(dialog);
  
    // Now that the dialog is gone, we can put all the  windows into separate
    // window groups so other dialogs are no longer app modal.
    gtk_util::AppModalDismissedUngroupWindows();
    delete this;
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  // NativeAppModalDialog, public:
  
  // static
  NativeAppModalDialog* NativeAppModalDialog::CreateNativeJavaScriptPrompt(
      JavaScriptAppModalDialog* dialog,
      gfx::NativeWindow parent_window) {
    return new JavaScriptAppModalDialogGtk(dialog, parent_window);
  }
  