145802 - session history in Go menu for Chimera, 145803 - urlbar autocomplete for chimera

This commit is contained in:
hewitt%netscape.com 2002-05-28 09:07:53 +00:00
parent c58b0f7441
commit d8e9c91f64
60 changed files with 3352 additions and 280 deletions

View File

@ -67,13 +67,14 @@
};
SUPERCLASS = NSWindowController;
},
{CLASS = CHAutoCompleteDataSource; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
CLASS = CHAutoCompleteTableView;
ACTIONS = {onBlur = id; onResize = id; };
CLASS = CHAutoCompleteTextField;
LANGUAGE = ObjC;
OUTLETS = {mURLBar = NSTextField; mWindowController = BrowserWindowController; };
SUPERCLASS = NSTableView;
OUTLETS = {mProxyIcon = CHPageProxyIcon; };
SUPERCLASS = NSTextField;
},
{CLASS = CHAutoCompleteTextField; LANGUAGE = ObjC; SUPERCLASS = NSTextField; },
{
CLASS = CHBookmarksOutlineView;
LANGUAGE = ObjC;

View File

@ -22,10 +22,8 @@
<string>22 500 93 162 0 0 1152 746 </string>
<key>463</key>
<string>7 444 200 252 0 0 1152 746 </string>
<key>475</key>
<string>493 338 207 230 0 0 1152 746 </string>
<key>56</key>
<string>340 478 343 68 0 0 1024 746 </string>
<string>378 463 343 68 0 0 1152 746 </string>
</dict>
<key>IBFramework Version</key>
<string>248.0</string>
@ -43,6 +41,10 @@
<array>
<integer>497</integer>
</array>
<key>IBOpenObjects</key>
<array>
<integer>56</integer>
</array>
<key>IBSystem Version</key>
<string>5Q125</string>
</dict>

Binary file not shown.

View File

@ -21,13 +21,30 @@
* David Hyatt <hyatt@netscape.com> (Original Author)
*/
#import <Foundation/Foundation.h>
#include "nsIAutoCompleteSession.h"
#import <AppKit/AppKit.h>
#import "CHAutoCompleteTextField.h"
#include "nsIAutoCompleteResults.h"
@interface CHAutoCompleteDataSource : NSObject {
nsIAutoCompleteSession* mAutoComplete;
@class CHAutoCompleteTextField;
@interface CHAutoCompleteDataSource : NSObject
{
CHAutoCompleteTextField *mTextField;
NSImage *mIconImage;
NSString* mErrorMessage;
nsIAutoCompleteResults *mResults;
}
-(void)initialize;
- (id) init;
- (int) rowCount;
- (id) resultString:(int)aRow column:(NSString *)aColumn;
- (void) setErrorMessage: (NSString*) error;
- (NSString*) errorMessage;
- (void) setResults: (nsIAutoCompleteResults*) results;
- (nsIAutoCompleteResults*) results;
@end

View File

@ -19,41 +19,106 @@
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
*
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIServiceManager.h"
#import "CHAutoCompleteTextField.h"
#include "nsString.h"
#include "nsCRT.h"
@implementation CHAutoCompleteDataSource
-(id)init
{
if ((self = [super init])) {
mAutoComplete = nsnull;
mResults = nil;
mIconImage = [[NSImage imageNamed:@"globe_ico"] autorelease];
}
return self;
}
-(void)initialize
-(void)dealloc
{
if (!mAutoComplete) {
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mAutoComplete = session;
if (!mAutoComplete)
printf("Could not find autocomplete!\n");
}
NS_IF_RELEASE(mResults);
}
-(int)numberOfRowsInTableView:(NSTableView*)aTableView
- (void) setErrorMessage: (NSString*) error
{
return 0;
[self setResults:nsnull];
mErrorMessage = error;
}
- (NSString*) errorMessage
{
return mErrorMessage;
}
- (void) setResults:(nsIAutoCompleteResults*)aResults
{
NS_IF_RELEASE(mResults);
mErrorMessage = nil;
mResults = aResults;
NS_IF_ADDREF(mResults);
}
- (nsIAutoCompleteResults *) results
{
return mResults;
}
- (int) rowCount
{
if (!mResults)
return 0;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
PRUint32 count;
items->Count(&count);
return count;
}
- (id) resultString:(int)aRow column:(NSString *)aColumn
{
NSString *result = @"";
if (!mResults)
return result;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
nsCOMPtr<nsISupports> itemSupports = dont_AddRef(items->ElementAt(aRow));
nsCOMPtr<nsIAutoCompleteItem> item = do_QueryInterface(itemSupports);
if (!item)
return result;
if ([aColumn isEqualToString:@"icon"]) {
return mIconImage;
} else if ([aColumn isEqualToString:@"col1"]) {
nsAutoString value;
item->GetValue(value);
result = [NSString stringWithCharacters:value.get() length:value.Length()];
} else if ([aColumn isEqualToString:@"col2"]) {
PRUnichar *comment;
item->GetComment(&comment);
result = [NSString stringWithCharacters:comment length:nsCRT::strlen(comment)];
}
return result;
}
-(int) numberOfRowsInTableView:(NSTableView*)aTableView
{
return [self rowCount];
}
-(id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)aRowIndex
{
return @"";
return [self resultString:aRowIndex column:[aTableColumn identifier]];
}
@end

View File

@ -1,68 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
*/
#import "CHAutoCompleteTableView.h"
#import "CHAutoCompleteDataSource.h"
@implementation CHAutoCompleteTableView
-(id)initWithFrame:(NSRect)aRect
{
if ((self = [super initWithFrame: aRect])) {
// Create our data source.
mDataSource = [[CHAutoCompleteDataSource alloc] init];
[self setDataSource: mDataSource];
// Create the URL column.
NSTableColumn* urlColumn = [[[NSTableColumn alloc] initWithIdentifier:@"URL"] autorelease];
[self addTableColumn: urlColumn];
NSTableColumn* titleColumn = [[[NSTableColumn alloc] initWithIdentifier:@"Title"] autorelease];
[self addTableColumn: titleColumn];
}
return self;
}
-(void)dealloc
{
[mDataSource release];
[super dealloc];
}
-(BOOL)isShowing
{
return ([self superview] != nil);
}
-(void)controlTextDidChange:(NSNotification*)aNotification
{
// Ensure that our data source is initialized.
[mDataSource initialize];
// [[[[mWindowController window] contentView] superview] addSubview: self];
}
-(void)controlTextDidEndEditing:(NSNotification*)aNotification
{
}
@end

View File

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIAutoCompleteSession.h"
#include "nsIAutoCompleteResults.h"
#include "nsIAutoCompleteListener.h"
@class CHAutoCompleteDataSource, CHPageProxyIcon;
@interface CHAutoCompleteTextField : NSTextField
{
IBOutlet CHPageProxyIcon *mProxyIcon;
NSWindow *mPopupWin;
NSTableView *mTableView;
CHAutoCompleteDataSource *mDataSource;
nsIAutoCompleteSession *mSession;
nsIAutoCompleteResults *mResults;
nsIAutoCompleteListener *mListener;
NSString* mSearchString;
BOOL mBackspaced;
NSTimer *mOpenTimer;
}
- (void) setSession:(NSString *)aSession;
- (NSString *) session;
- (NSTableView *) tableView;
- (int) visibleRows;
- (void) startSearch:(NSString*)aString;
- (void) performSearch;
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus;
- (void) searchTimer:(NSTimer *)aTimer;
- (void) completeDefaultResult;
- (void) completeResult:(int)aRow;
- (void) enterResult:(int)aRow;
- (void) selectRowAt:(int)aRow;
- (void) selectRowBy:(int)aRows;
- (void) openPopup;
- (void) closePopup;
- (void) resizePopup;
- (BOOL) isOpen;
- (void) onBlur:(id)sender;
- (void) onResize:(id)sender;
@end

View File

@ -0,0 +1,447 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHAutoCompleteTextField.h"
#import "CHPageProxyIcon.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"
#include "nsString.h"
static const int kMaxRows = 6;
static const int kFrameMargin = 1;
class AutoCompleteListener : public nsIAutoCompleteListener
{
public:
AutoCompleteListener(CHAutoCompleteTextField* aTextField)
{
NS_INIT_REFCNT();
mTextField = aTextField;
}
NS_DECL_ISUPPORTS
NS_IMETHODIMP OnStatus(const PRUnichar* aText) { return NS_OK; }
NS_IMETHODIMP SetParam(nsISupports *aParam) { return NS_OK; }
NS_IMETHODIMP GetParam(nsISupports **aParam) { return NS_OK; }
NS_IMETHODIMP OnAutoComplete(nsIAutoCompleteResults *aResults, AutoCompleteStatus aStatus)
{
[mTextField dataReady:aResults status:aStatus];
return NS_OK;
}
private:
CHAutoCompleteTextField *mTextField;
};
NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener)
////////////////////////////////////////////////////////////////////////
@implementation CHAutoCompleteTextField
- (void) awakeFromNib
{
NSTableColumn *column;
NSScrollView *scrollView;
NSCell *dataCell;
mSearchString = nil;
mBackspaced = NO;
mOpenTimer = nil;
mSession = nsnull;
mResults = nsnull;
mListener = (nsIAutoCompleteListener *)new AutoCompleteListener(self);
NS_IF_ADDREF(mListener);
[self setDelegate: self];
// XXX the owner of the textfield should set this
[self setSession:@"history"];
// construct and configure the popup window
mPopupWin = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,0,0)
styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
[mPopupWin setReleasedWhenClosed:NO];
[mPopupWin setLevel:NSPopUpMenuWindowLevel];
[mPopupWin setHasShadow:YES];
[mPopupWin setAlphaValue:0.9];
// construct and configure the view
mTableView = [[[NSTableView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[mTableView setIntercellSpacing:NSMakeSize(1, 2)];
// Create the icon column if we have a proxy icon
if (mProxyIcon != nil) {
column = [[[NSTableColumn alloc] initWithIdentifier:@"icon"] autorelease];
[column setWidth:[mProxyIcon frame].origin.x + [mProxyIcon frame].size.width];
dataCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
[column setDataCell:dataCell];
[mTableView addTableColumn: column];
}
// create the text columns
column = [[[NSTableColumn alloc] initWithIdentifier:@"col1"] autorelease];
[mTableView addTableColumn: column];
column = [[[NSTableColumn alloc] initWithIdentifier:@"col2"] autorelease];
[[column dataCell] setTextColor:[NSColor darkGrayColor]];
[mTableView addTableColumn: column];
// hide the table header
[mTableView setHeaderView:nil];
// construct the scroll view that contains the table view
scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[scrollView setHasVerticalScroller:YES];
[[scrollView verticalScroller] setControlSize:NSSmallControlSize];
[scrollView setDocumentView: mTableView];
// construct the datasource
mDataSource = [[[CHAutoCompleteDataSource alloc] init] retain];
[mTableView setDataSource: mDataSource];
[mPopupWin setContentView:scrollView];
// listen for when window resigns from key handling
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowDidResignKeyNotification object:nil];
// listen for when window is about to be moved or resized
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowWillMoveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResize:)
name:NSWindowDidResizeNotification object:nil];
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mSearchString)
[mSearchString release];
[mDataSource release];
NS_IF_RELEASE(mSession);
NS_IF_RELEASE(mResults);
NS_IF_RELEASE(mListener);
}
- (void) setSession:(NSString *)aSession
{
NS_IF_RELEASE(mSession);
// XXX add aSession to contract id
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mSession = session;
NS_IF_ADDREF(mSession);
}
- (NSString *) session
{
// XXX return session name
return @"";
}
- (NSTableView *) tableView
{
return mTableView;
}
- (int) visibleRows
{
int minRows = [mDataSource rowCount];
return minRows < kMaxRows ? minRows : kMaxRows;
}
// searching ////////////////////////////
- (void) startSearch:(NSString*)aString
{
if (mSearchString)
[mSearchString release];
mSearchString = [aString retain];
if ([self isOpen]) {
[self performSearch];
} else {
// delay the search when the popup is not yet opened so that users
// don't see a jerky flashing popup when they start typing for the first time
if (mOpenTimer) {
[mOpenTimer invalidate];
[mOpenTimer release];
}
mOpenTimer = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(searchTimer:)
userInfo:nil repeats:NO] retain];
// we need to reset mBackspaced here, or else it might still be true if the user backspaces
// the textfield to be empty, then starts typing, because it is normally reset in selectRowAt
// which won't be called until perhaps after several keystrokes (due to the timer)
mBackspaced = NO;
}
}
- (void) performSearch
{
PRUnichar* chars = nsMemory::Alloc(([mSearchString length]+1) * sizeof(PRUnichar));
[mSearchString getCharacters:chars];
chars[[mSearchString length]] = 0; // I shouldn't have to do this
nsresult rv = mSession->OnStartLookup(chars, mResults, mListener);
nsMemory::Free(chars);
if (NS_FAILED(rv))
NSLog(@"Unable to perform autocomplete lookup");
}
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus
{
NS_IF_RELEASE(mResults);
mResults = nsnull;
if (aStatus == nsIAutoCompleteStatus::failed) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::ignored) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::noMatch) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::matchFound) {
mResults = aResults;
NS_IF_ADDREF(mResults);
[mDataSource setResults:aResults];
[self completeDefaultResult];
}
if ([mDataSource rowCount] > 0) {
[mTableView noteNumberOfRowsChanged];
[self openPopup];
} else {
[self closePopup];
}
}
- (void) searchTimer:(NSTimer *)aTimer
{
[mOpenTimer release];
mOpenTimer = nil;
[self performSearch];
}
// handling the popup /////////////////////////////////
- (void) openPopup
{
[self resizePopup];
// show the popup
if ([mPopupWin isVisible] == NO)
[mPopupWin orderFront:nil];
}
- (void) resizePopup
{
NSRect locationFrame, winFrame;
NSPoint locationOrigin;
int tableHeight;
// get the origin of the location bar in coordinates of the root view
locationFrame = [[self superview] frame];
locationOrigin = [[[self superview] superview] convertPoint:locationFrame.origin
toView:[[[self window] contentView] superview]];
// get the height of the table view
winFrame = [[self window] frame];
tableHeight = (int)([mTableView rowHeight]+[mTableView intercellSpacing].height)*[self visibleRows];
// make the columns split the width of the popup
[[mTableView tableColumnWithIdentifier:@"col1"] setWidth:locationFrame.size.width/2];
// position the popup anchored to bottom/left of location bar (
[mPopupWin setFrame:NSMakeRect(winFrame.origin.x + locationOrigin.x + kFrameMargin,
((winFrame.origin.y + locationOrigin.y) - tableHeight) - kFrameMargin,
locationFrame.size.width - (2*kFrameMargin),
tableHeight) display:NO];
}
- (void) closePopup
{
[mPopupWin close];
}
- (BOOL) isOpen
{
return [mPopupWin isVisible];
}
// url completion ////////////////////////////
- (void) completeDefaultResult
{
PRInt32 defaultRow;
mResults->GetDefaultItemIndex(&defaultRow);
if (mBackspaced) {
[self selectRowAt:-1];
mBackspaced = NO;
} else {
[self selectRowAt:defaultRow];
[self completeResult:defaultRow];
}
}
- (void) completeResult:(int)aRow
{
NSRange matchRange;
NSText *text;
NSString *result1;
if (aRow < 0) {
[self setStringValue:mSearchString];
} else {
if ([mDataSource rowCount] <= 0)
return;
result1 = [mDataSource resultString:aRow column:@"col1"];
matchRange = [result1 rangeOfString:mSearchString];
if (matchRange.length > 0) {
// cut off everything in the result string before the search string
result1 = [result1 substringWithRange:NSMakeRange(matchRange.location, [result1 length]-matchRange.location)];
// fill in the textfield with the matching string
[self setStringValue:result1];
// select the text after the search string
text = [[self window] fieldEditor:NO forObject:self];
[text setSelectedRange:NSMakeRange([mSearchString length], [result1 length]-[mSearchString length])];
}
}
}
- (void) enterResult:(int)aRow
{
if ([self isOpen] && aRow >= 0) {
[self setStringValue: [mDataSource resultString:[mTableView selectedRow] column:@"col1"]];
}
}
// selecting rows /////////////////////////////////////////
- (void) selectRowAt:(int)aRow
{
[mTableView selectRow:aRow byExtendingSelection:NO];
[mTableView scrollRowToVisible: aRow];
}
- (void) selectRowBy:(int)aRows
{
int row = [mTableView selectedRow];
if (row == -1 && aRows < 0) {
// if nothing is selected and you scroll up, go to last row
row = [mTableView numberOfRows]-1;
} else if (row == [mTableView numberOfRows]-1 && aRows == 1) {
// if the last row is selected and you scroll down, go to first row
row = 0;
} else if (aRows+row < 0) {
// if you scroll up beyond first row...
if (row == 0)
row = -1; // ...and first row is selected, select nothing
else
row = 0; // ...else, go to first row
} else if (aRows+row >= [mTableView numberOfRows]) {
// if you scroll down beyond the last row...
if (row == [mTableView numberOfRows]-1)
row = 0; // and last row is selected, select first row
else
row = [mTableView numberOfRows]-1; // else, go to last row
} else {
// no special case, just increment current row
row += aRows;
}
[self selectRowAt:row];
}
// event handlers ////////////////////////////////////////////
- (void) onBlur:(id)sender
{
[self closePopup];
}
- (void) onResize:(id)sender
{
[self resizePopup];
}
// NSTextField ////////////////////////////////////////////
- (void)controlTextDidChange:(NSNotification *)aNotification
{
[self startSearch:[self stringValue]];
}
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
[self closePopup];
}
// NSTextField delegate //////////////////////////////////
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
{
if (command == @selector(insertNewline:)) {
[self enterResult:[mTableView selectedRow]];
} else if (command == @selector(moveUp:)) {
[self selectRowBy:-1];
return YES;
} else if (command == @selector(moveDown:)) {
[self selectRowBy:1];
return YES;
} else if (command == @selector(scrollPageUp:)) {
[self selectRowBy:-kMaxRows];
} else if (command == @selector(scrollPageDown:)) {
[self selectRowBy:kMaxRows];
} else if (command == @selector(moveToBeginningOfDocument:)) {
[self selectRowAt:0];
} else if (command == @selector(moveToEndOfDocument:)) {
[self selectRowAt:[mTableView numberOfRows]-1];
} else if (command == @selector(insertTab:) || command == @selector(insertNewline:)) {
[self closePopup];
} else if (command == @selector(deleteBackward:) ||
command == @selector(deleteForward:)) {
// if the user deletes characters, we need to know so that
// we can prevent autocompletion later when search results come in
if ([[self stringValue] length] > 1)
mBackspaced = YES;
}
return NO;
}
@end

View File

@ -18,19 +18,19 @@
* Rights Reserved.
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <AppKit/AppKit.h>
#import "BrowserWindowController.h"
#import "CHAutoCompleteDataSource.h"
#import <Appkit/Appkit.h>
@interface CHGoMenu : NSMenu {
@interface CHAutoCompleteTableView : NSTableView {
IBOutlet NSTextField* mURLBar;
IBOutlet BrowserWindowController* mWindowController;
CHAutoCompleteDataSource* mDataSource;
}
- (BOOL)isShowing;
- (void) emptyHistoryItems;
- (void) addHistoryItems;
// NSMenu
- (void) update;
@end

147
camino/CHGoMenu.mm Normal file
View File

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHGoMenu.h"
#import "BrowserWindowController.h"
#import "CHBrowserWrapper.h"
#import "CHBrowserView.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIWebBrowser.h"
#include "nsISHistory.h"
#include "nsIWebNavigation.h"
#include "nsIHistoryEntry.h"
#include "nsCRT.h"
// the tag of the separator after which to insert history menu items
static const int kDividerTag = 4000;
// the maximum number of history entry menuitems to display
static const int kMaxItems = 15;
// the maximum number of characters in a menu title before cropping it
static const int kMaxTitleLength = 20;
@implementation CHGoMenu
- (void) update
{
[self emptyHistoryItems];
[self addHistoryItems];
[super update];
}
- (nsIWebNavigation*) currentWebNavigation
{
// get controller for current window
BrowserWindowController *controller;
controller = (BrowserWindowController*)[[NSApp mainWindow] windowController];
if (!controller) return nsnull;
// get web navigation for current browser
CHBrowserWrapper* wrapper = [controller getBrowserWrapper];
if (!wrapper) return nsnull;
CHBrowserView* view = [wrapper getBrowserView];
if (!view) return nsnull;
nsCOMPtr<nsIWebBrowser> webBrowser = [view getWebBrowser];
if (!webBrowser) return nsnull;
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(webBrowser));
return webNav.get();
}
- (void) historyItemAction:(id)sender
{
// get web navigation for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
// browse to the history entry for the menuitem that was selected
PRInt32 historyIndex = ([sender tag] - 1) - kDividerTag;
webNav->GotoIndex(historyIndex);
}
- (void) emptyHistoryItems
{
// remove every history item after the insertion point
int insertionIndex = [self indexOfItemWithTag:kDividerTag];
for (int i = [self numberOfItems]-1; i > insertionIndex ; --i) {
[self removeItemAtIndex:i];
}
}
- (void) addHistoryItems
{
// get session history for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
nsCOMPtr<nsISHistory> sessionHistory;
webNav->GetSessionHistory(getter_AddRefs(sessionHistory));
PRInt32 count;
sessionHistory->GetCount(&count);
PRInt32 currentIndex;
sessionHistory->GetIndex(&currentIndex);
// determine the range of items to display
int rangeStart, rangeEnd;
int above = kMaxItems/2;
int below = (kMaxItems-above)-1;
if (count <= kMaxItems) {
// if the whole history fits within our menu, fit range to show all
rangeStart = count-1;
rangeEnd = 0;
} else {
// if the whole history is too large for menu, try to put current index as close to
// the middle of the list as possible, so the user can see both back and forward in session
rangeStart = currentIndex + above;
rangeEnd = currentIndex - below;
if (rangeStart >= count-1) {
rangeEnd -= (rangeStart-count)+1; // shift overflow to the end
rangeStart = count-1;
} else if (rangeEnd < 0) {
rangeStart -= rangeEnd; // shift overflow to the start
rangeEnd = 0;
}
}
// create a new menu item for each history entry (up to MAX_MENUITEM entries)
for (PRInt32 i = rangeStart; i >= rangeEnd; --i) {
nsCOMPtr<nsIHistoryEntry> entry;
sessionHistory->GetEntryAtIndex(i, PR_FALSE, getter_AddRefs(entry));
PRUnichar *text;
entry->GetTitle(&text);
NSString* title = [NSString stringWithCharacters: text length: nsCRT::strlen(text)];
if ([title length] > kMaxTitleLength) {
}
NSMenuItem *newItem = [self addItemWithTitle:title action:@selector(historyItemAction:) keyEquivalent:@""];
[newItem setTarget:self];
[newItem setTag:kDividerTag+1+i];
if (currentIndex == i)
[newItem setState:NSOnState];
}
}
@end

View File

@ -27,6 +27,7 @@
F52D5CD4027A88C601A80166,
F52F87CD027D75C301A80165,
F52F87CE027D75C301A80165,
2E748B73029A448D4B000102,
);
isa = PBXGroup;
name = Classes;
@ -409,6 +410,7 @@
F52F87D0027D75C301A80165,
2E293A01027F33604B000102,
2EEC3E63028138724B000102,
2E748B74029A448D4B000102,
);
isa = PBXHeadersBuildPhase;
name = Headers;
@ -510,6 +512,7 @@
F52F87D2027D75C301A80165,
2E293A02027F33604B000102,
2EEC3E64028138724B000102,
2E748B75029A448D4B000102,
);
isa = PBXSourcesBuildPhase;
name = Sources;
@ -569,6 +572,28 @@
settings = {
};
};
2E748B72029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.h;
refType = 4;
};
2E748B73029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.mm;
refType = 4;
};
2E748B74029A448D4B000102 = {
fileRef = 2E748B72029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2E748B75029A448D4B000102 = {
fileRef = 2E748B73029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2EEC3E6002810B184B000102 = {
indentWidth = 2;
isa = PBXFileReference;
@ -1009,6 +1034,7 @@
F52D5CD3027A88C601A80166,
F52F87CB027D75C301A80165,
F52F87CC027D75C301A80165,
2E748B72029A448D4B000102,
);
isa = PBXGroup;
name = Headers;
@ -1483,12 +1509,12 @@
};
F52D5CCF027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.h;
path = CHAutoCompleteTextField.h;
refType = 4;
};
F52D5CD0027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.mm;
path = CHAutoCompleteTextField.mm;
refType = 4;
};
F52D5CD1027A887001A80166 = {

View File

@ -67,13 +67,14 @@
};
SUPERCLASS = NSWindowController;
},
{CLASS = CHAutoCompleteDataSource; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
CLASS = CHAutoCompleteTableView;
ACTIONS = {onBlur = id; onResize = id; };
CLASS = CHAutoCompleteTextField;
LANGUAGE = ObjC;
OUTLETS = {mURLBar = NSTextField; mWindowController = BrowserWindowController; };
SUPERCLASS = NSTableView;
OUTLETS = {mProxyIcon = CHPageProxyIcon; };
SUPERCLASS = NSTextField;
},
{CLASS = CHAutoCompleteTextField; LANGUAGE = ObjC; SUPERCLASS = NSTextField; },
{
CLASS = CHBookmarksOutlineView;
LANGUAGE = ObjC;

View File

@ -22,10 +22,8 @@
<string>22 500 93 162 0 0 1152 746 </string>
<key>463</key>
<string>7 444 200 252 0 0 1152 746 </string>
<key>475</key>
<string>493 338 207 230 0 0 1152 746 </string>
<key>56</key>
<string>340 478 343 68 0 0 1024 746 </string>
<string>378 463 343 68 0 0 1152 746 </string>
</dict>
<key>IBFramework Version</key>
<string>248.0</string>
@ -43,6 +41,10 @@
<array>
<integer>497</integer>
</array>
<key>IBOpenObjects</key>
<array>
<integer>56</integer>
</array>
<key>IBSystem Version</key>
<string>5Q125</string>
</dict>

Binary file not shown.

View File

@ -5,6 +5,7 @@
LANGUAGE = ObjC;
SUPERCLASS = ToolbarController;
},
{CLASS = CHGoMenu; LANGUAGE = ObjC; SUPERCLASS = NSMenu; },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
ACTIONS = {

Binary file not shown.

View File

@ -5,6 +5,7 @@
LANGUAGE = ObjC;
SUPERCLASS = ToolbarController;
},
{CLASS = CHGoMenu; LANGUAGE = ObjC; SUPERCLASS = NSMenu; },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
ACTIONS = {

Binary file not shown.

View File

@ -27,6 +27,7 @@
F52D5CD4027A88C601A80166,
F52F87CD027D75C301A80165,
F52F87CE027D75C301A80165,
2E748B73029A448D4B000102,
);
isa = PBXGroup;
name = Classes;
@ -409,6 +410,7 @@
F52F87D0027D75C301A80165,
2E293A01027F33604B000102,
2EEC3E63028138724B000102,
2E748B74029A448D4B000102,
);
isa = PBXHeadersBuildPhase;
name = Headers;
@ -510,6 +512,7 @@
F52F87D2027D75C301A80165,
2E293A02027F33604B000102,
2EEC3E64028138724B000102,
2E748B75029A448D4B000102,
);
isa = PBXSourcesBuildPhase;
name = Sources;
@ -569,6 +572,28 @@
settings = {
};
};
2E748B72029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.h;
refType = 4;
};
2E748B73029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.mm;
refType = 4;
};
2E748B74029A448D4B000102 = {
fileRef = 2E748B72029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2E748B75029A448D4B000102 = {
fileRef = 2E748B73029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2EEC3E6002810B184B000102 = {
indentWidth = 2;
isa = PBXFileReference;
@ -1009,6 +1034,7 @@
F52D5CD3027A88C601A80166,
F52F87CB027D75C301A80165,
F52F87CC027D75C301A80165,
2E748B72029A448D4B000102,
);
isa = PBXGroup;
name = Headers;
@ -1483,12 +1509,12 @@
};
F52D5CCF027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.h;
path = CHAutoCompleteTextField.h;
refType = 4;
};
F52D5CD0027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.mm;
path = CHAutoCompleteTextField.mm;
refType = 4;
};
F52D5CD1027A887001A80166 = {

View File

@ -67,13 +67,14 @@
};
SUPERCLASS = NSWindowController;
},
{CLASS = CHAutoCompleteDataSource; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
CLASS = CHAutoCompleteTableView;
ACTIONS = {onBlur = id; onResize = id; };
CLASS = CHAutoCompleteTextField;
LANGUAGE = ObjC;
OUTLETS = {mURLBar = NSTextField; mWindowController = BrowserWindowController; };
SUPERCLASS = NSTableView;
OUTLETS = {mProxyIcon = CHPageProxyIcon; };
SUPERCLASS = NSTextField;
},
{CLASS = CHAutoCompleteTextField; LANGUAGE = ObjC; SUPERCLASS = NSTextField; },
{
CLASS = CHBookmarksOutlineView;
LANGUAGE = ObjC;

View File

@ -22,10 +22,8 @@
<string>22 500 93 162 0 0 1152 746 </string>
<key>463</key>
<string>7 444 200 252 0 0 1152 746 </string>
<key>475</key>
<string>493 338 207 230 0 0 1152 746 </string>
<key>56</key>
<string>340 478 343 68 0 0 1024 746 </string>
<string>378 463 343 68 0 0 1152 746 </string>
</dict>
<key>IBFramework Version</key>
<string>248.0</string>
@ -43,6 +41,10 @@
<array>
<integer>497</integer>
</array>
<key>IBOpenObjects</key>
<array>
<integer>56</integer>
</array>
<key>IBSystem Version</key>
<string>5Q125</string>
</dict>

View File

@ -5,6 +5,7 @@
LANGUAGE = ObjC;
SUPERCLASS = ToolbarController;
},
{CLASS = CHGoMenu; LANGUAGE = ObjC; SUPERCLASS = NSMenu; },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
ACTIONS = {

View File

@ -21,13 +21,30 @@
* David Hyatt <hyatt@netscape.com> (Original Author)
*/
#import <Foundation/Foundation.h>
#include "nsIAutoCompleteSession.h"
#import <AppKit/AppKit.h>
#import "CHAutoCompleteTextField.h"
#include "nsIAutoCompleteResults.h"
@interface CHAutoCompleteDataSource : NSObject {
nsIAutoCompleteSession* mAutoComplete;
@class CHAutoCompleteTextField;
@interface CHAutoCompleteDataSource : NSObject
{
CHAutoCompleteTextField *mTextField;
NSImage *mIconImage;
NSString* mErrorMessage;
nsIAutoCompleteResults *mResults;
}
-(void)initialize;
- (id) init;
- (int) rowCount;
- (id) resultString:(int)aRow column:(NSString *)aColumn;
- (void) setErrorMessage: (NSString*) error;
- (NSString*) errorMessage;
- (void) setResults: (nsIAutoCompleteResults*) results;
- (nsIAutoCompleteResults*) results;
@end

View File

@ -19,41 +19,106 @@
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
*
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIServiceManager.h"
#import "CHAutoCompleteTextField.h"
#include "nsString.h"
#include "nsCRT.h"
@implementation CHAutoCompleteDataSource
-(id)init
{
if ((self = [super init])) {
mAutoComplete = nsnull;
mResults = nil;
mIconImage = [[NSImage imageNamed:@"globe_ico"] autorelease];
}
return self;
}
-(void)initialize
-(void)dealloc
{
if (!mAutoComplete) {
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mAutoComplete = session;
if (!mAutoComplete)
printf("Could not find autocomplete!\n");
}
NS_IF_RELEASE(mResults);
}
-(int)numberOfRowsInTableView:(NSTableView*)aTableView
- (void) setErrorMessage: (NSString*) error
{
return 0;
[self setResults:nsnull];
mErrorMessage = error;
}
- (NSString*) errorMessage
{
return mErrorMessage;
}
- (void) setResults:(nsIAutoCompleteResults*)aResults
{
NS_IF_RELEASE(mResults);
mErrorMessage = nil;
mResults = aResults;
NS_IF_ADDREF(mResults);
}
- (nsIAutoCompleteResults *) results
{
return mResults;
}
- (int) rowCount
{
if (!mResults)
return 0;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
PRUint32 count;
items->Count(&count);
return count;
}
- (id) resultString:(int)aRow column:(NSString *)aColumn
{
NSString *result = @"";
if (!mResults)
return result;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
nsCOMPtr<nsISupports> itemSupports = dont_AddRef(items->ElementAt(aRow));
nsCOMPtr<nsIAutoCompleteItem> item = do_QueryInterface(itemSupports);
if (!item)
return result;
if ([aColumn isEqualToString:@"icon"]) {
return mIconImage;
} else if ([aColumn isEqualToString:@"col1"]) {
nsAutoString value;
item->GetValue(value);
result = [NSString stringWithCharacters:value.get() length:value.Length()];
} else if ([aColumn isEqualToString:@"col2"]) {
PRUnichar *comment;
item->GetComment(&comment);
result = [NSString stringWithCharacters:comment length:nsCRT::strlen(comment)];
}
return result;
}
-(int) numberOfRowsInTableView:(NSTableView*)aTableView
{
return [self rowCount];
}
-(id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)aRowIndex
{
return @"";
return [self resultString:aRowIndex column:[aTableColumn identifier]];
}
@end

View File

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIAutoCompleteSession.h"
#include "nsIAutoCompleteResults.h"
#include "nsIAutoCompleteListener.h"
@class CHAutoCompleteDataSource, CHPageProxyIcon;
@interface CHAutoCompleteTextField : NSTextField
{
IBOutlet CHPageProxyIcon *mProxyIcon;
NSWindow *mPopupWin;
NSTableView *mTableView;
CHAutoCompleteDataSource *mDataSource;
nsIAutoCompleteSession *mSession;
nsIAutoCompleteResults *mResults;
nsIAutoCompleteListener *mListener;
NSString* mSearchString;
BOOL mBackspaced;
NSTimer *mOpenTimer;
}
- (void) setSession:(NSString *)aSession;
- (NSString *) session;
- (NSTableView *) tableView;
- (int) visibleRows;
- (void) startSearch:(NSString*)aString;
- (void) performSearch;
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus;
- (void) searchTimer:(NSTimer *)aTimer;
- (void) completeDefaultResult;
- (void) completeResult:(int)aRow;
- (void) enterResult:(int)aRow;
- (void) selectRowAt:(int)aRow;
- (void) selectRowBy:(int)aRows;
- (void) openPopup;
- (void) closePopup;
- (void) resizePopup;
- (BOOL) isOpen;
- (void) onBlur:(id)sender;
- (void) onResize:(id)sender;
@end

View File

@ -0,0 +1,447 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHAutoCompleteTextField.h"
#import "CHPageProxyIcon.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"
#include "nsString.h"
static const int kMaxRows = 6;
static const int kFrameMargin = 1;
class AutoCompleteListener : public nsIAutoCompleteListener
{
public:
AutoCompleteListener(CHAutoCompleteTextField* aTextField)
{
NS_INIT_REFCNT();
mTextField = aTextField;
}
NS_DECL_ISUPPORTS
NS_IMETHODIMP OnStatus(const PRUnichar* aText) { return NS_OK; }
NS_IMETHODIMP SetParam(nsISupports *aParam) { return NS_OK; }
NS_IMETHODIMP GetParam(nsISupports **aParam) { return NS_OK; }
NS_IMETHODIMP OnAutoComplete(nsIAutoCompleteResults *aResults, AutoCompleteStatus aStatus)
{
[mTextField dataReady:aResults status:aStatus];
return NS_OK;
}
private:
CHAutoCompleteTextField *mTextField;
};
NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener)
////////////////////////////////////////////////////////////////////////
@implementation CHAutoCompleteTextField
- (void) awakeFromNib
{
NSTableColumn *column;
NSScrollView *scrollView;
NSCell *dataCell;
mSearchString = nil;
mBackspaced = NO;
mOpenTimer = nil;
mSession = nsnull;
mResults = nsnull;
mListener = (nsIAutoCompleteListener *)new AutoCompleteListener(self);
NS_IF_ADDREF(mListener);
[self setDelegate: self];
// XXX the owner of the textfield should set this
[self setSession:@"history"];
// construct and configure the popup window
mPopupWin = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,0,0)
styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
[mPopupWin setReleasedWhenClosed:NO];
[mPopupWin setLevel:NSPopUpMenuWindowLevel];
[mPopupWin setHasShadow:YES];
[mPopupWin setAlphaValue:0.9];
// construct and configure the view
mTableView = [[[NSTableView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[mTableView setIntercellSpacing:NSMakeSize(1, 2)];
// Create the icon column if we have a proxy icon
if (mProxyIcon != nil) {
column = [[[NSTableColumn alloc] initWithIdentifier:@"icon"] autorelease];
[column setWidth:[mProxyIcon frame].origin.x + [mProxyIcon frame].size.width];
dataCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
[column setDataCell:dataCell];
[mTableView addTableColumn: column];
}
// create the text columns
column = [[[NSTableColumn alloc] initWithIdentifier:@"col1"] autorelease];
[mTableView addTableColumn: column];
column = [[[NSTableColumn alloc] initWithIdentifier:@"col2"] autorelease];
[[column dataCell] setTextColor:[NSColor darkGrayColor]];
[mTableView addTableColumn: column];
// hide the table header
[mTableView setHeaderView:nil];
// construct the scroll view that contains the table view
scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[scrollView setHasVerticalScroller:YES];
[[scrollView verticalScroller] setControlSize:NSSmallControlSize];
[scrollView setDocumentView: mTableView];
// construct the datasource
mDataSource = [[[CHAutoCompleteDataSource alloc] init] retain];
[mTableView setDataSource: mDataSource];
[mPopupWin setContentView:scrollView];
// listen for when window resigns from key handling
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowDidResignKeyNotification object:nil];
// listen for when window is about to be moved or resized
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowWillMoveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResize:)
name:NSWindowDidResizeNotification object:nil];
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mSearchString)
[mSearchString release];
[mDataSource release];
NS_IF_RELEASE(mSession);
NS_IF_RELEASE(mResults);
NS_IF_RELEASE(mListener);
}
- (void) setSession:(NSString *)aSession
{
NS_IF_RELEASE(mSession);
// XXX add aSession to contract id
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mSession = session;
NS_IF_ADDREF(mSession);
}
- (NSString *) session
{
// XXX return session name
return @"";
}
- (NSTableView *) tableView
{
return mTableView;
}
- (int) visibleRows
{
int minRows = [mDataSource rowCount];
return minRows < kMaxRows ? minRows : kMaxRows;
}
// searching ////////////////////////////
- (void) startSearch:(NSString*)aString
{
if (mSearchString)
[mSearchString release];
mSearchString = [aString retain];
if ([self isOpen]) {
[self performSearch];
} else {
// delay the search when the popup is not yet opened so that users
// don't see a jerky flashing popup when they start typing for the first time
if (mOpenTimer) {
[mOpenTimer invalidate];
[mOpenTimer release];
}
mOpenTimer = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(searchTimer:)
userInfo:nil repeats:NO] retain];
// we need to reset mBackspaced here, or else it might still be true if the user backspaces
// the textfield to be empty, then starts typing, because it is normally reset in selectRowAt
// which won't be called until perhaps after several keystrokes (due to the timer)
mBackspaced = NO;
}
}
- (void) performSearch
{
PRUnichar* chars = nsMemory::Alloc(([mSearchString length]+1) * sizeof(PRUnichar));
[mSearchString getCharacters:chars];
chars[[mSearchString length]] = 0; // I shouldn't have to do this
nsresult rv = mSession->OnStartLookup(chars, mResults, mListener);
nsMemory::Free(chars);
if (NS_FAILED(rv))
NSLog(@"Unable to perform autocomplete lookup");
}
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus
{
NS_IF_RELEASE(mResults);
mResults = nsnull;
if (aStatus == nsIAutoCompleteStatus::failed) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::ignored) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::noMatch) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::matchFound) {
mResults = aResults;
NS_IF_ADDREF(mResults);
[mDataSource setResults:aResults];
[self completeDefaultResult];
}
if ([mDataSource rowCount] > 0) {
[mTableView noteNumberOfRowsChanged];
[self openPopup];
} else {
[self closePopup];
}
}
- (void) searchTimer:(NSTimer *)aTimer
{
[mOpenTimer release];
mOpenTimer = nil;
[self performSearch];
}
// handling the popup /////////////////////////////////
- (void) openPopup
{
[self resizePopup];
// show the popup
if ([mPopupWin isVisible] == NO)
[mPopupWin orderFront:nil];
}
- (void) resizePopup
{
NSRect locationFrame, winFrame;
NSPoint locationOrigin;
int tableHeight;
// get the origin of the location bar in coordinates of the root view
locationFrame = [[self superview] frame];
locationOrigin = [[[self superview] superview] convertPoint:locationFrame.origin
toView:[[[self window] contentView] superview]];
// get the height of the table view
winFrame = [[self window] frame];
tableHeight = (int)([mTableView rowHeight]+[mTableView intercellSpacing].height)*[self visibleRows];
// make the columns split the width of the popup
[[mTableView tableColumnWithIdentifier:@"col1"] setWidth:locationFrame.size.width/2];
// position the popup anchored to bottom/left of location bar (
[mPopupWin setFrame:NSMakeRect(winFrame.origin.x + locationOrigin.x + kFrameMargin,
((winFrame.origin.y + locationOrigin.y) - tableHeight) - kFrameMargin,
locationFrame.size.width - (2*kFrameMargin),
tableHeight) display:NO];
}
- (void) closePopup
{
[mPopupWin close];
}
- (BOOL) isOpen
{
return [mPopupWin isVisible];
}
// url completion ////////////////////////////
- (void) completeDefaultResult
{
PRInt32 defaultRow;
mResults->GetDefaultItemIndex(&defaultRow);
if (mBackspaced) {
[self selectRowAt:-1];
mBackspaced = NO;
} else {
[self selectRowAt:defaultRow];
[self completeResult:defaultRow];
}
}
- (void) completeResult:(int)aRow
{
NSRange matchRange;
NSText *text;
NSString *result1;
if (aRow < 0) {
[self setStringValue:mSearchString];
} else {
if ([mDataSource rowCount] <= 0)
return;
result1 = [mDataSource resultString:aRow column:@"col1"];
matchRange = [result1 rangeOfString:mSearchString];
if (matchRange.length > 0) {
// cut off everything in the result string before the search string
result1 = [result1 substringWithRange:NSMakeRange(matchRange.location, [result1 length]-matchRange.location)];
// fill in the textfield with the matching string
[self setStringValue:result1];
// select the text after the search string
text = [[self window] fieldEditor:NO forObject:self];
[text setSelectedRange:NSMakeRange([mSearchString length], [result1 length]-[mSearchString length])];
}
}
}
- (void) enterResult:(int)aRow
{
if ([self isOpen] && aRow >= 0) {
[self setStringValue: [mDataSource resultString:[mTableView selectedRow] column:@"col1"]];
}
}
// selecting rows /////////////////////////////////////////
- (void) selectRowAt:(int)aRow
{
[mTableView selectRow:aRow byExtendingSelection:NO];
[mTableView scrollRowToVisible: aRow];
}
- (void) selectRowBy:(int)aRows
{
int row = [mTableView selectedRow];
if (row == -1 && aRows < 0) {
// if nothing is selected and you scroll up, go to last row
row = [mTableView numberOfRows]-1;
} else if (row == [mTableView numberOfRows]-1 && aRows == 1) {
// if the last row is selected and you scroll down, go to first row
row = 0;
} else if (aRows+row < 0) {
// if you scroll up beyond first row...
if (row == 0)
row = -1; // ...and first row is selected, select nothing
else
row = 0; // ...else, go to first row
} else if (aRows+row >= [mTableView numberOfRows]) {
// if you scroll down beyond the last row...
if (row == [mTableView numberOfRows]-1)
row = 0; // and last row is selected, select first row
else
row = [mTableView numberOfRows]-1; // else, go to last row
} else {
// no special case, just increment current row
row += aRows;
}
[self selectRowAt:row];
}
// event handlers ////////////////////////////////////////////
- (void) onBlur:(id)sender
{
[self closePopup];
}
- (void) onResize:(id)sender
{
[self resizePopup];
}
// NSTextField ////////////////////////////////////////////
- (void)controlTextDidChange:(NSNotification *)aNotification
{
[self startSearch:[self stringValue]];
}
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
[self closePopup];
}
// NSTextField delegate //////////////////////////////////
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
{
if (command == @selector(insertNewline:)) {
[self enterResult:[mTableView selectedRow]];
} else if (command == @selector(moveUp:)) {
[self selectRowBy:-1];
return YES;
} else if (command == @selector(moveDown:)) {
[self selectRowBy:1];
return YES;
} else if (command == @selector(scrollPageUp:)) {
[self selectRowBy:-kMaxRows];
} else if (command == @selector(scrollPageDown:)) {
[self selectRowBy:kMaxRows];
} else if (command == @selector(moveToBeginningOfDocument:)) {
[self selectRowAt:0];
} else if (command == @selector(moveToEndOfDocument:)) {
[self selectRowAt:[mTableView numberOfRows]-1];
} else if (command == @selector(insertTab:) || command == @selector(insertNewline:)) {
[self closePopup];
} else if (command == @selector(deleteBackward:) ||
command == @selector(deleteForward:)) {
// if the user deletes characters, we need to know so that
// we can prevent autocompletion later when search results come in
if ([[self stringValue] length] > 1)
mBackspaced = YES;
}
return NO;
}
@end

View File

@ -18,19 +18,19 @@
* Rights Reserved.
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <AppKit/AppKit.h>
#import "BrowserWindowController.h"
#import "CHAutoCompleteDataSource.h"
#import <Appkit/Appkit.h>
@interface CHGoMenu : NSMenu {
@interface CHAutoCompleteTableView : NSTableView {
IBOutlet NSTextField* mURLBar;
IBOutlet BrowserWindowController* mWindowController;
CHAutoCompleteDataSource* mDataSource;
}
- (BOOL)isShowing;
- (void) emptyHistoryItems;
- (void) addHistoryItems;
// NSMenu
- (void) update;
@end

View File

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHGoMenu.h"
#import "BrowserWindowController.h"
#import "CHBrowserWrapper.h"
#import "CHBrowserView.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIWebBrowser.h"
#include "nsISHistory.h"
#include "nsIWebNavigation.h"
#include "nsIHistoryEntry.h"
#include "nsCRT.h"
// the tag of the separator after which to insert history menu items
static const int kDividerTag = 4000;
// the maximum number of history entry menuitems to display
static const int kMaxItems = 15;
// the maximum number of characters in a menu title before cropping it
static const int kMaxTitleLength = 20;
@implementation CHGoMenu
- (void) update
{
[self emptyHistoryItems];
[self addHistoryItems];
[super update];
}
- (nsIWebNavigation*) currentWebNavigation
{
// get controller for current window
BrowserWindowController *controller;
controller = (BrowserWindowController*)[[NSApp mainWindow] windowController];
if (!controller) return nsnull;
// get web navigation for current browser
CHBrowserWrapper* wrapper = [controller getBrowserWrapper];
if (!wrapper) return nsnull;
CHBrowserView* view = [wrapper getBrowserView];
if (!view) return nsnull;
nsCOMPtr<nsIWebBrowser> webBrowser = [view getWebBrowser];
if (!webBrowser) return nsnull;
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(webBrowser));
return webNav.get();
}
- (void) historyItemAction:(id)sender
{
// get web navigation for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
// browse to the history entry for the menuitem that was selected
PRInt32 historyIndex = ([sender tag] - 1) - kDividerTag;
webNav->GotoIndex(historyIndex);
}
- (void) emptyHistoryItems
{
// remove every history item after the insertion point
int insertionIndex = [self indexOfItemWithTag:kDividerTag];
for (int i = [self numberOfItems]-1; i > insertionIndex ; --i) {
[self removeItemAtIndex:i];
}
}
- (void) addHistoryItems
{
// get session history for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
nsCOMPtr<nsISHistory> sessionHistory;
webNav->GetSessionHistory(getter_AddRefs(sessionHistory));
PRInt32 count;
sessionHistory->GetCount(&count);
PRInt32 currentIndex;
sessionHistory->GetIndex(&currentIndex);
// determine the range of items to display
int rangeStart, rangeEnd;
int above = kMaxItems/2;
int below = (kMaxItems-above)-1;
if (count <= kMaxItems) {
// if the whole history fits within our menu, fit range to show all
rangeStart = count-1;
rangeEnd = 0;
} else {
// if the whole history is too large for menu, try to put current index as close to
// the middle of the list as possible, so the user can see both back and forward in session
rangeStart = currentIndex + above;
rangeEnd = currentIndex - below;
if (rangeStart >= count-1) {
rangeEnd -= (rangeStart-count)+1; // shift overflow to the end
rangeStart = count-1;
} else if (rangeEnd < 0) {
rangeStart -= rangeEnd; // shift overflow to the start
rangeEnd = 0;
}
}
// create a new menu item for each history entry (up to MAX_MENUITEM entries)
for (PRInt32 i = rangeStart; i >= rangeEnd; --i) {
nsCOMPtr<nsIHistoryEntry> entry;
sessionHistory->GetEntryAtIndex(i, PR_FALSE, getter_AddRefs(entry));
PRUnichar *text;
entry->GetTitle(&text);
NSString* title = [NSString stringWithCharacters: text length: nsCRT::strlen(text)];
if ([title length] > kMaxTitleLength) {
}
NSMenuItem *newItem = [self addItemWithTitle:title action:@selector(historyItemAction:) keyEquivalent:@""];
[newItem setTarget:self];
[newItem setTag:kDividerTag+1+i];
if (currentIndex == i)
[newItem setState:NSOnState];
}
}
@end

View File

@ -67,13 +67,14 @@
};
SUPERCLASS = NSWindowController;
},
{CLASS = CHAutoCompleteDataSource; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
CLASS = CHAutoCompleteTableView;
ACTIONS = {onBlur = id; onResize = id; };
CLASS = CHAutoCompleteTextField;
LANGUAGE = ObjC;
OUTLETS = {mURLBar = NSTextField; mWindowController = BrowserWindowController; };
SUPERCLASS = NSTableView;
OUTLETS = {mProxyIcon = CHPageProxyIcon; };
SUPERCLASS = NSTextField;
},
{CLASS = CHAutoCompleteTextField; LANGUAGE = ObjC; SUPERCLASS = NSTextField; },
{
CLASS = CHBookmarksOutlineView;
LANGUAGE = ObjC;

View File

@ -22,10 +22,8 @@
<string>22 500 93 162 0 0 1152 746 </string>
<key>463</key>
<string>7 444 200 252 0 0 1152 746 </string>
<key>475</key>
<string>493 338 207 230 0 0 1152 746 </string>
<key>56</key>
<string>340 478 343 68 0 0 1024 746 </string>
<string>378 463 343 68 0 0 1152 746 </string>
</dict>
<key>IBFramework Version</key>
<string>248.0</string>
@ -43,6 +41,10 @@
<array>
<integer>497</integer>
</array>
<key>IBOpenObjects</key>
<array>
<integer>56</integer>
</array>
<key>IBSystem Version</key>
<string>5Q125</string>
</dict>

Binary file not shown.

View File

@ -21,13 +21,30 @@
* David Hyatt <hyatt@netscape.com> (Original Author)
*/
#import <Foundation/Foundation.h>
#include "nsIAutoCompleteSession.h"
#import <AppKit/AppKit.h>
#import "CHAutoCompleteTextField.h"
#include "nsIAutoCompleteResults.h"
@interface CHAutoCompleteDataSource : NSObject {
nsIAutoCompleteSession* mAutoComplete;
@class CHAutoCompleteTextField;
@interface CHAutoCompleteDataSource : NSObject
{
CHAutoCompleteTextField *mTextField;
NSImage *mIconImage;
NSString* mErrorMessage;
nsIAutoCompleteResults *mResults;
}
-(void)initialize;
- (id) init;
- (int) rowCount;
- (id) resultString:(int)aRow column:(NSString *)aColumn;
- (void) setErrorMessage: (NSString*) error;
- (NSString*) errorMessage;
- (void) setResults: (nsIAutoCompleteResults*) results;
- (nsIAutoCompleteResults*) results;
@end

View File

@ -19,41 +19,106 @@
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
*
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIServiceManager.h"
#import "CHAutoCompleteTextField.h"
#include "nsString.h"
#include "nsCRT.h"
@implementation CHAutoCompleteDataSource
-(id)init
{
if ((self = [super init])) {
mAutoComplete = nsnull;
mResults = nil;
mIconImage = [[NSImage imageNamed:@"globe_ico"] autorelease];
}
return self;
}
-(void)initialize
-(void)dealloc
{
if (!mAutoComplete) {
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mAutoComplete = session;
if (!mAutoComplete)
printf("Could not find autocomplete!\n");
}
NS_IF_RELEASE(mResults);
}
-(int)numberOfRowsInTableView:(NSTableView*)aTableView
- (void) setErrorMessage: (NSString*) error
{
return 0;
[self setResults:nsnull];
mErrorMessage = error;
}
- (NSString*) errorMessage
{
return mErrorMessage;
}
- (void) setResults:(nsIAutoCompleteResults*)aResults
{
NS_IF_RELEASE(mResults);
mErrorMessage = nil;
mResults = aResults;
NS_IF_ADDREF(mResults);
}
- (nsIAutoCompleteResults *) results
{
return mResults;
}
- (int) rowCount
{
if (!mResults)
return 0;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
PRUint32 count;
items->Count(&count);
return count;
}
- (id) resultString:(int)aRow column:(NSString *)aColumn
{
NSString *result = @"";
if (!mResults)
return result;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
nsCOMPtr<nsISupports> itemSupports = dont_AddRef(items->ElementAt(aRow));
nsCOMPtr<nsIAutoCompleteItem> item = do_QueryInterface(itemSupports);
if (!item)
return result;
if ([aColumn isEqualToString:@"icon"]) {
return mIconImage;
} else if ([aColumn isEqualToString:@"col1"]) {
nsAutoString value;
item->GetValue(value);
result = [NSString stringWithCharacters:value.get() length:value.Length()];
} else if ([aColumn isEqualToString:@"col2"]) {
PRUnichar *comment;
item->GetComment(&comment);
result = [NSString stringWithCharacters:comment length:nsCRT::strlen(comment)];
}
return result;
}
-(int) numberOfRowsInTableView:(NSTableView*)aTableView
{
return [self rowCount];
}
-(id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)aRowIndex
{
return @"";
return [self resultString:aRowIndex column:[aTableColumn identifier]];
}
@end

View File

@ -1,68 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
*/
#import "CHAutoCompleteTableView.h"
#import "CHAutoCompleteDataSource.h"
@implementation CHAutoCompleteTableView
-(id)initWithFrame:(NSRect)aRect
{
if ((self = [super initWithFrame: aRect])) {
// Create our data source.
mDataSource = [[CHAutoCompleteDataSource alloc] init];
[self setDataSource: mDataSource];
// Create the URL column.
NSTableColumn* urlColumn = [[[NSTableColumn alloc] initWithIdentifier:@"URL"] autorelease];
[self addTableColumn: urlColumn];
NSTableColumn* titleColumn = [[[NSTableColumn alloc] initWithIdentifier:@"Title"] autorelease];
[self addTableColumn: titleColumn];
}
return self;
}
-(void)dealloc
{
[mDataSource release];
[super dealloc];
}
-(BOOL)isShowing
{
return ([self superview] != nil);
}
-(void)controlTextDidChange:(NSNotification*)aNotification
{
// Ensure that our data source is initialized.
[mDataSource initialize];
// [[[[mWindowController window] contentView] superview] addSubview: self];
}
-(void)controlTextDidEndEditing:(NSNotification*)aNotification
{
}
@end

View File

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIAutoCompleteSession.h"
#include "nsIAutoCompleteResults.h"
#include "nsIAutoCompleteListener.h"
@class CHAutoCompleteDataSource, CHPageProxyIcon;
@interface CHAutoCompleteTextField : NSTextField
{
IBOutlet CHPageProxyIcon *mProxyIcon;
NSWindow *mPopupWin;
NSTableView *mTableView;
CHAutoCompleteDataSource *mDataSource;
nsIAutoCompleteSession *mSession;
nsIAutoCompleteResults *mResults;
nsIAutoCompleteListener *mListener;
NSString* mSearchString;
BOOL mBackspaced;
NSTimer *mOpenTimer;
}
- (void) setSession:(NSString *)aSession;
- (NSString *) session;
- (NSTableView *) tableView;
- (int) visibleRows;
- (void) startSearch:(NSString*)aString;
- (void) performSearch;
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus;
- (void) searchTimer:(NSTimer *)aTimer;
- (void) completeDefaultResult;
- (void) completeResult:(int)aRow;
- (void) enterResult:(int)aRow;
- (void) selectRowAt:(int)aRow;
- (void) selectRowBy:(int)aRows;
- (void) openPopup;
- (void) closePopup;
- (void) resizePopup;
- (BOOL) isOpen;
- (void) onBlur:(id)sender;
- (void) onResize:(id)sender;
@end

View File

@ -0,0 +1,447 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHAutoCompleteTextField.h"
#import "CHPageProxyIcon.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"
#include "nsString.h"
static const int kMaxRows = 6;
static const int kFrameMargin = 1;
class AutoCompleteListener : public nsIAutoCompleteListener
{
public:
AutoCompleteListener(CHAutoCompleteTextField* aTextField)
{
NS_INIT_REFCNT();
mTextField = aTextField;
}
NS_DECL_ISUPPORTS
NS_IMETHODIMP OnStatus(const PRUnichar* aText) { return NS_OK; }
NS_IMETHODIMP SetParam(nsISupports *aParam) { return NS_OK; }
NS_IMETHODIMP GetParam(nsISupports **aParam) { return NS_OK; }
NS_IMETHODIMP OnAutoComplete(nsIAutoCompleteResults *aResults, AutoCompleteStatus aStatus)
{
[mTextField dataReady:aResults status:aStatus];
return NS_OK;
}
private:
CHAutoCompleteTextField *mTextField;
};
NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener)
////////////////////////////////////////////////////////////////////////
@implementation CHAutoCompleteTextField
- (void) awakeFromNib
{
NSTableColumn *column;
NSScrollView *scrollView;
NSCell *dataCell;
mSearchString = nil;
mBackspaced = NO;
mOpenTimer = nil;
mSession = nsnull;
mResults = nsnull;
mListener = (nsIAutoCompleteListener *)new AutoCompleteListener(self);
NS_IF_ADDREF(mListener);
[self setDelegate: self];
// XXX the owner of the textfield should set this
[self setSession:@"history"];
// construct and configure the popup window
mPopupWin = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,0,0)
styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
[mPopupWin setReleasedWhenClosed:NO];
[mPopupWin setLevel:NSPopUpMenuWindowLevel];
[mPopupWin setHasShadow:YES];
[mPopupWin setAlphaValue:0.9];
// construct and configure the view
mTableView = [[[NSTableView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[mTableView setIntercellSpacing:NSMakeSize(1, 2)];
// Create the icon column if we have a proxy icon
if (mProxyIcon != nil) {
column = [[[NSTableColumn alloc] initWithIdentifier:@"icon"] autorelease];
[column setWidth:[mProxyIcon frame].origin.x + [mProxyIcon frame].size.width];
dataCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
[column setDataCell:dataCell];
[mTableView addTableColumn: column];
}
// create the text columns
column = [[[NSTableColumn alloc] initWithIdentifier:@"col1"] autorelease];
[mTableView addTableColumn: column];
column = [[[NSTableColumn alloc] initWithIdentifier:@"col2"] autorelease];
[[column dataCell] setTextColor:[NSColor darkGrayColor]];
[mTableView addTableColumn: column];
// hide the table header
[mTableView setHeaderView:nil];
// construct the scroll view that contains the table view
scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[scrollView setHasVerticalScroller:YES];
[[scrollView verticalScroller] setControlSize:NSSmallControlSize];
[scrollView setDocumentView: mTableView];
// construct the datasource
mDataSource = [[[CHAutoCompleteDataSource alloc] init] retain];
[mTableView setDataSource: mDataSource];
[mPopupWin setContentView:scrollView];
// listen for when window resigns from key handling
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowDidResignKeyNotification object:nil];
// listen for when window is about to be moved or resized
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowWillMoveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResize:)
name:NSWindowDidResizeNotification object:nil];
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mSearchString)
[mSearchString release];
[mDataSource release];
NS_IF_RELEASE(mSession);
NS_IF_RELEASE(mResults);
NS_IF_RELEASE(mListener);
}
- (void) setSession:(NSString *)aSession
{
NS_IF_RELEASE(mSession);
// XXX add aSession to contract id
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mSession = session;
NS_IF_ADDREF(mSession);
}
- (NSString *) session
{
// XXX return session name
return @"";
}
- (NSTableView *) tableView
{
return mTableView;
}
- (int) visibleRows
{
int minRows = [mDataSource rowCount];
return minRows < kMaxRows ? minRows : kMaxRows;
}
// searching ////////////////////////////
- (void) startSearch:(NSString*)aString
{
if (mSearchString)
[mSearchString release];
mSearchString = [aString retain];
if ([self isOpen]) {
[self performSearch];
} else {
// delay the search when the popup is not yet opened so that users
// don't see a jerky flashing popup when they start typing for the first time
if (mOpenTimer) {
[mOpenTimer invalidate];
[mOpenTimer release];
}
mOpenTimer = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(searchTimer:)
userInfo:nil repeats:NO] retain];
// we need to reset mBackspaced here, or else it might still be true if the user backspaces
// the textfield to be empty, then starts typing, because it is normally reset in selectRowAt
// which won't be called until perhaps after several keystrokes (due to the timer)
mBackspaced = NO;
}
}
- (void) performSearch
{
PRUnichar* chars = nsMemory::Alloc(([mSearchString length]+1) * sizeof(PRUnichar));
[mSearchString getCharacters:chars];
chars[[mSearchString length]] = 0; // I shouldn't have to do this
nsresult rv = mSession->OnStartLookup(chars, mResults, mListener);
nsMemory::Free(chars);
if (NS_FAILED(rv))
NSLog(@"Unable to perform autocomplete lookup");
}
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus
{
NS_IF_RELEASE(mResults);
mResults = nsnull;
if (aStatus == nsIAutoCompleteStatus::failed) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::ignored) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::noMatch) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::matchFound) {
mResults = aResults;
NS_IF_ADDREF(mResults);
[mDataSource setResults:aResults];
[self completeDefaultResult];
}
if ([mDataSource rowCount] > 0) {
[mTableView noteNumberOfRowsChanged];
[self openPopup];
} else {
[self closePopup];
}
}
- (void) searchTimer:(NSTimer *)aTimer
{
[mOpenTimer release];
mOpenTimer = nil;
[self performSearch];
}
// handling the popup /////////////////////////////////
- (void) openPopup
{
[self resizePopup];
// show the popup
if ([mPopupWin isVisible] == NO)
[mPopupWin orderFront:nil];
}
- (void) resizePopup
{
NSRect locationFrame, winFrame;
NSPoint locationOrigin;
int tableHeight;
// get the origin of the location bar in coordinates of the root view
locationFrame = [[self superview] frame];
locationOrigin = [[[self superview] superview] convertPoint:locationFrame.origin
toView:[[[self window] contentView] superview]];
// get the height of the table view
winFrame = [[self window] frame];
tableHeight = (int)([mTableView rowHeight]+[mTableView intercellSpacing].height)*[self visibleRows];
// make the columns split the width of the popup
[[mTableView tableColumnWithIdentifier:@"col1"] setWidth:locationFrame.size.width/2];
// position the popup anchored to bottom/left of location bar (
[mPopupWin setFrame:NSMakeRect(winFrame.origin.x + locationOrigin.x + kFrameMargin,
((winFrame.origin.y + locationOrigin.y) - tableHeight) - kFrameMargin,
locationFrame.size.width - (2*kFrameMargin),
tableHeight) display:NO];
}
- (void) closePopup
{
[mPopupWin close];
}
- (BOOL) isOpen
{
return [mPopupWin isVisible];
}
// url completion ////////////////////////////
- (void) completeDefaultResult
{
PRInt32 defaultRow;
mResults->GetDefaultItemIndex(&defaultRow);
if (mBackspaced) {
[self selectRowAt:-1];
mBackspaced = NO;
} else {
[self selectRowAt:defaultRow];
[self completeResult:defaultRow];
}
}
- (void) completeResult:(int)aRow
{
NSRange matchRange;
NSText *text;
NSString *result1;
if (aRow < 0) {
[self setStringValue:mSearchString];
} else {
if ([mDataSource rowCount] <= 0)
return;
result1 = [mDataSource resultString:aRow column:@"col1"];
matchRange = [result1 rangeOfString:mSearchString];
if (matchRange.length > 0) {
// cut off everything in the result string before the search string
result1 = [result1 substringWithRange:NSMakeRange(matchRange.location, [result1 length]-matchRange.location)];
// fill in the textfield with the matching string
[self setStringValue:result1];
// select the text after the search string
text = [[self window] fieldEditor:NO forObject:self];
[text setSelectedRange:NSMakeRange([mSearchString length], [result1 length]-[mSearchString length])];
}
}
}
- (void) enterResult:(int)aRow
{
if ([self isOpen] && aRow >= 0) {
[self setStringValue: [mDataSource resultString:[mTableView selectedRow] column:@"col1"]];
}
}
// selecting rows /////////////////////////////////////////
- (void) selectRowAt:(int)aRow
{
[mTableView selectRow:aRow byExtendingSelection:NO];
[mTableView scrollRowToVisible: aRow];
}
- (void) selectRowBy:(int)aRows
{
int row = [mTableView selectedRow];
if (row == -1 && aRows < 0) {
// if nothing is selected and you scroll up, go to last row
row = [mTableView numberOfRows]-1;
} else if (row == [mTableView numberOfRows]-1 && aRows == 1) {
// if the last row is selected and you scroll down, go to first row
row = 0;
} else if (aRows+row < 0) {
// if you scroll up beyond first row...
if (row == 0)
row = -1; // ...and first row is selected, select nothing
else
row = 0; // ...else, go to first row
} else if (aRows+row >= [mTableView numberOfRows]) {
// if you scroll down beyond the last row...
if (row == [mTableView numberOfRows]-1)
row = 0; // and last row is selected, select first row
else
row = [mTableView numberOfRows]-1; // else, go to last row
} else {
// no special case, just increment current row
row += aRows;
}
[self selectRowAt:row];
}
// event handlers ////////////////////////////////////////////
- (void) onBlur:(id)sender
{
[self closePopup];
}
- (void) onResize:(id)sender
{
[self resizePopup];
}
// NSTextField ////////////////////////////////////////////
- (void)controlTextDidChange:(NSNotification *)aNotification
{
[self startSearch:[self stringValue]];
}
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
[self closePopup];
}
// NSTextField delegate //////////////////////////////////
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
{
if (command == @selector(insertNewline:)) {
[self enterResult:[mTableView selectedRow]];
} else if (command == @selector(moveUp:)) {
[self selectRowBy:-1];
return YES;
} else if (command == @selector(moveDown:)) {
[self selectRowBy:1];
return YES;
} else if (command == @selector(scrollPageUp:)) {
[self selectRowBy:-kMaxRows];
} else if (command == @selector(scrollPageDown:)) {
[self selectRowBy:kMaxRows];
} else if (command == @selector(moveToBeginningOfDocument:)) {
[self selectRowAt:0];
} else if (command == @selector(moveToEndOfDocument:)) {
[self selectRowAt:[mTableView numberOfRows]-1];
} else if (command == @selector(insertTab:) || command == @selector(insertNewline:)) {
[self closePopup];
} else if (command == @selector(deleteBackward:) ||
command == @selector(deleteForward:)) {
// if the user deletes characters, we need to know so that
// we can prevent autocompletion later when search results come in
if ([[self stringValue] length] > 1)
mBackspaced = YES;
}
return NO;
}
@end

36
chimera/CHGoMenu.h Normal file
View File

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <Appkit/Appkit.h>
@interface CHGoMenu : NSMenu {
}
- (void) emptyHistoryItems;
- (void) addHistoryItems;
// NSMenu
- (void) update;
@end

147
chimera/CHGoMenu.mm Normal file
View File

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHGoMenu.h"
#import "BrowserWindowController.h"
#import "CHBrowserWrapper.h"
#import "CHBrowserView.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIWebBrowser.h"
#include "nsISHistory.h"
#include "nsIWebNavigation.h"
#include "nsIHistoryEntry.h"
#include "nsCRT.h"
// the tag of the separator after which to insert history menu items
static const int kDividerTag = 4000;
// the maximum number of history entry menuitems to display
static const int kMaxItems = 15;
// the maximum number of characters in a menu title before cropping it
static const int kMaxTitleLength = 20;
@implementation CHGoMenu
- (void) update
{
[self emptyHistoryItems];
[self addHistoryItems];
[super update];
}
- (nsIWebNavigation*) currentWebNavigation
{
// get controller for current window
BrowserWindowController *controller;
controller = (BrowserWindowController*)[[NSApp mainWindow] windowController];
if (!controller) return nsnull;
// get web navigation for current browser
CHBrowserWrapper* wrapper = [controller getBrowserWrapper];
if (!wrapper) return nsnull;
CHBrowserView* view = [wrapper getBrowserView];
if (!view) return nsnull;
nsCOMPtr<nsIWebBrowser> webBrowser = [view getWebBrowser];
if (!webBrowser) return nsnull;
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(webBrowser));
return webNav.get();
}
- (void) historyItemAction:(id)sender
{
// get web navigation for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
// browse to the history entry for the menuitem that was selected
PRInt32 historyIndex = ([sender tag] - 1) - kDividerTag;
webNav->GotoIndex(historyIndex);
}
- (void) emptyHistoryItems
{
// remove every history item after the insertion point
int insertionIndex = [self indexOfItemWithTag:kDividerTag];
for (int i = [self numberOfItems]-1; i > insertionIndex ; --i) {
[self removeItemAtIndex:i];
}
}
- (void) addHistoryItems
{
// get session history for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
nsCOMPtr<nsISHistory> sessionHistory;
webNav->GetSessionHistory(getter_AddRefs(sessionHistory));
PRInt32 count;
sessionHistory->GetCount(&count);
PRInt32 currentIndex;
sessionHistory->GetIndex(&currentIndex);
// determine the range of items to display
int rangeStart, rangeEnd;
int above = kMaxItems/2;
int below = (kMaxItems-above)-1;
if (count <= kMaxItems) {
// if the whole history fits within our menu, fit range to show all
rangeStart = count-1;
rangeEnd = 0;
} else {
// if the whole history is too large for menu, try to put current index as close to
// the middle of the list as possible, so the user can see both back and forward in session
rangeStart = currentIndex + above;
rangeEnd = currentIndex - below;
if (rangeStart >= count-1) {
rangeEnd -= (rangeStart-count)+1; // shift overflow to the end
rangeStart = count-1;
} else if (rangeEnd < 0) {
rangeStart -= rangeEnd; // shift overflow to the start
rangeEnd = 0;
}
}
// create a new menu item for each history entry (up to MAX_MENUITEM entries)
for (PRInt32 i = rangeStart; i >= rangeEnd; --i) {
nsCOMPtr<nsIHistoryEntry> entry;
sessionHistory->GetEntryAtIndex(i, PR_FALSE, getter_AddRefs(entry));
PRUnichar *text;
entry->GetTitle(&text);
NSString* title = [NSString stringWithCharacters: text length: nsCRT::strlen(text)];
if ([title length] > kMaxTitleLength) {
}
NSMenuItem *newItem = [self addItemWithTitle:title action:@selector(historyItemAction:) keyEquivalent:@""];
[newItem setTarget:self];
[newItem setTag:kDividerTag+1+i];
if (currentIndex == i)
[newItem setState:NSOnState];
}
}
@end

View File

@ -27,6 +27,7 @@
F52D5CD4027A88C601A80166,
F52F87CD027D75C301A80165,
F52F87CE027D75C301A80165,
2E748B73029A448D4B000102,
);
isa = PBXGroup;
name = Classes;
@ -409,6 +410,7 @@
F52F87D0027D75C301A80165,
2E293A01027F33604B000102,
2EEC3E63028138724B000102,
2E748B74029A448D4B000102,
);
isa = PBXHeadersBuildPhase;
name = Headers;
@ -510,6 +512,7 @@
F52F87D2027D75C301A80165,
2E293A02027F33604B000102,
2EEC3E64028138724B000102,
2E748B75029A448D4B000102,
);
isa = PBXSourcesBuildPhase;
name = Sources;
@ -569,6 +572,28 @@
settings = {
};
};
2E748B72029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.h;
refType = 4;
};
2E748B73029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.mm;
refType = 4;
};
2E748B74029A448D4B000102 = {
fileRef = 2E748B72029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2E748B75029A448D4B000102 = {
fileRef = 2E748B73029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2EEC3E6002810B184B000102 = {
indentWidth = 2;
isa = PBXFileReference;
@ -1009,6 +1034,7 @@
F52D5CD3027A88C601A80166,
F52F87CB027D75C301A80165,
F52F87CC027D75C301A80165,
2E748B72029A448D4B000102,
);
isa = PBXGroup;
name = Headers;
@ -1483,12 +1509,12 @@
};
F52D5CCF027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.h;
path = CHAutoCompleteTextField.h;
refType = 4;
};
F52D5CD0027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.mm;
path = CHAutoCompleteTextField.mm;
refType = 4;
};
F52D5CD1027A887001A80166 = {

View File

@ -67,13 +67,14 @@
};
SUPERCLASS = NSWindowController;
},
{CLASS = CHAutoCompleteDataSource; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
CLASS = CHAutoCompleteTableView;
ACTIONS = {onBlur = id; onResize = id; };
CLASS = CHAutoCompleteTextField;
LANGUAGE = ObjC;
OUTLETS = {mURLBar = NSTextField; mWindowController = BrowserWindowController; };
SUPERCLASS = NSTableView;
OUTLETS = {mProxyIcon = CHPageProxyIcon; };
SUPERCLASS = NSTextField;
},
{CLASS = CHAutoCompleteTextField; LANGUAGE = ObjC; SUPERCLASS = NSTextField; },
{
CLASS = CHBookmarksOutlineView;
LANGUAGE = ObjC;

View File

@ -22,10 +22,8 @@
<string>22 500 93 162 0 0 1152 746 </string>
<key>463</key>
<string>7 444 200 252 0 0 1152 746 </string>
<key>475</key>
<string>493 338 207 230 0 0 1152 746 </string>
<key>56</key>
<string>340 478 343 68 0 0 1024 746 </string>
<string>378 463 343 68 0 0 1152 746 </string>
</dict>
<key>IBFramework Version</key>
<string>248.0</string>
@ -43,6 +41,10 @@
<array>
<integer>497</integer>
</array>
<key>IBOpenObjects</key>
<array>
<integer>56</integer>
</array>
<key>IBSystem Version</key>
<string>5Q125</string>
</dict>

Binary file not shown.

View File

@ -5,6 +5,7 @@
LANGUAGE = ObjC;
SUPERCLASS = ToolbarController;
},
{CLASS = CHGoMenu; LANGUAGE = ObjC; SUPERCLASS = NSMenu; },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
ACTIONS = {

Binary file not shown.

View File

@ -5,6 +5,7 @@
LANGUAGE = ObjC;
SUPERCLASS = ToolbarController;
},
{CLASS = CHGoMenu; LANGUAGE = ObjC; SUPERCLASS = NSMenu; },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
ACTIONS = {

Binary file not shown.

View File

@ -27,6 +27,7 @@
F52D5CD4027A88C601A80166,
F52F87CD027D75C301A80165,
F52F87CE027D75C301A80165,
2E748B73029A448D4B000102,
);
isa = PBXGroup;
name = Classes;
@ -409,6 +410,7 @@
F52F87D0027D75C301A80165,
2E293A01027F33604B000102,
2EEC3E63028138724B000102,
2E748B74029A448D4B000102,
);
isa = PBXHeadersBuildPhase;
name = Headers;
@ -510,6 +512,7 @@
F52F87D2027D75C301A80165,
2E293A02027F33604B000102,
2EEC3E64028138724B000102,
2E748B75029A448D4B000102,
);
isa = PBXSourcesBuildPhase;
name = Sources;
@ -569,6 +572,28 @@
settings = {
};
};
2E748B72029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.h;
refType = 4;
};
2E748B73029A448D4B000102 = {
isa = PBXFileReference;
path = CHGoMenu.mm;
refType = 4;
};
2E748B74029A448D4B000102 = {
fileRef = 2E748B72029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2E748B75029A448D4B000102 = {
fileRef = 2E748B73029A448D4B000102;
isa = PBXBuildFile;
settings = {
};
};
2EEC3E6002810B184B000102 = {
indentWidth = 2;
isa = PBXFileReference;
@ -1009,6 +1034,7 @@
F52D5CD3027A88C601A80166,
F52F87CB027D75C301A80165,
F52F87CC027D75C301A80165,
2E748B72029A448D4B000102,
);
isa = PBXGroup;
name = Headers;
@ -1483,12 +1509,12 @@
};
F52D5CCF027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.h;
path = CHAutoCompleteTextField.h;
refType = 4;
};
F52D5CD0027A887001A80166 = {
isa = PBXFileReference;
path = CHAutoCompleteTableView.mm;
path = CHAutoCompleteTextField.mm;
refType = 4;
};
F52D5CD1027A887001A80166 = {

View File

@ -67,13 +67,14 @@
};
SUPERCLASS = NSWindowController;
},
{CLASS = CHAutoCompleteDataSource; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
CLASS = CHAutoCompleteTableView;
ACTIONS = {onBlur = id; onResize = id; };
CLASS = CHAutoCompleteTextField;
LANGUAGE = ObjC;
OUTLETS = {mURLBar = NSTextField; mWindowController = BrowserWindowController; };
SUPERCLASS = NSTableView;
OUTLETS = {mProxyIcon = CHPageProxyIcon; };
SUPERCLASS = NSTextField;
},
{CLASS = CHAutoCompleteTextField; LANGUAGE = ObjC; SUPERCLASS = NSTextField; },
{
CLASS = CHBookmarksOutlineView;
LANGUAGE = ObjC;

View File

@ -22,10 +22,8 @@
<string>22 500 93 162 0 0 1152 746 </string>
<key>463</key>
<string>7 444 200 252 0 0 1152 746 </string>
<key>475</key>
<string>493 338 207 230 0 0 1152 746 </string>
<key>56</key>
<string>340 478 343 68 0 0 1024 746 </string>
<string>378 463 343 68 0 0 1152 746 </string>
</dict>
<key>IBFramework Version</key>
<string>248.0</string>
@ -43,6 +41,10 @@
<array>
<integer>497</integer>
</array>
<key>IBOpenObjects</key>
<array>
<integer>56</integer>
</array>
<key>IBSystem Version</key>
<string>5Q125</string>
</dict>

View File

@ -5,6 +5,7 @@
LANGUAGE = ObjC;
SUPERCLASS = ToolbarController;
},
{CLASS = CHGoMenu; LANGUAGE = ObjC; SUPERCLASS = NSMenu; },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
ACTIONS = {

View File

@ -21,13 +21,30 @@
* David Hyatt <hyatt@netscape.com> (Original Author)
*/
#import <Foundation/Foundation.h>
#include "nsIAutoCompleteSession.h"
#import <AppKit/AppKit.h>
#import "CHAutoCompleteTextField.h"
#include "nsIAutoCompleteResults.h"
@interface CHAutoCompleteDataSource : NSObject {
nsIAutoCompleteSession* mAutoComplete;
@class CHAutoCompleteTextField;
@interface CHAutoCompleteDataSource : NSObject
{
CHAutoCompleteTextField *mTextField;
NSImage *mIconImage;
NSString* mErrorMessage;
nsIAutoCompleteResults *mResults;
}
-(void)initialize;
- (id) init;
- (int) rowCount;
- (id) resultString:(int)aRow column:(NSString *)aColumn;
- (void) setErrorMessage: (NSString*) error;
- (NSString*) errorMessage;
- (void) setResults: (nsIAutoCompleteResults*) results;
- (nsIAutoCompleteResults*) results;
@end

View File

@ -19,41 +19,106 @@
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
*
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIServiceManager.h"
#import "CHAutoCompleteTextField.h"
#include "nsString.h"
#include "nsCRT.h"
@implementation CHAutoCompleteDataSource
-(id)init
{
if ((self = [super init])) {
mAutoComplete = nsnull;
mResults = nil;
mIconImage = [[NSImage imageNamed:@"globe_ico"] autorelease];
}
return self;
}
-(void)initialize
-(void)dealloc
{
if (!mAutoComplete) {
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mAutoComplete = session;
if (!mAutoComplete)
printf("Could not find autocomplete!\n");
}
NS_IF_RELEASE(mResults);
}
-(int)numberOfRowsInTableView:(NSTableView*)aTableView
- (void) setErrorMessage: (NSString*) error
{
return 0;
[self setResults:nsnull];
mErrorMessage = error;
}
- (NSString*) errorMessage
{
return mErrorMessage;
}
- (void) setResults:(nsIAutoCompleteResults*)aResults
{
NS_IF_RELEASE(mResults);
mErrorMessage = nil;
mResults = aResults;
NS_IF_ADDREF(mResults);
}
- (nsIAutoCompleteResults *) results
{
return mResults;
}
- (int) rowCount
{
if (!mResults)
return 0;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
PRUint32 count;
items->Count(&count);
return count;
}
- (id) resultString:(int)aRow column:(NSString *)aColumn
{
NSString *result = @"";
if (!mResults)
return result;
nsCOMPtr<nsISupportsArray> items;
mResults->GetItems(getter_AddRefs(items));
nsCOMPtr<nsISupports> itemSupports = dont_AddRef(items->ElementAt(aRow));
nsCOMPtr<nsIAutoCompleteItem> item = do_QueryInterface(itemSupports);
if (!item)
return result;
if ([aColumn isEqualToString:@"icon"]) {
return mIconImage;
} else if ([aColumn isEqualToString:@"col1"]) {
nsAutoString value;
item->GetValue(value);
result = [NSString stringWithCharacters:value.get() length:value.Length()];
} else if ([aColumn isEqualToString:@"col2"]) {
PRUnichar *comment;
item->GetComment(&comment);
result = [NSString stringWithCharacters:comment length:nsCRT::strlen(comment)];
}
return result;
}
-(int) numberOfRowsInTableView:(NSTableView*)aTableView
{
return [self rowCount];
}
-(id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)aRowIndex
{
return @"";
return [self resultString:aRowIndex column:[aTableColumn identifier]];
}
@end

View File

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <AppKit/AppKit.h>
#import "CHAutoCompleteDataSource.h"
#include "nsIAutoCompleteSession.h"
#include "nsIAutoCompleteResults.h"
#include "nsIAutoCompleteListener.h"
@class CHAutoCompleteDataSource, CHPageProxyIcon;
@interface CHAutoCompleteTextField : NSTextField
{
IBOutlet CHPageProxyIcon *mProxyIcon;
NSWindow *mPopupWin;
NSTableView *mTableView;
CHAutoCompleteDataSource *mDataSource;
nsIAutoCompleteSession *mSession;
nsIAutoCompleteResults *mResults;
nsIAutoCompleteListener *mListener;
NSString* mSearchString;
BOOL mBackspaced;
NSTimer *mOpenTimer;
}
- (void) setSession:(NSString *)aSession;
- (NSString *) session;
- (NSTableView *) tableView;
- (int) visibleRows;
- (void) startSearch:(NSString*)aString;
- (void) performSearch;
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus;
- (void) searchTimer:(NSTimer *)aTimer;
- (void) completeDefaultResult;
- (void) completeResult:(int)aRow;
- (void) enterResult:(int)aRow;
- (void) selectRowAt:(int)aRow;
- (void) selectRowBy:(int)aRows;
- (void) openPopup;
- (void) closePopup;
- (void) resizePopup;
- (BOOL) isOpen;
- (void) onBlur:(id)sender;
- (void) onResize:(id)sender;
@end

View File

@ -0,0 +1,447 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHAutoCompleteTextField.h"
#import "CHPageProxyIcon.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"
#include "nsString.h"
static const int kMaxRows = 6;
static const int kFrameMargin = 1;
class AutoCompleteListener : public nsIAutoCompleteListener
{
public:
AutoCompleteListener(CHAutoCompleteTextField* aTextField)
{
NS_INIT_REFCNT();
mTextField = aTextField;
}
NS_DECL_ISUPPORTS
NS_IMETHODIMP OnStatus(const PRUnichar* aText) { return NS_OK; }
NS_IMETHODIMP SetParam(nsISupports *aParam) { return NS_OK; }
NS_IMETHODIMP GetParam(nsISupports **aParam) { return NS_OK; }
NS_IMETHODIMP OnAutoComplete(nsIAutoCompleteResults *aResults, AutoCompleteStatus aStatus)
{
[mTextField dataReady:aResults status:aStatus];
return NS_OK;
}
private:
CHAutoCompleteTextField *mTextField;
};
NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener)
////////////////////////////////////////////////////////////////////////
@implementation CHAutoCompleteTextField
- (void) awakeFromNib
{
NSTableColumn *column;
NSScrollView *scrollView;
NSCell *dataCell;
mSearchString = nil;
mBackspaced = NO;
mOpenTimer = nil;
mSession = nsnull;
mResults = nsnull;
mListener = (nsIAutoCompleteListener *)new AutoCompleteListener(self);
NS_IF_ADDREF(mListener);
[self setDelegate: self];
// XXX the owner of the textfield should set this
[self setSession:@"history"];
// construct and configure the popup window
mPopupWin = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,0,0)
styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
[mPopupWin setReleasedWhenClosed:NO];
[mPopupWin setLevel:NSPopUpMenuWindowLevel];
[mPopupWin setHasShadow:YES];
[mPopupWin setAlphaValue:0.9];
// construct and configure the view
mTableView = [[[NSTableView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[mTableView setIntercellSpacing:NSMakeSize(1, 2)];
// Create the icon column if we have a proxy icon
if (mProxyIcon != nil) {
column = [[[NSTableColumn alloc] initWithIdentifier:@"icon"] autorelease];
[column setWidth:[mProxyIcon frame].origin.x + [mProxyIcon frame].size.width];
dataCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
[column setDataCell:dataCell];
[mTableView addTableColumn: column];
}
// create the text columns
column = [[[NSTableColumn alloc] initWithIdentifier:@"col1"] autorelease];
[mTableView addTableColumn: column];
column = [[[NSTableColumn alloc] initWithIdentifier:@"col2"] autorelease];
[[column dataCell] setTextColor:[NSColor darkGrayColor]];
[mTableView addTableColumn: column];
// hide the table header
[mTableView setHeaderView:nil];
// construct the scroll view that contains the table view
scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
[scrollView setHasVerticalScroller:YES];
[[scrollView verticalScroller] setControlSize:NSSmallControlSize];
[scrollView setDocumentView: mTableView];
// construct the datasource
mDataSource = [[[CHAutoCompleteDataSource alloc] init] retain];
[mTableView setDataSource: mDataSource];
[mPopupWin setContentView:scrollView];
// listen for when window resigns from key handling
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowDidResignKeyNotification object:nil];
// listen for when window is about to be moved or resized
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBlur:)
name:NSWindowWillMoveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResize:)
name:NSWindowDidResizeNotification object:nil];
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mSearchString)
[mSearchString release];
[mDataSource release];
NS_IF_RELEASE(mSession);
NS_IF_RELEASE(mResults);
NS_IF_RELEASE(mListener);
}
- (void) setSession:(NSString *)aSession
{
NS_IF_RELEASE(mSession);
// XXX add aSession to contract id
nsCOMPtr<nsIAutoCompleteSession> session =
do_GetService("@mozilla.org/autocompleteSession;1?type=history");
mSession = session;
NS_IF_ADDREF(mSession);
}
- (NSString *) session
{
// XXX return session name
return @"";
}
- (NSTableView *) tableView
{
return mTableView;
}
- (int) visibleRows
{
int minRows = [mDataSource rowCount];
return minRows < kMaxRows ? minRows : kMaxRows;
}
// searching ////////////////////////////
- (void) startSearch:(NSString*)aString
{
if (mSearchString)
[mSearchString release];
mSearchString = [aString retain];
if ([self isOpen]) {
[self performSearch];
} else {
// delay the search when the popup is not yet opened so that users
// don't see a jerky flashing popup when they start typing for the first time
if (mOpenTimer) {
[mOpenTimer invalidate];
[mOpenTimer release];
}
mOpenTimer = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(searchTimer:)
userInfo:nil repeats:NO] retain];
// we need to reset mBackspaced here, or else it might still be true if the user backspaces
// the textfield to be empty, then starts typing, because it is normally reset in selectRowAt
// which won't be called until perhaps after several keystrokes (due to the timer)
mBackspaced = NO;
}
}
- (void) performSearch
{
PRUnichar* chars = nsMemory::Alloc(([mSearchString length]+1) * sizeof(PRUnichar));
[mSearchString getCharacters:chars];
chars[[mSearchString length]] = 0; // I shouldn't have to do this
nsresult rv = mSession->OnStartLookup(chars, mResults, mListener);
nsMemory::Free(chars);
if (NS_FAILED(rv))
NSLog(@"Unable to perform autocomplete lookup");
}
- (void) dataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus
{
NS_IF_RELEASE(mResults);
mResults = nsnull;
if (aStatus == nsIAutoCompleteStatus::failed) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::ignored) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::noMatch) {
[mDataSource setErrorMessage:@""];
} else if (aStatus == nsIAutoCompleteStatus::matchFound) {
mResults = aResults;
NS_IF_ADDREF(mResults);
[mDataSource setResults:aResults];
[self completeDefaultResult];
}
if ([mDataSource rowCount] > 0) {
[mTableView noteNumberOfRowsChanged];
[self openPopup];
} else {
[self closePopup];
}
}
- (void) searchTimer:(NSTimer *)aTimer
{
[mOpenTimer release];
mOpenTimer = nil;
[self performSearch];
}
// handling the popup /////////////////////////////////
- (void) openPopup
{
[self resizePopup];
// show the popup
if ([mPopupWin isVisible] == NO)
[mPopupWin orderFront:nil];
}
- (void) resizePopup
{
NSRect locationFrame, winFrame;
NSPoint locationOrigin;
int tableHeight;
// get the origin of the location bar in coordinates of the root view
locationFrame = [[self superview] frame];
locationOrigin = [[[self superview] superview] convertPoint:locationFrame.origin
toView:[[[self window] contentView] superview]];
// get the height of the table view
winFrame = [[self window] frame];
tableHeight = (int)([mTableView rowHeight]+[mTableView intercellSpacing].height)*[self visibleRows];
// make the columns split the width of the popup
[[mTableView tableColumnWithIdentifier:@"col1"] setWidth:locationFrame.size.width/2];
// position the popup anchored to bottom/left of location bar (
[mPopupWin setFrame:NSMakeRect(winFrame.origin.x + locationOrigin.x + kFrameMargin,
((winFrame.origin.y + locationOrigin.y) - tableHeight) - kFrameMargin,
locationFrame.size.width - (2*kFrameMargin),
tableHeight) display:NO];
}
- (void) closePopup
{
[mPopupWin close];
}
- (BOOL) isOpen
{
return [mPopupWin isVisible];
}
// url completion ////////////////////////////
- (void) completeDefaultResult
{
PRInt32 defaultRow;
mResults->GetDefaultItemIndex(&defaultRow);
if (mBackspaced) {
[self selectRowAt:-1];
mBackspaced = NO;
} else {
[self selectRowAt:defaultRow];
[self completeResult:defaultRow];
}
}
- (void) completeResult:(int)aRow
{
NSRange matchRange;
NSText *text;
NSString *result1;
if (aRow < 0) {
[self setStringValue:mSearchString];
} else {
if ([mDataSource rowCount] <= 0)
return;
result1 = [mDataSource resultString:aRow column:@"col1"];
matchRange = [result1 rangeOfString:mSearchString];
if (matchRange.length > 0) {
// cut off everything in the result string before the search string
result1 = [result1 substringWithRange:NSMakeRange(matchRange.location, [result1 length]-matchRange.location)];
// fill in the textfield with the matching string
[self setStringValue:result1];
// select the text after the search string
text = [[self window] fieldEditor:NO forObject:self];
[text setSelectedRange:NSMakeRange([mSearchString length], [result1 length]-[mSearchString length])];
}
}
}
- (void) enterResult:(int)aRow
{
if ([self isOpen] && aRow >= 0) {
[self setStringValue: [mDataSource resultString:[mTableView selectedRow] column:@"col1"]];
}
}
// selecting rows /////////////////////////////////////////
- (void) selectRowAt:(int)aRow
{
[mTableView selectRow:aRow byExtendingSelection:NO];
[mTableView scrollRowToVisible: aRow];
}
- (void) selectRowBy:(int)aRows
{
int row = [mTableView selectedRow];
if (row == -1 && aRows < 0) {
// if nothing is selected and you scroll up, go to last row
row = [mTableView numberOfRows]-1;
} else if (row == [mTableView numberOfRows]-1 && aRows == 1) {
// if the last row is selected and you scroll down, go to first row
row = 0;
} else if (aRows+row < 0) {
// if you scroll up beyond first row...
if (row == 0)
row = -1; // ...and first row is selected, select nothing
else
row = 0; // ...else, go to first row
} else if (aRows+row >= [mTableView numberOfRows]) {
// if you scroll down beyond the last row...
if (row == [mTableView numberOfRows]-1)
row = 0; // and last row is selected, select first row
else
row = [mTableView numberOfRows]-1; // else, go to last row
} else {
// no special case, just increment current row
row += aRows;
}
[self selectRowAt:row];
}
// event handlers ////////////////////////////////////////////
- (void) onBlur:(id)sender
{
[self closePopup];
}
- (void) onResize:(id)sender
{
[self resizePopup];
}
// NSTextField ////////////////////////////////////////////
- (void)controlTextDidChange:(NSNotification *)aNotification
{
[self startSearch:[self stringValue]];
}
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
[self closePopup];
}
// NSTextField delegate //////////////////////////////////
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
{
if (command == @selector(insertNewline:)) {
[self enterResult:[mTableView selectedRow]];
} else if (command == @selector(moveUp:)) {
[self selectRowBy:-1];
return YES;
} else if (command == @selector(moveDown:)) {
[self selectRowBy:1];
return YES;
} else if (command == @selector(scrollPageUp:)) {
[self selectRowBy:-kMaxRows];
} else if (command == @selector(scrollPageDown:)) {
[self selectRowBy:kMaxRows];
} else if (command == @selector(moveToBeginningOfDocument:)) {
[self selectRowAt:0];
} else if (command == @selector(moveToEndOfDocument:)) {
[self selectRowAt:[mTableView numberOfRows]-1];
} else if (command == @selector(insertTab:) || command == @selector(insertNewline:)) {
[self closePopup];
} else if (command == @selector(deleteBackward:) ||
command == @selector(deleteForward:)) {
// if the user deletes characters, we need to know so that
// we can prevent autocompletion later when search results come in
if ([[self stringValue] length] > 1)
mBackspaced = YES;
}
return NO;
}
@end

View File

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import <Appkit/Appkit.h>
@interface CHGoMenu : NSMenu {
}
- (void) emptyHistoryItems;
- (void) addHistoryItems;
// NSMenu
- (void) update;
@end

View File

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*/
#import "CHGoMenu.h"
#import "BrowserWindowController.h"
#import "CHBrowserWrapper.h"
#import "CHBrowserView.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIWebBrowser.h"
#include "nsISHistory.h"
#include "nsIWebNavigation.h"
#include "nsIHistoryEntry.h"
#include "nsCRT.h"
// the tag of the separator after which to insert history menu items
static const int kDividerTag = 4000;
// the maximum number of history entry menuitems to display
static const int kMaxItems = 15;
// the maximum number of characters in a menu title before cropping it
static const int kMaxTitleLength = 20;
@implementation CHGoMenu
- (void) update
{
[self emptyHistoryItems];
[self addHistoryItems];
[super update];
}
- (nsIWebNavigation*) currentWebNavigation
{
// get controller for current window
BrowserWindowController *controller;
controller = (BrowserWindowController*)[[NSApp mainWindow] windowController];
if (!controller) return nsnull;
// get web navigation for current browser
CHBrowserWrapper* wrapper = [controller getBrowserWrapper];
if (!wrapper) return nsnull;
CHBrowserView* view = [wrapper getBrowserView];
if (!view) return nsnull;
nsCOMPtr<nsIWebBrowser> webBrowser = [view getWebBrowser];
if (!webBrowser) return nsnull;
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(webBrowser));
return webNav.get();
}
- (void) historyItemAction:(id)sender
{
// get web navigation for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
// browse to the history entry for the menuitem that was selected
PRInt32 historyIndex = ([sender tag] - 1) - kDividerTag;
webNav->GotoIndex(historyIndex);
}
- (void) emptyHistoryItems
{
// remove every history item after the insertion point
int insertionIndex = [self indexOfItemWithTag:kDividerTag];
for (int i = [self numberOfItems]-1; i > insertionIndex ; --i) {
[self removeItemAtIndex:i];
}
}
- (void) addHistoryItems
{
// get session history for current browser
nsCOMPtr<nsIWebNavigation> webNav = [self currentWebNavigation];
if (!webNav) return;
nsCOMPtr<nsISHistory> sessionHistory;
webNav->GetSessionHistory(getter_AddRefs(sessionHistory));
PRInt32 count;
sessionHistory->GetCount(&count);
PRInt32 currentIndex;
sessionHistory->GetIndex(&currentIndex);
// determine the range of items to display
int rangeStart, rangeEnd;
int above = kMaxItems/2;
int below = (kMaxItems-above)-1;
if (count <= kMaxItems) {
// if the whole history fits within our menu, fit range to show all
rangeStart = count-1;
rangeEnd = 0;
} else {
// if the whole history is too large for menu, try to put current index as close to
// the middle of the list as possible, so the user can see both back and forward in session
rangeStart = currentIndex + above;
rangeEnd = currentIndex - below;
if (rangeStart >= count-1) {
rangeEnd -= (rangeStart-count)+1; // shift overflow to the end
rangeStart = count-1;
} else if (rangeEnd < 0) {
rangeStart -= rangeEnd; // shift overflow to the start
rangeEnd = 0;
}
}
// create a new menu item for each history entry (up to MAX_MENUITEM entries)
for (PRInt32 i = rangeStart; i >= rangeEnd; --i) {
nsCOMPtr<nsIHistoryEntry> entry;
sessionHistory->GetEntryAtIndex(i, PR_FALSE, getter_AddRefs(entry));
PRUnichar *text;
entry->GetTitle(&text);
NSString* title = [NSString stringWithCharacters: text length: nsCRT::strlen(text)];
if ([title length] > kMaxTitleLength) {
}
NSMenuItem *newItem = [self addItemWithTitle:title action:@selector(historyItemAction:) keyEquivalent:@""];
[newItem setTarget:self];
[newItem setTag:kDividerTag+1+i];
if (currentIndex == i)
[newItem setState:NSOnState];
}
}
@end