home *** CD-ROM | disk | FTP | other *** search
- /*
- SambaManger. A graphical frontend to configure the NetInfo enhanced samba.
- Copyright (C) 1998 Robert Frank
-
- This program 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 program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Robert Frank, frank@ifi.unibas.ch
- */
-
- #import "NIProperty.h"
- #import "NIDirectory.h"
- #import <nikit/NIOpenPanel.h>
- #import <regex.h>
-
- //****************************************************************************
- // The class variables
- static id strings; // Localized strings for the panels
- static const ni_namelist noValues = {0, NULL}; // The empty name list
-
- //****************************************************************************
- // Macros
- #define HAVEVALUES(p) (p >= 0) && \
- (p < properties->ni_proplist_len) && \
- properties->ni_proplist_val[p].nip_val.ni_namelist_len
- #define VALUEINDEXOK(p,v) (v >= 0) && (v < properties->ni_proplist_val[p].nip_val.ni_namelist_len)
- #define PROPERTY(p) properties->ni_proplist_val[p]
- #define VALUELIST(p) properties->ni_proplist_val[p].nip_val
- #define VALUELISTITEM(p,v) properties->ni_proplist_val[p].nip_val.ni_namelist_val[v]
- #define VALUES(p) properties->ni_proplist_val[p].nip_val.ni_namelist_len
-
- //****************************************************************************
- // The base kind 'NIProperty'
- @implementation NIProperty
- //*******************************
- // Dummy methods for the compiler
- - (int)willSelect:sender what:(int)kind path:(char **)ni_path title:(char **)panel_title state:(int *)anInt
- {
- return 0;
- }
- - (int)didSelect:sender what:(int)kind value:(char **)selection domain:(const char *)niDomain state:(int)anInt
- {
- return 0;
- }
- - (BOOL)propertyWillChange:sender value:(char **)string default:(BOOL *)remove index:(int)anInt
- {
- return NO;
- }
- - propertyDidChange:sender value:(const char *)string
- {
- return self;
- }
-
- //**************
- // Local methods
-
- - (const char *)openNetInfo:(const char *)baseDir withTitle:(const char *)openTitle domain:(const char **)from
- {
- NIOpenPanel *openPanel;
-
- openPanel = [NIOpenPanel new];
- [openPanel setDirectoryPath:baseDir];
- [openPanel setPanelTitle:[strings valueForStringKey:"Title:Select in NetInfo Domain"]];
- [openPanel setListTitle:openTitle];
- if ([openPanel runModal] == NX_ALERTDEFAULT) {
- *from = [openPanel domain];
- return [openPanel directory];
- }
- return NULL;
- }
-
- - (const char *)openFilePath:(BOOL)dirsOnly
- {
- const char *docFileType[1] = {NULL};
- id openPanel;
-
- openPanel = [[OpenPanel new] allowMultipleFiles:NO];
- [openPanel chooseDirectories:dirsOnly];
- if (dirsOnly)
- [openPanel setTitle:[strings valueForStringKey:"Title:Select Directory"]];
- else
- [openPanel setTitle:[strings valueForStringKey:"Title:Select File"]];
-
- switch([openPanel runModalForTypes:docFileType]) {
- case NX_OKTAG:
- return [openPanel filename];
- default: ;
- }
- return NULL;
- }
-
- - (const char *)usePanel:sender
- // Display a panel for the selection of an existing value. Return any value selected or
- // NULL to signal abortion.
- {
- int m = setMode, rcode, anInt;
- const char *val = NULL, *p = path, *t = title, *where = NULL;
-
- do {
- // Is this what we wanted to call?
- if ([delegate respondsTo:@selector(willSelect:what:path:title:state:)])
- m = [delegate willSelect:self what:m path:&p title:&t state:&anInt];
-
- switch (m) {
- case NIPT_FILE:
- val = [self openFilePath:NO];
- break;
- case NIPT_DIR:
- val = [self openFilePath:YES];
- break;
- case NIPT_NETINFO:
- val = [self openNetInfo:p withTitle:t domain:&where];
- break;
- default: ;
- }
-
- // The null string signals abortion.
- if (!val)
- return val;
-
- // Copy to the temporary string buffer for possible modifications.
- if (temp)
- NXZoneFree([NXApp zone], temp);
- temp = NXCopyStringBufferFromZone(val ,[NXApp zone]);
-
- // Any modifications neccessary?
- if ([delegate respondsTo:@selector(didSelect:what:value:domain:state:)]) {
- rcode = [delegate didSelect:self what:m value:&temp domain:where state:anInt];
- if (rcode == 0) // accept
- return temp;
- else if (rcode < 0)
- return NULL;
- } else
- return temp;
- } while (YES);
- }
-
- //**************
- // Class methods
- + init:strgs
- {
- strings = strgs;
- return strings;
- }
-
- //***************
- // Public methods
- - init:sender properties:(ni_proplist *)props name:(const char *)label
- {
- [super init];
- delegate = sender;
- properties = props;
- name = label;
- temp = NULL;
- tag = -1;
-
- return self;
- }
-
- - updateProperty
- {
- return self;
- }
-
- - updateProperty:(const char *)value default:(BOOL)remove
- // Update the first (and possibly only) value of this property.
- // If there was no previous value (or no property at all) and remove is true,
- // or if the old and new values are identical, no changes are made.
- // Before accepting the new value, this method sends a propertyWillChange to
- // its delegate. If this returns YES, the value is discarded an no changes are
- // made. The delegate has the option to modify the value in the propertyWillChange.
- // If there is no delegate or the delegate does not respond to propertyWillChange,
- // the value will be accepted. If there was a previous value and remove is true,
- // the first value will be removed. If the list becomes empty, the entire property
- // will be removed. If there was a previous value and remove is false, the first
- // value will be updated. If there was no previous value or property, a new
- // property and value will be entered. After this, the delegate will be notified
- // by a call to propertyDidChange.
- {
- ni_property prop;
- char *copyOfVal = (char *)value;
- int index = [self index];
-
- // No changes?
- if (((index == NI_INDEX_NULL) && remove) || (HAVEVALUES(index) && value && !strcmp(VALUELISTITEM(index, 0), value)))
- return self;
-
- // Is the new value ok?
- if ([delegate respondsTo:@selector(propertyWillChange:value:default:index:)]) {
- copyOfVal = value?NXCopyStringBufferFromZone(value, [self zone]):NULL;
- if ([delegate propertyWillChange:self value:©OfVal default:&remove index:0]) {
- if (copyOfVal && (copyOfVal != value))
- NXZoneFree([NXApp zone], copyOfVal);
- return nil;
- }
- }
-
- if (index != NI_INDEX_NULL) { // remove or update
- if (remove) { // remove
- if (HAVEVALUES(index) > 1)
- ni_name_free(&(VALUELISTITEM(index,0)));
- else
- ni_proplist_delete(properties, index);
- } else { // update
- if (HAVEVALUES(index) > 1) {
- ni_name_free(&(VALUELISTITEM(index,0)));
- VALUELISTITEM(index,0) = ni_name_dup(copyOfVal);
- } else {
- ni_namelist_free(&(VALUELIST(index)));
- ni_namelist_insert(&(VALUELIST(index)), copyOfVal, NI_INDEX_NULL);
- }
- }
- } else if (!remove) { // create
- NI_INIT(&prop);
- prop.nip_name = (char *)name;
- ni_namelist_insert(&prop.nip_val, copyOfVal, NI_INDEX_NULL);
- ni_proplist_insert(properties, prop, NI_INDEX_NULL);
- ni_namelist_free(&prop.nip_val);
- index = ni_proplist_match(*properties, name, NULL);
- }
-
- // Tell the delegate that the property was updated.
- if ([delegate respondsTo:@selector(propertyDidChange:value:)])
- [delegate propertyDidChange:self value:copyOfVal];
-
- // Redisplay if the value was changed!
- if (copyOfVal && strcmp(value, copyOfVal))
- [self display];
-
- if (copyOfVal && (copyOfVal != value))
- NXZoneFree([NXApp zone], copyOfVal);
- return self;
- }
-
- - (int)index
- {
- return ni_proplist_match(*properties, name, NULL);
- }
-
- // Transfere the value from the property to the GUI
- - display
- {
- return self;
- }
-
- // Return the list of values or the empty list
- - (const ni_namelist *)valueList
- {
- int index = [self index];
-
- if (index != NI_INDEX_NULL)
- return &(VALUELIST(index));
- else
- return &noValues;
- }
-
- // Return the number of values
- - (int)values
- {
- int index = [self index];
-
- if (HAVEVALUES(index))
- return VALUES(index);
- else
- return 0;
- }
-
- // Return the value at index idx
- - (const char *)valueAt:(int)idx
- {
- int index = [self index];
-
- if (HAVEVALUES(index) && VALUEINDEXOK(index, idx))
- return VALUELISTITEM(index, idx);
- else
- return "";
- }
-
- // Insert a value at the end of the list, creating the property if necessary.
- - insertValue:(const char *)value
- {
- return [self insertValue:value at:NI_INDEX_NULL];
- }
-
- // Insert a value at position idx, creating the property if necessary.
- - insertValue:(const char *)value at:(int)idx
- {
- int index = [self index];
- ni_property prop;
-
- if (HAVEVALUES(index))
- ni_namelist_insert(&(VALUELIST(index)), value , idx);
- else {
- NI_INIT(&prop);
- prop.nip_name = (char *)name; // Not freed by ni_namelist_free!
- ni_namelist_insert(&prop.nip_val, value, NI_INDEX_NULL);
- ni_proplist_insert(properties, prop, NI_INDEX_NULL);
- ni_namelist_free(&prop.nip_val);
- }
- // Tell the delegate that the property was updated.
- if ([delegate respondsTo:@selector(propertyDidChange:value:)])
- [delegate propertyDidChange:self value:value];
- return self;
- }
-
- // Delete the value at index idx
- - deleteValue:(int)idx
- {
- int index = [self index];
-
- if (HAVEVALUES(index) && VALUEINDEXOK(index, idx)) {
- if (VALUES(index) > 1)
- ni_namelist_delete(&(VALUELIST(index)), idx);
- else
- ni_proplist_delete(properties, index);
- }
- // Tell the delegate that the property was updated.
- if ([delegate respondsTo:@selector(propertyDidChange:value:)])
- [delegate propertyDidChange:self value:NULL];
-
- return self;
- }
-
- // Change an existing value, create the property and insert the value, if necessary
- // or, if value is NULL, delete the value
- - updateValue:(const char *)value at:(int)idx
- {
- [self deleteValue:idx];
- if (value)
- [self insertValue:value at:idx];
-
- return self;
- }
-
- // Locate the index of the value with the given value
- - (int)findValue:(const char *)value how:(BOOL)mode
- {
- int i, index = [self index];
-
- if (HAVEVALUES(index)) {
- if (mode) // NI_FIND_EXACT
- return ni_namelist_match(VALUELIST(index), value);
- else {
- for (i = 0; i < VALUES(index); i++)
- if (!recmp(value, VALUELISTITEM(index, i)))
- return i;
- }
- }
- return NI_INDEX_NULL;
- }
-
- // Remove all values
- - removeValues
- {
- int index = [self index];
-
- if (HAVEVALUES(index))
- ni_proplist_delete(properties, index);
- // Tell the delegate that the property was updated.
- if ([delegate respondsTo:@selector(propertyDidChange:value:)])
- [delegate propertyDidChange:self value:NULL];
-
- return self;
- }
-
- - free
- {
- if (temp)
- NXZoneFree([self zone], temp);
- return [super free];
- }
-
- - (const char *)name
- {
- return name;
- }
-
- - delegate
- {
- return delegate;
- }
-
- - setDelegate:anObject
- {
- delegate = anObject;
- return self;
- }
-
- - (int)tag
- {
- return tag;
- }
-
- - setTag:(int)anInt
- {
- tag = anInt;
- return self;
- }
-
-
- //**********************
- // Delegates and Actions
-
- - hasChanged:sender
- {
- return self;
- }
- @end
-
- //****************************************************************************
- // Booleans
- @implementation NIBoolProperty
- - init:sender properties:(ni_proplist *)props name:(const char *)label outlet:o
- {
- [super init:sender properties:props name:label];
- outlet = o;
- [outlet setTarget:self];
- [outlet setAction:@selector(hasChanged:)];
- [outlet selectCellAt:0 :0];
- yes = NXCopyStringBufferFromZone("yes", [self zone]);
- no = NXCopyStringBufferFromZone("no", [self zone]);
- return self;
- }
-
- - setValues:(const char *)true :(const char *)false
- {
- yes = NXZoneRealloc([self zone], yes, strlen(yes)+1);
- no = NXZoneRealloc([self zone], no, strlen(no)+1);
- (void)strcpy(yes, true);
- (void)strcpy(no, false);
- return self;
- }
-
- - hasChanged:sender
- {
- int col = [outlet selectedCol];
- const char *v;
-
- v = (col < 1)?NULL:((col == 1)?yes:no);
- [self updateProperty:v default:(col < 1)];
- return self;
- }
-
- - display
- {
- int index = [self index];
-
- if (HAVEVALUES(index))
- if (!strcasecmp(VALUELISTITEM(index,0), "yes") ||
- !strcasecmp(VALUELISTITEM(index,0), "true") ||
- !strcasecmp(VALUELISTITEM(index,0), yes) ||
- VALUELISTITEM(index,0)[0] == '1')
- [outlet selectCellAt:0 :1];
- else
- [outlet selectCellAt:0 :2];
- else
- [outlet selectCellAt:0 :0];
-
- return self;
- }
- @end
-
- //****************************************************************************
- // Characters
- @implementation NICharProperty
- - init:sender properties:(ni_proplist *)props name:(const char *)label outlet:o
- {
- [super init:sender properties:(ni_proplist *)props name:label];
- outlet = o;
- needUpdate = NO;
- [outlet setTextDelegate:self];
- [outlet setStringValue:""];
- return self;
- }
-
- - updateProperty
- {
- const char *v;
- id obj = self;
-
- if (needUpdate) {
- v = [outlet stringValue];
- obj = [self updateProperty:v default:(!v || !*v)];
- needUpdate = obj == nil;
- }
- return obj;
- }
-
- - display
- {
- int index = [self index];
-
- if (HAVEVALUES(index))
- [outlet setStringValue:(const char *)VALUELISTITEM(index,0)];
- else
- [outlet setStringValue:""];
-
- return self;
- }
-
- //**************
- // Text delegates
-
- - textDidEnd:textObject endChar:(unsigned short)whyEnd
- {
- return [self updateProperty];
- }
-
- - textDidChange:sender
- {
- needUpdate = YES;
- // Tell the delegate that the property was updated.
- if ([delegate respondsTo:@selector(propertyDidChange:value:)])
- [delegate propertyDidChange:self value:NULL];
- return self;
- }
- @end
-
- //****************************************************************************
- // Integers
- @implementation NIIntProperty
- //**************
- // Slider action
- - setVal:sender
- {
- int v = [slider intValue];
- char buf[32];
-
- if (v != intVal) {
- sprintf(buf, "%d", v);
- if (![self updateProperty:buf default:(v < 0)])
- return nil;
- intVal = v;
- if (intVal < 0)
- [outlet setStringValue:defaultText];
- else if ((intVal == 0) && zero)
- [outlet setStringValue:zero];
- else
- [outlet setIntValue:intVal];
- }
- return self;
- }
-
- //*************
- // main methods
- - init:sender properties:(ni_proplist *)props name:(const char *)label
- text:t slider:s default:(const char *)d zero:(const char *)z
- {
- [super init:sender properties:(ni_proplist *)props name:label outlet:t];
- slider = s;
- defaultText = d;
- zero = z;
- intVal = -1;
-
- [outlet setStringValue:d];
-
- if (slider) {
- [slider setMinValue:-1.0];
- [slider setTarget:self];
- [slider setAction:@selector(setVal:)];
- [slider setIntValue:-1];
- }
-
- return self;
- }
-
- - display
- {
- int index = [self index];
-
- if (HAVEVALUES(index)) {
- intVal = atoi(VALUELISTITEM(index,0));
- if (intVal >= 0) {
- if (slider)
- [slider setIntValue:intVal];
- if ((intVal == 0) && zero)
- [outlet setStringValue:zero];
- else
- [outlet setIntValue:intVal];
- return self;
- }
- }
-
- if (slider)
- [slider setIntValue:-1];
- [outlet setStringValue:defaultText];
-
- return self;
- }
-
- - updateProperty
- {
- char buf[32];
- const char *s = [outlet stringValue];
- int v = atoi(s);
-
- if (s && !strcmp(s, defaultText))
- v = -1;
-
- if (v != intVal) {
- sprintf(buf, "%31d", v);
- if (![self updateProperty:buf default:(v < 0)])
- return nil;
- intVal = v;
- if (intVal < 0)
- [outlet setStringValue:defaultText];
- else if ((intVal == 0) && zero)
- [outlet setStringValue:zero];
- if (slider)
- [slider setIntValue:intVal];
- needUpdate = NO;
- }
-
- return self;
- }
-
- //**************
- // Text delegate
-
- - textDidEnd:textObject endChar:(unsigned short)whyEnd
- {
- return [self updateProperty];
- }
- @end
-
- //****************************************************************************
- // Strings
- @implementation NIStringProperty
- - setString:sender
- {
- const char *val;
-
- if (!(val = [self usePanel:sender]))
- return self;
-
- if ([self updateProperty:val default:(!val || !*val)])
- [outlet setStringValue:val];
-
- return self;
- }
-
- - init:sender properties:(ni_proplist *)props name:(const char *)label
- text:t button:b mode:(int)m path:(const char *)p title:(const char *)tString
- {
- [super init:sender properties:(ni_proplist *)props name:label outlet:t];
- setMode = m;
- path = p;
- title = tString;
- button = b;
- if (button) {
- [button setTarget:self];
- [button setAction:@selector(setString:)];
- }
-
- return self;
- }
- @end
-
- //****************************************************************************
- // Browsers
- // This will set the delegate and delegate of the browser to this object.
- // If there is a text field, then its textDelegate will also be set to this object.
- // The values are copied to a separate structure for resetting.
- @implementation NIBrowserProperty
-
- //******************************
- // Browser delegates and actions
-
- - hasChanged:sender
- {
- if (textField) {
- [textField setStringValue:[[sender selectedCell] stringValue]];
- [textField selectText:self];
- }
- return self;
- }
-
- - (int)browser:sender fillMatrix:matrix inColumn:(int)column
- {
- int i = 0,
- index = [self index];
- id cell;
-
- if (HAVEVALUES(index))
- for (i = 0; i < VALUES(index); i++) {
- [matrix addRow];
- cell = [matrix cellAt:i :0];
- [cell setStringValue:VALUELISTITEM(index,i)];
- [cell setLoaded:YES];
- [cell setLeaf:YES];
- }
-
- return i;
- }
-
- // The action taken when pressing the add button
- - add:sender
- {
- int i = NI_INDEX_NULL,
- index = [self index];
- const char *v = NULL;
- char *copyOfVal = (char *)v;
- BOOL remove = NO;
-
- if (textField)
- v = [textField stringValue];
- else
- v = [self usePanel:sender];
- if (!v || !*v)
- return self;
-
- // Is the new value ok?
- if ([delegate respondsTo:@selector(propertyWillChange:value:default:index:)]) {
- copyOfVal = NXCopyStringBufferFromZone(v, [self zone]);
- if ([delegate propertyWillChange:self value:©OfVal default:&remove index:i]) {
- if (copyOfVal && (copyOfVal != v))
- NXZoneFree([NXApp zone], copyOfVal);
- return self;
- }
- }
-
- // No changes?
- if (HAVEVALUES(index)) {
- for (i = 0; i < VALUES(index); i++)
- if (!strcasecmp(VALUELISTITEM(index,i), v)) {
- if (copyOfVal && (copyOfVal != v))
- NXZoneFree([NXApp zone], copyOfVal);
- [[outlet matrixInColumn:0] selectCellAt:i :0];
- return self;
- } else if (strcasecmp(VALUELISTITEM(index,i), v) > 0)
- break;
-
- ni_namelist_insert(&(VALUELIST(index)), copyOfVal , i);
-
- } else
- if (![self updateProperty:copyOfVal default:(!copyOfVal || !*copyOfVal)]) {
- if (copyOfVal && (copyOfVal != v))
- NXZoneFree([NXApp zone], copyOfVal);
- return self;
- }
-
- [outlet reloadColumn:0];
- [[outlet matrixInColumn:0] selectCellAt:i :0];
- [textField selectText:self];
-
- // Tell the delegate that the property was updated.
- if ([delegate respondsTo:@selector(propertyDidChange:)])
- [delegate propertyDidChange:self value:copyOfVal];
-
- if (copyOfVal && (copyOfVal != v))
- NXZoneFree([NXApp zone], copyOfVal);
-
- return self;
- }
-
- // The action taken when pressing the delete button
- - delete:sender
- {
- BOOL remove = YES;
- char *p = NULL;
- int i,
- index = [self index];
- id selCell = [outlet selectedCell];
-
- if (selCell && HAVEVALUES(index)) {
- i = ni_namelist_match(VALUELIST(index), [selCell stringValue]);
- if (i != NI_INDEX_NULL) {
- if ([delegate respondsTo:@selector(propertyWillChange:value:default:index:)])
- if ([delegate propertyWillChange:self value:&p default:&remove index:i])
- return nil;
- if ([self values] > 1) {
- ni_namelist_delete(&(VALUELIST(index)), i);
- if (--i > 0)
- [[outlet matrixInColumn:0] selectCellAt:i :0];
- else if (HAVEVALUES(index))
- [[outlet matrixInColumn:0] selectCellAt:0 :0];
- } else
- ni_proplist_delete(properties, index);
- [outlet reloadColumn:0];
- if ([delegate respondsTo:@selector(propertyDidChange:value:)])
- [delegate propertyDidChange:self value:p];
- }
- }
- return self;
- }
-
- //***************
- // Text delegates
-
- // Only update the browser if return was pressed.
- - textDidEnd:textObject endChar:(unsigned short)whyEnd
- {
- if (whyEnd == NX_RETURN)
- [self add:textObject];
-
- return self;
- }
-
- //************
- // The methods
-
- - init:sender properties:(ni_proplist *)props name:(const char *)label
- text:t browser:b mode:(int)m path:(const char *)p
- add:a remove:r title:(const char *)tString;
- {
- [super init:sender properties:(ni_proplist *)props name:label];
- outlet = b;
- [outlet setTarget:self];
- [outlet setAction:@selector(hasChanged:)];
- textField = t;
- addButton = a;
- deleteButton = r;
- setMode = m;
- path = p;
- title = tString;
-
- [b setDelegate:self];
- if (textField)
- [textField setTextDelegate:self];
- if (addButton) {
- [addButton setTarget:self];
- [addButton setAction:@selector(add:)];
- }
- if (deleteButton) {
- [deleteButton setTarget:self];
- [deleteButton setAction:@selector(delete:)];
- }
-
- return self;
- }
-
- // Set/reset the values
- - display
- {
- [outlet loadColumnZero];
- [[outlet matrixInColumn:0] selectCellAt:0 :0];
- return self;
- }
- @end
-
- //****************************************************************************
- // Popups
- @implementation NIPopupProperty
- - hasChanged:sender
- {
- const char *s = [[outlet target] selectedItem] ;
- int d = strcmp(defaultText, s);
-
- return [self updateProperty:s default:!d];
- }
-
- - init:sender properties:(ni_proplist *)props name:(const char *)label
- outlet:o default:(const char *)d;
- {
- [super init:sender properties:props name:label];
- outlet = o;
- [[outlet target] setTarget:self];
- [[outlet target] setAction:@selector(hasChanged:)];
- defaultText = d;
- return self;
- }
-
- - display
- {
- int index = [self index];
-
- if (HAVEVALUES(index))
- [outlet setTitle:(const char *)VALUELISTITEM(index,0)];
- else
- [outlet setTitle:defaultText];
- return self;
- }
- @end
-
- //****************************************************************************
- // Calls
- @implementation NICallProperty
- - nothing:sender
- {
- return self;
- }
-
- - init:sender properties:(ni_proplist *)props name:(const char *)label displayAction:(SEL)action;
- {
- [super init:sender properties:(ni_proplist *)props name:label];
- if (action)
- display = action;
- else {
- display = @selector(nothing:);
- delegate = self;
- }
- return self;
- }
-
- - display
- {
- return [delegate perform:display with:self];
- }
-
- @end
-
-