history now listed as a flat list by day, ordered by visit time (bug 227618)

This commit is contained in:
pinkerton%aol.net 2004-01-13 20:06:02 +00:00
Родитель 129535cc9c
Коммит 76a6e01068
11 изменённых файлов: 1057 добавлений и 582 удалений

Просмотреть файл

@ -760,6 +760,8 @@
3FB2BAB30545EA80002B9691,
3FB2BAB50545EA80002B9691,
3FC0FB8105A0D2DC002F47DE,
3F7C6CBD05B4819E002FFFD3,
3F7C6CBF05B4819E002FFFD3,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@ -934,6 +936,8 @@
3FB2BAB40545EA80002B9691,
3FB2BAB60545EA80002B9691,
3FC0FB8205A0D2DC002F47DE,
3F7C6CBE05B4819E002FFFD3,
3F7C6CC005B4819E002FFFD3,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@ -1375,6 +1379,82 @@
settings = {
};
};
3F7C6CB905B4819E002FFFD3 = {
fileEncoding = 30;
isa = PBXFileReference;
name = HistoryItem.h;
path = src/history/HistoryItem.h;
refType = 2;
};
3F7C6CBA05B4819E002FFFD3 = {
fileEncoding = 30;
isa = PBXFileReference;
name = HistoryItem.mm;
path = src/history/HistoryItem.mm;
refType = 2;
};
3F7C6CBB05B4819E002FFFD3 = {
fileEncoding = 30;
isa = PBXFileReference;
name = RDFItem.h;
path = src/history/RDFItem.h;
refType = 2;
};
3F7C6CBC05B4819E002FFFD3 = {
fileEncoding = 30;
isa = PBXFileReference;
name = RDFItem.mm;
path = src/history/RDFItem.mm;
refType = 2;
};
3F7C6CBD05B4819E002FFFD3 = {
fileRef = 3F7C6CB905B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F7C6CBE05B4819E002FFFD3 = {
fileRef = 3F7C6CBA05B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F7C6CBF05B4819E002FFFD3 = {
fileRef = 3F7C6CBB05B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F7C6CC005B4819E002FFFD3 = {
fileRef = 3F7C6CBC05B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F7C6CC105B4819E002FFFD3 = {
fileRef = 3F7C6CB905B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F7C6CC205B4819E002FFFD3 = {
fileRef = 3F7C6CBA05B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F7C6CC305B4819E002FFFD3 = {
fileRef = 3F7C6CBB05B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F7C6CC405B4819E002FFFD3 = {
fileRef = 3F7C6CBC05B4819E002FFFD3;
isa = PBXBuildFile;
settings = {
};
};
3F8D26DE0471ED0F00A80005 = {
fileRef = 3FAD95540461E43700A80005;
isa = PBXBuildFile;
@ -7444,6 +7524,10 @@
children = (
F52F87CD027D75C301A80165,
F52F87CE027D75C301A80165,
3F7C6CB905B4819E002FFFD3,
3F7C6CBA05B4819E002FFFD3,
3F7C6CBB05B4819E002FFFD3,
3F7C6CBC05B4819E002FFFD3,
);
isa = PBXGroup;
name = History;
@ -8447,6 +8531,8 @@
3FB2BAD00545EA80002B9691,
3FB2BAD20545EA80002B9691,
3FC7E24905A228E900C557AA,
3F7C6CC105B4819E002FFFD3,
3F7C6CC305B4819E002FFFD3,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@ -8622,6 +8708,8 @@
3FB2BAD10545EA80002B9691,
3FB2BAD30545EA80002B9691,
3FC7E24805A228E900C557AA,
3F7C6CC205B4819E002FFFD3,
3F7C6CC405B4819E002FFFD3,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;

Просмотреть файл

@ -716,7 +716,7 @@ const long kMinSearchPaneHeight = 80;
if ( inRowIndex == kHistoryContainerIndex ) {
[mItemPane setDataSource:mHistorySource];
[mItemPane setDelegate:mHistorySource];
[mHistorySource ensureDataSourceLoaded];
[mHistorySource loadLazily];
[self setCanEditSelectedContainerContents:NO];
[mItemPane setTarget:mHistorySource];
[mItemPane setDoubleAction: @selector(openHistoryItem:)];

Просмотреть файл

@ -629,7 +629,7 @@ static NSArray* sToolbarDefaults = nil;
[mBookmarkViewController ensureBookmarks];
if ([[[mSidebarTabView selectedTabViewItem] identifier] isEqual:@"historySidebarCHIconTabViewItem"]) {
[mHistoryDataSource ensureDataSourceLoaded];
[mHistoryDataSource loadLazily];
[mHistoryDataSource enableObserver];
}

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -41,73 +42,37 @@
class nsIRDFDataSource;
class nsIRDFContainer;
class nsIRDFContainerUtils;
class nsIRDFResource;
class nsIRDFService;
@class ExtendedOutlineView;
// RDF Resource Wrapper to make the Outline View happy
@interface RDFOutlineViewItem : NSObject
{
nsIRDFResource* mResource;
unsigned int mCacheVersion; // if matches the cache version in the data source,
// cached values are valid
BOOL mExpandable;
int mNumChildren;
NSArray* mChildNodes; // array of |RDFOutlineViewItem|'s
}
- (nsIRDFResource*)resource; // addRefs the result
- (void)setResource:(nsIRDFResource*) aResource;
@end
@class RDFItem;
@interface RDFOutlineViewDataSource : NSObject
{
nsIRDFDataSource* mDataSource;
nsIRDFContainer* mContainer;
nsIRDFContainerUtils* mContainerUtils;
nsIRDFResource* mRootResource;
nsIRDFService* mRDFService;
RDFItem * mRootRDFItem;
IBOutlet ExtendedOutlineView* mOutlineView;
IBOutlet ExtendedOutlineView* mOutlineView;
NSMutableDictionary* mDictionary;
unsigned int mCacheVersion;
nsIRDFDataSource* mRDFDataSource;
nsIRDFContainer* mRDFContainer;
nsIRDFContainerUtils* mRDFContainerUtils;
nsIRDFService* mRDFService;
}
- (RDFItem *)rootRDFItem;
- (void)setRootRDFItem:(RDFItem*)item;
// Initialization Methods
- (void) ensureDataSourceLoaded;
- (void) loadLazily;
- (void) cleanupDataSource;
- (nsIRDFDataSource*) dataSource;
- (nsIRDFResource*) rootResource;
- (void) setDataSource: (nsIRDFDataSource*) aDataSource;
- (void) setRootResource: (nsIRDFResource*) aResource;
- (void)reloadDataForItem:(id)item reloadChildren: (BOOL)aReloadChildren;
- (void)invalidateCachedItems;
// Outline View Data Source methods
// Outline View Data Source methods
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item;
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item;
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
// tooltip support from the ExtendedOutlineView. Override to provide a tooltip
// other than the Name property for each item in the view.
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)inItem;
- (void)reloadDataForItem:(id)item reloadChildren: (BOOL)aReloadChildren;
// Implementation Methods
- (id)getWrapperFor:(nsIRDFResource*) aRDFResource;
- (void)invalidateCachedItems;
// override to do something different with the cell data rather than just
// return a string (add an icon in an attributed string, for example).
- (id)createCellContents:(NSString*)inValue withColumn:(NSString*)inColumn byItem:(id)inItem;
- (NSString*)getPropertyString:(NSString*)inPropertyURI forItem:(RDFOutlineViewItem*)inItem;
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)anItem;
@end

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -41,6 +42,7 @@
#import "RDFOutlineViewDataSource.h"
#import "CHBrowserService.h"
#import "ExtendedOutlineView.h"
#import "RDFItem.h"
#include "nsCRT.h"
#include "nsIRDFDataSource.h"
@ -58,123 +60,20 @@
#include "nsString.h"
@interface RDFOutlineViewItem(Private)
- (unsigned int)cacheVersion;
- (void)setNumChildren:(int)numChildren isExpandable:(BOOL)expandable cacheVersion:(unsigned int)version;
- (void)setChildren:(NSArray*)childArray;
- (BOOL)cacheValid:(unsigned int)version needChildren:(BOOL)needChildren;
- (BOOL)cachedChildren;
- (BOOL)isExpandable;
- (int)numChildren;
- (id)childAtIndex:(int)index;
@end
@implementation RDFOutlineViewItem
- (id)init
{
if ((self = [super init]))
{
mCacheVersion = 0;
mExpandable = NO;
mNumChildren = 0;
}
return self;
}
- (void)dealloc
{
[mChildNodes release];
NS_IF_RELEASE(mResource);
[super dealloc];
}
- (nsIRDFResource*)resource
{
NS_IF_ADDREF(mResource);
return mResource;
}
- (void)setResource:(nsIRDFResource*) aResource
{
nsIRDFResource* oldResource = mResource;
NS_IF_ADDREF(mResource = aResource);
NS_IF_RELEASE(oldResource);
}
- (unsigned int)cacheVersion
{
return mCacheVersion;
}
- (void)setNumChildren:(int)numChildren isExpandable:(BOOL)expandable cacheVersion:(unsigned int)version;
{
mNumChildren = numChildren;
mExpandable = expandable;
mCacheVersion = version;
}
- (void)setChildren:(NSArray*)childArray
{
// childArray can legally be nil here. If it is, we're clearing the cached children
NSArray* oldChildren = mChildNodes;
mChildNodes = childArray;
[mChildNodes retain];
[oldChildren release];
}
- (BOOL)cacheValid:(unsigned int)version needChildren:(BOOL)needChildren;
{
return (mCacheVersion == version) && (needChildren ? (mChildNodes != nil) : 1);
}
- (BOOL)cachedChildren
{
return (mChildNodes != nil);
}
- (BOOL)isExpandable
{
return mExpandable;
}
- (int)numChildren
{
return mNumChildren;
}
- (id)childAtIndex:(int)index
{
if (mChildNodes)
return [mChildNodes objectAtIndex:index];
return nil;
}
@interface RDFOutlineViewDataSource(Private)
- (void)cleanup;
@end
#pragma mark -
@interface RDFOutlineViewDataSource(Private)
- (void)registerForShutdownNotification;
- (void)cleanup;
- (void)updateItemProperties:(id)item enumerateChildren:(BOOL)doChildren;
@end
@implementation RDFOutlineViewDataSource
- (id)init
{
if ((self = [super init]))
{
[self registerForShutdownNotification];
mCacheVersion = 1;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(shutdown:)
name:XPCOMShutDownNotificationName object:nil];
}
return self;
}
@ -189,15 +88,11 @@
- (void)cleanup
{
NS_IF_RELEASE(mContainer);
NS_IF_RELEASE(mContainerUtils);
NS_IF_RELEASE(mRDFContainer);
NS_IF_RELEASE(mRDFContainerUtils);
NS_IF_RELEASE(mRDFService);
NS_IF_RELEASE(mDataSource);
NS_IF_RELEASE(mRootResource);
[mDictionary release];
mDictionary = nil;
NS_IF_RELEASE(mRDFDataSource);
}
- (void)cleanupDataSource
@ -205,270 +100,142 @@
[self cleanup];
}
- (RDFItem *)rootRDFItem;
{
return mRootRDFItem;
}
- (void)setRootRDFItem:(RDFItem*)item;
{
[mRootRDFItem autorelease];
mRootRDFItem = [item retain];
}
//
// ensureDataSourceLoaded
// loadLazily
//
// defer loading all this rdf junk until it's requested because it's slow
// TODO measure how slow it really is
//
- (void) ensureDataSourceLoaded
- (void) loadLazily
{
if ( !mContainer ) {
if ( !mRDFContainer ) {
nsCOMPtr<nsIRDFContainer> ctr = do_CreateInstance("@mozilla.org/rdf/container;1");
NS_ADDREF(mContainer = ctr);
NS_ADDREF(mRDFContainer = ctr);
nsCOMPtr<nsIRDFContainerUtils> ctrUtils = do_GetService("@mozilla.org/rdf/container-utils;1");
NS_ADDREF(mContainerUtils = ctrUtils);
NS_ADDREF(mRDFContainerUtils = ctrUtils);
nsCOMPtr<nsIRDFService> rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1");
NS_ADDREF(mRDFService = rdfService);
mDictionary = [[NSMutableDictionary alloc] initWithCapacity: 30];
mDataSource = nsnull;
mRootResource = nsnull;
mRDFDataSource = nsnull;
}
}
- (void)registerForShutdownNotification
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(shutdown:)
name: XPCOMShutDownNotificationName
object: nil];
}
- (void)shutdown: (NSNotification*)aNotification
- (void)shutdown:(NSNotification*)aNotification
{
[self cleanupDataSource];
}
- (nsIRDFDataSource*) dataSource
{
NS_IF_ADDREF(mDataSource);
return mDataSource;
}
- (nsIRDFResource*) rootResource
{
NS_IF_ADDREF(mRootResource);
return mRootResource;
}
#pragma mark -
- (void) setDataSource: (nsIRDFDataSource*) aDataSource
{
nsIRDFDataSource* oldDataSource = mDataSource;
NS_IF_ADDREF(mDataSource = aDataSource);
NS_IF_RELEASE(oldDataSource);
}
// Implementation of NSOutlineViewDataSource protocol
- (void) setRootResource: (nsIRDFResource*) aResource
{
nsIRDFResource* oldResource = mRootResource;
NS_IF_ADDREF(mRootResource = aResource);
NS_IF_RELEASE(oldResource);
}
//
// XXX - For now, we'll just say that none of our items are editable, as we aren't using any
// RDF datasources that are mutable.
//
- (BOOL) outlineView: (NSOutlineView*)aOutlineView shouldEditTableColumn: (NSTableColumn*) aTableColumn
item: (id) aItem
- (BOOL)outlineView:(NSOutlineView*)aOutlineView shouldEditTableColumn:(NSTableColumn*)aTableColumn item:(id)aItem
{
return NO;
}
- (BOOL) outlineView: (NSOutlineView*)aOutlineView isItemExpandable:(id)aItem
- (BOOL)outlineView:(NSOutlineView*)aOutlineView isItemExpandable:(id)aItem
{
if (!mDataSource)
if (!mRDFDataSource)
return NO;
if (!aItem)
return YES; // The root is always open
if (![aItem cacheValid:mCacheVersion needChildren:NO])
[self updateItemProperties:aItem enumerateChildren:NO];
return [aItem isExpandable];
if( [aItem isKindOfClass:[RDFItem class]] ) {
RDFItem * rdfItem = aItem;
return [rdfItem isExpandable];
}
return NO;
}
- (id)outlineView:(NSOutlineView*)aOutlineView child:(int)aIndex ofItem:(id)aItem
{
if (!mDataSource)
if (!mRDFDataSource)
return nil;
if (!aItem)
{
nsCOMPtr<nsIRDFResource> rootResource = dont_AddRef([self rootResource]);
aItem = [self getWrapperFor:rootResource];
if (!aItem) {
aItem = [self rootRDFItem];
}
if (![aItem cacheValid:mCacheVersion needChildren:YES])
[self updateItemProperties:aItem enumerateChildren:YES];
return [aItem childAtIndex:aIndex];
if( [aItem isKindOfClass:[RDFItem class]] ) {
RDFItem * rdfItem = aItem;
return [rdfItem childAtIndex:aIndex];
}
return nil;
}
- (int)outlineView:(NSOutlineView*)aOutlineView numberOfChildrenOfItem:(id) aItem;
- (int)outlineView:(NSOutlineView*)aOutlineView numberOfChildrenOfItem:(id)aItem;
{
if (!mDataSource)
if (!mRDFDataSource)
return 0;
if (!aItem)
{
nsCOMPtr<nsIRDFResource> rootResource = dont_AddRef([self rootResource]);
aItem = [self getWrapperFor:rootResource];
if (!aItem) {
aItem = [self rootRDFItem];
}
if (![aItem cacheValid:mCacheVersion needChildren:YES])
[self updateItemProperties:aItem enumerateChildren:YES];
return [aItem numChildren];
if( [aItem isKindOfClass:[RDFItem class]] ) {
RDFItem * rdfItem = aItem;
return [rdfItem numChildren];
}
return 0;
}
- (id)outlineView:(NSOutlineView*)aOutlineView objectValueForTableColumn:(NSTableColumn*)aTableColumn
byItem:(id)aItem
// TODO move to subclass
- (id)outlineView:(NSOutlineView*)aOutlineView objectValueForTableColumn:(NSTableColumn*)aTableColumn byItem:(id)aItem
{
if (!mDataSource || !aItem)
if (!mRDFDataSource || !aItem)
return nil;
// The table column's identifier is the last part of the RDF Resource URI of the property
// being displayed in that column, e.g. "http://home.netscape.com/NC-rdf#Name"
// little hack inserted until history moves to new bookmark format
NSString *identifier = [aTableColumn identifier];
if ([identifier isEqualToString:@"title"])
identifier = [NSString stringWithString:@"Name"];
NSString* columnPropertyURI = [NSString stringWithFormat:@"http://home.netscape.com/NC-rdf#%@",
identifier];
NSString* propString = [self getPropertyString:columnPropertyURI forItem:aItem];
return [self createCellContents:propString withColumn:columnPropertyURI byItem:aItem];
NSString *resourceName;
if( [[aTableColumn identifier] isEqualToString:@"title"] )
resourceName = @"Name";
else if( [[aTableColumn identifier] isEqualToString:@"url"] )
resourceName = @"URL";
else
return nil;
NSString * RDFURI = @"http://home.netscape.com/NC-rdf#";
NSString * RDFpropertyURI = [RDFURI stringByAppendingString:resourceName];
NSString * propertyString = [aItem getStringForRDFPropertyURI:RDFpropertyURI];
return propertyString;
}
//
// createCellContents:withColumn:byItem
//
// Constructs a NSString from the given string data for this item in the given column.
// This should be overridden to do more fancy things, such as add an icon, etc.
//
- (id)createCellContents:(NSString*)inValue withColumn:(NSString*)inColumn byItem:(id)inItem
{
return inValue;
}
//
// outlineView:tooltipForString
//
// returns the value of the Name property as the tooltip for the given item. Override to do
// returns the value of the Name property as the tooltip for the given item. Override to do
// anything more complicated
//
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)inItem
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)anItem
{
return [self getPropertyString:@"http://home.netscape.com/NC-rdf#Name" forItem:inItem];
return [anItem getStringForRDFPropertyURI:@"http://home.netscape.com/NC-rdf#Name"];
}
- (void) reloadDataForItem: (id) aItem reloadChildren: (BOOL) aReloadChildren
#pragma mark -
- (void)reloadDataForItem:(id)aItem reloadChildren:(BOOL)aReloadChildren
{
if (!aItem)
[mOutlineView reloadData];
else
[mOutlineView reloadItem: aItem reloadChildren: aReloadChildren];
}
- (id)getWrapperFor:(nsIRDFResource*) aRDFResource
{
const char* k;
aRDFResource->GetValueConst(&k);
NSString* key = [NSString stringWithCString:k];
// see if we've created a wrapper already, if not, create a new wrapper object
// and stash it in our dictionary
RDFOutlineViewItem* item = [mDictionary objectForKey:key];
if (!item)
{
item = [[[RDFOutlineViewItem alloc] init] autorelease];
[item setResource: aRDFResource];
[mDictionary setObject:item forKey:key]; // retains |item|
}
return item;
[mOutlineView reloadItem:aItem reloadChildren:aReloadChildren];
}
- (void)invalidateCachedItems
{
mCacheVersion++;
[[self rootRDFItem] invalidateCache];
}
- (void)updateItemProperties:(id)item enumerateChildren:(BOOL)doChildren
{
BOOL isExpandable = NO;
NSMutableArray* itemChildren = doChildren ? [[[NSMutableArray alloc] initWithCapacity:10] autorelease] : nil;
nsCOMPtr<nsIRDFResource> itemResource = dont_AddRef([item resource]);
PRBool isSeq = PR_FALSE;
mContainerUtils->IsSeq(mDataSource, itemResource, &isSeq);
if (isSeq)
isExpandable = YES;
nsCOMPtr<nsIRDFResource> childProperty;
mRDFService->GetResource(nsDependentCString("http://home.netscape.com/NC-rdf#child"), getter_AddRefs(childProperty));
nsCOMPtr<nsISimpleEnumerator> childNodes;
mDataSource->GetTargets(itemResource, childProperty, PR_TRUE, getter_AddRefs(childNodes));
PRBool hasMore = PR_FALSE;
while (NS_SUCCEEDED(childNodes->HasMoreElements(&hasMore)) && hasMore)
{
nsCOMPtr<nsISupports> supp;
childNodes->GetNext(getter_AddRefs(supp));
nsCOMPtr<nsIRDFResource> childResource = do_QueryInterface(supp);
if (childResource)
{
id childItem = [self getWrapperFor:childResource];
if (childItem)
{
isExpandable = YES;
if (!itemChildren) break; // know enough already
[itemChildren addObject:childItem];
}
}
}
// itemChildren will be nil here if we don't care about children, but that's OK.
// the setChildren call will clear the cached child list.
[item setNumChildren:[itemChildren count] isExpandable:isExpandable cacheVersion:mCacheVersion];
[item setChildren:itemChildren];
}
- (NSString*)getPropertyString:(NSString*)inPropertyURI forItem:(RDFOutlineViewItem*)inItem
{
nsCOMPtr<nsIRDFResource> propertyResource;
mRDFService->GetResource(nsDependentCString([inPropertyURI UTF8String]), getter_AddRefs(propertyResource));
nsCOMPtr<nsIRDFResource> resource = dont_AddRef([inItem resource]);
nsCOMPtr<nsIRDFNode> valueNode;
mDataSource->GetTarget(resource, propertyResource, PR_TRUE, getter_AddRefs(valueNode));
if (!valueNode) {
#if DEBUG
NSLog(@"ValueNode is null in RDF objectValueForTableColumn");
#endif
return @"";
}
nsCOMPtr<nsIRDFLiteral> valueLiteral(do_QueryInterface(valueNode));
if (!valueLiteral)
return @"";
const PRUnichar* value = NULL;
valueLiteral->GetValueConst(&value);
if (value)
return [NSString stringWithCharacters:value length:nsCRT::strlen(value)];
return @"";
}
@end

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -39,23 +40,24 @@
#import "RDFOutlineViewDataSource.h"
class nsAString;
class HistoryDataSourceObserver;
class HistoryRDFObserver;
@class BrowserWindowController;
@class HistoryItem;
@interface HistoryDataSource : RDFOutlineViewDataSource
{
HistoryDataSourceObserver* mObserver; // STRONG ref, should be nsCOMPtr but can't
HistoryRDFObserver* mObserver; // STRONG ref, should be nsCOMPtr but can't
IBOutlet BrowserWindowController* mBrowserWindowController;
BOOL mUpdatesEnabled;
BOOL mNeedsRefresh;
BOOL mUpdatesEnabled;
BOOL mNeedsRefresh;
bool mLoaded;
HistoryItem * mRootHistoryItem;
}
// overridden to create a attributed string with icon
- (id)createCellContents:(NSString*)inValue withColumn:(NSString*)inColumn byItem:(id) inItem;
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)inItem;
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item;
- (HistoryItem *)rootRDFItem;
- (void)setRootRDFItem:(HistoryItem *)item;
- (void)enableObserver;
- (void)disableObserver;
@ -69,5 +71,11 @@ class HistoryDataSourceObserver;
- (IBAction)openHistoryItemInNewWindow:(id)aSender;
- (IBAction)openHistoryItemInNewTab:(id)aSender;
// NSOutlineViewDataSource protocol
- (BOOL)outlineView:(NSOutlineView *)ov writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard;
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)anItem;
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)inCell forTableColumn:(NSTableColumn *)tableColumn item:(id)item;
- (NSMenu *)outlineView:(NSOutlineView *)outlineView contextMenuForItem:(id)item;
@end

Просмотреть файл

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -43,6 +44,7 @@
#import "CHBrowserView.h"
#import "ExtendedOutlineView.h"
#import "PreferenceManager.h"
#import "HistoryItem.h"
#include "nsIRDFService.h"
#include "nsIRDFDataSource.h"
@ -56,21 +58,19 @@
#include "nsComponentManagerUtils.h"
//
// class HistoryDataSourceObserver
//
// An observer, enabled whenver the history panel in the drawer is selected. Tracks
// changes to the RDF data source and pokes the history outline view
// Uses the Gecko RDF data source for history,
// and presents a Cocoa API through the NSOutlineViewDataSource protocol
//
class HistoryDataSourceObserver : public nsIRDFObserver
class HistoryRDFObserver : public nsIRDFObserver
{
public:
HistoryDataSourceObserver(HistoryDataSource* dataSource)
HistoryRDFObserver(HistoryDataSource* dataSource)
: mHistoryDataSource(dataSource)
{
NS_INIT_ISUPPORTS();
}
virtual ~HistoryDataSourceObserver() { }
virtual ~HistoryRDFObserver() { }
NS_DECL_ISUPPORTS
@ -90,7 +90,7 @@ private:
HistoryDataSource* mHistoryDataSource;
};
NS_IMPL_ISUPPORTS1(HistoryDataSourceObserver, nsIRDFObserver)
NS_IMPL_ISUPPORTS1(HistoryRDFObserver, nsIRDFObserver)
//
@ -100,8 +100,8 @@ NS_IMPL_ISUPPORTS1(HistoryDataSourceObserver, nsIRDFObserver)
// open.
//
NS_IMETHODIMP
HistoryDataSourceObserver::OnAssert(nsIRDFDataSource*, nsIRDFResource*,
nsIRDFResource* aProperty, nsIRDFNode*)
HistoryRDFObserver::OnAssert(nsIRDFDataSource*, nsIRDFResource*,
nsIRDFResource* aProperty, nsIRDFNode*)
{
const char* p;
aProperty->GetValueConst(&p);
@ -117,8 +117,8 @@ HistoryDataSourceObserver::OnAssert(nsIRDFDataSource*, nsIRDFResource*,
// This gets called on redirects, when nsGlobalHistory::RemovePage is called.
//
NS_IMETHODIMP
HistoryDataSourceObserver::OnUnassert(nsIRDFDataSource*, nsIRDFResource*,
nsIRDFResource* aProperty, nsIRDFNode*)
HistoryRDFObserver::OnUnassert(nsIRDFDataSource*, nsIRDFResource*,
nsIRDFResource* aProperty, nsIRDFNode*)
{
const char* p;
aProperty->GetValueConst(&p);
@ -135,27 +135,33 @@ HistoryDataSourceObserver::OnUnassert(nsIRDFDataSource*, nsIRDFResource*,
// visiting them and they change date
//
NS_IMETHODIMP
HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
HistoryRDFObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
nsIRDFResource* aProperty, nsIRDFNode*, nsIRDFNode*)
{
const char* p;
aProperty->GetValueConst(&p);
if (strcmp("http://home.netscape.com/NC-rdf#Date", p) == 0)
[mHistoryDataSource setNeedsRefresh:YES];
return NS_OK;
}
#pragma mark -
@interface HistoryDataSource(Private)
- (void)cleanupHistory;
- (void)removeItemFromHistory:(id)inItem withService:(nsIBrowserHistory*)inHistService;
- (void)cleanupDataSource;
- (void)removeItemFromGeckoHistory:(id)anItem withService:(nsIBrowserHistory*)inHistService;
@end
#pragma mark -
@implementation HistoryDataSource
- (void) dealloc
@ -167,49 +173,69 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
// "non-virtual" cleanup method -- safe to call from dealloc.
- (void)cleanupHistory
{
if (mDataSource && mObserver)
{
mDataSource->RemoveObserver(mObserver);
if (mRDFDataSource && mObserver) {
mRDFDataSource->RemoveObserver(mObserver);
NS_RELEASE(mObserver); // nulls it
}
}
// "virtual" method; called from superclass
- (void)cleanupDataSource
- (void)cleanupDataSource;
{
[self cleanupHistory];
[self cleanupHistory];
[super cleanupDataSource];
}
-(bool) loaded;
{
return mLoaded;
}
- (HistoryItem *)rootRDFItem;
{
return mRootHistoryItem;
}
- (void)setRootRDFItem:(HistoryItem *)item;
{
[mRootHistoryItem autorelease];
mRootHistoryItem = [item retain];
}
//
// ensureDataSourceLoaded
// loadLazily
//
// Called when the history panel is selected or brought into view. Inits all
// the RDF-fu to make history go. We defer loading everything because it's
// sorta slow and we don't want to take the hit when the user creates new windows
// or just opens the bookmarks panel.
//
- (void) ensureDataSourceLoaded
// in addition currently we don't get any gecko updates after we load so this
// will make sure it's up-to-date
//
- (void) loadLazily;
{
[super ensureDataSourceLoaded];
[super loadLazily];
NS_ASSERTION(mRDFService, "Uh oh, RDF service not loaded in parent class");
if ( !mDataSource )
{
if( !mRDFDataSource ) {
// Get the Global History DataSource
mRDFService->GetDataSource("rdf:history", &mDataSource);
mRDFService->GetDataSource("rdf:history", &mRDFDataSource);
// Get the Date Folder Root
mRDFService->GetResource(nsDependentCString("NC:HistoryByDate"), &mRootResource);
nsIRDFResource* aRDFRootResource;
mRDFService->GetResource(nsDependentCString("NC:HistoryByDate"), &aRDFRootResource);
HistoryItem * newRoot = [HistoryItem alloc];
[newRoot initWithRDFResource:aRDFRootResource RDFDataSource:mRDFDataSource parent:nil];
[self setRootRDFItem:newRoot];
// identifiers: title url description keyword
[mOutlineView setTarget: self];
[mOutlineView setDoubleAction: @selector(openHistoryItem:)];
[mOutlineView setDeleteAction: @selector(deleteHistoryItems:)];
mObserver = new HistoryDataSourceObserver(self);
mObserver = new HistoryRDFObserver(self);
if ( mObserver ) {
NS_ADDREF(mObserver);
mDataSource->AddObserver(mObserver);
mRDFDataSource->AddObserver(mObserver);
}
[mOutlineView reloadData];
}
@ -220,7 +246,7 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
[mOutlineView reloadData];
}
NS_ASSERTION(mDataSource, "Uh oh, History RDF Data source not created");
NS_ASSERTION(mRDFDataSource, "Uh oh, History RDF Data source not created");
}
- (void)enableObserver
@ -250,82 +276,14 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
[self invalidateCachedItems];
if (mUpdatesEnabled)
{
// this can be very slow! See bug 180109.
//NSLog(@"history reload started");
[self reloadDataForItem:nil reloadChildren:NO];
//NSLog(@"history reload done");
}
mNeedsRefresh = NO;
}
}
//
// createCellContents:withColumn:byItem
//
// override to look up the URL if the name is empty. We'll obviously always have a
// name.
//
-(id) createCellContents:(NSString*)inValue withColumn:(NSString*)inColumn byItem:(id) inItem
{
NSString *fragment = [[NSURL URLWithString:inColumn] fragment];
if (([inValue length] == 0) && [fragment isEqualToString:@"Name"])
inValue = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:inItem];
return inValue;
}
//
// filterDragItems:
//
// Walk the list of items, filtering out any folder. Returns a new list
// that has been autoreleased.
//
- (NSArray*)filterDragItems:(NSArray*)inItems
{
NSMutableArray* outItems = [[[NSMutableArray alloc] init] autorelease];
NSEnumerator *enumerator = [inItems objectEnumerator];
id obj;
while ( (obj = [enumerator nextObject]) ) {
if ( ! [mOutlineView isExpandable: obj] ) // if it's not a folder, we can drag it
[outItems addObject:obj];
}
return outItems;
}
- (BOOL)outlineView:(NSOutlineView *)ov writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard
{
//Need to filter out folders from the list, only allow the urls to be dragged
NSArray *toDrag = [self filterDragItems:items];
int count = [toDrag count];
if (count > 0) {
if (count == 1) {
id item = [toDrag objectAtIndex: 0];
// if we have just one item, we add some more flavours
NSString* url = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:item];
NSString* title = [self getPropertyString:@"http://home.netscape.com/NC-rdf#Name" forItem:item];
NSString *cleanedTitle = [title stringByReplacingCharactersInSet:[NSCharacterSet controlCharacterSet] withString:@" "];
[pboard declareURLPasteboardWithAdditionalTypes:[NSArray array] owner:self];
[pboard setDataForURL:url title:cleanedTitle];
}
else {
// not sure what to do here. canceling the drag for now.
return NO;
}
return YES;
}
return NO;
}
//
// this better be coming from a context menu
//
// this should only come from a context menu
// ... because we use [aSender representedObject] apparently...
-(IBAction)openHistoryItemInNewTab:(id)aSender
{
NSString *url = [aSender representedObject];
@ -335,10 +293,8 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
}
}
//
// this better be coming from a context menu
//
// this should only come from a context menu
// ... because we use [aSender representedObject] apparently...
-(IBAction)openHistoryItemInNewWindow:(id)aSender
{
NSString *url = [aSender representedObject];
@ -348,150 +304,150 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
}
}
-(IBAction)openHistoryItem: (id)aSender
- (IBAction)openHistoryItem:(id)aSender
{
int index = [mOutlineView selectedRow];
if (index == -1)
return;
id item = [mOutlineView itemAtRow: index];
if (!item)
return;
// expand if collapsed and double click
if ([mOutlineView isExpandable: item]) {
if ([mOutlineView isItemExpanded: item])
[mOutlineView collapseItem: item];
else
[mOutlineView expandItem: item];
return;
}
// get uri
NSString* url = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:item];
[[mBrowserWindowController getBrowserWrapper] loadURI: url referrer: nil flags: NSLoadFlagsNone activate:YES];
int index = [mOutlineView selectedRow];
if (index == -1)
return;
id item = [mOutlineView itemAtRow: index];
if (!item)
return;
if ([mOutlineView isExpandable: item]) { //TODO [item class]
if ([mOutlineView isItemExpanded: item])
[mOutlineView collapseItem: item];
else
[mOutlineView expandItem: item];
} else {
[[mBrowserWindowController getBrowserWrapper] loadURI:[item url] referrer:nil flags:NSLoadFlagsNone activate:YES];
}
}
//
// deleteHistoryItems:
//
// Called when user hits backspace with the history view selected. Walks the selection
// removing the selected items from the global history in batch-mode, then reloads
// the outline.
//
-(IBAction)deleteHistoryItems: (id)aSender
// Walks the selection removing the selected items from the global history
// in batch-mode, then reload the outline.
- (IBAction)deleteHistoryItems:(id)aSender
{
int index = [mOutlineView selectedRow];
if (index == -1)
return;
// if the user selected a bunch of rows, we want to clear the selection since when
// those rows are deleted, the tree will try to keep the same # of rows selected and
// it will look really odd. If just 1 row was selected, we keep it around so the user
// can keep quickly deleting one row at a time.
BOOL clearSelectionWhenDone = [mOutlineView numberOfSelectedRows] > 1;
// If just 1 row was selected, keep it so the user can delete again immediately
BOOL clearSelectionWhenDone = ([mOutlineView numberOfSelectedRows] > 1);
nsCOMPtr<nsIBrowserHistory> history = do_GetService("@mozilla.org/browser/global-history;1");
if ( history ) {
// Even though it looks like relying on row numbers as we delete will get us in trouble
// and out of sync, until we actually invalidate the table, the rows keep their prior values.
// If children are selected as well as the parent, we'll just end up trying to remove the
// host string from history which will silently fail. It's extra work, but harmless.
nsCOMPtr<nsIRDFDataSource> ds = do_QueryInterface(history);
ds->BeginUpdateBatch();
NSEnumerator* rowEnum = [mOutlineView selectedRowEnumerator];
for ( NSNumber* currIndex = [rowEnum nextObject]; currIndex; currIndex = [rowEnum nextObject]) {
index = [currIndex intValue];
RDFOutlineViewItem* item = [mOutlineView itemAtRow: index];
if ([mOutlineView isExpandable: item]) {
// delete a folder by iterating over each of its children and deleting them. There
// should be a better way, but the history api's don't really support them. Expand
// the folder before we delete it otherwise we don't get any child nodes to delete.
[mOutlineView expandItem:item];
NSEnumerator* childEnum = [item->mChildNodes objectEnumerator];
RDFOutlineViewItem* currChild = nil;
while ( (currChild = [childEnum nextObject]) )
[self removeItemFromHistory:currChild withService:history];
}
else
[self removeItemFromHistory:item withService:history];
// row numbers will stay in sync until table is invalidated
// removing children of removed items will fail silently (harmless)
NSEnumerator* rowEnum = [mOutlineView selectedRowEnumerator];
int currentRow;
while( (currentRow = [[rowEnum nextObject] intValue]) ) {
HistoryItem * item = [mOutlineView itemAtRow:currentRow];
if( [item isKindOfClass:[RDFItem class]] ) {
[(HistoryItem*)item deleteFromGecko];
[[item parent] deleteChildFromCache:item];
}
ds->EndUpdateBatch();
if ( clearSelectionWhenDone )
[mOutlineView deselectAll:self];
[self invalidateCachedItems];
[mOutlineView reloadData]; // necessary or the outline is really horked
}
}
-(void)removeItemFromHistory:(id)inItem withService:(nsIBrowserHistory*)inHistService;
{
NSString* urlString = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:inItem];
inHistService->RemovePage([urlString UTF8String]);
if ( clearSelectionWhenDone )
[mOutlineView deselectAll:self];
[mOutlineView reloadData]; // tell outline view the data source has changed
}
//
// outlineView:tooltipForString
//
// Only show a tooltip if we're not a folder. For urls, use title\nurl as the format.
// We can re-use the base-class impl to get the title of the page and then just add on
// to that.
//
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)inItem
#pragma mark -
// Implementation of NSOutlineViewDataSource protocol
// identifiers: title url description keyword
- (id)outlineView:(NSOutlineView*)aOutlineView objectValueForTableColumn:(NSTableColumn*)aTableColumn byItem:(id)aItem
{
if ( ! [mOutlineView isExpandable: inItem] ) {
// use baseclass to get title of page
NSString* pageTitle = [super outlineView:outlineView tooltipStringForItem:inItem];
if (!mRDFDataSource || !aItem)
return nil;
// use the table column identifier as a key
if( [[aTableColumn identifier] isEqualToString:@"title"] )
return [aItem name];
else if( [[aTableColumn identifier] isEqualToString:@"url"] )
return [aItem url];
// else if( [[aTableColumn identifier] isEqualToString:@"description"] )
// return [aItem date];
return nil;
// TODO truncate string
// - (void)truncateToWidth:(float)maxWidth at:kTruncateAtMiddle withAttributes:(NSDictionary *)attributes
}
- (BOOL)outlineView:(NSOutlineView *)ov writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard;
{
//Need to filter out folders from the list, only allow the urls to be dragged
NSMutableArray* toDrag = [[[NSMutableArray alloc] init] autorelease];
NSEnumerator *enumerator = [items objectEnumerator];
id obj;
while( (obj = [enumerator nextObject]) ) {
if( ![obj isExpandable] )
[toDrag addObject:obj];
}
// append url
NSString* url = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:inItem];
int count = [toDrag count];
if (count == 1) {
id item = [toDrag objectAtIndex: 0];
// if we have just one item, we add some more flavours
NSString* title = [item name];
NSString *cleanedTitle
= [title stringByReplacingCharactersInSet:[NSCharacterSet controlCharacterSet] withString:@" "];
[pboard declareURLPasteboardWithAdditionalTypes:[NSArray array] owner:self];
[pboard setDataForURL:[item url] title:cleanedTitle];
return YES;
}
if( count > 1 ) {
// not sure what to do for multiple drag. just cancel for now.
return NO;
}
return NO;
}
// Only show a tooltip if we're not a folder. For items, use name\nurl as the format.
- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)anItem;
{
if( ![anItem isExpandable] ) {
NSString* pageTitle = [anItem name];
NSString* url = [anItem url];
return [NSString stringWithFormat:@"%@\n%@", pageTitle, [url stringByTruncatingTo:80 at:kTruncateAtEnd]];
}
return nil;
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)inCell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
// TODO site icons
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)inCell forTableColumn:(NSTableColumn *)tableColumn item:(id)item;
{
// set the image on the name column. the url column doesn't have an image.
if ([[tableColumn identifier] isEqualToString: @"title"]) {
if ( [outlineView isExpandable: item] )
if ([[tableColumn identifier] isEqualToString:@"title"]) {
if ( [outlineView isExpandable:item] )
[inCell setImage:[NSImage imageNamed:@"folder"]];
else
[inCell setImage:[NSImage imageNamed:@"smallbookmark"]];
}
}
- (NSMenu *)outlineView:(NSOutlineView *)outlineView contextMenuForItem:(id)item
- (NSMenu *)outlineView:(NSOutlineView *)outlineView contextMenuForItem:(id)item;
{
if (nil == item || [mOutlineView isExpandable: item])
return nil;
// get item's URL, prep context menu
NSString* url = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:item];
NSString *nulString = [NSString string];
NSMenu *contextMenu = [[[NSMenu alloc] initWithTitle:@"snookums"] autorelease];
// open in new window
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Open in New Window",@"Open in New Window") action:@selector(openHistoryItemInNewWindow:) keyEquivalent:nulString];
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Open in New Window",@"") action:@selector(openHistoryItemInNewWindow:) keyEquivalent:nulString];
[menuItem setTarget:self];
[menuItem setRepresentedObject:url];
[menuItem setRepresentedObject:[item url]];
[contextMenu addItem:menuItem];
[menuItem release];
// open in new tab
menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Open in New Tab",@"Open in New Tab") action:@selector(openHistoryItemInNewTab:) keyEquivalent:nulString];
menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Open in New Tab",@"") action:@selector(openHistoryItemInNewTab:) keyEquivalent:nulString];
[menuItem setTarget:self];
[menuItem setRepresentedObject:url];
[menuItem setRepresentedObject:[item url]];
[contextMenu addItem:menuItem];
[menuItem release];
// delete
menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete",@"Delete") action:@selector(deleteHistoryItems:) keyEquivalent:nulString];
menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete",@"") action:@selector(deleteHistoryItems:) keyEquivalent:nulString];
[menuItem setTarget:self];
[contextMenu addItem:menuItem];
[menuItem release];

Просмотреть файл

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#import <Appkit/Appkit.h>
#import "RDFItem.h"
// YES if you want to have history flattened
// eliminates the per-site folders that nsGlobalHistory returns
// and also sorts the resulting flat list by date
const bool kFlattenHistory = YES;
// URIs for RDF properties
#define NC_NAME_KEY @"http://home.netscape.com/NC-rdf#Name"
#define NC_URL_KEY @"http://home.netscape.com/NC-rdf#URL"
#define NC_DATE_KEY @"http://home.netscape.com/NC-rdf#Date"
// HistoryItem's are the rows of the history outline view
// extends RDFItem to support flattened history list
@interface HistoryItem : RDFItem
{
NSArray * mGrandChildNodes;
}
- (void)deleteFromGecko;
- (void)buildGrandChildCache;
- (HistoryItem*)childAtIndex:(int)index;
// RDF properties
- (NSString*)name;
- (NSString*)url;
- (NSString*)date;
@end

Просмотреть файл

@ -0,0 +1,233 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// these objects add an additional grand child cache
// why? because we flatten the list by skipping the children RDFItems
// and just returning the full list of grandchildren as our children
#import "HistoryItem.h"
#include "nsIRDFService.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFResource.h"
#include "nsIBrowserHistory.h"
#include "nsIServiceManager.h"
#include "nsXPIDLString.h"
#include "nsString.h"
#include "nsComponentManagerUtils.h"
@interface HistoryItem (Private)
- (HistoryItem*)newChild;
- (void)invalidateGrandChildCache;
@end
#pragma mark -
@implementation HistoryItem
- (id)init
{
if( (self = [super init]) ) {
mGrandChildNodes = nil;
}
return self;
}
- (void)dealloc
{
[mGrandChildNodes autorelease];
[super dealloc];
}
- (HistoryItem*)newChild;
{
return [HistoryItem alloc];
}
- (void)deleteFromGecko;
{
nsCOMPtr<nsIBrowserHistory> historyService = do_GetService("@mozilla.org/browser/global-history;1");
if( !historyService )
return;
nsCOMPtr<nsIRDFDataSource> histDataSource = do_QueryInterface(historyService);
if( !histDataSource )
return;
histDataSource->BeginUpdateBatch();
if( ![self isExpandable] ) {
NSString* urlString = [self url];
historyService->RemovePage([urlString UTF8String]);
}
else {
// delete a folder by iterating over each of its children and deleting them. There
// should be a better way, but the history api's don't really support them. Expand
// the folder before we delete it otherwise we don't get any child nodes to delete.
//TODO what if the children have children?
int numChilds = [self numChildren];
for( int i = 0; i < numChilds; i++ ) {
HistoryItem * deleteChild = [self childAtIndex:i];
[deleteChild deleteFromGecko];
}
}
histDataSource->EndUpdateBatch();
}
// Explicitly call to superclass
// to avoid recursive loops
- (bool)nativeIsExpandable;
{ return [super isExpandable]; }
- (int)nativeNumChildren;
{ return [super numChildren]; }
- (id)nativeChildAtIndex:(int)index;
{ return [super childAtIndex:index]; }
- (void)nativeBuildChildCache;
{ [super buildChildCache]; }
// should we return grandchildren nodes instead of the children nodes?
// this determines if we flatten the RDF at this "level"
- (bool)shouldUseGrandChildNodes;
{
if( !kFlattenHistory )
return NO;
if( ![self nativeIsExpandable] )
return NO;
if( !mChildNodes ) [self nativeBuildChildCache];
HistoryItem * firstChild = [mChildNodes objectAtIndex:0];
if( [firstChild nativeIsExpandable] ) {
HistoryItem * grandChild = [firstChild nativeChildAtIndex:0];
//is it a leaf node?
if( ![grandChild nativeIsExpandable] )
return YES;
}
return NO;
}
- (HistoryItem*)childAtIndex:(int)index;
{
if( !kFlattenHistory )
return [super childAtIndex:index];
if( ![self shouldUseGrandChildNodes] )
return [super childAtIndex:index];
if( !mGrandChildNodes )
[self buildGrandChildCache];
return [mGrandChildNodes objectAtIndex:index];
}
- (int)numChildren;
{
if( !kFlattenHistory )
return [super numChildren];
if( ![self shouldUseGrandChildNodes] )
return [super numChildren];
if( !mGrandChildNodes )
[self buildGrandChildCache];
int result = [mGrandChildNodes count];
return result;
}
- (NSComparisonResult)historyItemDateCompare:(HistoryItem *)aItem;
{
//backwards for reverse order
NSComparisonResult result = [[aItem date] caseInsensitiveCompare:[self date]];
return result;
}
- (void)buildGrandChildCache;
{
if( !kFlattenHistory )
return;
if( ![self shouldUseGrandChildNodes] )
return;
// NSLog(@"Building grandChildNodes list for name=%@", [self name]);
NSMutableArray * grandChildNodes = [[NSMutableArray alloc] init];
int childCount = [mChildNodes count];
for( int i=0; i < childCount; i++ ) {
HistoryItem * curChild = [mChildNodes objectAtIndex:i];
int grandChildCount = [curChild nativeNumChildren];
for( int j=0; j < grandChildCount; j++ ) {
HistoryItem * curGrandChild = [curChild nativeChildAtIndex:j];
[grandChildNodes addObject:curGrandChild];
}
}
NSArray * sorted = [grandChildNodes sortedArrayUsingSelector:@selector(historyItemDateCompare:)];
mGrandChildNodes = [sorted retain];
}
- (void)invalidateGrandChildCache;
{
[mGrandChildNodes autorelease];
mGrandChildNodes = nil;
}
// override superclass, because we need to also invalidate the
// grandchildren cache of our parent (the caller's grandparent)
- (void)deleteChildFromCache:(HistoryItem*)child;
{
[super deleteChildFromCache:child];
if( [self parent] )
[(HistoryItem*)[self parent] invalidateGrandChildCache];
}
// RDF Properties
- (NSString*)name;
{
return [self getStringForRDFPropertyURI:NC_NAME_KEY];
}
- (NSString*)url;
{
return [self getStringForRDFPropertyURI:NC_URL_KEY];
}
- (NSString*)date;
{
return [self getStringForRDFPropertyURI:NC_DATE_KEY];
}
@end

Просмотреть файл

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#import <Appkit/Appkit.h>
class nsIRDFDataSource;
class nsIRDFContainer;
class nsIRDFContainerUtils;
class nsIRDFResource;
class nsIRDFService;
// RDFItems make up rows of an RDF outline view
@interface RDFItem : NSObject
{
NSMutableArray* mChildNodes; // array of RDFItem
RDFItem* mParent;
NSMutableDictionary * mPropertyCache;
nsIRDFResource* mRDFResource;
nsIRDFDataSource* mRDFDataSource;
nsIRDFContainer* mRDFContainer;
nsIRDFContainerUtils* mRDFContainerUtils;
nsIRDFService* mRDFService;
}
- (id)initWithRDFResource:(nsIRDFResource*)aRDFResource RDFDataSource:(nsIRDFDataSource*)aRDFDataSource parent:(RDFItem*)newparent;
- (NSString*)getStringForRDFPropertyURI:(NSString*)aPropertyURI;
- (BOOL)isExpandable;
- (RDFItem*)childAtIndex:(int)index;
- (int)numChildren;
- (RDFItem*)parent;
- (void)deleteChildFromCache:(RDFItem*)child;
- (void)buildChildCache;
- (void)invalidateCache;
@end

Просмотреть файл

@ -0,0 +1,311 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
* Simon Woodside <sbwoodside@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// These objects cache their children to avoid excessive lookups through
// the gecko API, which can be slow
#import "RDFItem.h"
#import "NSString+Utils.h"
#include "nsCRT.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFService.h"
#include "nsIRDFLiteral.h"
#include "nsIRDFResource.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
#include "nsRDFCID.h"
#include "nsComponentManagerUtils.h"
#include "nsIServiceManagerUtils.h"
#include "nsString.h"
@interface RDFItem (Private)
- (unsigned int)cacheVersion;
- (BOOL)cachedChildren;
- (void)buildChildCache;
- (NSString*)getTextForNode:(nsIRDFNode *)aNode;
@end
#pragma mark -
@implementation RDFItem
- (id)init
{
if ((self = [super init])) {
nsCOMPtr<nsIRDFContainer> ctr = do_CreateInstance("@mozilla.org/rdf/container;1");
NS_ADDREF(mRDFContainer = ctr);
nsCOMPtr<nsIRDFContainerUtils> ctrUtils = do_GetService("@mozilla.org/rdf/container-utils;1");
NS_ADDREF(mRDFContainerUtils = ctrUtils);
nsCOMPtr<nsIRDFService> rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1");
NS_ADDREF(mRDFService = rdfService);
mRDFDataSource = nsnull;
mRDFResource = nsnull;
mPropertyCache = [[[NSMutableDictionary alloc] init] retain];
}
return self;
}
- (id)initWithRDFResource:(nsIRDFResource*)aRDFResource RDFDataSource:(nsIRDFDataSource*)aRDFDataSource parent:(RDFItem*)newparent;
{
if( (self = [self init]) ) {
NS_IF_ADDREF(mRDFResource = aRDFResource);
mRDFDataSource = aRDFDataSource;
mParent = newparent;
}
return self;
}
- (void)dealloc
{
[mChildNodes autorelease];
[mPropertyCache autorelease];
NS_IF_RELEASE(mRDFResource);
[super dealloc];
}
- (RDFItem*)newChild;
{
return [RDFItem alloc];
}
- (void)invalidateCache;
{
if( mChildNodes ) {
//invalidate children first (in case they have children)
[mChildNodes makeObjectsPerformSelector:@selector(invalidateCache)];
[mChildNodes autorelease];
mChildNodes = nil;
}
}
- (BOOL)cachedChildren
{
return (mChildNodes != nil);
}
- (BOOL)isExpandable
{
if( mChildNodes )
return YES; // cached already
// Query gecko for the answer
PRBool isSeq = PR_FALSE;
mRDFContainerUtils->IsSeq(mRDFDataSource, mRDFResource, &isSeq);
if (isSeq)
return YES;
nsCOMPtr<nsIRDFResource> childRDFProperty;
mRDFService->GetResource(nsDependentCString("http://home.netscape.com/NC-rdf#child"),
getter_AddRefs(childRDFProperty));
nsCOMPtr<nsISimpleEnumerator> childNodesEnum;
mRDFDataSource->GetTargets(mRDFResource, childRDFProperty, PR_TRUE, getter_AddRefs(childNodesEnum));
PRBool hasMore = PR_FALSE;
if( NS_SUCCEEDED(childNodesEnum->HasMoreElements(&hasMore)) && hasMore ) {
nsCOMPtr<nsISupports> supp;
childNodesEnum->GetNext(getter_AddRefs(supp));
nsCOMPtr<nsIRDFResource> childResource = do_QueryInterface(supp);
if (childResource)
return YES;
}
return NO;
}
- (int)numChildren;
{
if( mChildNodes )
return [mChildNodes count];
// Since gecko RDF enumerator->HasMoreElements
// is so slow, we may as well build the cache now
// because the extra time it takes is tiny
[self buildChildCache];
int numChilds = [mChildNodes count];
return numChilds;
}
// 0- based
- (RDFItem*)childAtIndex:(int)index;
{
if( !mChildNodes )
[self buildChildCache];
return [mChildNodes objectAtIndex:index];
}
- (void)buildChildCache;
{
NSMutableArray * childNodes = [[[NSMutableArray alloc] init] retain];
[self invalidateCache];
nsCOMPtr<nsIRDFResource> childRDFProperty;
mRDFService->GetResource(nsDependentCString("http://home.netscape.com/NC-rdf#child"),
getter_AddRefs(childRDFProperty));
nsCOMPtr<nsISimpleEnumerator> childNodesEnum;
mRDFDataSource->GetTargets(mRDFResource, childRDFProperty, PR_TRUE, getter_AddRefs(childNodesEnum));
PRBool hasMore = PR_FALSE;
// this call to HasMoreElements can take more than 10 ms !!
// maybe not any more though
while( NS_SUCCEEDED(childNodesEnum->HasMoreElements(&hasMore)) && hasMore ) {
nsCOMPtr<nsISupports> supp;
childNodesEnum->GetNext(getter_AddRefs(supp));
nsCOMPtr<nsIRDFResource> childResource = do_QueryInterface(supp);
if (childResource) {
RDFItem * childItem = [self newChild];
[childItem initWithRDFResource:childResource RDFDataSource:mRDFDataSource parent:self];
if (childItem)
[childNodes addObject:childItem];
}
}
mChildNodes = [childNodes retain];
}
- (void)deleteChildFromCache:(RDFItem*)child;
{
[mChildNodes removeObject:child];
}
- (RDFItem*)parent;
{
return mParent;
}
#pragma mark -
- (NSString*)getStringForRDFPropertyURI:(NSString*)aPropertyURI;
{
NSString * cached = [mPropertyCache objectForKey:aPropertyURI];
if( cached )
return cached;
nsCOMPtr<nsIRDFResource> myRDFProperty;
mRDFService->GetResource(nsDependentCString([aPropertyURI UTF8String]),
getter_AddRefs(myRDFProperty));
nsCOMPtr<nsIRDFNode> resultNode;
nsresult rv;
rv = mRDFDataSource->GetTarget(mRDFResource, myRDFProperty, PR_TRUE, getter_AddRefs(resultNode));
if( NS_FAILED(rv) )
return @"mRDFDataSource->GetTarget NS_FAILED";
if (!resultNode) {
// NSLog(@"resultNode is null in getStringForRDFPropertyURI, aPropertyURI= %@", aPropertyURI);
return @"";
}
NSString * result = [self getTextForNode:resultNode];
[mPropertyCache setObject:result forKey:aPropertyURI];
return result;
}
- (NSString *)description;
{
return [self getStringForRDFPropertyURI:@"http://home.netscape.com/NC-rdf#Name"];
}
- (NSString*)getTextForNode:(nsIRDFNode *)aNode;
{
nsString aResult;
NSString * resultString = nil;
nsresult rv;
nsIRDFResource *resource;
nsIRDFLiteral *literal;
nsIRDFDate *dateLiteral;
nsIRDFInt *intLiteral;
if (! aNode) {
aResult.Truncate();
rv = NS_OK;
}
else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFResource), (void**) &resource))) {
const char *p = nsnull;
if (NS_SUCCEEDED(rv = resource->GetValueConst( &p )) && (p)) {
aResult.AssignWithConversion(p);
}
NS_RELEASE(resource);
}
else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFDate), (void**) &dateLiteral))) {
PRInt64 theDate, million;
if (NS_SUCCEEDED(rv = dateLiteral->GetValue( &theDate ))) {
LL_I2L(million, PR_USEC_PER_SEC);
LL_DIV(theDate, theDate, million); // convert from microseconds (PRTime) to seconds
PRInt32 now32;
LL_L2I(now32, theDate);
double interval = (double)now32;
NSDate * date = [NSDate dateWithTimeIntervalSince1970:interval];
resultString = [date description];
}
NS_RELEASE(dateLiteral);
}
else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFInt), (void**) &intLiteral)))
{
PRInt32 theInt;
aResult.Truncate();
if (NS_SUCCEEDED(rv = intLiteral->GetValue( &theInt ))) {
aResult.AppendInt(theInt, 10);
}
NS_RELEASE(intLiteral);
}
else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), (void**) &literal))) {
const PRUnichar *p = nsnull;
if (NS_SUCCEEDED(rv = literal->GetValueConst( &p )) && (p)) {
aResult = p;
}
NS_RELEASE(literal);
}
else {
NS_ERROR("not a resource or a literal");
rv = NS_ERROR_UNEXPECTED;
return @"Not a resource or a literal";
}
if( !resultString )
resultString = [NSString stringWith_nsAString:aResult];
return resultString;
}
@end