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.