Chromium Code Reviews| 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 |