This is NIProperty.m in view mode; [Download] [Up]
/* 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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.