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.