#include "MyDocument.h" #include "GNUstep.h" #include "CalendarView.h" @implementation MyDocument - (void) calendarViewSelectionDidChange: (NSNotification *) not { NSCalendarDate *date = [[not object] date]; NSArray *titles = [dataSource titlesCreatedOnDate: date]; NSEnumerator *e = [titles objectEnumerator]; id object; NSMutableString *result = [[NSMutableString alloc] init];; [result appendString: [NSString stringWithFormat: @"%@
\n", [date descriptionWithCalendarFormat: DateFormat]]]; while ((object = [e nextObject])) { [result appendString: [NSString stringWithFormat: @"%@\n\n", object]]; } [dataSource setContent: AUTORELEASE([result copy]) withTitle: JournalTitle]; //[editButton setState: NSOffState]; [tableView reloadData]; [self selectTitle: JournalTitle]; [self updateTextView]; } - (void) calendarView: (CalendarView *) view willDisplayCell: (id) cell ofDate: (NSCalendarDate *) date { if ([dataSource hasContentCreatedOnDate: date] == YES) { [cell setBordered: YES]; //[cell setBezeled: YES]; [cell setBezelStyle: NSShadowlessSquareBezelStyle]; //[cell setGradientType: NSGradientConcaveStrong]; } } - (BOOL) selectTitle: (NSString *) title { unsigned int row = [dataSource indexOfTitle: title]; if (row == -1) return NO; #ifdef GNUSTEP [tableView selectRow: row byExtendingSelection: NO]; #else [tableView selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO]; #endif return YES; } - (IBAction)homeAction:(id)sender { if ([self selectTitle: HomeTitle] == NO) { #ifdef GNUSTEP [tableView selectRow: 0 byExtendingSelection: NO]; #else [tableView selectRowIndexes: [NSIndexSet indexSetWithIndex: 0] byExtendingSelection: NO]; #endif } } - (IBAction)backAction:(id)sender { historyIndex--; if (historyIndex < 1) // Reach to the first [backButton setEnabled: NO]; [forwardButton setEnabled: YES]; backOrForwardAction = YES; [self selectTitle: [history objectAtIndex: historyIndex]]; } - (IBAction)forwardAction:(id)sender { historyIndex++; if (historyIndex >= [history count]-1) [forwardButton setEnabled: NO]; [backButton setEnabled: YES]; backOrForwardAction = YES; [self selectTitle: [history objectAtIndex: historyIndex]]; } - (IBAction)addAction:(id)sender { NSString *title = @"New Page"; int row; if ([dataSource indexOfTitle: title] != NSNotFound) { // Find avaiable title int i; for(i = 1;;i++) { title = [NSString stringWithFormat: @"New Page %d", i]; if ([dataSource indexOfTitle: title] == NSNotFound) break; } } [dataSource setContent: @"Put new content here" withTitle: title]; [tableView reloadData]; [self selectTitle: title]; row = [dataSource indexOfTitle: title]; [tableView editColumn: 0 row: row withEvent: nil select: YES]; } - (IBAction)deleteAction:(id)sender { unsigned int row = [tableView selectedRow]; if (row == -1) return; NSString *title = [dataSource titleAtIndex: row]; if ([dataSource canRemoveContentOfTitle: title] == NO) return; int result = NSRunAlertPanel(@"Delete page ?", [NSString stringWithFormat: @"\"%@\" will be deleted. Are you sure ?", title], @"Delete", @"Don't Delete", nil, nil); if (result != NSAlertDefaultReturn) return; [dataSource removeContentOfTitle: title]; [tableView reloadData]; // Remove the history associated with the delete page int i, count = [history count];; for (i = count-1; i > -1; i--) { if ([[history objectAtIndex: i] isEqualToString: title]) { [history removeObjectAtIndex: i]; historyIndex--; } } //NSLog(@"deleteAction %@ %d", history, historyIndex); row--; if (row < 0) row = 0; #ifdef GNUSTEP [tableView selectRow: row byExtendingSelection: NO]; #else [tableView selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO]; #endif } - (IBAction)searchAction:(id)sender { NSString *searchString = [[sender stringValue] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]]; if ((searchString == nil) || ([searchString isEqualToString: @""])) { return; } NSEnumerator *e = [[dataSource titles] objectEnumerator]; id object, content; NSRange range; NSMutableString *searchResult = [[NSMutableString alloc] init];; [searchResult appendString: [NSString stringWithFormat: @"These pages contains \"%@\"
\n", searchString]]; while ((object = [e nextObject])) { if ([object isEqualToString: SearchResultTitle]) continue; if ([object isEqualToString: JournalTitle]) continue; content = [[dataSource renderWithTitle: object] string]; range = [content rangeOfString: searchString options: NSCaseInsensitiveSearch]; if (range.location == NSNotFound) continue; else { [searchResult appendString: [NSString stringWithFormat: @"%@\n\n", object]]; } } [dataSource setContent: AUTORELEASE([searchResult copy]) withTitle: SearchResultTitle]; //[editButton setState: NSOffState]; [tableView reloadData]; [self selectTitle: SearchResultTitle]; [self updateTextView]; } - (IBAction)calendarAction:(id)sender { BOOL state = [sender state]; NSRect frame; id scrollView = [[tableView superview] superview]; id view = [scrollView superview]; if (state == NSOnState) { // Put on CalendarView if ([calendarView superview] != nil) { // Already put on, do nothing } else { //NSLog(@"%@", [[[tableView superview] superview] superview]); frame = NSMakeRect(0, [calendarView bounds].size.height+8, [view bounds].size.width, [view bounds].size.height-[calendarView frame].size.height-8); [scrollView setFrame: frame]; frame = NSMakeRect(0, 0, [view bounds].size.width, [calendarView frame].size.height); [calendarView setFrame: frame]; [view addSubview: calendarView]; RELEASE(calendarView); [view setNeedsDisplay: YES]; } } else { // Take off CalendarView if ([calendarView superview] == nil) { // Already take off, do nothing } else { RETAIN(calendarView); [calendarView removeFromSuperview]; [scrollView setFrame: [view bounds]]; } } } - (BOOL) textView: (NSTextView *) aView clickedOnLink: (id) link atIndex: (unsigned) charIndex { NSRange range; NSString *string; id attribute =[[textView textStorage] attribute: NSLinkAttributeName atIndex: charIndex effectiveRange: &range]; if (attribute == nil) { NSLog(@"Error: no link"); return NO; } else if ([attribute isKindOfClass: [NSURL class]]) { string = [[[textView string] substringWithRange: range] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]]; return [[NSWorkspace sharedWorkspace] openURL: attribute]; } else if ([attribute isKindOfClass: [NSString class]]) { if ([attribute isEqualToString: @"Link"]) { string = [[[textView string] substringWithRange: range] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]]; backOrForwardAction = NO; // Create new page if not existed NSEnumerator *e = [[dataSource titles] objectEnumerator]; NSString *key; BOOL isPageExisted = NO; unsigned int i = 0; while ((key = [e nextObject])) { if ([key caseInsensitiveCompare: string] == NSOrderedSame) { isPageExisted = YES; break; } i++; } if (isPageExisted == NO) { //NSLog(@"Create page \"%@\"", string); [dataSource setContent: @"Put new content here" withTitle: string]; [tableView reloadData]; [self selectTitle: string]; } else { #ifdef GNUSTEP [tableView selectRow: i byExtendingSelection: NO]; #else [tableView selectRowIndexes: [NSIndexSet indexSetWithIndex: i] byExtendingSelection: NO]; #endif } return YES; } else if ([attribute hasPrefix: @"<"]) { NSLog(@"Email to %@", attribute); return YES; } else // Assume it is a file { // Should handle saving file first NSString *fullpath = [NSString stringWithFormat: @"%@/%@", [self fileName], attribute]; NSLog(@"Open file \"%@\"", fullpath); return [[NSWorkspace sharedWorkspace] openFile: fullpath]; } } return NO; } - (void) updateTextView { if ([editButton state] == NSOnState) { [textView setString: [dataSource sourceWithTitle: index]]; } else { NSAttributedString *as = [dataSource renderWithTitle: index]; [[textView textStorage] setAttributedString: as]; } } - (void) tableViewSelectionDidChange: (NSNotification *) not { // Render text int selectedRow = [[not object] selectedRow]; if (selectedRow == -1) return; ASSIGN(index, [dataSource titleAtIndex: selectedRow]); if (([index isEqualToString: [history lastObject]] == NO) && (backOrForwardAction == NO)) { [history removeObjectsInRange: NSMakeRange(historyIndex+1, [history count]-historyIndex-1)]; [history addObject: AUTORELEASE([index copy])]; historyIndex = [history count]-1; [forwardButton setEnabled: NO]; if ([history count] > 1) [backButton setEnabled: YES]; else [backButton setEnabled: NO]; } backOrForwardAction = NO; if ([dataSource canRemoveContentOfTitle: index] == NO) [deleteButton setEnabled: NO]; else [deleteButton setEnabled: YES]; if ([dataSource canEditContentOfTitle: index] == NO) { [editButton setState: NSOffState]; [editButton setEnabled: NO]; } else [editButton setEnabled: YES]; if ([editButton state] == NSOnState) { [textView setString: [dataSource sourceWithTitle: index]]; [textView setBackgroundColor: [NSColor yellowColor]]; [textView setEditable: YES]; } else { NSAttributedString *as = [dataSource renderWithTitle: index]; [[textView textStorage] setAttributedString: as]; [textView setBackgroundColor: [NSColor whiteColor]]; [textView setEditable: NO]; } } - (int) numberOfRowsInTableView: (NSTableView *) aTableView { return [dataSource numberOfPages]; } - (id) tableView: (NSTableView *) view iconForTableColumn: (NSTableColumn *) column row: (int) row; { NSString *t = [dataSource titleAtIndex: row];; NSImage *image; NSCell *cell = [column dataCellForRow: row]; #ifdef GNUSTEP if([t isEqualToString: HomeTitle]) image = [NSImage imageNamed: @"Home12black.png"]; else if ([t isEqualToString: SearchResultTitle]) image = [NSImage imageNamed: @"Search12black.png"]; else if ([t isEqualToString: JournalTitle]) image = [NSImage imageNamed: @"Journal12black.png"]; else image = [NSImage imageNamed: @"Page12black.png"]; #else if (([[view window] isKeyWindow] == YES) && ((([view selectedRow] == row) && ([[view window] firstResponder] == view)) || (row == [view editedRow]))) { if([t isEqualToString: HomeTitle]) image = [NSImage imageNamed: @"Home12white.png"]; else if ([t isEqualToString: SearchResultTitle]) image = [NSImage imageNamed: @"Search12white.png"]; else if ([t isEqualToString: JournalTitle]) image = [NSImage imageNamed: @"Journal12white.png"]; else image = [NSImage imageNamed: @"Page12white.png"]; } else { if([t isEqualToString: HomeTitle]) image = [NSImage imageNamed: @"Home12black.png"]; else if ([t isEqualToString: SearchResultTitle]) image = [NSImage imageNamed: @"Search12black.png"]; else if ([t isEqualToString: JournalTitle]) image = [NSImage imageNamed: @"Journal12black.png"]; else image = [NSImage imageNamed: @"Page12black.png"]; } #endif return image; } - (id) tableView: (NSTableView *) aTableView objectValueForTableColumn: (NSTableColumn *) aTableColumn row: (int) rowIndex { NSString *title = [dataSource titleAtIndex: rowIndex]; if ([title isEqualToString: SearchResultTitle] == YES) return @"Search Result"; else if ([title isEqualToString: JournalTitle] == YES) return @"Journal"; else return title; } - (void) tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { unsigned int titleIndex; NSString *newTitle, *oldTitle = [[dataSource titleAtIndex: rowIndex] copy]; newTitle = [anObject stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]]; if ([oldTitle isEqualToString: anObject]) { return; } titleIndex = [dataSource indexOfTitle: newTitle]; if (titleIndex != NSNotFound) { NSRunAlertPanel(@"Title Conflict", [NSString stringWithFormat: @"Title \"%@\" already exist, please use another name", anObject], @"OK", nil, nil, nil); [aTableView editColumn: 0 row: rowIndex withEvent: nil select: YES]; return; } //NSLog(@"%@", newTitle); isDocumentEdited = YES; [window setDocumentEdited: YES]; [dataSource replaceTitle: oldTitle withTitle: newTitle]; // Replace all the link with new title int result = NSAlertDefaultReturn; if ([dataSource isLinkedByOthers: oldTitle] == YES) { result= NSRunInformationalAlertPanel(@"Update link ?", @"The title of this page is changed. Do you want to update all the links to this page ?", @"Update", @"Don't Update", nil, nil); } if (result == NSAlertDefaultReturn) { // Yes to replace //NSLog(@"update all the link"); [dataSource replaceContentOfLink: oldTitle withString: newTitle]; } RELEASE(oldTitle); [tableView reloadData]; } - (BOOL) tableView: (NSTableView *) aTableView shouldEditTableColumn: (NSTableColumn *) aTableColumn row: (int) rowIndex { if (rowIndex == 0) return NO; return YES; } - (BOOL) tableView:(NSTableView *)tableView writeRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard { if ([rows count] < 0) return NO; int row = [[rows objectAtIndex: 0] intValue]; NSString *title = [dataSource titleAtIndex: row]; [pboard declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil]; return [pboard setString: [NSString stringWithFormat: @"%@", title] forType: NSStringPboardType]; } - (float) splitView: (NSSplitView *) sender constrainMinCoordinate: (float) proposedMin ofSubviewAt: (int) offset { if (proposedMin < 130) return 130; else return proposedMin; } - (BOOL) splitView: (NSSplitView *) sender canCollapseSubview: (NSView *) subview { return YES; } // Copy the backup to the file name - (BOOL)writeToFile:(NSString *)fullDocumentPath ofType:(NSString *)docType originalFile:(NSString *)fullOriginalDocumentPath saveOperation:(NSSaveOperationType)saveOperationType { if (fullOriginalDocumentPath == nil) { return [super writeToFile: fullDocumentPath ofType: docType originalFile: fullOriginalDocumentPath saveOperation: saveOperationType]; } else { NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL succeed; if ([fileManager fileExistsAtPath: fullDocumentPath]) { succeed = [fileManager removeFileAtPath: fullDocumentPath handler: nil]; if (succeed == NO) return NO; } succeed = [fileManager movePath: fullOriginalDocumentPath toPath: fullDocumentPath handler: nil]; if (succeed == NO) return NO; // Overwrite the index.xml NSString *path = [fullDocumentPath stringByAppendingPathComponent: IndexFile]; if ([editButton state] == NSOnState) { [dataSource setContent: [textView string] withTitle: index]; } NSData *data = [dataSource dataOfIndexFile]; succeed = [data writeToFile: path atomically: YES]; if (succeed == YES) { isDocumentEdited = NO; [window setDocumentEdited: NO]; } return succeed; } } // Only called when saving to new name - (NSFileWrapper *) fileWrapperRepresentationOfType: (NSString *) aType { NSData *data; NSFileWrapper *docFileWrapper, *indexFileWrapper; NSDictionary *dict; if ([editButton state] == NSOnState) { [dataSource setContent: [textView string] withTitle: index]; } data = [dataSource dataOfIndexFile]; indexFileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents: data]; dict = [NSDictionary dictionaryWithObject: indexFileWrapper forKey: IndexFile]; docFileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers: dict]; AUTORELEASE(indexFileWrapper); AUTORELEASE(docFileWrapper); isDocumentEdited = NO; [window setDocumentEdited: NO]; return docFileWrapper; } - (BOOL)loadFileWrapperRepresentation:(NSFileWrapper *)docFileWrapper ofType:(NSString *)aType { NSData *data; NSString *string; RETAIN(docFileWrapper); if ([docFileWrapper isDirectory] == YES) { NSDictionary *dict = [docFileWrapper fileWrappers]; /// Should only have "index.xml" NSFileWrapper *indexFileWrapper = [dict valueForKey: IndexFile]; if ((indexFileWrapper == nil) || ([indexFileWrapper isRegularFile] == NO)) { NSLog(@"No index.xml"); RELEASE(docFileWrapper); return NO; } data = [indexFileWrapper regularFileContents]; string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; [dataSource setDataFromContentOfFile: string]; } RELEASE(docFileWrapper); return YES; } - (IBAction)editAction:(id)sender { BOOL state = [editButton state]; if (state == NSOnState) { id source = [dataSource sourceWithTitle: index]; [textView setRichText: NO]; [textView setEditable: YES]; [textView setBackgroundColor: [NSColor yellowColor]]; // Allow drop [textView registerForDraggedTypes: [NSArray arrayWithObjects: NSStringPboardType, NSFilenamesPboardType, nil]]; // Put back original source // Reset the attributed string in case it contain , , etc.. if (source) [[textView textStorage] setAttributedString: [[NSAttributedString alloc] initWithString: source]]; } else { NSAttributedString *as; [textView setRichText: YES]; // Save source [dataSource setContent: [textView string] withTitle: index]; // Render text as = [dataSource renderWithTitle: index]; [[textView textStorage] setAttributedString: as]; [textView setEditable: NO]; [textView setBackgroundColor: [NSColor whiteColor]]; [textView unregisterDraggedTypes]; [tableView reloadData]; } } - (void)editMenuAction:(id)sender { [editButton performClick: sender]; } - (void)windowControllerDidLoadNib:(NSWindowController *) aController { [super windowControllerDidLoadNib:aController]; // Add any code here that needs to be executed once the windowController has // loaded the document's window. [editButton setState: NSOffState]; [calendarButton setState: NSOnState]; [backButton setEnabled: NO]; [forwardButton setEnabled: NO]; [deleteButton setEnabled: NO]; if ([dataSource indexOfTitle: HomeTitle] == NSNotFound) { [dataSource setContent: @"Welcome to use MyWiki" withTitle: HomeTitle]; } [textView setRichText: YES]; [textView setEditable: NO]; [textView setBackgroundColor: [NSColor whiteColor]]; [textView unregisterDraggedTypes]; // Reload titles [tableView reloadData]; #ifdef GNUSTEP [tableView selectRow: 0 byExtendingSelection: NO]; #else [tableView selectRowIndexes: [NSIndexSet indexSetWithIndex: 0] byExtendingSelection: NO]; #endif #ifdef GNUSTEP [splitView setDelegate: self]; #endif [[NSNotificationCenter defaultCenter] postNotificationName: NSTableViewSelectionDidChangeNotification object: tableView]; [[aController window] makeFirstResponder: textView]; // Not sure why this is required since it is connected in IB already window = [aController window]; } - (BOOL) isDocumentEdited { return isDocumentEdited; } - (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString { [window setDocumentEdited: YES]; isDocumentEdited = YES; return YES; } - (id) init { self = [super init]; ASSIGN(index, HomeTitle); ASSIGN(dataSource, [[DataSource alloc] init]); isDocumentEdited = NO; history = [[NSMutableArray alloc] init]; [history addObject: AUTORELEASE([index copy])]; historyIndex = 0; backOrForwardAction = NO; return self; } - (void) dealloc { RELEASE(index); RELEASE(dataSource); RELEASE(history); [super dealloc]; } - (NSString *)windowNibName { return @"MyDocument"; } @end