OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2009 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/gtk/bookmark_editor_gtk.h" | |
6 | |
7 #include <gtk/gtk.h> | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/gfx/gtk_util.h" | |
11 #include "base/logging.h" | |
12 #include "base/string_util.h" | |
13 #include "chrome/browser/history/history.h" | |
14 #include "chrome/browser/profile.h" | |
15 #include "chrome/browser/net/url_fixer_upper.h" | |
16 #include "chrome/common/l10n_util.h" | |
17 #include "googleurl/src/gurl.h" | |
18 #include "grit/chromium_strings.h" | |
19 #include "grit/generated_resources.h" | |
20 #include "grit/locale_settings.h" | |
21 | |
22 namespace { | |
23 | |
24 // Background color of text field when URL is invalid. | |
25 const GdkColor kErrorColor = GDK_COLOR_RGB(0xFF, 0xBC, 0xBC); | |
26 | |
27 // Preferred width of the tree. | |
28 static const int kTreeWidth = 300; | |
29 | |
30 } // namespace | |
31 | |
32 // static | |
33 void BookmarkEditor::Show(gfx::NativeWindow parent_hwnd, | |
34 Profile* profile, | |
35 BookmarkNode* parent, | |
36 BookmarkNode* node, | |
37 Configuration configuration, | |
38 Handler* handler) { | |
39 DCHECK(profile); | |
40 BookmarkEditorGtk* editor = | |
41 new BookmarkEditorGtk(parent_hwnd, profile, parent, node, configuration, | |
42 handler); | |
43 editor->Show(); | |
44 } | |
45 | |
46 BookmarkEditorGtk::BookmarkEditorGtk( | |
47 GtkWindow* window, | |
48 Profile* profile, | |
49 BookmarkNode* parent, | |
50 BookmarkNode* node, | |
51 BookmarkEditor::Configuration configuration, | |
52 BookmarkEditor::Handler* handler) | |
53 : profile_(profile), | |
54 dialog_(NULL), | |
55 parent_(parent), | |
56 node_(node), | |
57 running_menu_for_root_(false), | |
58 show_tree_(configuration == SHOW_TREE), | |
59 handler_(handler) { | |
60 DCHECK(profile); | |
61 Init(window); | |
62 } | |
63 | |
64 BookmarkEditorGtk::~BookmarkEditorGtk() { | |
65 // The tree model is deleted before the view. Reset the model otherwise the | |
66 // tree will reference a deleted model. | |
67 | |
68 // TODO(erg): Enable this when we have a |tree_view_|. | |
69 // if (tree_view_) | |
70 // tree_view_->SetModel(NULL); | |
71 bb_model_->RemoveObserver(this); | |
72 } | |
73 | |
74 void BookmarkEditorGtk::Init(GtkWindow* parent_window) { | |
75 bb_model_ = profile_->GetBookmarkModel(); | |
76 DCHECK(bb_model_); | |
77 bb_model_->AddObserver(this); | |
78 | |
79 dialog_ = gtk_dialog_new_with_buttons( | |
80 l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_TITLE).c_str(), | |
81 parent_window, | |
82 GTK_DIALOG_MODAL, | |
83 NULL); | |
84 | |
85 // TODO(erg): Add "New Folder" button here and insert at correct place; to | |
86 // the extreme left of the dialog. | |
87 close_button_ = gtk_dialog_add_button(GTK_DIALOG(dialog_), | |
88 GTK_STOCK_CANCEL, | |
89 GTK_RESPONSE_REJECT); | |
90 ok_button_ = gtk_dialog_add_button(GTK_DIALOG(dialog_), | |
91 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT); | |
92 gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_ACCEPT); | |
93 | |
94 // The GTK dialog content area layout (overview) | |
95 // | |
96 // +- GtkVBox |content_area| --------------------------------------+ | |
97 // |+- GtkTable |table| ------------------------------------------+| | |
98 // ||+- GtkLabel ------+ +- GtkEntry |name_entry_| --------------+|| | |
99 // ||| | | ||| | |
100 // ||+-----------------+ +---------------------------------------+|| | |
101 // ||+- GtkLabel ------+ +- GtkEntry |url_entry_| ---------------+|| | |
102 // ||| | | ||| | |
103 // ||+-----------------+ +---------------------------------------+|| | |
104 // |--------------------------------------------------------------+| | |
105 // |---------------------------------------------------------------- | |
tony
2009/04/28 19:24:34
Awesome.
| |
106 GtkWidget* content_area = GTK_DIALOG(dialog_)->vbox; | |
107 gtk_container_set_border_width(GTK_CONTAINER(content_area), 12); | |
108 GtkWidget* table = gtk_table_new(2, 2, FALSE); | |
109 | |
110 GtkWidget* label = gtk_label_new( | |
111 l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_NAME_LABEL).c_str()); | |
112 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(label), | |
113 0, 1, 0, 1, | |
114 (GtkAttachOptions)(GTK_SHRINK), | |
115 (GtkAttachOptions)(GTK_SHRINK), | |
116 12, 0); | |
117 name_entry_ = gtk_entry_new(); | |
118 gtk_entry_set_text(GTK_ENTRY(name_entry_), | |
119 node_ ? WideToUTF8(node_->GetTitle()).c_str() : ""); | |
120 g_signal_connect(G_OBJECT(name_entry_), "changed", | |
121 G_CALLBACK(OnEntryChanged), this); | |
122 g_object_set(G_OBJECT(name_entry_), "activates-default", TRUE, NULL); | |
123 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(name_entry_), | |
124 1, 2, 0, 1, | |
125 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), | |
126 (GtkAttachOptions)(GTK_FILL), | |
127 0, 0); | |
128 | |
129 label = gtk_label_new( | |
130 l10n_util::GetStringUTF8(IDS_BOOMARK_EDITOR_URL_LABEL).c_str()); | |
131 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(label), | |
132 0, 1, 1, 2, | |
133 (GtkAttachOptions)(GTK_SHRINK), | |
134 (GtkAttachOptions)(GTK_SHRINK), | |
135 12, 0); | |
136 url_entry_ = gtk_entry_new(); | |
137 gtk_entry_set_text(GTK_ENTRY(url_entry_), | |
138 node_ ? node_->GetURL().spec().c_str() : ""); | |
139 g_signal_connect(G_OBJECT(url_entry_), "changed", | |
140 G_CALLBACK(OnEntryChanged), this); | |
141 g_object_set(G_OBJECT(url_entry_), "activates-default", TRUE, NULL); | |
142 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(url_entry_), | |
143 1, 2, 1, 2, | |
144 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), | |
145 (GtkAttachOptions)(GTK_FILL), | |
146 0, 0); | |
147 | |
148 gtk_box_pack_start(GTK_BOX(content_area), table, FALSE, FALSE, 0); | |
149 | |
150 // TODO(erg): Port the windows bookmark tree model and enable this tree view. | |
151 // | |
152 // folder_tree_ = gtk_tree_view_new(); | |
153 // gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(folder_tree_), TRUE); | |
154 // gtk_widget_set_size_request(folder_tree_, kTreeWidth, -1); | |
155 // gtk_widget_show(folder_tree_); | |
156 // gtk_box_pack_start(GTK_BOX(content_area), folder_tree_, FALSE, FALSE, 0); | |
157 | |
158 g_signal_connect(dialog_, "response", | |
159 G_CALLBACK(OnResponse), this); | |
160 g_signal_connect(dialog_, "delete-event", | |
161 G_CALLBACK(OnWindowDeleteEvent), this); | |
162 g_signal_connect(dialog_, "destroy", | |
163 G_CALLBACK(OnWindowDestroy), this); | |
164 } | |
165 | |
166 void BookmarkEditorGtk::Show() { | |
167 // Manually call our OnEntryChanged handler to set the initial state. | |
168 OnEntryChanged(NULL, this); | |
169 | |
170 gtk_widget_show_all(dialog_); | |
171 } | |
172 | |
173 void BookmarkEditorGtk::Close() { | |
174 // Under the model that we've inherited from Windows, dialogs can receive | |
175 // more than one Close() call inside the current message loop event. | |
176 if (dialog_) { | |
177 gtk_widget_destroy(dialog_); | |
178 dialog_ = NULL; | |
179 } | |
180 } | |
181 | |
182 void BookmarkEditorGtk::BookmarkNodeMoved(BookmarkModel* model, | |
183 BookmarkNode* old_parent, | |
184 int old_index, | |
185 BookmarkNode* new_parent, | |
186 int new_index) { | |
187 Reset(); | |
188 } | |
189 | |
190 void BookmarkEditorGtk::BookmarkNodeAdded(BookmarkModel* model, | |
191 BookmarkNode* parent, | |
192 int index) { | |
193 Reset(); | |
194 } | |
195 | |
196 void BookmarkEditorGtk::BookmarkNodeRemoved(BookmarkModel* model, | |
197 BookmarkNode* parent, | |
198 int index, | |
199 BookmarkNode* node) { | |
200 if ((node_ && node_->HasAncestor(node)) || | |
201 (parent_ && parent_->HasAncestor(node))) { | |
202 // The node, or its parent was removed. Close the dialog. | |
203 Close(); | |
204 } else { | |
205 Reset(); | |
206 } | |
207 } | |
208 | |
209 void BookmarkEditorGtk::BookmarkNodeChildrenReordered(BookmarkModel* model, | |
210 BookmarkNode* node) { | |
211 Reset(); | |
212 } | |
213 | |
214 void BookmarkEditorGtk::Reset() { | |
215 // TODO(erg): The windows implementation tries to be smart. For now, just | |
216 // close the window. | |
217 Close(); | |
218 } | |
219 | |
220 GURL BookmarkEditorGtk::GetInputURL() const { | |
221 std::wstring input = URLFixerUpper::FixupURL( | |
222 UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(url_entry_))), L""); | |
223 return GURL(WideToUTF8(input)); | |
224 } | |
225 | |
226 std::wstring BookmarkEditorGtk::GetInputTitle() const { | |
227 return UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(name_entry_))); | |
228 } | |
229 | |
230 void BookmarkEditorGtk::ApplyEdits() { | |
231 // TODO(erg): This is massively simplified because I haven't added a | |
232 // GtkTreeView to this class yet. Then, this will have to be a copy of | |
233 // BookmarkEditorView::ApplyEdits(). | |
234 | |
235 // We're going to apply edits to the bookmark bar model, which will call us | |
236 // back. Normally when a structural edit occurs we reset the tree model. | |
237 // We don't want to do that here, so we remove ourselves as an observer. | |
238 bb_model_->RemoveObserver(this); | |
239 | |
240 GURL new_url(GetInputURL()); | |
241 std::wstring new_title(GetInputTitle()); | |
242 | |
243 BookmarkNode* old_parent = node_ ? node_->GetParent() : NULL; | |
244 const int old_index = old_parent ? old_parent->IndexOfChild(node_) : -1; | |
245 | |
246 if (!node_) { | |
247 BookmarkNode* node = | |
248 bb_model_->AddURL(parent_, parent_->GetChildCount(), new_title, | |
249 new_url); | |
250 if (handler_.get()) | |
251 handler_->NodeCreated(node); | |
252 return; | |
253 } | |
254 // If we're not showing the tree we only need to modify the node. | |
255 if (old_index == -1) { | |
256 NOTREACHED(); | |
257 return; | |
258 } | |
259 if (new_url != node_->GetURL()) { | |
260 bb_model_->AddURLWithCreationTime(old_parent, old_index, new_title, | |
261 new_url, node_->date_added()); | |
262 bb_model_->Remove(old_parent, old_index + 1); | |
263 } else { | |
264 bb_model_->SetTitle(node_, new_title); | |
265 } | |
266 } | |
267 | |
268 // static | |
269 void BookmarkEditorGtk::OnResponse(GtkDialog* dialog, int response_id, | |
270 BookmarkEditorGtk* window) { | |
271 if (response_id == GTK_RESPONSE_ACCEPT) { | |
272 window->ApplyEdits(); | |
273 } | |
274 | |
275 window->Close(); | |
276 } | |
277 | |
278 // static | |
279 gboolean BookmarkEditorGtk::OnWindowDeleteEvent( | |
280 GtkWidget* widget, | |
281 GdkEvent* event, | |
282 BookmarkEditorGtk* dialog) { | |
283 dialog->Close(); | |
284 | |
285 // Return true to prevent the gtk dialog from being destroyed. Close will | |
286 // destroy it for us and the default gtk_dialog_delete_event_handler() will | |
287 // force the destruction without us being able to stop it. | |
288 return TRUE; | |
289 } | |
290 | |
291 // static | |
292 void BookmarkEditorGtk::OnWindowDestroy(GtkWidget* widget, | |
293 BookmarkEditorGtk* dialog) { | |
294 MessageLoop::current()->DeleteSoon(FROM_HERE, dialog); | |
295 } | |
296 | |
297 // static | |
298 void BookmarkEditorGtk::OnEntryChanged(GtkEditable* entry, | |
299 BookmarkEditorGtk* dialog) { | |
300 const GURL url(dialog->GetInputURL()); | |
301 if (!url.is_valid()) { | |
302 gtk_widget_modify_base(dialog->url_entry_, GTK_STATE_NORMAL, &kErrorColor); | |
303 gtk_widget_set_sensitive(GTK_WIDGET(dialog->ok_button_), false); | |
304 } else { | |
305 gtk_widget_modify_base(dialog->url_entry_, GTK_STATE_NORMAL, NULL); | |
306 gtk_widget_set_sensitive(GTK_WIDGET(dialog->ok_button_), true); | |
307 } | |
308 } | |
OLD | NEW |