5 // Created by Marc Liyanage on Sun Mar 03 2002.
6 // Copyright (c) 2001 __MyCompanyName__. All rights reserved.
10 #import "MyDocument.h"
16 @implementation MyDocument
20 if (self = [super init]) {
21 workset = [[Workset alloc] init];
22 processor = [XSLTProcessorFactory makeProcessorOfType:PROCESSORTYPE_SABLOTRON];
23 wellFormedParser = [[XMLParserLibxml alloc] init];
37 [wellFormedParser release];
38 [findPanelController release];
39 [jumpToLinePanelController release];
40 [unsavedChangesPanelController release];
44 - (IBAction)selectTab:(id)sender {
46 [self selectTabById:[sender tag]];
55 - (IBAction)selectTabById:(int)tabId {
70 tabName = @"parametersTab";
75 tabName = @"resultTab";
81 [tabView selectTabViewItemWithIdentifier:tabName];
86 - (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem {
88 NSLog(@"switch tab xyz x");
89 [[self undoManager] removeAllActions];
91 if ([[tabViewItem identifier] isEqualToString:@"htmlResult"]) {
102 // NSLog(@"updateUI running...");
104 [uiUpdateTimer invalidate];
105 [uiUpdateTimer release];
107 uiUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(uiUpdateTimerTarget:) userInfo:nil repeats:NO];
109 [uiUpdateTimer retain];
113 - (void)uiUpdateTimerTarget:(NSTimer *)timer {
124 // NSLog(@"doUpdateUI running...");
125 // NSLog(@"debug: %@", [[[NSApp mainWindow] document] tabView]);
128 [saveXmlAsButton setEnabled:[workset hasXmlCode]];
129 [saveXsltAsButton setEnabled:[workset hasXsltCode]];
130 [saveResultAsButton setEnabled:[workset hasResult]];
132 [saveResultButton setEnabled:[workset hasResultFilename] && resultDirty];
133 [autoSaveCheckbox setEnabled:[workset hasResultFilename]];
135 [openResultURLButton setEnabled:[workset hasResultFilename]];
136 [autoShowCheckbox setEnabled:[openResultURLButton isEnabled]];
138 [paramRemoveButton setEnabled:[parameterTable selectedRow] != -1];
140 [processButton setEnabled:[self canProcessNow]];
142 [saveResultFilenameField setObjectValue:[workset resultFilename]];
143 [saveResultFilenameField setToolTip:[workset resultFilename]];
146 [saveXmlFilenameField setObjectValue:[workset xmlFilename]];
147 [saveXmlFilenameField setToolTip:[workset xmlFilename]];
148 [saveXmlButton setEnabled:[workset hasXmlFilename] && xmlDirty];
151 [saveXsltFilenameField setObjectValue:[workset xsltFilename]];
152 [saveXsltFilenameField setToolTip:[workset xsltFilename]];
153 [saveXsltButton setEnabled:[workset hasXsltFilename] && xsltDirty];
155 [self updateWellFormedIcons];
157 [parameterTable reloadData];
159 [resultView setString:[workset result]];
161 WebFrame *mainFrame = [resultWebView mainFrame];
162 [mainFrame loadHTMLString:[workset result] baseURL:nil];
165 NSLog(@"webview frame: %@", NSStringFromRect([resultWebView frame]));
166 NSLog(@"webview main frame: %@", NSStringFromRect([[mainFrame frameView] frame]));
167 NSLog(@"textview frame: %@", NSStringFromRect([resultView frame]));
174 - (void)updateWellFormedIcons {
176 if (![workset hasXmlCode] || [wellFormedParser checkWellFormedString:[workset xmlCode]]) {
177 [xmlWellFormedIcon setImage:nil];
178 [xmlWellFormedIcon setToolTip:nil];
180 [xmlWellFormedIcon setImage:warningIcon];
181 [xmlWellFormedIcon setToolTip:[NSString stringWithFormat:@"The XML code is not well-formed, error at line %d:\n%@", [wellFormedParser errorLine], [wellFormedParser errorMessage]]];
185 if (![workset hasXsltCode] || [wellFormedParser checkWellFormedString:[workset xsltCode]]) {
186 [xsltWellFormedIcon setImage:nil];
187 [xsltWellFormedIcon setToolTip:nil];
189 [xsltWellFormedIcon setImage:warningIcon];
190 [xsltWellFormedIcon setToolTip:[NSString stringWithFormat:@"The XSLT code is not well-formed, error at line %d:\n%@", [wellFormedParser errorLine], [wellFormedParser errorMessage]]];
197 - (BOOL)canProcessNow {
199 return [workset hasXmlCode] && [workset hasXsltCode];
203 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
205 // NSLog(@"validate: %@, tag: %d", menuItem, [menuItem tag]);
207 switch ([menuItem tag]) {
210 return [self canProcessNow];
214 return [workset hasResultFilename];
218 return [self canFindNow];
221 case 13: // Find Next
222 case 14: // Find Previous
223 return [self canFindAgainNow];
226 case 15: // Use Selection for Find
227 return [self canUseSelectionForFindNow];
230 case 16: // Jump to Line
231 return [self canJumpToLineNow];
243 - (IBAction)showInBrowser:(id)sender {
250 - (void)updateCompleteUI {
252 [xmlView setString:[workset xmlCode]];
253 [xsltView setString:[workset xsltCode]];
260 - (void)textDidChange:(NSNotification *)aNotification {
262 id sender = [aNotification object];
264 if ([sender isEqual:xmlView]) {
265 [workset setXmlCode:[xmlView string]];
267 } else if ([sender isEqual:xsltView]) {
268 [workset setXsltCode:[xsltView string]];
272 [self updateChangeCount:NSChangeDone];
279 - (NSTabView *)tabView {
285 - (BOOL)canJumpToLineNow {
287 NSTextView *view = [self currentTextView];
289 return (view == xmlView) || (view == xsltView);
294 - (IBAction)showJumpToLinePanel:(id)sender {
296 [NSApp beginSheet:[jumpToLinePanelController window]
297 modalForWindow:[[[self windowControllers] objectAtIndex:0] window]
302 [NSApp runModalForWindow:[jumpToLinePanelController window]];
303 [NSApp endSheet:[jumpToLinePanelController window]];
304 [[jumpToLinePanelController window] orderOut:self];
306 if ([jumpToLinePanelController lineNumber] == 0) {
310 [((XMLTextView *)[self currentTextView]) selectLineByNumber:[jumpToLinePanelController lineNumber]];
312 // NSLog(@"jump to line: %d", [jumpToLinePanelController lineNumber]);
317 - (IBAction)showFindPanel:(id)sender {
319 [findPanelController refresh];
321 [NSApp beginSheet:[findPanelController window]
322 modalForWindow:[[[self windowControllers] objectAtIndex:0] window]
327 [NSApp runModalForWindow:[findPanelController window]];
328 [NSApp endSheet:[findPanelController window]];
329 [[findPanelController window] orderOut:self];
331 if ([findPanelController aborted]) {
335 [self findStringWithSearchFlags:[findPanelController searchFlags]];
342 - (IBAction)findNext:(id)sender {
344 [self findStringWithSearchFlags:[findPanelController searchFlags] & NSCaseInsensitiveSearch];
349 - (IBAction)findPrevious:(id)sender {
351 [self findStringWithSearchFlags:([findPanelController searchFlags] | NSBackwardsSearch)];
355 - (BOOL)canUseSelectionForFindNow {
357 NSTextView *view = [self currentTextView];
358 NSRange selectedRange;
364 selectedRange = [view selectedRange];
366 if (selectedRange.length < 1) {
376 - (IBAction)useSelectionForFind:(id)sender {
379 NSTextView *view = [self currentTextView];
381 text = [[view string] substringWithRange:[view selectedRange]];
383 [findPanelController setFindString:text];
392 NSString *currentTabViewItem = [[[self tabView] selectedTabViewItem] identifier];
394 if ([currentTabViewItem isEqualToString:@"xmlTab"]
395 || [currentTabViewItem isEqualToString:@"xsltTab"]
396 || [currentTabViewItem isEqualToString:@"resultTab"]) {
404 - (BOOL)canFindAgainNow {
406 return ([findPanelController findString] != nil) && [self canFindNow];
411 - (void)findStringWithSearchFlags:(int)flags {
413 NSTextView *currentView;
416 NSRange selectedRange, leftRange, rightRange, resultRange, searchRange;
418 NSPasteboard *findBoard = [NSPasteboard pasteboardWithName:NSFindPboard];
420 string = [findBoard stringForType:NSStringPboardType];
422 currentView = [self currentTextView];
423 if (currentView == nil) {
427 text = [currentView string];
429 selectedRange = [currentView selectedRange];
430 leftRange = NSMakeRange(0, selectedRange.location);
431 rightRange = NSMakeRange(NSMaxRange(selectedRange), [text length] - NSMaxRange(selectedRange));
433 if (flags & NSBackwardsSearch) {
434 searchRange = leftRange;
436 searchRange = rightRange;
439 resultRange = [text rangeOfString:string options:flags range:searchRange];
441 if (resultRange.location == NSNotFound) {
446 [currentView setSelectedRange:resultRange];
447 [currentView scrollRangeToVisible:resultRange];
454 - (NSTextView *)currentTextView {
456 NSString *currentTabViewItem = [[[self tabView] selectedTabViewItem] identifier];
458 if ([currentTabViewItem isEqualToString:@"xmlTab"]) {
460 } else if ([currentTabViewItem isEqualToString:@"xsltTab"]) {
462 } else if ([currentTabViewItem isEqualToString:@"resultTab"]) {
474 - (IBAction)process:(id)sender {
477 const char **params = [[workset parameterSet] cArray];
479 struct timeval tstart, tend;
480 gettimeofday(&tstart, NULL);
484 if ([workset hasXsltFilename]) {
485 [processor setBaseUri:[NSString stringWithFormat:@"file://%@", [workset xsltFilename]]];
488 if (![processor processStrings:[workset xmlCode] withXslt:[workset xsltCode] andParameters:params]) {
490 [drawerMessageField setStringValue:[NSString stringWithFormat:@"Error on line %d of your %@ code:\n%@", [processor errorLine], ([processor errorSource] == XSLT_ERROR_SOURCE_XML ? @"XML" : @"XSLT"), [processor errorMessage]]];
493 [errorDrawer openOnEdge:NSMinYEdge];
494 [self showErrorLocation:nil];
498 gettimeofday(&tend, NULL);
500 processingTime = ((tend.tv_sec * 1000000 + tend.tv_usec) - (tstart.tv_sec * 1000000 + tstart.tv_usec)) / 1000;
502 [workset setResult:[processor result]];
506 [self selectTabById:RESULT];
507 [processingTimeField setStringValue:[NSString stringWithFormat:@"Time: %ldms", processingTime]];
516 if (resultDirty && [autoSaveCheckbox state] == NSOnState) {
517 [self saveResult:nil];
524 if ([autoShowCheckbox state] == NSOnState) {
525 [self openResultURL:nil];
531 - (IBAction)loadXml:(id)sender {
533 NSOpenPanel *panel = [NSOpenPanel openPanel];
535 if ([panel runModalForDirectory:nil file:nil types:nil] == NSOKButton) {
537 // NSLog(@"choosen: %@", [[panel filenames] objectAtIndex:0]);
539 [workset setXmlCode:[NSString stringWithContentsOfFile:[[panel filenames] objectAtIndex:0]]];
540 [workset setXmlFilename:[[panel filenames] objectAtIndex:0]];
541 [self updateChangeCount:NSChangeDone];
542 [self updateCompleteUI];
550 - (IBAction)loadXslt:(id)sender {
552 NSOpenPanel *panel = [NSOpenPanel openPanel];
554 if ([panel runModalForDirectory:nil file:nil types:nil] == NSOKButton) {
556 // NSLog(@"choosen: %@", [[panel filenames] objectAtIndex:0]);
558 [workset setXsltCode:[NSString stringWithContentsOfFile:[[panel filenames] objectAtIndex:0]]];
559 [workset setXsltFilename:[[panel filenames] objectAtIndex:0]];
560 [self updateChangeCount:NSChangeDone];
561 [self updateCompleteUI];
568 - (IBAction)saveXmlAs:(id)sender {
570 NSSavePanel *panel = [NSSavePanel savePanel];
572 if ([panel runModal] == NSFileHandlingPanelOKButton) {
574 [workset setXmlFilename:[panel filename]];
582 - (IBAction)saveXml:(id)sender {
585 if ([workset hasXmlFilename]) {
594 - (IBAction)saveXsltAs:(id)sender {
596 NSSavePanel *panel = [NSSavePanel savePanel];
598 if ([panel runModal] == NSFileHandlingPanelOKButton) {
600 [workset setXsltFilename:[panel filename]];
608 - (IBAction)saveXslt:(id)sender {
610 if ([workset hasXsltFilename]) {
620 - (IBAction)saveResultAs:(id)sender {
622 NSSavePanel *panel = [NSSavePanel savePanel];
624 if ([panel runModal] == NSFileHandlingPanelOKButton) {
626 [workset setResultFilename:[panel filename]];
628 [self saveResult:nil];
633 - (IBAction)saveResult:(id)sender {
635 if ([workset hasResultFilename]) {
637 [[workset result] writeToFile:[workset resultFilename] atomically:NO];
648 - (IBAction)openResultURL:(id)sender {
650 // NSLog(@"openResultURL running...");
652 if ([workset hasResultFilename]) {
654 [[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:[workset resultFilename]]];
660 - (IBAction)newParameter:(id)sender {
662 [[workset parameterSet] addParameter:@"name" withValue:@"value"];
667 - (IBAction)removeParameter:(id)sender {
669 int row = [parameterTable selectedRow];
672 [[workset parameterSet] removeParameterAtIndex:row];
680 - (int)numberOfRowsInTableView:(NSTableView *)aTableView {
681 return [[workset parameterSet] count];
684 - (id)tableView:(NSTableView *)aTableView
685 objectValueForTableColumn:(NSTableColumn *)aTableColumn
688 return [[workset parameterSet] getField:[aTableColumn identifier] atIndex:rowIndex];
693 - (void)tableView:(NSTableView *)aTableView
694 setObjectValue:(id)anObject
695 forTableColumn:(NSTableColumn *)aTableColumn
698 [[workset parameterSet] setField:[aTableColumn identifier] atIndex:rowIndex toString:anObject];
704 - (IBAction)setProcessorType:(id)sender {
706 int newType = [sender tag];
708 if ([processor processorType] == newType) {
712 [self switchProcessorToType:newType updateUI:NO];
714 NSLog(@"change processor type menu: %d", [sender tag]);
719 - (IBAction)switchProcessorToType:(int)newType updateUI:(BOOL)updateUI {
721 XSLTProcessor *newProcessor = nil;
723 newProcessor = [XSLTProcessorFactory makeProcessorOfType:newType];
726 NSLog(@"Unable to create new processor of type '%d'", newType);
730 processor = newProcessor;
733 [processorTypePopUp selectItemAtIndex:[processorTypePopUp indexOfItemWithTag:newType]];
741 - (id)handleProcessScriptCommand:(NSScriptCommand *)command {
743 if ([self canProcessNow]) {
752 - (id)handleExportScriptCommand:(NSScriptCommand *)command {
754 NSDictionary *args = [command evaluatedArguments];
755 NSString *file = [args objectForKey:@"File"];
761 [workset setResultFilename:file];
763 [self saveResult:nil];
770 - (id)handleSetParamScriptCommand:(NSScriptCommand *)command {
772 NSDictionary *args = [command evaluatedArguments];
773 NSString *paramName = [args objectForKey:@"Name"];
774 NSString *paramValue = [args objectForKey:@"Value"];
776 [[workset parameterSet] removeParameterByName:paramName];
778 [[workset parameterSet] addParameter:paramName withValue:paramValue];
785 - (id)handleClearParamScriptCommand:(NSScriptCommand *)command {
787 NSDictionary *args = [command evaluatedArguments];
788 NSString *paramName = [args objectForKey:@"Name"];
790 [[workset parameterSet] removeParameterByName:paramName];
795 - (id)handleSetProcessorTypeScriptCommand:(NSScriptCommand *)command {
797 NSDictionary *args = [command evaluatedArguments];
798 NSString *processorType = [args objectForKey:@"Name"];
800 if ([processorType caseInsensitiveCompare:@"libxslt"] == NSOrderedSame) {
802 [self switchProcessorToType:PROCESSORTYPE_LIBXSLT updateUI:YES];
804 } else if ([processorType caseInsensitiveCompare:@"sablotron"] == NSOrderedSame) {
806 [self switchProcessorToType:PROCESSORTYPE_SABLOTRON updateUI:YES];
808 } else if ([processorType caseInsensitiveCompare:@"saxon"] == NSOrderedSame) {
810 [self switchProcessorToType:PROCESSORTYPE_SAXON updateUI:YES];
813 NSLog(@"unknown processor");
821 - (BOOL)handleDroppedFile:(NSString *)filename forTextView:(NSTextView *)sender {
824 NSString *fileContents = [NSString stringWithContentsOfFile:filename];
826 if (fileContents == nil) {
830 if ([sender isEqual:xmlView]) {
832 [self setXmlcode:fileContents];
833 [workset setXmlFilename:filename];
835 } else if ([sender isEqual:xsltView]) {
837 [self setXsltcode:fileContents];
838 [workset setXsltFilename:filename];
842 NSLog(@"Unknown sender view");
852 - (NSString *)xmlcode {
853 return [workset xmlCode];
856 - (void)setXmlcode:(NSString *)s {
858 NSString *currentContents = [[[NSString alloc] initWithString:[self xmlcode]] autorelease];
860 [[self undoManager] registerUndoWithTarget:self
861 selector:@selector(setXmlcode:)
862 object:currentContents];
864 [workset setXmlCode:s];
865 [self updateCompleteUI];
870 - (NSString *)xsltcode {
871 return [workset xsltCode];
874 - (void)setXsltcode:(NSString *)s {
876 NSString *currentContents = [[[NSString alloc] initWithString:[self xsltcode]] autorelease];
878 [[self undoManager] registerUndoWithTarget:self
879 selector:@selector(setXsltcode:)
880 object:currentContents];
882 [workset setXsltCode:s];
883 [self updateCompleteUI];
887 - (NSString *)result {
888 return [workset result];
893 - (void)tableViewSelectionDidChange:(NSNotification *)notification {
898 - (void)checkForExternalModifications {
902 if ([workset xmlModifiedExternally] && xmlDirty || [workset xsltModifiedExternally] && xsltDirty) {
904 /* external changes conflicting with local changes detected.
905 * Ask the user if we should keep the local unsaved changes
907 keep = [self showUnsavedChangesPanel];
911 if ([workset xmlModifiedExternally] && !(xmlDirty && keep)) {
912 [workset reloadXmlFromFile];
913 [self updateChangeCount:NSChangeDone];
914 [self updateCompleteUI];
918 if ([workset xsltModifiedExternally] && !(xsltDirty && keep)) {
919 [workset reloadXsltFromFile];
920 [self updateChangeCount:NSChangeDone];
921 [self updateCompleteUI];
930 - (BOOL)showUnsavedChangesPanel {
932 [NSApp beginSheet:[unsavedChangesPanelController window]
933 modalForWindow:[[[self windowControllers] objectAtIndex:0] window]
938 [NSApp runModalForWindow:[unsavedChangesPanelController window]];
939 [NSApp endSheet:[unsavedChangesPanelController window]];
940 [[unsavedChangesPanelController window] orderOut:self];
942 return ([unsavedChangesPanelController keepChanges]);
947 - (void)windowDidBecomeMain:(NSNotification *)aNotification {
949 [self checkForExternalModifications];
953 - (void)windowDidResize:(NSNotification *)aNotification {
955 [self resizeWebView];
959 - (void)resizeWebView {
961 [[[resultWebView mainFrame] frameView] setFrame:[resultWebView frame]];
962 [resultWebView setNeedsDisplay:YES];
970 - (NSString *)windowNibName
972 // Override returning the nib file name of the document
973 // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
974 return @"MyDocument";
977 - (void)windowControllerDidLoadNib:(NSWindowController *) aController
979 NSFont *computerFont = [resultView font];
981 NSLog(@"resultview: ", resultView);
982 NSLog(@"font: ", computerFont);
984 NSSize errorDrawerSize;
986 [super windowControllerDidLoadNib:aController];
989 // Add any code here that need to be executed once the windowController has loaded the document's window.
991 // register our two input text views to receive file drags
993 // [xmlView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
994 // [xsltView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
997 [xmlView setFont:computerFont];
998 [xsltView setFont:computerFont];
1000 [xmlView setAllowsUndo:YES];
1001 [xsltView setAllowsUndo:YES];
1003 [resultWebView setTextSizeMultiplier:0.9];
1005 warningIcon = [xmlWellFormedIcon image];
1007 [self updateCompleteUI];
1009 errorDrawerSize = [errorDrawer contentSize];
1010 errorDrawerSize.height = 130;
1011 [errorDrawer setContentSize:errorDrawerSize];
1013 if (findPanelController == nil) {
1014 findPanelController = [[FindPanelController alloc] initWithWindowNibName:@"FindPanel"];
1015 // NSLog(@"init find panel controller: %@", findPanelController);
1018 if (jumpToLinePanelController == nil) {
1019 jumpToLinePanelController = [[JumpToLinePanelController alloc] initWithWindowNibName:@"JumpToLine"];
1020 // NSLog(@"init jump to line panel controller: %@", jumpToLinePanelController);
1023 if (unsavedChangesPanelController == nil) {
1024 unsavedChangesPanelController = [[UnsavedChangesPanelController alloc] initWithWindowNibName:@"UnsavedChanges"];
1025 // NSLog(@"init unsaved changes panel controller: %@", unsavedChangesPanelController);
1035 - (IBAction)showErrorLocation:(id)sender {
1037 XMLTextView *textView;
1040 if ([processor errorSource] == XSLT_ERROR_SOURCE_XML) {
1041 [self selectTabById:XML];
1044 [self selectTabById:XSLT];
1045 textView = xsltView;
1048 errorLine = [processor errorLine];
1050 [textView selectLineByNumber:errorLine];
1054 - (NSData *)dataRepresentationOfType:(NSString *)aType
1056 // Insert code here to write your document from the given data. You can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.
1057 return [NSArchiver archivedDataWithRootObject:workset];
1060 - (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
1062 // Insert code here to read your document from the given data. You can also choose to override -loadFileWrapperRepresentation:ofType: or -readFromFile:ofType: instead.
1065 workset = [[NSUnarchiver unarchiveObjectWithData:data] retain];
1066 [self updateCompleteUI];