#import "qedefs.h" id zview_i; id zscrollview_i, zscalemenu_i, zscalebutton_i; float zplane; float zplanedir; @implementation ZView /* ================== initFrame: ================== */ - initFrame:(const NXRect *)frameRect { NXPoint pt; origin[0] = 0.333; origin[1] = 0.333; [super initFrame:frameRect]; [self allocateGState]; [self clearBounds]; zview_i = self; scale = 1; // // initialize the pop up menus // zscalemenu_i = [[PopUpList alloc] init]; [zscalemenu_i setTarget: self]; [zscalemenu_i setAction: @selector(scaleMenuTarget:)]; [zscalemenu_i addItem: "12.5%"]; [zscalemenu_i addItem: "25%"]; [zscalemenu_i addItem: "50%"]; [zscalemenu_i addItem: "75%"]; [zscalemenu_i addItem: "100%"]; [zscalemenu_i addItem: "200%"]; [zscalemenu_i addItem: "300%"]; [[zscalemenu_i itemList] selectCellAt: 4 : 0]; zscalebutton_i = NXCreatePopUpListButton(zscalemenu_i); // initialize the scroll view zscrollview_i = [[ZScrollView alloc] initFrame: frameRect button1: zscalebutton_i ]; [zscrollview_i setAutosizing: NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE]; [[zscrollview_i setDocView: self] free]; // [superview setDrawOrigin: 0 : 0]; minheight = 0; maxheight = 64; pt.x = -bounds.size.width; pt.y = -128; [self newRealBounds]; [self setOrigin: &pt scale: 1]; return zscrollview_i; } - setXYOrigin: (NXPoint *)pt { origin[0] = pt->x + 0.333; origin[1] = pt->y + 0.333; return self; } - (float)currentScale { return scale; } /* =================== setOrigin:scale: =================== */ - setOrigin: (NXPoint *)pt scale: (float)sc { NXRect sframe; NXRect newbounds; // // calculate the area visible in the cliprect // scale = sc; [superview getFrame: &sframe]; [superview getFrame: &newbounds]; newbounds.origin = *pt; newbounds.size.width /= scale; newbounds.size.height /= scale; // // union with the realbounds // if (newbounds.origin.y > oldminheight) { newbounds.size.height += newbounds.origin.y - oldminheight; newbounds.origin.y = oldminheight; } if (newbounds.origin.y+newbounds.size.height < oldmaxheight) { newbounds.size.height += oldmaxheight - (newbounds.origin.y + newbounds.size.height); } // // redisplay everything // [quakeed_i disableDisplay]; // // size this view // [self sizeTo: newbounds.size.width : newbounds.size.height]; [self setDrawOrigin: -newbounds.size.width/2 : newbounds.origin.y]; [self moveTo: -newbounds.size.width/2 : newbounds.origin.y]; // // scroll and scale the clip view // [superview setDrawSize : sframe.size.width/scale : sframe.size.height/scale]; [superview setDrawOrigin: pt->x : pt->y]; [quakeed_i reenableDisplay]; [zscrollview_i display]; return self; } /* ==================== scaleMenuTarget: Called when the scaler popup on the window is used ==================== */ - scaleMenuTarget: sender { char const *item; NXRect visrect, sframe; float nscale; item = [[sender selectedCell] title]; sscanf (item,"%f",&nscale); nscale /= 100; if (nscale == scale) return NULL; // keep the center of the view constant [superview getBounds: &visrect]; [superview getFrame: &sframe]; visrect.origin.x += visrect.size.width/2; visrect.origin.y += visrect.size.height/2; visrect.origin.x -= sframe.size.width/2/nscale; visrect.origin.y -= sframe.size.height/2/nscale; [self setOrigin: &visrect.origin scale: nscale]; return self; } - clearBounds { topbound = 999999; bottombound = -999999; return self; } - getBounds: (float *)top :(float *)bottom; { *top = topbound; *bottom = bottombound; return self; } /* ================== addToHeightRange: ================== */ - addToHeightRange: (float)height { if (height < minheight) minheight = height; if (height > maxheight) maxheight = height; return self; } /* ================== newSuperBounds When superview is resized ================== */ - newSuperBounds { oldminheight++; [self newRealBounds]; return self; } /* =================== newRealBounds Should only change the scroll bars, not cause any redraws. If realbounds has shrunk, nothing will change. =================== */ - newRealBounds { NXRect sbounds; float vistop, visbottom; if (minheight == oldminheight && maxheight == oldmaxheight) return self; oldminheight = minheight; oldmaxheight = maxheight; minheight -= 16; maxheight += 16; // // calculate the area visible in the cliprect // [superview getBounds: &sbounds]; visbottom = sbounds.origin.y; vistop = visbottom + sbounds.size.height; if (vistop > maxheight) maxheight = vistop; if (visbottom < minheight) minheight = visbottom; if (minheight == bounds.origin.y && maxheight-minheight == bounds.size.height) return self; sbounds.origin.y = minheight; sbounds.size.height = maxheight - minheight; // // size this view // [quakeed_i disableDisplay]; [self suspendNotifyAncestorWhenFrameChanged:YES]; [self sizeTo: sbounds.size.width : sbounds.size.height]; [self setDrawOrigin: -sbounds.size.width/2 : sbounds.origin.y]; [self moveTo: -sbounds.size.width/2 : sbounds.origin.y]; [self suspendNotifyAncestorWhenFrameChanged:NO]; [[superview superview] reflectScroll: superview]; [quakeed_i reenableDisplay]; [[[[self superview] superview] vertScroller] display]; return self; } /* ============ drawGrid Draws tile markings every 64 units, and grid markings at the grid scale if the grid lines are >= 4 pixels apart Rect is in global world (unscaled) coordinates ============ */ - drawGrid: (const NXRect *)rect { int y, stopy; float top,bottom; int left, right; int gridsize; char text[10]; BOOL showcoords; showcoords = [quakeed_i showCoordinates]; PSsetlinewidth (0); gridsize = [xyview_i gridsize]; left = bounds.origin.x; right = 24; bottom = rect->origin.y-1; top = rect->origin.y+rect->size.height+2; // // 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); y *= gridsize; stopy *= gridsize; if (y= top) stopy -= 32; beginUserPath (upath,NO); for ( ; y<=stopy ; y+= 64) { UPmoveto (upath, left, y); UPlineto (upath, right, y); } endUserPath (upath, dps_ustroke); PSsetgray (12.0/16.0); sendUserPath (upath); // // tiles // y = floor(bottom/64); stopy = floor(top/64); y *= 64; stopy *= 64; if (y= top) stopy -= 64; beginUserPath (upath,NO); PSsetgray (0); // for text PSselectfont("Helvetica-Medium",10/scale); PSrotate(0); for ( ; y<=stopy ; y+= 64) { if (showcoords) { sprintf (text, "%i",y); PSmoveto(left,y); PSshow(text); } UPmoveto (upath, left+24, y); UPlineto (upath, right, y); } // divider UPmoveto (upath, 0, bounds.origin.y); UPlineto (upath, 0, bounds.origin.y + bounds.size.height); endUserPath (upath, dps_ustroke); PSsetgray (10.0/16.0); sendUserPath (upath); // // origin // PSsetlinewidth (5); PSsetgray (4.0/16.0); PSmoveto (right,0); PSlineto (left,0); PSstroke (); PSsetlinewidth (0.15); return self; } - drawZplane { PSsetrgbcolor (0.2, 0.2, 0); PSarc (0, zplane, 4, 0, M_PI*2); PSfill (); return self; } /* =============================================================================== drawSelf =============================================================================== */ - drawSelf:(const NXRect *)rects :(int)rectCount { NXRect visRect; minheight = 999999; maxheight = -999999; // always draw the entire bar [self getVisibleRect:&visRect]; rects = &visRect; // erase window NXEraseRect (&rects[0]); // draw grid [self drawGrid: &rects[0]]; // draw zplane // [self drawZplane]; // draw all entities [map_i makeUnselectedPerform: @selector(ZDrawSelf)]; // possibly resize the view [self newRealBounds]; return self; } /* ============== XYDrawSelf ============== */ - XYDrawSelf { PSsetrgbcolor (0,0.5,1.0); PSsetlinewidth (0.15); PSmoveto (origin[0]-16, origin[1]-16); PSrlineto (32,32); PSmoveto (origin[0]-16, origin[1]+16); PSrlineto (32,-32); PSstroke (); return self; } /* ============== getPoint: (NXPoint *)pt ============== */ - getPoint: (NXPoint *)pt { pt->x = origin[0] + 0.333; // offset a bit to avoid edge cases pt->y = origin[1] + 0.333; return self; } - setPoint: (NXPoint *)pt { origin[0] = pt->x; origin[1] = pt->y; return self; } /* ============================================================================== MOUSE CLICKING ============================================================================== */ /* ================ dragLoop: ================ */ static NXPoint oldreletive; - dragFrom: (NXEvent *)startevent useGrid: (BOOL)ug callback: (void (*) (float dy)) callback { NXEvent *event; NXPoint startpt, newpt; NXPoint reletive, delta; int gridsize; gridsize = [xyview_i gridsize]; startpt = startevent->location; [self convertPoint:&startpt fromView:NULL]; oldreletive.x = oldreletive.y = 0; while (1) { event = [NXApp getNextEvent: NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK | NX_RMOUSEUPMASK | NX_RMOUSEDRAGGEDMASK]; if (event->type == NX_LMOUSEUP || event->type == NX_RMOUSEUP) break; newpt = event->location; [self convertPoint:&newpt fromView:NULL]; reletive.y = newpt.y - startpt.y; if (ug) { // we want truncate towards 0 behavior here reletive.y = gridsize * (int)(reletive.y / gridsize); } if (reletive.y == oldreletive.y) continue; delta.y = reletive.y - oldreletive.y; oldreletive = reletive; callback (delta.y); } return self; } //============================================================================ void ZDragCallback (float dy) { sb_translate[0] = 0; sb_translate[1] = 0; sb_translate[2] = dy; [map_i makeSelectedPerform: @selector(translate)]; [quakeed_i redrawInstance]; } - selectionDragFrom: (NXEvent*)theEvent { qprintf ("dragging selection"); [self dragFrom: theEvent useGrid: YES callback: ZDragCallback ]; [quakeed_i updateCamera]; qprintf (""); return self; } //============================================================================ void ZScrollCallback (float dy) { NXRect basebounds; NXPoint neworg; float scale; [ [zview_i superview] getBounds: &basebounds]; [zview_i convertRectFromSuperview: &basebounds]; neworg.y = basebounds.origin.y - dy; scale = [zview_i currentScale]; oldreletive.y -= dy; [zview_i setOrigin: &neworg scale: scale]; } - scrollDragFrom: (NXEvent*)theEvent { qprintf ("scrolling view"); [self dragFrom: theEvent useGrid: YES callback: ZScrollCallback ]; qprintf (""); return self; } //============================================================================ void ZControlCallback (float dy) { int i; for (i=0 ; ilocation; [self convertPoint:&pt fromView:NULL]; dragpoint[0] = origin[0]; dragpoint[1] = origin[1]; dragpoint[2] = pt.y; [[map_i selectedBrush] getZdragface: dragpoint]; if (!numcontrolpoints) return NO; qprintf ("dragging brush plane"); pt= theEvent->location; [self convertPoint:&pt fromView:NULL]; [self dragFrom: theEvent useGrid: YES callback: ZControlCallback ]; [[map_i selectedBrush] removeIfInvalid]; [quakeed_i updateCamera]; qprintf (""); return YES; } //============================================================================ /* =================== mouseDown =================== */ - mouseDown:(NXEvent *)theEvent { NXPoint pt; int flags; vec3_t p1; pt= theEvent->location; [self convertPoint:&pt fromView:NULL]; p1[0] = origin[0]; p1[1] = origin[1]; p1[2] = pt.y; flags = theEvent->flags & (NX_SHIFTMASK | NX_CONTROLMASK | NX_ALTERNATEMASK | NX_COMMANDMASK); // // shift click to select / deselect a brush from the world // if (flags == NX_SHIFTMASK) { [map_i selectRay: p1 : p1 : NO]; return self; } // // alt click = set entire brush texture // if (flags == NX_ALTERNATEMASK) { [map_i setTextureRay: p1 : p1 : YES]; return self; } // // control click = position view // if (flags == NX_CONTROLMASK) { [cameraview_i setZOrigin: pt.y]; [quakeed_i updateAll]; [cameraview_i ZmouseDown: &pt flags:theEvent->flags]; return self; } // // bare click to drag icons or new brush drag // if ( flags == 0 ) { // check eye if ( [cameraview_i ZmouseDown: &pt flags:theEvent->flags] ) return self; if ([map_i numSelected]) { if ( pt.x > 0) { if ([self planeDragFrom: theEvent]) return self; } [self selectionDragFrom: theEvent]; return self; } } qprintf ("bad flags for click"); NopSound (); return self; } /* =================== rightMouseDown =================== */ - rightMouseDown:(NXEvent *)theEvent { NXPoint pt; int flags; pt= theEvent->location; [self convertPoint:&pt fromView:NULL]; flags = theEvent->flags & (NX_SHIFTMASK | NX_CONTROLMASK | NX_ALTERNATEMASK | NX_COMMANDMASK); // // click = scroll view // if (flags == 0) { return [self scrollDragFrom: theEvent]; } qprintf ("bad flags for click"); NopSound (); return self; } /* =============================================================================== XY mouse view methods =============================================================================== */ /* ================ modalMoveLoop ================ */ - modalMoveLoop: (NXPoint *)basept :(vec3_t)movemod : converter { vec3_t originbase; NXEvent *event; NXPoint newpt; vec3_t delta; int i; VectorCopy (origin, originbase); // // modal event loop using instance drawing // goto drawentry; while (event->type != NX_LMOUSEUP) { // // calculate new point // newpt = event->location; [converter convertPoint:&newpt fromView:NULL]; delta[0] = newpt.x-basept->x; delta[1] = newpt.y-basept->y; delta[2] = delta[1]; // height change for (i=0 ; i<3 ; i++) origin[i] = originbase[i]+movemod[i]*delta[i]; drawentry: // // instance draw new frame // [quakeed_i newinstance]; [self display]; NXPing (); event = [NXApp getNextEvent: NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK]; } // // draw the brush back into the window buffer // // [xyview_i display]; return self; } /* =============== XYmouseDown =============== */ - (BOOL)XYmouseDown: (NXPoint *)pt { vec3_t movemod; if (fabs(pt->x - origin[0]) > 16 || fabs(pt->y - origin[1]) > 16) return NO; movemod[0] = 1; movemod[1] = 1; movemod[2] = 0; [self modalMoveLoop: pt : movemod : xyview_i]; return YES; } @end