mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 22:31:05 +00:00
7946fec523
I don't know that fileSystemRepresentation is any better than cString for general usage (due to wchar on windows).
1058 lines
21 KiB
Objective-C
1058 lines
21 KiB
Objective-C
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include "QF/dstring.h"
|
|
#include "QF/quakeio.h"
|
|
#include "QF/sys.h"
|
|
|
|
#include "QuakeEd.h"
|
|
#include "Clipper.h"
|
|
#include "XYView.h"
|
|
#include "Map.h"
|
|
#include "CameraView.h"
|
|
#include "ZView.h"
|
|
#include "Preferences.h"
|
|
#include "InspectorControl.h"
|
|
#include "Project.h"
|
|
|
|
id quakeed_i;
|
|
id entclasses_i;
|
|
|
|
extern NSBezierPath *path;
|
|
id g_cmd_out_i;
|
|
|
|
BOOL autodirty;
|
|
BOOL filter_light, filter_path, filter_entities;
|
|
BOOL filter_clip_brushes, filter_water_brushes, filter_world;
|
|
|
|
BOOL running;
|
|
|
|
int bsppid;
|
|
|
|
#if 0
|
|
// example command strings
|
|
|
|
char *fullviscmd =
|
|
"rsh satan \"/LocalApps/qbsp $1 $2 ; /LocalApps/light $2 ; /LocalApps/vis $2\"";
|
|
char *fastviscmd =
|
|
"rsh satan \"/LocalApps/qbsp $1 $2 ; /LocalApps/light $2 ; /LocalApps/vis -fast $2\"";
|
|
char *noviscmd =
|
|
"rsh satan \"/LocalApps/qbsp $1 $2 ; /LocalApps/light $2\"";
|
|
char *relightcmd = "rsh satan \"/LocalApps/light $2\"";
|
|
char *leakcmd = "rsh satan \"/LocalApps/qbsp -mark -notjunc $1 $2\"";
|
|
#endif
|
|
|
|
void
|
|
NopSound (void)
|
|
{
|
|
NSBeep ();
|
|
}
|
|
|
|
void
|
|
My_Malloc_Error (int code)
|
|
{
|
|
// recursive toast Error ("Malloc error: %i\n", code);
|
|
write (1, "malloc error!\n", strlen ("malloc error!\n") + 1);
|
|
}
|
|
|
|
#define FN_CMDOUT "/tmp/QuakeEdCmd.txt"
|
|
void
|
|
DisplayCmdOutput (void)
|
|
{
|
|
char *buffer;
|
|
QFile *file;
|
|
int size;
|
|
|
|
file = Qopen (FN_CMDOUT, "rt");
|
|
if (!file)
|
|
return;
|
|
size = Qfilesize (file);
|
|
buffer = malloc (size + 1);
|
|
size = Qread (file, buffer, size);
|
|
Qclose (file);
|
|
unlink (FN_CMDOUT);
|
|
[project_i addToOutput: buffer];
|
|
free (buffer);
|
|
|
|
if ([preferences_i getShowBSP])
|
|
[inspcontrol_i setCurrentInspector: i_output];
|
|
|
|
[preferences_i playBspSound];
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CheckCmdDone
|
|
|
|
See if the BSP is done
|
|
===============
|
|
*/
|
|
// DPSTimedEntry cmdte;
|
|
void
|
|
CheckCmdDone (/* DPSTimedEntry tag, */ double now, void *userData)
|
|
{
|
|
union wait statusp;
|
|
struct rusage rusage;
|
|
|
|
if (!wait4 (bsppid, &statusp, WNOHANG, &rusage))
|
|
return;
|
|
DisplayCmdOutput ();
|
|
bsppid = 0;
|
|
// DPSRemoveTimedEntry( cmdte );
|
|
}
|
|
|
|
void
|
|
QuakeEd_print (const char *fmt, va_list args)
|
|
{
|
|
static dstring_t *output;
|
|
NSString *string;
|
|
|
|
if (!output)
|
|
output = dstring_new ();
|
|
dvsprintf (output, fmt, args);
|
|
string = [NSString stringWithCString: output->str];
|
|
[g_cmd_out_i setStringValue: string];
|
|
fputs (output->str, stdout);
|
|
}
|
|
|
|
// ============================================================================
|
|
|
|
@implementation QuakeEd
|
|
/*
|
|
===============
|
|
AutoSave
|
|
|
|
Every five minutes, save a modified map
|
|
===============
|
|
*/
|
|
- (void) AutoSave
|
|
{
|
|
// automatic backup
|
|
if (autodirty) {
|
|
autodirty = NO;
|
|
#define FN_AUTOSAVE @"/qcache/AutoSaveMap.map"
|
|
[map_i writeMapFile: FN_AUTOSAVE useRegion: NO];
|
|
}
|
|
[map_i writeStats];
|
|
}
|
|
|
|
#define FN_TEMPSAVE @"/qcache/temp.map"
|
|
- (id) setDefaultFilename
|
|
{
|
|
filename = FN_TEMPSAVE;
|
|
[filename retain];
|
|
[self setTitleWithRepresentedFilename: filename];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (BOOL) dirty
|
|
{
|
|
return dirty;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
DISPLAY UPDATING (handles both camera and XYView)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
BOOL updateinflight;
|
|
|
|
BOOL clearinstance;
|
|
|
|
BOOL updatexy;
|
|
BOOL updatez;
|
|
BOOL updatecamera;
|
|
|
|
void
|
|
postappdefined (void)
|
|
{
|
|
NSEvent *ev;
|
|
|
|
if (updateinflight)
|
|
return;
|
|
// post an event at the end of the que
|
|
ev = [NSEvent otherEventWithType: NSApplicationDefined
|
|
location: NSZeroPoint
|
|
modifierFlags: 0
|
|
timestamp: [[NSDate date] timeIntervalSinceReferenceDate]
|
|
windowNumber: 0
|
|
context: [NSApp context]
|
|
subtype: 0
|
|
data1: 0
|
|
data2: 0];
|
|
[NSApp postEvent: ev atStart: NO];
|
|
updateinflight = YES;
|
|
}
|
|
|
|
int c_updateall;
|
|
|
|
- (id) updateAll // when a model has been changed
|
|
{
|
|
updatecamera = updatexy = updatez = YES;
|
|
c_updateall++;
|
|
postappdefined ();
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) updateAll: sender
|
|
{
|
|
[self updateAll];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) updateCamera // when the camera has moved
|
|
{
|
|
updatecamera = YES;
|
|
clearinstance = YES;
|
|
|
|
postappdefined ();
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) updateXY
|
|
{
|
|
updatexy = YES;
|
|
postappdefined ();
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) updateZ
|
|
{
|
|
updatez = YES;
|
|
postappdefined ();
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) cameraNoRestore: (NSRect)rect
|
|
{
|
|
no_restore[0] = YES;
|
|
}
|
|
|
|
- (void) xyNoRestore: (NSRect)rect
|
|
{
|
|
no_restore[1] = YES;
|
|
}
|
|
|
|
- (void) zNoRestore: (NSRect)rect
|
|
{
|
|
no_restore[2] = YES;
|
|
}
|
|
|
|
- (id) newinstance
|
|
{
|
|
clearinstance = YES;
|
|
return self;
|
|
}
|
|
|
|
- (id) redrawInstance
|
|
{
|
|
clearinstance = YES;
|
|
[self flushWindow];
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
flushWindow
|
|
|
|
instance draw the brush after each flush
|
|
===============
|
|
*/
|
|
- (void) flushWindow
|
|
{
|
|
NSRect rect;
|
|
int i;
|
|
NSView *cv;
|
|
|
|
[super flushWindow];
|
|
|
|
// don't lock focus before nib is finished loading
|
|
if (!running)
|
|
return;
|
|
|
|
if (_disableFlushWindow)
|
|
return;
|
|
|
|
cv = [self contentView];
|
|
[cv lockFocus];
|
|
for (i = 3; i >= 0; i--) {
|
|
if (cache[i]) {
|
|
if (!no_restore[i]) {
|
|
rect = cache_rect[i];
|
|
[cache[i] drawAtPoint: rect.origin];
|
|
}
|
|
no_restore[i] = NO;
|
|
[cache[i] release];
|
|
cache[i] = 0;
|
|
}
|
|
}
|
|
rect = [cameraview_i frame];
|
|
// rect = [cv convertRect: rect fromView: cameraview_i];
|
|
cache_rect[0] = rect = NSIntegralRect (rect);
|
|
cache[0] = [[NSBitmapImageRep alloc] initWithFocusedViewRect: rect];
|
|
|
|
rect = [[xyview_i superview] frame];
|
|
rect = [cv convertRect: rect fromView: [[xyview_i superview] superview]];
|
|
cache_rect[1] = rect = NSIntegralRect (rect);
|
|
cache[1] = [[NSBitmapImageRep alloc] initWithFocusedViewRect: rect];
|
|
|
|
rect = [[zview_i superview] frame];
|
|
rect = [cv convertRect: rect fromView: [[zview_i superview] superview]];
|
|
cache_rect[2] = rect = NSIntegralRect (rect);
|
|
cache[2] = [[NSBitmapImageRep alloc] initWithFocusedViewRect: rect];
|
|
[[self contentView] unlockFocus];
|
|
|
|
[cameraview_i lockFocus];
|
|
linestart (0, 0, 0);
|
|
[map_i makeSelectedPerform: @selector (CameraDrawSelf)];
|
|
lineflush ();
|
|
[clipper_i cameraDrawSelf];
|
|
[cameraview_i unlockFocus];
|
|
|
|
[xyview_i lockFocus];
|
|
linestart (0, 0, 0);
|
|
[map_i makeSelectedPerform: @selector (XYDrawSelf)];
|
|
lineflush ();
|
|
[cameraview_i XYDrawSelf];
|
|
[zview_i XYDrawSelf];
|
|
[clipper_i XYDrawSelf];
|
|
[xyview_i unlockFocus];
|
|
|
|
[zview_i lockFocus];
|
|
[map_i makeSelectedPerform: @selector (ZDrawSelf)];
|
|
[cameraview_i ZDrawSelf];
|
|
[clipper_i ZDrawSelf];
|
|
[zview_i unlockFocus];
|
|
|
|
clearinstance = NO;
|
|
|
|
[super flushWindow];
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
App delegate methods
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
- (id) applicationDefined: (NSEvent *)theEvent
|
|
{
|
|
NSEvent *evp;
|
|
|
|
updateinflight = NO;
|
|
|
|
// update screen
|
|
evp = [NSApp nextEventMatchingMask: NSAnyEventMask
|
|
untilDate: [NSDate distantPast]
|
|
inMode: NSEventTrackingRunLoopMode
|
|
dequeue: NO];
|
|
if (evp) {
|
|
postappdefined ();
|
|
return self;
|
|
}
|
|
|
|
[self disableFlushWindow];
|
|
|
|
if ([map_i count] != (unsigned) [entitycount_i intValue])
|
|
[entitycount_i setIntValue: [map_i count]];
|
|
|
|
if ([[map_i currentEntity] count] != (unsigned) [brushcount_i intValue])
|
|
[brushcount_i setIntValue: [[map_i currentEntity] count]];
|
|
|
|
if (updatecamera)
|
|
[cameraview_i display];
|
|
|
|
if (updatexy)
|
|
[xyview_i display];
|
|
|
|
if (updatez)
|
|
[zview_i display];
|
|
|
|
updatecamera = updatexy = updatez = NO;
|
|
|
|
[self enableFlushWindow];
|
|
[self flushWindow];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) awakeFromNib
|
|
{
|
|
// XXX [self addToEventMask:
|
|
// XXX NSRightMouseDragged|NSLeftMouseDragged];
|
|
|
|
// XXX malloc_error(My_Malloc_Error);
|
|
|
|
quakeed_i = self;
|
|
dirty = autodirty = NO;
|
|
|
|
[NSTimer timerWithTimeInterval: 5 * 60
|
|
target: self
|
|
selector: @selector (AutoSave)
|
|
userInfo: nil
|
|
repeats: YES];
|
|
|
|
path = [NSBezierPath new];
|
|
}
|
|
|
|
- (void) applicationDidFinishLaunching: (NSNotification *)notification
|
|
{
|
|
NSArray *screens;
|
|
NSScreen *scrn;
|
|
|
|
running = YES;
|
|
g_cmd_out_i = cmd_out_i; // for qprintf
|
|
Sys_SetStdPrintf (QuakeEd_print);
|
|
|
|
[preferences_i readDefaults];
|
|
[project_i initProject];
|
|
|
|
[xyview_i setModeRadio: xy_drawmode_i];
|
|
// because xy view is inside
|
|
// scrollview and can't be
|
|
// connected directly in IB
|
|
|
|
[self setFrameAutosaveName: @"EditorWinFrame"];
|
|
[self clear: self];
|
|
|
|
// go to my second monitor
|
|
screens = [NSScreen screens];
|
|
if ([screens count] == 2) {
|
|
NSRect frm;
|
|
scrn = [screens objectAtIndex: 1];
|
|
frm = [scrn frame];
|
|
[self setFrameTopLeftPoint: NSMakePoint (frm.origin.x, frm.size.height)];
|
|
}
|
|
|
|
[self makeKeyAndOrderFront: self];
|
|
|
|
[map_i newMap];
|
|
|
|
Sys_Printf ("ready.\n");
|
|
}
|
|
|
|
- (id) appWillTerminate: sender
|
|
{
|
|
// FIXME: save dialog if dirty
|
|
return self;
|
|
}
|
|
|
|
// ===========================================================================
|
|
|
|
- (id) textCommand: sender
|
|
{
|
|
NSString *t;
|
|
|
|
t = [sender stringValue];
|
|
|
|
if (![t isEqualToString: @"texname"]) {
|
|
texturedef_t *td;
|
|
id b;
|
|
|
|
b = [map_i selectedBrush];
|
|
if (!b) {
|
|
Sys_Printf ("nothing selected\n");
|
|
return self;
|
|
}
|
|
td = [b texturedef];
|
|
Sys_Printf ("%s\n", td->texture);
|
|
return self;
|
|
} else {
|
|
Sys_Printf ("Unknown command\n");
|
|
} return self;
|
|
}
|
|
|
|
- (id) openProject: sender
|
|
{
|
|
[project_i openProject];
|
|
return self;
|
|
}
|
|
|
|
- (id) clear: sender
|
|
{
|
|
[map_i newMap];
|
|
|
|
[self updateAll];
|
|
[regionbutton_i setIntValue: 0];
|
|
[self setDefaultFilename];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) centerCamera: sender
|
|
{
|
|
NSRect sbounds;
|
|
|
|
sbounds = [[xyview_i superview] bounds];
|
|
|
|
sbounds.origin.x += sbounds.size.width / 2;
|
|
sbounds.origin.y += sbounds.size.height / 2;
|
|
|
|
[cameraview_i setXYOrigin: &sbounds.origin];
|
|
[self updateAll];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) centerZChecker: sender
|
|
{
|
|
NSRect sbounds;
|
|
|
|
sbounds = [[xyview_i superview] bounds];
|
|
|
|
sbounds.origin.x += sbounds.size.width / 2;
|
|
sbounds.origin.y += sbounds.size.height / 2;
|
|
|
|
[zview_i setPoint: &sbounds.origin];
|
|
[self updateAll];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) changeXYLookUp: sender
|
|
{
|
|
if ([sender intValue])
|
|
xy_viewnormal[2] = 1;
|
|
else
|
|
xy_viewnormal[2] = -1;
|
|
[self updateAll];
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
REGION MODIFICATION
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
applyRegion:
|
|
==================
|
|
*/
|
|
- (id) applyRegion: sender
|
|
{
|
|
filter_clip_brushes = [filter_clip_i intValue];
|
|
filter_water_brushes = [filter_water_i intValue];
|
|
filter_light = [filter_light_i intValue];
|
|
filter_path = [filter_path_i intValue];
|
|
filter_entities = [filter_entities_i intValue];
|
|
filter_world = [filter_world_i intValue];
|
|
|
|
if (![regionbutton_i intValue]) {
|
|
region_min[0] = region_min[1] = region_min[2] = -9999;
|
|
region_max[0] = region_max[1] = region_max[2] = 9999;
|
|
}
|
|
|
|
[map_i makeGlobalPerform: @selector (newRegion)];
|
|
|
|
[self updateAll];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) setBrushRegion: sender
|
|
{
|
|
id b;
|
|
|
|
// get the bounds of the current selection
|
|
if ([map_i numSelected] != 1) {
|
|
Sys_Printf ("must have a single brush selected\n");
|
|
return self;
|
|
}
|
|
|
|
b = [map_i selectedBrush];
|
|
[b getMins: region_min maxs: region_max];
|
|
[b remove];
|
|
|
|
// turn region on
|
|
[regionbutton_i setIntValue: 1];
|
|
[self applyRegion: self];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) setXYRegion: sender
|
|
{
|
|
NSRect bounds;
|
|
|
|
// get xy size
|
|
bounds = [[xyview_i superview] bounds];
|
|
|
|
region_min[0] = bounds.origin.x;
|
|
region_min[1] = bounds.origin.y;
|
|
region_min[2] = -99999;
|
|
region_max[0] = bounds.origin.x + bounds.size.width;
|
|
region_max[1] = bounds.origin.y + bounds.size.height;
|
|
region_max[2] = 99999;
|
|
|
|
// turn region on
|
|
[regionbutton_i setIntValue: 1];
|
|
[self applyRegion: self];
|
|
|
|
return self;
|
|
}
|
|
|
|
// UI querie for other objects
|
|
- (BOOL) showCoordinates
|
|
{
|
|
return [show_coordinates_i intValue];
|
|
}
|
|
|
|
- (BOOL) showNames
|
|
{
|
|
return [show_names_i intValue];
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
BSP PROCESSING
|
|
|
|
==============================================================================
|
|
*/
|
|
void
|
|
ExpandCommand (const char *in, char *out, const char *src, const char *dest)
|
|
{
|
|
while (*in) {
|
|
if (in[0] == '$') {
|
|
if (in[1] == '1') {
|
|
strcpy (out, src);
|
|
out += strlen (src);
|
|
} else if (in[1] == '2') {
|
|
strcpy (out, dest);
|
|
out += strlen (dest);
|
|
}
|
|
in += 2;
|
|
continue;
|
|
}
|
|
*out++ = *in++;
|
|
}
|
|
*out = 0;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
saveBSP
|
|
=============
|
|
*/
|
|
- (id) saveBSP: (const char *)cmdline dialog: (BOOL)wt
|
|
{
|
|
char expandedcmd[1024];
|
|
NSString *mappath;
|
|
NSString *bsppath;
|
|
int oldLightFilter;
|
|
int oldPathFilter;
|
|
NSString *destdir;
|
|
|
|
if (bsppid) {
|
|
NSBeep ();
|
|
return self;
|
|
}
|
|
|
|
// turn off the filters so all entities get saved
|
|
oldLightFilter = [filter_light_i intValue];
|
|
oldPathFilter = [filter_path_i intValue];
|
|
[filter_light_i setIntValue: 0];
|
|
[filter_path_i setIntValue: 0];
|
|
[self applyRegion: self];
|
|
|
|
if ([regionbutton_i intValue]) {
|
|
mappath = [[filename stringByDeletingPathExtension]
|
|
stringByAppendingPathExtension: @"reg"];
|
|
[map_i writeMapFile: mappath useRegion: YES];
|
|
wt = YES; // allways pop the dialog on region ops
|
|
} else {
|
|
mappath = filename;
|
|
}
|
|
// save the entire thing, just in case there is a problem
|
|
[self save: self];
|
|
|
|
[filter_light_i setIntValue: oldLightFilter];
|
|
[filter_path_i setIntValue: oldPathFilter];
|
|
[self applyRegion: self];
|
|
|
|
// write the command to the bsp host
|
|
destdir = [project_i getFinalMapDirectory];
|
|
|
|
bsppath = [destdir stringByAppendingPathComponent:
|
|
[[[mappath lastPathComponent] stringByDeletingPathExtension]
|
|
stringByAppendingPathExtension: @"bsp"]];
|
|
|
|
ExpandCommand (cmdline, expandedcmd,
|
|
[mappath fileSystemRepresentation],
|
|
[bsppath fileSystemRepresentation]);
|
|
|
|
strcat (expandedcmd, " > ");
|
|
strcat (expandedcmd, FN_CMDOUT);
|
|
strcat (expandedcmd, "\n");
|
|
printf ("system: %s", expandedcmd);
|
|
|
|
[project_i addToOutput: (char *) "\n\n========= BUSY =========\n\n"];
|
|
[project_i addToOutput: expandedcmd];
|
|
|
|
if ([preferences_i getShowBSP])
|
|
[inspcontrol_i setCurrentInspector: i_output];
|
|
if (wt) {
|
|
id panel;
|
|
NSModalSession session;
|
|
|
|
panel = NSGetAlertPanel (@"BSP In Progress",
|
|
[NSString stringWithCString: expandedcmd],
|
|
NULL, NULL, NULL);
|
|
session = [NSApp beginModalSessionForWindow: panel];
|
|
system (expandedcmd);
|
|
[NSApp endModalSession: session];
|
|
[panel close];
|
|
NSReleaseAlertPanel (panel);
|
|
DisplayCmdOutput ();
|
|
} else {
|
|
// cmdte = DPSAddTimedEntry(1, CheckCmdDone, self, NS_BASETHRESHOLD);
|
|
if (!(bsppid = fork ())) {
|
|
system (expandedcmd);
|
|
exit (0);
|
|
}
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) BSP_Full: sender
|
|
{
|
|
[self saveBSP: [project_i getFullVisCmd] dialog: NO];
|
|
return self;
|
|
}
|
|
|
|
- (id) BSP_FastVis: sender
|
|
{
|
|
[self saveBSP: [project_i getFastVisCmd] dialog: NO];
|
|
return self;
|
|
}
|
|
|
|
- (id) BSP_NoVis: sender
|
|
{
|
|
[self saveBSP: [project_i getNoVisCmd] dialog: NO];
|
|
return self;
|
|
}
|
|
|
|
- (id) BSP_relight: sender
|
|
{
|
|
[self saveBSP: [project_i getRelightCmd] dialog: NO];
|
|
return self;
|
|
}
|
|
|
|
- (id) BSP_entities: sender
|
|
{
|
|
[self saveBSP: [project_i getEntitiesCmd] dialog: NO];
|
|
return self;
|
|
}
|
|
|
|
- (id) BSP_stop: sender
|
|
{
|
|
if (!bsppid) {
|
|
NSBeep ();
|
|
return self;
|
|
}
|
|
|
|
kill (bsppid, 9);
|
|
// CheckCmdDone (cmdte, 0, NULL);
|
|
[project_i addToOutput: (char *) "\n\n========= STOPPED =========\n\n"];
|
|
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
doOpen:
|
|
|
|
Called by open or the project panel
|
|
==============
|
|
*/
|
|
- (id) doOpen: (NSString *)fname;
|
|
{
|
|
[fname retain];
|
|
[filename release];
|
|
filename = fname;
|
|
|
|
[map_i readMapFile: filename];
|
|
|
|
[regionbutton_i setIntValue: 0];
|
|
[self setTitleWithRepresentedFilename: fname];
|
|
[self updateAll];
|
|
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
open
|
|
==============
|
|
*/
|
|
- (id) open: sender;
|
|
{
|
|
id openpanel;
|
|
NSString *suffixlist[] = {@"map"};
|
|
NSArray *types = [NSArray arrayWithObjects: suffixlist count: 1];
|
|
|
|
openpanel = [NSOpenPanel new];
|
|
|
|
if ([openpanel
|
|
runModalForDirectory: [project_i getMapDirectory]
|
|
file: @""
|
|
types: types] != NSOKButton)
|
|
return self;
|
|
[self doOpen: [openpanel filename]];
|
|
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
save:
|
|
==============
|
|
*/
|
|
- (id) save: sender;
|
|
{
|
|
NSString *backup;
|
|
NSFileManager *fm = [NSFileManager defaultManager];
|
|
|
|
// force a name change if using tempname
|
|
if (![filename compare: FN_TEMPSAVE])
|
|
return [self saveAs: self];
|
|
dirty = autodirty = NO;
|
|
|
|
backup = [[filename stringByDeletingPathExtension]
|
|
stringByAppendingPathExtension: @"bak"];
|
|
[fm copyPath: filename toPath: backup handler: nil]; // copy old to .bak
|
|
|
|
[map_i writeMapFile: filename useRegion: NO];
|
|
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
saveAs
|
|
==============
|
|
*/
|
|
- (id) saveAs: sender;
|
|
{
|
|
id panel_i;
|
|
NSString *dir;
|
|
NSString *fname;
|
|
|
|
panel_i = [NSSavePanel new];
|
|
dir = [filename stringByDeletingLastPathComponent];
|
|
[panel_i setRequiredFileType: @"map"];
|
|
if ([panel_i runModalForDirectory: [project_i getMapDirectory] file: dir]
|
|
!= NSOKButton)
|
|
return self;
|
|
fname = [[panel_i filename] retain];
|
|
[filename release];
|
|
filename = fname;
|
|
|
|
[self setTitleWithRepresentedFilename: filename];
|
|
|
|
[self save: self];
|
|
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
OTHER METHODS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
// AJR - added this for Project info
|
|
- (NSString *) currentFilename
|
|
{
|
|
return filename;
|
|
}
|
|
|
|
- (id) deselect: sender
|
|
{
|
|
if ([clipper_i hide]) // first click hides only the clipper
|
|
return [self updateAll];
|
|
|
|
[map_i setCurrentEntity: [map_i objectAtIndex: 0]];
|
|
|
|
// make world selected
|
|
[map_i makeSelectedPerform: @selector (deselect)];
|
|
[self updateAll];
|
|
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
keyDown
|
|
===============
|
|
*/
|
|
|
|
- (id) keyDown: (NSEvent *)theEvent
|
|
{
|
|
NSString *chars = [theEvent characters];
|
|
unichar c = ([chars length] == 1) ? [chars characterAtIndex: 0] : '\0';
|
|
|
|
// function keys
|
|
switch (c) {
|
|
case NSF2FunctionKey:
|
|
[cameraview_i setDrawMode: dr_wire];
|
|
Sys_Printf ("wire draw mode\n");
|
|
return self;
|
|
|
|
case NSF3FunctionKey:
|
|
[cameraview_i setDrawMode: dr_flat];
|
|
Sys_Printf ("flat draw mode\n");
|
|
return self;
|
|
|
|
case NSF4FunctionKey:
|
|
[cameraview_i setDrawMode: dr_texture];
|
|
Sys_Printf ("texture draw mode\n");
|
|
return self;
|
|
|
|
case NSF5FunctionKey:
|
|
[xyview_i setDrawMode: dr_wire];
|
|
Sys_Printf ("wire draw mode\n");
|
|
return self;
|
|
|
|
case NSF6FunctionKey:
|
|
Sys_Printf ("texture draw mode\n");
|
|
return self;
|
|
|
|
case NSF8FunctionKey:
|
|
[cameraview_i homeView: self];
|
|
return self;
|
|
|
|
case NSF12FunctionKey:
|
|
[map_i subtractSelection: self];
|
|
return self;
|
|
|
|
case NSPageUpFunctionKey:
|
|
[cameraview_i upFloor: self];
|
|
return self;
|
|
|
|
case NSPageDownFunctionKey:
|
|
[cameraview_i downFloor: self];
|
|
return self;
|
|
|
|
case NSEndFunctionKey:
|
|
[self deselect: self];
|
|
return self;
|
|
|
|
case NSRightArrowFunctionKey:
|
|
case NSLeftArrowFunctionKey:
|
|
case NSUpArrowFunctionKey:
|
|
case NSDownArrowFunctionKey:
|
|
case 'a':
|
|
case 'A':
|
|
case 'z':
|
|
case 'Z':
|
|
case 'd':
|
|
case 'D':
|
|
case 'c':
|
|
case 'C':
|
|
case '.':
|
|
case ',':
|
|
[cameraview_i _keyDown: theEvent];
|
|
break;
|
|
|
|
case 27: // escape
|
|
autodirty = dirty = YES;
|
|
[self deselect: self];
|
|
return self;
|
|
|
|
case 127: // delete
|
|
autodirty = dirty = YES;
|
|
[map_i makeSelectedPerform: @selector (remove)];
|
|
[clipper_i hide];
|
|
[self updateAll];
|
|
break;
|
|
|
|
case '/':
|
|
[clipper_i flipNormal];
|
|
[self updateAll];
|
|
break;
|
|
|
|
case 13: // enter
|
|
[clipper_i carve];
|
|
[self updateAll];
|
|
Sys_Printf ("carved brush\n");
|
|
break;
|
|
|
|
case ' ':
|
|
[map_i cloneSelection: self];
|
|
break;
|
|
|
|
// move selection keys
|
|
case '2':
|
|
VectorCopy (vec3_origin, sb_translate);
|
|
sb_translate[1] = -[xyview_i gridsize];
|
|
[map_i makeSelectedPerform: @selector (translate)];
|
|
[self updateAll];
|
|
break;
|
|
|
|
case '8':
|
|
VectorCopy (vec3_origin, sb_translate);
|
|
sb_translate[1] = [xyview_i gridsize];
|
|
[map_i makeSelectedPerform: @selector (translate)];
|
|
[self updateAll];
|
|
break;
|
|
|
|
case '4':
|
|
VectorCopy (vec3_origin, sb_translate);
|
|
sb_translate[0] = -[xyview_i gridsize];
|
|
[map_i makeSelectedPerform: @selector (translate)];
|
|
[self updateAll];
|
|
break;
|
|
|
|
case '6':
|
|
VectorCopy (vec3_origin, sb_translate);
|
|
sb_translate[0] = [xyview_i gridsize];
|
|
[map_i makeSelectedPerform: @selector (translate)];
|
|
[self updateAll];
|
|
break;
|
|
|
|
case '-':
|
|
VectorCopy (vec3_origin, sb_translate);
|
|
sb_translate[2] = -[xyview_i gridsize];
|
|
[map_i makeSelectedPerform: @selector (translate)];
|
|
[self updateAll];
|
|
break;
|
|
|
|
case '+':
|
|
VectorCopy (vec3_origin, sb_translate);
|
|
sb_translate[2] = [xyview_i gridsize];
|
|
[map_i makeSelectedPerform: @selector (translate)];
|
|
[self updateAll];
|
|
break;
|
|
|
|
default:
|
|
Sys_Printf ("undefined keypress\n");
|
|
NopSound ();
|
|
break;
|
|
} /* switch */
|
|
|
|
return self;
|
|
}
|
|
|
|
@end
|