home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // 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
-