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