slightly better font handling, enjoy!
[divverent/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                 float offx = m_vOrigin[nDim2] + h - (1 + GlobalOpenGL().m_fontAscent) / m_fScale;
1846                 float offy = m_vOrigin[nDim1] - w +  1                                / m_fScale;
1847                 for (x = xb - fmod(xb, stepx); x <= xe ; x += stepx) {
1848                         glRasterPos2f (x, offx);
1849                         sprintf (text, "%g", x);
1850                         GlobalOpenGL().drawString(text);
1851                 }
1852                 for (y = yb - fmod(yb, stepy); y <= ye ; y += stepy) {
1853                         glRasterPos2f (offy, y);
1854                         sprintf (text, "%g", y);
1855                         GlobalOpenGL().drawString(text);
1856                 }
1857
1858                 if (Active())
1859                         glColor3fv(vector3_to_array(g_xywindow_globals.color_viewname));
1860
1861                 // we do this part (the old way) only if show_axis is disabled
1862                 if (!g_xywindow_globals_private.show_axis) {
1863                         glRasterPos2f ( m_vOrigin[nDim1] - w + 35 / m_fScale, m_vOrigin[nDim2] + h - 20 / m_fScale );
1864
1865                         GlobalOpenGL().drawString(ViewType_getTitle(m_viewType));
1866                 }
1867         }
1868
1869         XYWnd::XY_DrawAxis();
1870
1871         // show current work zone?
1872         // the work zone is used to place dropped points and brushes
1873         if (g_xywindow_globals_private.d_show_work) {
1874                 glColor3f( 1.0f, 0.0f, 0.0f );
1875                 glBegin( GL_LINES );
1876                 glVertex2f( xb, Select_getWorkZone().d_work_min[nDim2] );
1877                 glVertex2f( xe, Select_getWorkZone().d_work_min[nDim2] );
1878                 glVertex2f( xb, Select_getWorkZone().d_work_max[nDim2] );
1879                 glVertex2f( xe, Select_getWorkZone().d_work_max[nDim2] );
1880                 glVertex2f( Select_getWorkZone().d_work_min[nDim1], yb );
1881                 glVertex2f( Select_getWorkZone().d_work_min[nDim1], ye );
1882                 glVertex2f( Select_getWorkZone().d_work_max[nDim1], yb );
1883                 glVertex2f( Select_getWorkZone().d_work_max[nDim1], ye );
1884                 glEnd();
1885         }
1886 }
1887
1888 /*
1889 ==============
1890 XY_DrawBlockGrid
1891 ==============
1892 */
1893 void XYWnd::XY_DrawBlockGrid()
1894 {
1895   if(Map_FindWorldspawn(g_map) == 0)
1896   {
1897     return;
1898   }
1899   const char *value = Node_getEntity(*Map_GetWorldspawn(g_map))->getKeyValue("_blocksize" );
1900   if (strlen(value))
1901         sscanf( value, "%i", &g_xywindow_globals_private.blockSize );
1902
1903   if (!g_xywindow_globals_private.blockSize || g_xywindow_globals_private.blockSize > 65536 || g_xywindow_globals_private.blockSize < 1024)
1904           // don't use custom blocksize if it is less than the default, or greater than the maximum world coordinate
1905         g_xywindow_globals_private.blockSize = 1024;
1906
1907   float x, y, xb, xe, yb, ye;
1908   float         w, h;
1909   char  text[32];
1910
1911   glDisable(GL_TEXTURE_2D);
1912   glDisable(GL_TEXTURE_1D);
1913   glDisable(GL_DEPTH_TEST);
1914   glDisable(GL_BLEND);
1915
1916   w = (m_nWidth / 2 / m_fScale);
1917   h = (m_nHeight / 2 / m_fScale);
1918
1919   int nDim1 = (m_viewType == YZ) ? 1 : 0;
1920   int nDim2 = (m_viewType == XY) ? 1 : 2;
1921
1922   xb = m_vOrigin[nDim1] - w;
1923   if (xb < region_mins[nDim1])
1924     xb = region_mins[nDim1];
1925   xb = static_cast<float>(g_xywindow_globals_private.blockSize * floor (xb/g_xywindow_globals_private.blockSize));
1926
1927   xe = m_vOrigin[nDim1] + w;
1928   if (xe > region_maxs[nDim1])
1929     xe = region_maxs[nDim1];
1930   xe = static_cast<float>(g_xywindow_globals_private.blockSize * ceil (xe/g_xywindow_globals_private.blockSize));
1931
1932   yb = m_vOrigin[nDim2] - h;
1933   if (yb < region_mins[nDim2])
1934     yb = region_mins[nDim2];
1935   yb = static_cast<float>(g_xywindow_globals_private.blockSize * floor (yb/g_xywindow_globals_private.blockSize));
1936
1937   ye = m_vOrigin[nDim2] + h;
1938   if (ye > region_maxs[nDim2])
1939     ye = region_maxs[nDim2];
1940   ye = static_cast<float>(g_xywindow_globals_private.blockSize * ceil (ye/g_xywindow_globals_private.blockSize));
1941
1942   // draw major blocks
1943
1944   glColor3fv(vector3_to_array(g_xywindow_globals.color_gridblock));
1945   glLineWidth (2);
1946
1947   glBegin (GL_LINES);
1948
1949   for (x=xb ; x<=xe ; x+=g_xywindow_globals_private.blockSize)
1950   {
1951     glVertex2f (x, yb);
1952     glVertex2f (x, ye);
1953   }
1954
1955   if (m_viewType == XY)
1956   {
1957         for (y=yb ; y<=ye ; y+=g_xywindow_globals_private.blockSize)
1958         {
1959           glVertex2f (xb, y);
1960           glVertex2f (xe, y);
1961         }
1962   }
1963
1964   glEnd();
1965   glLineWidth (1);
1966
1967   // draw coordinate text if needed
1968
1969   if (m_viewType == XY && m_fScale > .1)
1970   {
1971         for (x=xb ; x<xe ; x+=g_xywindow_globals_private.blockSize)
1972                 for (y=yb ; y<ye ; y+=g_xywindow_globals_private.blockSize)
1973                 {
1974                   glRasterPos2f (x+(g_xywindow_globals_private.blockSize/2), y+(g_xywindow_globals_private.blockSize/2));
1975                         sprintf (text, "%i,%i",(int)floor(x/g_xywindow_globals_private.blockSize), (int)floor(y/g_xywindow_globals_private.blockSize) );
1976                         GlobalOpenGL().drawString(text);
1977                 }
1978   }
1979
1980   glColor4f(0, 0, 0, 0);
1981 }
1982
1983 void XYWnd::DrawCameraIcon(const Vector3& origin, const Vector3& angles)
1984 {
1985   float x, y, fov, box;
1986   double a;
1987
1988   fov = 48 / m_fScale;
1989   box = 16 / m_fScale;
1990
1991   if (m_viewType == XY)
1992   {
1993     x = origin[0];
1994     y = origin[1];
1995     a = degrees_to_radians(angles[CAMERA_YAW]);
1996   }
1997   else if (m_viewType == YZ)
1998   {
1999     x = origin[1];
2000     y = origin[2];
2001     a = degrees_to_radians(angles[CAMERA_PITCH]);
2002   }
2003   else
2004   {
2005     x = origin[0];
2006     y = origin[2];
2007     a = degrees_to_radians(angles[CAMERA_PITCH]);
2008   }
2009
2010   glColor3f (0.0, 0.0, 1.0);
2011   glBegin(GL_LINE_STRIP);
2012   glVertex3f (x-box,y,0);
2013   glVertex3f (x,y+(box/2),0);
2014   glVertex3f (x+box,y,0);
2015   glVertex3f (x,y-(box/2),0);
2016   glVertex3f (x-box,y,0);
2017   glVertex3f (x+box,y,0);
2018   glEnd();
2019
2020   glBegin(GL_LINE_STRIP);
2021   glVertex3f (x + static_cast<float>(fov*cos(a+c_pi/4)), y + static_cast<float>(fov*sin(a+c_pi/4)), 0);
2022   glVertex3f (x, y, 0);
2023   glVertex3f (x + static_cast<float>(fov*cos(a-c_pi/4)), y + static_cast<float>(fov*sin(a-c_pi/4)), 0);
2024   glEnd();
2025
2026 }
2027
2028
2029 float Betwixt(float f1, float f2)
2030 {
2031   if (f1 > f2)
2032     return f2 + ((f1 - f2) / 2);
2033   else
2034     return f1 + ((f2 - f1) / 2);
2035 }
2036
2037
2038 // can be greatly simplified but per usual i am in a hurry
2039 // which is not an excuse, just a fact
2040 void XYWnd::PaintSizeInfo(int nDim1, int nDim2, Vector3& vMinBounds, Vector3& vMaxBounds)
2041 {
2042   if(vector3_equal(vMinBounds, vMaxBounds))
2043   {
2044     return;
2045   }
2046   const char* g_pDimStrings[] = {"x:", "y:", "z:"};
2047   typedef const char* OrgStrings[2];
2048   const OrgStrings g_pOrgStrings[] = { { "x:", "y:", }, { "x:", "z:", }, { "y:", "z:", } };
2049
2050   Vector3 vSize(vector3_subtracted(vMaxBounds, vMinBounds));
2051
2052   glColor3f(g_xywindow_globals.color_selbrushes[0] * .65f,
2053              g_xywindow_globals.color_selbrushes[1] * .65f,
2054              g_xywindow_globals.color_selbrushes[2] * .65f);
2055
2056   StringOutputStream dimensions(16);
2057
2058   if (m_viewType == XY)
2059   {
2060     glBegin (GL_LINES);
2061
2062     glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale, 0.0f);
2063     glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
2064
2065     glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale, 0.0f);
2066     glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale, 0.0f);
2067
2068     glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale, 0.0f);
2069     glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
2070
2071
2072     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, vMinBounds[nDim2], 0.0f);
2073     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2], 0.0f);
2074
2075     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2], 0.0f);
2076     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2], 0.0f);
2077
2078     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, vMaxBounds[nDim2], 0.0f);
2079     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2], 0.0f);
2080
2081     glEnd();
2082
2083     glRasterPos3f (Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]),  vMinBounds[nDim2] - 20.0f  / m_fScale, 0.0f);
2084     dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
2085     GlobalOpenGL().drawString(dimensions.c_str());
2086     dimensions.clear();
2087
2088     glRasterPos3f (vMaxBounds[nDim1] + 16.0f  / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]), 0.0f);
2089     dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
2090     GlobalOpenGL().drawString(dimensions.c_str());
2091     dimensions.clear();
2092
2093     glRasterPos3f (vMinBounds[nDim1] + 4, vMaxBounds[nDim2] + 8 / m_fScale, 0.0f);
2094     dimensions << "(" << g_pOrgStrings[0][0] << vMinBounds[nDim1] << "  " << g_pOrgStrings[0][1] << vMaxBounds[nDim2] << ")";
2095     GlobalOpenGL().drawString(dimensions.c_str());
2096   }
2097   else if (m_viewType == XZ)
2098   {
2099     glBegin (GL_LINES);
2100
2101     glVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 6.0f  / m_fScale);
2102     glVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale);
2103
2104     glVertex3f(vMinBounds[nDim1], 0,vMinBounds[nDim2] - 10.0f  / m_fScale);
2105     glVertex3f(vMaxBounds[nDim1], 0,vMinBounds[nDim2] - 10.0f  / m_fScale);
2106
2107     glVertex3f(vMaxBounds[nDim1], 0,vMinBounds[nDim2] - 6.0f  / m_fScale);
2108     glVertex3f(vMaxBounds[nDim1], 0,vMinBounds[nDim2] - 10.0f / m_fScale);
2109
2110
2111     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, 0,vMinBounds[nDim2]);
2112     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMinBounds[nDim2]);
2113
2114     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMinBounds[nDim2]);
2115     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMaxBounds[nDim2]);
2116
2117     glVertex3f(vMaxBounds[nDim1] + 6.0f  / m_fScale, 0,vMaxBounds[nDim2]);
2118     glVertex3f(vMaxBounds[nDim1] + 10.0f  / m_fScale, 0,vMaxBounds[nDim2]);
2119
2120     glEnd();
2121
2122     glRasterPos3f (Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), 0, vMinBounds[nDim2] - 20.0f  / m_fScale);
2123     dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
2124     GlobalOpenGL().drawString(dimensions.c_str());
2125     dimensions.clear();
2126
2127     glRasterPos3f (vMaxBounds[nDim1] + 16.0f  / m_fScale, 0, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]));
2128     dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
2129     GlobalOpenGL().drawString(dimensions.c_str());
2130     dimensions.clear();
2131
2132     glRasterPos3f (vMinBounds[nDim1] + 4, 0, vMaxBounds[nDim2] + 8 / m_fScale);
2133     dimensions << "(" << g_pOrgStrings[1][0] << vMinBounds[nDim1] << "  " << g_pOrgStrings[1][1] << vMaxBounds[nDim2] << ")";
2134     GlobalOpenGL().drawString(dimensions.c_str());
2135   }
2136   else
2137   {
2138     glBegin (GL_LINES);
2139
2140     glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale);
2141     glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
2142
2143     glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale);
2144     glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f  / m_fScale);
2145
2146     glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f  / m_fScale);
2147     glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
2148
2149
2150     glVertex3f(0, vMaxBounds[nDim1] + 6.0f  / m_fScale, vMinBounds[nDim2]);
2151     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2]);
2152
2153     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMinBounds[nDim2]);
2154     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2]);
2155
2156     glVertex3f(0, vMaxBounds[nDim1] + 6.0f  / m_fScale, vMaxBounds[nDim2]);
2157     glVertex3f(0, vMaxBounds[nDim1] + 10.0f  / m_fScale, vMaxBounds[nDim2]);
2158
2159     glEnd();
2160
2161     glRasterPos3f (0, Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]),  vMinBounds[nDim2] - 20.0f  / m_fScale);
2162     dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
2163     GlobalOpenGL().drawString(dimensions.c_str());
2164     dimensions.clear();
2165
2166     glRasterPos3f (0, vMaxBounds[nDim1] + 16.0f  / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]));
2167     dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
2168     GlobalOpenGL().drawString(dimensions.c_str());
2169     dimensions.clear();
2170
2171     glRasterPos3f (0, vMinBounds[nDim1] + 4.0f, vMaxBounds[nDim2] + 8 / m_fScale);
2172     dimensions << "(" << g_pOrgStrings[2][0] << vMinBounds[nDim1] << "  " << g_pOrgStrings[2][1] << vMaxBounds[nDim2] << ")";
2173     GlobalOpenGL().drawString(dimensions.c_str());
2174   }
2175 }
2176
2177 class XYRenderer: public Renderer
2178 {
2179   struct state_type
2180   {
2181     state_type() :
2182     m_highlight(0),
2183     m_state(0)
2184     {
2185     }
2186     unsigned int m_highlight;
2187     Shader* m_state;
2188   };
2189 public:
2190   XYRenderer(RenderStateFlags globalstate, Shader* selected) :
2191     m_globalstate(globalstate),
2192     m_state_selected(selected)
2193   {
2194     ASSERT_NOTNULL(selected);
2195     m_state_stack.push_back(state_type());
2196   }
2197
2198   void SetState(Shader* state, EStyle style)
2199   {
2200     ASSERT_NOTNULL(state);
2201     if(style == eWireframeOnly)
2202       m_state_stack.back().m_state = state;
2203   }
2204   const EStyle getStyle() const
2205   {
2206     return eWireframeOnly;
2207   }
2208   void PushState()
2209   {
2210     m_state_stack.push_back(m_state_stack.back());
2211   }
2212   void PopState()
2213   {
2214     ASSERT_MESSAGE(!m_state_stack.empty(), "popping empty stack");
2215     m_state_stack.pop_back();
2216   }
2217   void Highlight(EHighlightMode mode, bool bEnable = true)
2218   {
2219     (bEnable)
2220       ? m_state_stack.back().m_highlight |= mode
2221       : m_state_stack.back().m_highlight &= ~mode;
2222   }
2223   void addRenderable(const OpenGLRenderable& renderable, const Matrix4& localToWorld)
2224   {
2225     if(m_state_stack.back().m_highlight & ePrimitive)
2226     {
2227       m_state_selected->addRenderable(renderable, localToWorld);
2228     }
2229     else
2230     {
2231       m_state_stack.back().m_state->addRenderable(renderable, localToWorld);
2232     }
2233   }
2234
2235   void render(const Matrix4& modelview, const Matrix4& projection)
2236   {
2237     GlobalShaderCache().render(m_globalstate, modelview, projection);
2238   }
2239 private:
2240   std::vector<state_type> m_state_stack;
2241   RenderStateFlags m_globalstate;
2242   Shader* m_state_selected;
2243 };
2244
2245 void XYWnd::updateProjection()
2246 {
2247   m_projection[0] = 1.0f / static_cast<float>(m_nWidth / 2);
2248   m_projection[5] = 1.0f / static_cast<float>(m_nHeight / 2);
2249   m_projection[10] = 1.0f / (g_MaxWorldCoord * m_fScale);
2250
2251   m_projection[12] = 0.0f;
2252   m_projection[13] = 0.0f;
2253   m_projection[14] = -1.0f;
2254
2255   m_projection[1] =
2256   m_projection[2] =
2257   m_projection[3] =
2258
2259   m_projection[4] =
2260   m_projection[6] =
2261   m_projection[7] =
2262
2263   m_projection[8] =
2264   m_projection[9] =
2265   m_projection[11] = 0.0f;
2266
2267   m_projection[15] = 1.0f;
2268
2269   m_view.Construct(m_projection, m_modelview, m_nWidth, m_nHeight);
2270 }
2271
2272 // note: modelview matrix must have a uniform scale, otherwise strange things happen when rendering the rotation manipulator.
2273 void XYWnd::updateModelview()
2274 {
2275   int nDim1 = (m_viewType == YZ) ? 1 : 0;
2276   int nDim2 = (m_viewType == XY) ? 1 : 2;
2277
2278   // translation
2279   m_modelview[12] = -m_vOrigin[nDim1] * m_fScale;
2280   m_modelview[13] = -m_vOrigin[nDim2] * m_fScale;
2281   m_modelview[14] = g_MaxWorldCoord * m_fScale;
2282
2283   // axis base
2284   switch(m_viewType)
2285   {
2286   case XY:
2287     m_modelview[0]  =  m_fScale;
2288     m_modelview[1]  =  0;
2289     m_modelview[2]  =  0;
2290
2291     m_modelview[4]  =  0;
2292     m_modelview[5]  =  m_fScale;
2293     m_modelview[6]  =  0;
2294
2295     m_modelview[8]  =  0;
2296     m_modelview[9]  =  0;
2297     m_modelview[10] = -m_fScale;
2298     break;
2299   case XZ:
2300     m_modelview[0]  =  m_fScale;
2301     m_modelview[1]  =  0;
2302     m_modelview[2]  =  0;
2303
2304     m_modelview[4]  =  0;
2305     m_modelview[5]  =  0;
2306     m_modelview[6]  =  m_fScale;
2307
2308     m_modelview[8]  =  0;
2309     m_modelview[9]  =  m_fScale;
2310     m_modelview[10] =  0;
2311     break;
2312   case YZ:
2313     m_modelview[0]  =  0;
2314     m_modelview[1]  =  0;
2315     m_modelview[2]  = -m_fScale;
2316
2317     m_modelview[4]  =  m_fScale;
2318     m_modelview[5]  =  0;
2319     m_modelview[6]  =  0;
2320
2321     m_modelview[8]  =  0;
2322     m_modelview[9]  =  m_fScale;
2323     m_modelview[10] =  0;
2324     break;
2325   }
2326
2327   m_modelview[3] = m_modelview[7] = m_modelview[11] = 0;
2328   m_modelview[15] = 1;
2329
2330   m_view.Construct(m_projection, m_modelview, m_nWidth, m_nHeight);
2331 }
2332
2333 /*
2334 ==============
2335 XY_Draw
2336 ==============
2337 */
2338
2339 //#define DBG_SCENEDUMP
2340
2341 void XYWnd::XY_Draw()
2342 {
2343   //
2344   // clear
2345   //
2346   glViewport(0, 0, m_nWidth, m_nHeight);
2347   glClearColor (g_xywindow_globals.color_gridback[0],
2348                  g_xywindow_globals.color_gridback[1],
2349                  g_xywindow_globals.color_gridback[2],0);
2350
2351   glClear(GL_COLOR_BUFFER_BIT);
2352
2353   //
2354   // set up viewpoint
2355   //
2356
2357   glMatrixMode(GL_PROJECTION);
2358   glLoadMatrixf(reinterpret_cast<const float*>(&m_projection));
2359
2360   glMatrixMode(GL_MODELVIEW);
2361   glLoadIdentity();
2362   glScalef(m_fScale, m_fScale, 1);
2363   int nDim1 = (m_viewType == YZ) ? 1 : 0;
2364   int nDim2 = (m_viewType == XY) ? 1 : 2;
2365   glTranslatef(-m_vOrigin[nDim1], -m_vOrigin[nDim2], 0);
2366
2367   glDisable (GL_LINE_STIPPLE);
2368   glLineWidth(1);
2369   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
2370   glDisableClientState(GL_NORMAL_ARRAY);
2371   glDisableClientState(GL_COLOR_ARRAY);
2372   glDisable(GL_TEXTURE_2D);
2373   glDisable(GL_LIGHTING);
2374   glDisable(GL_COLOR_MATERIAL);
2375   glDisable(GL_DEPTH_TEST);
2376
2377   if (m_backgroundActivated)
2378     XY_DrawBackground();
2379   XY_DrawGrid();
2380
2381   if ( g_xywindow_globals_private.show_blocks)
2382     XY_DrawBlockGrid();
2383
2384   glLoadMatrixf(reinterpret_cast<const float*>(&m_modelview));
2385
2386   unsigned int globalstate = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_POLYGONSMOOTH | RENDER_LINESMOOTH;
2387   if(!g_xywindow_globals.m_bNoStipple)
2388   {
2389     globalstate |= RENDER_LINESTIPPLE;
2390   }
2391
2392   {
2393     XYRenderer renderer(globalstate, m_state_selected);
2394
2395     Scene_Render(renderer, m_view);
2396
2397     GlobalOpenGL_debugAssertNoErrors();
2398     renderer.render(m_modelview, m_projection);
2399     GlobalOpenGL_debugAssertNoErrors();
2400   }
2401
2402   glDepthMask(GL_FALSE);
2403
2404   GlobalOpenGL_debugAssertNoErrors();
2405
2406   glLoadMatrixf(reinterpret_cast<const float*>(&m_modelview));
2407
2408   GlobalOpenGL_debugAssertNoErrors();
2409   glDisable(GL_LINE_STIPPLE);
2410   GlobalOpenGL_debugAssertNoErrors();
2411   glLineWidth(1);
2412   GlobalOpenGL_debugAssertNoErrors();
2413   if(GlobalOpenGL().GL_1_3())
2414   {
2415     glActiveTexture(GL_TEXTURE0);
2416     glClientActiveTexture(GL_TEXTURE0);
2417   }
2418   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
2419   GlobalOpenGL_debugAssertNoErrors();
2420   glDisableClientState(GL_NORMAL_ARRAY);
2421   GlobalOpenGL_debugAssertNoErrors();
2422   glDisableClientState(GL_COLOR_ARRAY);
2423   GlobalOpenGL_debugAssertNoErrors();
2424   glDisable(GL_TEXTURE_2D);
2425   GlobalOpenGL_debugAssertNoErrors();
2426   glDisable(GL_LIGHTING);
2427   GlobalOpenGL_debugAssertNoErrors();
2428   glDisable(GL_COLOR_MATERIAL);
2429   GlobalOpenGL_debugAssertNoErrors();
2430
2431   GlobalOpenGL_debugAssertNoErrors();
2432
2433
2434   // size info
2435   if(g_xywindow_globals_private.m_bSizePaint && GlobalSelectionSystem().countSelected() != 0)
2436   {
2437     Vector3 min, max;
2438     Select_GetBounds(min, max);
2439     PaintSizeInfo(nDim1, nDim2, min, max);
2440   }
2441
2442   if (g_bCrossHairs)
2443   {
2444     glColor4f(0.2f, 0.9f, 0.2f, 0.8f);
2445     glBegin (GL_LINES);
2446     if (m_viewType == XY)
2447     {
2448       glVertex2f(2.0f * g_MinWorldCoord, m_mousePosition[1]);
2449       glVertex2f(2.0f * g_MaxWorldCoord, m_mousePosition[1]);
2450       glVertex2f(m_mousePosition[0], 2.0f * g_MinWorldCoord);
2451       glVertex2f(m_mousePosition[0], 2.0f * g_MaxWorldCoord);
2452     }
2453     else if (m_viewType == YZ)
2454     {
2455       glVertex3f(m_mousePosition[0], 2.0f * g_MinWorldCoord, m_mousePosition[2]);
2456       glVertex3f(m_mousePosition[0], 2.0f * g_MaxWorldCoord, m_mousePosition[2]);
2457       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MinWorldCoord);
2458       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MaxWorldCoord);
2459     }
2460     else
2461     {
2462       glVertex3f (2.0f * g_MinWorldCoord, m_mousePosition[1], m_mousePosition[2]);
2463       glVertex3f (2.0f * g_MaxWorldCoord, m_mousePosition[1], m_mousePosition[2]);
2464       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MinWorldCoord);
2465       glVertex3f(m_mousePosition[0], m_mousePosition[1], 2.0f * g_MaxWorldCoord);
2466     }
2467     glEnd();
2468   }
2469
2470   if (ClipMode())
2471   {
2472     GlobalClipPoints_Draw(m_fScale);
2473   }
2474
2475   GlobalOpenGL_debugAssertNoErrors();
2476
2477     // reset modelview
2478   glLoadIdentity();
2479   glScalef(m_fScale, m_fScale, 1);
2480   glTranslatef(-m_vOrigin[nDim1], -m_vOrigin[nDim2], 0);
2481
2482   DrawCameraIcon (Camera_getOrigin(*g_pParentWnd->GetCamWnd()), Camera_getAngles(*g_pParentWnd->GetCamWnd()));
2483
2484   Feedback_draw2D( m_viewType );
2485
2486   if (g_xywindow_globals_private.show_outline)
2487   {
2488     if (Active())
2489     {
2490       glMatrixMode (GL_PROJECTION);
2491       glLoadIdentity();
2492       glOrtho (0, m_nWidth, 0, m_nHeight, 0, 1);
2493
2494       glMatrixMode (GL_MODELVIEW);
2495       glLoadIdentity();
2496
2497       // four view mode doesn't colorize
2498       if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit)
2499         glColor3fv(vector3_to_array(g_xywindow_globals.color_viewname));
2500       else
2501       {
2502         switch(m_viewType)
2503         {
2504         case YZ:
2505           glColor3fv(vector3_to_array(g_xywindow_globals.AxisColorX));
2506           break;
2507         case XZ:
2508           glColor3fv(vector3_to_array(g_xywindow_globals.AxisColorY));
2509           break;
2510         case XY:
2511           glColor3fv(vector3_to_array(g_xywindow_globals.AxisColorZ));
2512           break;
2513         }
2514       }
2515       glBegin (GL_LINE_LOOP);
2516       glVertex2i (0, 0);
2517       glVertex2i (m_nWidth-1, 0);
2518       glVertex2i (m_nWidth-1, m_nHeight-1);
2519       glVertex2i (0, m_nHeight-1);
2520       glEnd();
2521     }
2522   }
2523
2524   GlobalOpenGL_debugAssertNoErrors();
2525
2526   glFinish();
2527 }
2528
2529 void XYWnd_MouseToPoint(XYWnd* xywnd, int x, int y, Vector3& point)
2530 {
2531   xywnd->XY_ToPoint(x, y, point);
2532   xywnd->XY_SnapToGrid(point);
2533
2534   int nDim = (xywnd->GetViewType() == XY) ? 2 : (xywnd->GetViewType() == YZ) ? 0 : 1;
2535   float fWorkMid = float_mid(Select_getWorkZone().d_work_min[nDim], Select_getWorkZone().d_work_max[nDim]);
2536   point[nDim] = float_snapped(fWorkMid, GetGridSize());
2537 }
2538
2539 void XYWnd::OnEntityCreate (const char* item)
2540 {
2541   StringOutputStream command;
2542   command << "entityCreate -class " << item;
2543   UndoableCommand undo(command.c_str());
2544   Vector3 point;
2545   XYWnd_MouseToPoint(this, m_entityCreate_x, m_entityCreate_y, point);
2546   Entity_createFromSelection(item, point);
2547 }
2548
2549
2550
2551 void GetFocusPosition(Vector3& position)
2552 {
2553   if(GlobalSelectionSystem().countSelected() != 0)
2554   {
2555     Select_GetMid(position);
2556   }
2557   else
2558   {
2559     position = Camera_getOrigin(*g_pParentWnd->GetCamWnd());
2560   }
2561 }
2562
2563 void XYWnd_Focus(XYWnd* xywnd)
2564 {
2565   Vector3 position;
2566   GetFocusPosition(position);
2567   xywnd->PositionView(position);
2568 }
2569
2570 void XY_Split_Focus()
2571 {
2572   Vector3 position;
2573   GetFocusPosition(position);
2574   g_pParentWnd->GetXYWnd()->PositionView(position);
2575   g_pParentWnd->GetXZWnd()->PositionView(position);
2576   g_pParentWnd->GetYZWnd()->PositionView(position);
2577 }
2578
2579 void XY_Focus()
2580 {
2581   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2582   {
2583           // cannot do this in a split window
2584           // do something else that the user may want here
2585           XY_Split_Focus();
2586           return;
2587   }
2588
2589   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2590   XYWnd_Focus(xywnd);
2591 }
2592
2593 void XY_Top()
2594 {
2595   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2596   {
2597           // cannot do this in a split window
2598           // do something else that the user may want here
2599           XY_Split_Focus();
2600           return;
2601   }
2602
2603   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2604   xywnd->SetViewType(XY);
2605   XYWnd_Focus(xywnd);
2606 }
2607
2608 void XY_Side()
2609 {
2610   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2611   {
2612           // cannot do this in a split window
2613           // do something else that the user may want here
2614           XY_Split_Focus();
2615           return;
2616   }
2617
2618   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2619   xywnd->SetViewType(XZ);
2620   XYWnd_Focus(xywnd);
2621 }
2622
2623 void XY_Front()
2624 {
2625   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2626   {
2627           // cannot do this in a split window
2628           // do something else that the user may want here
2629           XY_Split_Focus();
2630           return;
2631   }
2632
2633   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2634   xywnd->SetViewType(YZ);
2635   XYWnd_Focus(xywnd);
2636 }
2637
2638 void XY_Next()
2639 {
2640   if (g_pParentWnd->CurrentStyle() == MainFrame::eSplit || g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
2641   {
2642           // cannot do this in a split window
2643           // do something else that the user may want here
2644           XY_Split_Focus();
2645           return;
2646   }
2647
2648   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
2649   if (xywnd->GetViewType() == XY)
2650     xywnd->SetViewType(XZ);
2651   else if (xywnd->GetViewType() ==  XZ)
2652     xywnd->SetViewType(YZ);
2653   else
2654     xywnd->SetViewType(XY);
2655   XYWnd_Focus(xywnd);
2656 }
2657
2658 void XY_Zoom100()
2659 {
2660   if (g_pParentWnd->GetXYWnd())
2661     g_pParentWnd->GetXYWnd()->SetScale(1);
2662   if (g_pParentWnd->GetXZWnd())
2663     g_pParentWnd->GetXZWnd()->SetScale(1);
2664   if (g_pParentWnd->GetYZWnd())
2665     g_pParentWnd->GetYZWnd()->SetScale(1);
2666 }
2667
2668 void XY_ZoomIn()
2669 {
2670   XYWnd_ZoomIn(g_pParentWnd->ActiveXY());
2671 }
2672
2673 // NOTE: the zoom out factor is 4/5, we could think about customizing it
2674 //  we don't go below a zoom factor corresponding to 10% of the max world size
2675 //  (this has to be computed against the window size)
2676 void XY_ZoomOut()
2677 {
2678   XYWnd_ZoomOut(g_pParentWnd->ActiveXY());
2679 }
2680
2681
2682
2683 void ToggleShowCrosshair()
2684 {
2685   g_bCrossHairs ^= 1;
2686   XY_UpdateAllWindows();
2687 }
2688
2689 void ToggleShowSizeInfo()
2690 {
2691   g_xywindow_globals_private.m_bSizePaint = !g_xywindow_globals_private.m_bSizePaint;
2692   XY_UpdateAllWindows();
2693 }
2694
2695 void ToggleShowGrid()
2696 {
2697   g_xywindow_globals_private.d_showgrid = !g_xywindow_globals_private.d_showgrid;
2698   XY_UpdateAllWindows();
2699 }
2700
2701 ToggleShown g_xy_top_shown(true);
2702
2703 void XY_Top_Shown_Construct(GtkWindow* parent)
2704 {
2705   g_xy_top_shown.connect(GTK_WIDGET(parent));
2706 }
2707
2708 ToggleShown g_yz_side_shown(false);
2709
2710 void YZ_Side_Shown_Construct(GtkWindow* parent)
2711 {
2712   g_yz_side_shown.connect(GTK_WIDGET(parent));
2713 }
2714
2715 ToggleShown g_xz_front_shown(false);
2716
2717 void XZ_Front_Shown_Construct(GtkWindow* parent)
2718 {
2719   g_xz_front_shown.connect(GTK_WIDGET(parent));
2720 }
2721
2722
2723 class EntityClassMenu : public ModuleObserver
2724 {
2725   std::size_t m_unrealised;
2726 public:
2727   EntityClassMenu() : m_unrealised(1)
2728   {
2729   }
2730   void realise()
2731   {
2732     if(--m_unrealised == 0)
2733     {
2734     }
2735   }
2736   void unrealise()
2737   {
2738     if(++m_unrealised == 1)
2739     {
2740       if(XYWnd::m_mnuDrop != 0)
2741       {
2742         gtk_widget_destroy(GTK_WIDGET(XYWnd::m_mnuDrop));
2743         XYWnd::m_mnuDrop = 0;
2744       }
2745     }
2746   }
2747 };
2748
2749 EntityClassMenu g_EntityClassMenu;
2750
2751
2752
2753
2754 void ShowNamesToggle()
2755 {
2756   GlobalEntityCreator().setShowNames(!GlobalEntityCreator().getShowNames());
2757   XY_UpdateAllWindows();
2758 }
2759 typedef FreeCaller<ShowNamesToggle> ShowNamesToggleCaller;
2760 void ShowNamesExport(const BoolImportCallback& importer)
2761 {
2762   importer(GlobalEntityCreator().getShowNames());
2763 }
2764 typedef FreeCaller1<const BoolImportCallback&, ShowNamesExport> ShowNamesExportCaller;
2765
2766 void ShowAnglesToggle()
2767 {
2768   GlobalEntityCreator().setShowAngles(!GlobalEntityCreator().getShowAngles());
2769   XY_UpdateAllWindows();
2770 }
2771 typedef FreeCaller<ShowAnglesToggle> ShowAnglesToggleCaller;
2772 void ShowAnglesExport(const BoolImportCallback& importer)
2773 {
2774   importer(GlobalEntityCreator().getShowAngles());
2775 }
2776 typedef FreeCaller1<const BoolImportCallback&, ShowAnglesExport> ShowAnglesExportCaller;
2777
2778 void ShowBlocksToggle()
2779 {
2780   g_xywindow_globals_private.show_blocks ^= 1;
2781   XY_UpdateAllWindows();
2782 }
2783 typedef FreeCaller<ShowBlocksToggle> ShowBlocksToggleCaller;
2784 void ShowBlocksExport(const BoolImportCallback& importer)
2785 {
2786   importer(g_xywindow_globals_private.show_blocks);
2787 }
2788 typedef FreeCaller1<const BoolImportCallback&, ShowBlocksExport> ShowBlocksExportCaller;
2789
2790 void ShowCoordinatesToggle()
2791 {
2792   g_xywindow_globals_private.show_coordinates ^= 1;
2793   XY_UpdateAllWindows();
2794 }
2795 typedef FreeCaller<ShowCoordinatesToggle> ShowCoordinatesToggleCaller;
2796 void ShowCoordinatesExport(const BoolImportCallback& importer)
2797 {
2798   importer(g_xywindow_globals_private.show_coordinates);
2799 }
2800 typedef FreeCaller1<const BoolImportCallback&, ShowCoordinatesExport> ShowCoordinatesExportCaller;
2801
2802 void ShowOutlineToggle()
2803 {
2804   g_xywindow_globals_private.show_outline ^= 1;
2805   XY_UpdateAllWindows();
2806 }
2807 typedef FreeCaller<ShowOutlineToggle> ShowOutlineToggleCaller;
2808 void ShowOutlineExport(const BoolImportCallback& importer)
2809 {
2810   importer(g_xywindow_globals_private.show_outline);
2811 }
2812 typedef FreeCaller1<const BoolImportCallback&, ShowOutlineExport> ShowOutlineExportCaller;
2813
2814 void ShowAxesToggle()
2815 {
2816   g_xywindow_globals_private.show_axis ^= 1;
2817   XY_UpdateAllWindows();
2818 }
2819 typedef FreeCaller<ShowAxesToggle> ShowAxesToggleCaller;
2820 void ShowAxesExport(const BoolImportCallback& importer)
2821 {
2822   importer(g_xywindow_globals_private.show_axis);
2823 }
2824 typedef FreeCaller1<const BoolImportCallback&, ShowAxesExport> ShowAxesExportCaller;
2825
2826 void ShowWorkzoneToggle()
2827 {
2828   g_xywindow_globals_private.d_show_work ^= 1;
2829   XY_UpdateAllWindows();
2830 }
2831 typedef FreeCaller<ShowWorkzoneToggle> ShowWorkzoneToggleCaller;
2832 void ShowWorkzoneExport(const BoolImportCallback& importer)
2833 {
2834   importer(g_xywindow_globals_private.d_show_work);
2835 }
2836 typedef FreeCaller1<const BoolImportCallback&, ShowWorkzoneExport> ShowWorkzoneExportCaller;
2837
2838 ShowNamesExportCaller g_show_names_caller;
2839 BoolExportCallback g_show_names_callback(g_show_names_caller);
2840 ToggleItem g_show_names(g_show_names_callback);
2841
2842 ShowAnglesExportCaller g_show_angles_caller;
2843 BoolExportCallback g_show_angles_callback(g_show_angles_caller);
2844 ToggleItem g_show_angles(g_show_angles_callback);
2845
2846 ShowBlocksExportCaller g_show_blocks_caller;
2847 BoolExportCallback g_show_blocks_callback(g_show_blocks_caller);
2848 ToggleItem g_show_blocks(g_show_blocks_callback);
2849
2850 ShowCoordinatesExportCaller g_show_coordinates_caller;
2851 BoolExportCallback g_show_coordinates_callback(g_show_coordinates_caller);
2852 ToggleItem g_show_coordinates(g_show_coordinates_callback);
2853
2854 ShowOutlineExportCaller g_show_outline_caller;
2855 BoolExportCallback g_show_outline_callback(g_show_outline_caller);
2856 ToggleItem g_show_outline(g_show_outline_callback);
2857
2858 ShowAxesExportCaller g_show_axes_caller;
2859 BoolExportCallback g_show_axes_callback(g_show_axes_caller);
2860 ToggleItem g_show_axes(g_show_axes_callback);
2861
2862 ShowWorkzoneExportCaller g_show_workzone_caller;
2863 BoolExportCallback g_show_workzone_callback(g_show_workzone_caller);
2864 ToggleItem g_show_workzone(g_show_workzone_callback);
2865
2866 void XYShow_registerCommands()
2867 {
2868   GlobalToggles_insert("ShowAngles", ShowAnglesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_angles));
2869   GlobalToggles_insert("ShowNames", ShowNamesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_names));
2870   GlobalToggles_insert("ShowBlocks", ShowBlocksToggleCaller(), ToggleItem::AddCallbackCaller(g_show_blocks));
2871   GlobalToggles_insert("ShowCoordinates", ShowCoordinatesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_coordinates));
2872   GlobalToggles_insert("ShowWindowOutline", ShowOutlineToggleCaller(), ToggleItem::AddCallbackCaller(g_show_outline));
2873   GlobalToggles_insert("ShowAxes", ShowAxesToggleCaller(), ToggleItem::AddCallbackCaller(g_show_axes));
2874   GlobalToggles_insert("ShowWorkzone", ShowWorkzoneToggleCaller(), ToggleItem::AddCallbackCaller(g_show_workzone));
2875 }
2876
2877 void XYWnd_registerShortcuts()
2878 {
2879   command_connect_accelerator("ToggleCrosshairs");
2880   command_connect_accelerator("ToggleSizePaint");
2881 }
2882
2883
2884
2885 void Orthographic_constructPreferences(PreferencesPage& page)
2886 {
2887   page.appendCheckBox("", "Solid selection boxes", g_xywindow_globals.m_bNoStipple);
2888   page.appendCheckBox("", "Display size info", g_xywindow_globals_private.m_bSizePaint);
2889   page.appendCheckBox("", "Chase mouse during drags", g_xywindow_globals_private.m_bChaseMouse);
2890   page.appendCheckBox("", "Update views on camera move", g_xywindow_globals_private.m_bCamXYUpdate);
2891 }
2892 void Orthographic_constructPage(PreferenceGroup& group)
2893 {
2894   PreferencesPage page(group.createPage("Orthographic", "Orthographic View Preferences"));
2895   Orthographic_constructPreferences(page);
2896 }
2897 void Orthographic_registerPreferencesPage()
2898 {
2899   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, Orthographic_constructPage>());
2900 }
2901
2902 void Clipper_constructPreferences(PreferencesPage& page)
2903 {
2904   page.appendCheckBox("", "Clipper tool uses caulk", g_clip_useCaulk);
2905 }
2906 void Clipper_constructPage(PreferenceGroup& group)
2907 {
2908   PreferencesPage page(group.createPage("Clipper", "Clipper Tool Settings"));
2909   Clipper_constructPreferences(page);
2910 }
2911 void Clipper_registerPreferencesPage()
2912 {
2913   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, Clipper_constructPage>());
2914 }
2915
2916
2917 #include "preferencesystem.h"
2918 #include "stringio.h"
2919
2920
2921
2922
2923 void ToggleShown_importBool(ToggleShown& self, bool value)
2924 {
2925   self.set(value);
2926 }
2927 typedef ReferenceCaller1<ToggleShown, bool, ToggleShown_importBool> ToggleShownImportBoolCaller;
2928 void ToggleShown_exportBool(const ToggleShown& self, const BoolImportCallback& importer)
2929 {
2930   importer(self.active());
2931 }
2932 typedef ConstReferenceCaller1<ToggleShown, const BoolImportCallback&, ToggleShown_exportBool> ToggleShownExportBoolCaller;
2933
2934
2935 void XYWindow_Construct()
2936 {
2937   GlobalCommands_insert("ToggleCrosshairs", FreeCaller<ToggleShowCrosshair>(), Accelerator('X', (GdkModifierType)GDK_SHIFT_MASK));
2938   GlobalCommands_insert("ToggleSizePaint", FreeCaller<ToggleShowSizeInfo>(), Accelerator('J'));
2939   GlobalCommands_insert("ToggleGrid", FreeCaller<ToggleShowGrid>(), Accelerator('0'));
2940
2941   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)));
2942   GlobalToggles_insert("ToggleSideView", ToggleShown::ToggleCaller(g_yz_side_shown), ToggleItem::AddCallbackCaller(g_yz_side_shown.m_item));
2943   GlobalToggles_insert("ToggleFrontView", ToggleShown::ToggleCaller(g_xz_front_shown), ToggleItem::AddCallbackCaller(g_xz_front_shown.m_item));
2944   GlobalCommands_insert("NextView", FreeCaller<XY_Next>(), Accelerator(GDK_Tab, (GdkModifierType)GDK_CONTROL_MASK));
2945   GlobalCommands_insert("ZoomIn", FreeCaller<XY_ZoomIn>(), Accelerator(GDK_Delete));
2946   GlobalCommands_insert("ZoomOut", FreeCaller<XY_ZoomOut>(), Accelerator(GDK_Insert));
2947   GlobalCommands_insert("ViewTop", FreeCaller<XY_Top>());
2948   GlobalCommands_insert("ViewSide", FreeCaller<XY_Side>());
2949   GlobalCommands_insert("ViewFront", FreeCaller<XY_Front>());
2950   GlobalCommands_insert("Zoom100", FreeCaller<XY_Zoom100>());
2951   GlobalCommands_insert("CenterXYView", FreeCaller<XY_Focus>(), Accelerator(GDK_Tab, (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2952
2953   GlobalPreferenceSystem().registerPreference("ClipCaulk", BoolImportStringCaller(g_clip_useCaulk), BoolExportStringCaller(g_clip_useCaulk));
2954
2955   GlobalPreferenceSystem().registerPreference("NewRightClick", BoolImportStringCaller(g_xywindow_globals.m_bRightClick), BoolExportStringCaller(g_xywindow_globals.m_bRightClick));
2956   GlobalPreferenceSystem().registerPreference("ChaseMouse", BoolImportStringCaller(g_xywindow_globals_private.m_bChaseMouse), BoolExportStringCaller(g_xywindow_globals_private.m_bChaseMouse));
2957   GlobalPreferenceSystem().registerPreference("SizePainting", BoolImportStringCaller(g_xywindow_globals_private.m_bSizePaint), BoolExportStringCaller(g_xywindow_globals_private.m_bSizePaint));
2958   GlobalPreferenceSystem().registerPreference("NoStipple", BoolImportStringCaller(g_xywindow_globals.m_bNoStipple), BoolExportStringCaller(g_xywindow_globals.m_bNoStipple));
2959   GlobalPreferenceSystem().registerPreference("SI_ShowCoords", BoolImportStringCaller(g_xywindow_globals_private.show_coordinates), BoolExportStringCaller(g_xywindow_globals_private.show_coordinates));
2960   GlobalPreferenceSystem().registerPreference("SI_ShowOutlines", BoolImportStringCaller(g_xywindow_globals_private.show_outline), BoolExportStringCaller(g_xywindow_globals_private.show_outline));
2961   GlobalPreferenceSystem().registerPreference("SI_ShowAxis", BoolImportStringCaller(g_xywindow_globals_private.show_axis), BoolExportStringCaller(g_xywindow_globals_private.show_axis));
2962   GlobalPreferenceSystem().registerPreference("CamXYUpdate", BoolImportStringCaller(g_xywindow_globals_private.m_bCamXYUpdate), BoolExportStringCaller(g_xywindow_globals_private.m_bCamXYUpdate));
2963   GlobalPreferenceSystem().registerPreference("ShowWorkzone", BoolImportStringCaller(g_xywindow_globals_private.d_show_work), BoolExportStringCaller(g_xywindow_globals_private.d_show_work));
2964
2965   GlobalPreferenceSystem().registerPreference("SI_AxisColors0", Vector3ImportStringCaller(g_xywindow_globals.AxisColorX), Vector3ExportStringCaller(g_xywindow_globals.AxisColorX));
2966   GlobalPreferenceSystem().registerPreference("SI_AxisColors1", Vector3ImportStringCaller(g_xywindow_globals.AxisColorY), Vector3ExportStringCaller(g_xywindow_globals.AxisColorY));
2967   GlobalPreferenceSystem().registerPreference("SI_AxisColors2", Vector3ImportStringCaller(g_xywindow_globals.AxisColorZ), Vector3ExportStringCaller(g_xywindow_globals.AxisColorZ));
2968   GlobalPreferenceSystem().registerPreference("SI_Colors1", Vector3ImportStringCaller(g_xywindow_globals.color_gridback), Vector3ExportStringCaller(g_xywindow_globals.color_gridback));
2969   GlobalPreferenceSystem().registerPreference("SI_Colors2", Vector3ImportStringCaller(g_xywindow_globals.color_gridminor), Vector3ExportStringCaller(g_xywindow_globals.color_gridminor));
2970   GlobalPreferenceSystem().registerPreference("SI_Colors3", Vector3ImportStringCaller(g_xywindow_globals.color_gridmajor), Vector3ExportStringCaller(g_xywindow_globals.color_gridmajor));
2971   GlobalPreferenceSystem().registerPreference("SI_Colors6", Vector3ImportStringCaller(g_xywindow_globals.color_gridblock), Vector3ExportStringCaller(g_xywindow_globals.color_gridblock));
2972   GlobalPreferenceSystem().registerPreference("SI_Colors7", Vector3ImportStringCaller(g_xywindow_globals.color_gridtext), Vector3ExportStringCaller(g_xywindow_globals.color_gridtext));
2973   GlobalPreferenceSystem().registerPreference("SI_Colors8", Vector3ImportStringCaller(g_xywindow_globals.color_brushes), Vector3ExportStringCaller(g_xywindow_globals.color_brushes));
2974   GlobalPreferenceSystem().registerPreference("SI_Colors9", Vector3ImportStringCaller(g_xywindow_globals.color_selbrushes), Vector3ExportStringCaller(g_xywindow_globals.color_selbrushes));
2975   GlobalPreferenceSystem().registerPreference("SI_Colors10", Vector3ImportStringCaller(g_xywindow_globals.color_clipper), Vector3ExportStringCaller(g_xywindow_globals.color_clipper));
2976   GlobalPreferenceSystem().registerPreference("SI_Colors11", Vector3ImportStringCaller(g_xywindow_globals.color_viewname), Vector3ExportStringCaller(g_xywindow_globals.color_viewname));
2977   GlobalPreferenceSystem().registerPreference("SI_Colors13", Vector3ImportStringCaller(g_xywindow_globals.color_gridminor_alt), Vector3ExportStringCaller(g_xywindow_globals.color_gridminor_alt));
2978   GlobalPreferenceSystem().registerPreference("SI_Colors14", Vector3ImportStringCaller(g_xywindow_globals.color_gridmajor_alt), Vector3ExportStringCaller(g_xywindow_globals.color_gridmajor_alt));
2979
2980
2981   GlobalPreferenceSystem().registerPreference("XZVIS", makeBoolStringImportCallback(ToggleShownImportBoolCaller(g_xz_front_shown)), makeBoolStringExportCallback(ToggleShownExportBoolCaller(g_xz_front_shown)));
2982   GlobalPreferenceSystem().registerPreference("YZVIS", makeBoolStringImportCallback(ToggleShownImportBoolCaller(g_yz_side_shown)), makeBoolStringExportCallback(ToggleShownExportBoolCaller(g_yz_side_shown)));
2983
2984   Orthographic_registerPreferencesPage();
2985   Clipper_registerPreferencesPage();
2986
2987   XYWnd::captureStates();
2988   GlobalEntityClassManager().attach(g_EntityClassMenu);
2989 }
2990
2991 void XYWindow_Destroy()
2992 {
2993   GlobalEntityClassManager().detach(g_EntityClassMenu);
2994   XYWnd::releaseStates();
2995 }