summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm
diff options
context:
space:
mode:
authorshess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-06 22:51:11 +0000
committershess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-06 22:51:11 +0000
commit5856887365d0b2a48ed2c5d160b50904959edb59 (patch)
tree8684e18b08031dff4a953a3c07f63795bbbdb729 /chrome/browser/autocomplete/autocomplete_edit_view_mac.mm
parente713032402418e679b6ad93f75ac33e879b9ceee (diff)
downloadchromium_src-5856887365d0b2a48ed2c5d160b50904959edb59.zip
chromium_src-5856887365d0b2a48ed2c5d160b50904959edb59.tar.gz
chromium_src-5856887365d0b2a48ed2c5d160b50904959edb59.tar.bz2
Initial implemention of Mac Omnibox.
AutocompletePopupViewMac implements AutocompletePopupView in terms of a bare NSWindow containing an NSTableView. AutocompleteTableTarget implements an Obj-C class to bridge from appkit callbacks back to the popup view (and from there to the model which contains the data it needs). AutocompleteEditViewMac implements AutocompleteEditView in terms of an NSTextField, which is passed down from a nib owner. It works with the popup view to make sure the popup is positioned correctly. AutocompleteFieldDelegate is an internal Obj-C class to bridge from appkit callbacks back to the edit view (and then the edit model). LocationBarViewMac implements LocationBar for interacting with the rest of the browser, and AutocompleteEditController for managing the edit and popup views. It is mostly placeholder code stolen from the gtk implementation. --- I've tried to implement an amount of code which worked and was useful, but which didn't drag on and on into the future. So no tab to search or hints or anything, sometimes ugly, selection may be funky, etc. Review URL: http://codereview.chromium.org/50074 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13201 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/autocomplete/autocomplete_edit_view_mac.mm')
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_mac.mm309
1 files changed, 309 insertions, 0 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm
new file mode 100644
index 0000000..cc0bffa
--- /dev/null
+++ b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm
@@ -0,0 +1,309 @@
+// Copyright (c) 2009 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/autocomplete/autocomplete_edit_view_mac.h"
+
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h"
+
+// Thin Obj-C bridge class that is the delegate of the omnibox field.
+// It intercepts various control delegate methods and vectors them to
+// the edit view.
+
+@interface AutocompleteFieldDelegate : NSObject {
+ @private
+ AutocompleteEditViewMac* edit_view_; // weak, owns us.
+}
+- initWithEditView:(AutocompleteEditViewMac*)view;
+@end
+
+AutocompleteEditViewMac::AutocompleteEditViewMac(
+ AutocompleteEditController* controller,
+ ToolbarModel* toolbar_model,
+ Profile* profile,
+ CommandUpdater* command_updater)
+ : model_(new AutocompleteEditModel(this, controller, profile)),
+ popup_view_(new AutocompletePopupViewMac(this, model_.get(), profile)),
+ controller_(controller),
+ toolbar_model_(toolbar_model),
+ command_updater_(command_updater),
+ field_(nil),
+ edit_helper_([[AutocompleteFieldDelegate alloc] initWithEditView:this]) {
+ DCHECK(controller);
+ DCHECK(toolbar_model);
+ DCHECK(profile);
+ DCHECK(command_updater);
+}
+
+AutocompleteEditViewMac::~AutocompleteEditViewMac() {
+ // TODO(shess): Having to be aware of destructor ordering in this
+ // way seems brittle. There must be a better way.
+
+ // Destroy popup view before this object in case it tries to call us
+ // back in the destructor. Likewise for destroying the model before
+ // this object.
+ popup_view_.reset();
+ model_.reset();
+
+ // Disconnect field_ from edit_helper_ so that we don't get calls
+ // after destruction.
+ [field_ setDelegate:nil];
+}
+
+// TODO(shess): This is the minimal change which seems to unblock
+// getting the minimal Omnibox code checked in without making the
+// world worse. Browser::TabSelectedAt() calls this when the tab
+// changes, but that is only wired up for Windows. I do not yet
+// understand that code well enough to go for it. Once wired up, then
+// code can be removed at:
+// [TabContentsController defocusLocationBar]
+// [TabStripController selectTabWithContents:...]
+void AutocompleteEditViewMac::SaveStateToTab(TabContents* tab) {
+ // TODO(shess): Actually save the state to the tab area.
+
+ // Drop the popup before we change to another tab.
+ ClosePopup();
+}
+
+void AutocompleteEditViewMac::OpenURL(const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const GURL& alternate_nav_url,
+ size_t selected_line,
+ const std::wstring& keyword) {
+ // TODO(shess): Why is the caller passing an invalid url in the
+ // first place? Make sure that case isn't being dropped on the
+ // floor.
+ if (!url.is_valid()) {
+ return;
+ }
+
+ model_->SendOpenNotification(selected_line, keyword);
+
+ if (disposition != NEW_BACKGROUND_TAB)
+ RevertAll(); // Revert the box to its unedited state.
+ controller_->OnAutocompleteAccept(url, disposition, transition,
+ alternate_nav_url);
+}
+
+std::wstring AutocompleteEditViewMac::GetText() const {
+ return base::SysNSStringToWide([field_ stringValue]);
+}
+
+void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const std::wstring& text,
+ size_t caret_pos) {
+ UpdateAndStyleText(text, text.size());
+}
+
+void AutocompleteEditViewMac::SelectAll(bool reversed) {
+ // TODO(shess): Figure out what reversed implies. The gtk version
+ // has it imply inverting the selection front to back, but I don't
+ // even know if that makes sense for Mac.
+ UpdateAndStyleText(GetText(), 0);
+}
+
+void AutocompleteEditViewMac::RevertAll() {
+ ClosePopup();
+ model_->Revert();
+
+ std::wstring tt = GetText();
+ UpdateAndStyleText(tt, tt.size());
+ controller_->OnChanged();
+}
+
+void AutocompleteEditViewMac::UpdatePopup() {
+ model_->SetInputInProgress(true);
+ if (!model_->has_focus())
+ return;
+
+ // TODO(shess):
+ // Shouldn't inline autocomplete when the caret/selection isn't at
+ // the end of the text.
+ //
+ // One option would seem to be to check for a non-nil field
+ // editor, and check it's selected range against its length.
+ model_->StartAutocomplete(false);
+}
+
+void AutocompleteEditViewMac::ClosePopup() {
+ popup_view_->StopAutocomplete();
+}
+
+void AutocompleteEditViewMac::UpdateAndStyleText(
+ const std::wstring& display_text, size_t user_text_length) {
+ NSString* ss = base::SysWideToNSString(display_text);
+ NSMutableAttributedString* as =
+ [[[NSMutableAttributedString alloc] initWithString:ss] autorelease];
+
+ url_parse::Parsed parts;
+ AutocompleteInput::Parse(display_text, model_->GetDesiredTLD(),
+ &parts, NULL);
+ bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0);
+ if (emphasize) {
+ // TODO(shess): Pull color out as a constant.
+ [as addAttribute:NSForegroundColorAttributeName
+ value:[NSColor greenColor]
+ range:NSMakeRange((NSInteger)parts.host.begin,
+ (NSInteger)parts.host.end())];
+ }
+
+ // TODO(shess): GTK has this as a member var, figure out why.
+ ToolbarModel::SecurityLevel scheme_security_level =
+ toolbar_model_->GetSchemeSecurityLevel();
+
+ // Emphasize the scheme for security UI display purposes (if necessary).
+ if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() &&
+ (scheme_security_level != ToolbarModel::NORMAL)) {
+ // TODO(shess): Pull colors out as constants.
+ NSColor* color;
+ if (scheme_security_level == ToolbarModel::SECURE) {
+ color = [NSColor blueColor];
+ } else {
+ color = [NSColor blackColor];
+ }
+ [as addAttribute:NSForegroundColorAttributeName value:color
+ range:NSMakeRange((NSInteger)parts.scheme.begin,
+ (NSInteger)parts.scheme.end())];
+ }
+
+ // TODO(shess): Check that this updates the model's sense of focus
+ // correctly.
+ [field_ setObjectValue:as];
+ if (![field_ currentEditor]) {
+ [field_ becomeFirstResponder];
+ DCHECK_EQ(field_, [[field_ window] firstResponder]);
+ }
+
+ NSRange selected_range = NSMakeRange(user_text_length, [as length]);
+ // TODO(shess): What if it didn't get first responder, and there is
+ // no field editor? This will do nothing. Well, at least it won't
+ // crash. Think of something more productive to do, or prove that
+ // it cannot occur and DCHECK appropriately.
+ [[field_ currentEditor] setSelectedRange:selected_range];
+}
+
+void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged(
+ const std::wstring& display_text, bool save_original_selection) {
+ // TODO(shess): I believe this is for when the user arrows around
+ // the popup, will be restored if they hit escape. Figure out if
+ // that is for certain it.
+ if (save_original_selection) {
+ saved_temporary_text_ = GetText();
+ }
+
+ UpdateAndStyleText(display_text, display_text.size());
+}
+
+bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged(
+ const std::wstring& display_text, size_t user_text_length) {
+ // TODO(shess): Make sure that this actually works. The round trip
+ // to native form and back may mean that it's the same but not the
+ // same.
+ if (display_text == GetText()) {
+ return false;
+ }
+
+ UpdateAndStyleText(display_text, user_text_length);
+ return true;
+}
+
+void AutocompleteEditViewMac::OnRevertTemporaryText() {
+ UpdateAndStyleText(saved_temporary_text_, saved_temporary_text_.size());
+ saved_temporary_text_.clear();
+}
+
+void AutocompleteEditViewMac::OnUpOrDownKeyPressed(int dir) {
+ model_->OnUpOrDownKeyPressed(dir);
+}
+void AutocompleteEditViewMac::OnEscapeKeyPressed() {
+ model_->OnEscapeKeyPressed();
+}
+void AutocompleteEditViewMac::OnSetFocus(bool f) {
+ model_->OnSetFocus(f);
+}
+void AutocompleteEditViewMac::OnKillFocus() {
+ model_->OnKillFocus();
+}
+void AutocompleteEditViewMac::AcceptInput(
+ WindowOpenDisposition disposition, bool for_drop) {
+ model_->AcceptInput(disposition, for_drop);
+}
+void AutocompleteEditViewMac::OnAfterPossibleChange(
+ const std::wstring& new_text,
+ bool selection_differs,
+ bool text_differs,
+ bool just_deleted_text,
+ bool at_end_of_edit) {
+ model_->OnAfterPossibleChange(new_text, selection_differs, text_differs,
+ just_deleted_text, at_end_of_edit);
+}
+void AutocompleteEditViewMac::SetField(NSTextField* field) {
+ field_ = field;
+ [field_ setDelegate:edit_helper_];
+
+ // The popup code needs the field for sizing and placement.
+ popup_view_->SetField(field_);
+}
+
+void AutocompleteEditViewMac::FocusLocation() {
+ [[field_ window] makeFirstResponder:field_];
+}
+
+@implementation AutocompleteFieldDelegate
+
+- initWithEditView:(AutocompleteEditViewMac*)view {
+ self = [super init];
+ if (self) {
+ edit_view_ = view;
+ }
+ return self;
+}
+
+- (BOOL)control:(NSControl*)control
+ textView:(NSTextView*)textView doCommandBySelector:(SEL)cmd {
+ if (cmd == @selector(moveDown:)) {
+ edit_view_->OnUpOrDownKeyPressed(1);
+ return YES;
+ }
+
+ if (cmd == @selector(moveUp:)) {
+ edit_view_->OnUpOrDownKeyPressed(-1);
+ return YES;
+ }
+
+ if (cmd == @selector(cancelOperation:)) {
+ edit_view_->OnEscapeKeyPressed();
+ return YES;
+ }
+
+ if (cmd == @selector(insertNewline:)) {
+ edit_view_->AcceptInput(CURRENT_TAB, false);
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)controlTextDidBeginEditing:(NSNotification*)aNotification {
+ edit_view_->OnSetFocus(false);
+}
+
+- (void)controlTextDidChange:(NSNotification*)aNotification {
+ // TODO(shess): Make this more efficient? Or not. For now, just
+ // pass in the current text, indicating that the text and
+ // selection differ, ignoring deletions, and assuming that we're
+ // at the end of the text.
+ edit_view_->OnAfterPossibleChange(edit_view_->GetText(),
+ true, true, false, true);
+}
+
+- (void)controlTextDidEndEditing:(NSNotification*)aNotification {
+ edit_view_->OnKillFocus();
+
+ // TODO(shess): Figure out where the selection belongs. On GTK,
+ // it's set to the start of the text.
+}
+
+@end