summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm
diff options
context:
space:
mode:
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