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