home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / OPENSTEP / Utilities / Comics-1.0-MIS / ComicsObj.m < prev    next >
Encoding:
Text File  |  1997-09-25  |  18.8 KB  |  591 lines

  1. /*
  2.  File:       ComicsObj.m
  3.  
  4.  Contains:   Source code for the 3 classes used for the Comics database management:
  5.              CIssue, CTitle, CComics
  6.  
  7.  Written by: Eric Simenel
  8.  
  9.  Created:    May 1997
  10.  
  11.  Copyright:  (c)1997 by Apple Computer, Inc., all rights reserved.
  12.  
  13.  Change History (most recent first):
  14.  
  15.  You may incorporate this sample code into your applications without
  16.  restriction, though the sample code has been provided "AS IS" and the
  17.  responsibility for its operation is 100% yours.  However, what you are
  18.  not permitted to do is to redistribute the source as "DSC Sample Code"
  19.  after having made changes. If you're going to re-distribute the source,
  20.  we require that you make it clear in the source that the code was
  21.  descended from Apple Sample Code, but that you've made changes.
  22. */
  23.  
  24. #import "ComicsObj.h"
  25.  
  26. // ComicsObj.h is a project header, so that comicsBase is known in all other source files
  27. // There is only one instantiated object (when the main nib opens) of class CComics, and
  28. // comicsBase is it. Simpler to use than having a "comics" outlet and a "comics" accessor
  29. // in the main controller "VerifyController", and having all other source files refer to
  30. // the database as [[NSApp delegate] comics]
  31. CComics *comicsBase = nil;
  32.  
  33. // The following global arrays are here to "speed up" number to string conversion,
  34. // see the initGlobals method of CComics
  35. tnumstr gnumstr;
  36. Str3 gnums[1000];
  37.  
  38. // Since we're dealing with American Comics only, the strings for edit and buy month are
  39. // "JAN", "FEB", etc. contained in this array.
  40. Str3 gmonths[12];
  41.  
  42. // This is the notification which will be sent by the InputController when the content of
  43. // the database is changed. Some other Controllers will register to be notified.
  44. NSString *ComicsDidChangeNotification = @"ComicsDidChangeNotification";
  45.  
  46. @implementation CIssue
  47.  
  48. - (id)init
  49. {
  50.     return [self initWithIsh:-1 withEdit:-1 withBuy:-1 withFlag:-1];
  51. }
  52. - (id)initWithIsh:(short)ish withEdit:(short)edit withBuy:(short)buy withFlag:(short)flag
  53. {
  54.     if (self = [super init])
  55.       {
  56.         [self setIssueNumber:ish];
  57.         [self setEditMonth:edit];
  58.         [self setBuyMonth:buy];
  59.         [self setIssueFlags:flag];
  60.       }
  61.     return self;
  62. }
  63.  
  64. - (void)setIssueFlags:(short)value { issueFlags = value; }
  65. - (short)issueFlags { return issueFlags; }
  66. - (void)setIssueNumber:(short)value { issueNumber = value; }
  67. - (short)issueNumber { return issueNumber; }
  68. - (void)setEditMonth:(short)value { editMonth = value; }
  69. - (short)editMonth { return editMonth; }
  70. - (void)setBuyMonth:(short)value { buyMonth = value; }
  71. - (short)buyMonth { return buyMonth; }
  72.  
  73. - (NSString *)grade
  74. {
  75.     if (issueFlags & mskMint) return @"Mint";
  76.     if (issueFlags & mskFine) return @"Fine";
  77.     if (issueFlags & mskMiss) return @"Missing";
  78.     if (issueFlags & mskPoor) return @"Poor"; else return @"Good";
  79. }
  80. - (NSString *)ishtype
  81. {
  82.     if (issueFlags & mskComics  ) return @"Comics";
  83.     if (issueFlags & mskNewForm ) return @"New Format";
  84.     if (issueFlags & mskLuxe    ) return @"Luxe"; else return @"Magazine";
  85. }
  86. - (NSString *)content
  87. {
  88.     if (issueFlags & mskStory  ) return @"Story";
  89.     if (issueFlags & mskInfo   ) return @"Information"; else return @"Reprint";
  90. }
  91.  
  92. - (id)initWithCoder:(NSCoder *)coder
  93. {
  94.     short temp;
  95.     [coder decodeValuesOfObjCTypes:"s", &temp];
  96.     [self setIssueNumber:temp];
  97.     [coder decodeValuesOfObjCTypes:"s", &temp];
  98.     [self setEditMonth:temp];
  99.     [coder decodeValuesOfObjCTypes:"s", &temp];
  100.     [self setBuyMonth:temp];
  101.     [coder decodeValuesOfObjCTypes:"s", &temp];
  102.     [self setIssueFlags:temp];
  103.     return self;
  104. }
  105. - (void)encodeWithCoder:(NSCoder *)coder
  106. {
  107.     short temp;
  108.     temp = [self issueNumber];
  109.     [coder encodeValuesOfObjCTypes:"s", &temp];
  110.     temp = [self editMonth];
  111.     [coder encodeValuesOfObjCTypes:"s", &temp];
  112.     temp = [self buyMonth];
  113.     [coder encodeValuesOfObjCTypes:"s", &temp];
  114.     temp = [self issueFlags];
  115.     [coder encodeValuesOfObjCTypes:"s", &temp];
  116. }
  117.  
  118. @end
  119.  
  120. @implementation CTitle
  121.  
  122. - (id)init
  123. {
  124.     return [self initWithAbb:@"ZZZ" withTitle:@"Zzzzzzzzzz" withFlag:-1];
  125. }
  126. - (id)initWithAbb:(NSString *)theAbb withTitle:(NSString *)theTitle withFlag:(short)flag
  127. {
  128.     if (self = [super init])
  129.       {
  130.         [self setAbb:theAbb];
  131.         [self setTitle:theTitle];
  132.         [self setTitleFlags:flag];
  133.         [self setIssues:[NSMutableArray array]];
  134.       }
  135.     return self;
  136. }
  137. - (void)dealloc
  138. {
  139.     [abb release];
  140.     [title release];
  141.     [issues release];
  142.     [super dealloc];
  143. }
  144.  
  145. - (void)setTitleFlags:(short)value { titleFlags = value; }
  146. - (short)titleFlags { return titleFlags; }
  147. - (void)setAbb:(NSString *)value { [abb autorelease]; abb = [value copy]; }
  148. - (NSString *)abb { return abb; }
  149. - (void)setTitle:(NSString *)value { [title autorelease]; title = [value copy]; }
  150. - (NSString *)title { return title; }
  151. - (NSString *)brand { return (titleFlags & mskMarvel)?@"Marvel":((titleFlags & mskDC)?@"DC":@"Other"); }
  152. - (NSString *)tstate { return (titleFlags & mskLive)?@"Live":@"Dead"; }
  153. - (NSString *)series { return (titleFlags & mskLong)?@"Long":@"Mini"; }
  154. - (NSString *)kind { return (titleFlags & mskMain)?@"Main":@"Dual"; }
  155. - (short)nbIssues { return [issues count]; }
  156. - (void)setIssues:(NSMutableArray *)theArray { [theArray retain]; [issues release]; issues = theArray; }
  157. - (NSMutableArray *)issues { return issues; }
  158.  
  159. // Returns a NSString containing the list of the issues of this title with the following format:
  160. // "1-66,94-345" meaning all issues between 1 and 66 (included) and all issues between 94 and 345.
  161. // Optimized (other time) to the point where it's obfuscated... Sorry.
  162. - (NSString *)listIssues
  163. {
  164.     NSString *result;
  165.     long ref, oref, nref, diff, i = 0, n = [issues count];
  166.     char str[1000], *starts, *s;
  167.     starts = s = str;
  168.     while (i < n)
  169.       {
  170.         nref = oref = ref = [[issues objectAtIndex:i] issueNumber];
  171.         *((long *)s) = gnumstr.nstrs[ref];
  172.         s += gnumstr.lens[ref];
  173.         while ((++i < n) && ((ref+1) == (nref = [[issues objectAtIndex:i] issueNumber]))) ref = nref;
  174.         if ((diff = (ref - oref)) > 0)
  175.           {
  176.             *s++ = (diff > 1)?'-':',';
  177.             *((long *)s) = gnumstr.nstrs[ref];
  178.             s += gnumstr.lens[ref];
  179.           }
  180.         *s++ = ',';
  181.       }
  182.     if (s != starts) *--s = 0; else *s = 0;
  183.     result = [[NSString alloc] initWithCString:str];
  184.     [result autorelease];
  185.     return result;
  186. }
  187.  
  188. - (id)initWithCoder:(NSCoder *)coder
  189. {
  190.     short temp;
  191.     [self setAbb: [coder decodeObject]];
  192.     [self setTitle: [coder decodeObject]];
  193.     [self setIssues: [coder decodeObject]];
  194.     [coder decodeValuesOfObjCTypes:"s", &temp];
  195.     [self setTitleFlags:temp];
  196.     return self;
  197. }
  198. - (void)encodeWithCoder:(NSCoder *)coder
  199. {
  200.     short temp = [self titleFlags];
  201.     [coder encodeObject: [self abb]];
  202.     [coder encodeObject: [self title]];
  203.     [coder encodeObject: [self issues]];
  204.     [coder encodeValuesOfObjCTypes:"s", &temp];
  205. }
  206.  
  207. - (short)findIssue:(short)byIshNum
  208. {
  209.     int i, j = -1, found = 0, n = [issues count];
  210.     for(i = 0; (i < n) && (!found); i++)
  211.         if (found = ([[issues objectAtIndex:i] issueNumber] == byIshNum)) j = i;
  212.     return j;
  213. }
  214. - (short)addIssue:(CIssue *)theIssue
  215. {
  216.     int i, j = -1, ok = 1, n = [issues count];
  217.     CIssue *thisIssue;
  218.     for(i = 0; (i < n) && ok; i++)
  219.       {
  220.         ok = ([(thisIssue = [issues objectAtIndex:i]) issueNumber] != [theIssue issueNumber]);
  221.         if (j == -1) if ([theIssue editMonth] < [thisIssue editMonth]) j = i;
  222.       }
  223.     if (ok)
  224.       {
  225.         [comicsBase setIssue:theIssue];
  226.         [comicsBase modNbIssues:1];
  227.         if (j == -1) [issues addObject:theIssue];
  228.         else [issues insertObject:theIssue atIndex:j];
  229.         return 0;
  230.       }
  231.     else return -1;
  232. }
  233. - (short)modIssue:(CIssue *)oldIssue withNewIssue:(CIssue *)newIssue
  234. {
  235.     short theIndex;
  236.     if ([oldIssue issueNumber] != [newIssue issueNumber]) return -2;
  237.     if ((theIndex = [self findIssue:[oldIssue issueNumber]]) == -1) return -1;
  238.     [comicsBase setIssue:newIssue];
  239.     [issues replaceObjectAtIndex:theIndex withObject:newIssue];
  240. #if debug
  241.     NSLog(@"CTitle::modIssue");
  242. #endif
  243.     return 0;
  244. }
  245. - (short)deleteIssue:(CIssue *)theIssue
  246. {
  247.     short theIndex;
  248.     if ((theIndex = [self findIssue:[theIssue issueNumber]]) == -1) return -1;
  249.     [comicsBase modNbIssues:-1];
  250.     [issues removeObjectAtIndex:theIndex];
  251.     return 0;
  252. }
  253.  
  254. // Comparison methods used in sortArray of CComics
  255. - (NSComparisonResult)compareAbb:(CTitle *)aTitle { return [abb compare:[aTitle abb]]; }
  256. - (NSComparisonResult)compareTitle:(CTitle *)aTitle { return [title compare:[aTitle title]]; }
  257. - (NSComparisonResult)compareChrono:(CTitle *)aTitle
  258. {
  259.     short firstEdit = [[issues objectAtIndex:0] editMonth];
  260.     short theOtherFirstEdit = [[[aTitle issues] objectAtIndex:0] editMonth];
  261.     if (firstEdit == theOtherFirstEdit) return NSOrderedSame;
  262.     else if (firstEdit < theOtherFirstEdit) return NSOrderedAscending;
  263.     else return NSOrderedDescending;
  264. }
  265. - (NSComparisonResult)compareMaxIssue:(CTitle *)aTitle
  266. {
  267.     short lastMax = [[issues lastObject] issueNumber];
  268.     short theOtherLastMax = [[[aTitle issues] lastObject] issueNumber];
  269.     if (lastMax == theOtherLastMax) return NSOrderedSame;
  270.     else if (lastMax < theOtherLastMax) return NSOrderedDescending;
  271.     else return NSOrderedAscending;
  272. }
  273.  
  274. @end
  275.  
  276. char strdate[10];
  277.  
  278. @implementation CComics
  279.  
  280. + (char *)cStrDate:(short)theMonth
  281. {
  282.     strcpy(strdate, gmonths[(theMonth-1)%12]);
  283.     strcat(strdate, gnums[(theMonth-1)/12]);
  284.     return strdate;
  285. }
  286. + (NSString *)nsStrDate:(short)theMonth
  287. {
  288.     return [NSString stringWithCString:[CComics cStrDate:theMonth]];
  289. }
  290.  
  291. // Private helper to get database path
  292. - (NSString *)dbPath
  293. {
  294.     NSMutableString *result = [[NSMutableString alloc] initWithString:[[NSBundle mainBundle] bundlePath]];
  295.     [result autorelease];
  296.     [result appendString:@"/ComicsDataBase"];
  297.  
  298. #if debug
  299.     NSLog(@"dbPath = '%@'", result);
  300. #endif
  301.  
  302.     return result;
  303. }
  304.  
  305. // Fills the global arrays to speed up number to string conversion
  306. - (void)initGlobals
  307. {
  308.     long i, j, n, *pi, *pl;
  309.     char ns[] = "0123456789";
  310.     char s[5], *p1, *p2;
  311.  
  312.     *(pi = &gnumstr.lens[0]) = 1;
  313.     pl = &gnumstr.nstrs[0];
  314.     *(p1 = (char *)pl) = '0';
  315.     for(i=1; i<1001; i++)
  316.       {
  317.         j = i; p1 = s; n = 0;
  318.         while (j>0)
  319.           {
  320.             *p1++ = ns[j % 10];
  321.             j = j / 10;
  322.             n++;
  323.           }
  324.         *++pi = n;
  325.         p2 = (char *)(++pl);
  326.         while (n--) *p2++ = *--p1;
  327.       }
  328.     strcpy(gmonths[ 0], "JAN");
  329.     strcpy(gmonths[ 1], "FEB");
  330.     strcpy(gmonths[ 2], "MAR");
  331.     strcpy(gmonths[ 3], "APR");
  332.     strcpy(gmonths[ 4], "MAY");
  333.     strcpy(gmonths[ 5], "JUN");
  334.     strcpy(gmonths[ 6], "JUL");
  335.     strcpy(gmonths[ 7], "AUG");
  336.     strcpy(gmonths[ 8], "SEP");
  337.     strcpy(gmonths[ 9], "OCT");
  338.     strcpy(gmonths[10], "NOV");
  339.     strcpy(gmonths[11], "DEC");
  340.     for(i=0; i<=9; i++)
  341.       {gnums[i][0] = 32; gnums[i][1] = 32;
  342.       gnums[i][2] = ns[i]; gnums[i][3] = 0;}
  343.     for(i=1; i<=9; i++) for(j=0; j<= 9; j++)
  344.       {gnums[i*10+j][0] = 32; gnums[i*10+j][1] = ns[i];
  345.       gnums[i*10+j][2] = ns[j]; gnums[i*10+j][3] = 0;}
  346.     for(i=1; i<=9; i++) for(j=0; j<= 9; j++) for(n=0; n<=9; n++)
  347.       {gnums[i*100+j*10+n][0] = ns[i]; gnums[i*100+j*10+n][1] = ns[j];
  348.       gnums[i*100+j*10+n][2] = ns[n]; gnums[i*100+j*10+n][3] = 0;}
  349. }
  350.  
  351. // Private method to convert the Mac database which I already have.
  352. // Better than reenter 22,000+ issues...
  353. - (void)convertFromMacFormat
  354. {
  355.     CTitle *theTitle;
  356.     CIssue *theIssue;
  357.     short i, j, nbt, nbi, flag, ish, edit, buy;
  358.     unsigned char *buf, sl;
  359.     long pos, pos2;
  360.     char thestring[100];
  361.     NSString *theAbbStr, *theTitleStr;
  362.     NSMutableString *fileName = [[NSMutableString alloc] initWithString:[[NSBundle mainBundle] pathForResource:@"Comics.mac" ofType:@""]];
  363.     NSFileManager *nsfm = [NSFileManager defaultManager];
  364.     NSData *data = [nsfm contentsAtPath:fileName];
  365.     buf = (unsigned char *)[data bytes];
  366.  
  367. #if debug
  368.     NSLog(@"fileName = '%@'", fileName);
  369. #endif
  370.  
  371.     nbt = (buf[0] << 8) | buf [1];
  372. #if debug
  373.     NSLog(@"nbt = %d", nbt);
  374. #endif
  375.     pos = 14 + (4 * nbt);
  376.     for(i = 0; i < nbt; i++)
  377.       {
  378.         sl = buf[pos]; pos2 = pos+1;
  379.         for(j = 0; j < sl; j++) thestring[j] = buf[pos2++];
  380.         thestring[sl] = 0;
  381.         theAbbStr = [[NSString alloc] initWithCString:thestring];
  382.         pos += 6;
  383.         sl = buf[pos]; pos2 = pos+1;
  384.         for(j = 0; j < sl; j++) thestring[j] = buf[pos2++];
  385.         thestring[sl] = 0;
  386.         theTitleStr = [[NSString alloc] initWithCString:thestring];
  387.         pos += 50;
  388.         flag = (buf[pos] << 8) | buf [pos+1]; pos += 2;
  389.         theTitle = [[CTitle alloc] initWithAbb:theAbbStr withTitle:theTitleStr withFlag:flag];
  390.         [comicsBase addTitle:theTitle];
  391.         [theTitle release];
  392.         [theAbbStr release];
  393.         [theTitleStr release];
  394.         nbi = (buf[pos] << 8) | buf [pos+1]; pos += 2;
  395.         for(j = 0; j < nbi; j++)
  396.           {
  397.             ish = (buf[pos] << 8) | buf [pos+1]; pos += 2;
  398.             edit = (buf[pos] << 8) | buf [pos+1]; pos += 2;
  399.             buy = (buf[pos] << 8) | buf [pos+1]; pos += 2;
  400.             flag = (buf[pos] << 8) | buf [pos+1]; pos += 2;
  401.             theIssue = [[CIssue alloc] initWithIsh:ish withEdit:edit withBuy:buy withFlag:flag];
  402.             [theTitle addIssue:theIssue];
  403.             [theIssue release];
  404.           }
  405.       }
  406.     [fileName release];
  407. }
  408.  
  409. // if realLoad is 1, then get the data from the Yellow archived database using NSUnarchiver
  410. // if realLoad is 0, then get the data from the Mac database
  411. #define realLoad 0
  412. #define withConvert 1
  413. - (id)init
  414. {
  415.     if (self = [super init])
  416.       {
  417.         [self initGlobals];
  418.         [self reset];
  419.  
  420. #if realLoad
  421.         self = [NSUnarchiver unarchiveObjectWithFile:[self dbPath]];
  422.         [self retain];
  423.         comicsBase = self;
  424. #else
  425.         [self setTitles:[NSMutableArray array]];
  426.         comicsBase = self;
  427. #if withConvert
  428.         [self convertFromMacFormat];
  429. #endif
  430.         [self save:nil];
  431. #endif
  432.  
  433. #if debug
  434.         NSLog(@"startEditMonth = %d", startEditMonth);
  435.         NSLog(@"lastEditMonth = %d", lastEditMonth);
  436.         NSLog(@"startBuyMonth = %d", startBuyMonth);
  437.         NSLog(@"lastBuyMonth = %d", lastBuyMonth);
  438. #endif
  439.       }
  440.     return self;
  441. }
  442. - (void)dealloc
  443. {
  444.     [titles release];
  445.     [super dealloc];
  446. }
  447.  
  448. - (void)reset
  449. {
  450.     NSCalendarDate *date = [NSCalendarDate calendarDate];
  451.     nbIssues = 0;
  452.     maxIssue = -1;
  453.     startBuyMonth = [date monthOfYear] + 12 * ([date yearOfCommonEra] -1900);
  454.     lastBuyMonth = startBuyMonth - 6;
  455.     startEditMonth = startBuyMonth + 3;
  456.     lastEditMonth = lastBuyMonth;
  457. }
  458. - (void)setIssue:(CIssue *)theIssue
  459. {
  460.     if ([theIssue issueNumber] > maxIssue) maxIssue = [theIssue issueNumber];
  461.     if ([theIssue editMonth] < startEditMonth) startEditMonth = [theIssue editMonth];
  462.     if ([theIssue editMonth] > lastEditMonth) lastEditMonth = [theIssue editMonth];
  463.     if (!([theIssue issueFlags] & mskMiss))
  464.         if ([theIssue buyMonth] < startBuyMonth) startBuyMonth = [theIssue buyMonth];
  465.     if ([theIssue buyMonth] > lastBuyMonth) lastBuyMonth = [theIssue buyMonth];
  466. }
  467. - (void)setAll
  468. {
  469.     int i, j, n = [titles count], n2;
  470.     NSMutableArray* theseIssues;
  471.     [self reset];
  472.     for(i = 0; i < n; i++)
  473.       {
  474.         CTitle *thisTitle = [titles objectAtIndex:i];
  475.         nbIssues += [thisTitle nbIssues];
  476.         theseIssues = [thisTitle issues];
  477.         n2 = [theseIssues count];
  478.         for(j = 0; j < n2; j++)
  479.           {
  480.             CIssue *thisIssue = [theseIssues objectAtIndex:j];
  481.             [self setIssue:thisIssue];
  482.           }
  483.       }
  484. }
  485.  
  486. - (void)modNbIssues:(short)delta { nbIssues += delta; }
  487. - (short)nbIssues { return nbIssues; }
  488. - (short)maxIssue { return maxIssue; }
  489. - (short)startEditMonth { return startEditMonth; }
  490. - (short)lastEditMonth { return lastEditMonth; }
  491. - (short)startBuyMonth { return startBuyMonth; }
  492. - (short)lastBuyMonth { return lastBuyMonth; }
  493. - (short)nbTitles { return [titles count]; }
  494. - (NSMutableArray *)titles { return titles; }
  495. - (void)setTitles:(NSMutableArray *)theArray
  496. {
  497.     [theArray retain];
  498.     [titles release];
  499.     titles = theArray;
  500. }
  501.  
  502. - (void)save:(id)sender
  503. {
  504.     [NSArchiver archiveRootObject:self toFile:[self dbPath]];
  505. }
  506. - (id)initWithCoder:(NSCoder *)coder
  507. {
  508.     [self setTitles: [coder decodeObject]];
  509.     [self setAll];
  510.     return self;
  511. }
  512. - (void)encodeWithCoder:(NSCoder *)coder
  513. {
  514.     [coder encodeObject: [self titles]];
  515. }
  516.  
  517. // This method first clears the array which is passed in as a parameter,
  518. // then fills it only with the titles that conform to the criteria (also passed in as parameters),
  519. // and then sort those title using the comparison methods of CTitle
  520. - (void)sortArray:(NSMutableArray *)theArray withBrand:(short)brand withSeries:(short)series withKind:(short)kind withState:(short)state withSort:(short)sort
  521. {
  522.     int i, ok, n = [titles count];
  523.     CTitle* thisTitle;
  524.     short brands[2] = {mskMarvel, mskDC | mskOther};
  525.     short seriesa[2] = {mskLong, mskMini};
  526.     short states[2] = {mskDead, mskLive};
  527.     short  kinds[2] = {mskMain, mskDual};
  528.     
  529.     // clear
  530.     [theArray removeAllObjects];
  531.  
  532.     // fill
  533.     for(i = 0; i < n; i++)
  534.       {
  535.         thisTitle = [titles objectAtIndex:i];
  536.         ok = (brand == 0) || ([thisTitle titleFlags] & brands[brand-1]);
  537.         if (ok) ok = (series == 0) || ([thisTitle titleFlags] & seriesa[series-1]);
  538.         if (ok) ok = (state == 0) || ([thisTitle titleFlags] & states[state-1]);
  539.         if (ok) ok = (kind == 0) || ([thisTitle titleFlags] & kinds[kind-1]);
  540.         if (ok) [theArray addObject:thisTitle];
  541.       }
  542.  
  543.     // sort
  544.     switch(sort)
  545.       {
  546.         case 0: [theArray sortUsingSelector:@selector(compareAbb:)]; break;
  547.         case 1: [theArray sortUsingSelector:@selector(compareTitle:)]; break;
  548.         case 2: [theArray sortUsingSelector:@selector(compareChrono:)]; break;
  549.         case 3: [theArray sortUsingSelector:@selector(compareMaxIssue:)]; break;
  550.       }
  551. }
  552.  
  553. - (short)findTitleByAbb:(NSString *)theAbb
  554. {
  555.     int i, j = -1, found = 0, n = [titles count];
  556.     for(i = 0; (i < n) && (!found); i++)
  557.         if (found = [[[titles objectAtIndex:i] abb] isEqualToString:theAbb]) j = i;
  558.     return j;
  559. }
  560. - (short)findTitleByTitle:(NSString *)theTitle
  561. {
  562.     int i, j = -1, found = 0, n = [titles count];
  563.     for(i = 0; (i < n) && (!found); i++)
  564.         if (found = [[[titles objectAtIndex:i] title] isEqualToString:theTitle]) j = i;
  565.     return j;
  566. }
  567. - (short)addTitle:(CTitle *)theTitle
  568. {
  569.     if ([self findTitleByAbb:[theTitle abb]] != -1) return -1;
  570.     if ([self findTitleByTitle:[theTitle title]] != -1) return -2;
  571.     [titles addObject:theTitle];
  572.     return 0;
  573. }
  574. - (short)modTitle:(CTitle *)oldTitle withNewTitle:(CTitle *)newTitle
  575. {
  576.     short theIndex = [self findTitleByAbb:[oldTitle abb]];
  577.     if (theIndex == -1) return -1;
  578.     [newTitle setIssues:[oldTitle issues]];
  579.     [titles replaceObjectAtIndex:theIndex withObject:newTitle];
  580.     return 0;
  581. }
  582. - (short)deleteTitle:(CTitle *)theTitle
  583. {
  584.     short theIndex = [self findTitleByAbb:[theTitle abb]];
  585.     if (theIndex == -1) return -1;
  586.     [comicsBase modNbIssues:-[theTitle nbIssues]];
  587.     [titles removeObjectAtIndex:theIndex];
  588.     return 0;
  589. }
  590.  
  591. @end