]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/xywindow.cpp
always use courier new 8 on win32 again
[xonotic/netradiant.git] / radiant / xywindow.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 //
23 // XY Window
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 #include "xywindow.h"
29
30 #include "debugging/debugging.h"
31
32 #include "ientity.h"
33 #include "igl.h"
34 #include "ibrush.h"
35 #include "iundo.h"
36 #include "iimage.h"
37 #include "ifilesystem.h"
38 #include "os/path.h"
39 #include "image.h"
40 #include "gtkutil/messagebox.h"
41
42 #include <gtk/gtklabel.h>
43 #include <gtk/gtkmenuitem.h>
44
45 #include "generic/callback.h"
46 #include "string/string.h"
47 #include "stream/stringstream.h"
48
49 #include "scenelib.h"
50 #include "eclasslib.h"
51 #include "renderer.h"
52 #include "moduleobserver.h"
53
54 #include "gtkutil/menu.h"
55 #include "gtkutil/container.h"
56 #include "gtkutil/widget.h"
57 #include "gtkutil/glwidget.h"
58 #include "gtkutil/filechooser.h"
59 #include "gtkmisc.h"
60 #include "select.h"
61 #include "csg.h"
62 #include "brushmanip.h"
63 #include "selection.h"
64 #include "entity.h"
65 #include "camwindow.h"
66 #include "texwindow.h"
67 #include "mainframe.h"
68 #include "preferences.h"
69 #include "commands.h"
70 #include "feedback.h"
71 #include "grid.h"
72 #include "windowobservers.h"
73
74 void LoadTextureRGBA(qtexture_t* q, unsigned char* pPixels, int nWidth, int nHeight);
75
76 // d1223m
77 extern bool g_brush_always_caulk;
78
79 //!\todo Rewrite.
80 class ClipPoint
81 {
82 public:
83   Vector3 m_ptClip;      // the 3d point
84   bool m_bSet;
85
86   ClipPoint()
87   {
88     Reset();
89   };
90   void Reset()
91   {
92     m_ptClip[0] = m_ptClip[1] = m_ptClip[2] = 0.0;
93     m_bSet = false;
94   }
95   bool Set()
96   {
97     return m_bSet;
98   }
99   void Set(bool b)
100   {
101     m_bSet = b;
102   }
103   operator Vector3&()
104   {
105     return m_ptClip;
106   };
107
108   /*! Draw clip/path point with rasterized number label */
109   void Draw(int num, float scale);
110   /*! Draw clip/path point with rasterized string label */
111   void Draw(const char *label, float scale);
112 };
113
114 VIEWTYPE g_clip_viewtype;
115 bool g_bSwitch = true;
116 bool g_clip_useCaulk = false;
117 ClipPoint g_Clip1;
118 ClipPoint g_Clip2;
119 ClipPoint g_Clip3;
120 ClipPoint* g_pMovingClip = 0;
121
122 /* Drawing clip points */
123 void ClipPoint::Draw(int num, float scale)
124 {
125   StringOutputStream label(4);
126   label << num;
127   Draw(label.c_str(), scale);
128 }
129
130 void ClipPoint::Draw(const char *label, float scale)
131 {
132   // draw point
133   glPointSize (4);
134   glColor3fv(vector3_to_array(g_xywindow_globals.color_clipper));
135   glBegin (GL_POINTS);
136   glVertex3fv(vector3_to_array(m_ptClip));
137   glEnd();
138   glPointSize (1);
139
140   float offset = 2.0f / scale;
141
142   // draw label
143   glRasterPos3f (m_ptClip[0] + offset, m_ptClip[1] + offset, m_ptClip[2] + offset);
144   glCallLists (GLsizei(strlen(label)), GL_UNSIGNED_BYTE, label);
145 }
146
147 float fDiff(float f1, float f2)
148 {
149   if (f1 > f2)
150     return f1 - f2;
151   else
152     return f2 - f1;
153 }
154
155 inline double ClipPoint_Intersect(const ClipPoint& clip, const Vector3& point, VIEWTYPE viewtype, float scale)
156 {
157   int nDim1 = (viewtype == YZ) ? 1 : 0;
158   int nDim2 = (viewtype == XY) ? 1 : 2;
159   double screenDistanceSquared(vector2_length_squared(Vector2(fDiff(clip.m_ptClip[nDim1], point[nDim1]) * scale, fDiff(clip.m_ptClip[nDim2], point[nDim2])  * scale)));
160   if(screenDistanceSquared < 8*8)
161   {
162     return screenDistanceSquared;
163   }
164   return FLT_MAX;
165 }
166
167 inline void ClipPoint_testSelect(ClipPoint& clip, const Vector3& point, VIEWTYPE viewtype, float scale, double& bestDistance, ClipPoint*& bestClip)
168 {
169   if(clip.Set())
170   {
171     double distance = ClipPoint_Intersect(clip, point, viewtype, scale);
172     if(distance < bestDistance)
173     {
174       bestDistance = distance;
175       bestClip = &clip;
176     }
177   }
178 }
179
180 inline ClipPoint* GlobalClipPoints_Find(const Vector3& point, VIEWTYPE viewtype, float scale)
181 {
182   double bestDistance = FLT_MAX;
183   ClipPoint* bestClip = 0;
184   ClipPoint_testSelect(g_Clip1, point, viewtype, scale, bestDistance, bestClip);
185   ClipPoint_testSelect(g_Clip2, point, viewtype, scale, bestDistance, bestClip);
186   ClipPoint_testSelect(g_Clip3, point, viewtype, scale, bestDistance, bestClip);
187   return bestClip;
188 }
189
190 inline void GlobalClipPoints_Draw(float scale)
191 {
192   // Draw clip points
193   if (g_Clip1.Set())
194     g_Clip1.Draw(1, scale);
195   if (g_Clip2.Set())
196     g_Clip2.Draw(2, scale);
197   if (g_Clip3.Set())
198     g_Clip3.Draw(3, scale);
199 }
200
201 inline bool GlobalClipPoints_valid()
202 {
203   return g_Clip1.Set() && g_Clip2.Set();
204 }
205
206 void PlanePointsFromClipPoints(Vector3 planepts[3], const AABB& bounds, int viewtype)
207 {
208   ASSERT_MESSAGE(GlobalClipPoints_valid(), "clipper points not initialised");
209   planepts[0] = g_Clip1.m_ptClip;
210         planepts[1] = g_Clip2.m_ptClip;
211         planepts[2] = g_Clip3.m_ptClip;
212   Vector3 maxs(vector3_added(bounds.origin, bounds.extents));
213   Vector3 mins(vector3_subtracted(bounds.origin, bounds.extents));
214         if(!g_Clip3.Set())
215         {
216                 int n = (viewtype == XY) ? 2 : (viewtype == YZ) ? 0 : 1;
217                 int x = (n == 0) ? 1 : 0;
218                 int y = (n == 2) ? 1 : 2;
219
220                 if (n == 1) // on viewtype XZ, flip clip points
221                 {
222                   planepts[0][n] = maxs[n];
223                   planepts[1][n] = maxs[n];
224                   planepts[2][x] = g_Clip1.m_ptClip[x];
225                   planepts[2][y] = g_Clip1.m_ptClip[y];
226                   planepts[2][n] = mins[n];
227                 }
228                 else
229                 {
230                   planepts[0][n] = mins[n];
231                   planepts[1][n] = mins[n];
232                   planepts[2][x] = g_Clip1.m_ptClip[x];
233                   planepts[2][y] = g_Clip1.m_ptClip[y];
234                   planepts[2][n] = maxs[n];
235                 }
236         }
237 }
238
239 void Clip_Update()
240 {
241   Vector3 planepts[3];
242   if(!GlobalClipPoints_valid())
243   {
244     planepts[0] = Vector3(0, 0, 0);
245           planepts[1] = Vector3(0, 0, 0);
246           planepts[2] = Vector3(0, 0, 0);
247     Scene_BrushSetClipPlane(GlobalSceneGraph(), Plane3(0, 0, 0, 0));
248   }
249   else
250   {
251     AABB bounds(Vector3(0, 0, 0), Vector3(64, 64, 64));
252     PlanePointsFromClipPoints(planepts, bounds, g_clip_viewtype);
253     if(g_bSwitch)
254     {
255       std::swap(planepts[0], planepts[1]);
256     }
257     Scene_BrushSetClipPlane(GlobalSceneGraph(), plane3_for_points(planepts[0], planepts[1], planepts[2]));
258   }
259   ClipperChangeNotify();
260 }
261
262 const char* Clip_getShader()
263 {
264   return g_clip_useCaulk ? "textures/common/caulk" : TextureBrowser_GetSelectedShader(GlobalTextureBrowser());
265 }
266
267 void Clip()
268 {
269   if (ClipMode() && GlobalClipPoints_valid())
270   {
271     Vector3 planepts[3];
272     AABB bounds(Vector3(0, 0, 0), Vector3(64, 64, 64));
273     PlanePointsFromClipPoints(planepts, bounds, g_clip_viewtype);
274     Scene_BrushSplitByPlane(GlobalSceneGraph(), planepts[0], planepts[1], planepts[2], Clip_getShader(), (!g_bSwitch) ? eFront : eBack);
275     g_Clip1.Reset();
276     g_Clip2.Reset();
277     g_Clip3.Reset();
278     Clip_Update();
279     ClipperChangeNotify();
280   }
281 }
282
283 void SplitClip()
284 {
285   if (ClipMode() && GlobalClipPoints_valid())
286   {
287     Vector3 planepts[3];
288     AABB bounds(Vector3(0, 0, 0), Vector3(64, 64, 64));
289     PlanePointsFromClipPoints(planepts, bounds, g_clip_viewtype);
290     Scene_BrushSplitByPlane(GlobalSceneGraph(), planepts[0], planepts[1], planepts[2], Clip_getShader(), eFrontAndBack);
291     g_Clip1.Reset();
292     g_Clip2.Reset();
293     g_Clip3.Reset();
294     Clip_Update();
295     ClipperChangeNotify();
296   }
297 }
298
299 void FlipClip()
300 {
301   g_bSwitch = !g_bSwitch;
302   Clip_Update();
303   ClipperChangeNotify();
304 }
305
306 void OnClipMode(bool enabled)
307 {
308   g_Clip1.Reset();
309   g_Clip2.Reset();
310   g_Clip3.Reset();
311
312   if(!enabled && g_pMovingClip)
313   {
314     g_pMovingClip = 0;
315   }
316
317   Clip_Update();
318   ClipperChangeNotify();
319 }
320
321 bool ClipMode()
322 {
323   return GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip;
324 }
325
326 void NewClipPoint(const Vector3& point)
327 {
328   if (g_Clip1.Set() == false)
329   {
330     g_Clip1.m_ptClip = point;
331     g_Clip1.Set(true);
332   }
333   else if (g_Clip2.Set() == false)
334   {
335     g_Clip2.m_ptClip = point;
336     g_Clip2.Set(true);
337   }
338   else if (g_Clip3.Set() == false)
339   {
340     g_Clip3.m_ptClip = point;
341     g_Clip3.Set(true);
342   }
343   else
344   {
345     g_Clip1.Reset();
346     g_Clip2.Reset();
347     g_Clip3.Reset();
348     g_Clip1.m_ptClip = point;
349     g_Clip1.Set(true);
350   }
351
352   Clip_Update();
353   ClipperChangeNotify();
354 }
355
356
357
358 struct xywindow_globals_private_t
359 {
360   bool  d_showgrid;
361
362   // these are in the View > Show menu with Show coordinates
363   bool  show_names;
364   bool  show_coordinates;
365   bool  show_angles;
366   bool  show_outline;
367   bool  show_axis;
368
369   bool d_show_work;
370
371   bool     show_blocks;
372   int                  blockSize;
373
374   bool m_bCamXYUpdate;
375   bool m_bChaseMouse;
376   bool m_bSizePaint;
377
378   xywindow_globals_private_t() :
379     d_showgrid(true),
380
381     show_names(false),
382     show_coordinates(true),
383     show_angles(true),
384     show_outline(false),
385     show_axis(true),
386
387     d_show_work(false),
388
389     show_blocks(false),
390
391     m_bCamXYUpdate(true),
392     m_bChaseMouse(true),
393     m_bSizePaint(false)
394   {
395   }
396
397 };
398
399 xywindow_globals_t g_xywindow_globals;
400 xywindow_globals_private_t g_xywindow_globals_private;
401
402 const unsigned int RAD_NONE =    0x00;
403 const unsigned int RAD_SHIFT =   0x01;
404 const unsigned int RAD_ALT =     0x02;
405 const unsigned int RAD_CONTROL = 0x04;
406 const unsigned int RAD_PRESS   = 0x08;
407 const unsigned int RAD_LBUTTON = 0x10;
408 const unsigned int RAD_MBUTTON = 0x20;
409 const unsigned int RAD_RBUTTON = 0x40;
410
411 inline ButtonIdentifier button_for_flags(unsigned int flags)
412 {
413   if(flags & RAD_LBUTTON)
414     return c_buttonLeft;
415   if(flags & RAD_RBUTTON)
416     return c_buttonRight;
417   if(flags & RAD_MBUTTON)
418     return c_buttonMiddle;
419   return c_buttonInvalid;
420 }
421
422 inline ModifierFlags modifiers_for_flags(unsigned int flags)
423 {
424   ModifierFlags modifiers = c_modifierNone;
425   if(flags & RAD_SHIFT)
426     modifiers |= c_modifierShift;
427   if(flags & RAD_CONTROL)
428     modifiers |= c_modifierControl;
429   if(flags & RAD_ALT)
430     modifiers |= c_modifierAlt;
431   return modifiers;
432 }
433
434 inline unsigned int buttons_for_button_and_modifiers(ButtonIdentifier button, ModifierFlags flags)
435 {
436   unsigned int buttons = 0;
437
438   switch (button.get())
439   {
440   case ButtonEnumeration::LEFT: buttons |= RAD_LBUTTON; break;
441   case ButtonEnumeration::MIDDLE: buttons |= RAD_MBUTTON; break;
442   case ButtonEnumeration::RIGHT: buttons |= RAD_RBUTTON; break;
443   }
444
445   if(bitfield_enabled(flags, c_modifierControl))
446     buttons |= RAD_CONTROL;
447
448   if(bitfield_enabled(flags, c_modifierShift))
449     buttons |= RAD_SHIFT;
450
451   if(bitfield_enabled(flags, c_modifierAlt))
452     buttons |= RAD_ALT;
453
454   return buttons;
455 }
456
457 inline unsigned int buttons_for_event_button(GdkEventButton* event)
458 {
459   unsigned int flags = 0;
460
461   switch (event->button)
462   {
463   case 1: flags |= RAD_LBUTTON; break;
464   case 2: flags |= RAD_MBUTTON; break;
465   case 3: flags |= RAD_RBUTTON; break;
466   }
467
468   if ((event->state & GDK_CONTROL_MASK) != 0)
469     flags |= RAD_CONTROL;
470
471   if ((event->state & GDK_SHIFT_MASK) != 0)
472     flags |= RAD_SHIFT;
473
474   if((event->state & GDK_MOD1_MASK) != 0)
475     flags |= RAD_ALT;
476
477   return flags;
478 }
479
480 inline unsigned int buttons_for_state(guint state)
481 {
482   unsigned int flags = 0;
483
484   if ((state & GDK_BUTTON1_MASK) != 0)
485     flags |= RAD_LBUTTON;
486
487   if ((state & GDK_BUTTON2_MASK) != 0)
488     flags |= RAD_MBUTTON;
489
490   if ((state & GDK_BUTTON3_MASK) != 0)
491     flags |= RAD_RBUTTON;
492
493   if ((state & GDK_CONTROL_MASK) != 0)
494     flags |= RAD_CONTROL;
495
496   if ((state & GDK_SHIFT_MASK) != 0)
497     flags |= RAD_SHIFT;
498
499   if ((state & GDK_MOD1_MASK) != 0)
500     flags |= RAD_ALT;
501
502   return flags;
503 }
504
505
506 void XYWnd::SetScale(float f)
507 {
508   m_fScale = f;
509   updateProjection();
510   updateModelview();
511   XYWnd_Update(*this);
512 }
513
514 void XYWnd_ZoomIn(XYWnd* xy)
515 {
516   float max_scale = 64;
517   float scale = xy->Scale() * 5.0f / 4.0f;
518   if(scale > max_scale)
519   {
520     if(xy->Scale() != max_scale)
521     {
522       xy->SetScale (max_scale);
523     }
524   }
525   else
526   {
527     xy->SetScale(scale);
528   }
529 }
530
531
532 // NOTE: the zoom out factor is 4/5, we could think about customizing it
533 //  we don't go below a zoom factor corresponding to 10% of the max world size
534 //  (this has to be computed against the window size)
535 void XYWnd_ZoomOut(XYWnd* xy)
536 {
537   float min_scale = MIN(xy->Width(),xy->Height()) / ( 1.1f * (g_MaxWorldCoord-g_MinWorldCoord));
538   float scale = xy->Scale() * 4.0f / 5.0f;
539   if(scale < min_scale)
540   {
541     if(xy->Scale() != min_scale)
542     {
543       xy->SetScale (min_scale);
544     }
545   }
546   else
547   {
548     xy->SetScale(scale);
549   }
550 }
551
552 VIEWTYPE GlobalXYWnd_getCurrentViewType()
553 {
554   ASSERT_NOTNULL(g_pParentWnd);
555   ASSERT_NOTNULL(g_pParentWnd->ActiveXY());
556   return g_pParentWnd->ActiveXY()->GetViewType();
557 }
558
559 // =============================================================================
560 // variables
561
562 bool g_bCrossHairs = false;
563
564 GtkMenu* XYWnd::m_mnuDrop = 0;
565
566 // this is disabled, and broken
567 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=394
568 #if 0
569 void WXY_Print()
570 {
571   long width, height;
572   width = g_pParentWnd->ActiveXY()->Width();
573   height = g_pParentWnd->ActiveXY()->Height();
574   unsigned char* img;
575   const char* filename;
576
577   filename = file_dialog(GTK_WIDGET(MainFrame_getWindow()), FALSE, "Save Image", 0, FILTER_BMP);
578   if (!filename)
579     return;
580
581   g_pParentWnd->ActiveXY()->MakeCurrent();
582   img = (unsigned char*)malloc (width*height*3);
583   glReadPixels (0,0,width,height,GL_RGB,GL_UNSIGNED_BYTE,img);
584
585   FILE *fp;
586   fp = fopen(filename, "wb");
587   if (fp)
588   {
589     unsigned short bits;
590     unsigned long cmap, bfSize;
591
592     bits = 24;
593     cmap = 0;
594     bfSize = 54 + width*height*3;
595
596     long byteswritten = 0;
597     long pixoff = 54 + cmap*4;
598     short res = 0;
599     char m1 ='B', m2 ='M';
600     fwrite(&m1, 1, 1, fp);      byteswritten++; // B
601     fwrite(&m2, 1, 1, fp);      byteswritten++; // M
602     fwrite(&bfSize, 4, 1, fp);  byteswritten+=4;// bfSize
603     fwrite(&res, 2, 1, fp);     byteswritten+=2;// bfReserved1
604     fwrite(&res, 2, 1, fp);     byteswritten+=2;// bfReserved2
605     fwrite(&pixoff, 4, 1, fp);  byteswritten+=4;// bfOffBits
606
607     unsigned long biSize = 40, compress = 0, size = 0;
608     long pixels = 0;
609     unsigned short planes = 1;
610     fwrite(&biSize, 4, 1, fp);  byteswritten+=4;// biSize
611     fwrite(&width, 4, 1, fp);   byteswritten+=4;// biWidth
612     fwrite(&height, 4, 1, fp);  byteswritten+=4;// biHeight
613     fwrite(&planes, 2, 1, fp);  byteswritten+=2;// biPlanes
614     fwrite(&bits, 2, 1, fp);    byteswritten+=2;// biBitCount
615     fwrite(&compress, 4, 1, fp);byteswritten+=4;// biCompression
616     fwrite(&size, 4, 1, fp);    byteswritten+=4;// biSizeImage
617     fwrite(&pixels, 4, 1, fp);  byteswritten+=4;// biXPelsPerMeter
618     fwrite(&pixels, 4, 1, fp);  byteswritten+=4;// biYPelsPerMeter
619     fwrite(&cmap, 4, 1, fp);    byteswritten+=4;// biClrUsed
620     fwrite(&cmap, 4, 1, fp);    byteswritten+=4;// biClrImportant
621
622     unsigned long widthDW = (((width*24) + 31) / 32 * 4);
623     long row, row_size = width*3;
624     for (row = 0; row < height; row++)
625     {
626         unsigned char* buf = img+row*row_size;
627
628       // write a row
629       int col;
630       for (col = 0; col < row_size; col += 3)
631         {
632           putc(buf[col+2], fp);
633           putc(buf[col+1], fp);
634           putc(buf[col], fp);
635         }
636       byteswritten += row_size;
637
638       unsigned long count;
639       for (count = row_size; count < widthDW; count++)
640         {
641         putc(0, fp);    // dummy
642           byteswritten++;
643         }
644     }
645
646     fclose(fp);
647   }
648
649   free (img);
650 }
651 #endif
652
653
654 #include "timer.h"
655
656 Timer g_chasemouse_timer;
657
658 void XYWnd::ChaseMouse()
659 {
660   float multiplier = g_chasemouse_timer.elapsed_msec() / 10.0f;
661   Scroll(float_to_integer(multiplier * m_chasemouse_delta_x), float_to_integer(multiplier * -m_chasemouse_delta_y));
662
663   //globalOutputStream() << "chasemouse: multiplier=" << multiplier << " x=" << m_chasemouse_delta_x << " y=" << m_chasemouse_delta_y << '\n';
664
665   XY_MouseMoved(m_chasemouse_current_x, m_chasemouse_current_y , getButtonState());
666   g_chasemouse_timer.start();
667 }
668
669 gboolean xywnd_chasemouse(gpointer data)
670 {
671   reinterpret_cast<XYWnd*>(data)->ChaseMouse();
672   return TRUE;
673 }
674
675 inline const int& min_int(const int& left, const int& right)
676 {
677   return std::min(left, right);
678 }
679
680 bool XYWnd::chaseMouseMotion(int pointx, int pointy)
681 {
682   m_chasemouse_delta_x = 0;
683   m_chasemouse_delta_y = 0;
684
685   if (g_xywindow_globals_private.m_bChaseMouse && getButtonState() == RAD_LBUTTON)
686   {
687     const int epsilon = 16;
688
689     if (pointx < epsilon)
690     {
691       m_chasemouse_delta_x = std::max(pointx, 0) - epsilon;
692     }
693     else if ((pointx - m_nWidth) > -epsilon)
694     {
695       m_chasemouse_delta_x = min_int((pointx - m_nWidth), 0) + epsilon;
696     }
697
698     if (pointy < epsilon)
699     {
700       m_chasemouse_delta_y = std::max(pointy, 0) - epsilon;
701     }
702     else if ((pointy - m_nHeight) > -epsilon)
703     {
704       m_chasemouse_delta_y = min_int((pointy - m_nHeight), 0) + epsilon;
705     }
706
707     if(m_chasemouse_delta_y != 0 || m_chasemouse_delta_x != 0)
708     {
709       //globalOutputStream() << "chasemouse motion: x=" << pointx << " y=" << pointy << "... ";
710       m_chasemouse_current_x = pointx;
711       m_chasemouse_current_y = pointy;
712       if(m_chasemouse_handler == 0)
713       {
714         //globalOutputStream() << "chasemouse timer start... ";
715         g_chasemouse_timer.start();
716         m_chasemouse_handler = g_idle_add(xywnd_chasemouse, this);
717       }
718       return true;
719     }
720     else
721     {
722       if(m_chasemouse_handler != 0)
723       {
724         //globalOutputStream() << "chasemouse cancel\n";
725         g_source_remove(m_chasemouse_handler);
726         m_chasemouse_handler = 0;
727       }
728     }
729   }
730   else
731   {
732     if(m_chasemouse_handler != 0)
733     {
734       //globalOutputStream() << "chasemouse cancel\n";
735       g_source_remove(m_chasemouse_handler);
736       m_chasemouse_handler = 0;
737     }
738   }
739   return false;
740 }
741
742 // =============================================================================
743 // XYWnd class
744 Shader* XYWnd::m_state_selected = 0;
745
746 void xy_update_xor_rectangle(XYWnd& self, rect_t area)
747 {
748   if(GTK_WIDGET_VISIBLE(self.GetWidget()))
749   {
750     self.m_XORRectangle.set(rectangle_from_area(area.min, area.max, self.Width(), self.Height()));
751   }
752 }
753
754 gboolean xywnd_button_press(GtkWidget* widget, GdkEventButton* event, XYWnd* xywnd)
755 {
756   if(event->type == GDK_BUTTON_PRESS)
757   {
758     g_pParentWnd->SetActiveXY(xywnd);
759
760     xywnd->ButtonState_onMouseDown(buttons_for_event_button(event));
761
762     xywnd->onMouseDown(WindowVector(event->x, event->y), button_for_button(event->button), modifiers_for_state(event->state));
763   }
764   return FALSE;
765 }
766
767 gboolean xywnd_button_release(GtkWidget* widget, GdkEventButton* event, XYWnd* xywnd)
768 {
769   if(event->type == GDK_BUTTON_RELEASE)
770   {
771     xywnd->XY_MouseUp(static_cast<int>(event->x), static_cast<int>(event->y), buttons_for_event_button(event));
772
773     xywnd->ButtonState_onMouseUp(buttons_for_event_button(event));
774   }
775   return FALSE;
776 }
777
778 void xywnd_motion(gdouble x, gdouble y, guint state, void* data)
779 {
780   if(reinterpret_cast<XYWnd*>(data)->chaseMouseMotion(static_cast<int>(x), static_cast<int>(y)))
781   {
782     return;
783   }
784   reinterpret_cast<XYWnd*>(data)->XY_MouseMoved(static_cast<int>(x), static_cast<int>(y), buttons_for_state(state));
785 }
786
787 gboolean xywnd_wheel_scroll(GtkWidget* widget, GdkEventScroll* event, XYWnd* xywnd)
788 {
789   if(event->direction == GDK_SCROLL_UP)
790   {
791     XYWnd_ZoomIn(xywnd);
792   }
793   else if(event->direction == GDK_SCROLL_DOWN)
794   {
795     XYWnd_ZoomOut(xywnd);
796   }
797   return FALSE;
798 }
799
800 gboolean xywnd_size_allocate(GtkWidget* widget, GtkAllocation* allocation, XYWnd* xywnd)
801 {
802   xywnd->m_nWidth = allocation->width;
803   xywnd->m_nHeight = allocation->height;
804   xywnd->updateProjection();
805   xywnd->m_window_observer->onSizeChanged(xywnd->Width(), xywnd->Height());
806   return FALSE;
807 }
808
809 gboolean xywnd_expose(GtkWidget* widget, GdkEventExpose* event, XYWnd* xywnd)
810 {
811   if(glwidget_make_current(xywnd->GetWidget()) != FALSE)
812   {
813     if(Map_Valid(g_map) && ScreenUpdates_Enabled())
814     {
815       GlobalOpenGL_debugAssertNoErrors();
816       xywnd->XY_Draw();
817       GlobalOpenGL_debugAssertNoErrors();
818
819       xywnd->m_XORRectangle.set(rectangle_t());
820     }
821     glwidget_swap_buffers(xywnd->GetWidget());
822   }
823   return FALSE;
824 }
825
826
827 void XYWnd_CameraMoved(XYWnd& xywnd)
828 {
829   if(g_xywindow_globals_private.m_bCamXYUpdate)
830   {
831     XYWnd_Update(xywnd);
832   }
833 }
834
835 XYWnd::XYWnd() :
836   m_gl_widget(glwidget_new(FALSE)),
837   m_deferredDraw(WidgetQueueDrawCaller(*m_gl_widget)),
838   m_deferred_motion(xywnd_motion, this),
839   m_parent(0),
840   m_window_observer(NewWindowObserver()),
841   m_XORRectangle(m_gl_widget),
842   m_chasemouse_handler(0)
843 {
844   m_bActive = false;
845   m_buttonstate = 0;
846
847   m_bNewBrushDrag = false;
848   m_move_started = false;
849   m_zoom_started = false;
850
851   m_nWidth = 0;
852   m_nHeight = 0;
853
854   m_vOrigin[0] = 0;
855   m_vOrigin[1] = 20;
856   m_vOrigin[2] = 46;
857   m_fScale = 1;
858   m_viewType = XY;
859
860   m_backgroundActivated = false;
861   m_alpha = 1.0f;
862   m_xmin = 0.0f;
863   m_ymin = 0.0f;
864   m_xmax = 0.0f;
865   m_ymax = 0.0f;
866
867   m_entityCreate = false;
868
869   m_mnuDrop = 0;
870
871   GlobalWindowObservers_add(m_window_observer);
872   GlobalWindowObservers_connectWidget(m_gl_widget);
873
874   m_window_observer->setRectangleDrawCallback(ReferenceCaller1<XYWnd, rect_t, xy_update_xor_rectangle>(*this));
875   m_window_observer->setView(m_view);
876
877   gtk_widget_ref(m_gl_widget);
878
879   gtk_widget_set_events(m_gl_widget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK);
880   GTK_WIDGET_SET_FLAGS(m_gl_widget, GTK_CAN_FOCUS);
881
882   m_sizeHandler = g_signal_connect(G_OBJECT(m_gl_widget), "size_allocate", G_CALLBACK(xywnd_size_allocate), this);
883   m_exposeHandler = g_signal_connect(G_OBJECT(m_gl_widget), "expose_event", G_CALLBACK(xywnd_expose), this);
884
885   g_signal_connect(G_OBJECT(m_gl_widget), "button_press_event", G_CALLBACK(xywnd_button_press), this);
886   g_signal_connect(G_OBJECT(m_gl_widget), "button_release_event", G_CALLBACK(xywnd_button_release), this);
887   g_signal_connect(G_OBJECT(m_gl_widget), "motion_notify_event", G_CALLBACK(DeferredMotion::gtk_motion), &m_deferred_motion);
888
889   g_signal_connect(G_OBJECT(m_gl_widget), "scroll_event", G_CALLBACK(xywnd_wheel_scroll), this);
890
891   Map_addValidCallback(g_map, DeferredDrawOnMapValidChangedCaller(m_deferredDraw));
892
893   updateProjection();
894   updateModelview();
895
896   AddSceneChangeCallback(ReferenceCaller<XYWnd, &XYWnd_Update>(*this));
897   AddCameraMovedCallback(ReferenceCaller<XYWnd, &XYWnd_CameraMoved>(*this));
898
899   PressedButtons_connect(g_pressedButtons, m_gl_widget);
900
901   onMouseDown.connectLast(makeSignalHandler3(MouseDownCaller(), *this));
902 }
903
904 XYWnd::~XYWnd()
905 {
906   onDestroyed();
907
908   if(m_mnuDrop != 0)
909   {
910     gtk_widget_destroy(GTK_WIDGET(m_mnuDrop));
911     m_mnuDrop = 0;
912   }
913
914   g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_sizeHandler);
915   g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_exposeHandler);
916
917   gtk_widget_unref(m_gl_widget);
918
919   m_window_observer->release();
920 }
921
922 void XYWnd::captureStates()
923 {
924   m_state_selected = GlobalShaderCache().capture("$XY_OVERLAY");
925 }
926
927 void XYWnd::releaseStates()
928 {
929   GlobalShaderCache().release("$XY_OVERLAY");
930 }
931
932 const Vector3& XYWnd::GetOrigin()
933 {
934   return m_vOrigin;
935 }
936
937 void XYWnd::SetOrigin(const Vector3& origin)
938 {
939   m_vOrigin = origin;
940   updateModelview();
941 }
942
943 void XYWnd::Scroll(int x, int y)
944 {
945   int nDim1 = (m_viewType == YZ) ? 1 : 0;
946   int nDim2 = (m_viewType == XY) ? 1 : 2;
947   m_vOrigin[nDim1] += x / m_fScale;
948   m_vOrigin[nDim2] += y / m_fScale;
949   updateModelview();
950   queueDraw();
951 }
952
953 unsigned int Clipper_buttons()
954 {
955   return RAD_LBUTTON;
956 }
957
958 void XYWnd::DropClipPoint(int pointx, int pointy)
959 {
960   Vector3 point;
961
962   XY_ToPoint(pointx, pointy, point);
963
964   Vector3 mid;
965   Select_GetMid(mid);
966   g_clip_viewtype = static_cast<VIEWTYPE>(GetViewType());
967   const int nDim = (g_clip_viewtype == YZ ) ? 0 : ( (g_clip_viewtype == XZ) ? 1 : 2 );
968   point[nDim] = mid[nDim];
969   vector3_snap(point, GetGridSize());
970   NewClipPoint(point);
971 }
972
973 void XYWnd::Clipper_OnLButtonDown(int x, int y)
974 {
975   Vector3 mousePosition;
976   XY_ToPoint(x, y , mousePosition);
977   g_pMovingClip = GlobalClipPoints_Find(mousePosition, (VIEWTYPE)m_viewType, m_fScale);
978   if(!g_pMovingClip)
979   {
980     DropClipPoint(x, y);
981   }
982 }
983
984 void XYWnd::Clipper_OnLButtonUp(int x, int y)
985 {
986   if (g_pMovingClip)
987   {
988     g_pMovingClip = 0;
989   }
990 }
991
992 void XYWnd::Clipper_OnMouseMoved(int x, int y)
993 {
994   if (g_pMovingClip)
995   {
996     XY_ToPoint(x, y , g_pMovingClip->m_ptClip);
997     XY_SnapToGrid(g_pMovingClip->m_ptClip);
998     Clip_Update();
999     ClipperChangeNotify();
1000   }
1001 }
1002
1003 void XYWnd::Clipper_Crosshair_OnMouseMoved(int x, int y)
1004 {
1005   Vector3 mousePosition;
1006   XY_ToPoint(x, y , mousePosition);
1007   if(ClipMode() && GlobalClipPoints_Find(mousePosition, (VIEWTYPE)m_viewType, m_fScale) != 0)
1008   {
1009     GdkCursor *cursor;
1010     cursor = gdk_cursor_new (GDK_CROSSHAIR);
1011     gdk_window_set_cursor (m_gl_widget->window, cursor);
1012     gdk_cursor_unref (cursor);
1013   }
1014   else
1015   {
1016     gdk_window_set_cursor (m_gl_widget->window, 0);
1017   }
1018 }
1019
1020 unsigned int MoveCamera_buttons()
1021 {
1022   return RAD_CONTROL | (g_glwindow_globals.m_nMouseType == ETwoButton ? RAD_RBUTTON : RAD_MBUTTON);
1023 }
1024
1025 void XYWnd_PositionCamera(XYWnd* xywnd, int x, int y, CamWnd& camwnd)
1026 {
1027   Vector3 origin(Camera_getOrigin(camwnd));
1028   xywnd->XY_ToPoint(x, y, origin);
1029   xywnd->XY_SnapToGrid(origin);
1030   Camera_setOrigin(camwnd, origin);
1031 }
1032
1033 unsigned int OrientCamera_buttons()
1034 {
1035   if(g_glwindow_globals.m_nMouseType == ETwoButton)
1036     return RAD_RBUTTON | RAD_SHIFT | RAD_CONTROL;
1037   return RAD_MBUTTON;
1038 }
1039
1040 void XYWnd_OrientCamera(XYWnd* xywnd, int x, int y, CamWnd& camwnd)
1041 {
1042   Vector3       point = g_vector3_identity;
1043   xywnd->XY_ToPoint(x, y, point);
1044   xywnd->XY_SnapToGrid(point);
1045   vector3_subtract(point, Camera_getOrigin(camwnd));
1046
1047   int n1 = (xywnd->GetViewType() == XY) ? 1 : 2;
1048   int n2 = (xywnd->GetViewType() == YZ) ? 1 : 0;
1049   int nAngle = (xywnd->GetViewType() == XY) ? CAMERA_YAW : CAMERA_PITCH;
1050   if (point[n1] || point[n2])
1051   {
1052     Vector3 angles(Camera_getAngles(camwnd));
1053     angles[nAngle] = static_cast<float>(radians_to_degrees(atan2 (point[n1], point[n2])));
1054     Camera_setAngles(camwnd, angles);
1055   }
1056 }
1057
1058 /*
1059 ==============
1060 NewBrushDrag
1061 ==============
1062 */
1063 unsigned int NewBrushDrag_buttons()
1064 {
1065   return RAD_LBUTTON;
1066 }
1067
1068 void XYWnd::NewBrushDrag_Begin(int x, int y)
1069 {
1070   m_NewBrushDrag = 0;
1071   m_nNewBrushPressx = x;
1072   m_nNewBrushPressy = y;
1073
1074   m_bNewBrushDrag = true;
1075   GlobalUndoSystem().start();
1076 }
1077
1078 void XYWnd::NewBrushDrag_End(int x, int y)
1079 {
1080   if(m_NewBrushDrag != 0)
1081   {
1082     GlobalUndoSystem().finish("brushDragNew");
1083   }
1084 }
1085
1086 void XYWnd::NewBrushDrag(int x, int y)
1087 {
1088   Vector3       mins, maxs;
1089   XY_ToPoint(m_nNewBrushPressx, m_nNewBrushPressy, mins);
1090   XY_SnapToGrid(mins);
1091         XY_ToPoint(x, y, maxs);
1092   XY_SnapToGrid(maxs);
1093
1094   int nDim = (m_viewType == XY) ? 2 : (m_viewType == YZ) ? 0 : 1;
1095
1096   mins[nDim] = float_snapped(Select_getWorkZone().d_work_min[nDim], GetGridSize());
1097   maxs[nDim] = float_snapped(Select_getWorkZone().d_work_max[nDim], GetGridSize());
1098
1099   if (maxs[nDim] <= mins[nDim])
1100     maxs[nDim] = mins[nDim] + GetGridSize();
1101
1102   for(int i=0 ; i<3 ; i++)
1103   {
1104     if (mins[i] == maxs[i])
1105       return;   // don't create a degenerate brush
1106     if (mins[i] > maxs[i])
1107     {
1108       float     temp = mins[i];
1109       mins[i] = maxs[i];
1110       maxs[i] = temp;
1111     }
1112   }
1113
1114   if(m_NewBrushDrag == 0)
1115   {
1116     NodeSmartReference node(GlobalBrushCreator().createBrush());
1117     Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(node);
1118
1119     scene::Path brushpath(makeReference(GlobalSceneGraph().root()));
1120     brushpath.push(makeReference(*Map_GetWorldspawn(g_map)));
1121     brushpath.push(makeReference(node.get()));
1122     selectPath(brushpath, true);
1123
1124     m_NewBrushDrag = node.get_pointer();
1125   }
1126
1127   // d1223m
1128   //Scene_BrushResize_Selected(GlobalSceneGraph(), aabb_for_minmax(mins, maxs), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
1129   Scene_BrushResize_Selected(GlobalSceneGraph(), aabb_for_minmax(mins, maxs),
1130         g_brush_always_caulk ?
1131             "textures/common/caulk" : TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
1132 }
1133
1134 void entitycreate_activated(GtkWidget* item)
1135 {
1136   scene::Node* world_node = Map_FindWorldspawn(g_map);
1137   const char* entity_name = gtk_label_get_text(GTK_LABEL(GTK_BIN(item)->child));
1138
1139   if(!(world_node && string_equal(entity_name, "worldspawn")))
1140   {
1141     g_pParentWnd->ActiveXY()->OnEntityCreate(entity_name);
1142   } else {
1143     GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(MainFrame_getWindow()), "There's already a worldspawn in your map!"
1144                                         "",
1145                                         "Info",
1146                                         eMB_OK,
1147                                         eMB_ICONDEFAULT);
1148   }
1149 }
1150
1151 void EntityClassMenu_addItem(GtkMenu* menu, const char* name)
1152 {
1153   GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_label(name));
1154   g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(entitycreate_activated), item);
1155   gtk_widget_show(GTK_WIDGET(item));
1156   menu_add_item(menu, item);
1157 }
1158
1159 class EntityClassMenuInserter : public EntityClassVisitor
1160 {
1161   typedef std::pair<GtkMenu*, CopiedString> MenuPair;
1162   typedef std::vector<MenuPair> MenuStack;
1163   MenuStack m_stack;
1164   CopiedString m_previous;
1165 public:
1166   EntityClassMenuInserter(GtkMenu* menu)
1167   {
1168     m_stack.reserve(2);
1169     m_stack.push_back(MenuPair(menu, ""));
1170   }
1171   ~EntityClassMenuInserter()
1172   {
1173     if(!string_empty(m_previous.c_str()))
1174     {
1175       addItem(m_previous.c_str(), "");
1176     }
1177   }
1178   void visit(EntityClass* e)
1179   {
1180     ASSERT_MESSAGE(!string_empty(e->name()), "entity-class has no name");
1181     if(!string_empty(m_previous.c_str()))
1182     {
1183       addItem(m_previous.c_str(), e->name());
1184     }
1185     m_previous = e->name();
1186   }
1187   void pushMenu(const CopiedString& name)
1188   {
1189     GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_label(name.c_str()));
1190     gtk_widget_show(GTK_WIDGET(item));
1191     container_add_widget(GTK_CONTAINER(m_stack.back().first), GTK_WIDGET(item));
1192
1193     GtkMenu* submenu = GTK_MENU(gtk_menu_new());
1194     gtk_menu_item_set_submenu(item, GTK_WIDGET(submenu));
1195
1196     m_stack.push_back(MenuPair(submenu, name));
1197   }
1198   void popMenu()
1199   {
1200     m_stack.pop_back();
1201   }
1202   void addItem(const char* name, const char* next)
1203   {
1204     const char* underscore = strchr(name, '_');
1205
1206     if(underscore != 0 && underscore != name)
1207     {
1208       bool nextEqual = string_equal_n(name, next, (underscore + 1) - name);
1209       const char* parent = m_stack.back().second.c_str();
1210
1211       if(!string_empty(parent)
1212         && string_length(parent) == std::size_t(underscore - name)
1213         && string_equal_n(name, parent, underscore - name)) // this is a child
1214       {
1215       }
1216       else if(nextEqual)
1217       {
1218         if(m_stack.size() == 2)
1219         {
1220           popMenu();
1221         }
1222         pushMenu(CopiedString(StringRange(name, underscore)));
1223       }
1224       else if(m_stack.size() == 2)
1225       {
1226         popMenu();
1227       }
1228     }
1229     else if(m_stack.size() == 2)
1230     {
1231       popMenu();
1232     }
1233
1234     EntityClassMenu_addItem(m_stack.back().first, name);
1235   }
1236 };
1237
1238 void XYWnd::OnContextMenu()
1239 {
1240   if (g_xywindow_globals.m_bRightClick == false)
1241     return;
1242
1243   if (m_mnuDrop == 0) // first time, load it up
1244   {
1245     GtkMenu* menu = m_mnuDrop = GTK_MENU(gtk_menu_new());
1246
1247     EntityClassMenuInserter inserter(menu);
1248     GlobalEntityClassManager().forEach(inserter);
1249   }
1250
1251   gtk_menu_popup(m_mnuDrop, 0, 0, 0, 0, 1, GDK_CURRENT_TIME);
1252 }
1253
1254 FreezePointer g_xywnd_freezePointer;
1255
1256 unsigned int Move_buttons()
1257 {
1258   return RAD_RBUTTON;
1259 }
1260
1261 void XYWnd_moveDelta(int x, int y, unsigned int state, void* data)
1262 {
1263   reinterpret_cast<XYWnd*>(data)->EntityCreate_MouseMove(x, y);
1264   reinterpret_cast<XYWnd*>(data)->Scroll(-x, y);
1265 }
1266
1267 gboolean XYWnd_Move_focusOut(GtkWidget* widget, GdkEventFocus* event, XYWnd* xywnd)
1268 {
1269   xywnd->Move_End();
1270   return FALSE;
1271 }
1272
1273 void XYWnd::Move_Begin()
1274 {
1275   if(m_move_started)
1276   {
1277     Move_End();
1278   }
1279   m_move_started = true;
1280   g_xywnd_freezePointer.freeze_pointer(m_parent != 0 ? m_parent : MainFrame_getWindow(), XYWnd_moveDelta, this);
1281   m_move_focusOut = g_signal_connect(G_OBJECT(m_gl_widget), "focus_out_event", G_CALLBACK(XYWnd_Move_focusOut), this);
1282 }
1283
1284 void XYWnd::Move_End()
1285 {
1286   m_move_started = false;
1287   g_xywnd_freezePointer.unfreeze_pointer(m_parent != 0 ? m_parent : MainFrame_getWindow());
1288   g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_move_focusOut);
1289 }
1290
1291 unsigned int Zoom_buttons()
1292 {
1293   return RAD_RBUTTON | RAD_SHIFT;
1294 }
1295
1296 int g_dragZoom = 0;
1297
1298 void XYWnd_zoomDelta(int x, int y, unsigned int state, void* data)
1299 {
1300   if(y != 0)
1301   {
1302     g_dragZoom += y;
1303
1304     while(abs(g_dragZoom) > 8)
1305     {
1306       if(g_dragZoom > 0)
1307       {
1308         XYWnd_ZoomOut(reinterpret_cast<XYWnd*>(data));
1309         g_dragZoom -= 8;
1310       }
1311       else
1312       {
1313         XYWnd_ZoomIn(reinterpret_cast<XYWnd*>(data));
1314         g_dragZoom += 8;
1315       }
1316     }
1317   }
1318 }
1319
1320 gboolean XYWnd_Zoom_focusOut(GtkWidget* widget, GdkEventFocus* event, XYWnd* xywnd)
1321 {
1322   xywnd->Zoom_End();
1323   return FALSE;
1324 }
1325
1326 void XYWnd::Zoom_Begin()
1327 {
1328   if(m_zoom_started)
1329   {
1330     Zoom_End();
1331   }
1332   m_zoom_started = true;
1333   g_dragZoom = 0;
1334   g_xywnd_freezePointer.freeze_pointer(m_parent != 0 ? m_parent : MainFrame_getWindow(), XYWnd_zoomDelta, this);
1335   m_zoom_focusOut = g_signal_connect(G_OBJECT(m_gl_widget), "focus_out_event", G_CALLBACK(XYWnd_Zoom_focusOut), this);
1336 }
1337
1338 void XYWnd::Zoom_End()
1339 {
1340   m_zoom_started = false;
1341   g_xywnd_freezePointer.unfreeze_pointer(m_parent != 0 ? m_parent : MainFrame_getWindow());
1342   g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_zoom_focusOut);
1343 }
1344
1345 // makes sure the selected brush or camera is in view
1346 void XYWnd::PositionView(const Vector3& position)
1347 {
1348   int nDim1 = (m_viewType == YZ) ? 1 : 0;
1349   int nDim2 = (m_viewType == XY) ? 1 : 2;
1350
1351   m_vOrigin[nDim1] = position[nDim1];
1352   m_vOrigin[nDim2] = position[nDim2];
1353
1354   updateModelview();
1355
1356   XYWnd_Update(*this);
1357 }
1358
1359 void XYWnd::SetViewType(VIEWTYPE viewType)
1360 {
1361   m_viewType = viewType;
1362   updateModelview();
1363
1364   if(m_parent != 0)
1365   {
1366     gtk_window_set_title(m_parent, ViewType_getTitle(m_viewType));
1367   }
1368 }
1369
1370
1371 inline WindowVector WindowVector_forInteger(int x, int y)
1372 {
1373   return WindowVector(static_cast<float>(x), static_cast<float>(y));
1374 }
1375
1376 void XYWnd::mouseDown(const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers)
1377 {
1378   XY_MouseDown(static_cast<int>(position.x()), static_cast<int>(position.y()), buttons_for_button_and_modifiers(button, modifiers));
1379 }
1380 void XYWnd::XY_MouseDown (int x, int y, unsigned int buttons)
1381 {
1382   if(buttons == Move_buttons())
1383   {
1384     Move_Begin();
1385     EntityCreate_MouseDown(x, y);
1386   }
1387   else if(buttons == Zoom_buttons())
1388   {
1389     Zoom_Begin();
1390   }
1391   else if(ClipMode() && buttons == Clipper_buttons())
1392   {
1393     Clipper_OnLButtonDown(x, y);
1394   }
1395   else if(buttons == NewBrushDrag_buttons() && GlobalSelectionSystem().countSelected() == 0)
1396   {
1397     NewBrushDrag_Begin(x, y);
1398   }
1399   // control mbutton = move camera
1400   else if (buttons == MoveCamera_buttons())
1401   {
1402     XYWnd_PositionCamera(this, x, y, *g_pParentWnd->GetCamWnd());
1403   }
1404   // mbutton = angle camera
1405   else if(buttons == OrientCamera_buttons())
1406   {
1407     XYWnd_OrientCamera(this, x, y, *g_pParentWnd->GetCamWnd());
1408   }
1409   else
1410   {
1411     m_window_observer->onMouseDown(WindowVector_forInteger(x, y), button_for_flags(buttons), modifiers_for_flags(buttons));
1412   }
1413 }
1414
1415 void XYWnd::XY_MouseUp(int x, int y, unsigned int buttons)
1416 {
1417   if(m_move_started)
1418   {
1419     Move_End();
1420     EntityCreate_MouseUp(x, y);
1421   }
1422   else if(m_zoom_started)
1423   {
1424     Zoom_End();
1425   }
1426   else if (ClipMode() && buttons == Clipper_buttons())
1427   {
1428     Clipper_OnLButtonUp(x, y);
1429   }
1430   else if (m_bNewBrushDrag)
1431   {
1432     m_bNewBrushDrag = false;
1433     NewBrushDrag_End(x, y);
1434   }
1435   else
1436   {
1437     m_window_observer->onMouseUp(WindowVector_forInteger(x, y), button_for_flags(buttons), modifiers_for_flags(buttons));
1438   }
1439 }
1440
1441 void XYWnd::XY_MouseMoved (int x, int y, unsigned int buttons)
1442 {
1443   // rbutton = drag xy origin
1444   if(m_move_started)
1445   {
1446   }
1447   // zoom in/out
1448   else if(m_zoom_started)
1449   {
1450   }
1451
1452   else if (ClipMode() && g_pMovingClip != 0)
1453   {
1454     Clipper_OnMouseMoved(x, y);
1455   }
1456   // lbutton without selection = drag new brush
1457   else if (m_bNewBrushDrag)
1458   {
1459     NewBrushDrag(x, y);
1460   }
1461
1462   // control mbutton = move camera
1463   else if (getButtonState() == MoveCamera_buttons())
1464   {
1465     XYWnd_PositionCamera(this, x, y, *g_pParentWnd->GetCamWnd());
1466   }
1467
1468   // mbutton = angle camera
1469   else if (getButtonState() == OrientCamera_buttons())
1470   {
1471     XYWnd_OrientCamera(this, x, y, *g_pParentWnd->GetCamWnd());
1472   }
1473
1474   else
1475   {
1476     m_window_observer->onMouseMotion(WindowVector_forInteger(x, y), modifiers_for_flags(buttons));
1477
1478     m_mousePosition[0] = m_mousePosition[1] = m_mousePosition[2] = 0.0;
1479     XY_ToPoint(x, y , m_mousePosition);
1480     XY_SnapToGrid(m_mousePosition);
1481
1482     StringOutputStream status(64);
1483     status << "x:: " << FloatFormat(m_mousePosition[0], 6, 1)
1484       << "  y:: " << FloatFormat(m_mousePosition[1], 6, 1)
1485       << "  z:: " << FloatFormat(m_mousePosition[2], 6, 1);
1486     g_pParentWnd->SetStatusText(g_pParentWnd->m_position_status, status.c_str());
1487
1488     if (g_bCrossHairs)
1489     {
1490       XYWnd_Update(*this);
1491     }
1492
1493     Clipper_Crosshair_OnMouseMoved(x, y);
1494   }
1495 }
1496
1497 void XYWnd::EntityCreate_MouseDown(int x, int y)
1498 {
1499   m_entityCreate = true;
1500   m_entityCreate_x = x;
1501   m_entityCreate_y = y;
1502 }
1503
1504 void XYWnd::EntityCreate_MouseMove(int x, int y)
1505 {
1506   if(m_entityCreate && (m_entityCreate_x != x || m_entityCreate_y != y))
1507   {
1508     m_entityCreate = false;
1509   }
1510 }
1511
1512 void XYWnd::EntityCreate_MouseUp(int x, int y)
1513 {
1514   if(m_entityCreate)
1515   {
1516     m_entityCreate = false;
1517     OnContextMenu();
1518   }
1519 }
1520
1521 inline float screen_normalised(int pos, unsigned int size)
1522 {
1523   return ((2.0f * pos) / size) - 1.0f;
1524 }
1525
1526 inline float normalised_to_world(float normalised, float world_origin, float normalised2world_scale)
1527 {
1528   return world_origin + normalised * normalised2world_scale;
1529 }
1530
1531
1532 // TTimo: watch it, this doesn't init one of the 3 coords
1533 void XYWnd::XY_ToPoint (int x, int y, Vector3& point)
1534 {
1535   float normalised2world_scale_x = m_nWidth / 2 / m_fScale;
1536   float normalised2world_scale_y = m_nHeight / 2 / m_fScale;
1537   if (m_viewType == XY)
1538   {
1539     point[0] = normalised_to_world(screen_normalised(x, m_nWidth), m_vOrigin[0], normalised2world_scale_x);
1540     point[1] = normalised_to_world(-screen_normalised(y, m_nHeight), m_vOrigin[1], normalised2world_scale_y);
1541   }
1542   else if (m_viewType == YZ)
1543   {
1544     point[1] = normalised_to_world(screen_normalised(x, m_nWidth), m_vOrigin[1], normalised2world_scale_x);
1545     point[2] = normalised_to_world(-screen_normalised(y, m_nHeight), m_vOrigin[2], normalised2world_scale_y);
1546   }
1547   else
1548   {
1549     point[0] = normalised_to_world(screen_normalised(x, m_nWidth), m_vOrigin[0], normalised2world_scale_x);
1550     point[2] = normalised_to_world(-screen_normalised(y, m_nHeight), m_vOrigin[2], normalised2world_scale_y);
1551   }
1552 }
1553
1554 void XYWnd::XY_SnapToGrid(Vector3& point)
1555 {
1556   if (m_viewType == XY)
1557   {
1558     point[0] = float_snapped(point[0], GetGridSize());
1559     point[1] = float_snapped(point[1], GetGridSize());
1560   }
1561   else if (m_viewType == YZ)
1562   {
1563     point[1] = float_snapped(point[1], GetGridSize());
1564     point[2] = float_snapped(point[2], GetGridSize());
1565   }
1566   else
1567   {
1568     point[0] = float_snapped(point[0], GetGridSize());
1569     point[2] = float_snapped(point[2], GetGridSize());
1570   }
1571 }
1572
1573 void XYWnd::XY_LoadBackgroundImage(const char *name)
1574 {
1575         const char* relative = path_make_relative(name, GlobalFileSystem().findRoot(name));
1576         if (relative == name)
1577                 globalOutputStream() << "WARNING: could not extract the relative path, using full path instead\n";
1578
1579         char fileNameWithoutExt[512];
1580         strncpy(fileNameWithoutExt, relative, sizeof(fileNameWithoutExt) - 1);
1581         fileNameWithoutExt[512 - 1] = '\0';
1582         fileNameWithoutExt[strlen(fileNameWithoutExt) - 4] = '\0';
1583
1584         Image *image = QERApp_LoadImage(0, fileNameWithoutExt);
1585         if (!image) {
1586                 globalOutputStream() << "Could not load texture " << fileNameWithoutExt << "\n";
1587                 return;
1588         }
1589         g_pParentWnd->ActiveXY()->m_tex = (qtexture_t*)malloc(sizeof(qtexture_t));
1590         LoadTextureRGBA(g_pParentWnd->ActiveXY()->XYWnd::m_tex, image->getRGBAPixels(), image->getWidth(), image->getHeight());
1591         globalOutputStream() << "Loaded background texture " << relative << "\n";
1592         g_pParentWnd->ActiveXY()->m_backgroundActivated = true;
1593
1594         int m_ix, m_iy;
1595         switch(g_pParentWnd->ActiveXY()->m_viewType)
1596         {
1597                 case XY:
1598                         m_ix = 0;
1599                         m_iy = 1;
1600                         break;
1601                 case XZ:
1602                         m_ix = 0;
1603                         m_iy = 2;
1604                         break;
1605                 case YZ:
1606                         m_ix = 1;
1607                         m_iy = 2;
1608                         break;
1609         }
1610
1611         Vector3 min, max;
1612         Select_GetBounds(min, max);
1613         g_pParentWnd->ActiveXY()->m_xmin = min[m_ix];
1614         g_pParentWnd->ActiveXY()->m_ymin = min[m_iy];
1615         g_pParentWnd->ActiveXY()->m_xmax = max[m_ix];
1616         g_pParentWnd->ActiveXY()->m_ymax = max[m_iy];
1617 }
1618
1619 void XYWnd::XY_DisableBackground (void)
1620 {
1621         g_pParentWnd->ActiveXY()->m_backgroundActivated = false;
1622         if (g_pParentWnd->ActiveXY()->m_tex)
1623                 free(g_pParentWnd->ActiveXY()->m_tex);
1624         g_pParentWnd->ActiveXY()->m_tex = NULL;
1625 }
1626
1627 void WXY_BackgroundSelect(void)
1628 {
1629         bool brushesSelected = Scene_countSelectedBrushes(GlobalSceneGraph()) != 0;
1630         if (!brushesSelected) {
1631                 gtk_MessageBox(0, "You have to select some brushes to get the bounding box for.\n",
1632                                         "No selection", eMB_OK, eMB_ICONERROR);
1633                 return;
1634         }
1635
1636         const char *filename = file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, "Background Image", NULL, NULL);
1637         g_pParentWnd->ActiveXY()->XY_DisableBackground();
1638         if (filename)
1639                 g_pParentWnd->ActiveXY()->XY_LoadBackgroundImage(filename);
1640 }
1641
1642 /*
1643 ============================================================================
1644
1645 DRAWING
1646
1647 ============================================================================
1648 */
1649
1650 /*
1651 ==============
1652 XY_DrawGrid
1653 ==============
1654 */
1655
1656 double two_to_the_power(int power)
1657 {
1658   return pow(2.0f, power);
1659 }
1660
1661 void XYWnd::XY_DrawAxis(void)
1662 {
1663         if ( g_xywindow_globals_private.show_axis) {
1664                 const char g_AxisName[3] = { 'X', 'Y', 'Z' };
1665                 const int nDim1 = (m_viewType == YZ) ? 1 : 0;
1666                 const int nDim2 = (m_viewType == XY) ? 1 : 2;
1667                 const int w = (m_nWidth / 2 / m_fScale);
1668                 const int h = (m_nHeight / 2 / m_fScale);
1669
1670                 const Vector3& colourX = (m_viewType == YZ) ? g_xywindow_globals.AxisColorY : g_xywindow_globals.AxisColorX;
1671                 const Vector3& colourY = (m_viewType == XY) ? g_xywindow_globals.AxisColorY : g_xywindow_globals.AxisColorZ;
1672
1673                 // draw two lines with corresponding axis colors to highlight current view
1674                 // horizontal line: nDim1 color
1675                 glLineWidth(2);
1676                 glBegin( GL_LINES );
1677                 glColor3fv (vector3_to_array(colourX));
1678                 glVertex2f( m_vOrigin[nDim1] - w + 40 / m_fScale, m_vOrigin[nDim2] + h - 45 / m_fScale );
1679                 glVertex2f( m_vOrigin[nDim1] - w + 65 / m_fScale, m_vOrigin[nDim2] + h - 45 / m_fScale );
1680                 glVertex2f( 0, 0 );
1681                 glVertex2f( 32 / m_fScale, 0 );
1682                 glColor3fv (vector3_to_array(colourY));
1683                 glVertex2f( m_vOrigin[nDim1] - w + 40 / m_fScale, m_vOrigin[nDim2] + h - 45 / m_fScale );
1684                 glVertex2f( m_vOrigin[nDim1] - w + 40 / m_fScale, m_vOrigin[nDim2] + h - 20 / m_fScale );
1685                 glVertex2f( 0, 0 );
1686                 glVertex2f( 0, 32 / m_fScale );
1687                 glEnd();
1688                 glLineWidth(1);
1689                 // now print axis symbols
1690                 glColor3fv (vector3_to_array(colourX));
1691                 glRasterPos2f ( m_vOrigin[nDim1] - w + 55 / m_fScale, m_vOrigin[nDim2] + h - 55 / m_fScale );
1692                 GlobalOpenGL().drawChar(g_AxisName[nDim1]);
1693                 glRasterPos2f (28 / m_fScale, -10 / m_fScale );
1694                 GlobalOpenGL().drawChar(g_AxisName[nDim1]);
1695                 glColor3fv (vector3_to_array(colourY));
1696                 glRasterPos2f ( m_vOrigin[nDim1] - w + 25 / m_fScale, m_vOrigin[nDim2] + h - 30 / m_fScale );
1697                 GlobalOpenGL().drawChar(g_AxisName[nDim2]);
1698                 glRasterPos2f ( -10 / m_fScale, 28 / m_fScale );
1699                 GlobalOpenGL().drawChar(g_AxisName[nDim2]);
1700         }
1701 }
1702
1703 void XYWnd::XY_DrawBackground(void)
1704 {
1705         glPushAttrib(GL_ALL_ATTRIB_BITS);
1706
1707         glEnable(GL_TEXTURE_2D);
1708         glEnable(GL_BLEND);
1709         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1710         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1711         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1712         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1713
1714         glPolygonMode(GL_FRONT, GL_FILL);
1715
1716         glBindTexture(GL_TEXTURE_2D, m_tex->texture_number);
1717         glBegin(GL_QUADS);
1718
1719         glColor4f(1.0, 1.0, 1.0, m_alpha);
1720         glTexCoord2f(0.0, 1.0);
1721         glVertex2f(m_xmin, m_ymin);
1722
1723         glTexCoord2f(1.0, 1.0);
1724         glVertex2f(m_xmax, m_ymin);
1725
1726         glTexCoord2f(1.0, 0.0);
1727         glVertex2f(m_xmax, m_ymax);
1728
1729         glTexCoord2f(0.0, 0.0);
1730         glVertex2f(m_xmin, m_ymax);
1731
1732         glEnd();
1733         glBindTexture(GL_TEXTURE_2D, 0);
1734
1735         glPopAttrib();
1736 }
1737
1738 void XYWnd::XY_DrawGrid(void) {
1739         float   x, y, xb, xe, yb, ye;
1740         float           w, h;
1741         char    text[32];
1742         float step, minor_step, stepx, stepy;
1743         step = minor_step = stepx = stepy = GetGridSize();
1744
1745         int minor_power = Grid_getPower();
1746         int mask;
1747
1748         while ((minor_step * m_fScale) <= 4.0f) { // make sure minor grid spacing is at least 4 pixels on the screen
1749                 ++minor_power;
1750                 minor_step *= 2;
1751         }
1752         int power = minor_power;
1753         while ((power % 3) != 0 || (step * m_fScale) <= 32.0f) { // make sure major grid spacing is at least 32 pixels on the screen
1754                 ++power;
1755                 step = float(two_to_the_power(power));
1756         }
1757         mask = (1 << (power - minor_power)) - 1;
1758         while ((stepx * m_fScale) <= 32.0f) // text step x must be at least 32
1759                 stepx *= 2;
1760         while ((stepy * m_fScale) <= 32.0f) // text step y must be at least 32
1761                 stepy *= 2;
1762
1763
1764         glDisable(GL_TEXTURE_2D);
1765         glDisable(GL_TEXTURE_1D);
1766         glDisable(GL_DEPTH_TEST);
1767         glDisable(GL_BLEND);
1768         glLineWidth(1);
1769
1770         w = (m_nWidth / 2 / m_fScale);
1771         h = (m_nHeight / 2 / m_fScale);
1772
1773         const int nDim1 = (m_viewType == YZ) ? 1 : 0;
1774         const int nDim2 = (m_viewType == XY) ? 1 : 2;
1775
1776         xb = m_vOrigin[nDim1] - w;
1777         if (xb < region_mins[nDim1])
1778                 xb = region_mins[nDim1];
1779         xb = step * floor (xb / step);
1780
1781         xe = m_vOrigin[nDim1] + w;
1782         if (xe > region_maxs[nDim1])
1783                 xe = region_maxs[nDim1];
1784         xe = step * ceil (xe / step);
1785
1786         yb = m_vOrigin[nDim2] - h;
1787         if (yb < region_mins[nDim2])
1788                 yb = region_mins[nDim2];
1789         yb = step * floor (yb / step);
1790
1791         ye = m_vOrigin[nDim2] + h;
1792         if (ye > region_maxs[nDim2])
1793                 ye = region_maxs[nDim2];
1794         ye = step * ceil (ye / step);
1795
1796 #define COLORS_DIFFER(a,b) \
1797   ((a)[0] != (b)[0] || \
1798    (a)[1] != (b)[1] || \
1799    (a)[2] != (b)[2])
1800
1801         // djbob
1802         // draw minor blocks
1803         if (g_xywindow_globals_private.d_showgrid) {
1804                 if (COLORS_DIFFER(g_xywindow_globals.color_gridminor, g_xywindow_globals.color_gridback)) {
1805                         glColor3fv(vector3_to_array(g_xywindow_globals.color_gridminor));
1806
1807                         glBegin (GL_LINES);
1808                         int i = 0;
1809                         for (x = xb ; x < xe ; x += minor_step, ++i) {
1810                                 if ((i & mask) != 0) {
1811                                         glVertex2f (x, yb);
1812                                         glVertex2f (x, ye);
1813                                 }
1814                         }
1815                         i = 0;
1816                         for (y = yb ; y < ye ; y += minor_step, ++i) {
1817                                 if ((i & mask) != 0) {
1818                                         glVertex2f (xb, y);
1819                                         glVertex2f (xe, y);
1820                                 }
1821                         }
1822                         glEnd();
1823                 }
1824
1825                 // draw major blocks
1826                 if (COLORS_DIFFER(g_xywindow_globals.color_gridmajor, g_xywindow_globals.color_gridback)) {
1827                         glColor3fv(vector3_to_array(g_xywindow_globals.color_gridmajor));
1828
1829                         glBegin (GL_LINES);
1830                         for (x = xb ; x <= xe ; x += step) {
1831                                 glVertex2f (x, yb);
1832                                 glVertex2f (x, ye);
1833                         }
1834                         for (y = yb ; y <= ye ; y += step) {
1835                                 glVertex2f (xb, y);
1836                                 glVertex2f (xe, y);
1837                         }
1838                         glEnd();
1839                 }
1840         }
1841
1842         // draw coordinate text if needed
1843         if ( g_xywindow_globals_private.show_coordinates) {
1844                 glColor3fv(vector3_to_array(g_xywindow_globals.color_gridtext));
1845                 // why does this not work on windows:
1846                 //   float offx = m_vOrigin[nDim2] + h - (1 + GlobalOpenGL().m_fontAscent) / m_fScale;
1847                 float offx = m_vOrigin[nDim2] + h - 13                                / m_fScale;
1848                 float offy = m_vOrigin[nDim1] - w +  1                                / m_fScale;
1849                 for (x = xb - fmod(xb, stepx); x <= xe ; x += stepx) {
1850                         glRasterPos2f (x, offx);
1851                         sprintf (text, "%g", x);
1852                         GlobalOpenGL().drawString(text);
1853                 }
1854                 for (y = yb - fmod(yb, stepy); y <= ye ; y += stepy) {
1855                         glRasterPos2f (offy, y);
1856                         sprintf (text, "%g", y);
1857                         GlobalOpenGL().drawString(text);
1858                 }
1859
1860                 if (Active())
1861                         glColor3fv(vector3_to_array(g_xywindow_globals.color_viewname));
1862
1863                 // we do this part (the old way) only if show_axis is disabled
1864                 if (!g_xywindow_globals_private.show_axis) {
1865                         glRasterPos2f ( m_vOrigin[nDim1] - w + 35 / m_fScale, m_vOrigin[nDim2] + h - 20 / m_fScale );
1866
1867                         GlobalOpenGL().drawString(ViewType_getTitle(m_viewType));
1868                 }
1869         }
1870
1871         XYWnd::XY_DrawAxis();
1872
1873         // show current work zone?
1874         // the work zone is used to place dropped points and brushes
1875         if (g_xywindow_globals_private.d_show_work) {
1876                 glColor3f( 1.0f, 0.0f, 0.0f );
1877                 glBegin( GL_LINES );
1878                 glVertex2f( xb, Select_getWorkZone().d_work_min[nDim2] );
1879                 glVertex2f( xe, Select_getWorkZone().d_work_min[nDim2] );
1880                 glVertex2f( xb, Select_getWorkZone().d_work_max[nDim2] );
1881                 glVertex2f( xe, Select_getWorkZone().d_work_max[nDim2] );
1882                 glVertex2f( Select_getWorkZone().d_work_min[nDim1], yb );
1883                 glVertex2f( Select_getWorkZone().d_work_min[nDim1], ye );
1884                 glVertex2f( Select_getWorkZone().d_work_max[nDim1], yb );
1885                 glVertex2f( Select_getWorkZone().d_work_max[nDim1], ye );
1886                 glEnd();
1887         }
1888 }
1889
1890 /*
1891 ==============
1892 XY_DrawBlockGrid
1893 ==============
1894 */
1895 void XYWnd::XY_DrawBlockGrid()
1896 {
1897   if(Map_FindWorldspawn(g_map) == 0)
1898   {
1899     return;
1900   }
1901   const char *value = Node_getEntity(*Map_GetWorldspawn(g_map))->getKeyValue("_blocksize" );
1902   if (strlen(value))
1903         sscanf( value, "%i", &g_xywindow_globals_private.blockSize );
1904
1905   if (!g_xywindow_globals_private.blockSize || g_xywindow_globals_private.blockSize > 65536 || g_xywindow_globals_private.blockSize < 1024)
1906           // don't use custom blocksize if it is less than the default, or greater than the maximum world coordinate
1907         g_xywindow_globals_private.blockSize = 1024;
1908
1909   float x, y, xb, xe, yb, ye;
1910   float         w, h;
1911   char  text[32];
1912
1913   glDisable(GL_TEXTURE_2D);
1914   glDisable(GL_TEXTURE_1D);
1915   glDisable(GL_DEPTH_TEST);
1916   glDisable(GL_BLEND);
1917
1918   w = (m_nWidth / 2 / m_fScale);
1919   h = (m_nHeight / 2 / m_fScale);
1920
1921   int nDim1 = (m_viewType == YZ) ? 1 : 0;
1922   int nDim2 = (m_viewType == XY) ? 1 : 2;
1923
1924   xb = m_vOrigin[nDim1] - w;
1925   if (xb < region_mins[nDim1])
1926     xb = region_mins[nDim1];
1927   xb = static_cast<float>(g_xywindow_globals_private.blockSize * floor (xb/g_xywindow_globals_private.blockSize));
1928
1929   xe = m_vOrigin[nDim1] + w;
1930   if (xe > region_maxs[nDim1])
1931     xe = region_maxs[nDim1];
1932   xe = static_cast<float>(g_xywindow_globals_private.blockSize * ceil (xe/g_xywindow_globals_private.blockSize));
1933
1934   yb = m_vOrigin[nDim2] - h;
1935   if (yb < region_mins[nDim2])
1936     yb = region_mins[nDim2];
1937   yb = static_cast<float>(g_xywindow_globals_private.blockSize * floor (yb/g_xywindow_globals_private.blockSize));
1938
1939   ye = m_vOrigin[nDim2] + h;
1940   if (ye > region_maxs[nDim2])
1941     ye = region_maxs[nDim2];
1942   ye = static_cast<float>(g_xywindow_globals_private.blockSize * ceil (ye/g_xywindow_globals_private.blockSize));
1943
1944   // draw major blocks
1945
1946   glColor3fv(vector3_to_array(g_xywindow_globals.color_gridblock));
1947   glLineWidth (2);
1948
1949   glBegin (GL_LINES);
1950
1951   for (x=xb ; x<=xe ; x+=g_xywindow_globals_private.blockSize)
1952   {
1953     glVertex2f (x, yb);
1954     glVertex2f (x, ye);
1955   }
1956
1957   if (m_viewType == XY)
1958   {
1959         for (y=yb ; y<=ye ; y+=g_xywindow_globals_private.blockSize)
1960         {
1961           glVertex2f (xb, y);
1962           glVertex2f (xe, y);
1963         }
1964   }
1965
1966   glEnd();
1967   glLineWidth (1);
1968
1969   // draw coordinate text if needed
1970
1971   if (m_viewType == XY && m_fScale > .1)
1972   {
1973         for (x=xb ; x<xe ; x+=g_xywindow_globals_private.blockSize)
1974                 for (y=yb ; y<ye ; y+=g_xywindow_globals_private.blockSize)
1975                 {
1976                   glRasterPos2f (x+(g_xywindow_globals_private.blockSize/2), y+(g_xywindow_globals_private.blockSize/2));
1977                         sprintf (text, "%i,%i",(int)floor(x/g_xywindow_globals_private.blockSize), (int)floor(y/g_xywindow_globals_private.blockSize) );
1978                         GlobalOpenGL().drawString(text);
1979                 }
1980   }
1981
1982   glColor4f(0, 0, 0, 0);
1983 }
1984
1985 void XYWnd::DrawCameraIcon(const Vector3& origin, const Vector3& angles)
1986 {
1987   float x, y, fov, box;
1988   double a;
1989
1990   fov = 48 / m_fScale;
1991   box = 16 / m_fScale;
1992
1993   if (m_viewType == XY)
1994   {
1995     x = origin[0];
1996     y = origin[1];
1997     a = degrees_to_radians(angles[CAMERA_YAW]);
1998   }
1999   else if (m_viewType == YZ)
2000   {
2001     x = origin[1];
2002     y = origin[2];
2003     a = degrees_to_radians(angles[CAMERA_PITCH]);
2004   }
2005   else
2006   {
2007     x = origin[0];
2008     y = origin[2];
2009     a = degrees_to_radians(angles[CAMERA_PITCH]);
2010   }
2011
2012   glColor3f (0.0, 0.0, 1.0);
2013   glBegin(GL_LINE_STRIP);
2014   glVertex3f (x-box,y,0);
2015   glVertex3f (x,y+(box/2),0);
2016   glVertex3f (x+box,y,0);
2017   glVertex3f (x,y-(box/2),0);
2018   glVertex3f (x-box,y,0);
2019   glVertex3f (x+box,y,0);
2020   glEnd();
2021
2022   glBegin(GL_LINE_STRIP);
2023   glVertex3f (x + static_cast<float>(fov*cos(a+c_pi/4)), y + static_cast<float>(fov*sin(a+c_pi/4)), 0);
2024   glVertex3f (x, y, 0);
2025   glVertex3f (x + static_cast<float>(fov*cos(a-c_pi/4)), y + static_cast<float>(fov*sin(a-c_pi/4)), 0);
2026   glEnd();
2027
2028 }
2029
2030
2031 float Betwixt(float f1, float f2)
2032 {
2033   if (f1 > f2)
2034     return f2 + ((f1 - f2) / 2);
2035   else
2036     return f1 + ((f2 - f1) / 2);
2037 }
2038
2039
2040 // can be greatly simplified but per usual i am in a hurry
2041 // which is not an excuse, just a fact
2042 void XYWnd::PaintSizeInfo(int nDim1, int nDim2, Vector3& vMinBounds, Vector3& vMaxBounds)
2043 {
2044   if(vector3_equal(vMinBounds, vMaxBounds))
2045   {
2046     return;
2047   }
2048   const char* g_pDimStrings[] = {"x:", "y:", "z:"};
2049   typedef const char* OrgStrings[2];
2050   const OrgStrings g_pOrgStrings[] = { { "x:", "y:", }, { "x:", "z:", }, { "y:", "z:", } };
2051
2052   Vector3 vSize(vector3_subtracted(vMaxBounds, vMinBounds));
2053
2054   glColor3f(g_xywindow_globals.color_selbrushes[0] * .65f,
2055              g_xywindow_globals.color_selbrushes[1] * .65f,
2056              g_xywindow_globals.color_selbrushes[2] * .65f);
2057
2058   StringOutputStream dimensions(16);
2059
2060   if (m_viewType == XY)
2061   {
2062     glBegin (GL_LINES);
2063
2064     glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale, 0.0f);
2065     glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
2066
2067     glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale, 0.0f);
2068     glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale, 0.0f);
2069
2070     glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale, 0.0f);
2071     glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
2072
2073
2074     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, vMinBounds[nDim2], 0.0f);
2075     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2], 0.0f);
2076
2077     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2], 0.0f);
2078     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2], 0.0f);
2079
2080     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, vMaxBounds[nDim2], 0.0f);
2081     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2], 0.0f);
2082
2083     glEnd();
2084
2085     glRasterPos3f (Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]),  vMinBounds[nDim2] - 20.0f  / m_fScale, 0.0f);
2086     dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
2087     GlobalOpenGL().drawString(dimensions.c_str());
2088     dimensions.clear();
2089
2090     glRasterPos3f (vMaxBounds[nDim1] + 16.0f  / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]), 0.0f);
2091     dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
2092     GlobalOpenGL().drawString(dimensions.c_str());
2093     dimensions.clear();
2094
2095     glRasterPos3f (vMinBounds[nDim1] + 4, vMaxBounds[nDim2] + 8 / m_fScale, 0.0f);
2096     dimensions << "(" << g_pOrgStrings[0][0] << vMinBounds[nDim1] << "  " << g_pOrgStrings[0][1] << vMaxBounds[nDim2] << ")";
2097     GlobalOpenGL().drawString(dimensions.c_str());
2098   }
2099   else if (m_viewType == XZ)
2100   {
2101     glBegin (GL_LINES);
2102
2103     glVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 6.0f  / m_fScale);
2104     glVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale);
2105
2106     glVertex3f(vMinBounds[nDim1], 0,vMinBounds[nDim2] - 10.0f  / m_fScale);
2107     glVertex3f(vMaxBounds[nDim1], 0,vMinBounds[nDim2] - 10.0f  / m_fScale);
2108
2109     glVertex3f(vMaxBounds[nDim1], 0,vMinBounds[nDim2] - 6.0f  / m_fScale);
2110     glVertex3f(vMaxBounds[nDim1], 0,vMinBounds[nDim2] - 10.0f / m_fScale);
2111
2112
2113     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, 0,vMinBounds[nDim2]);
2114     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMinBounds[nDim2]);
2115
2116     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMinBounds[nDim2]);
2117     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMaxBounds[nDim2]);
2118
2119     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, 0,vMaxBounds[nDim2]);
2120     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMaxBounds[nDim2]);
2121
2122     glEnd();
2123
2124     glRasterPos3f (Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), 0, vMinBounds[nDim2] - 20.0f  / m_fScale);
2125     dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
2126     GlobalOpenGL().drawString(dimensions.c_str());
2127     dimensions.clear();
2128
2129     glRasterPos3f (vMaxBounds[nDim1] + 16.0f  / m_fScale, 0, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]));
2130     dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
2131     GlobalOpenGL().drawString(dimensions.c_str());
2132     dimensions.clear();
2133
2134     glRasterPos3f (vMinBounds[nDim1] + 4, 0, vMaxBounds[nDim2] + 8 / m_fScale);
2135     dimensions << "(" << g_pOrgStrings[1][0] << vMinBounds[nDim1] << "  " << g_pOrgStrings[1][1] << vMaxBounds[nDim2] << ")";
2136     GlobalOpenGL().drawString(dimensions.c_str());
2137   }
2138   else
2139   {
2140     glBegin (GL_LINES);
2141
2142     glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale);
2143     glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
2144
2145     glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale);
2146     glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale);
2147
2148     glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale);
2149     glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
2150
2151
2152     glVertex3f(0, vMaxBounds[nDim1] + 6.0f  / m_fScale, vMinBounds[nDim2]);
2153     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2]);
2154
2155     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2]);
2156     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2]);
2157
2158     glVertex3f(0, vMaxBounds[nDim1] + 6.0f  / m_fScale, vMaxBounds[nDim2]);
2159     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2]);
2160
2161     glEnd();
2162
2163     glRasterPos3f (0, Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]),  vMinBounds[nDim2] - 20.0f  / m_fScale);
2164     dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
2165     GlobalOpenGL().drawString(dimensions.c_str());
2166     dimensions.clear();
2167
2168     glRasterPos3f (0, vMaxBounds[nDim1] + 16.0f  / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]));
2169     dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
2170     GlobalOpenGL().drawString(dimensions.c_str());
2171     dimensions.clear();
2172
2173     glRasterPos3f (0, vMinBounds[nDim1] + 4.0f, vMaxBounds[nDim2] + 8 / m_fScale);
2174     dimensions << "(" << g_pOrgStrings[2][0] << vMinBounds[nDim1] << "  " << g_pOrgStrings[2][1] << vMaxBounds[nDim2] << ")";
2175     GlobalOpenGL().drawString(dimensions.c_str());
2176   }
2177 }
2178
2179 class XYRenderer: public Renderer
2180 {
2181   struct state_type
2182   {
2183     state_type() :
2184     m_highlight(0),
2185     m_state(0)
2186     {
2187     }
2188     unsigned int m_highlight;
2189     Shader* m_state;
2190   };
2191 public:
2192   XYRenderer(RenderStateFlags globalstate, Shader* selected) :
2193     m_globalstate(globalstate),
2194     m_state_selected(selected)
2195   {
2196     ASSERT_NOTNULL(selected);
2197     m_state_stack.push_back(state_type());
2198   }
2199
2200   void SetState(Shader* state, EStyle style)
2201   {
2202     ASSERT_NOTNULL(state);
2203     if(style == eWireframeOnly)
2204       m_state_stack.back().m_state = state;
2205   }
2206   const EStyle getStyle() const
2207   {
2208     return eWireframeOnly;
2209   }
2210   void PushState()
2211   {
2212     m_state_stack.push_back(m_state_stack.back());
2213   }
2214   void PopState()
2215   {
2216     ASSERT_MESSAGE(!m_state_stack.empty(), "popping empty stack");
2217     m_state_stack.pop_back();
2218   }
2219   void Highlight(EHighlightMode mode, bool bEnable = true)
2220   {
2221     (bEnable)
2222       ? m_state_stack.back().m_highlight |= mode
2223       : m_state_stack.back().m_highlight &= ~mode;
2224   }
2225   void addRenderable(const OpenGLRenderable& renderable, const Matrix4& localToWorld)
2226   {
2227     if(m_state_stack.back().m_highlight & ePrimitive)
2228     {
2229       m_state_selected->addRenderable(renderable, localToWorld);
2230     }
2231     else
2232     {
2233       m_state_stack.back().m_state->addRenderable(renderable, localToWorld);
2234     }
2235   }
2236
2237   void render(const Matrix4& modelview, const Matrix4& projection)
2238   {
2239     GlobalShaderCache().render(m_globalstate, modelview, projection);
2240   }
2241 private:
2242   std::vector<state_type> m_state_stack;
2243   RenderStateFlags m_globalstate;
2244   Shader* m_state_selected;
2245 };
2246
2247 void XYWnd::updateProjection()
2248 {
2249   m_projection[0] = 1.0f / static_cast<float>(m_nWidth / 2);
2250   m_projection[5] = 1.0f / static_cast<float>(m_nHeight / 2);
2251   m_projection[10] = 1.0f / (g_MaxWorldCoord * m_fScale);
2252
2253   m_projection[12] = 0.0f;
2254   m_projection[13] = 0.0f;
2255   m_projection[14] = -1.0f;
2256
2257   m_projection[1] =
2258   m_projection[2] =
2259   m_projection[3] =
2260
2261   m_projection[4] =
2262   m_projection[6] =
2263   m_projection[7] =
2264
2265   m_projection[8] =
2266   m_projection[9] =
2267   m_projection[11] = 0.0f;
2268
2269   m_projection[15] = 1.0f;
2270
2271   m_view.Construct(m_projection, m_modelview, m_nWidth, m_nHeight);
2272 }
2273
2274 // note: modelview matrix must have a uniform scale, otherwise strange things happen when rendering the rotation manipulator.
2275 void XYWnd::updateModelview()
2276 {
2277   int nDim1 = (m_viewType == YZ) ? 1 : 0;
2278   int nDim2 = (m_viewType == XY) ? 1 : 2;
2279
2280   // translation
2281   m_modelview[12] = -m_vOrigin[nDim1] * m_fScale;
2282   m_modelview[13] = -m_vOrigin[nDim2] * m_fScale;
2283   m_modelview[14] = g_MaxWorldCoord * m_fScale;
2284
2285   // axis base
2286   switch(m_viewType)
2287   {
2288   case XY:
2289     m_modelview[0]  =  m_fScale;
2290     m_modelview[1]  =  0;
2291     m_modelview[2]  =  0;
2292
2293     m_modelview[4]  =  0;
2294     m_modelview[5]  =  m_fScale;
2295     m_modelview[6]  =  0;
2296
2297     m_modelview[8]  =  0;
2298     m_modelview[9]  =  0;
2299     m_modelview[10] = -m_fScale;
2300     break;
2301   case XZ:
2302     m_modelview[0]  =  m_fScale;
2303     m_modelview[1]  =  0;
2304     m_modelview[2]  =  0;
2305
2306     m_modelview[4]  =  0;
2307     m_modelview[5]  =  0;
2308     m_modelview[6]  =  m_fScale;
2309
2310     m_modelview[8]  =  0;
2311     m_modelview[9]  =  m_fScale;
2312     m_modelview[10] =  0;
2313     break;
2314   case YZ:
2315     m_modelview[0]  =  0;
2316     m_modelview[1]  =  0;
2317     m_modelview[2]  = -m_fScale;
2318
2319     m_modelview[4]  =  m_fScale;
2320     m_modelview[5]  =  0;
2321     m_modelview[6]  =  0;
2322
2323     m_modelview[8]  =  0;
2324     m_modelview[9]  =  m_fScale;
2325     m_modelview[10] =  0;
2326     break;
2327   }
2328
2329   m_modelview[3] = m_modelview[7] = m_modelview[11] = 0;
2330   m_modelview[15] = 1;
2331
2332   m_view.Construct(m_projection, m_modelview, m_nWidth, m_nHeight);
2333 }
2334
2335 /*
2336 ==============
2337 XY_Draw
2338 ==============
2339 */
2340
2341 //#define DBG_SCENEDUMP
2342
2343 void XYWnd::XY_Draw()
2344 {
2345   //
2346   // clear
2347   //
2348   glViewport(0, 0, m_nWidth, m_nHeight);
2349   glClearColor (g_xywindow_globals.color_gridback[0],
2350                  g_xywindow_globals.color_gridback[1],
2351                  g_xywindow_globals.color_gridback[2],0);
2352
2353   glClear(GL_COLOR_BUFFER_BIT);
2354
2355   //
2356   // set up viewpoint
2357   //
2358
2359   glMatrixMode(GL_PROJECTION);
2360   glLoadMatrixf(reinterpret_cast<const float*>(&m_projection));
2361
2362   glMatrixMode(GL_MODELVIEW);
2363   glLoadIdentity();
2364   glScalef(m_fScale, m_fScale, 1);
2365   int nDim1 = (m_viewType == YZ) ? 1 : 0;
2366   int nDim2 = (m_viewType == XY) ? 1 : 2;
2367   glTranslatef(-m_vOrigin[nDim1], -m_vOrigin[nDim2], 0);
2368
2369   glDisable (GL_LINE_STIPPLE);
2370   glLineWidth(1);
2371   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
2372   glDisableClientState(GL_NORMAL_ARRAY);
2373   glDisableClientState(GL_COLOR_ARRAY);
2374   glDisable(GL_TEXTURE_2D);
2375   glDisable(GL_LIGHTING);
2376   glDisable(GL_COLOR_MATERIAL);
2377   glDisable(GL_DEPTH_TEST);
2378
2379   if (m_backgroundActivated)
2380     XY_DrawBackground();
2381   XY_DrawGrid();
2382
2383   if ( g_xywindow_globals_private.show_blocks)
2384     XY_DrawBlockGrid();
2385
2386   glLoadMatrixf(reinterpret_cast<const float*>(&m_modelview));
2387
2388   unsigned int globalstate = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_POLYGONSMOOTH | RENDER_LINESMOOTH;
2389   if(!g_xywindow_globals.m_bNoStipple)
2390   {
2391     globalstate |= RENDER_LINESTIPPLE;
2392   }
2393
2394   {
2395     XYRenderer renderer(globalstate, m_state_selected);
2396
2397     Scene_Render(renderer, m_view);
2398
2399     GlobalOpenGL_debugAssertNoErrors();
2400     renderer.render(m_modelview, m_projection);
2401     GlobalOpenGL_debugAssertNoErrors();
2402   }
2403
2404   glDepthMask(GL_FALSE);
2405
2406   GlobalOpenGL_debugAssertNoErrors();
2407
2408   glLoadMatrixf(reinterpret_cast<const float*>(&m_modelview));
2409
2410   GlobalOpenGL_debugAssertNoErrors();
2411   glDisable(GL_LINE_STIPPLE);
2412   GlobalOpenGL_debugAssertNoErrors();
2413   glLineWidth(1);
2414   GlobalOpenGL_debugAssertNoErrors();
2415   if(GlobalOpenGL().GL_1_3())
2416   {
2417     glActiveTexture(GL_TEXTURE0);
2418     glClientActiveTexture(GL_TEXTURE0);
2419   }
2420   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
2421   GlobalOpenGL_debugAssertNoErrors();
2422   glDisableClientState(GL_NORMAL_ARRAY);
2423   GlobalOpenGL_debugAssertNoErrors();
2424   glDisableClientState(GL_COLOR_ARRAY);
2425   GlobalOpenGL_debugAssertNoErrors();
2426   glDisable(GL_TEXTURE_2D);
2427   GlobalOpenGL_debugAssertNoErrors();
2428   glDisable(GL_LIGHTING);
2429   GlobalOpenGL_debugAssertNoErrors();
2430   glDisable(GL_COLOR_MATERIAL);
2431   GlobalOpenGL_debugAssertNoErrors();
2432
2433   GlobalOpenGL_debugAssertNoErrors();
2434
2435
2436   // size info
2437   if(g_xywindow_globals_private.m_bSizePaint && GlobalSelectionSystem().countSelected() != 0)
2438   {
2439     Vector3 min, max;
2440     Select_GetBounds(min, max);
2441     PaintSizeInfo(nDim1, nDim2, min, max);
2442   }
2443
2444   if (g_bCrossHairs)
2445   {
2446     glColor4f(0.2f, 0.9f, 0.2f, 0.8f);
2447     glBegin (GL_LINES);
2448     if (m_viewType == XY)
2449     {
2450       glVertex2f(2.0f * g_MinWorldCoord, m_mousePosition[1]);
2451       glVertex2f(2.0f * g_MaxWorldCoord, m_mousePosition[1]);
2452       glVertex2f(m_mousePosition[0], 2.0f * g_MinWorldCoord);
2453       glVertex2f(m_mousePosition[0], 2.0f * g_MaxWorldCoord);
2454     }
2455     else if (m_viewType == YZ)
2456     {
2457       glVertex3f(m_mousePosition[0], 2.0f * g_MinWorldCoord, m_mousePosition[2]);
2458       glVertex3f(m_mousePosition[0], 2.0f * g_MaxWorldCoord, m_mousePosition[2]);
2459       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MinWorldCoord);
2460       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MaxWorldCoord);
2461     }
2462     else
2463     {
2464       glVertex3f (2.0f * g_MinWorldCoord, m_mousePosition[1], m_mousePosition[2]);
2465       glVertex3f (2.0f * g_MaxWorldCoord, m_mousePosition[1], m_mousePosition[2]);
2466       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MinWorldCoord);
2467       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MaxWorldCoord);
2468     }
2469     glEnd();
2470   }
2471
2472   if (ClipMode())
2473   {
2474     GlobalClipPoints_Draw(m_fScale);
2475   }
2476
2477   GlobalOpenGL_debugAssertNoErrors();
2478
2479     // reset modelview
2480   glLoadIdentity();
2481   glScalef(m_fScale, m_fScale, 1);
2482   glTranslatef(-m_vOrigin[nDim1], -m_vOrigin[nDim2], 0);
2483
2484   DrawCameraIcon (Camera_getOrigin(*g_pParentWnd->GetCamWnd()), Camera_getAngles(*g_pParentWnd->GetCamWnd()));
2485
2486   Feedback_draw2D( m_viewType );
2487
2488   if (g_xywindow_globals_private.show_outline)
2489   {
2490     if (Active())
2491     {
2492       glMatrixMode (GL_PROJECTION);
2493       glLoadIdentity();
2494       glOrtho (0, m_nWidth, 0, m_nHeight, 0, 1);
2495
2496       glMatrixMode (GL_MODELVIEW);
2497       glLoadIdentity();
2498
2499       // four view mode doesn't colorize
2500       if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit)
2501         glColor3fv(vector3_to_array(g_xywindow_globals.color_viewname));
2502       else
2503       {
2504         switch(m_viewType)
2505         {
2506         case YZ:
2507           glColor3fv(vector3_to_array(g_xywindow_globals.AxisColorX));
2508           break;
2509         case XZ:
2510           glColor3fv(vector3_to_array(g_xywindow_globals.AxisColorY));
2511           break;
2512         case XY:
2513           glColor3fv(vector3_to_array(g_xywindow_globals.AxisColorZ));
2514           break;
2515         }
2516       }
2517       glBegin (GL_LINE_LOOP);
2518       glVertex2i (0, 0);
2519       glVertex2i (m_nWidth-1, 0);
2520       glVertex2i (m_nWidth-1, m_nHeight-1);
2521       glVertex2i (0, m_nHeight-1);
2522       glEnd();
2523     }
2524   }
2525
2526   GlobalOpenGL_debugAssertNoErrors();
2527
2528   glFinish();
2529 }
2530
2531 void XYWnd_MouseToPoint(XYWnd* xywnd, int x, int y, Vector3& point)
2532 {
2533   xywnd->XY_ToPoint(x, y, point);
2534   xywnd->XY_SnapToGrid(point);
2535
2536   int nDim = (xywnd->GetViewType() == XY) ? 2 : (xywnd->GetViewType() == YZ) ? 0 : 1;
2537   float fWorkMid = float_mid(Select_getWorkZone().d_work_min[nDim], Select_getWorkZone().d_work_max[nDim]);
2538   point[nDim] = float_snapped(fWorkMid, GetGridSize());
2539 }
2540
2541 void XYWnd::OnEntityCreate (const char* item)
2542 {
2543   StringOutputStream command;
2544   command << "entityCreate -class " << item;
2545   UndoableCommand undo(command.c_str());
2546   Vector3 point;
2547   XYWnd_MouseToPoint(this, m_entityCreate_x, m_entityCreate_y, point);
2548   Entity_createFromSelection(item, point);
2549 }
2550
2551
2552
2553 void GetFocusPosition(Vector3& position)
2554 {
2555   if(GlobalSelectionSystem().countSelected() != 0)
2556   {
2557     Select_GetMid(position);
2558   }
2559   else
2560   {
2561     position = Camera_getOrigin(*g_pParentWnd->GetCamWnd());
2562   }
2563 }
2564
2565 void XYWnd_Focus(XYWnd* xywnd)
2566 {
2567   Vector3 position;
2568   GetFocusPosition(position);
2569   xywnd->PositionView(position);
2570 }
2571
2572 void XY_Split_Focus()
2573 {
2574   Vector3 position;
2575   GetFocusPosition(position);
2576   g_pParentWnd->GetXYWnd()->PositionView(position);
2577   g_pParentWnd->GetXZWnd()->PositionView(position);
2578   g_pParentWnd->GetYZWnd()->PositionView(position);
2579 }
2580
2581 void XY_Focus()
2582 {
2583   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2584   {
2585           // cannot do this in a split window
2586           // do something else that the user may want here
2587           XY_Split_Focus();
2588           return;
2589   }
2590
2591   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2592   XYWnd_Focus(xywnd);
2593 }
2594
2595 void XY_Top()
2596 {
2597   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2598   {
2599           // cannot do this in a split window
2600           // do something else that the user may want here
2601           XY_Split_Focus();
2602           return;
2603   }
2604
2605   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2606   xywnd->SetViewType(XY);
2607   XYWnd_Focus(xywnd);
2608 }
2609
2610 void XY_Side()
2611 {
2612   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2613   {
2614           // cannot do this in a split window
2615           // do something else that the user may want here
2616           XY_Split_Focus();
2617           return;
2618   }
2619
2620   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2621   xywnd->SetViewType(XZ);
2622   XYWnd_Focus(xywnd);
2623 }
2624
2625 void XY_Front()
2626 {
2627   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2628   {
2629           // cannot do this in a split window
2630           // do something else that the user may want here
2631           XY_Split_Focus();
2632           return;
2633   }
2634
2635   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2636   xywnd->SetViewType(YZ);
2637   XYWnd_Focus(xywnd);
2638 }
2639
2640 void XY_Next()
2641 {
2642   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2643   {
2644           // cannot do this in a split window
2645           // do something else that the user may want here
2646           XY_Split_Focus();
2647           return;
2648   }
2649
2650   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2651   if (xywnd->GetViewType() == XY)
2652     xywnd->SetViewType(XZ);
2653   else if (xywnd->GetViewType() ==  XZ)
2654     xywnd->SetViewType(YZ);
2655   else
2656     xywnd->SetViewType(XY);
2657   XYWnd_Focus(xywnd);
2658 }
2659
2660 void XY_Zoom100()
2661 {
2662   if (g_pParentWnd->GetXYWnd())
2663     g_pParentWnd->GetXYWnd()->SetScale(1);
2664   if (g_pParentWnd->GetXZWnd())
2665     g_pParentWnd->GetXZWnd()->SetScale(1);
2666   if (g_pParentWnd->GetYZWnd())
2667     g_pParentWnd->GetYZWnd()->SetScale(1);
2668 }
2669
2670 void XY_ZoomIn()
2671 {
2672   XYWnd_ZoomIn(g_pParentWnd->ActiveXY());
2673 }
2674
2675 // NOTE: the zoom out factor is 4/5, we could think about customizing it
2676 //  we don't go below a zoom factor corresponding to 10% of the max world size
2677 //  (this has to be computed against the window size)
2678 void XY_ZoomOut()
2679 {
2680   XYWnd_ZoomOut(g_pParentWnd->ActiveXY());
2681 }
2682
2683
2684
2685 void ToggleShowCrosshair()
2686 {
2687   g_bCrossHairs ^= 1;
2688   XY_UpdateAllWindows();
2689 }
2690
2691 void ToggleShowSizeInfo()
2692 {
2693   g_xywindow_globals_private.m_bSizePaint = !g_xywindow_globals_private.m_bSizePaint;
2694   XY_UpdateAllWindows();
2695 }
2696
2697 void ToggleShowGrid()
2698 {
2699   g_xywindow_globals_private.d_showgrid = !g_xywindow_globals_private.d_showgrid;
2700   XY_UpdateAllWindows();
2701 }
2702
2703 ToggleShown g_xy_top_shown(true);
2704
2705 void XY_Top_Shown_Construct(GtkWindow* parent)
2706 {
2707   g_xy_top_shown.connect(GTK_WIDGET(parent));
2708 }
2709
2710 ToggleShown g_yz_side_shown(false);
2711
2712 void YZ_Side_Shown_Construct(GtkWindow* parent)
2713 {
2714   g_yz_side_shown.connect(GTK_WIDGET(parent));
2715 }
2716
2717 ToggleShown g_xz_front_shown(false);
2718
2719 void XZ_Front_Shown_Construct(GtkWindow* parent)
2720 {
2721   g_xz_front_shown.connect(GTK_WIDGET(parent));
2722 }
2723
2724
2725 class EntityClassMenu : public ModuleObserver
2726 {
2727   std::size_t m_unrealised;
2728 public:
2729   EntityClassMenu() : m_unrealised(1)
2730   {
2731   }
2732   void realise()
2733   {
2734     if(--m_unrealised == 0)
2735     {
2736     }
2737   }
2738   void unrealise()
2739   {
2740     if(++m_unrealised == 1)
2741     {
2742       if(XYWnd::m_mnuDrop != 0)
2743       {
2744         gtk_widget_destroy(GTK_WIDGET(XYWnd::m_mnuDrop));
2745         XYWnd::m_mnuDrop = 0;
2746       }
2747     }
2748   }
2749 };
2750
2751 EntityClassMenu g_EntityClassMenu;
2752
2753
2754
2755
2756 void ShowNamesToggle()
2757 {
2758   GlobalEntityCreator().setShowNames(!GlobalEntityCreator().getShowNames());
2759   XY_UpdateAllWindows();
2760 }
2761 typedef FreeCaller<ShowNamesToggle> ShowNamesToggleCaller;
2762 void ShowNamesExport(const BoolImportCallback& importer)
2763 {
2764   importer(GlobalEntityCreator().getShowNames());
2765 }
2766 typedef FreeCaller1<const BoolImportCallback&, ShowNamesExport> ShowNamesExportCaller;
2767
2768 void ShowAnglesToggle()
2769 {
2770   GlobalEntityCreator().setShowAngles(!GlobalEntityCreator().getShowAngles());
2771   XY_UpdateAllWindows();
2772 }
2773 typedef FreeCaller<ShowAnglesToggle> ShowAnglesToggleCaller;
2774 void ShowAnglesExport(const BoolImportCallback& importer)
2775 {
2776   importer(GlobalEntityCreator().getShowAngles());
2777 }
2778 typedef FreeCaller1<const BoolImportCallback&, ShowAnglesExport> ShowAnglesExportCaller;
2779
2780 void ShowBlocksToggle()
2781 {
2782   g_xywindow_globals_private.show_blocks ^= 1;
2783   XY_UpdateAllWindows();
2784 }
2785 typedef FreeCaller<ShowBlocksToggle> ShowBlocksToggleCaller;
2786 void ShowBlocksExport(const BoolImportCallback& importer)
2787 {
2788   importer(g_xywindow_globals_private.show_blocks);
2789 }
2790 typedef FreeCaller1<const BoolImportCallback&, ShowBlocksExport> ShowBlocksExportCaller;
2791
2792 void ShowCoordinatesToggle()
2793 {
2794   g_xywindow_globals_private.show_coordinates ^= 1;
2795   XY_UpdateAllWindows();
2796 }
2797 typedef FreeCaller<ShowCoordinatesToggle> ShowCoordinatesToggleCaller;
2798 void ShowCoordinatesExport(const BoolImportCallback& importer)
2799 {
2800   importer(g_xywindow_globals_private.show_coordinates);
2801 }
2802 typedef FreeCaller1<const BoolImportCallback&, ShowCoordinatesExport> ShowCoordinatesExportCaller;
2803
2804 void ShowOutlineToggle()
2805 {
2806   g_xywindow_globals_private.show_outline ^= 1;
2807   XY_UpdateAllWindows();
2808 }
2809 typedef FreeCaller<ShowOutlineToggle> ShowOutlineToggleCaller;
2810 void ShowOutlineExport(const BoolImportCallback& importer)
2811 {
2812   importer(g_xywindow_globals_private.show_outline);
2813 }
2814 typedef FreeCaller1<const BoolImportCallback&, ShowOutlineExport> ShowOutlineExportCaller;
2815
2816 void ShowAxesToggle()
2817 {
2818   g_xywindow_globals_private.show_axis ^= 1;
2819   XY_UpdateAllWindows();
2820 }
2821 typedef FreeCaller<ShowAxesToggle> ShowAxesToggleCaller;
2822 void ShowAxesExport(const BoolImportCallback& importer)
2823 {
2824   importer(g_xywindow_globals_private.show_axis);
2825 }
2826 typedef FreeCaller1<const BoolImportCallback&, ShowAxesExport> ShowAxesExportCaller;
2827
2828 void ShowWorkzoneToggle()
2829 {
2830   g_xywindow_globals_private.d_show_work ^= 1;
2831   XY_UpdateAllWindows();
2832 }
2833 typedef FreeCaller<ShowWorkzoneToggle> ShowWorkzoneToggleCaller;
2834 void ShowWorkzoneExport(const BoolImportCallback& importer)
2835 {
2836   importer(g_xywindow_globals_private.d_show_work);
2837 }
2838 typedef FreeCaller1<const BoolImportCallback&, ShowWorkzoneExport> ShowWorkzoneExportCaller;
2839
2840 ShowNamesExportCaller g_show_names_caller;
2841 BoolExportCallback g_show_names_callback(g_show_names_caller);
2842 ToggleItem g_show_names(g_show_names_callback);
2843
2844 ShowAnglesExportCaller g_show_angles_caller;
2845 BoolExportCallback g_show_angles_callback(g_show_angles_caller);
2846 ToggleItem g_show_angles(g_show_angles_callback);
2847
2848 ShowBlocksExportCaller g_show_blocks_caller;
2849 BoolExportCallback g_show_blocks_callback(g_show_blocks_caller);
2850 ToggleItem g_show_blocks(g_show_blocks_callback);
2851
2852 ShowCoordinatesExportCaller g_show_coordinates_caller;
2853 BoolExportCallback g_show_coordinates_callback(g_show_coordinates_caller);
2854 ToggleItem g_show_coordinates(g_show_coordinates_callback);
2855
2856 ShowOutlineExportCaller g_show_outline_caller;
2857 BoolExportCallback g_show_outline_callback(g_show_outline_caller);
2858 ToggleItem g_show_outline(g_show_outline_callback);
2859
2860 ShowAxesExportCaller g_show_axes_caller;
2861 BoolExportCallback g_show_axes_callback(g_show_axes_caller);
2862 ToggleItem g_show_axes(g_show_axes_callback);
2863
2864 ShowWorkzoneExportCaller g_show_workzone_caller;
2865 BoolExportCallback g_show_workzone_callback(g_show_workzone_caller);
2866 ToggleItem g_show_workzone(g_show_workzone_callback);
2867
2868 void XYShow_registerCommands()
2869 {
2870   GlobalToggles_insert("ShowAngles", ShowAnglesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_angles));
2871   GlobalToggles_insert("ShowNames", ShowNamesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_names));
2872   GlobalToggles_insert("ShowBlocks", ShowBlocksToggleCaller(), ToggleItem::AddCallbackCaller(g_show_blocks));
2873   GlobalToggles_insert("ShowCoordinates", ShowCoordinatesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_coordinates));
2874   GlobalToggles_insert("ShowWindowOutline", ShowOutlineToggleCaller(), ToggleItem::AddCallbackCaller(g_show_outline));
2875   GlobalToggles_insert("ShowAxes", ShowAxesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_axes));
2876   GlobalToggles_insert("ShowWorkzone", ShowWorkzoneToggleCaller(), ToggleItem::AddCallbackCaller(g_show_workzone));
2877 }
2878
2879 void XYWnd_registerShortcuts()
2880 {
2881   command_connect_accelerator("ToggleCrosshairs");
2882   command_connect_accelerator("ToggleSizePaint");
2883 }
2884
2885
2886
2887 void Orthographic_constructPreferences(PreferencesPage& page)
2888 {
2889   page.appendCheckBox("", "Solid selection boxes", g_xywindow_globals.m_bNoStipple);
2890   page.appendCheckBox("", "Display size info", g_xywindow_globals_private.m_bSizePaint);
2891   page.appendCheckBox("", "Chase mouse during drags", g_xywindow_globals_private.m_bChaseMouse);
2892   page.appendCheckBox("", "Update views on camera move", g_xywindow_globals_private.m_bCamXYUpdate);
2893 }
2894 void Orthographic_constructPage(PreferenceGroup& group)
2895 {
2896   PreferencesPage page(group.createPage("Orthographic", "Orthographic View Preferences"));
2897   Orthographic_constructPreferences(page);
2898 }
2899 void Orthographic_registerPreferencesPage()
2900 {
2901   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, Orthographic_constructPage>());
2902 }
2903
2904 void Clipper_constructPreferences(PreferencesPage& page)
2905 {
2906   page.appendCheckBox("", "Clipper tool uses caulk", g_clip_useCaulk);
2907 }
2908 void Clipper_constructPage(PreferenceGroup& group)
2909 {
2910   PreferencesPage page(group.createPage("Clipper", "Clipper Tool Settings"));
2911   Clipper_constructPreferences(page);
2912 }
2913 void Clipper_registerPreferencesPage()
2914 {
2915   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, Clipper_constructPage>());
2916 }
2917
2918
2919 #include "preferencesystem.h"
2920 #include "stringio.h"
2921
2922
2923
2924
2925 void ToggleShown_importBool(ToggleShown& self, bool value)
2926 {
2927   self.set(value);
2928 }
2929 typedef ReferenceCaller1<ToggleShown, bool, ToggleShown_importBool> ToggleShownImportBoolCaller;
2930 void ToggleShown_exportBool(const ToggleShown& self, const BoolImportCallback& importer)
2931 {
2932   importer(self.active());
2933 }
2934 typedef ConstReferenceCaller1<ToggleShown, const BoolImportCallback&, ToggleShown_exportBool> ToggleShownExportBoolCaller;
2935
2936
2937 void XYWindow_Construct()
2938 {
2939   GlobalCommands_insert("ToggleCrosshairs", FreeCaller<ToggleShowCrosshair>(), Accelerator('X', (GdkModifierType)GDK_SHIFT_MASK));
2940   GlobalCommands_insert("ToggleSizePaint", FreeCaller<ToggleShowSizeInfo>(), Accelerator('J'));
2941   GlobalCommands_insert("ToggleGrid", FreeCaller<ToggleShowGrid>(), Accelerator('0'));
2942
2943   GlobalToggles_insert("ToggleView", ToggleShown::ToggleCaller(g_xy_top_shown), ToggleItem::AddCallbackCaller(g_xy_top_shown.m_item), Accelerator('V', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2944   GlobalToggles_insert("ToggleSideView", ToggleShown::ToggleCaller(g_yz_side_shown), ToggleItem::AddCallbackCaller(g_yz_side_shown.m_item));
2945   GlobalToggles_insert("ToggleFrontView", ToggleShown::ToggleCaller(g_xz_front_shown), ToggleItem::AddCallbackCaller(g_xz_front_shown.m_item));
2946   GlobalCommands_insert("NextView", FreeCaller<XY_Next>(), Accelerator(GDK_Tab, (GdkModifierType)GDK_CONTROL_MASK));
2947   GlobalCommands_insert("ZoomIn", FreeCaller<XY_ZoomIn>(), Accelerator(GDK_Delete));
2948   GlobalCommands_insert("ZoomOut", FreeCaller<XY_ZoomOut>(), Accelerator(GDK_Insert));
2949   GlobalCommands_insert("ViewTop", FreeCaller<XY_Top>());
2950   GlobalCommands_insert("ViewSide", FreeCaller<XY_Side>());
2951   GlobalCommands_insert("ViewFront", FreeCaller<XY_Front>());
2952   GlobalCommands_insert("Zoom100", FreeCaller<XY_Zoom100>());
2953   GlobalCommands_insert("CenterXYView", FreeCaller<XY_Focus>(), Accelerator(GDK_Tab, (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2954
2955   GlobalPreferenceSystem().registerPreference("ClipCaulk", BoolImportStringCaller(g_clip_useCaulk), BoolExportStringCaller(g_clip_useCaulk));
2956
2957   GlobalPreferenceSystem().registerPreference("NewRightClick", BoolImportStringCaller(g_xywindow_globals.m_bRightClick), BoolExportStringCaller(g_xywindow_globals.m_bRightClick));
2958   GlobalPreferenceSystem().registerPreference("ChaseMouse", BoolImportStringCaller(g_xywindow_globals_private.m_bChaseMouse), BoolExportStringCaller(g_xywindow_globals_private.m_bChaseMouse));
2959   GlobalPreferenceSystem().registerPreference("SizePainting", BoolImportStringCaller(g_xywindow_globals_private.m_bSizePaint), BoolExportStringCaller(g_xywindow_globals_private.m_bSizePaint));
2960   GlobalPreferenceSystem().registerPreference("NoStipple", BoolImportStringCaller(g_xywindow_globals.m_bNoStipple), BoolExportStringCaller(g_xywindow_globals.m_bNoStipple));
2961   GlobalPreferenceSystem().registerPreference("SI_ShowCoords", BoolImportStringCaller(g_xywindow_globals_private.show_coordinates), BoolExportStringCaller(g_xywindow_globals_private.show_coordinates));
2962   GlobalPreferenceSystem().registerPreference("SI_ShowOutlines", BoolImportStringCaller(g_xywindow_globals_private.show_outline), BoolExportStringCaller(g_xywindow_globals_private.show_outline));
2963   GlobalPreferenceSystem().registerPreference("SI_ShowAxis", BoolImportStringCaller(g_xywindow_globals_private.show_axis), BoolExportStringCaller(g_xywindow_globals_private.show_axis));
2964   GlobalPreferenceSystem().registerPreference("CamXYUpdate", BoolImportStringCaller(g_xywindow_globals_private.m_bCamXYUpdate), BoolExportStringCaller(g_xywindow_globals_private.m_bCamXYUpdate));
2965   GlobalPreferenceSystem().registerPreference("ShowWorkzone", BoolImportStringCaller(g_xywindow_globals_private.d_show_work), BoolExportStringCaller(g_xywindow_globals_private.d_show_work));
2966
2967   GlobalPreferenceSystem().registerPreference("SI_AxisColors0", Vector3ImportStringCaller(g_xywindow_globals.AxisColorX), Vector3ExportStringCaller(g_xywindow_globals.AxisColorX));
2968   GlobalPreferenceSystem().registerPreference("SI_AxisColors1", Vector3ImportStringCaller(g_xywindow_globals.AxisColorY), Vector3ExportStringCaller(g_xywindow_globals.AxisColorY));
2969   GlobalPreferenceSystem().registerPreference("SI_AxisColors2", Vector3ImportStringCaller(g_xywindow_globals.AxisColorZ), Vector3ExportStringCaller(g_xywindow_globals.AxisColorZ));
2970   GlobalPreferenceSystem().registerPreference("SI_Colors1", Vector3ImportStringCaller(g_xywindow_globals.color_gridback), Vector3ExportStringCaller(g_xywindow_globals.color_gridback));
2971   GlobalPreferenceSystem().registerPreference("SI_Colors2", Vector3ImportStringCaller(g_xywindow_globals.color_gridminor), Vector3ExportStringCaller(g_xywindow_globals.color_gridminor));
2972   GlobalPreferenceSystem().registerPreference("SI_Colors3", Vector3ImportStringCaller(g_xywindow_globals.color_gridmajor), Vector3ExportStringCaller(g_xywindow_globals.color_gridmajor));
2973   GlobalPreferenceSystem().registerPreference("SI_Colors6", Vector3ImportStringCaller(g_xywindow_globals.color_gridblock), Vector3ExportStringCaller(g_xywindow_globals.color_gridblock));
2974   GlobalPreferenceSystem().registerPreference("SI_Colors7", Vector3ImportStringCaller(g_xywindow_globals.color_gridtext), Vector3ExportStringCaller(g_xywindow_globals.color_gridtext));
2975   GlobalPreferenceSystem().registerPreference("SI_Colors8", Vector3ImportStringCaller(g_xywindow_globals.color_brushes), Vector3ExportStringCaller(g_xywindow_globals.color_brushes));
2976   GlobalPreferenceSystem().registerPreference("SI_Colors9", Vector3ImportStringCaller(g_xywindow_globals.color_selbrushes), Vector3ExportStringCaller(g_xywindow_globals.color_selbrushes));
2977   GlobalPreferenceSystem().registerPreference("SI_Colors10", Vector3ImportStringCaller(g_xywindow_globals.color_clipper), Vector3ExportStringCaller(g_xywindow_globals.color_clipper));
2978   GlobalPreferenceSystem().registerPreference("SI_Colors11", Vector3ImportStringCaller(g_xywindow_globals.color_viewname), Vector3ExportStringCaller(g_xywindow_globals.color_viewname));
2979   GlobalPreferenceSystem().registerPreference("SI_Colors13", Vector3ImportStringCaller(g_xywindow_globals.color_gridminor_alt), Vector3ExportStringCaller(g_xywindow_globals.color_gridminor_alt));
2980   GlobalPreferenceSystem().registerPreference("SI_Colors14", Vector3ImportStringCaller(g_xywindow_globals.color_gridmajor_alt), Vector3ExportStringCaller(g_xywindow_globals.color_gridmajor_alt));
2981
2982
2983   GlobalPreferenceSystem().registerPreference("XZVIS", makeBoolStringImportCallback(ToggleShownImportBoolCaller(g_xz_front_shown)), makeBoolStringExportCallback(ToggleShownExportBoolCaller(g_xz_front_shown)));
2984   GlobalPreferenceSystem().registerPreference("YZVIS", makeBoolStringImportCallback(ToggleShownImportBoolCaller(g_yz_side_shown)), makeBoolStringExportCallback(ToggleShownExportBoolCaller(g_yz_side_shown)));
2985
2986   Orthographic_registerPreferencesPage();
2987   Clipper_registerPreferencesPage();
2988
2989   XYWnd::captureStates();
2990   GlobalEntityClassManager().attach(g_EntityClassMenu);
2991 }
2992
2993 void XYWindow_Destroy()
2994 {
2995   GlobalEntityClassManager().detach(g_EntityClassMenu);
2996   XYWnd::releaseStates();
2997 }