Rework symbol chooser code

The symbol chooser is now owned by DocumentView instead of DocumentController.

The symbol chooser does not know about its owner and is not an observer of any notifications. Instead it has a property for “document” and one for “selectionString”. It is the owners job to update these properties (i.e. when switching document or changing the selection).
This commit is contained in:
Allan Odgaard
2013-01-30 13:38:25 +01:00
parent b9d9e9140a
commit 0413f35eeb
8 changed files with 199 additions and 144 deletions

View File

@@ -58,7 +58,6 @@ PUBLIC @interface DocumentController : NSObject
// =============================
- (IBAction)orderFrontFindPanel:(id)sender;
- (IBAction)showSymbolChooser:(id)sender;
- (IBAction)goToFile:(id)sender;
// ==================

View File

@@ -18,7 +18,6 @@
#import <OakFileBrowser/OakFileBrowser.h>
#import <HTMLOutputWindow/HTMLOutputWindow.h>
#import <OakFilterList/FileChooser.h>
#import <OakFilterList/SymbolChooser.h>
#import <OakSystem/application.h>
#import <Find/Find.h>
#import <file/path_info.h>
@@ -68,8 +67,6 @@ static BOOL IsInShouldTerminateEventLoop = NO;
@property (nonatomic) NSString* pathAttributes;
@property (nonatomic) NSString* projectPath;
@property (nonatomic) OakFilterWindowController* filterWindowController;
@property (nonatomic) NSArray* urlArrayForQuickLook;
+ (void)scheduleSessionBackup:(id)sender;
@@ -267,7 +264,6 @@ namespace
self.tabBarView.dataSource = nil;
self.tabBarView.delegate = nil;
self.textView.delegate = nil;
self.filterWindowController = nil; // ensures we removeObserver: and set target to nil
}
- (void)windowWillClose:(NSNotification*)aNotification
@@ -1384,27 +1380,6 @@ namespace
// = Opening Auxiliary Windows =
// =============================
- (void)setFilterWindowController:(OakFilterWindowController*)controller
{
if(_filterWindowController != controller)
{
if(_filterWindowController)
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:_filterWindowController.window];
_filterWindowController.target = nil;
[_filterWindowController close];
}
if(_filterWindowController = controller)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterWindowWillClose:) name:NSWindowWillCloseNotification object:_filterWindowController.window];
}
}
- (void)filterWindowWillClose:(NSNotification*)notification
{
self.filterWindowController = nil;
}
- (IBAction)orderFrontFindPanel:(id)sender
{
Find* find = [Find sharedInstance];
@@ -1454,20 +1429,6 @@ namespace
[find showFindPanel:self];
}
- (IBAction)showSymbolChooser:(id)sender
{
self.filterWindowController = [OakFilterWindowController new];
self.filterWindowController.dataSource = [SymbolChooser symbolChooserForDocumentView:[self documentView]];
self.filterWindowController.action = @selector(symbolChooserDidSelectItems:);
self.filterWindowController.sendActionOnSingleClick = YES;
[self.filterWindowController showWindowRelativeToWindow:self.window];
}
- (void)symbolChooserDidSelectItems:(id)sender
{
[self openItems:[sender selectedItems] closingOtherTabs:NO];
}
// ==================
// = OakFileChooser =
// ==================

View File

@@ -3,21 +3,11 @@
#else
#import "../OakFilterList.h"
#endif
#import <OakTextView/OakDocumentView.h>
#import <document/document.h>
#import <oak/misc.h>
@class SymbolChooserViewController;
PUBLIC @interface SymbolChooser : NSObject <FilterListDataSource>
{
OBJC_WATCH_LEAKS(SymbolChooser);
document::document_ptr document;
OakDocumentView* documentView;
std::string filterString;
SymbolChooserViewController* viewController;
NSArray* _items;
}
+ (id)symbolChooserForDocumentView:(OakDocumentView *)aDocumentView;
@property (nonatomic, readonly) NSString* filterString;
@property (nonatomic) document::document_ptr const& document;
@property (nonatomic, retain) NSString* selectionString;
+ (id)symbolChooserForDocument:(document::document_ptr)aDocument;
@end

View File

@@ -1,10 +1,10 @@
#import "SymbolChooser.h"
#import "SymbolList.h"
#import <OakFoundation/NSString Additions.h>
#import <OakTextView/OakDocumentView.h>
#import <text/ranker.h>
#import <text/case.h>
#import "../highlight_ranges.h"
#import <ns/ns.h>
OAK_DEBUG_VAR(FilterList_SymbolChooser);
@@ -48,83 +48,115 @@ OAK_DEBUG_VAR(FilterList_SymbolChooser);
@end
@interface SymbolChooser ()
- (void)updateSymbols;
{
OBJC_WATCH_LEAKS(SymbolChooser);
document::document_ptr _document;
SymbolChooserViewController* _viewController;
}
@property (nonatomic) NSArray* items;
@property (nonatomic) NSArray* selectedItems;
@property (nonatomic) NSString* filterString;
@property (nonatomic) BOOL needsReload;
@end
@implementation SymbolChooser
- (NSViewController*)viewController
+ (id)symbolChooserForDocument:(document::document_ptr)aDocument
{
if(!viewController)
viewController = [[SymbolChooserViewController alloc] initWithSymbolChooser:self];
return viewController;
SymbolChooser* res = [SymbolChooser new];
res.document = aDocument;
return res;
}
- (id)initWithDocumentView:(OakDocumentView *)aDocumentView
- (void)dealloc
{
if(self = [super init])
self.document = document::document_ptr();
}
- (void)updateItemsArray
{
self.selectedItems = nil;
self.items = SymbolListForDocument(_document, to_s(_filterString));
[self updateSelectedItemsArray];
}
- (void)updateSelectedItemsArray
{
if(!_selectionString || [_selectionString isEqualToString:@""])
{
struct callback_t : document::open_callback_t
{
callback_t (SymbolChooser* self) : _self(self) { }
void show_error (std::string const& path, document::document_ptr document, std::string const& message, oak::uuid_t const& filter)
{
fprintf(stderr, "%s: %s\n", path.c_str(), message.c_str());
}
void show_document (std::string const& path, document::document_ptr document)
{
[_self updateSymbols];
}
private:
SymbolChooser* _self;
};
document = [aDocumentView document];
documentView = aDocumentView;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSymbols) name:FLDataSourceItemsDidChangeNotification object:documentView];
if(document->try_open(document::open_callback_ptr((document::open_callback_t*)new callback_t(self))))
[self updateSymbols];
self.selectedItems = nil;
return;
}
return self;
NSMutableArray* res = [NSMutableArray array];
std::map<text::pos_t, FileChooserSymbolItem*> symbolItems;
for(FileChooserSymbolItem* item in _items)
{
text::selection_t sel(to_s(item.selectionString));
text::pos_t pos = sel.last().min();
symbolItems.insert(std::make_pair(pos, item));
}
for(text::range_t const& range : text::selection_t(to_s(_selectionString)))
{
auto it = symbolItems.upper_bound(range.min());
if(it != symbolItems.begin())
[res addObject:(--it)->second];
}
self.selectedItems = res;
}
+ (id)symbolChooserForDocumentView:(OakDocumentView *)aDocumentView
- (void)delayedReload:(id)sender
{
return [[SymbolChooser alloc] initWithDocumentView:aDocumentView];
[[NSNotificationCenter defaultCenter] postNotificationName:FLDataSourceItemsDidChangeNotification object:self];
self.needsReload = NO;
}
- (void)setNeedsReload:(BOOL)flag
{
if(_needsReload == flag)
return;
if(_needsReload = flag)
[self performSelector:@selector(delayedReload:) withObject:self afterDelay:0];
}
- (void)setItems:(NSArray*)anArray
{
_items = anArray;
self.needsReload = YES;
}
- (void)setSelectedItems:(NSArray*)anArray
{
_selectedItems = anArray;
self.needsReload = YES;
}
// ==================
// = Action methods =
// ==================
- (IBAction)search:(id)sender
{
ASSERT([sender respondsToSelector:@selector(stringValue)]);
self.filterString = [[sender stringValue] lowercaseString];
}
// ========================
// = FilterListDataSource =
// ========================
- (NSString*)title
{
return @"Go to Symbol";
}
- (NSString*)filterString
- (NSViewController*)viewController
{
return [NSString stringWithCxxString:filterString];
}
- (void)updateSymbols
{
[[NSNotificationCenter defaultCenter] postNotificationName:FLDataSourceItemsDidChangeNotification object:self];
}
- (IBAction)search:(id)sender
{
ASSERT([sender respondsToSelector:@selector(stringValue)]);
std::string const& newFilterString = [[[sender stringValue] lowercaseString] UTF8String];
if(newFilterString != filterString)
{
filterString = newFilterString;
[[NSNotificationCenter defaultCenter] postNotificationName:FLDataSourceItemsDidChangeNotification object:self];
}
}
- (NSArray*)items
{
_items = SymbolListForDocument(document, filterString);
return _items;
if(!_viewController)
_viewController = [[SymbolChooserViewController alloc] initWithSymbolChooser:self];
return _viewController;
}
- (NSAttributedString*)displayStringForItem:(id)item
@@ -137,43 +169,55 @@ OAK_DEBUG_VAR(FilterList_SymbolChooser);
return [item infoString];
}
- (NSArray*)selectedItems
// =======
// = API =
// =======
- (void)setDocument:(document::document_ptr const&)aDocument
{
text::selection_t sel([[documentView textView].selectionString UTF8String]);
ng::buffer_t const& buf = document->buffer();
size_t min = buf.convert(sel.last().min());
size_t max = buf.convert(sel.last().max());
BOOL past_selection = NO;
NSUInteger last_index;
NSUInteger index = 0;
NSMutableIndexSet* indexesToSelect = [NSMutableIndexSet indexSet];
for (id item in _items)
struct callback_t : document::open_callback_t
{
text::selection_t pos([[item selectionString] UTF8String]);
size_t start = buf.convert(pos.last().min());
size_t end = buf.convert(pos.last().max());
last_index = index;
index = [_items indexOfObject:item];
if (start > min)
callback_t (SymbolChooser* self) : _self(self) { }
void show_error (std::string const& path, document::document_ptr document, std::string const& message, oak::uuid_t const& filter)
{
if (!past_selection && last_index != NSNotFound)
[indexesToSelect addIndex:last_index];
past_selection = YES;
if (end <= max)
{
if(index != NSNotFound)
[indexesToSelect addIndex:index];
}
fprintf(stderr, "%s: %s\n", path.c_str(), message.c_str());
}
void show_document (std::string const& path, document::document_ptr document)
{
[_self updateItemsArray];
}
private:
SymbolChooser* _self;
};
if(_document)
_document->close();
if(_document = aDocument)
{
if(_document->try_open(document::open_callback_ptr((document::open_callback_t*)new callback_t(self))))
[self updateItemsArray];
}
if (!past_selection)
[indexesToSelect addIndex:index];
return [_items objectsAtIndexes:indexesToSelect];
}
- (void)dealloc
- (void)setSelectionString:(NSString*)aString
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:FLDataSourceItemsDidChangeNotification object:documentView];
document->close();
if(_selectionString != aString && ![_selectionString isEqualToString:aString])
{
_selectionString = aString;
[self updateSelectedItemsArray];
}
}
- (void)setFilterString:(NSString*)aString
{
if(_filterString != aString && ![_filterString isEqualToString:aString])
{
_filterString = aString;
[self updateItemsArray];
}
}
@end

View File

@@ -1,5 +1,5 @@
SOURCES = src/*.mm src/datasources/*.{cc,mm}
EXPORT = src/OakFilterList.h src/FileChooser.h src/datasources/*.h
CP_Resources = resources/*
LINK += text bundles document io plist MGScopeBar ns OakAppKit OakFoundation OakSystem OakTextView regexp scope OakFileBrowser
LINK += text bundles document io plist MGScopeBar ns OakAppKit OakFoundation OakSystem regexp scope OakFileBrowser
FRAMEWORKS = Cocoa Carbon

View File

@@ -38,4 +38,6 @@ PUBLIC @interface OakDocumentView : NSView <GutterViewDelegate, GutterViewColumn
- (void)addAuxiliaryView:(NSView*)aView atEdge:(NSRectEdge)anEdge;
- (void)removeAuxiliaryView:(NSView*)aView;
- (IBAction)showSymbolChooser:(id)sender;
@end

View File

@@ -7,6 +7,7 @@
#import <ns/ns.h>
#import <oak/debug.h>
#import <bundles/bundles.h>
#import <OakFilterList/SymbolChooser.h>
#import <OakFoundation/NSString Additions.h>
#import <OakAppKit/OakAppKit.h>
#import <OakAppKit/NSColor Additions.h>
@@ -36,6 +37,8 @@ static NSString* const kFoldingsColumnIdentifier = @"foldings";
@property (nonatomic, retain) NSDictionary* gutterImages;
@property (nonatomic, retain) NSDictionary* gutterHoverImages;
@property (nonatomic, retain) NSDictionary* gutterPressedImages;
@property (nonatomic) OakFilterWindowController* filterWindowController;
@property (nonatomic) SymbolChooser* symbolChooser;
- (void)updateStyle;
@end
@@ -231,12 +234,12 @@ private:
char const* str = [[textView valueForKey:@"selectionString"] UTF8String] ?: "1";
[gutterView setHighlightedRange:str];
[statusBar setCaretPosition:str];
_symbolChooser.selectionString = textView.selectionString;
ng::buffer_t const& buf = document->buffer();
text::selection_t sel([textView.selectionString UTF8String]);
size_t i = buf.convert(sel.last().max());
statusBar.symbolName = [NSString stringWithCxxString:buf.symbol_at(i)];
[[NSNotificationCenter defaultCenter] postNotificationName:@"FLDataSourceItemsDidChangeNotification" object:self];
}
else if([aKeyPath isEqualToString:@"tabSize"])
{
@@ -265,6 +268,9 @@ private:
[self setDocument:document::document_ptr()];
delete callback;
self.filterWindowController = nil;
self.symbolChooser = nil;
[gutterScrollView release];
[gutterView release];
[gutterDividerView release];
@@ -311,6 +317,9 @@ private:
[gutterView reloadData:self];
[self updateStyle];
if(_symbolChooser)
_symbolChooser.document = document;
if(oldDocument)
{
oldDocument->hide();
@@ -457,6 +466,56 @@ private:
[[OakPasteboard pasteboardWithName:NSFindPboard] selectItemAtPosition:[textView positionForWindowUnderCaret] andCall:@selector(findNext:)];
}
// ==================
// = Symbol Chooser =
// ==================
- (void)setFilterWindowController:(OakFilterWindowController*)controller
{
if(_filterWindowController == controller)
return;
if(_filterWindowController)
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:_filterWindowController.window];
_filterWindowController.target = nil;
[_filterWindowController close];
}
if(_filterWindowController = controller)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterWindowWillClose:) name:NSWindowWillCloseNotification object:_filterWindowController.window];
}
- (void)filterWindowWillClose:(NSNotification*)notification
{
self.filterWindowController = nil;
self.symbolChooser = nil;
}
- (IBAction)showSymbolChooser:(id)sender
{
if(self.filterWindowController)
{
[self.filterWindowController.window makeKeyAndOrderFront:self];
return;
}
self.symbolChooser = [SymbolChooser symbolChooserForDocument:document];
self.symbolChooser.selectionString = textView.selectionString;
self.filterWindowController = [OakFilterWindowController new];
self.filterWindowController.dataSource = self.symbolChooser;
self.filterWindowController.action = @selector(symbolChooserDidSelectItems:);
self.filterWindowController.sendActionOnSingleClick = YES;
[self.filterWindowController showWindowRelativeToWindow:self.window];
}
- (void)symbolChooserDidSelectItems:(id)sender
{
for(id item in [sender selectedItems])
[textView setSelectionString:[item selectionString]];
}
// =======================
// = Status bar delegate =
// =======================

View File

@@ -1,7 +1,7 @@
SOURCES = src/*.{cc,mm}
EXPORT = src/OakDocumentView.h src/GutterView.h src/OakTextView.h
CP_Resources = resources/*
LINK += layout text bundles editor document theme ns plist OakAppKit OakFoundation OakSystem regexp scope settings BundleMenu
LINK += layout text bundles editor document theme ns plist OakAppKit OakFoundation OakSystem regexp scope settings BundleMenu OakFilterList
FRAMEWORKS = Cocoa
OBJCXX_FLAGS += -fno-objc-arc