Logo Search packages:      
Sourcecode: adun.app version File versions  Download package

ULAnalyser.m

/*
   Project: UL

   Copyright (C) 2005 Michael Johnston & Jordi Villa-Freixa

   Author: Michael Johnston

   This application is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include "ULAnalyser.h"

//Private category handling the toolbar
@interface ULAnalyser (NSToolbarDelegate)
@end

@implementation ULAnalyser

- (id) initWithModelViewController: (id) mVC
{
      if((self = [super init]) != nil)
      {
            if([NSBundle loadNibNamed: @"Results" owner: self] == NO)
            {
                  NSWarnLog(@"Problem loading interface");
                  return nil;
            }
            
            analysisManager = [ULAnalysisManager new];
            mainViewController = mVC;
            outlineDelegate = nil;
            threadError = NO;
            selectedDataSet = nil;
            pluginDataSets = nil;
            pluginResults = nil;
            progressPanel = nil;
            selectedPlugin = nil;
            loadedObjects = [NSMutableArray new];
            selectedObjects = [NSMutableArray new];
            checkCount = 0;
            loadableTypes = [[NSArray alloc] initWithObjects: 
                              @"ULSimulation",
                              @"ULProcess",
                              @"AdDataSet",
                              @"ULSystem",
                              @"Structure",
                              nil];

            classMap = [NSDictionary dictionaryWithObjectsAndKeys:
                        @"Simulation", @"ULSimulation",
                        @"Data Set", @"AdDataSet",
                        @"System", @"ULSystem",
                        @"Structure", @"Structure", nil];
            [classMap retain];      
      }

      return self;
}

/**FIXME: mainViewController should be accessible through some class method*/

00074 - (id) init
{
      return [self initWithModelViewController: nil];
}

- (void) _setToolbarImages
{
      id path;

      path = [[NSBundle mainBundle] pathForImageResource: @"document-save.png"];
            if (path != nil)
      {
            saveImage = [[NSImage alloc] initWithContentsOfFile: path];
      }
      
      path = [[NSBundle mainBundle] pathForImageResource: @"apply.png"];
            if (path != nil)
      {
            applyImage = [[NSImage alloc] initWithContentsOfFile: path];
      }
      
      path = [[NSBundle mainBundle] pathForImageResource: @"reload.png"];
            if (path != nil)
      {
            reloadImage = [[NSImage alloc] initWithContentsOfFile: path];
      }
}

- (void)awakeFromNib
{
      ULIOManager* ioManager; 
      NSArray* plugins, *columns;

      ioManager = [ULIOManager appIOManager];
      
      [self updateAvailablePlugins];
      
      [optionsView sizeToFit];
      [window center];
      [window setDelegate: self];

      columns = [loadedObjectsTable tableColumns];
      [[[columns objectAtIndex: 0] headerCell] setStringValue: @"Data Name"];
      [[columns objectAtIndex: 0] setIdentifier: @"dataName"];
      [[[columns objectAtIndex: 0] dataCell] setAlignment: NSCenterTextAlignment];
      [[[columns objectAtIndex: 1] headerCell] setStringValue: @"Data Type"];
      [[columns objectAtIndex: 1] setIdentifier: @"dataType"];
      [[[columns objectAtIndex: 1] dataCell] setAlignment: NSCenterTextAlignment];
      [loadedObjectsTable setDataSource: self];
      [loadedObjectsTable setDelegate: self];
      [loadedObjectsTable setAllowsMultipleSelection: YES];
  
      [self _setToolbarImages];
      toolbar = [[NSToolbar alloc] initWithIdentifier: @"AnalyseToolbar"];
      [toolbar setAllowsUserCustomization: NO];
      [toolbar setDelegate: self];
      [window setToolbar: toolbar];
      [toolbar release];
       
      [self setupGnuplotInterface];
}

- (void) dealloc
{
      [classMap release];
      [loadableTypes release];
      [selectedObjects release];
      [loadedObjects release];
      [analysisManager release];
      [self gnuplotDealloc];
      [outlineDelegate release];
      [selectedDataSet release];
      [pluginDataSets release];
      [pluginResults release];
      [super dealloc];
}

- (void) handleThreadError: (NSException*) anException
{
      NSRunAlertPanel(@"Error",
                  [anException reason],
                  @"Dismiss", 
                  nil,
                  nil);
      
      //In this object the subthreads obtain results while the main thread
      //displays progress. When the subthread finishes the main thread expects
      //to use the results. When a thread error occurs we dont want the main thread
      //to do what it normally would. Unfortunately exceptions dont work well with
      //the main (gui) thread often casuing it to stop responding. To get around
      //this if there is an error we set the variable threadError. When the subthread
      //exits and the main thread resumes it can check for this error. It is important
      //to make sure this is set to NO at the start of the thread and that subthreads
      //dont use it.

      threadError = YES;
}

- (void) displayObjectInfo: (id) object
{
      NSRange endRange;

      endRange.location = 0;
      endRange.length = 0;
      [resultsLog replaceCharactersInRange:endRange withString: 
            @"-------------------------------------------------------------------------\n"];
      endRange.location = 0;
      endRange.length = 0;
      //FIXME: implement model object method that returns an informative string
      [resultsLog replaceCharactersInRange:endRange withString: [object availableInfo]];
      endRange.location = 0;
      endRange.length = 0;
      [resultsLog replaceCharactersInRange:endRange withString: 
            [NSString stringWithFormat: @"Trajectory - %@:\n", [(AdModelObject*)object name]]];
}

- (void) setAvailableDataSets: (NSArray*) array
{
      [dataSetList removeAllItems];
      [dataSetList addItemWithTitle: [(AdDataSet*)[array objectAtIndex: 0] name]];
      [dataSetList selectItemAtIndex: 0];
}

//Called when user selects a different data set
//from the data set list
00199 - (void) dataSetDidChange: (id) sender
{
      NSString* name;
      NSEnumerator* dataSetEnum;
      AdDataSet* dataSet;

      name = [dataSetList titleOfSelectedItem];
      dataSetEnum = [pluginDataSets objectEnumerator];
      while(dataSet = [dataSetEnum nextObject])
            if([[dataSet name] isEqual: name])
            {
                  [selectedDataSet release];
                  selectedDataSet = dataSet;
                  [selectedDataSet retain];
                  [dataView setDataSet: selectedDataSet];
                  [dataView displayData];
                  break;
            }
}

/***************

Opening and Closing the view

*****************/

- (void) open: (id) sender
{
      NSRange endRange;

      commandRange.location = [[gnuplotInterface textStorage] length];
      [self updateAvailablePlugins];
      [window makeKeyAndOrderFront: self];
}

- (void) close: (id) sender
{
      [selectedObjects removeAllObjects];
      [loadedObjects removeAllObjects];
      [loadedObjectsTable reloadData];
      [dataView clearDataSet];
      [pluginList selectItemWithTitle: @"None"];
      [selectedDataSet release];
      [dataSetList removeAllItems];
      [pluginDataSets release];
      [pluginResults release];
      [currentOptions release];
      selectedDataSet = nil;
      currentOptions = nil;
      pluginDataSets = nil;
      pluginResults = nil;
      [optionsView setDataSource: nil];
      [optionsView setDelegate: nil];
      [optionsView reloadData];
      [resultsLog 
            replaceCharactersInRange: NSMakeRange(0, [[resultsLog textStorage] length])
            withString: @""];
      [window orderOut: self];
}


/***************

 Validation

****************/

//initial implementation of load validation

- (NSNumber*) _validateLoad: (id) sender
{
      ULPasteboard* pasteboard = [ULPasteboard appPasteboard];
      id type, process;
      BOOL retval;

      //only load if ULAnalyser is not the current pasteboard
      //owner (Since then the object is already loaded).

      if(checkCount == [pasteboard changeCount])
            return [NSNumber numberWithBool: NO];
      
      type = [pasteboard availableTypeFromArray: loadableTypes];
      if(type != nil)
      {
            //If were loading a process check its been started
            if([type isEqual: @"ULProcess"])
            {
                  process = [pasteboard objectForType: type];
                  if([[process processStatus] isEqual: @"Waiting"])
                        retval = NO;
                  else
                        retval = YES;
            }
            else
                  retval = YES;
      }     
      else  
            retval = NO;

      return [NSNumber numberWithBool: retval]; 
}

//Only valid when the dataSet currently being displayed
//by ULAnalyserDataSetView i.e. the selectedDataSet, 
//is in pluginDataSets
- (NSNumber*) _validateSave: (id) sender
{
      if(selectedDataSet != nil)
            if([pluginDataSets containsObject: selectedDataSet])
                  return [NSNumber numberWithBool: YES];
      
      return [NSNumber numberWithBool: NO];
}

- (NSNumber*) _validateRemove: (id) sender
{
      if([selectedObjects count] != 0)
            return [NSNumber numberWithBool: YES];

      return [NSNumber numberWithBool: NO];     
}

- (NSNumber*) _validateDisplay: (id) sender
{
      ULPasteboard* pasteboard = [ULPasteboard appPasteboard];
      NSArray* availableTypes;

      availableTypes = [pasteboard availableTypes];
      if([availableTypes containsObject: @"AdDataSet"])
            return [NSNumber numberWithBool: YES];
      else
            return [NSNumber numberWithBool: NO];
}

- (NSNumber*) _validateAnalyse: (id) sender
{
      if([window isKeyWindow])
            return NO;
      else  
            return [self _validateLoad: sender];
}

- (BOOL) validateMenuItem: (NSMenuItem*) menuItem
{
      id action;  
      SEL selector;
      
      //NSPopUpButton is also a menu - however we want
      //it to be always active

      if([menuItem action] == NULL)
            return YES;
      
      if([NSStringFromSelector([menuItem action]) isEqual: @"deselectAllRows:"])
      {
            if([[loadedObjectsTable selectedRowIndexes] count] > 0)
                  return YES; 
            else
                  return NO;
      }     

      if([NSStringFromSelector([menuItem action]) isEqual: @"loadExternal:"])
                  return YES; 

      action = [NSStringFromSelector([menuItem action]) capitalizedString];
      action = [NSString stringWithFormat: @"_validate%@", action];
      selector = NSSelectorFromString(action);

      if([self respondsToSelector: selector])
            return [[self performSelector: selector] boolValue];
      else 
            return NO;
}

/***************

Menu Commands

****************/

//These methods deal with loading ULSimulation
//objects data in a threaded manner.

- (void) _threadedLoadSimulation: (id) object 
{
      NSAutoreleasePool* pool = [NSAutoreleasePool new];
      id holder;
      
      NS_DURING
      {
            sleep(0.5);
            [object loadData];
            [progressPanel performSelectorOnMainThread: @selector(setProgressInfo:)
                  withObject: @"Caching energies ..."
                  waitUntilDone: NO];
            [progressPanel performSelectorOnMainThread: @selector(setProgressBarValue:)
                  withObject: [NSNumber numberWithDouble: 40.0]
                  waitUntilDone: NO];
            //FIXME: This causes the simulation to load its energies
            //but its fairly hacky
            [object availableInfo]; 
            [progressPanel performSelectorOnMainThread: @selector(setProgressBarValue:)
                  withObject: [NSNumber numberWithDouble: 100.0]
                  waitUntilDone: YES];
            [progressPanel performSelectorOnMainThread: @selector(setProgressInfo:)
                  withObject: @"Complete"
                  waitUntilDone: NO];

            sleep(1.0);

            [progressPanel performSelectorOnMainThread: @selector(endPanel)
                  withObject: nil
                  waitUntilDone: NO];
      }
      NS_HANDLER
      {
            NSWarnLog(@"Caught plugin exception %@, %@, %@", 
                  [localException name], 
                  [localException reason],
                  [localException userInfo]);   
            [self performSelectorOnMainThread: @selector(handleThreadError:)
                  withObject: localException 
                  waitUntilDone: YES];
            [progressPanel performSelectorOnMainThread: @selector(endPanel)
                  withObject: nil
                  waitUntilDone: NO];
      }     
      NS_ENDHANDLER

      [pool release];
      [NSThread exit];
}

- (void) _loadSimulationData: (id) simulation
{
      if(selectedDataSet != nil)
            [dataView clearDataSet];

      progressPanel = [ULProgressPanel progressPanelWithTitle: @"Loading Data"
                        message: @"Loading Simulation"
                        progressInfo: @"Accessing trajectory ..."];
      [progressPanel setProgressBarValue: [NSNumber numberWithDouble: 0.0]];

      //detach the thread

      threadError = NO;
      [NSThread detachNewThreadSelector: @selector(_threadedLoadSimulation:)
            toTarget: self
            withObject: simulation];
      [progressPanel runProgressPanel: YES];
}

/*
- (void) reloadSimulation: (id) sender
{
      [self _loadSimulationData];
}*/

- (void) _flushProcessEnergies: (ULProcess*) process
{
      NSMutableDictionary* commandDict;
      NSError* error;
      NSString* alertTitle;
      id result;

      if([[process valueForKey:@"processStatus"] isEqual: @"Running"])
      {
            commandDict = [NSMutableDictionary dictionary];
            [commandDict setObject: @"flushEnergies"
                  forKey: @"command"];
            result = [[ULProcessManager appProcessManager]  
                        execute: commandDict 
                        error: &error 
                        process: process];
      
            if(error != nil)
            {
                  alertTitle = [NSString stringWithFormat: 
                              @"Alert: %@", 
                              [error domain]];
                  NSRunAlertPanel(alertTitle, 
                        [[error userInfo] objectForKey: NSLocalizedDescriptionKey], 
                        @"Dismiss", 
                        nil, 
                        nil);
            }
      }
}

- (id) _retrieveControllerResults: (ULProcess*) process
{
      NSMutableDictionary* commandDict;
      NSError* error;
      NSString* alertTitle;
      id result;

      result = nil;
      if([[process valueForKey:@"processStatus"] isEqual: @"Running"])
      {
            commandDict = [NSMutableDictionary dictionary];
            [commandDict setObject: @"controllerResults"
                  forKey: @"command"];
            result = [[ULProcessManager appProcessManager]  
                        execute: commandDict 
                        error: &error 
                        process: process];
      
            if(error != nil)
            {
                  alertTitle = [NSString stringWithFormat: 
                              @"Alert: %@", 
                              [error domain]];
                  NSRunAlertPanel(alertTitle, 
                        [[error userInfo] objectForKey: NSLocalizedDescriptionKey], 
                        @"Dismiss", 
                        nil, 
                        nil);
            }
      }

      return result;
}

- (void) load: (id) sender
{
      NSString* type;
      NSEnumerator* dataEnum;
      ULPasteboard* pasteboard = [ULPasteboard appPasteboard];
      id object, process, dataSet;
      
      //FIXME: Should support loading of mulitple objects simultaneously
      
      process = nil;
      type = [pasteboard availableTypeFromArray: loadableTypes];
      object = [pasteboard objectForType: type];

      //if the object is a ULSimulation we load up its data now
      //if its a ULProcess we flush its energies first

      if([type isEqual: @"ULSimulation"])
            [self _loadSimulationData: object];
      else if([type isEqual: @"ULProcess"])
      {
            process = object;
            //extract the simulation data from the process
            [self _flushProcessEnergies: process];
            object = [process simulationData];
            type = @"ULSimulation";
            [self _loadSimulationData: object];
            
      
      }     
      
      //check for errors after preprocessing

      if(!threadError)
            //FIXME: All model objects should return an info string
            if([type isEqual: @"ULSimulation"]) 
                  [self displayObjectInfo: object];

      //Add the object to the loaded objects list
      
      [loadedObjects addObject: object];

      if(process != nil)
      {
            //check if there are any controller results
            //from the running simulation
            object = [self _retrieveControllerResults: process];
            NSDebugLLog(@"Controller results are %@", object);
            if(object != nil)
            {
                  dataEnum = [object objectEnumerator];
                  while(dataSet = [dataEnum nextObject])
                        [loadedObjects addObject: dataSet];
            }           
      }           

      //Update the loaded objects table
      [loadedObjectsTable reloadData];
}

- (void) remove: (id) sender
{
      NSEnumerator* selectedObjectsEnum;
      id object;

      selectedObjectsEnum = [selectedObjects objectEnumerator];
      while(object = [selectedObjectsEnum nextObject])
            [loadedObjects removeObject: object];
      
      [loadedObjectsTable reloadData];
      
      [selectedObjects removeAllObjects];
      [analysisManager removeAllInputObjects];
      if([loadedObjects count] > 0)       
      {
            [loadedObjectsTable selectRowIndexes: 
                  [NSIndexSet indexSetWithIndex: 0]
                  byExtendingSelection: NO];
      }           
      [self updateAvailablePlugins];
      [self updatePluginOptions];
}

- (void) analyse: (id) sender
{
      [self load: self];
      [self open: self];
}

//Displays the selected data set
- (void) display: (id) sender
{
      ULPasteboard* pasteboard = [ULPasteboard appPasteboard];
      id object, type;

      //If we are not supplying the data then we must
      //load it first
      if([pasteboard changeCount] != checkCount)
      {
            [self load: self];
            object = [loadedObjects lastObject];
      }
      else
      {
            //we are supplying the data (we go through the
            //pasteboard anyway)
            type = [[pasteboard availableTypes] objectAtIndex: 0];
            object = [pasteboard objectForType: type];
      }

      [self setAvailableDataSets: [NSArray arrayWithObject: object]];
      [selectedDataSet release];
      selectedDataSet = [object retain];
      [dataView setDataSet: object];
      [dataView displayData];
      if(![window isKeyWindow])
            [self open: self];
}

//Only valid when the dataSet currently being displayed
//by ULAnalyserDataSetView is in pluginDataSets
- (void) save: (id) sender
{
      id dataSet, databaseInterface;
      AdModelObject *inputObject;
      NSEnumerator* resultsEnum, *inputObjectsEnum;
      
      //check there is an object available

      if(selectedDataSet == nil)
      {
            NSRunAlertPanel(@"Alert",
                  @"No data available to be saved.",
                  @"Dismiss", 
                  nil,
                  nil);
            return;
      }     
      
      //check if the object has already been saved
      
      databaseInterface = [ULDatabaseInterface databaseInterface];
      if(![databaseInterface objectInFileSystemDatabase: selectedDataSet])
      {     
            //FIXME: Change way of calling properties display tool
            [[mainViewController metadataController]
                  displayMetadataForModelObject: selectedDataSet
                  allowEditing: YES
                  runModal: YES];
      
            //The analysis manager saves the data set taking
            //care of all references
            [analysisManager saveOutputDataSet: selectedDataSet];

            //Reset the dataSet in the dataView and update the 
            //available data sets so the name change will be refelected

            [self setAvailableDataSets: [NSArray arrayWithObject: selectedDataSet]];
            [dataView setDataSet: selectedDataSet];
            [dataView displayData];
            [window makeKeyAndOrderFront: self];
      }
      else
            NSRunAlertPanel(@"Error",
                  @"Displayed data set already saved to database",
                  @"Dismiss", 
                  nil,
                  nil);
}

- (void) deselectAllRows: (id) sender
{
      int row;
      id selectedRows;
      
      selectedRows = [loadedObjectsTable selectedRowIndexes];
      if([selectedRows count] == 0)
            return;

      row = [selectedRows firstIndex];
      while(row != NSNotFound)
      {
            [loadedObjectsTable deselectRow: row];
            row = [selectedRows indexGreaterThanIndex: row];
      }
      
      [loadedObjectsTable setNeedsDisplay: YES];
      [selectedObjects removeAllObjects];
      [analysisManager removeAllInputObjects];
      [self updateAvailablePlugins];
      [self updatePluginOptions];
} 

- (void) logString: (NSString*) string
{
      NSRange endRange;

      endRange.location = 0;
      endRange.length = 0;
      [resultsLog replaceCharactersInRange:endRange withString: 
            @"-------------------------------------------------------------------------\n"];
      endRange.location = 0;
      endRange.length = 0;
      [resultsLog replaceCharactersInRange:endRange withString: string];
}

//for loading pdbs
- (void) loadExternal: (NSString*) string
{
      id fileBrowser;
      int result;
      NSArray* allowedFileTypes;
      Structure* structure;

      fileBrowser = [NSOpenPanel openPanel];
      [fileBrowser setTitle: @"Load External Object"];
      [fileBrowser setDirectory: [[NSUserDefaults standardUserDefaults] stringForKey:@"PDBDirectory"]];
      [fileBrowser setAllowsMultipleSelection: NO];
      allowedFileTypes = [NSArray arrayWithObjects: @"pdb", @"PDB", nil];
      result = [fileBrowser runModalForTypes: allowedFileTypes];
                  
      if(result == NSOKButton)
      {
            if(![allowedFileTypes containsObject: [[fileBrowser filename] pathExtension]])
                  NSRunAlertPanel(@"Error", 
                        [NSString stringWithFormat: @"Unknown file type - %@\nSupported types %@", 
                              [[fileBrowser filename] pathExtension], allowedFileTypes],
                        @"Dismiss",
                        nil,
                        nil);

            structure = [StructureFactory newStructureFromPDBFile: [fileBrowser filename]];
            [loadedObjects addObject: structure];
            [loadedObjectsTable reloadData];
      }

}

/**
Pasteboard Methods
**/

00763 - (NSArray*) availableTypes
{
      NSString* type;

      if([selectedObjects count] > 0)
      {
            type = NSStringFromClass([[selectedObjects objectAtIndex: 0] class]);
            return [NSArray arrayWithObject: type];
      }     
      else
            return [NSArray array];
            
}

- (id) objectForType: (NSString*) type;
{
      if([[self availableTypes] containsObject: type])
            return [selectedObjects objectAtIndex: 0];
      else
            return nil;
}

- (NSArray*) objectsForType: (NSString*) type
{
      if([[self availableTypes] containsObject: type])
            return [NSArray arrayWithObject: [selectedObjects objectAtIndex: 0]];
      else
            return nil;

}

- (int) countOfObjectsForType: (NSString*) type
{
      if([[self availableTypes] containsObject: type])
            return 1;
      else
            return 0;

}

- (void) pasteboardChangedOwner: (id) pasteboard
{
      [self deselectAllRows: self];
}

/***************

Loaded Objects Table Data Source Methods

*****************/

- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
      return [loadedObjects count];
}

- (id)tableView:(NSTableView *)aTableView 
      objectValueForTableColumn:(NSTableColumn *)aTableColumn 
      row:(int)rowIndex
{
      id object;

      object = [loadedObjects objectAtIndex: rowIndex];
      if([[aTableColumn identifier] isEqual: @"dataType"])
            return [classMap objectForKey: NSStringFromClass([object class])];
      else  
      {
            if([object isKindOfClass: [AdModelObject class]])
                  return [(AdModelObject*)object name];
            else
                  return [object pdbcode] == nil ? @"Unknown" : [object pdbcode];
      }           
}

/***************

Loaded Objects Table Delegate Methods

*****************/

- (void) tableViewSelectionDidChange: (NSNotification*) aNotification
{
      int row;
      id selectedRows;
      
      [selectedObjects removeAllObjects];
      [analysisManager removeAllInputObjects];
      selectedRows = [loadedObjectsTable selectedRowIndexes];
      if([selectedRows count] == 0)
            return;

      row = [selectedRows firstIndex];
      while(row != NSNotFound)
      {
            [selectedObjects addObject: 
                  [loadedObjects objectAtIndex: row]];
            [analysisManager addInputObject: 
                  [loadedObjects objectAtIndex: row]];
            row = [selectedRows indexGreaterThanIndex: row];
      }

      [self updateAvailablePlugins];
      [self updatePluginOptions];
}

- (BOOL) tableView: (NSTableView*) table shouldSelectRow: (int) row
{
      ULPasteboard* pasteboard = [ULPasteboard appPasteboard];

      if([pasteboard changeCount] != checkCount)
      {
            [pasteboard setPasteboardOwner: self];
            checkCount = [pasteboard changeCount];
      }

      return YES;
}     

/***************

Window Delegate Methods

*****************/

- (void) windowDidResize: (NSNotification*) aNotification
{
      [optionsView sizeToFit];      
}

- (void) windowWillClose: (NSNotification*) aNotification
{
      [self close: self];
}

@end

/*
 *
 * Extensions to ULAnalyser to
 * enable gnuplot integration.
 *
*/

@implementation ULAnalyser (ULAnalyserGnuplotExtensions)

- (void) gnuplotDealloc
{
      [pipey release];
      [outPipe release];
      [gnuplotOutput release];
      [gnuplotError release];
      [gnuplot terminate];
      [gnuplot release];
      [history release];
}

- (void) setupGnuplotInterface
{
      NSRange endRange;
      ULIOManager* ioManager = [ULIOManager appIOManager];

      [gnuplotInterface setDelegate: self];
      pipey =  [NSPipe new]; 
      outPipe = [NSPipe new];
      gnuplotOutput =  [[pipey fileHandleForWriting] retain]; 
      gnuplotError = [[outPipe fileHandleForReading] retain];
      gnuplot = [NSTask new];
      //FIXME: Read launch path from defaults
      [gnuplot setLaunchPath: @"/usr/bin/gnuplot"];
      [gnuplot setCurrentDirectoryPath: [ioManager applicationDir]];
      [gnuplot setStandardInput: pipey];
      [gnuplot setStandardError: outPipe];
      [gnuplot launch];

      history = [[NSMutableArray arrayWithCapacity: 1] retain];
      historyDepth = 100;
      currentHistoryPosition = 0;
      
      endRange.location = 0;
      endRange.length = 0;
      [gnuplotInterface replaceCharactersInRange:endRange withString:@"gnuplot> "];

      gnuplotPrompt.location = 0;
      gnuplotPrompt.length = [[gnuplotInterface textStorage] length];

}

/******************

Gnuplot History

*******************/

- (void) _addStringToHistory: (NSString*) string
{
      [history addObject: string];

      if([history count] == historyDepth)
            [history removeObjectAtIndex: 0];

      currentHistoryPosition = [history count];
}

- (NSString*) _previousStringFromHistory
{
      id string;

      NS_DURING
      {
            currentHistoryPosition--;
            string = [history objectAtIndex: currentHistoryPosition];
            return string;
      }
      NS_HANDLER
      {
            if([[localException name] isEqual: NSRangeException])
            {
                  if([history count] != 0)
                  {
                        currentHistoryPosition = 0;
                        string = [history objectAtIndex: currentHistoryPosition];
                        return string;
                  }
                  else
                        return @"";
            }
      }
      NS_ENDHANDLER
}

- (NSString*) _nextStringFromHistory
{
      id string;

      NS_DURING
      {
            currentHistoryPosition++;
            string = [history objectAtIndex: currentHistoryPosition];
            return string;
      }
      NS_HANDLER
      {
            if([[localException name] isEqual: NSRangeException])
            {
                  currentHistoryPosition = [history count];
                  return @"";
            }
      }
      NS_ENDHANDLER
}

/********************

Gnuplot TextView Delegate Methods 

********************/

- (BOOL) textView: (NSTextView*) aTextView doCommandBySelector:(SEL)aSelector
{
      NSRange endRange;
      NSString* string, *errorString;
      NSData* data;

      if([NSStringFromSelector(aSelector) isEqual: @"insertNewline:"])
      {
            commandRange.length = [[aTextView textStorage] length] - commandRange.location;
            string = [[[aTextView textStorage] attributedSubstringFromRange: commandRange] string];
            [self _addStringToHistory: string];
            string = [NSString stringWithFormat: @"%@\n", string];
            data  = [string dataUsingEncoding: NSASCIIStringEncoding];
            [gnuplotOutput writeData: data];
            endRange.location = [[aTextView textStorage] length];
            endRange.length = 0;
            [aTextView replaceCharactersInRange:endRange withString:
                  [NSString stringWithFormat: @"\n", string]];

            endRange.location = [[aTextView textStorage] length];
            gnuplotPrompt.location = endRange.location;
            endRange.length = 0;
            [aTextView replaceCharactersInRange:endRange withString: @"gnuplot> "];
            gnuplotPrompt.length = [[aTextView textStorage] length] - gnuplotPrompt.location;
            //make sure the cursor appears after the ">"
            endRange.location = [[aTextView textStorage] length];
            [aTextView setSelectedRange: endRange];
            [aTextView scrollRangeToVisible: endRange];
            commandRange.location = [[aTextView textStorage] length];
            return YES;
      }
      else if([NSStringFromSelector(aSelector) isEqual: @"moveUp:"])
      {
            string = [self _previousStringFromHistory];
            commandRange.length = [[aTextView textStorage] length] - commandRange.location;
            [aTextView replaceCharactersInRange:commandRange withString: string];
            return YES;
      }     
      else if([NSStringFromSelector(aSelector) isEqual: @"moveDown:"])
      {
            string = [self _nextStringFromHistory];
            commandRange.length = [[aTextView textStorage] length] - commandRange.location;
            [aTextView replaceCharactersInRange:commandRange withString: string];
            return YES;
      }

      return NO;
}

- (BOOL) textView: (NSTextView*) aTextView
      shouldChangeTextInRange:  (NSRange) range
      replacementString: (NSString*) string
{
      NSRange intersectionRange;
      intersectionRange = NSIntersectionRange(range, gnuplotPrompt);
      
      if(intersectionRange.length == 0)
            return YES;
      else
            return NO;
}

@end

@implementation ULAnalyser (NSToolbarDelegate)

- (NSToolbarItem*)toolbar: (NSToolbar*)toolbar
    itemForItemIdentifier: (NSString*)itemIdentifier
willBeInsertedIntoToolbar: (BOOL)flag
{
  NSToolbarItem *toolbarItem = AUTORELEASE([[NSToolbarItem alloc]
                                   initWithItemIdentifier: itemIdentifier]);

      if([itemIdentifier isEqual: @"ApplyItem"])
      {
            [toolbarItem setLabel: @"Apply"];
            [toolbarItem setImage: applyImage];
            [toolbarItem setTarget: self];
            [toolbarItem setAction: @selector(applyCurrentPlugin:)];     
            [toolbarItem setTag: 0];
      }
      else if([itemIdentifier isEqual: @"SaveItem"])
      {
            [toolbarItem setLabel: @"Save"];
            [toolbarItem setImage: saveImage];
            [toolbarItem setTarget: self];
            [toolbarItem setAction: @selector(save:)];     
            [toolbarItem setTag: 1];
      }
      else if([itemIdentifier isEqual: @"ReloadItem"])
      {
            [toolbarItem setLabel: @"Reload"];
            [toolbarItem setImage: reloadImage];
            [toolbarItem setTarget: self];
            //[toolbarItem setAction: @selector(save:)];     
            [toolbarItem setTag: 2];
      }

       
  return toolbarItem;
}

- (NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar*)toolbar
{
  return [NSArray arrayWithObjects: @"ApplyItem", 
              @"SaveItem",
              @"ReloadItem",
              nil];
}

- (NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar*)toolbar
{ 
  return [NSArray arrayWithObjects: @"ApplyItem", 
              @"SaveItem", 
              @"ReloadItem",
              nil];
}

- (NSArray*) toolbarSelectableItemIdentifiers: (NSToolbar*)toolbar
{ 
  return [NSArray arrayWithObjects: @"ApplyItem", 
              @"SaveItem", 
              @"ReloadItem",
              nil];
}
@end

Generated by  Doxygen 1.6.0   Back to index