home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Networking / SambaManager / NIProperty.m < prev    next >
Encoding:
Text File  |  1998-03-25  |  22.1 KB  |  913 lines

  1. /*
  2.     SambaManger. A graphical frontend to configure the NetInfo enhanced samba.
  3.     Copyright (C) 1998  Robert Frank
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 2 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.         
  19.         Robert Frank, frank@ifi.unibas.ch
  20. */
  21.  
  22. #import "NIProperty.h"
  23. #import "NIDirectory.h"
  24. #import <nikit/NIOpenPanel.h>
  25. #import <regex.h>
  26.  
  27. //****************************************************************************
  28. // The class variables
  29. static     id                                strings;                            // Localized strings for the panels
  30. static    const    ni_namelist    noValues = {0, NULL};    // The empty name list
  31.  
  32. //****************************************************************************
  33. // Macros
  34. #define HAVEVALUES(p)             (p >= 0) && \
  35.                                                         (p < properties->ni_proplist_len) && \
  36.                                                         properties->ni_proplist_val[p].nip_val.ni_namelist_len
  37. #define VALUEINDEXOK(p,v)        (v >= 0) && (v < properties->ni_proplist_val[p].nip_val.ni_namelist_len)
  38. #define PROPERTY(p)                    properties->ni_proplist_val[p]
  39. #define VALUELIST(p)                properties->ni_proplist_val[p].nip_val
  40. #define VALUELISTITEM(p,v)    properties->ni_proplist_val[p].nip_val.ni_namelist_val[v]
  41. #define VALUES(p)                        properties->ni_proplist_val[p].nip_val.ni_namelist_len
  42.  
  43. //****************************************************************************
  44. // The base kind 'NIProperty'
  45. @implementation NIProperty
  46. //*******************************
  47. // Dummy methods for the compiler
  48. - (int)willSelect:sender what:(int)kind path:(char **)ni_path title:(char **)panel_title state:(int *)anInt
  49. {
  50.         return 0;
  51. }
  52. - (int)didSelect:sender what:(int)kind value:(char **)selection domain:(const char *)niDomain state:(int)anInt
  53. {
  54.         return 0;
  55. }
  56. - (BOOL)propertyWillChange:sender value:(char **)string default:(BOOL *)remove index:(int)anInt
  57. {
  58.         return NO;
  59. }
  60. - propertyDidChange:sender value:(const char *)string
  61. {
  62.         return self;
  63. }
  64.  
  65. //**************
  66. // Local methods
  67.  
  68. - (const char *)openNetInfo:(const char *)baseDir withTitle:(const char *)openTitle domain:(const char **)from
  69. {
  70. NIOpenPanel    *openPanel;
  71.  
  72.         openPanel = [NIOpenPanel new];
  73.         [openPanel setDirectoryPath:baseDir];
  74.         [openPanel setPanelTitle:[strings valueForStringKey:"Title:Select in NetInfo Domain"]];
  75.         [openPanel setListTitle:openTitle];
  76.         if ([openPanel runModal] == NX_ALERTDEFAULT) {
  77.             *from = [openPanel domain];
  78.             return [openPanel directory];
  79.         }
  80.         return NULL;
  81. }
  82.  
  83. - (const char *)openFilePath:(BOOL)dirsOnly
  84. {
  85. const char    *docFileType[1] = {NULL};
  86. id                    openPanel;
  87.  
  88.         openPanel = [[OpenPanel new] allowMultipleFiles:NO];
  89.         [openPanel chooseDirectories:dirsOnly];
  90.         if (dirsOnly)
  91.             [openPanel setTitle:[strings valueForStringKey:"Title:Select Directory"]];
  92.         else
  93.             [openPanel setTitle:[strings valueForStringKey:"Title:Select File"]]; 
  94.     
  95.         switch([openPanel runModalForTypes:docFileType]) {
  96.             case NX_OKTAG:
  97.                 return [openPanel filename];
  98.             default: ;
  99.         }
  100.         return NULL;
  101. }
  102.  
  103. - (const char *)usePanel:sender
  104. // Display a panel for the selection of an existing value. Return any value selected or
  105. // NULL to signal abortion.
  106. {
  107. int    m = setMode, rcode, anInt;
  108. const char    *val = NULL, *p = path, *t = title, *where = NULL;
  109.  
  110.         do {
  111.             // Is this what we wanted to call?
  112.             if ([delegate respondsTo:@selector(willSelect:what:path:title:state:)])
  113.                 m = [delegate willSelect:self what:m path:&p title:&t state:&anInt];
  114.     
  115.             switch (m) {
  116.                 case NIPT_FILE:
  117.                     val = [self openFilePath:NO];
  118.                     break;
  119.                 case NIPT_DIR:
  120.                     val =  [self openFilePath:YES];
  121.                     break;
  122.                 case NIPT_NETINFO:
  123.                     val =  [self openNetInfo:p withTitle:t domain:&where];
  124.                     break;
  125.                 default: ;
  126.             }
  127.     
  128.             // The null string signals abortion.
  129.             if (!val)
  130.                 return val;
  131.             
  132.             // Copy to the temporary string buffer for possible modifications.
  133.             if (temp)
  134.                 NXZoneFree([NXApp zone], temp);
  135.             temp = NXCopyStringBufferFromZone(val ,[NXApp zone]);
  136.             
  137.             // Any modifications neccessary?
  138.             if ([delegate respondsTo:@selector(didSelect:what:value:domain:state:)]) {
  139.                 rcode = [delegate didSelect:self what:m value:&temp domain:where state:anInt];
  140.                 if (rcode == 0) // accept
  141.                     return temp;
  142.                 else if (rcode < 0)
  143.                     return NULL;
  144.             } else
  145.                 return temp;
  146.         } while (YES);
  147. }
  148.  
  149. //**************
  150. // Class methods
  151. + init:strgs
  152. {
  153.         strings = strgs;
  154.         return strings;
  155. }
  156.  
  157. //***************
  158. // Public methods
  159. - init:sender properties:(ni_proplist *)props name:(const char *)label
  160. {
  161.         [super init];
  162.         delegate = sender;
  163.         properties = props;
  164.         name = label;
  165.         temp = NULL;
  166.         tag = -1;
  167.  
  168.         return self;
  169. }
  170.  
  171. - updateProperty
  172. {
  173.         return self;
  174. }
  175.  
  176. - updateProperty:(const char *)value default:(BOOL)remove
  177. // Update the first (and possibly only) value of this property.
  178. // If there was no previous value (or no property at all) and remove is true,
  179. // or if the old and new values are identical, no changes are made.
  180. // Before accepting the new value, this method sends a propertyWillChange to
  181. // its delegate. If this returns YES, the value is discarded an no changes are
  182. // made. The delegate has the option to modify the value in the propertyWillChange.
  183. // If  there is no delegate or the delegate does not respond to propertyWillChange,
  184. // the value will be accepted. If there was a previous value and remove is true,
  185. // the first value will be removed. If the list becomes empty, the entire property
  186. // will be removed. If there was a previous value and remove is false, the first
  187. // value will be updated. If there was no previous value or property, a new
  188. // property and value will be entered. After this, the delegate will be notified
  189. // by a call to propertyDidChange.
  190. {
  191. ni_property prop;
  192. char                *copyOfVal = (char *)value;
  193. int                    index = [self index];
  194.  
  195.         // No changes?
  196.         if (((index == NI_INDEX_NULL) && remove) || (HAVEVALUES(index) && value && !strcmp(VALUELISTITEM(index, 0), value)))
  197.             return self;
  198.         
  199.         // Is the new value ok?
  200.     if ([delegate respondsTo:@selector(propertyWillChange:value:default:index:)]) {
  201.             copyOfVal = value?NXCopyStringBufferFromZone(value, [self zone]):NULL;
  202.             if ([delegate propertyWillChange:self value:©OfVal default:&remove index:0]) {
  203.                 if (copyOfVal && (copyOfVal != value))
  204.                     NXZoneFree([NXApp zone], copyOfVal);
  205.                 return nil;
  206.             }
  207.         }
  208.  
  209.         if (index != NI_INDEX_NULL) {    // remove or update
  210.             if (remove) {    // remove
  211.                 if (HAVEVALUES(index) > 1)
  212.                     ni_name_free(&(VALUELISTITEM(index,0)));
  213.                 else
  214.                     ni_proplist_delete(properties, index);
  215.             } else { // update
  216.                 if (HAVEVALUES(index) > 1) {
  217.                     ni_name_free(&(VALUELISTITEM(index,0)));
  218.                     VALUELISTITEM(index,0) = ni_name_dup(copyOfVal);
  219.                 } else {
  220.                     ni_namelist_free(&(VALUELIST(index)));
  221.                 ni_namelist_insert(&(VALUELIST(index)), copyOfVal, NI_INDEX_NULL);
  222.                 }
  223.             }
  224.         } else if (!remove) {  // create
  225.             NI_INIT(&prop);
  226.         prop.nip_name = (char *)name;
  227.         ni_namelist_insert(&prop.nip_val, copyOfVal, NI_INDEX_NULL);
  228.         ni_proplist_insert(properties, prop, NI_INDEX_NULL);
  229.             ni_namelist_free(&prop.nip_val);
  230.             index = ni_proplist_match(*properties, name, NULL);
  231.         }
  232.         
  233.         // Tell the delegate that the property was updated.
  234.     if ([delegate respondsTo:@selector(propertyDidChange:value:)])
  235.             [delegate propertyDidChange:self value:copyOfVal];
  236.  
  237.         // Redisplay if the value was changed!
  238.         if (copyOfVal &&  strcmp(value, copyOfVal))
  239.             [self display];
  240.  
  241.         if (copyOfVal && (copyOfVal != value))
  242.             NXZoneFree([NXApp zone], copyOfVal);
  243.         return self;
  244. }
  245.  
  246. - (int)index
  247. {
  248.         return ni_proplist_match(*properties, name, NULL);
  249. }
  250.  
  251. // Transfere the value from the property to the GUI
  252. - display
  253. {
  254.         return self;
  255. }
  256.  
  257. // Return the list of values or the empty list
  258. - (const ni_namelist *)valueList
  259. {
  260. int                    index = [self index];
  261.  
  262.         if (index != NI_INDEX_NULL)
  263.             return &(VALUELIST(index));
  264.         else
  265.             return &noValues;
  266. }
  267.  
  268. // Return the number of values
  269. - (int)values
  270. {
  271. int    index = [self index];
  272.  
  273.         if (HAVEVALUES(index))
  274.             return VALUES(index);
  275.         else
  276.             return 0;
  277. }
  278.  
  279. // Return the value at index idx
  280. - (const char *)valueAt:(int)idx
  281. {
  282. int    index = [self index];
  283.  
  284.     if (HAVEVALUES(index) && VALUEINDEXOK(index, idx))
  285.         return VALUELISTITEM(index, idx);
  286.     else
  287.         return "";
  288. }
  289.  
  290. // Insert a value at the end of the list, creating the property if necessary.
  291. - insertValue:(const char *)value
  292. {
  293.         return [self insertValue:value at:NI_INDEX_NULL];
  294. }
  295.  
  296. // Insert a value at position idx, creating the property if necessary.
  297. - insertValue:(const char *)value at:(int)idx
  298. {
  299. int                    index = [self index];
  300. ni_property prop;
  301.  
  302.         if (HAVEVALUES(index))
  303.             ni_namelist_insert(&(VALUELIST(index)), value , idx);
  304.         else {
  305.             NI_INIT(&prop);
  306.         prop.nip_name = (char *)name; // Not freed by ni_namelist_free!
  307.         ni_namelist_insert(&prop.nip_val, value, NI_INDEX_NULL);
  308.         ni_proplist_insert(properties, prop, NI_INDEX_NULL);
  309.             ni_namelist_free(&prop.nip_val);
  310.         }
  311.         // Tell the delegate that the property was updated.
  312.     if ([delegate respondsTo:@selector(propertyDidChange:value:)])
  313.             [delegate propertyDidChange:self value:value];
  314.         return self;
  315. }
  316.  
  317. // Delete the value at index idx
  318. - deleteValue:(int)idx
  319. {
  320. int    index = [self index];
  321.  
  322.         if (HAVEVALUES(index) && VALUEINDEXOK(index, idx)) {
  323.             if (VALUES(index) > 1)
  324.                 ni_namelist_delete(&(VALUELIST(index)), idx);
  325.             else
  326.                 ni_proplist_delete(properties, index);
  327.         }
  328.         // Tell the delegate that the property was updated.
  329.     if ([delegate respondsTo:@selector(propertyDidChange:value:)])
  330.             [delegate propertyDidChange:self value:NULL];
  331.  
  332.         return self;
  333. }
  334.  
  335. // Change an existing value, create the property and insert the value, if necessary
  336. // or, if value is NULL, delete the value
  337. - updateValue:(const char *)value at:(int)idx
  338. {
  339.         [self deleteValue:idx];
  340.         if (value)
  341.             [self insertValue:value at:idx];
  342.  
  343.         return self;
  344. }
  345.  
  346. // Locate the index of the value with the given value
  347. - (int)findValue:(const char *)value how:(BOOL)mode
  348. {
  349. int    i, index = [self index];
  350.  
  351.         if (HAVEVALUES(index)) {
  352.             if (mode) // NI_FIND_EXACT
  353.                 return ni_namelist_match(VALUELIST(index), value);
  354.             else {
  355.                 for (i = 0; i < VALUES(index); i++)
  356.                     if (!recmp(value, VALUELISTITEM(index, i)))
  357.                         return i;
  358.             }
  359.         }
  360.         return NI_INDEX_NULL;
  361. }
  362.  
  363. // Remove all values
  364. - removeValues
  365. {
  366. int    index = [self index];
  367.  
  368.         if (HAVEVALUES(index))
  369.             ni_proplist_delete(properties, index);
  370.         // Tell the delegate that the property was updated.
  371.     if ([delegate respondsTo:@selector(propertyDidChange:value:)])
  372.             [delegate propertyDidChange:self value:NULL];
  373.  
  374.         return self;
  375. }
  376.  
  377. - free
  378. {
  379.         if (temp)
  380.             NXZoneFree([self zone], temp);
  381.         return [super free];
  382. }
  383.  
  384. - (const char *)name
  385. {
  386.         return name;
  387. }
  388.  
  389. - delegate
  390. {
  391.         return delegate;
  392. }
  393.  
  394. - setDelegate:anObject
  395. {
  396.         delegate = anObject;
  397.         return self;
  398. }
  399.  
  400. - (int)tag
  401. {
  402.         return tag;
  403. }
  404.  
  405. - setTag:(int)anInt
  406. {
  407.         tag = anInt;
  408.         return self;
  409. }
  410.  
  411.  
  412. //**********************
  413. // Delegates and Actions
  414.  
  415. - hasChanged:sender
  416. {
  417.         return self;
  418. }
  419. @end
  420.  
  421. //****************************************************************************
  422. // Booleans
  423. @implementation NIBoolProperty
  424. - init:sender properties:(ni_proplist *)props name:(const char *)label outlet:o
  425. {
  426.         [super init:sender properties:props name:label];
  427.         outlet = o;
  428.         [outlet setTarget:self];
  429.         [outlet setAction:@selector(hasChanged:)];
  430.         [outlet selectCellAt:0 :0];
  431.         yes = NXCopyStringBufferFromZone("yes", [self zone]);
  432.         no = NXCopyStringBufferFromZone("no", [self zone]);
  433.         return self;
  434. }
  435.  
  436. - setValues:(const char *)true :(const char *)false
  437. {
  438.         yes = NXZoneRealloc([self zone], yes, strlen(yes)+1);
  439.         no = NXZoneRealloc([self zone], no, strlen(no)+1);
  440.         (void)strcpy(yes, true);
  441.         (void)strcpy(no, false);
  442.         return self;
  443. }
  444.  
  445. - hasChanged:sender
  446. {
  447. int                    col = [outlet selectedCol];
  448. const char    *v;
  449.  
  450.         v = (col < 1)?NULL:((col == 1)?yes:no);
  451.         [self updateProperty:v default:(col < 1)];
  452.         return self;
  453. }
  454.  
  455. - display
  456. {
  457. int                    index = [self index];
  458.  
  459.         if (HAVEVALUES(index))
  460.             if (!strcasecmp(VALUELISTITEM(index,0), "yes") ||
  461.                     !strcasecmp(VALUELISTITEM(index,0), "true") ||
  462.                     !strcasecmp(VALUELISTITEM(index,0), yes) ||
  463.                     VALUELISTITEM(index,0)[0] == '1')
  464.                 [outlet selectCellAt:0 :1];
  465.             else
  466.                 [outlet selectCellAt:0 :2];
  467.         else
  468.             [outlet selectCellAt:0 :0];
  469.  
  470.         return self;
  471. }
  472. @end
  473.  
  474. //****************************************************************************
  475. // Characters
  476. @implementation NICharProperty
  477. - init:sender properties:(ni_proplist *)props name:(const char *)label outlet:o
  478. {
  479.         [super init:sender properties:(ni_proplist *)props name:label];
  480.         outlet = o;
  481.         needUpdate = NO;
  482.         [outlet setTextDelegate:self];
  483.         [outlet setStringValue:""];
  484.         return self;
  485. }
  486.  
  487. - updateProperty
  488. {
  489. const char    *v;
  490. id                    obj = self;
  491.  
  492.         if (needUpdate) {
  493.             v = [outlet stringValue];
  494.             obj = [self updateProperty:v default:(!v || !*v)];
  495.             needUpdate = obj == nil;
  496.         }
  497.         return obj;
  498. }
  499.  
  500. - display
  501. {
  502. int    index = [self index];
  503.  
  504.         if (HAVEVALUES(index))
  505.             [outlet setStringValue:(const char *)VALUELISTITEM(index,0)];
  506.         else
  507.             [outlet setStringValue:""];
  508.  
  509.         return self;
  510. }
  511.  
  512. //**************
  513. // Text delegates
  514.  
  515. - textDidEnd:textObject endChar:(unsigned short)whyEnd
  516. {
  517.         return [self updateProperty];
  518. }
  519.  
  520. - textDidChange:sender
  521. {
  522.         needUpdate = YES;
  523.         // Tell the delegate that the property was updated.
  524.     if ([delegate respondsTo:@selector(propertyDidChange:value:)])
  525.             [delegate propertyDidChange:self value:NULL];
  526.         return self;
  527. }
  528. @end
  529.  
  530. //****************************************************************************
  531. // Integers
  532. @implementation NIIntProperty
  533. //**************
  534. // Slider action
  535. - setVal:sender
  536. {
  537. int        v = [slider intValue];
  538. char    buf[32];
  539.  
  540.         if (v != intVal) {
  541.             sprintf(buf, "%d", v);
  542.             if (![self updateProperty:buf default:(v < 0)])
  543.                 return nil;
  544.             intVal = v;
  545.             if (intVal < 0)
  546.                 [outlet setStringValue:defaultText];
  547.             else if ((intVal == 0) && zero)
  548.                 [outlet setStringValue:zero];
  549.             else
  550.                 [outlet setIntValue:intVal];
  551.         }
  552.         return self;
  553. }
  554.  
  555. //*************
  556. // main methods
  557. - init:sender properties:(ni_proplist *)props name:(const char *)label
  558.         text:t slider:s default:(const char *)d zero:(const char *)z
  559. {
  560.         [super init:sender properties:(ni_proplist *)props name:label outlet:t];
  561.         slider = s;
  562.         defaultText = d;
  563.         zero = z;
  564.         intVal = -1;
  565.         
  566.         [outlet setStringValue:d];
  567.  
  568.         if (slider) {
  569.             [slider setMinValue:-1.0];
  570.             [slider setTarget:self];
  571.             [slider setAction:@selector(setVal:)];
  572.             [slider setIntValue:-1];
  573.         }
  574.         
  575.          return self;
  576. }
  577.  
  578. - display
  579. {
  580. int    index = [self index];
  581.  
  582.         if (HAVEVALUES(index)) {
  583.             intVal = atoi(VALUELISTITEM(index,0));
  584.             if (intVal >= 0) {
  585.                 if (slider)
  586.                     [slider setIntValue:intVal];
  587.                 if ((intVal == 0) && zero)
  588.                     [outlet setStringValue:zero];
  589.                 else
  590.                     [outlet setIntValue:intVal];
  591.                 return self;
  592.             }
  593.         }
  594.         
  595.         if (slider)
  596.             [slider setIntValue:-1];
  597.         [outlet setStringValue:defaultText];
  598.  
  599.         return self;
  600. }
  601.  
  602. - updateProperty
  603. {
  604. char                buf[32];
  605. const char    *s = [outlet stringValue];
  606. int                    v = atoi(s);
  607.  
  608.         if (s && !strcmp(s, defaultText))
  609.             v = -1;
  610.  
  611.         if (v != intVal) {
  612.             sprintf(buf, "%31d", v);
  613.             if (![self updateProperty:buf default:(v < 0)])
  614.                 return nil;
  615.             intVal = v;
  616.             if (intVal < 0)
  617.                 [outlet setStringValue:defaultText];
  618.             else if ((intVal == 0) && zero)
  619.                 [outlet setStringValue:zero];
  620.             if (slider)
  621.                 [slider setIntValue:intVal];
  622.             needUpdate = NO;
  623.         }
  624.         
  625.         return self;
  626. }
  627.  
  628. //**************
  629. // Text delegate
  630.  
  631. - textDidEnd:textObject endChar:(unsigned short)whyEnd
  632. {
  633.         return [self updateProperty];
  634. }
  635. @end
  636.  
  637. //****************************************************************************
  638. // Strings
  639. @implementation NIStringProperty
  640. - setString:sender
  641. {
  642. const char    *val;
  643.  
  644.         if (!(val = [self usePanel:sender]))
  645.             return self;
  646.         
  647.         if ([self updateProperty:val default:(!val || !*val)])
  648.             [outlet setStringValue:val];
  649.  
  650.         return self;
  651. }
  652.  
  653. - init:sender properties:(ni_proplist *)props name:(const char *)label
  654.         text:t button:b mode:(int)m path:(const char *)p title:(const char *)tString
  655. {
  656.         [super init:sender properties:(ni_proplist *)props name:label outlet:t];
  657.         setMode = m;
  658.         path = p;
  659.         title = tString;
  660.         button = b;
  661.         if (button) {
  662.             [button setTarget:self];
  663.             [button setAction:@selector(setString:)];
  664.         }
  665.  
  666.         return self;
  667. }
  668. @end
  669.  
  670. //****************************************************************************
  671. // Browsers
  672. // This will set the delegate and delegate of the browser to this object.
  673. // If there is a text field, then its textDelegate will also be set to this object.
  674. // The values are copied to a separate structure for resetting.
  675. @implementation NIBrowserProperty
  676.  
  677. //******************************
  678. // Browser delegates and actions
  679.  
  680. - hasChanged:sender
  681. {
  682.         if (textField) {
  683.           [textField setStringValue:[[sender selectedCell] stringValue]];
  684.           [textField selectText:self];
  685.         }
  686.         return self;
  687. }
  688.  
  689. - (int)browser:sender fillMatrix:matrix inColumn:(int)column
  690. {
  691. int        i = 0,
  692.             index = [self index];
  693. id        cell;
  694.         
  695.         if (HAVEVALUES(index))
  696.             for (i = 0; i < VALUES(index); i++) {
  697.                 [matrix addRow];
  698.                 cell = [matrix cellAt:i :0];
  699.                 [cell setStringValue:VALUELISTITEM(index,i)];
  700.                 [cell setLoaded:YES];
  701.                 [cell setLeaf:YES];
  702.             }
  703.         
  704.         return i;
  705. }
  706.  
  707. // The action taken when pressing the add button
  708. - add:sender
  709. {
  710. int                    i = NI_INDEX_NULL,
  711.                         index = [self index];
  712. const char    *v = NULL;
  713. char                *copyOfVal = (char *)v;
  714. BOOL                remove = NO;
  715.  
  716.         if (textField)
  717.             v = [textField stringValue];
  718.         else 
  719.             v = [self usePanel:sender];
  720.         if (!v || !*v)
  721.             return self;
  722.         
  723.         // Is the new value ok?
  724.     if ([delegate respondsTo:@selector(propertyWillChange:value:default:index:)]) {
  725.             copyOfVal = NXCopyStringBufferFromZone(v, [self zone]);
  726.             if ([delegate propertyWillChange:self value:©OfVal default:&remove index:i]) {
  727.                 if (copyOfVal && (copyOfVal != v))
  728.                     NXZoneFree([NXApp zone], copyOfVal);
  729.                 return self;
  730.             }
  731.         }
  732.  
  733.         // No changes?
  734.         if (HAVEVALUES(index)) {
  735.             for (i = 0; i < VALUES(index); i++)
  736.                 if (!strcasecmp(VALUELISTITEM(index,i), v)) {
  737.                     if (copyOfVal && (copyOfVal != v))
  738.                         NXZoneFree([NXApp zone], copyOfVal);
  739.                     [[outlet matrixInColumn:0] selectCellAt:i :0];
  740.                     return self;
  741.                 } else if (strcasecmp(VALUELISTITEM(index,i), v) > 0)
  742.                     break; 
  743.  
  744.             ni_namelist_insert(&(VALUELIST(index)), copyOfVal , i);
  745.             
  746.         } else
  747.           if (![self updateProperty:copyOfVal default:(!copyOfVal || !*copyOfVal)]) {
  748.                 if (copyOfVal && (copyOfVal != v))
  749.                     NXZoneFree([NXApp zone], copyOfVal);
  750.                 return self;
  751.         }
  752.     
  753.         [outlet reloadColumn:0];
  754.         [[outlet matrixInColumn:0] selectCellAt:i :0];
  755.         [textField selectText:self];
  756.  
  757.         // Tell the delegate that the property was updated.
  758.         if ([delegate respondsTo:@selector(propertyDidChange:)])
  759.             [delegate propertyDidChange:self value:copyOfVal];
  760.  
  761.         if (copyOfVal && (copyOfVal != v))
  762.             NXZoneFree([NXApp zone], copyOfVal);
  763.  
  764.         return self;
  765. }
  766.  
  767. // The action taken when pressing the delete button
  768. - delete:sender
  769. {
  770. BOOL    remove = YES;
  771. char    *p = NULL;
  772. int        i,
  773.             index = [self index];
  774. id        selCell = [outlet selectedCell];
  775.  
  776.         if (selCell && HAVEVALUES(index)) {
  777.             i = ni_namelist_match(VALUELIST(index), [selCell stringValue]);
  778.             if (i != NI_INDEX_NULL) {
  779.                 if ([delegate respondsTo:@selector(propertyWillChange:value:default:index:)])
  780.                     if ([delegate propertyWillChange:self value:&p default:&remove index:i])
  781.                         return nil;
  782.                 if ([self values] > 1) {
  783.                     ni_namelist_delete(&(VALUELIST(index)), i);
  784.                     if (--i > 0)
  785.                         [[outlet matrixInColumn:0] selectCellAt:i :0];
  786.                     else if (HAVEVALUES(index))
  787.                         [[outlet matrixInColumn:0] selectCellAt:0 :0];
  788.                 } else
  789.                     ni_proplist_delete(properties, index);
  790.                 [outlet reloadColumn:0];
  791.                 if ([delegate respondsTo:@selector(propertyDidChange:value:)])
  792.                     [delegate propertyDidChange:self value:p];
  793.             }
  794.         }
  795.         return self;
  796. }
  797.  
  798. //***************
  799. // Text delegates
  800.  
  801. // Only update the browser if return was pressed.
  802. - textDidEnd:textObject endChar:(unsigned short)whyEnd
  803. {
  804.         if (whyEnd == NX_RETURN)
  805.             [self add:textObject];
  806.  
  807.         return self;
  808. }
  809.  
  810. //************
  811. // The methods
  812.  
  813. - init:sender properties:(ni_proplist *)props name:(const char *)label
  814.         text:t browser:b mode:(int)m path:(const char *)p
  815.         add:a remove:r title:(const char *)tString;
  816. {
  817.         [super init:sender properties:(ni_proplist *)props name:label];
  818.         outlet = b;
  819.         [outlet setTarget:self];
  820.         [outlet setAction:@selector(hasChanged:)];
  821.         textField = t;
  822.         addButton = a;
  823.         deleteButton = r;
  824.         setMode = m;
  825.         path = p;
  826.         title = tString;
  827.         
  828.         [b setDelegate:self];
  829.         if (textField)
  830.             [textField setTextDelegate:self];
  831.         if (addButton) {
  832.             [addButton setTarget:self];
  833.             [addButton setAction:@selector(add:)];
  834.         }
  835.         if (deleteButton) {
  836.             [deleteButton setTarget:self];
  837.             [deleteButton setAction:@selector(delete:)];
  838.         }
  839.         
  840.         return self;
  841. }
  842.  
  843. // Set/reset the values
  844. - display
  845. {
  846.         [outlet loadColumnZero];
  847.         [[outlet matrixInColumn:0] selectCellAt:0 :0];        
  848.         return self;
  849. }
  850. @end
  851.  
  852. //****************************************************************************
  853. // Popups
  854. @implementation NIPopupProperty
  855. - hasChanged:sender
  856. {
  857. const char    *s = [[outlet target] selectedItem] ;
  858. int        d = strcmp(defaultText, s);
  859.  
  860.         return [self updateProperty:s default:!d];
  861. }
  862.  
  863. - init:sender properties:(ni_proplist *)props name:(const char *)label
  864.         outlet:o default:(const char *)d;
  865. {
  866.         [super init:sender properties:props name:label];
  867.         outlet = o;
  868.         [[outlet target] setTarget:self];
  869.         [[outlet target] setAction:@selector(hasChanged:)];
  870.         defaultText = d;
  871.         return self;
  872. }
  873.  
  874. - display
  875. {
  876. int    index = [self index];
  877.  
  878.         if (HAVEVALUES(index))
  879.             [outlet setTitle:(const char *)VALUELISTITEM(index,0)];
  880.         else
  881.             [outlet setTitle:defaultText];
  882.         return self;
  883. }
  884. @end
  885.  
  886. //****************************************************************************
  887. // Calls
  888. @implementation NICallProperty
  889. - nothing:sender
  890. {
  891.         return self;
  892. }
  893.  
  894. - init:sender properties:(ni_proplist *)props name:(const char *)label displayAction:(SEL)action;
  895. {
  896.         [super init:sender properties:(ni_proplist *)props name:label];
  897.         if (action)
  898.             display = action;
  899.         else {
  900.             display = @selector(nothing:);
  901.             delegate = self;
  902.         }
  903.         return self;
  904. }
  905.  
  906. - display
  907. {
  908.         return [delegate perform:display with:self];
  909. }
  910.  
  911. @end
  912.  
  913.