1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/input_window_dialog.h"
6
7#include <gtk/gtk.h>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/message_loop.h"
11#include "base/string_piece.h"
12#include "base/utf_string_conversions.h"
13#include "chrome/browser/ui/gtk/gtk_util.h"
14#include "ui/base/gtk/gtk_signal.h"
15
16class InputWindowDialogGtk : public InputWindowDialog {
17 public:
18  // Creates a dialog. Takes ownership of |delegate|.
19  InputWindowDialogGtk(GtkWindow* parent,
20                       const std::string& window_title,
21                       const std::string& label,
22                       const std::string& contents,
23                       Delegate* delegate);
24  virtual ~InputWindowDialogGtk();
25
26  virtual void Show();
27  virtual void Close();
28
29 private:
30  CHROMEG_CALLBACK_0(InputWindowDialogGtk, void, OnEntryChanged, GtkEditable*);
31  CHROMEGTK_CALLBACK_1(InputWindowDialogGtk, void, OnResponse, int);
32  CHROMEGTK_CALLBACK_1(InputWindowDialogGtk, gboolean,
33                       OnWindowDeleteEvent, GdkEvent*);
34  CHROMEGTK_CALLBACK_0(InputWindowDialogGtk, void, OnWindowDestroy);
35
36  // The underlying gtk dialog window.
37  GtkWidget* dialog_;
38
39  // The GtkEntry in this form.
40  GtkWidget* input_;
41
42  // Our delegate. Consumes the window's output.
43  scoped_ptr<InputWindowDialog::Delegate> delegate_;
44};
45
46
47InputWindowDialogGtk::InputWindowDialogGtk(GtkWindow* parent,
48                                           const std::string& window_title,
49                                           const std::string& label,
50                                           const std::string& contents,
51                                           Delegate* delegate)
52    : dialog_(gtk_dialog_new_with_buttons(
53                  window_title.c_str(),
54                  parent,
55                  GTK_DIALOG_MODAL,
56                  GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
57                  GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
58                  NULL)),
59      delegate_(delegate) {
60  gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_ACCEPT);
61  gtk_dialog_set_has_separator(GTK_DIALOG(dialog_), FALSE);
62  gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE);
63
64  GtkWidget* content_area = GTK_DIALOG(dialog_)->vbox;
65  gtk_box_set_spacing(GTK_BOX(content_area), 18);
66
67  GtkWidget* hbox = gtk_hbox_new(FALSE, 6);
68  GtkWidget* label_widget = gtk_label_new(label.c_str());
69  gtk_box_pack_start(GTK_BOX(hbox), label_widget, FALSE, FALSE, 0);
70
71  input_ = gtk_entry_new();
72  gtk_entry_set_text(GTK_ENTRY(input_), contents.c_str());
73  g_signal_connect(input_, "changed",
74                   G_CALLBACK(OnEntryChangedThunk), this);
75  g_object_set(G_OBJECT(input_), "activates-default", TRUE, NULL);
76  gtk_box_pack_start(GTK_BOX(hbox), input_, TRUE, TRUE, 0);
77
78  gtk_widget_show_all(hbox);
79
80  gtk_box_pack_start(GTK_BOX(content_area), hbox, FALSE, FALSE, 0);
81
82  g_signal_connect(dialog_, "response",
83                   G_CALLBACK(OnResponseThunk), this);
84  g_signal_connect(dialog_, "delete-event",
85                   G_CALLBACK(OnWindowDeleteEventThunk), this);
86  g_signal_connect(dialog_, "destroy",
87                   G_CALLBACK(OnWindowDestroyThunk), this);
88}
89
90InputWindowDialogGtk::~InputWindowDialogGtk() {
91}
92
93void InputWindowDialogGtk::Show() {
94  gtk_util::ShowDialog(dialog_);
95}
96
97void InputWindowDialogGtk::Close() {
98  // Under the model that we've inherited from Windows, dialogs can receive
99  // more than one Close() call inside the current message loop event.
100  if (dialog_) {
101    gtk_widget_destroy(dialog_);
102    dialog_ = NULL;
103  }
104}
105
106void InputWindowDialogGtk::OnEntryChanged(GtkEditable* entry) {
107  std::wstring value(UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(entry))));
108  gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog_),
109                                    GTK_RESPONSE_ACCEPT,
110                                    delegate_->IsValid(value));
111}
112
113void InputWindowDialogGtk::OnResponse(GtkWidget* dialog, int response_id) {
114  if (response_id == GTK_RESPONSE_ACCEPT) {
115    std::wstring value(UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(input_))));
116    delegate_->InputAccepted(value);
117  } else {
118    delegate_->InputCanceled();
119  }
120  Close();
121}
122
123gboolean InputWindowDialogGtk::OnWindowDeleteEvent(GtkWidget* widget,
124                                                   GdkEvent* event) {
125  Close();
126
127  // Return true to prevent the gtk dialog from being destroyed. Close will
128  // destroy it for us and the default gtk_dialog_delete_event_handler() will
129  // force the destruction without us being able to stop it.
130  return TRUE;
131}
132
133void InputWindowDialogGtk::OnWindowDestroy(GtkWidget* widget) {
134  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
135}
136
137InputWindowDialog* InputWindowDialog::Create(gfx::NativeWindow parent,
138                                             const std::wstring& window_title,
139                                             const std::wstring& label,
140                                             const std::wstring& contents,
141                                             Delegate* delegate) {
142  return new InputWindowDialogGtk(parent,
143                                  WideToUTF8(window_title),
144                                  WideToUTF8(label),
145                                  WideToUTF8(contents),
146                                  delegate);
147}
148