home *** CD-ROM | disk | FTP | other *** search
/ OpenStep (Enterprise) / OpenStepENTCD.toast / OEDEV / DEV.Z / ToDoDoc.m < prev    next >
Encoding:
Text File  |  1996-07-30  |  12.3 KB  |  383 lines

  1. /*
  2.   You may freely copy, distribute, and reuse the code in this example.
  3.   NeXT disclaims any warranty of any kind, expressed or implied, as to its
  4.   fitness for any particular use.
  5. */
  6.  
  7. #import "ToDoDoc.h"
  8. #import "ToDoController.h"
  9. #import "ToDoInspector.h"
  10. #import "ToDoItem.h"
  11. #import "CalendarMatrix.h"
  12. #import "SelectionNotifMatrix.h"
  13.  
  14. extern void clearButtonMatrix(id matrix);
  15.  
  16. NSString *ToDoItemChangedNotification = @"ToDoItemChangedNotification";
  17.  
  18. @implementation ToDoDoc
  19.  
  20. /******************** MATRIX MANAGEMENT (item fields and time cells) ***********/
  21.  
  22. /* display itemNames in itemMatrix and update markMatrix cells with times & state */
  23.  
  24. - (void)updateMatrix
  25. {
  26.     int i, cnt = [currentItems count], rows = [[itemMatrix cells] count];
  27.     ToDoItem *thisItem;
  28.  
  29.     for (i=0; i<cnt, i<rows; i++) {
  30.         NSDate *due;
  31.         thisItem = [currentItems objectAtIndex:i];
  32.         if ([thisItem isKindOfClass:[ToDoItem class]]) {
  33.             if ( [thisItem secsUntilDue] )
  34.                 due = [[thisItem day] addTimeInterval:[thisItem secsUntilDue]];
  35.             else
  36.                 due = nil;
  37.             [[itemMatrix cellAtRow:i column:0] setStringValue:[thisItem itemName]];
  38.             [[markMatrix cellAtRow:i column:0] setTimeDue:due];
  39.             [[markMatrix cellAtRow:i column:0] setTriState:[thisItem itemStatus]];
  40.         }
  41.         else  {
  42.             [[itemMatrix cellAtRow:i column:0] setStringValue:@""];
  43.             [[markMatrix cellAtRow:i column:0] setTitle:@""];
  44.             [[markMatrix cellAtRow:i column:0] setImage:nil];
  45.         }
  46.     }
  47. }
  48.  
  49. - (void)selectItem:(int)item
  50. {
  51.     id thisItem = [currentItems objectAtIndex:item];
  52.     [itemMatrix selectCellAtRow:item column:0];
  53.     if (thisItem) {
  54.         if (![thisItem isKindOfClass:[ToDoItem class]]) thisItem = nil;
  55.         [[NSNotificationCenter defaultCenter]
  56. postNotificationName:ToDoItemChangedNotification
  57.               object:thisItem
  58.             userInfo:nil];
  59.     }
  60. }
  61.  
  62. - (void)itemChecked:sender
  63. {
  64.     int row = [sender selectedRow];
  65.     ToDoCell *cell = [sender cellAtRow:row column:0];
  66.     if (cell && [currentItems count]) {
  67.         id item = [currentItems objectAtIndex:row];
  68.         if (item && [item isKindOfClass:[ToDoItem class]]) {
  69.             [item setItemStatus:[cell triState]];
  70.             [[sender window] setDocumentEdited:YES];
  71.         }
  72.     }
  73. }
  74.  
  75.  
  76. /******************* STARTUP AND SHUTDOWN ***************************/
  77.    
  78. - initWithFile:(NSString *)aFile
  79. {
  80.     NSEnumerator *dayenum;
  81.     NSDate *itemDate;
  82.  
  83.     [super init];
  84.  
  85.     if (aFile) {
  86.         activeDays = [NSUnarchiver unarchiveObjectWithFile:aFile];
  87.         if (activeDays) 
  88.             activeDays = [activeDays retain];
  89.         else {
  90.             NSRunAlertPanel(@"To Do", @"Couldn't unarchive file %@", nil, nil, nil, aFile);
  91.             return nil;
  92.         }
  93.     } else {
  94.         activeDays = [[NSMutableDictionary alloc] init];
  95.         [self setCurrentItems:nil];
  96.     }
  97.  
  98.     if (![NSBundle loadNibNamed:@"ToDoDoc.nib" owner:self] )
  99.           return nil;
  100.  
  101.     if (aFile)
  102.         [[itemMatrix window] setTitleWithRepresentedFilename:aFile];
  103.     else
  104.         [[itemMatrix window] setTitle:@"UNTITLED"];
  105.  
  106.     // traverse activeDays->items and re-set timers
  107.     if ([self activeDays]) {
  108.         dayenum = [[self activeDays] keyEnumerator];
  109.         while (itemDate = [dayenum nextObject]) {
  110.             NSEnumerator *itemenum;
  111.             ToDoItem *anItem=nil;
  112.             NSArray *itemArray = [[self activeDays] objectForKey:itemDate];
  113.             itemenum = [itemArray objectEnumerator];
  114.             while ((anItem = [itemenum nextObject]) &&
  115.                    [anItem isKindOfClass:[ToDoItem class]] &&
  116.                    [anItem secsUntilNotif]) {
  117.                 [self setTimerForItem:anItem];
  118.             }
  119.         }
  120.     }
  121.     [[itemMatrix window] makeKeyAndOrderFront:self];   
  122.     return self;
  123. }
  124.  
  125. - (void)awakeFromNib
  126. {
  127.     int i;
  128.     NSDate *date;
  129.      
  130.     // get the items for today
  131.     date = [calendar selectedDay];
  132.     [self setCurrentItems:[activeDays objectForKey:date]];
  133.     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectionInMatrix:) name:@"SelectionInMatrixNotification" object:itemMatrix];
  134.  
  135.     /* set up self as delegates */
  136.     [[itemMatrix window] setDelegate:self];
  137.     [itemMatrix setDelegate:self];
  138.  
  139.     /* replace markMatrix with ToDoCells */
  140.     i = [[markMatrix cells] count];
  141.     while (i--) {
  142.         ToDoCell *aCell = [[ToDoCell alloc] init];
  143.         [aCell setTarget:self];
  144.         [aCell setAction:@selector(itemChecked:)];
  145.         [markMatrix putCell:aCell atRow:i column:0];
  146.         [aCell release];
  147.     }
  148.  
  149.     
  150.  }
  151.  
  152. - (void)activateDoc
  153. {
  154.     if ([currentItems count]) [self updateMatrix];
  155.     [dayLabel setStringValue:[[calendar selectedDay] descriptionWithCalendarFormat:@"To Do on %a %B %d %Y" timeZone:[NSTimeZone defaultTimeZone] locale:nil]];
  156. }
  157.  
  158. - (void)dealloc
  159. {
  160.     if (activeDays) [activeDays release];
  161.     if (currentItems) [currentItems release];
  162.     [super dealloc];
  163. }
  164.  
  165. /*********************** ACCESSOR METHODS **************************************/
  166.    
  167. - (NSMatrix *)itemMatrix {return itemMatrix;}
  168.  
  169. - (NSMatrix *)markMatrix {return markMatrix;}
  170.  
  171. - (NSMutableDictionary *)activeDays { return activeDays; }
  172.  
  173. - (CalendarMatrix *)calendar { return calendar; }
  174.  
  175. - (NSMutableArray *)currentItems { return currentItems; }
  176.  
  177. - (void)setCurrentItems:(NSMutableArray *)newItems
  178. {
  179.     if (currentItems) [currentItems autorelease];
  180.  
  181.     if (newItems) 
  182.         currentItems = [newItems mutableCopy];
  183.     else {
  184.         int numRows, numCols;
  185.         [itemMatrix getNumberOfRows:&numRows columns:&numCols];
  186.         currentItems = [[NSMutableArray alloc] initWithCapacity:numRows];
  187.         while (--numRows >= 0)
  188.             [currentItems addObject:@""];
  189.     }        
  190. }
  191.  
  192.  
  193. /************************ SAVING THE DOCUMENT ***********************************/
  194.    
  195. - (void)saveDocItems
  196. {
  197.     ToDoItem *anItem;
  198.     int i, cnt = [currentItems count];
  199.     // save day's current items (array) to document dictionary
  200.     for (i=0; i<cnt; i++) {
  201.         if ( (anItem = [currentItems objectAtIndex:i])  &&
  202.              ([anItem isKindOfClass:[ToDoItem class]]) ) {
  203.             [activeDays setObject:currentItems forKey:[anItem day]];
  204.             break;
  205.         }
  206.     }
  207. }
  208.  
  209. - (void)saveDoc
  210. {
  211.     NSString *fn;
  212.  
  213.     if (![[[itemMatrix window] title] hasPrefix:@"UNTITLED"]) {
  214.         fn = [[itemMatrix window] representedFilename];
  215.     } else {
  216.         int result;
  217.         NSSavePanel *sPanel = [NSSavePanel savePanel];
  218.         [sPanel setRequiredFileType:@"td"];
  219.         result = [sPanel runModalForDirectory:NSHomeDirectory() file:nil];
  220.         if (result == NSOKButton) {
  221.             fn = [sPanel filename];
  222.             [[itemMatrix window] setTitleWithRepresentedFilename:fn];
  223.             if (fn && ![fn isEqualToString:@""]) {
  224.                 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  225.                 [defaults setObject:fn forKey:@"ToDoDocumentLastSaved"];
  226.                 [defaults synchronize];
  227.             }
  228.         } else
  229.             return;
  230.     }
  231.     if (![NSArchiver archiveRootObject:activeDays toFile:fn])
  232.         NSRunAlertPanel(@"To Do", @"Couldn't archive file %@", nil, nil, nil, fn);
  233.     else
  234.         [[itemMatrix window] setDocumentEdited:NO];
  235. }
  236.  
  237.  
  238. /************************ SETTING AND RESPONDING TO TIMERS ***************************/
  239.    
  240. - (void)setTimerForItem:(ToDoItem *)anItem
  241. {
  242.     NSDate *notifDate;
  243.     NSTimer *aTimer;
  244.     if ([anItem secsUntilNotif]) {
  245.         notifDate = [[anItem day] addTimeInterval:[anItem secsUntilNotif]];
  246.         aTimer = [NSTimer scheduledTimerWithTimeInterval:[notifDate timeIntervalSinceNow]
  247.                                          target:self
  248.                                        selector:@selector(itemTimerFired:)
  249.                                        userInfo:anItem
  250.                                         repeats:NO];
  251.         [anItem setItemTimer:aTimer];
  252.     } else  // remove timer
  253.         [[anItem itemTimer] invalidate];
  254. }
  255.  
  256. - (void)itemTimerFired:(id)timer
  257. {
  258.     id anItem = [timer userInfo];
  259.     ToDoInspector *inspController = [[[NSApp delegate] inspector] delegate];
  260.     NSDate *dueDate = [[anItem day] addTimeInterval:[anItem secsUntilDue]];
  261.  
  262.     NSBeep();
  263.     NSRunAlertPanel(@"To Do", @"%@ on %@", nil, nil, nil, [anItem itemName], [dueDate descriptionWithCalendarFormat:@"%b %d, %Y at %I:%M %p" timeZone:[NSTimeZone defaultTimeZone] locale:nil]);
  264.     [anItem setSecsUntilNotif:0];
  265.     [inspController resetNotifSwitch];   
  266. }
  267.  
  268. /*************************** DOCUMENT MANAGEMENT--DELEGATION *****************/
  269.  
  270. - (void)controlTextDidChange:(NSNotification *)notif
  271. {
  272.     [[itemMatrix window] setDocumentEdited:YES];
  273. }
  274.  
  275.  
  276. - (void)controlTextDidEndEditing:(NSNotification *)notif
  277. {
  278.     id curItem, newItem;
  279.     int row = [itemMatrix selectedRow];
  280.     NSString *selName = [[itemMatrix selectedCell] stringValue];
  281.  
  282.     // here save the item just modified, if any
  283.     // Note: matrix of fields and array of items should mirror each other
  284.  
  285.     if (![[itemMatrix window] isDocumentEdited] ||
  286.         (row >= [currentItems count])) return;
  287.     if (![self currentItems])
  288.         [self setCurrentItems:nil];
  289.         
  290.     // Empty entry means delete corresponding item
  291.     if ([selName isEqualToString:@""] && 
  292.         ([[currentItems objectAtIndex:row] isKindOfClass:[ToDoItem class]]) &&
  293.         (![[[currentItems objectAtIndex:row] itemName] isEqualToString:@""])) 
  294.         [currentItems replaceObjectAtIndex:row withObject:@""];
  295.     
  296.     // change name if matching currentItem has name
  297.     else if ([[currentItems objectAtIndex:row] isKindOfClass:[ToDoItem class]] &&
  298.              ([[[currentItems objectAtIndex:row] itemName] isEqualToString:selName]) )
  299.         [[currentItems objectAtIndex:row] setItemName:selName];
  300.     
  301.     // item is new, add to currentItems
  302.     else if (![selName isEqualToString:@""]) {
  303.         newItem = [[ToDoItem alloc] initWithName:selName andDate:[calendar selectedDay]];
  304.         [currentItems replaceObjectAtIndex:row withObject:newItem];
  305.         [newItem release];
  306.     }    
  307.     // update the matrix
  308.     [self updateMatrix];
  309.  
  310.     // post notification to update inspector
  311.     if (curItem = [currentItems objectAtIndex:row]) {
  312.         if (![curItem isKindOfClass:[ToDoItem class]]) curItem = nil;
  313.         [[NSNotificationCenter defaultCenter] postNotificationName:ToDoItemChangedNotification object:curItem userInfo:nil];
  314.     }
  315.    
  316. }
  317.  
  318. - (void)selectionInMatrix:(NSNotification *)notif
  319. {
  320.     id curItem;
  321.     int row = [itemMatrix selectedRow];
  322.  
  323.     if (!currentItems || (![[itemMatrix window] isKeyWindow])) return;
  324.     if ( !(row < [currentItems count]) ) return;
  325.  
  326.     if (curItem = [currentItems objectAtIndex:row]) {
  327.         if (![curItem isKindOfClass:[ToDoItem class]]) curItem = nil;
  328.         [[NSNotificationCenter defaultCenter] postNotificationName:ToDoItemChangedNotification object:curItem userInfo:nil];
  329.     }
  330. }
  331.  
  332. - (BOOL)windowShouldClose:(id)sender
  333. {
  334.     int result;
  335.     if (![[itemMatrix window] isDocumentEdited]) {
  336.         [[NSNotificationCenter defaultCenter] postNotificationName:ToDoItemChangedNotification object:nil userInfo:nil];
  337.         return YES;
  338.     }
  339.     [[itemMatrix window] makeFirstResponder:[itemMatrix window]];
  340.  
  341.     result = NSRunAlertPanel(@"Close", @"Document has been edited. Save changes before closing?", @"Save", @"Don't Save", @"Cancel");
  342.  
  343.     switch(result) {
  344.       case NSAlertDefaultReturn: {
  345.           [self saveDocItems]; 
  346.           [self saveDoc];
  347.           [[NSNotificationCenter defaultCenter] postNotificationName:ToDoItemChangedNotification object:nil userInfo:nil];  
  348.           return YES;
  349.       }
  350.         case NSAlertAlternateReturn: {
  351.             [[NSNotificationCenter defaultCenter] postNotificationName:ToDoItemChangedNotification object:nil userInfo:nil];
  352.           return YES;
  353.         }
  354.         case NSAlertOtherReturn: {
  355.           return NO;
  356.         }
  357.     }
  358.     return NO;
  359. }
  360.  
  361. - (void)calendarMatrix:(CalendarMatrix *)matrix didChangeToDate:(NSDate *)date
  362. {
  363.     [self saveDocItems]; 
  364.  
  365.     // use date as key to get currentItems array
  366.     [self setCurrentItems:[activeDays objectForKey:date]];
  367.     [dayLabel setStringValue:[date descriptionWithCalendarFormat:@"To Do on %a %B %d %Y" timeZone:[NSTimeZone defaultTimeZone] locale:nil]];
  368.     [self updateMatrix];
  369.     [self selectItem:0];
  370. }
  371.  
  372. - (void)calendarMatrix:(CalendarMatrix *)matrix didChangeToMonth:(int)mo year:(int)yr
  373. {
  374.     [self saveDocItems];
  375.     [self setCurrentItems:nil];
  376.     [self updateMatrix];
  377. }
  378.  
  379.  
  380.  
  381. @end
  382.  
  383.