quakeforge/tools/Forge/Bundles/MapEdit/XYView.m
Bill Currie fb65cd2b44 Cache needed fonts.
Obtaining a font has become rather expensive in recent gnustep, so cache
fonts, obtaining new ones only when needed rather than every redraw. Makes
things snappy again :)
2011-04-16 12:17:23 +09:00

1334 lines
28 KiB
Objective-C

#include "QF/sys.h"
#include "XYView.h"
#include "ZView.h"
#include "CameraView.h"
#include "Clipper.h"
#include "QuakeEd.h"
#include "Map.h"
#include "Entity.h"
#include "PopScrollView.h"
id xyview_i;
id scrollview_i, gridbutton_i, scalebutton_i;
vec3_t xy_viewnormal; // v_forward for xy view
float xy_viewdist; // clip behind this plane
@implementation NSView (XYView)
- (void) setFrame: (NSRect)frame
bounds: (NSRect)bounds
scale: (NSSize)scale
{
// XXX[quakeed_i disableDisplay];
// [self setPostsFrameChangedNotifications:NO];
// [self setPostsBoundsChangedNotifications:NO];
[self setFrame: frame];
if (_boundsMatrix) {
// FIXME workaround for a bug (?) in GNUstep
NSAffineTransformStruct t = [_boundsMatrix transformStruct];
t.m11 = t.m22 = 1;
t.m12 = t.m21 = 0;
[_boundsMatrix setTransformStruct: t];
}
[self setBounds: bounds];
if (_boundsMatrix) {
// FIXME workaround for a bug (?) in GNUstep
NSAffineTransformStruct t = [_boundsMatrix transformStruct];
t.tX *= scale.width;
t.tY *= scale.height;
[_boundsMatrix setTransformStruct: t];
}
// [self setPostsFrameChangedNotifications:YES];
// [self setPostsBoundsChangedNotifications:YES];
// XXX[quakeed_i reenableDisplay];
}
@end
@implementation XYView
/*
==================
initWithFrame:
==================
*/
- (id) initWithFrame: (NSRect)frameRect
{
[super initWithFrame: frameRect];
[self allocateGState];
font = [[NSFont systemFontOfSize: 10] retain];
realbounds = NSMakeRect (0, 0, 0, 0);
gridsize = 16;
scale = 1.0;
xyview_i = self;
xy_viewnormal[2] = -1;
xy_viewdist = -1024;
// initialize the pop up menus
scalebutton_i = [[NSPopUpButton alloc] init];
[scalebutton_i setTarget: self];
[scalebutton_i setAction: @selector (scaleMenuTarget:)];
[scalebutton_i addItemWithTitle: @"12.5%"];
[scalebutton_i addItemWithTitle: @"25%"];
[scalebutton_i addItemWithTitle: @"50%"];
[scalebutton_i addItemWithTitle: @"75%"];
[scalebutton_i addItemWithTitle: @"100%"];
[scalebutton_i addItemWithTitle: @"200%"];
[scalebutton_i addItemWithTitle: @"300%"];
[scalebutton_i sizeToFit];
[scalebutton_i selectItemAtIndex: 4];
gridbutton_i = [[NSPopUpButton alloc] init];
[gridbutton_i setTarget: self];
[gridbutton_i setAction: @selector (gridMenuTarget:)];
[gridbutton_i addItemWithTitle: @"grid 1"];
[gridbutton_i addItemWithTitle: @"grid 2"];
[gridbutton_i addItemWithTitle: @"grid 4"];
[gridbutton_i addItemWithTitle: @"grid 8"];
[gridbutton_i addItemWithTitle: @"grid 16"];
[gridbutton_i addItemWithTitle: @"grid 32"];
[gridbutton_i addItemWithTitle: @"grid 64"];
[gridbutton_i sizeToFit];
[gridbutton_i selectItemAtIndex: 4];
// initialize the scroll view
scrollview_i = [[PopScrollView alloc] initWithFrame: frameRect
button1: scalebutton_i
button2: gridbutton_i];
[scrollview_i setLineScroll: 64];
[scrollview_i setAutoresizingMask: (NSViewWidthSizable |
NSViewHeightSizable)];
// link objects together
[scrollview_i setDocumentView: self];
return scrollview_i;
}
- (BOOL) acceptsFirstResponder
{
return YES;
}
- (BOOL) isOpaque
{
return YES;
}
- (id) setModeRadio: m
{
// this should be set from IB, but because I toss myself in a
// popscrollview the connection gets lost
mode_radio_i = m;
[mode_radio_i setTarget: self];
[mode_radio_i setAction: @selector (drawMode:)];
return self;
}
- (id) drawMode: sender
{
drawmode = [sender selectedColumn];
[quakeed_i updateXY];
return self;
}
- (id) setDrawMode: (drawmode_t)mode
{
drawmode = mode;
[mode_radio_i selectCellAtRow: 0 column: mode];
[quakeed_i updateXY];
return self;
}
- (float) currentScale
{
return scale;
}
- (id) setOrigin: (NSPoint)pt
scale: (float)sc
{
NSRect sframe;
NSRect bounds;
NSClipView *cv = (NSClipView *) _super_view;
// calculate the area visible in the cliprect
scale = sc;
bounds = [_super_view bounds];
bounds.origin = pt;
bounds.size.width /= scale;
bounds.size.height /= scale;
// union with the realbounds
bounds = NSUnionRect (realbounds, bounds);
sframe = bounds;
sframe.origin.x *= scale;
sframe.origin.y *= scale;
sframe.size.width *= scale;
sframe.size.height *= scale;
// redisplay everything
// size this view
[quakeed_i disableFlushWindow];
[self setFrame: sframe bounds: bounds scale: NSMakeSize (scale, scale)];
// scroll the clip view
pt.x *= scale;
pt.y *= scale;
[cv setBoundsOrigin: pt];
[scrollview_i reflectScrolledClipView: cv];
[quakeed_i enableFlushWindow];
[scrollview_i display];
return self;
}
- (id) centerOn: (vec3_t)org
{
NSRect sbounds;
NSPoint mid, origin;
sbounds = [_super_view bounds];
mid.x = sbounds.size.width / 2;
mid.y = sbounds.size.height / 2;
origin.x = org[0] - mid.x / scale;
origin.y = org[1] - mid.y / scale;
[self setOrigin: origin scale: scale];
return self;
}
/*
==================
newSuperBounds
When superview is resized
==================
*/
- (id) newSuperBounds
{
NSRect r;
r = [[self superview] bounds];
[self newRealBounds: r];
return self;
}
/*
===================
newRealBounds
Called when the realbounds rectangle is changed.
Should only change the scroll bars, not cause any redraws.
If realbounds has shrunk, nothing will change.
===================
*/
- (id) newRealBounds: (NSRect)nb
{
NSRect bounds;
NSRect sframe;
realbounds = nb;
// calculate the area visible in the cliprect
bounds = [_super_view bounds];
bounds.origin.x /= scale;
bounds.origin.y /= scale;
bounds.size.width /= scale;
bounds.size.height /= scale;
bounds = NSUnionRect (realbounds, bounds);
sframe = bounds;
sframe.origin.x *= scale;
sframe.origin.y *= scale;
sframe.size.width *= scale;
sframe.size.height *= scale;
// size this view
[quakeed_i disableFlushWindow];
[self setFrame: sframe bounds: bounds scale: NSMakeSize (scale, scale)];
[quakeed_i enableFlushWindow];
[scrollview_i reflectScrolledClipView: [scrollview_i contentView]];
return self;
}
/*
====================
scaleMenuTarget:
Called when the scaler popup on the window is used
====================
*/
- (id) scaleMenuTarget: sender
{
NSRect rect;
NSPoint mid, org, origin;
NSFont *f;
float nscale;
nscale = [[[sender selectedCell] title] floatValue] / 100;
if (nscale == scale)
return NULL;
f = [[NSFont systemFontOfSize: 10 / nscale] retain];
[scaledFont release];
scaledFont = f;
// keep the center of the view constant
rect = [_super_view bounds];
mid.x = rect.size.width / 2;
mid.y = rect.size.height / 2;
org.x = (rect.origin.x + mid.x) / scale;
org.y = (rect.origin.y + mid.y) / scale;
origin.x = org.x - mid.x / nscale;
origin.y = org.y - mid.y / nscale;
[self setOrigin: origin scale: nscale];
return self;
}
/*
==============
zoomIn
==============
*/
- (id) zoomIn: (NSPoint *)constant
{
id itemlist, selectedItem;
int selected, numrows;
NSRect visrect;
NSPoint ofs, new;
// set the popup
itemlist = [scalebutton_i itemArray];
numrows = [itemlist count];
selectedItem = [scalebutton_i selectedItem];
selected = [itemlist indexOfObject: selectedItem] + 1;
if (selected >= numrows)
return NULL;
[scalebutton_i selectItemAtIndex: selected];
// zoom the view
visrect = [[self superview] bounds];
ofs.x = constant->x - visrect.origin.x;
ofs.y = constant->y - visrect.origin.y;
new.x = constant->x - ofs.x / 2;
new.y = constant->y - ofs.y / 2;
[self setOrigin: new scale: scale * 2];
return self;
}
/*
==============
zoomOut
==============
*/
- (id) zoomOut: (NSPoint *)constant
{
id itemlist, selectedItem;
int selected;
NSRect visrect;
NSPoint ofs, new;
// set the popup
itemlist = [scalebutton_i itemArray];
selectedItem = [scalebutton_i selectedItem];
selected = [itemlist indexOfObject: selectedItem] - 1;
if (selected < 0)
return NULL;
[scalebutton_i selectItemAtIndex: selected];
// zoom the view
visrect = [[self superview] bounds];
ofs.x = constant->x - visrect.origin.x;
ofs.y = constant->y - visrect.origin.y;
new.x = constant->x - ofs.x * 2;
new.y = constant->y - ofs.y * 2;
[self setOrigin: new scale: scale / 2];
return self;
}
/*
====================
gridMenuTarget:
Called when the scaler popup on the window is used
====================
*/
- (id) gridMenuTarget: sender
{
int grid;
grid = [[[sender selectedCell] title] intValue];
if (grid == gridsize)
return NULL;
gridsize = grid;
[quakeed_i updateAll];
return self;
}
/*
====================
snapToGrid
====================
*/
- (float) snapToGrid: (float)f
{
int i;
i = rint (f / gridsize);
return i * gridsize;
}
- (int) gridsize
{
return gridsize;
}
/*
===================
addToScrollRange::
===================
*/
- (id) addToScrollRange: (float)x
: (float)y;
{
if (x < newrect.origin.x) {
newrect.size.width += newrect.origin.x - x;
newrect.origin.x = x;
}
if (y < newrect.origin.y) {
newrect.size.height += newrect.origin.y - y;
newrect.origin.y = y;
}
if (x > newrect.origin.x + newrect.size.width)
newrect.size.width += x - (newrect.origin.x + newrect.size.width);
if (y > newrect.origin.y + newrect.size.height)
newrect.size.height += y - (newrect.origin.y + newrect.size.height);
return self;
}
/*
===================
superviewChanged
===================
*/
- (id) superviewChanged
{
[self newRealBounds: realbounds];
return self;
}
/*
===============================================================================
DRAWING METHODS
===============================================================================
*/
vec3_t cur_linecolor;
NSBezierPath *path;
void
linestart (float r, float g, float b)
{
[path removeAllPoints];
VectorSet (r, g, b, cur_linecolor);
}
void
lineflush (void)
{
if ([path isEmpty])
return;
// endUserPath (upath, dps_ustroke);
[[NSColor colorWithCalibratedRed: cur_linecolor[0]
green: cur_linecolor[1]
blue: cur_linecolor[2]
alpha: 1.0] set];
[path stroke];
[path removeAllPoints];
}
void
linecolor (float r, float g, float b)
{
if (cur_linecolor[0] == r && cur_linecolor[1] == g && cur_linecolor[2] == b)
return; // do nothing
lineflush ();
VectorSet (r, g, b, cur_linecolor);
}
void
XYmoveto (vec3_t pt)
{
NSPoint point = {pt[0], pt[1]};
if ([path elementCount] > 2048)
lineflush ();
[path moveToPoint: point];
}
void
XYlineto (vec3_t pt)
{
NSPoint point = {pt[0], pt[1]};
[path lineToPoint: point];
}
/*
============
drawGrid
Draws tile markings every 64 units, and grid markings at the grid scale if
the grid lines are greater than or equal to 4 pixels apart
Rect is in global world (unscaled) coordinates
============
*/
- (id) drawGrid: (NSRect)rect
{
int x, y, stopx, stopy;
float top, bottom, right, left;
NSMutableDictionary *attribs = [NSMutableDictionary dictionary];
BOOL showcoords;
NSPoint point;
showcoords = [quakeed_i showCoordinates];
left = rect.origin.x - 1;
bottom = rect.origin.y - 1;
right = rect.origin.x + rect.size.width + 2;
top = rect.origin.y + rect.size.height + 2;
[path removeAllPoints];
[path setLineWidth: 0.15];
// grid
//
// can't just divide by grid size because of negetive coordinate
// truncating direction
if (gridsize >= 4 / scale) {
y = floor (bottom / gridsize);
stopy = floor (top / gridsize);
x = floor (left / gridsize);
stopx = floor (right / gridsize);
y *= gridsize;
stopy *= gridsize;
x *= gridsize;
stopx *= gridsize;
if (y < bottom)
y += gridsize;
if (x < left)
x += gridsize;
if (stopx >= right)
stopx -= gridsize;
if (stopy >= top)
stopy -= gridsize;
[path removeAllPoints];
for ( ; y <= stopy; y += gridsize) {
if (y & 63) {
point.x = left;
point.y = y;
[path moveToPoint: point];
point.x = right;
[path lineToPoint: point];
}
}
for ( ; x <= stopx; x += gridsize) {
if (x & 63) {
point.x = x;
point.y = top;
[path moveToPoint: point];
point.y = bottom;
[path lineToPoint: point];
}
}
// endUserPath (upath, dps_ustroke);
[[NSColor colorWithCalibratedRed: 0.8
green: 0.8
blue: 1.0
alpha: 1.0] set]; // thin grid color
[path stroke];
}
// tiles
// for text
[[NSColor colorWithCalibratedWhite: 0.0 / 16.0 alpha: 1.0] set];
if (scale > 4.0 / 64) {
y = floor (bottom / 64);
stopy = floor (top / 64);
x = floor (left / 64);
stopx = floor (right / 64);
y *= 64;
stopy *= 64;
x *= 64;
stopx *= 64;
if (y < bottom)
y += 64;
if (x < left)
x += 64;
if (stopx >= right)
stopx -= 64;
if (stopy >= top)
stopy -= 64;
[path removeAllPoints];
for ( ; y <= stopy; y += 64) {
if (showcoords) {
NSString *s = [NSString stringWithFormat: @"%i", y];
[s drawAtPoint: NSMakePoint (left, y) withAttributes: attribs];
}
[path moveToPoint: point];
point.x = right;
[path lineToPoint: point];
}
for ( ; x <= stopx; x += 64) {
if (showcoords) {
NSString *s = [NSString stringWithFormat: @"%i", x];
[s drawAtPoint: NSMakePoint (x, bottom + 2)
withAttributes: attribs];
}
point.x = x;
point.y = top;
[path moveToPoint: point];
point.y = bottom;
[path lineToPoint: point];
}
// endUserPath (upath, dps_ustroke);
[[NSColor colorWithCalibratedWhite: 12.0 / 16.0 alpha: 1.0] set];
[path stroke];
}
[path setLineWidth: 1];
return self;
}
/*
==================
drawWire
==================
*/
- (id) drawWire: (NSRect)rect
{
NSRect visRect;
int i, j, c, c2;
id ent, brush;
vec3_t mins, maxs;
BOOL drawnames;
NSMutableDictionary *attribs = [NSMutableDictionary dictionary];
drawnames = [quakeed_i showNames];
if ([quakeed_i showCoordinates]) { // coords are showing, update everything
visRect = [self visibleRect];
rect = visRect;
xy_draw_rect = rect;
}
NSRectClip (rect);
// erase window
NSEraseRect (rect);
// draw grid
[self drawGrid: rect];
// draw all entities, world first so entities take priority
linestart (0, 0, 0);
c = [map_i count];
for (i = 0; i < c; i++) {
ent = [map_i objectAtIndex: i];
c2 = [ent count];
for (j = c2 - 1; j >= 0; j--) {
brush = [ent objectAtIndex: j];
if ([brush selected])
continue;
if ([brush regioned])
continue;
[brush XYDrawSelf];
}
if (i > 0 && drawnames) { // draw entity names
brush = [ent objectAtIndex: 0];
if (![brush regioned]) {
const char *classname = [ent valueForQKey: "classname"];
NSString *s = [NSString stringWithCString: classname];
[[NSColor colorWithCalibratedWhite: 0.0 / 16.0 alpha: 1.0]
set];
[brush getMins: mins maxs: maxs];
[s drawAtPoint: NSMakePoint (mins[0], mins[1])
withAttributes: attribs];
}
}
}
lineflush ();
// resize if needed
newrect.origin.x -= gridsize;
newrect.origin.y -= gridsize;
newrect.size.width += 2 * gridsize;
newrect.size.height += 2 * gridsize;
if (!NSEqualRects (newrect, realbounds))
[self newRealBounds: newrect];
return self;
}
/*
=============
drawSolid
=============
*/
- (id) drawSolid
{
const unsigned char *planes[5];
NSRect visRect;
visRect = [self visibleRect];
//
// draw the image into imagebuffer
//
r_origin[0] = visRect.origin.x;
r_origin[1] = visRect.origin.y;
r_origin[2] = scale / 2; // using Z as a scale for the 2D projection
r_width = visRect.size.width * r_origin[2];
r_height = visRect.size.height * r_origin[2];
if (r_width != xywidth || r_height != xyheight) {
xywidth = r_width;
xyheight = r_height;
if (xypicbuffer) {
free (xypicbuffer);
free (xyzbuffer);
}
xypicbuffer = malloc (r_width * (r_height + 1) * 4);
xyzbuffer = malloc (r_width * (r_height + 1) * 4);
}
r_picbuffer = xypicbuffer;
r_zbuffer = xyzbuffer;
REN_BeginXY ();
REN_ClearBuffers ();
//
// render the entities
//
[map_i makeAllPerform: @selector (XYRenderSelf)];
//
// display the output
//
[self lockFocus];
[[self window] setBackingType: NSBackingStoreRetained];
planes[0] = (unsigned char *) r_picbuffer;
NSDrawBitmap (visRect, r_width, r_height, 8, 3, 32, r_width * 4, NO, NO,
NSCalibratedRGBColorSpace, planes);
[[self window] setBackingType: NSBackingStoreBuffered];
[self unlockFocus];
return self;
}
/*
===================
drawSelf
===================
*/
NSRect xy_draw_rect;
- (id) drawRect: (NSRect)rect
{
float drawtime = 0;
if (timedrawing)
drawtime = Sys_DoubleTime ();
xy_draw_rect = rect;
newrect.origin.x = newrect.origin.y = 99999;
newrect.size.width = newrect.size.height = -2 * 99999;
// setup for text
[font set];
if (drawmode == dr_texture || drawmode == dr_flat) {
[quakeed_i xyNoRestore: [self visibleRect]];
[self drawSolid];
} else {
[quakeed_i xyNoRestore: rect];
[self drawWire: rect];
}
if (timedrawing) {
// NSPing ();
drawtime = Sys_DoubleTime () - drawtime;
printf ("CameraView drawtime: %5.3f\n", drawtime);
}
return self;
}
/*
===============================================================================
USER INTERACTION
===============================================================================
*/
/*
================
dragLoop:
================
*/
static NSPoint oldreletive;
- (id) dragFrom: (NSEvent *)startevent
useGrid: (BOOL)ug
callback: (void (*)(float dx, float dy))callback
{
NSEvent *event;
NSPoint startpt, newpt;
NSPoint reletive, delta;
startpt = [startevent locationInWindow];
startpt = [self convertPoint: startpt fromView: NULL];
oldreletive.x = oldreletive.y = 0;
if (ug) {
startpt.x = [self snapToGrid: startpt.x];
startpt.y = [self snapToGrid: startpt.y];
}
while (1) {
unsigned eventMask = NSLeftMouseUpMask | NSLeftMouseDraggedMask
| NSRightMouseUpMask | NSRightMouseDraggedMask
| NSApplicationDefinedMask;
event =
[NSApp nextEventMatchingMask: eventMask untilDate: [NSDate distantFuture
]
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
if ([event type] == NSLeftMouseUp || [event type] == NSRightMouseUp)
break;
if ([event type] == NSApplicationDefined) { // doesn't work.
// grrr.
// [quakeed_i applicationDefined:event];
continue;
}
newpt = [event locationInWindow];
newpt = [self convertPoint: newpt fromView: NULL];
if (ug) {
newpt.x = [self snapToGrid: newpt.x];
newpt.y = [self snapToGrid: newpt.y];
}
reletive.x = newpt.x - startpt.x;
reletive.y = newpt.y - startpt.y;
if (reletive.x == oldreletive.x && reletive.y == oldreletive.y)
continue;
delta.x = reletive.x - oldreletive.x;
delta.y = reletive.y - oldreletive.y;
oldreletive = reletive;
callback (delta.x, delta.y);
}
return self;
}
// ============================================================================
void
DragCallback (float dx, float dy)
{
sb_translate[0] = dx;
sb_translate[1] = dy;
sb_translate[2] = 0;
[map_i makeSelectedPerform: @selector (translate)];
[quakeed_i redrawInstance];
}
- (id) selectionDragFrom: (NSEvent *)theEvent
{
Sys_Printf ("dragging selection\n");
[self dragFrom: theEvent useGrid: YES callback: DragCallback];
[quakeed_i updateAll];
return self;
}
// ============================================================================
void
ScrollCallback (float dx, float dy)
{
NSRect basebounds;
NSPoint neworg;
float scale;
basebounds = [[xyview_i superview] bounds];
[xyview_i convertRect: basebounds fromView: [xyview_i superview]];
neworg.x = basebounds.origin.x - dx;
neworg.y = basebounds.origin.y - dy;
scale = [xyview_i currentScale];
oldreletive.x -= dx;
oldreletive.y -= dy;
[xyview_i setOrigin: neworg scale: scale];
}
- (id) scrollDragFrom: (NSEvent *)theEvent
{
Sys_Printf ("scrolling view\n");
[self dragFrom: theEvent useGrid: YES callback: ScrollCallback];
return self;
}
// ============================================================================
vec3_t direction;
void
DirectionCallback (float dx, float dy)
{
vec3_t org;
float ya;
direction[0] += dx;
direction[1] += dy;
[cameraview_i getOrigin: org];
if (direction[0] == org[0] && direction[1] == org[1])
return;
ya = atan2 (direction[1] - org[1], direction[0] - org[0]);
[cameraview_i setOrigin: org angle: ya];
[quakeed_i newinstance];
[cameraview_i display];
}
- (id) directionDragFrom: (NSEvent *)theEvent
{
NSPoint pt;
Sys_Printf ("changing camera direction\n");
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
direction[0] = pt.x;
direction[1] = pt.y;
DirectionCallback (0, 0);
[self dragFrom: theEvent useGrid: NO callback: DirectionCallback];
return self;
}
// ============================================================================
id newbrush;
vec3_t neworg, newdrag;
void
NewCallback (float dx, float dy)
{
vec3_t min, max;
int i;
newdrag[0] += dx;
newdrag[1] += dy;
for (i = 0; i < 3; i++) {
if (neworg[i] < newdrag[i]) {
min[i] = neworg[i];
max[i] = newdrag[i];
} else {
min[i] = newdrag[i];
max[i] = neworg[i];
}
}
[newbrush setMins: min maxs: max];
[quakeed_i redrawInstance];
}
- (id) newBrushDragFrom: (NSEvent *)theEvent
{
id owner;
texturedef_t td;
NSPoint pt;
Sys_Printf ("sizing new brush\n");
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
neworg[0] = [self snapToGrid: pt.x];
neworg[1] = [self snapToGrid: pt.y];
neworg[2] = [map_i currentMinZ];
newdrag[0] = neworg[0];
newdrag[1] = neworg[1];
newdrag[2] = [map_i currentMaxZ];
owner = [map_i currentEntity];
[texturepalette_i getTextureDef: &td];
newbrush = [[SetBrush alloc] initOwner: owner
mins: neworg
maxs: newdrag
texture: &td];
[owner addObject: newbrush];
[newbrush setSelected: YES];
[self dragFrom: theEvent useGrid: YES callback: NewCallback];
[newbrush removeIfInvalid];
[quakeed_i updateCamera];
return self;
}
// ============================================================================
void
ControlCallback (float dx, float dy)
{
int i;
for (i = 0; i < numcontrolpoints; i++) {
controlpoints[i][0] += dx;
controlpoints[i][1] += dy;
}
[[map_i selectedBrush] calcWindings];
[quakeed_i redrawInstance];
}
- (BOOL) planeDragFrom: (NSEvent *)theEvent
{
NSPoint pt;
vec3_t dragpoint;
if ([map_i numSelected] != 1)
return NO;
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
dragpoint[0] = pt.x;
dragpoint[1] = pt.y;
dragpoint[2] = 2048;
[[map_i selectedBrush] getXYdragface: dragpoint];
if (!numcontrolpoints)
return NO;
Sys_Printf ("dragging brush plane\n");
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
[self dragFrom: theEvent useGrid: YES callback: ControlCallback];
[[map_i selectedBrush] removeIfInvalid];
[quakeed_i updateAll];
return YES;
}
- (BOOL) shearDragFrom: (NSEvent *)theEvent
{
NSPoint pt;
vec3_t dragpoint;
vec3_t p1, p2;
float time;
id br;
int face;
if ([map_i numSelected] != 1)
return NO;
br = [map_i selectedBrush];
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
// if the XY point is inside the brush, make the point on top
p1[0] = pt.x;
p1[1] = pt.y;
VectorCopy (p1, p2);
p1[2] = -2048 * xy_viewnormal[2];
p2[2] = 2048 * xy_viewnormal[2];
VectorCopy (p1, dragpoint);
[br hitByRay: p1 : p2 : &time : &face];
if (time > 0) {
dragpoint[2] = p1[2] + (time - 0.01) * xy_viewnormal[2];
} else {
[br getMins: p1 maxs: p2];
dragpoint[2] = (p1[2] + p2[2]) / 2;
}
[br getXYShearPoints: dragpoint];
if (!numcontrolpoints)
return NO;
Sys_Printf ("dragging brush plane\n");
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
[self dragFrom: theEvent useGrid: YES callback: ControlCallback];
[br removeIfInvalid];
[quakeed_i updateAll];
return YES;
}
/*
===============================================================================
INPUT METHODS
===============================================================================
*/
/*
===================
mouseDown
===================
*/
- (id) mouseDown: (NSEvent *)theEvent
{
NSPoint pt;
id ent;
vec3_t p1, p2;
int flags;
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
p1[0] = p2[0] = pt.x;
p1[1] = p2[1] = pt.y;
p1[2] = xy_viewnormal[2] * -4096;
p2[2] = xy_viewnormal[2] * 4096;
flags =
[theEvent modifierFlags] & (NSShiftKeyMask | NSControlKeyMask |
NSAlternateKeyMask | NSCommandKeyMask);
// shift click to select / deselect a brush from the world
if (flags == NSShiftKeyMask) {
[map_i selectRay: p1: p2: YES];
return self;
}
// cmd-shift click to set a target/targetname entity connection
if (flags == (NSShiftKeyMask | NSCommandKeyMask)) {
[map_i entityConnect: p1: p2];
return self;
}
// bare click to either drag selection, or rubber band a new brush
if (flags == 0) {
// if double click, position Z checker
if ([theEvent clickCount] > 1) {
Sys_Printf ("positioned Z checker\n");
[zview_i setPoint: &pt];
[quakeed_i newinstance];
[quakeed_i updateZ];
return self;
}
// check eye
if ([cameraview_i XYmouseDown: &pt flags: [theEvent modifierFlags]])
return self; // camera move
// check z post
if ([zview_i XYmouseDown: &pt])
return self; // z view move
// check clippers
if ([clipper_i XYDrag: &pt])
return self;
// check single plane dragging
if ([self planeDragFrom: theEvent])
return self;
// check selection
ent = [map_i grabRay: p1 : p2];
if (ent)
return [self selectionDragFrom: theEvent];
if ([map_i numSelected]) {
Sys_Printf ("missed\n");
return self;
}
return [self newBrushDragFrom: theEvent];
}
// control click = position and drag camera
if (flags == NSControlKeyMask) {
[cameraview_i setXYOrigin: &pt];
[quakeed_i newinstance];
[cameraview_i display];
[cameraview_i XYmouseDown: &pt flags: [theEvent modifierFlags]];
return self;
}
// command click = drag Z checker
if (flags == NSCommandKeyMask) {
// check single plane dragging
[self shearDragFrom: theEvent];
return self;
Sys_Printf ("moving Z checker\n");
[zview_i setXYOrigin: &pt];
[quakeed_i updateAll];
[zview_i XYmouseDown: &pt];
return self;
}
// alt click = set entire brush texture
if (flags == NSAlternateKeyMask) {
if (drawmode != dr_texture) {
Sys_Printf ("No texture setting except in texture mode!\n");
NopSound ();
return self;
}
[map_i setTextureRay: p1 : p2 : YES];
[quakeed_i updateAll];
return self;
}
// ctrl-alt click = set single face texture
if (flags == (NSControlKeyMask | NSAlternateKeyMask)) {
if (drawmode != dr_texture) {
Sys_Printf ("No texture setting except in texture mode!\n");
NopSound ();
return self;
}
[map_i setTextureRay: p1 : p2 : NO];
[quakeed_i updateAll];
return self;
}
Sys_Printf ("bad flags for click %x\n", flags);
NopSound ();
return self;
}
/*
===================
rightMouseDown
===================
*/
- (id) rightMouseDown: (NSEvent *)theEvent
{
NSPoint pt;
int flags;
pt = [theEvent locationInWindow];
pt = [self convertPoint: pt fromView: NULL];
flags =
[theEvent modifierFlags] & (NSShiftKeyMask | NSControlKeyMask |
NSAlternateKeyMask | NSCommandKeyMask);
if (flags == NSCommandKeyMask)
return [self scrollDragFrom: theEvent];
if (flags == NSAlternateKeyMask)
return [clipper_i XYClick: pt];
if (flags == 0 || flags == NSControlKeyMask)
return [self directionDragFrom: theEvent];
Sys_Printf ("bad flags for click\n");
NopSound ();
return self;
}
- (NSFont *) scaledFont
{
return scaledFont;
}
@end