This is DataEditor.m in view mode; [Download] [Up]
// -------------------------------------------------------------------------------------
// Editor panel
// This software is without warranty of any kind. Use at your own risk.
// -------------------------------------------------------------------------------------
#import <appkit/appkit.h>
#import <dbkit/dbkit.h>
#import <objc/objc.h>
#import <libc.h>
#import <stdlib.h>
#import <string.h>
#import <c.h>
#import <ctype.h>
#import <sys/param.h>
#import <sys/types.h>
#import <sys/time.h>
#import <sys/dir.h>
#import "FileTable.h"
#import "BoolFormatter.h"
#import "EditFormatter.h"
#import "DataTableView.h"
#import "SimpleTableView.h"
#import "DataEditor.h"
// -------------------------------------------------------------------------------------
// DataEditor global vars
static id instanceList = (id)nil; // list of instantiated editors
static BOOL shutDown = NO; // becomes true when terminating
// -------------------------------------------------------------------------------------
// global preferences
static BOOL prefShowTableGrid = YES;
// -------------------------------------------------------------------------------------
// NXRect abbreviations
#define X origin.x
#define Y origin.y
#define W size.width
#define H size.height
// -------------------------------------------------------------------------------------
// misc constants/macros
#define LOCALIZE(X) NXLocalizedString((X), cpNIL, cpNIL)
#define cpNIL (char*)nil
#define KEY_COLUMN 0
// -------------------------------------------------------------------------------------
// textWidth macro
#define textWidth(T) ({ \
NXSize s; \
[[[[Cell alloc] initTextCell:T] calcCellSize:&s] free]; \
rint(s.width * 1.40); \
})
// -------------------------------------------------------------------------------------
@implementation DataEditor
// -------------------------------------------------------------------------------------
/* return custom column formatter */
- _columnFormatterForType:(int)type
{
id fmtId = (id)nil;
switch (type) {
case CDT_BOOLEAN:
fmtId = [[BoolFormatter alloc] init];
[fmtId setIcon:""];
[fmtId setAltIcon:"checkH"];
break;
case CDT_UNKNOWN:
case CDT_STRING:
case CDT_INTEGER:
default:
fmtId = [[EditFormatter alloc] init];
break;
}
return fmtId;
}
/* return vector column for identifier */
- (int)_vectorColumn:(int)col
{
int c;
for (c = 0; c < [dataTableView columnCount]; c++) {
id <DBTableVectors> v = [dataTableView columnAt:c];
if ([v identifier] == (id)col) return c;
}
return -1;
}
/* add column at view order */
- _addColumnAt:(int)i
{
id fmtId;
id <DBTableVectors> vectId;
dataColumn_t *ci = [dataTable orderedColumnInfoAt:i];
/* column not found, or is hidden */
if (!ci || ci->isHidden) return self;
/* add column to TableView */
fmtId = [self _columnFormatterForType:ci->type];
[dataTableView addColumn:(id)ci->index withFormatter:fmtId andTitle:ci->title at:i];
/* set column attributes */
vectId = [dataTableView columnAt:i];
[vectId setTitle:ci->title];
[vectId setTitleAlignment:NX_CENTERED];
[vectId sizeTo:((ci->size > 0.0)? ci->size : textWidth(ci->title))];
[vectId setMinSize:ci->minSize];
[vectId setContentAlignment:ci->alignment];
[vectId setEditable:ci->isEditable];
[vectId setResizable:YES];
[vectId setAutosizable:NO];
return self;
}
/* reset column info */
- _resetColumnInfo
{
int c;
/* update dataTable column header info */
for (c = 0; c < [dataTable actualColumnCount]; c++) {
int n = [self _vectorColumn:c];
float size = n>=0? [[dataTableView columnAt:n] size] : 0.0;
[dataTable setDisplayOrder:n andWidth:size forColumnIndex:c];
}
return self;
}
/* reload tableView data */
- _reloadData
{
[rcdCount setIntValue:[dataTable rowCount]];
[dataTableView reloadData:self];
return self;
}
/* hide selected column */
- hideColumn:sender
{
int col = [dataTableView selectedColumn];
if (col >= 0) {
[dataTableView removeColumnAt:col];
[self _resetColumnInfo];
[self _reloadData];
} else NXBeep();
return self;
}
/* reset column info */
- unhideAllColumns:sender
{
int c, vcnt = [dataTable columnCount], acnt = [dataTable actualColumnCount];
/* display display */
[editorWindow disableFlushWindow];
[editorWindow disableDisplay];
/* unhide hidden columns */
for (c = 0; (c < acnt) && (vcnt < acnt); c++) {
dataColumn_t *ci = [dataTable columnInfoAt:c];
if (ci && ci->isHidden) {
[dataTable setDisplayOrder:vcnt andWidth:-1.0 forColumnIndex:c];
[self _addColumnAt:vcnt++];
}
}
/* reload data */
[self _resetColumnInfo];
[editorWindow reenableDisplay];
[editorWindow reenableFlushWindow];
[self _reloadData];
return self;
}
// -------------------------------------------------------------------------------------
// DataEditor initialization
/* initialize window */
- _initWindowViews
{
int i, c;
NXRect rect;
char *docTitle;
id tblViewSav;
/* disable window flushing */
[editorWindow disableFlushWindow];
[editorWindow disableDisplay];
/* set document title */
docTitle = (char*)[dataTable tableTitle];
if (*docTitle == '/') [editorWindow setTitleAsFilename:docTitle];
else [editorWindow setTitle:docTitle];
/* init window header box */
[headerBox setBorderType:NX_NOBORDER];
[rcdCount setIntValue:[dataTable rowCount]];
/* initialize dataTableBox & contents */
[dataTableBox setBorderType:NX_NOBORDER];
tblViewSav = dataTableView;
[tblViewSav getFrame:&rect];
dataTableView = [[TableView alloc] initFrame:&rect];
[[tblViewSav superview] addSubview:dataTableView];
[tblViewSav free];
/* set tableview attributes */
[dataTableView setMode:DB_LISTMODE];
[dataTableView setTarget:self];
[dataTableView setAction:@selector(selectTableCell1:)];
[dataTableView setDoubleAction:@selector(selectTableCell2:)];
[dataTableView setDelegate:self];
[dataTableView allowVectorReordering:YES];
[dataTableView allowVectorResizing:YES];
[dataTableView setEditable:YES];
[dataTableView setGridVisible:prefShowTableGrid];
[dataTableView setHorizScrollerRequired:YES];
/* add columns */
for (c = [dataTable columnCount], i = 0; i < c; i++) [self _addColumnAt:i];
[dataTableView setDataSource:dataTable];
/* re-enable window flushing */
[editorWindow reenableDisplay];
[editorWindow reenableFlushWindow];
[editorWindow display];
return self;
}
/* place window */
- _placeWindow:(BOOL)moveWindow
{
NXSize scrSize;
NXRect rect;
[NXApp getScreenSize:&scrSize];
[editorWindow getFrame:&rect];
if (moveWindow) {
static int winCount = 0;
rect.X = floor(scrSize.width * 0.20) + ((float)winCount * 64.0);
rect.Y = floor(scrSize.height * 0.85) + ((float)winCount * -24.0);
if (rect.X > (scrSize.width - 64.0)) rect.X = scrSize.width - 64.0;
if (rect.Y > (scrSize.height - 24.0)) rect.Y = scrSize.height - 24.0;
rect.Y -= rect.H;
winCount = (++winCount) % 5;
}
if ([dataTable viewSize]) rect.size = *[dataTable viewSize];
[editorWindow placeWindow:&rect];
[self windowDidResize:editorWindow];
return self;
}
/* initialization */
- initFromFile:(const char*)fileName
{
/* init super and add to instance list */
[super init];
if (!instanceList) instanceList = [[[List alloc] initCount:1] empty];
[instanceList addObject:self];
/* load nib */
if (![NXApp loadNibSection:"DataEditor.nib" owner:self]) {
NXLogError("Could not load nib file 'DataEditor.nib'");
[NXApp delayedFree:self];
return (id)nil;
}
/* open/fill table */
dataTable = [[FileTable alloc] initFromFile:fileName];
[dataTable setDelegate:self];
[self sortByDisplayOrder];
/* init/show window */
[editorWindow setDelegate:self];
[self _initWindowViews];
[self _placeWindow:YES];
[[editorWindow display] makeKeyAndOrderFront:(id)nil];
/* return */
return self;
}
/* normal free */
- free
{
[instanceList removeObject:self];
[dataTable free];
return [super free];
}
/* close window (free) */
- closeWindow
{
[editorWindow performClose:self];
return self;
}
/* free all instances */
+ terminateAllEditors
{
shutDown = YES;
[[[instanceList copy] makeObjectsPerform:@selector(closeWindow)] free];
return self;
}
// -------------------------------------------------------------------------------------
// key name
/* get a new key name */
- (const char*)getKeyName:(const char*)keyName
{
char title[256], *newName, *tableName = (char*)[dataTable tableTitle];
/* init panel fields and show */
sprintf(title, "%s: Key Name", (*tableName=='/'?"<file>":tableName));
[dataNamePanel setTitle:title];
[dataNameField setStringValue:(keyName?keyName:"")];
[dataNameError setStringValue:""];
[[[dataNamePanel center] display] makeKeyAndOrderFront:(id)nil];
/* loop until successful */
for (;;) {
/* show panel */
[NXApp runModalFor:dataNamePanel];
newName = (char*)[dataNameField stringValue];
if (dataNameFlag || !*newName || !strcmp(newName, keyName)) {
newName = (char*)nil;
break;
}
/* check for existance */
if ([dataTable indexForRowName:newName exactMatch:YES] < 0) break;
[dataNameError setStringValue:"Key name already exists. Please try again."];
NXLogError("Key %s already exists", newName);
NXBeep();
}
[dataNamePanel orderOut:(id)nil];
return newName;
}
/* add key button selection */
- keyResponse:sender
{
dataNameFlag = [sender tag];
[NXApp abortModal];
return self;
}
// -------------------------------------------------------------------------------------
// adding/duplicating rows
/* add a new row */
- addRow:sender
{
char *keyName;
if (keyName = (char*)[self getKeyName:""]) {
[dataTable newRowName:keyName copyFromRow:-1];
[self resort:(id)nil];
}
return self;
}
/* duplicate selected row */
- duplicateRow:sender
{
int row = [dataTableView selectedRow];
char *keyName;
if ((row >= 0) && (keyName = (char*)[self getKeyName:""])) {
[dataTable newRowName:keyName copyFromRow:row];
[self resort:(id)nil];
}
return self;
}
/* delete selected row */
- deleteRow:sender
{
int row = [dataTableView selectedRow];
if (row >= 0) {
char *value = (char*)[dataTable valueFor:row:KEY_COLUMN];
const char *ttl = LOCALIZE("Delete");
const char *msg = LOCALIZE("%s: Delete Record Entry '%s'?");
const char *op1 = LOCALIZE("Delete");
const char *op2 = LOCALIZE("Don't Delete");
const char *op3 = cpNIL;
int rtn = NXRunAlertPanel(ttl,msg,op1,op2,op3, [dataTable tableName], value);
if (rtn == NX_ALERTDEFAULT) {
[dataTable deleteRowAt:row];
[self _reloadData];
}
} else NXBeep();
return self;
}
// -------------------------------------------------------------------------------------
// table cell selection
/* single-click table cell selection */
- selectTableCell1:sender
{
return self;
}
/* double-click table cell selection: edit record name field */
- selectTableCell2:sender
{
char *keyName;
dataColumn_t *ci;
int row = [dataTableView selectedCellRow];
int col = [dataTableView selectedCellColumn];
/* check for proper row */
if (row < 0) return self;
/* check for proper column */
ci = [dataTable orderedColumnInfoAt:col];
if (!ci || (ci->index != KEY_COLUMN)) return self;
/* get new key name */
keyName = (char*)[self getKeyName:[dataTable valueFor:row:KEY_COLUMN]];
if (!keyName) return self;
/* change key name */
[dataTable setValue:keyName atIndex:row:KEY_COLUMN];
[dataTableView rowsChangedFrom:row to:row];
return self;
}
// -------------------------------------------------------------------------------------
// sorting functions
/* sort by display order */
- sortByDisplayOrder
{
dataColumn_t *pci = [dataTable orderedColumnInfoAt:0];
dataColumn_t *sci = pci? [dataTable orderedColumnInfoAt:1] : (dataColumn_t*)nil;
if (!pci || ![dataTable sortTableByColumn:pci->index :(sci?sci->index:-1)]) {
NXLogError("Cannot sort by display order");
return self;
}
return self;
}
/* sort */
- resort:sender
{
[self sortByDisplayOrder];
[self _reloadData];
return self;
}
// -------------------------------------------------------------------------------------
// pasteboard functions
/* delete column/row */
- delete:sender
{
int row = [dataTableView selectedRow], col = [dataTableView selectedColumn];
if ((row < 0) && (col >= 0)) [self hideColumn:(id)nil];
else [self deleteRow:(id)nil];
return self;
}
/* cut column/row */
- cut:sender
{
// not supported
NXBeep();
return self;
}
/* copy column/row */
- copy:sender
{
// not supported
NXBeep();
return self;
}
/* paste column/row */
- paste:sender
{
// not supported
NXBeep();
return self;
}
// -------------------------------------------------------------------------------------
// save
/* set doc modified flag */
- setDocEdited:(BOOL)flag
{
return [editorWindow setDocEdited:flag];
}
/* save contents of document */
- save:sender
{
NXRect rect;
/* reset column info */
[self _resetColumnInfo];
/* check for existing errors */
if ([dataTable hasVerificationErrors]) {
const char *ttl = LOCALIZE("Save");
const char *msg = LOCALIZE("%s has errors");
const char *op1 = LOCALIZE("Save Anyway");
const char *op2 = LOCALIZE("Don't Save");
const char *op3 = cpNIL;
int rtn = NXRunAlertPanel(ttl,msg,op1,op2,op3, [dataTable tableName]);
if (rtn != NX_ALERTDEFAULT) return (id)nil; // Don't Save
}
/* check for table changes */
if ([dataTable tableHasChanged]) {
const char *ttl = LOCALIZE("Save");
const char *msg = LOCALIZE("%s has been edited by someone else");
const char *op1 = LOCALIZE("Overwrite");
const char *op2 = LOCALIZE("Cancel");
const char *op3 = cpNIL;
int rtn = NXRunAlertPanel(ttl,msg,op1,op2,op3, [dataTable tableName]);
if (rtn != NX_ALERTDEFAULT) return (id)nil; // Cancel
}
/* commit(save) table */
[editorWindow getFrame:&rect];
[dataTable setViewSize:&rect.size];
[dataTable commitTable];
return self;
}
/* save contents of document */
- saveAs:sender
{
NXLogError("SaveAs not implemented...");
NXBeep();
return self;
}
/* revert to saved */
- revertToSaved:sender
{
id oldData;
if ([editorWindow isDocEdited]) {
const char *ttl = LOCALIZE("Revert");
const char *msg = LOCALIZE("Revert changes to %s?");
const char *op1 = LOCALIZE("Revert");
const char *op2 = LOCALIZE("Cancel");
const char *op3 = cpNIL;
int rtn = NXRunAlertPanel(ttl,msg,op1,op2,op3, [dataTable tableName]);
if (rtn != NX_ALERTDEFAULT) return (id)nil; // Cancel
}
oldData = dataTable;
dataTable = [[FileTable alloc] initFromFile:[oldData tableAccess]];
[dataTable setDelegate:self];
[self sortByDisplayOrder];
[self _initWindowViews];
[self _placeWindow:NO];
[self setDocEdited:NO];
[oldData free];
return self;
}
/* close window */
- close:sender
{
return [self closeWindow];
}
// -------------------------------------------------------------------------------------
// TableView delegate methods
/* user move column */
- tableView:aTableView movedColumnFrom:(u_int)oldpos to:(u_int)newpos
{
[self _resetColumnInfo];
if ((oldpos <= 1) || (newpos <= 1)) [self resort:(id)nil];
[self setDocEdited:YES];
return self;
}
/* selection will change */
- (BOOL)tableViewWillChangeSelection:aTableView
{
return YES; // allow selection change
}
/* user selection changed */
- tableViewDidChangeSelection:aTableView
{
return self;
}
// -------------------------------------------------------------------------------------
// window delegate methods
/* window is resizing */
- windowWillResize:windowId toSize:(NXSize*)newSize
{
NXRect fRect, cRect;
/* set max width: account for ScrollView(TableView) */
[[dataTableView docView] getFrame:&cRect];
[[dataTableView class] getFrameSize:&fRect.size forContentSize:&cRect.size
horizScroller:YES vertScroller:YES borderType:[dataTableView borderType]];
/* set max width: account for Box (nil since Box has no border) */
/* set max width: account for Window */
cRect.size = fRect.size;
cRect.X = cRect.Y = 0.0;
[[windowId class] getFrameRect:&fRect forContentRect:&cRect style:[windowId style]];
/* reset width */
if (newSize->width > fRect.W) newSize->width = fRect.W;
return self;
}
/* window is resizing */
- windowDidResize:windowId
{
NXRect winFrame, rect;
/* get window size */
[[windowId contentView] getFrame:&winFrame];
/* reset header width */
[headerBox getFrame:&rect];
winFrame.H -= rect.H;
rect.X = 0.0;
rect.Y = winFrame.H;
rect.W = winFrame.W;
[headerBox setFrame:&rect];
/* reset table box frame */
[dataTableBox getFrame:&rect];
rect.X = 0.0;
rect.Y = 0.0;
rect.size = winFrame.size;
[dataTableBox setFrame:&rect];
/* resize contents of table box */
[[dataTableBox contentView] getFrame:&rect];
[dataTableView setFrame:&rect];
return self;
}
/* window will close */
- windowWillClose:windowId
{
/* check for edited document */
if ([editorWindow isDocEdited]) {
const char *ttl = LOCALIZE("Save");
const char *msg = LOCALIZE("Save changes to %s?");
const char *op1 = LOCALIZE("Save");
const char *op2 = LOCALIZE("Don't Save");
const char *op3 = shutDown? cpNIL : LOCALIZE("Cancel");
int rtn = NXRunAlertPanel(ttl,msg,op1,op2,op3, [dataTable tableName]);
if (!shutDown && (rtn == NX_ALERTOTHER)) return (id)nil; // Cancel
if (rtn == NX_ALERTDEFAULT) [self save:(id)nil]; // Save (else No)
}
/* free/close window */
[instanceList removeObject:self];
[NXApp delayedFree:self];
return self;
}
// -------------------------------------------------------------------------------------
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.