QuakeEd.m 17 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. #import "qedefs.h"
  2. id quakeed_i;
  3. id entclasses_i;
  4. id g_cmd_out_i;
  5. BOOL autodirty;
  6. BOOL filter_light, filter_path, filter_entities;
  7. BOOL filter_clip_brushes, filter_water_brushes, filter_world;
  8. BOOL running;
  9. int bsppid;
  10. #if 0
  11. // example command strings
  12. char *fullviscmd = "rsh satan \"/LocalApps/qbsp $1 $2 ; /LocalApps/light $2 ; /LocalApps/vis $2\"";
  13. char *fastviscmd = "rsh satan \"/LocalApps/qbsp $1 $2 ; /LocalApps/light $2 ; /LocalApps/vis -fast $2\"";
  14. char *noviscmd = "rsh satan \"/LocalApps/qbsp $1 $2 ; /LocalApps/light $2\"";
  15. char *relightcmd = "rsh satan \"/LocalApps/light $2\"";
  16. char *leakcmd = "rsh satan \"/LocalApps/qbsp -mark -notjunc $1 $2\"";
  17. #endif
  18. void NopSound (void)
  19. {
  20. NXBeep ();
  21. }
  22. UserPath *upath;
  23. void My_Malloc_Error (int code)
  24. {
  25. // recursive toast Error ("Malloc error: %i\n", code);
  26. write (1, "malloc error!\n", strlen("malloc error!\n")+1);
  27. }
  28. /*
  29. ===============
  30. AutoSave
  31. Every five minutes, save a modified map
  32. ===============
  33. */
  34. void AutoSave(DPSTimedEntry tag, double now, void *userData)
  35. {
  36. // automatic backup
  37. if (autodirty)
  38. {
  39. autodirty = NO;
  40. [map_i writeMapFile: FN_AUTOSAVE useRegion: NO];
  41. }
  42. [map_i writeStats];
  43. }
  44. void DisplayCmdOutput (void)
  45. {
  46. char *buffer;
  47. LoadFile (FN_CMDOUT, (void **)&buffer);
  48. unlink (FN_CMDOUT);
  49. [project_i addToOutput:buffer];
  50. free (buffer);
  51. if ([preferences_i getShowBSP])
  52. [inspcontrol_i changeInspectorTo:i_output];
  53. [preferences_i playBspSound];
  54. NXPing ();
  55. }
  56. /*
  57. ===============
  58. CheckCmdDone
  59. See if the BSP is done
  60. ===============
  61. */
  62. DPSTimedEntry cmdte;
  63. void CheckCmdDone(DPSTimedEntry tag, double now, void *userData)
  64. {
  65. union wait statusp;
  66. struct rusage rusage;
  67. if (!wait4(bsppid, &statusp, WNOHANG, &rusage))
  68. return;
  69. DisplayCmdOutput ();
  70. bsppid = 0;
  71. DPSRemoveTimedEntry( cmdte );
  72. }
  73. //============================================================================
  74. @implementation QuakeEd
  75. /*
  76. ===============
  77. init
  78. ===============
  79. */
  80. - initContent:(const NXRect *)contentRect
  81. style:(int)aStyle
  82. backing:(int)backingType
  83. buttonMask:(int)mask
  84. defer:(BOOL)flag
  85. {
  86. [super initContent:contentRect
  87. style:aStyle
  88. backing:backingType
  89. buttonMask:mask
  90. defer:flag];
  91. [self addToEventMask:
  92. NX_RMOUSEDRAGGEDMASK|NX_LMOUSEDRAGGEDMASK];
  93. malloc_error(My_Malloc_Error);
  94. quakeed_i = self;
  95. dirty = autodirty = NO;
  96. DPSAddTimedEntry(5*60, AutoSave, self, NX_BASETHRESHOLD);
  97. upath = newUserPath ();
  98. return self;
  99. }
  100. - setDefaultFilename
  101. {
  102. strcpy (filename, FN_TEMPSAVE);
  103. [self setTitleAsFilename:filename];
  104. return self;
  105. }
  106. - (BOOL)dirty
  107. {
  108. return dirty;
  109. }
  110. /*
  111. ===============================================================================
  112. DISPLAY UPDATING (handles both camera and XYView)
  113. ===============================================================================
  114. */
  115. BOOL updateinflight;
  116. BOOL clearinstance;
  117. BOOL updatexy;
  118. BOOL updatez;
  119. BOOL updatecamera;
  120. void postappdefined (void)
  121. {
  122. NXEvent ev;
  123. if (updateinflight)
  124. return;
  125. // post an event at the end of the que
  126. ev.type = NX_APPDEFINED;
  127. if (DPSPostEvent(&ev, 0) == -1)
  128. printf ("WARNING: DPSPostEvent: full\n");
  129. //printf ("posted\n");
  130. updateinflight = YES;
  131. }
  132. int c_updateall;
  133. - updateAll // when a model has been changed
  134. {
  135. updatecamera = updatexy = updatez = YES;
  136. c_updateall++;
  137. postappdefined ();
  138. return self;
  139. }
  140. - updateAll:sender
  141. {
  142. [self updateAll];
  143. return self;
  144. }
  145. - updateCamera // when the camera has moved
  146. {
  147. updatecamera = YES;
  148. clearinstance = YES;
  149. postappdefined ();
  150. return self;
  151. }
  152. - updateXY
  153. {
  154. updatexy = YES;
  155. postappdefined ();
  156. return self;
  157. }
  158. - updateZ
  159. {
  160. updatez = YES;
  161. postappdefined ();
  162. return self;
  163. }
  164. - newinstance
  165. {
  166. clearinstance = YES;
  167. return self;
  168. }
  169. - redrawInstance
  170. {
  171. clearinstance = YES;
  172. [self flushWindow];
  173. return self;
  174. }
  175. /*
  176. ===============
  177. flushWindow
  178. instance draw the brush after each flush
  179. ===============
  180. */
  181. -flushWindow
  182. {
  183. [super flushWindow];
  184. if (!running || in_error)
  185. return self; // don't lock focus before nib is finished loading
  186. if (_flushDisabled)
  187. return self;
  188. [cameraview_i lockFocus];
  189. if (clearinstance)
  190. {
  191. PSnewinstance ();
  192. clearinstance = NO;
  193. }
  194. PSsetinstance (1);
  195. linestart (0,0,0);
  196. [map_i makeSelectedPerform: @selector(CameraDrawSelf)];
  197. [clipper_i cameraDrawSelf];
  198. lineflush ();
  199. PSsetinstance (0);
  200. [cameraview_i unlockFocus];
  201. [xyview_i lockFocus];
  202. PSsetinstance (1);
  203. linestart (0,0,0);
  204. [map_i makeSelectedPerform: @selector(XYDrawSelf)];
  205. lineflush ();
  206. [cameraview_i XYDrawSelf];
  207. [zview_i XYDrawSelf];
  208. [clipper_i XYDrawSelf];
  209. PSsetinstance (0);
  210. [xyview_i unlockFocus];
  211. [zview_i lockFocus];
  212. PSsetinstance (1);
  213. [map_i makeSelectedPerform: @selector(ZDrawSelf)];
  214. [cameraview_i ZDrawSelf];
  215. [clipper_i ZDrawSelf];
  216. PSsetinstance (0);
  217. [zview_i unlockFocus];
  218. return self;
  219. }
  220. /*
  221. ==============================================================================
  222. App delegate methods
  223. ==============================================================================
  224. */
  225. - applicationDefined:(NXEvent *)theEvent
  226. {
  227. NXEvent ev, *evp;
  228. updateinflight = NO;
  229. //printf ("serviced\n");
  230. // update screen
  231. evp = [NXApp peekNextEvent:-1 into:&ev];
  232. if (evp)
  233. {
  234. postappdefined();
  235. return self;
  236. }
  237. [self disableFlushWindow];
  238. if ([map_i count] != [entitycount_i intValue])
  239. [entitycount_i setIntValue: [map_i count]];
  240. if ([[map_i currentEntity] count] != [brushcount_i intValue])
  241. [brushcount_i setIntValue: [[map_i currentEntity] count]];
  242. if (updatecamera)
  243. [cameraview_i display];
  244. if (updatexy)
  245. [xyview_i display];
  246. if (updatez)
  247. [zview_i display];
  248. updatecamera = updatexy = updatez = NO;
  249. [self reenableFlushWindow];
  250. [self flushWindow];
  251. // NXPing ();
  252. return self;
  253. }
  254. - appDidInit:sender
  255. {
  256. NXScreen const *screens;
  257. int screencount;
  258. running = YES;
  259. g_cmd_out_i = cmd_out_i; // for qprintf
  260. [preferences_i readDefaults];
  261. [project_i initProject];
  262. [xyview_i setModeRadio: xy_drawmode_i]; // because xy view is inside
  263. // scrollview and can't be
  264. // connected directly in IB
  265. [self setFrameAutosaveName:"EditorWinFrame"];
  266. [self clear: self];
  267. // go to my second monitor
  268. [NXApp getScreens:&screens count:&screencount];
  269. if (screencount == 2)
  270. [self moveTopLeftTo:0 : screens[1].screenBounds.size.height
  271. screen:screens+1];
  272. [self makeKeyAndOrderFront: self];
  273. //[self doOpen: "/raid/quake/id1_/maps/amlev1.map"]; // DEBUG
  274. [map_i newMap];
  275. qprintf ("ready.");
  276. //malloc_debug(-1); // DEBUG
  277. return self;
  278. }
  279. - appWillTerminate:sender
  280. {
  281. // FIXME: save dialog if dirty
  282. return self;
  283. }
  284. //===========================================================================
  285. - textCommand: sender
  286. {
  287. char const *t;
  288. t = [sender stringValue];
  289. if (!strcmp (t, "texname"))
  290. {
  291. texturedef_t *td;
  292. id b;
  293. b = [map_i selectedBrush];
  294. if (!b)
  295. {
  296. qprintf ("nothing selected");
  297. return self;
  298. }
  299. td = [b texturedef];
  300. qprintf (td->texture);
  301. return self;
  302. }
  303. else
  304. qprintf ("Unknown command\n");
  305. return self;
  306. }
  307. - openProject:sender
  308. {
  309. [project_i openProject];
  310. return self;
  311. }
  312. - clear: sender
  313. {
  314. [map_i newMap];
  315. [self updateAll];
  316. [regionbutton_i setIntValue: 0];
  317. [self setDefaultFilename];
  318. return self;
  319. }
  320. - centerCamera: sender
  321. {
  322. NXRect sbounds;
  323. [[xyview_i superview] getBounds: &sbounds];
  324. sbounds.origin.x += sbounds.size.width/2;
  325. sbounds.origin.y += sbounds.size.height/2;
  326. [cameraview_i setXYOrigin: &sbounds.origin];
  327. [self updateAll];
  328. return self;
  329. }
  330. - centerZChecker: sender
  331. {
  332. NXRect sbounds;
  333. [[xyview_i superview] getBounds: &sbounds];
  334. sbounds.origin.x += sbounds.size.width/2;
  335. sbounds.origin.y += sbounds.size.height/2;
  336. [zview_i setPoint: &sbounds.origin];
  337. [self updateAll];
  338. return self;
  339. }
  340. - changeXYLookUp: sender
  341. {
  342. if ([sender intValue])
  343. {
  344. xy_viewnormal[2] = 1;
  345. }
  346. else
  347. {
  348. xy_viewnormal[2] = -1;
  349. }
  350. [self updateAll];
  351. return self;
  352. }
  353. /*
  354. ==============================================================================
  355. REGION MODIFICATION
  356. ==============================================================================
  357. */
  358. /*
  359. ==================
  360. applyRegion:
  361. ==================
  362. */
  363. - applyRegion: sender
  364. {
  365. filter_clip_brushes = [filter_clip_i intValue];
  366. filter_water_brushes = [filter_water_i intValue];
  367. filter_light = [filter_light_i intValue];
  368. filter_path = [filter_path_i intValue];
  369. filter_entities = [filter_entities_i intValue];
  370. filter_world = [filter_world_i intValue];
  371. if (![regionbutton_i intValue])
  372. {
  373. region_min[0] = region_min[1] = region_min[2] = -9999;
  374. region_max[0] = region_max[1] = region_max[2] = 9999;
  375. }
  376. [map_i makeGlobalPerform: @selector(newRegion)];
  377. [self updateAll];
  378. return self;
  379. }
  380. - setBrushRegion: sender
  381. {
  382. id b;
  383. // get the bounds of the current selection
  384. if ([map_i numSelected] != 1)
  385. {
  386. qprintf ("must have a single brush selected");
  387. return self;
  388. }
  389. b = [map_i selectedBrush];
  390. [b getMins: region_min maxs: region_max];
  391. [b remove];
  392. // turn region on
  393. [regionbutton_i setIntValue: 1];
  394. [self applyRegion: self];
  395. return self;
  396. }
  397. - setXYRegion: sender
  398. {
  399. NXRect bounds;
  400. // get xy size
  401. [[xyview_i superview] getBounds: &bounds];
  402. region_min[0] = bounds.origin.x;
  403. region_min[1] = bounds.origin.y;
  404. region_min[2] = -99999;
  405. region_max[0] = bounds.origin.x + bounds.size.width;
  406. region_max[1] = bounds.origin.y + bounds.size.height;
  407. region_max[2] = 99999;
  408. // turn region on
  409. [regionbutton_i setIntValue: 1];
  410. [self applyRegion: self];
  411. return self;
  412. }
  413. //
  414. // UI querie for other objects
  415. //
  416. - (BOOL)showCoordinates
  417. {
  418. return [show_coordinates_i intValue];
  419. }
  420. - (BOOL)showNames
  421. {
  422. return [show_names_i intValue];
  423. }
  424. /*
  425. ==============================================================================
  426. BSP PROCESSING
  427. ==============================================================================
  428. */
  429. void ExpandCommand (char *in, char *out, char *src, char *dest)
  430. {
  431. while (*in)
  432. {
  433. if (in[0] == '$')
  434. {
  435. if (in[1] == '1')
  436. {
  437. strcpy (out, src);
  438. out += strlen(src);
  439. }
  440. else if (in[1] == '2')
  441. {
  442. strcpy (out, dest);
  443. out += strlen(dest);
  444. }
  445. in += 2;
  446. continue;
  447. }
  448. *out++ = *in++;
  449. }
  450. *out = 0;
  451. }
  452. /*
  453. =============
  454. saveBSP
  455. =============
  456. */
  457. - saveBSP:(char *)cmdline dialog:(BOOL)wt
  458. {
  459. char expandedcmd[1024];
  460. char mappath[1024];
  461. char bsppath[1024];
  462. int oldLightFilter;
  463. int oldPathFilter;
  464. char *destdir;
  465. if (bsppid)
  466. {
  467. NXBeep();
  468. return self;
  469. }
  470. //
  471. // turn off the filters so all entities get saved
  472. //
  473. oldLightFilter = [filter_light_i intValue];
  474. oldPathFilter = [filter_path_i intValue];
  475. [filter_light_i setIntValue:0];
  476. [filter_path_i setIntValue:0];
  477. [self applyRegion: self];
  478. if ([regionbutton_i intValue])
  479. {
  480. strcpy (mappath, filename);
  481. StripExtension (mappath);
  482. strcat (mappath, ".reg");
  483. [map_i writeMapFile: mappath useRegion: YES];
  484. wt = YES; // allways pop the dialog on region ops
  485. }
  486. else
  487. strcpy (mappath, filename);
  488. // save the entire thing, just in case there is a problem
  489. [self save: self];
  490. [filter_light_i setIntValue:oldLightFilter];
  491. [filter_path_i setIntValue:oldPathFilter];
  492. [self applyRegion: self];
  493. //
  494. // write the command to the bsp host
  495. //
  496. destdir = [project_i getFinalMapDirectory];
  497. strcpy (bsppath, destdir);
  498. strcat (bsppath, "/");
  499. ExtractFileBase (mappath, bsppath + strlen(bsppath));
  500. strcat (bsppath, ".bsp");
  501. ExpandCommand (cmdline, expandedcmd, mappath, bsppath);
  502. strcat (expandedcmd, " > ");
  503. strcat (expandedcmd, FN_CMDOUT);
  504. strcat (expandedcmd, "\n");
  505. printf ("system: %s", expandedcmd);
  506. [project_i addToOutput: "\n\n========= BUSY =========\n\n"];
  507. [project_i addToOutput: expandedcmd];
  508. if ([preferences_i getShowBSP])
  509. [inspcontrol_i changeInspectorTo:i_output];
  510. if (wt)
  511. {
  512. id panel;
  513. panel = NXGetAlertPanel("BSP In Progress",expandedcmd,NULL,NULL,NULL);
  514. [panel makeKeyAndOrderFront:NULL];
  515. system(expandedcmd);
  516. NXFreeAlertPanel(panel);
  517. [self makeKeyAndOrderFront:NULL];
  518. DisplayCmdOutput ();
  519. }
  520. else
  521. {
  522. cmdte = DPSAddTimedEntry(1, CheckCmdDone, self, NX_BASETHRESHOLD);
  523. if (! (bsppid = fork ()) )
  524. {
  525. system (expandedcmd);
  526. exit (0);
  527. }
  528. }
  529. return self;
  530. }
  531. - BSP_Full: sender
  532. {
  533. [self saveBSP:[project_i getFullVisCmd] dialog: NO];
  534. return self;
  535. }
  536. - BSP_FastVis: sender
  537. {
  538. [self saveBSP:[project_i getFastVisCmd] dialog: NO];
  539. return self;
  540. }
  541. - BSP_NoVis: sender
  542. {
  543. [self saveBSP:[project_i getNoVisCmd] dialog: NO];
  544. return self;
  545. }
  546. - BSP_relight: sender
  547. {
  548. [self saveBSP:[project_i getRelightCmd] dialog: NO];
  549. return self;
  550. }
  551. - BSP_entities: sender
  552. {
  553. [self saveBSP:[project_i getEntitiesCmd] dialog: NO];
  554. return self;
  555. }
  556. - BSP_stop: sender
  557. {
  558. if (!bsppid)
  559. {
  560. NXBeep();
  561. return self;
  562. }
  563. kill (bsppid, 9);
  564. CheckCmdDone (cmdte, 0, NULL);
  565. [project_i addToOutput: "\n\n========= STOPPED =========\n\n"];
  566. return self;
  567. }
  568. /*
  569. ==============
  570. doOpen:
  571. Called by open or the project panel
  572. ==============
  573. */
  574. - doOpen: (char *)fname;
  575. {
  576. strcpy (filename, fname);
  577. [map_i readMapFile:filename];
  578. [regionbutton_i setIntValue: 0];
  579. [self setTitleAsFilename:fname];
  580. [self updateAll];
  581. qprintf ("%s loaded\n", fname);
  582. return self;
  583. }
  584. /*
  585. ==============
  586. open
  587. ==============
  588. */
  589. - open: sender;
  590. {
  591. id openpanel;
  592. static char *suffixlist[] = {"map", 0};
  593. openpanel = [OpenPanel new];
  594. if ( [openpanel
  595. runModalForDirectory: [project_i getMapDirectory]
  596. file: ""
  597. types: suffixlist] != NX_OKTAG)
  598. return self;
  599. [self doOpen: (char *)[openpanel filename]];
  600. return self;
  601. }
  602. /*
  603. ==============
  604. save:
  605. ==============
  606. */
  607. - save: sender;
  608. {
  609. char backup[1024];
  610. // force a name change if using tempname
  611. if (!strcmp (filename, FN_TEMPSAVE) )
  612. return [self saveAs: self];
  613. dirty = autodirty = NO;
  614. strcpy (backup, filename);
  615. StripExtension (backup);
  616. strcat (backup, ".bak");
  617. rename (filename, backup); // copy old to .bak
  618. [map_i writeMapFile: filename useRegion: NO];
  619. return self;
  620. }
  621. /*
  622. ==============
  623. saveAs
  624. ==============
  625. */
  626. - saveAs: sender;
  627. {
  628. id panel_i;
  629. char dir[1024];
  630. panel_i = [SavePanel new];
  631. ExtractFileBase (filename, dir);
  632. [panel_i setRequiredFileType: "map"];
  633. if ( [panel_i runModalForDirectory:[project_i getMapDirectory] file: dir] != NX_OKTAG)
  634. return self;
  635. strcpy (filename, [panel_i filename]);
  636. [self setTitleAsFilename:filename];
  637. [self save: self];
  638. return self;
  639. }
  640. /*
  641. ===============================================================================
  642. OTHER METHODS
  643. ===============================================================================
  644. */
  645. //
  646. // AJR - added this for Project info
  647. //
  648. - (char *)currentFilename
  649. {
  650. return filename;
  651. }
  652. - deselect: sender
  653. {
  654. if ([clipper_i hide]) // first click hides clipper only
  655. return [self updateAll];
  656. [map_i setCurrentEntity: [map_i objectAt: 0]]; // make world selected
  657. [map_i makeSelectedPerform: @selector(deselect)];
  658. [self updateAll];
  659. return self;
  660. }
  661. /*
  662. ===============
  663. keyDown
  664. ===============
  665. */
  666. #define KEY_RIGHTARROW 0xae
  667. #define KEY_LEFTARROW 0xac
  668. #define KEY_UPARROW 0xad
  669. #define KEY_DOWNARROW 0xaf
  670. - keyDown:(NXEvent *)theEvent
  671. {
  672. int ch;
  673. // function keys
  674. switch (theEvent->data.key.keyCode)
  675. {
  676. case 60: // F2
  677. [cameraview_i setDrawMode: dr_wire];
  678. qprintf ("wire draw mode");
  679. return self;
  680. case 61: // F3
  681. [cameraview_i setDrawMode: dr_flat];
  682. qprintf ("flat draw mode");
  683. return self;
  684. case 62: // F4
  685. [cameraview_i setDrawMode: dr_texture];
  686. qprintf ("texture draw mode");
  687. return self;
  688. case 63: // F5
  689. [xyview_i setDrawMode: dr_wire];
  690. qprintf ("wire draw mode");
  691. return self;
  692. case 64: // F6
  693. qprintf ("texture draw mode");
  694. return self;
  695. case 66: // F8
  696. [cameraview_i homeView: self];
  697. return self;
  698. case 88: // F12
  699. [map_i subtractSelection: self];
  700. return self;
  701. case 106: // page up
  702. [cameraview_i upFloor: self];
  703. return self;
  704. case 107: // page down
  705. [cameraview_i downFloor: self];
  706. return self;
  707. case 109: // end
  708. [self deselect: self];
  709. return self;
  710. }
  711. // portable things
  712. ch = tolower(theEvent->data.key.charCode);
  713. switch (ch)
  714. {
  715. case KEY_RIGHTARROW:
  716. case KEY_LEFTARROW:
  717. case KEY_UPARROW:
  718. case KEY_DOWNARROW:
  719. case 'a':
  720. case 'z':
  721. case 'd':
  722. case 'c':
  723. case '.':
  724. case ',':
  725. [cameraview_i _keyDown: theEvent];
  726. break;
  727. case 27: // escape
  728. autodirty = dirty = YES;
  729. [self deselect: self];
  730. return self;
  731. case 127: // delete
  732. autodirty = dirty = YES;
  733. [map_i makeSelectedPerform: @selector(remove)];
  734. [clipper_i hide];
  735. [self updateAll];
  736. break;
  737. case '/':
  738. [clipper_i flipNormal];
  739. [self updateAll];
  740. break;
  741. case 13: // enter
  742. [clipper_i carve];
  743. [self updateAll];
  744. qprintf ("carved brush");
  745. break;
  746. case ' ':
  747. [map_i cloneSelection: self];
  748. break;
  749. //
  750. // move selection keys
  751. //
  752. case '2':
  753. VectorCopy (vec3_origin, sb_translate);
  754. sb_translate[1] = -[xyview_i gridsize];
  755. [map_i makeSelectedPerform: @selector(translate)];
  756. [self updateAll];
  757. break;
  758. case '8':
  759. VectorCopy (vec3_origin, sb_translate);
  760. sb_translate[1] = [xyview_i gridsize];
  761. [map_i makeSelectedPerform: @selector(translate)];
  762. [self updateAll];
  763. break;
  764. case '4':
  765. VectorCopy (vec3_origin, sb_translate);
  766. sb_translate[0] = -[xyview_i gridsize];
  767. [map_i makeSelectedPerform: @selector(translate)];
  768. [self updateAll];
  769. break;
  770. case '6':
  771. VectorCopy (vec3_origin, sb_translate);
  772. sb_translate[0] = [xyview_i gridsize];
  773. [map_i makeSelectedPerform: @selector(translate)];
  774. [self updateAll];
  775. break;
  776. case '-':
  777. VectorCopy (vec3_origin, sb_translate);
  778. sb_translate[2] = -[xyview_i gridsize];
  779. [map_i makeSelectedPerform: @selector(translate)];
  780. [self updateAll];
  781. break;
  782. case '+':
  783. VectorCopy (vec3_origin, sb_translate);
  784. sb_translate[2] = [xyview_i gridsize];
  785. [map_i makeSelectedPerform: @selector(translate)];
  786. [self updateAll];
  787. break;
  788. default:
  789. qprintf ("undefined keypress");
  790. NopSound ();
  791. break;
  792. }
  793. return self;
  794. }
  795. @end