refactored plugin api; refactored callback library; added signals library
[xonotic/netradiant.git] / radiant / surfacedialog.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 // Surface Dialog
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 #include "surfacedialog.h"
29
30 #include "debugging/debugging.h"
31 #include "warnings.h"
32
33 #include "iscenegraph.h"
34 #include "itexdef.h"
35 #include "iundo.h"
36 #include "iselection.h"
37
38 #include <gtk/gtkhbox.h>
39 #include <gtk/gtkvbox.h>
40 #include <gtk/gtkframe.h>
41 #include <gtk/gtklabel.h>
42 #include <gtk/gtktable.h>
43 #include <gtk/gtkbutton.h>
44 #include <gtk/gtkspinbutton.h>
45 #include <gdk/gdkkeysyms.h>
46 #include <gtk/gtkcheckbutton.h> //Shamus: For Textool
47
48 #include "signal/isignal.h"
49 #include "generic/object.h"
50 #include "math/vector.h"
51 #include "texturelib.h"
52 #include "shaderlib.h"
53 #include "stringio.h"
54
55 #include "gtkutil/idledraw.h"
56 #include "gtkutil/dialog.h"
57 #include "gtkutil/entry.h"
58 #include "gtkutil/nonmodal.h"
59 #include "gtkutil/pointer.h"
60 #include "gtkutil/glwidget.h"                   //Shamus: For Textool
61 #include "gtkutil/button.h"
62 #include "map.h"
63 #include "select.h"
64 #include "patchmanip.h"
65 #include "brushmanip.h"
66 #include "patchdialog.h"
67 #include "preferences.h"
68 #include "brush_primit.h"
69 #include "xywindow.h"
70 #include "mainframe.h"
71 #include "gtkdlgs.h"
72 #include "dialog.h"
73 #include "brush.h"                              //Shamus: for Textool
74 #include "patch.h"
75 #include "commands.h"
76 #include "stream/stringstream.h"
77 #include "grid.h"
78 #include "textureentry.h"
79
80 //NOTE: Proper functioning of Textool currently requires that the "#if 1" lines in
81 //      brush_primit.h be changed to "#if 0". add/removeScale screws this up ATM. :-)
82 //      Plus, Radiant seems to work just fine without that stuff. ;-)
83
84 #define TEXTOOL_ENABLED 0
85
86 #if TEXTOOL_ENABLED
87
88 namespace TexTool
89 {
90
91 //Shamus: Textool function prototypes
92 gboolean size_allocate(GtkWidget *, GtkAllocation *, gpointer);
93 gboolean expose(GtkWidget *, GdkEventExpose *, gpointer);
94 gboolean button_press(GtkWidget *, GdkEventButton *, gpointer);
95 gboolean button_release(GtkWidget *, GdkEventButton *, gpointer);
96 gboolean motion(GtkWidget *, GdkEventMotion *, gpointer);
97 void flipX(GtkToggleButton *, gpointer);
98 void flipY(GtkToggleButton *, gpointer);
99
100 //End Textool function prototypes
101
102 //Shamus: Textool globals
103 GtkWidget * g_textoolWin;
104 //End Textool globals
105
106 void queueDraw()
107 {
108   gtk_widget_queue_draw(g_textoolWin);
109 }
110
111 }
112
113 #endif
114
115 inline void spin_button_set_step(GtkSpinButton* spin, gfloat step)
116 {
117 #if 1
118   gtk_spin_button_get_adjustment(spin)->step_increment = step;
119 #else
120   GValue gvalue = GValue_default();
121   g_value_init(&gvalue, G_TYPE_DOUBLE);
122   g_value_set_double(&gvalue, step);
123   g_object_set(G_OBJECT(gtk_spin_button_get_adjustment(spin)), "step-increment", &gvalue, NULL);
124 #endif
125 }
126
127 class Increment
128 {
129   float& m_f;
130 public:
131   GtkSpinButton* m_spin;
132   GtkEntry* m_entry;
133   Increment(float& f) : m_f(f), m_spin(0), m_entry(0)
134   {
135   }
136   void cancel()
137   {
138     entry_set_float(m_entry, m_f);
139   }
140   typedef MemberCaller<Increment, &Increment::cancel> CancelCaller;
141   void apply()
142   {
143     m_f = static_cast<float>(entry_get_float(m_entry));
144     spin_button_set_step(m_spin, m_f);
145   }
146   typedef MemberCaller<Increment, &Increment::apply> ApplyCaller;
147 };
148
149 void SurfaceInspector_GridChange();
150
151 class SurfaceInspector : public Dialog
152 {
153   GtkWindow* BuildDialog();
154
155   NonModalEntry m_textureEntry;
156   NonModalSpinner m_hshiftSpinner;
157   NonModalEntry m_hshiftEntry;
158   NonModalSpinner m_vshiftSpinner;
159   NonModalEntry m_vshiftEntry;
160   NonModalSpinner m_hscaleSpinner;
161   NonModalEntry m_hscaleEntry;
162   NonModalSpinner m_vscaleSpinner;
163   NonModalEntry m_vscaleEntry;
164   NonModalSpinner m_rotateSpinner;
165   NonModalEntry m_rotateEntry;
166
167   IdleDraw m_idleDraw;
168
169   GtkCheckButton* m_surfaceFlags[32];
170   GtkCheckButton* m_contentFlags[32];
171
172   NonModalEntry m_valueEntry;
173   GtkEntry* m_valueEntryWidget;
174 public:
175   WindowPositionTracker m_positionTracker;
176   WindowPositionTrackerImportStringCaller m_importPosition;
177   WindowPositionTrackerExportStringCaller m_exportPosition;
178
179   // Dialog Data
180   float m_fitHorizontal;
181   float m_fitVertical;
182
183   Increment m_hshiftIncrement;
184   Increment m_vshiftIncrement;
185   Increment m_hscaleIncrement;
186   Increment m_vscaleIncrement;
187   Increment m_rotateIncrement;
188   GtkEntry* m_texture;
189
190   SurfaceInspector() :
191     m_textureEntry(ApplyShaderCaller(*this), UpdateCaller(*this)),
192     m_hshiftSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
193     m_hshiftEntry(Increment::ApplyCaller(m_hshiftIncrement), Increment::CancelCaller(m_hshiftIncrement)),
194     m_vshiftSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
195     m_vshiftEntry(Increment::ApplyCaller(m_vshiftIncrement), Increment::CancelCaller(m_vshiftIncrement)),
196     m_hscaleSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
197     m_hscaleEntry(Increment::ApplyCaller(m_hscaleIncrement), Increment::CancelCaller(m_hscaleIncrement)),
198     m_vscaleSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
199     m_vscaleEntry(Increment::ApplyCaller(m_vscaleIncrement), Increment::CancelCaller(m_vscaleIncrement)),
200     m_rotateSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
201     m_rotateEntry(Increment::ApplyCaller(m_rotateIncrement), Increment::CancelCaller(m_rotateIncrement)),
202     m_idleDraw(UpdateCaller(*this)),
203     m_valueEntry(ApplyFlagsCaller(*this), UpdateCaller(*this)),
204     m_importPosition(m_positionTracker),
205     m_exportPosition(m_positionTracker),
206     m_hshiftIncrement(g_si_globals.shift[0]),
207     m_vshiftIncrement(g_si_globals.shift[1]),
208     m_hscaleIncrement(g_si_globals.scale[0]),
209     m_vscaleIncrement(g_si_globals.scale[1]),
210     m_rotateIncrement(g_si_globals.rotate)
211   {
212     m_fitVertical = 1;
213     m_fitHorizontal = 1;
214     m_positionTracker.setPosition(c_default_window_pos);
215   }
216
217   void constructWindow(GtkWindow* main_window)
218   {
219     m_parent = main_window;
220     Create();
221     AddGridChangeCallback(FreeCaller<SurfaceInspector_GridChange>());
222   }
223   void destroyWindow()
224   {
225     Destroy();
226   }
227   bool visible() const
228   {
229     return GTK_WIDGET_VISIBLE(const_cast<GtkWindow*>(GetWidget()));
230   }
231   void queueDraw()
232   {
233     if(visible())
234     {
235       m_idleDraw.queueDraw();
236     }
237   }
238
239   void Update();
240   typedef MemberCaller<SurfaceInspector, &SurfaceInspector::Update> UpdateCaller;
241   void ApplyShader();
242   typedef MemberCaller<SurfaceInspector, &SurfaceInspector::ApplyShader> ApplyShaderCaller;
243   void ApplyTexdef();
244   typedef MemberCaller<SurfaceInspector, &SurfaceInspector::ApplyTexdef> ApplyTexdefCaller;
245   void ApplyFlags();
246   typedef MemberCaller<SurfaceInspector, &SurfaceInspector::ApplyFlags> ApplyFlagsCaller;
247 };
248
249 namespace
250 {
251   SurfaceInspector* g_SurfaceInspector;
252
253   inline SurfaceInspector& getSurfaceInspector()
254   {
255     ASSERT_NOTNULL(g_SurfaceInspector);
256     return *g_SurfaceInspector;
257   }
258 }
259
260 void SurfaceInspector_constructWindow(GtkWindow* main_window)
261 {
262   getSurfaceInspector().constructWindow(main_window);
263 }
264 void SurfaceInspector_destroyWindow()
265 {
266   getSurfaceInspector().destroyWindow();
267 }
268
269 void SurfaceInspector_queueDraw()
270 {
271   getSurfaceInspector().queueDraw();
272 }
273
274 namespace
275 {
276   CopiedString g_selectedShader;
277   TextureProjection g_selectedTexdef;
278   ContentsFlagsValue g_selectedFlags;
279 }
280
281 void SurfaceInspector_SetSelectedShader(const char* shader)
282 {
283   g_selectedShader = shader;
284   SurfaceInspector_queueDraw();
285 }
286
287 void SurfaceInspector_SetSelectedTexdef(const TextureProjection& projection)
288 {
289   g_selectedTexdef = projection;
290   SurfaceInspector_queueDraw();
291 }
292
293 void SurfaceInspector_SetSelectedFlags(const ContentsFlagsValue& flags)
294 {
295   g_selectedFlags = flags;
296   SurfaceInspector_queueDraw();
297 }
298
299 static bool s_texture_selection_dirty = false;
300
301 void SurfaceInspector_updateSelection()
302 {
303   s_texture_selection_dirty = true;
304   SurfaceInspector_queueDraw();
305
306 #if TEXTOOL_ENABLED
307   if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
308   {
309     TexTool::queueDraw();
310     //globalOutputStream() << "textool texture changed..\n";
311   }
312 #endif
313 }
314
315 void SurfaceInspector_SelectionChanged(const Selectable& selectable)
316 {
317   SurfaceInspector_updateSelection();
318 }
319
320 void SurfaceInspector_SetCurrent_FromSelected()
321 {
322   if(s_texture_selection_dirty == true)
323   {
324     s_texture_selection_dirty = false;
325     if(!g_SelectedFaceInstances.empty())
326     {
327       TextureProjection projection;
328 //This *may* be the point before it fucks up... Let's see!
329 //Yep, there was a call to removeScale in there...
330       Scene_BrushGetTexdef_Component_Selected(GlobalSceneGraph(), projection);
331       SurfaceInspector_SetSelectedTexdef(projection);
332
333       CopiedString name;
334       Scene_BrushGetShader_Component_Selected(GlobalSceneGraph(), name);
335       if(string_not_empty(name.c_str()))
336       {
337         SurfaceInspector_SetSelectedShader(name.c_str());
338       }
339
340       ContentsFlagsValue flags;
341       Scene_BrushGetFlags_Component_Selected(GlobalSceneGraph(), flags);
342       SurfaceInspector_SetSelectedFlags(flags);
343     }
344     else
345     {
346       TextureProjection projection;
347       Scene_BrushGetTexdef_Selected(GlobalSceneGraph(), projection);
348       SurfaceInspector_SetSelectedTexdef(projection);
349
350       CopiedString name;
351       Scene_BrushGetShader_Selected(GlobalSceneGraph(), name);
352       if(string_empty(name.c_str()))
353       {
354         Scene_PatchGetShader_Selected(GlobalSceneGraph(), name);
355       }
356       if(string_not_empty(name.c_str()))
357       {
358         SurfaceInspector_SetSelectedShader(name.c_str());
359       }
360
361       ContentsFlagsValue flags(0, 0, 0, false);
362       Scene_BrushGetFlags_Selected(GlobalSceneGraph(), flags);
363       SurfaceInspector_SetSelectedFlags(flags);
364     }
365   }
366 }
367
368 const char* SurfaceInspector_GetSelectedShader()
369 {
370   SurfaceInspector_SetCurrent_FromSelected();
371   return g_selectedShader.c_str();
372 }
373
374 const TextureProjection& SurfaceInspector_GetSelectedTexdef()
375 {
376   SurfaceInspector_SetCurrent_FromSelected();
377   return g_selectedTexdef;
378 }
379
380 const ContentsFlagsValue& SurfaceInspector_GetSelectedFlags()
381 {
382   SurfaceInspector_SetCurrent_FromSelected();
383   return g_selectedFlags;
384 }
385
386
387
388 /*
389 ===================================================
390
391   SURFACE INSPECTOR
392
393 ===================================================
394 */
395
396 si_globals_t g_si_globals;
397
398
399 // make the shift increments match the grid settings
400 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
401 // this depends on a scale value if you have selected a particular texture on which you want it to work:
402 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
403 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
404 // increment * scale = gridsize
405 // hscale and vscale are optional parameters, if they are zero they will be set to the default scale
406 // NOTE: the default scale depends if you are using BP mode or regular.
407 // For regular it's 0.5f (128 pixels cover 64 world units), for BP it's simply 1.0f
408 // see fenris #2810
409 void DoSnapTToGrid(float hscale, float vscale)
410 {
411   g_si_globals.shift[0] = static_cast<float>(float_to_integer(static_cast<float>(GetGridSize()) / hscale));
412   g_si_globals.shift[1] = static_cast<float>(float_to_integer(static_cast<float>(GetGridSize()) / vscale));
413   getSurfaceInspector().queueDraw();
414 }
415
416 void SurfaceInspector_GridChange()
417 {
418   if (g_si_globals.m_bSnapTToGrid)
419     DoSnapTToGrid(Texdef_getDefaultTextureScale(), Texdef_getDefaultTextureScale());
420 }
421
422 // make the shift increments match the grid settings
423 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
424 // this depends on the current texture scale used?
425 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
426 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
427 // increment * scale = gridsize
428 static void OnBtnMatchGrid(GtkWidget *widget, gpointer data)
429 {
430   float hscale, vscale;
431   hscale = static_cast<float>(gtk_spin_button_get_value_as_float(getSurfaceInspector().m_hscaleIncrement.m_spin));
432   vscale = static_cast<float>(gtk_spin_button_get_value_as_float(getSurfaceInspector().m_vscaleIncrement.m_spin));
433
434   if (hscale == 0.0f || vscale == 0.0f)
435   {
436     globalOutputStream() << "ERROR: unexpected scale == 0.0f\n";
437     return;
438   }
439
440   DoSnapTToGrid (hscale, vscale);
441 }
442
443 // DoSurface will always try to show the surface inspector
444 // or update it because something new has been selected
445 // Shamus: It does get called when the SI is hidden, but not when you select something new. ;-)
446 void DoSurface (void)
447 {
448   if(getSurfaceInspector().GetWidget() == 0)
449   {    
450     getSurfaceInspector().Create();
451   
452   }
453   getSurfaceInspector().Update();
454   getSurfaceInspector().importData();
455   getSurfaceInspector().ShowDlg();
456 }
457
458 void SurfaceInspector_toggleShown()
459 {
460   if (getSurfaceInspector().visible())
461   {
462     getSurfaceInspector().HideDlg();
463   }
464   else
465   {
466     DoSurface();
467   }
468 }
469
470 void SurfaceInspector_FitTexture()
471 {
472   UndoableCommand undo("textureAutoFit");
473   Select_FitTexture(getSurfaceInspector().m_fitHorizontal, getSurfaceInspector().m_fitVertical);
474 }
475
476 static void OnBtnPatchdetails(GtkWidget *widget, gpointer data) 
477 {
478   Scene_PatchCapTexture_Selected(GlobalSceneGraph());
479 }
480
481 static void OnBtnPatchnatural(GtkWidget *widget, gpointer data) 
482 {
483   Scene_PatchNaturalTexture_Selected(GlobalSceneGraph());
484 }
485
486 static void OnBtnPatchreset(GtkWidget *widget, gpointer data) 
487 {
488   float fx, fy;
489
490   if (DoTextureLayout (&fx, &fy) == eIDOK)
491   {
492     Scene_PatchTileTexture_Selected(GlobalSceneGraph(), fx, fy);
493   }
494 }
495
496 static void OnBtnPatchFit(GtkWidget *widget, gpointer data) 
497 {
498   Scene_PatchTileTexture_Selected(GlobalSceneGraph(), 1, 1);
499 }
500
501 static void OnBtnAxial(GtkWidget *widget, gpointer data)
502 {
503 //globalOutputStream() << "--> [OnBtnAxial]...\n";
504   UndoableCommand undo("textureDefault");
505   TextureProjection projection;
506 //globalOutputStream() << "    TexDef_Construct_Default()...\n";
507   TexDef_Construct_Default(projection);
508 //globalOutputStream() << "    Select_SetTexdef()...\n";
509
510 #if TEXTOOL_ENABLED
511
512   //Shamus:
513   if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
514   {
515     // Scale up texture width/height if in BP mode...
516 //NOTE: This may not be correct any more! :-P
517     if (!g_SelectedFaceInstances.empty())
518     {
519       Face & face = g_SelectedFaceInstances.last().getFace();
520       float x = face.getShader().m_state->getTexture().width;
521       float y = face.getShader().m_state->getTexture().height;
522       projection.m_brushprimit_texdef.coords[0][0] /= x;
523       projection.m_brushprimit_texdef.coords[0][1] /= y;
524       projection.m_brushprimit_texdef.coords[1][0] /= x;
525       projection.m_brushprimit_texdef.coords[1][1] /= y;
526     }
527   }
528 #endif
529
530   Select_SetTexdef(projection);
531 }
532
533 static void OnBtnFaceFit(GtkWidget *widget, gpointer data) 
534 {
535   getSurfaceInspector().exportData();
536   SurfaceInspector_FitTexture();
537 }
538
539 typedef const char* FlagName;
540
541 const FlagName surfaceflagNamesDefault[32] = {
542   "surf1",
543   "surf2",
544   "surf3",
545   "surf4",
546   "surf5",
547   "surf6",
548   "surf7",
549   "surf8",
550   "surf9",
551   "surf10",
552   "surf11",
553   "surf12",
554   "surf13",
555   "surf14",
556   "surf15",
557   "surf16",
558   "surf17",
559   "surf18",
560   "surf19",
561   "surf20",
562   "surf21",
563   "surf22",
564   "surf23",
565   "surf24",
566   "surf25",
567   "surf26",
568   "surf27",
569   "surf28",
570   "surf29",
571   "surf30",
572   "surf31",
573   "surf32"
574 };
575
576 const FlagName contentflagNamesDefault[32] = {
577   "cont1",
578   "cont2",
579   "cont3",
580   "cont4",
581   "cont5",
582   "cont6",
583   "cont7",
584   "cont8",
585   "cont9",
586   "cont10",
587   "cont11",
588   "cont12",
589   "cont13",
590   "cont14",
591   "cont15",
592   "cont16",
593   "cont17",
594   "cont18",
595   "cont19",
596   "cont20",
597   "cont21",
598   "cont22",
599   "cont23",
600   "cont24",
601   "cont25",
602   "cont26",
603   "cont27",
604   "cont28",
605   "cont29",
606   "cont30",
607   "cont31",
608   "cont32"
609 };
610
611 const char* getSurfaceFlagName(std::size_t bit)
612 {
613   const char* value = g_pGameDescription->getKeyValue(surfaceflagNamesDefault[bit]);
614   if(string_empty(value))
615   {
616     return surfaceflagNamesDefault[bit];
617   }
618   return value;
619 }
620
621 const char* getContentFlagName(std::size_t bit)
622 {
623   const char* value = g_pGameDescription->getKeyValue(contentflagNamesDefault[bit]);
624   if(string_empty(value))
625   {
626     return contentflagNamesDefault[bit];
627   }
628   return value;
629 }
630
631
632 // =============================================================================
633 // SurfaceInspector class
634
635 guint togglebutton_connect_toggled(GtkToggleButton* button, const Callback& callback)
636 {
637   return g_signal_connect_swapped(G_OBJECT(button), "toggled", G_CALLBACK(callback.getThunk()), callback.getEnvironment());
638 }
639
640 GtkWindow* SurfaceInspector::BuildDialog()
641 {
642   GtkWindow* window = create_floating_window("Surface Inspector", m_parent);
643
644   m_positionTracker.connect(window);
645
646   global_accel_connect_window(window);
647
648   window_connect_focus_in_clear_focus_widget(window);
649   
650
651   {
652     // replaced by only the vbox:
653     GtkWidget* vbox = gtk_vbox_new (FALSE, 5);
654     gtk_widget_show (vbox);
655     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
656     gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
657
658     {
659       GtkWidget* hbox2 = gtk_hbox_new (FALSE, 5);
660       gtk_widget_show (hbox2);
661       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox2), FALSE, FALSE, 0);
662
663       {
664         GtkWidget* label = gtk_label_new ("Texture");
665         gtk_widget_show (label);
666         gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, TRUE, 0);
667       }
668       {
669         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
670         gtk_widget_show(GTK_WIDGET(entry));
671         gtk_box_pack_start(GTK_BOX(hbox2), GTK_WIDGET(entry), TRUE, TRUE, 0);
672         m_texture = entry;
673         m_textureEntry.connect(entry);
674         GlobalTextureEntryCompletion::instance().connect(entry);
675       }
676     }
677
678
679     {
680       GtkWidget* table = gtk_table_new (6, 4, FALSE);
681       gtk_widget_show (table);
682       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0);
683       gtk_table_set_row_spacings(GTK_TABLE(table), 5);
684       gtk_table_set_col_spacings(GTK_TABLE(table), 5);
685       {
686         GtkWidget* label = gtk_label_new ("Horizontal shift");
687         gtk_widget_show (label);
688         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
689         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
690                           (GtkAttachOptions) (GTK_FILL),
691                           (GtkAttachOptions) (0), 0, 0);
692       }
693       {
694         GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 2, 8, 8)), 0, 2));
695         m_hshiftIncrement.m_spin = spin;
696         m_hshiftSpinner.connect(spin);
697         gtk_widget_show(GTK_WIDGET(spin));
698         gtk_table_attach (GTK_TABLE(table), GTK_WIDGET(spin), 1, 2, 0, 1,
699                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
700                           (GtkAttachOptions) (0), 0, 0);
701         gtk_widget_set_usize(GTK_WIDGET(spin), 60, -2);
702       }
703       {
704         GtkWidget* label = gtk_label_new ("Step");
705         gtk_widget_show (label);
706         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
707         gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1,
708                           (GtkAttachOptions) (GTK_FILL),
709                           (GtkAttachOptions) (0), 0, 0);
710       }
711       {
712         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
713         gtk_widget_show(GTK_WIDGET(entry));
714         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(entry), 3, 4, 0, 1,
715                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
716                           (GtkAttachOptions) (0), 0, 0);
717         gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
718         m_hshiftIncrement.m_entry = entry;
719         m_hshiftEntry.connect(entry);
720       }
721       {
722         GtkWidget* label = gtk_label_new ("Vertical shift");
723         gtk_widget_show (label);
724         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
725         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
726                           (GtkAttachOptions) (GTK_FILL),
727                           (GtkAttachOptions) (0), 0, 0);
728       }
729       {
730         GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 2, 8, 8)), 0, 2));
731         m_vshiftIncrement.m_spin = spin;
732         m_vshiftSpinner.connect(spin);
733         gtk_widget_show(GTK_WIDGET(spin));
734         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(spin), 1, 2, 1, 2,
735                           (GtkAttachOptions) (GTK_FILL),
736                           (GtkAttachOptions) (0), 0, 0);
737         gtk_widget_set_usize(GTK_WIDGET(spin), 60, -2);
738       }
739       {
740         GtkWidget* label = gtk_label_new ("Step");
741         gtk_widget_show (label);
742         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
743         gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2,
744                           (GtkAttachOptions) (GTK_FILL),
745                           (GtkAttachOptions) (0), 0, 0);
746       }
747       {
748         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
749         gtk_widget_show(GTK_WIDGET(entry));
750         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(entry), 3, 4, 1, 2,
751                           (GtkAttachOptions) (GTK_FILL),
752                           (GtkAttachOptions) (0), 0, 0);
753         gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
754         m_vshiftIncrement.m_entry = entry;
755         m_vshiftEntry.connect(entry);
756       }
757       {
758         GtkWidget* label = gtk_label_new ("Horizontal stretch");
759         gtk_widget_show (label);
760         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
761         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
762                           (GtkAttachOptions) (GTK_FILL),
763                           (GtkAttachOptions) (0), 0, 0);
764       }
765       {
766         GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 2, 8, 8)), 0, 5));
767         m_hscaleIncrement.m_spin = spin;
768         m_hscaleSpinner.connect(spin);
769         gtk_widget_show(GTK_WIDGET(spin));
770         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(spin), 1, 2, 2, 3,
771                           (GtkAttachOptions) (GTK_FILL),
772                           (GtkAttachOptions) (0), 0, 0);
773         gtk_widget_set_usize(GTK_WIDGET(spin), 60, -2);
774       }
775       {
776         GtkWidget* label = gtk_label_new ("Step");
777         gtk_widget_show (label);
778         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
779         gtk_table_attach(GTK_TABLE(table), label, 2, 3, 2, 3,
780                           (GtkAttachOptions) (GTK_FILL),
781                           (GtkAttachOptions) (0), 2, 3);
782       }
783       {
784         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
785         gtk_widget_show(GTK_WIDGET(entry));
786         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(entry), 3, 4, 2, 3,
787                           (GtkAttachOptions) (GTK_FILL),
788                           (GtkAttachOptions) (0), 2, 3);
789         gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
790         m_hscaleIncrement.m_entry = entry;
791         m_hscaleEntry.connect(entry);
792       }
793       {
794         GtkWidget* label = gtk_label_new ("Vertical stretch");
795         gtk_widget_show (label);
796         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
797         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4,
798                           (GtkAttachOptions) (GTK_FILL),
799                           (GtkAttachOptions) (0), 0, 0);
800       }
801       {
802         GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 2, 8, 8)), 0, 5));
803         m_vscaleIncrement.m_spin = spin;
804         m_vscaleSpinner.connect(spin);
805         gtk_widget_show(GTK_WIDGET(spin));
806         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(spin), 1, 2, 3, 4,
807                           (GtkAttachOptions) (GTK_FILL),
808                           (GtkAttachOptions) (0), 0, 0);
809         gtk_widget_set_usize(GTK_WIDGET(spin), 60, -2);
810       }
811       {
812         GtkWidget* label = gtk_label_new ("Step");
813         gtk_widget_show (label);
814         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
815         gtk_table_attach(GTK_TABLE(table), label, 2, 3, 3, 4,
816                           (GtkAttachOptions) (GTK_FILL),
817                           (GtkAttachOptions) (0), 0, 0);
818       }
819       {
820         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
821         gtk_widget_show(GTK_WIDGET(entry));
822         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(entry), 3, 4, 3, 4,
823                           (GtkAttachOptions) (GTK_FILL),
824                           (GtkAttachOptions) (0), 0, 0);
825         gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
826         m_vscaleIncrement.m_entry = entry;
827         m_vscaleEntry.connect(entry);
828       }
829       {
830         GtkWidget* label = gtk_label_new ("Rotate");
831         gtk_widget_show (label);
832         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
833         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,
834                           (GtkAttachOptions) (GTK_FILL),
835                           (GtkAttachOptions) (0), 0, 0);
836       }
837       {
838         GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 2, 8, 8)), 0, 2));
839         m_rotateIncrement.m_spin = spin;
840         m_rotateSpinner.connect(spin);
841         gtk_widget_show(GTK_WIDGET(spin));
842         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(spin), 1, 2, 4, 5,
843                           (GtkAttachOptions) (GTK_FILL),
844                           (GtkAttachOptions) (0), 0, 0);
845         gtk_widget_set_usize(GTK_WIDGET(spin), 60, -2);
846         gtk_spin_button_set_wrap(spin, TRUE);
847       }
848       {
849         GtkWidget* label = gtk_label_new ("Step");
850         gtk_widget_show (label);
851         gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
852         gtk_table_attach(GTK_TABLE(table), label, 2, 3, 4, 5,
853                           (GtkAttachOptions) (GTK_FILL),
854                           (GtkAttachOptions) (0), 0, 0);
855       }
856       {
857         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
858         gtk_widget_show(GTK_WIDGET(entry));
859         gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(entry), 3, 4, 4, 5,
860                           (GtkAttachOptions) (GTK_FILL),
861                           (GtkAttachOptions) (0), 0, 0);
862         gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
863         m_rotateIncrement.m_entry = entry;
864         m_rotateEntry.connect(entry);
865       }
866       {
867         // match grid button
868         GtkWidget* button = gtk_button_new_with_label ("Match Grid");
869         gtk_widget_show (button);
870         gtk_table_attach(GTK_TABLE(table), button, 2, 4, 5, 6,
871                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
872                           (GtkAttachOptions) (0), 0, 0);
873         g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnMatchGrid), 0);
874       }
875     }
876
877     {
878       GtkWidget* frame = gtk_frame_new ("Texturing");
879       gtk_widget_show (frame);
880       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), FALSE, FALSE, 0);
881       {
882         GtkWidget* table = gtk_table_new (4, 4, FALSE);
883         gtk_widget_show (table);
884         gtk_container_add (GTK_CONTAINER (frame), table);
885         gtk_table_set_row_spacings(GTK_TABLE(table), 5);
886         gtk_table_set_col_spacings(GTK_TABLE(table), 5);
887         gtk_container_set_border_width (GTK_CONTAINER (table), 5);
888         {
889           GtkWidget* label = gtk_label_new ("Brush");
890           gtk_widget_show (label);
891           gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
892                             (GtkAttachOptions) (GTK_FILL),
893                             (GtkAttachOptions) (0), 0, 0);
894         }
895         {
896           GtkWidget* label = gtk_label_new ("Patch");
897           gtk_widget_show (label);
898           gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
899                             (GtkAttachOptions) (GTK_FILL),
900                             (GtkAttachOptions) (0), 0, 0);
901         }
902         {
903           GtkWidget* label = gtk_label_new ("Width");
904           gtk_widget_show (label);
905           gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1,
906                             (GtkAttachOptions) (GTK_FILL),
907                             (GtkAttachOptions) (0), 0, 0);
908         }
909         {
910           GtkWidget* label = gtk_label_new ("Height");
911           gtk_widget_show (label);
912           gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1,
913                             (GtkAttachOptions) (GTK_FILL),
914                             (GtkAttachOptions) (0), 0, 0);
915         }
916         {
917           GtkWidget* button = gtk_button_new_with_label ("Axial");
918           gtk_widget_show (button);
919           gtk_table_attach(GTK_TABLE(table), button, 0, 1, 1, 2,
920                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
921                             (GtkAttachOptions) (0), 0, 0);
922           g_signal_connect(G_OBJECT(button), "clicked",
923                               G_CALLBACK(OnBtnAxial), 0);
924           gtk_widget_set_usize (button, 60, -2);
925         }
926         {
927           GtkWidget* button = gtk_button_new_with_label ("Fit");
928           gtk_widget_show (button);
929           gtk_table_attach(GTK_TABLE(table), button, 1, 2, 1, 2,
930                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
931                             (GtkAttachOptions) (0), 0, 0);
932           g_signal_connect(G_OBJECT(button), "clicked",
933                               G_CALLBACK(OnBtnFaceFit), 0);
934           gtk_widget_set_usize (button, 60, -2);
935         }
936         {
937           GtkWidget* button = gtk_button_new_with_label ("CAP");
938           gtk_widget_show (button);
939           gtk_table_attach(GTK_TABLE(table), button, 0, 1, 3, 4,
940                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
941                             (GtkAttachOptions) (0), 0, 0);
942           g_signal_connect(G_OBJECT(button), "clicked",
943                               G_CALLBACK(OnBtnPatchdetails), 0);
944           gtk_widget_set_usize (button, 60, -2);
945         }
946         {
947           GtkWidget* button = gtk_button_new_with_label ("Set...");
948           gtk_widget_show (button);
949           gtk_table_attach(GTK_TABLE(table), button, 1, 2, 3, 4,
950                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
951                             (GtkAttachOptions) (0), 0, 0);
952           g_signal_connect(G_OBJECT(button), "clicked",
953                               G_CALLBACK(OnBtnPatchreset), 0);
954           gtk_widget_set_usize (button, 60, -2);
955         }
956         {
957           GtkWidget* button = gtk_button_new_with_label ("Natural");
958           gtk_widget_show (button);
959           gtk_table_attach(GTK_TABLE(table), button, 2, 3, 3, 4,
960                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
961                             (GtkAttachOptions) (0), 0, 0);
962           g_signal_connect(G_OBJECT(button), "clicked",
963                               G_CALLBACK(OnBtnPatchnatural), 0);
964           gtk_widget_set_usize (button, 60, -2);
965         }
966         {
967           GtkWidget* button = gtk_button_new_with_label ("Fit");
968           gtk_widget_show (button);
969           gtk_table_attach(GTK_TABLE(table), button, 3, 4, 3, 4,
970                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
971                             (GtkAttachOptions) (0), 0, 0);
972           g_signal_connect(G_OBJECT(button), "clicked",
973                               G_CALLBACK(OnBtnPatchFit), 0);
974           gtk_widget_set_usize (button, 60, -2);
975         }
976         {
977           GtkWidget* spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (1, 0, 1 << 16, 1, 10, 10)), 0, 6);
978           gtk_widget_show (spin);
979           gtk_table_attach(GTK_TABLE(table), spin, 2, 3, 1, 2,
980                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
981                             (GtkAttachOptions) (0), 0, 0);
982           gtk_widget_set_usize (spin, 60, -2);
983           AddDialogData(*GTK_SPIN_BUTTON(spin), m_fitHorizontal);
984         }
985         {
986           GtkWidget* spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (1, 0, 1 << 16, 1, 10, 10)), 0, 6);
987           gtk_widget_show (spin);
988           gtk_table_attach(GTK_TABLE(table), spin, 3, 4, 1, 2,
989                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
990                             (GtkAttachOptions) (0), 0, 0);
991           gtk_widget_set_usize (spin, 60, -2);
992           AddDialogData(*GTK_SPIN_BUTTON(spin), m_fitVertical);
993         }
994       }
995     }
996     if(!string_empty(g_pGameDescription->getKeyValue("si_flags")))
997     {
998       {
999         GtkFrame* frame = GTK_FRAME(gtk_frame_new("Surface Flags"));
1000         gtk_widget_show(GTK_WIDGET(frame));
1001         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), TRUE, TRUE, 0);
1002         {
1003           GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 4));
1004           //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
1005           gtk_widget_show(GTK_WIDGET(vbox3));
1006           gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3));
1007           {
1008             GtkTable* table = GTK_TABLE(gtk_table_new(8, 4, FALSE));
1009             gtk_widget_show(GTK_WIDGET(table));
1010             gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0);
1011             gtk_table_set_row_spacings(table, 0);
1012             gtk_table_set_col_spacings(table, 0);
1013
1014             GtkCheckButton** p = m_surfaceFlags;
1015
1016             for(int c = 0; c != 4; ++c)
1017             {
1018               for(int r = 0; r != 8; ++r)
1019               {
1020                 GtkCheckButton* check = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(getSurfaceFlagName(c * 8 + r)));
1021                 gtk_widget_show(GTK_WIDGET(check));
1022                 gtk_table_attach(table, GTK_WIDGET(check), c, c+1, r, r+1,
1023                                   (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
1024                                   (GtkAttachOptions)(0), 0, 0);
1025                 *p++ = check;
1026                 guint handler_id = togglebutton_connect_toggled(GTK_TOGGLE_BUTTON(check), ApplyFlagsCaller(*this));
1027                 g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id));
1028               }
1029             }
1030           }
1031         }
1032       }
1033       {
1034         GtkFrame* frame = GTK_FRAME(gtk_frame_new("Content Flags"));
1035         gtk_widget_show(GTK_WIDGET(frame));
1036         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), TRUE, TRUE, 0);
1037         {
1038           GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 4));
1039           //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
1040           gtk_widget_show(GTK_WIDGET(vbox3));
1041           gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3));
1042           {
1043
1044             GtkTable* table = GTK_TABLE(gtk_table_new(8, 4, FALSE));
1045             gtk_widget_show(GTK_WIDGET(table));
1046             gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0);
1047             gtk_table_set_row_spacings(table, 0);
1048             gtk_table_set_col_spacings(table, 0);
1049
1050             GtkCheckButton** p = m_contentFlags;
1051
1052             for(int c = 0; c != 4; ++c)
1053             {
1054               for(int r = 0; r != 8; ++r)
1055               {
1056                 GtkCheckButton* check = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(getContentFlagName(c * 8 + r)));
1057                 gtk_widget_show(GTK_WIDGET(check));
1058                 gtk_table_attach(table, GTK_WIDGET(check), c, c+1, r, r+1,
1059                                   (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
1060                                   (GtkAttachOptions)(0), 0, 0);
1061                 *p++ = check;
1062                 guint handler_id = togglebutton_connect_toggled(GTK_TOGGLE_BUTTON(check), ApplyFlagsCaller(*this));
1063                 g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id));
1064               }
1065             }
1066           }
1067         }
1068       }
1069       {
1070         GtkFrame* frame = GTK_FRAME(gtk_frame_new("Value"));
1071         gtk_widget_show(GTK_WIDGET(frame));
1072         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), TRUE, TRUE, 0);
1073         {
1074           GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 4));
1075           gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
1076           gtk_widget_show(GTK_WIDGET(vbox3));
1077           gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3));
1078
1079           {
1080             GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
1081             gtk_widget_show(GTK_WIDGET(entry));
1082             gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(entry), TRUE, TRUE, 0);
1083             m_valueEntryWidget = entry;
1084             m_valueEntry.connect(entry);
1085           }
1086         }
1087       }
1088     }
1089
1090 #if TEXTOOL_ENABLED
1091     if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1092 // Shamus: Textool goodies...
1093     {
1094       GtkWidget * frame = gtk_frame_new("Textool");
1095       gtk_widget_show(frame);
1096       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), FALSE, FALSE, 0);
1097       {
1098         //Prolly should make this a member or global var, so the SI can draw on it...
1099         TexTool::g_textoolWin = glwidget_new(FALSE);
1100         // --> Dunno, but this stuff may be necessary... (Looks like it!)
1101         gtk_widget_ref(TexTool::g_textoolWin);
1102         gtk_widget_set_events(TexTool::g_textoolWin, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
1103         GTK_WIDGET_SET_FLAGS(TexTool::g_textoolWin, GTK_CAN_FOCUS);
1104         // <-- end stuff...
1105         gtk_widget_show(TexTool::g_textoolWin);
1106         gtk_widget_set_usize(TexTool::g_textoolWin, -1, 240);   //Yeah!
1107         gtk_container_add(GTK_CONTAINER(frame), TexTool::g_textoolWin);
1108
1109         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "size_allocate", G_CALLBACK(TexTool::size_allocate), NULL);
1110         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "expose_event", G_CALLBACK(TexTool::expose), NULL);
1111         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "button_press_event", G_CALLBACK(TexTool::button_press), NULL);
1112         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "button_release_event", G_CALLBACK(TexTool::button_release), NULL);
1113         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "motion_notify_event", G_CALLBACK(TexTool::motion), NULL);
1114       }
1115       {
1116         GtkWidget * hbox = gtk_hbox_new(FALSE, 5);
1117         gtk_widget_show(hbox);
1118         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 0);
1119         // Checkboxes go here... (Flip X/Y)
1120         GtkWidget * flipX = gtk_check_button_new_with_label("Flip X axis");
1121         GtkWidget * flipY = gtk_check_button_new_with_label("Flip Y axis");
1122         gtk_widget_show(flipX);
1123         gtk_widget_show(flipY);
1124         gtk_box_pack_start(GTK_BOX(hbox), flipX, FALSE, FALSE, 0);
1125         gtk_box_pack_start(GTK_BOX(hbox), flipY, FALSE, FALSE, 0);
1126         
1127 //Instead of this, we probably need to create a vbox to put into the frame, then the
1128 //window, then the hbox. !!! FIX !!!
1129 //        gtk_container_add(GTK_CONTAINER(frame), hbox);
1130
1131 //Hmm. Do we really need g_object_set_data? Mebbe not... And we don't! :-)
1132 //        g_object_set_data(G_OBJECT(flipX), "handler", gint_to_pointer(g_signal_connect(G_OBJECT(flipX), "toggled", G_CALLBACK(TexTool::flipX), 0)));
1133 //        g_object_set_data(G_OBJECT(flipY), "handler", gint_to_pointer(g_signal_connect(G_OBJECT(flipY), "toggled", G_CALLBACK(TexTool::flipY), 0)));
1134 //Instead, just do:
1135         g_signal_connect(G_OBJECT(flipX), "toggled", G_CALLBACK(TexTool::flipX), NULL);
1136         g_signal_connect(G_OBJECT(flipY), "toggled", G_CALLBACK(TexTool::flipY), NULL);
1137       }
1138     }
1139 #endif
1140   }
1141
1142   return window;
1143 }
1144
1145 /*
1146 ==============
1147 Update
1148
1149 Set the fields to the current texdef (i.e. map/texdef -> dialog widgets)
1150 if faces selected (instead of brushes) -> will read this face texdef, else current texdef
1151 if only patches selected, will read the patch texdef
1152 ===============
1153 */
1154
1155 void spin_button_set_value_no_signal(GtkSpinButton* spin, gdouble value)
1156 {
1157   guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(spin), "handler"));
1158   g_signal_handler_block(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id);
1159   gtk_spin_button_set_value(spin, value);
1160   g_signal_handler_unblock(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id);
1161 }
1162
1163 void spin_button_set_step_increment(GtkSpinButton* spin, gdouble value)
1164 {
1165   GtkAdjustment* adjust = gtk_spin_button_get_adjustment(spin);
1166   adjust->step_increment = value;
1167 }
1168
1169 void SurfaceInspector::Update()
1170 {
1171   const char * name = SurfaceInspector_GetSelectedShader();
1172   
1173   if(shader_is_texture(name))
1174   {
1175     gtk_entry_set_text(m_texture, shader_get_textureName(name));
1176   }
1177   else
1178   {
1179     gtk_entry_set_text(m_texture, "");
1180   }
1181
1182   texdef_t shiftScaleRotate;
1183 //Shamus: This is where we get into trouble--the BP code tries to convert to a "faked"
1184 //shift, rotate & scale values from the brush face, which seems to screw up for some reason.
1185 //!!! FIX !!!
1186 /*globalOutputStream() << "--> SI::Update. About to do ShiftScaleRotate_fromFace()...\n";
1187 SurfaceInspector_GetSelectedBPTexdef();
1188 globalOutputStream() << "BP: (" << g_selectedBrushPrimitTexdef.coords[0][0] << ", " << g_selectedBrushPrimitTexdef.coords[0][1] << ")("
1189         << g_selectedBrushPrimitTexdef.coords[1][0] << ", " << g_selectedBrushPrimitTexdef.coords[1][1] << ")("
1190         << g_selectedBrushPrimitTexdef.coords[0][2] << ", " << g_selectedBrushPrimitTexdef.coords[1][2] << ") SurfaceInspector::Update\n";//*/
1191 //Ok, it's screwed up *before* we get here...
1192   ShiftScaleRotate_fromFace(shiftScaleRotate, SurfaceInspector_GetSelectedTexdef());
1193
1194   {
1195     spin_button_set_value_no_signal(m_hshiftIncrement.m_spin, shiftScaleRotate.shift[0]);
1196     spin_button_set_step_increment(m_hshiftIncrement.m_spin, g_si_globals.shift[0]);
1197     entry_set_float(m_hshiftIncrement.m_entry, g_si_globals.shift[0]);
1198   }
1199
1200   {
1201     spin_button_set_value_no_signal(m_vshiftIncrement.m_spin, shiftScaleRotate.shift[1]);
1202     spin_button_set_step_increment(m_vshiftIncrement.m_spin, g_si_globals.shift[1]);
1203     entry_set_float(m_vshiftIncrement.m_entry, g_si_globals.shift[1]);
1204   }
1205
1206   {
1207     spin_button_set_value_no_signal(m_hscaleIncrement.m_spin, shiftScaleRotate.scale[0]);
1208     spin_button_set_step_increment(m_hscaleIncrement.m_spin, g_si_globals.scale[0]);
1209     entry_set_float(m_hscaleIncrement.m_entry, g_si_globals.scale[0]);
1210   }
1211
1212   {
1213     spin_button_set_value_no_signal(m_vscaleIncrement.m_spin, shiftScaleRotate.scale[1]);
1214     spin_button_set_step_increment(m_vscaleIncrement.m_spin, g_si_globals.scale[1]);
1215     entry_set_float(m_vscaleIncrement.m_entry, g_si_globals.scale[1]);
1216   }
1217
1218   {
1219     spin_button_set_value_no_signal(m_rotateIncrement.m_spin, shiftScaleRotate.rotate);
1220     spin_button_set_step_increment(m_rotateIncrement.m_spin, g_si_globals.rotate);
1221     entry_set_float(m_rotateIncrement.m_entry, g_si_globals.rotate);
1222   }
1223
1224   if(!string_empty(g_pGameDescription->getKeyValue("si_flags")))
1225   {
1226     ContentsFlagsValue flags(SurfaceInspector_GetSelectedFlags());
1227
1228     entry_set_int(m_valueEntryWidget, flags.m_value);
1229
1230     for(GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p)
1231     {
1232       toggle_button_set_active_no_signal(GTK_TOGGLE_BUTTON(*p), flags.m_surfaceFlags & (1 << (p - m_surfaceFlags)));
1233     }
1234
1235     for(GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p)
1236     {
1237       toggle_button_set_active_no_signal(GTK_TOGGLE_BUTTON(*p), flags.m_contentFlags & (1 << (p - m_contentFlags)));
1238     }
1239   }
1240 }
1241
1242 /*
1243 ==============
1244 Apply
1245
1246 Reads the fields to get the current texdef (i.e. widgets -> MAP)
1247 in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix
1248 ===============
1249 */
1250 void SurfaceInspector::ApplyShader()
1251 {
1252   StringOutputStream name(256);
1253   name << GlobalTexturePrefix_get() << gtk_entry_get_text(m_texture);
1254
1255   // TTimo: detect and refuse invalid texture names (at least the ones with spaces)
1256   if(!texdef_name_valid(name.c_str()))
1257   {
1258     globalErrorStream() << "invalid texture name '" << name.c_str() << "'\n";
1259     SurfaceInspector_queueDraw();
1260     return;
1261   }
1262
1263   UndoableCommand undo("textureNameSetSelected");
1264   Select_SetShader(name.c_str());
1265 }
1266
1267 void SurfaceInspector::ApplyTexdef()
1268 {
1269   texdef_t shiftScaleRotate;
1270
1271   shiftScaleRotate.shift[0] = static_cast<float>(gtk_spin_button_get_value_as_float(m_hshiftIncrement.m_spin));
1272   shiftScaleRotate.shift[1] = static_cast<float>(gtk_spin_button_get_value_as_float(m_vshiftIncrement.m_spin));
1273   shiftScaleRotate.scale[0] = static_cast<float>(gtk_spin_button_get_value_as_float(m_hscaleIncrement.m_spin));
1274   shiftScaleRotate.scale[1] = static_cast<float>(gtk_spin_button_get_value_as_float(m_vscaleIncrement.m_spin));
1275   shiftScaleRotate.rotate = static_cast<float>(gtk_spin_button_get_value_as_float(m_rotateIncrement.m_spin));
1276
1277   TextureProjection projection;
1278 //Shamus: This is the other place that screws up, it seems, since it doesn't seem to do the
1279 //conversion from the face (I think) and so bogus values end up in the thing... !!! FIX !!!
1280 //This is actually OK. :-P
1281   ShiftScaleRotate_toFace(shiftScaleRotate, projection);
1282
1283   UndoableCommand undo("textureProjectionSetSelected");
1284   Select_SetTexdef(projection);
1285 }
1286
1287 void SurfaceInspector::ApplyFlags()
1288 {
1289   unsigned int surfaceflags = 0;
1290   for(GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p)
1291   {
1292     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p)))
1293     {
1294       surfaceflags |= (1 << (p - m_surfaceFlags));
1295     }
1296   }
1297
1298   unsigned int contentflags = 0;
1299   for(GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p)
1300   {
1301     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p)))
1302     {
1303       contentflags |= (1 << (p - m_contentFlags));
1304     }
1305   }
1306
1307   int value = entry_get_int(m_valueEntryWidget);
1308
1309   UndoableCommand undo("flagsSetSelected");
1310   Select_SetFlags(ContentsFlagsValue(surfaceflags, contentflags, value, true));
1311 }
1312
1313 void SurfaceInspector_constructPreferences(PreferencesPage& page)
1314 {
1315   page.appendCheckBox("", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid);
1316 }
1317 void SurfaceInspector_constructPage(PreferenceGroup& group)
1318 {
1319   PreferencesPage page(group.createPage("Surface Inspector", "Surface Inspector Preferences"));
1320   SurfaceInspector_constructPreferences(page);
1321 }
1322 void SurfaceInspector_registerPreferencesPage()
1323 {
1324   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, SurfaceInspector_constructPage>());
1325 }
1326
1327 void SurfaceInspector_registerCommands()
1328 {
1329   GlobalCommands_insert("FitTexture", FreeCaller<SurfaceInspector_FitTexture>(), Accelerator('B', (GdkModifierType)GDK_SHIFT_MASK));
1330   GlobalCommands_insert("SurfaceInspector", FreeCaller<SurfaceInspector_toggleShown>(), Accelerator('S'));
1331 }
1332
1333
1334 #include "preferencesystem.h"
1335
1336
1337 void SurfaceInspector_Construct()
1338 {
1339   g_SurfaceInspector = new SurfaceInspector;
1340
1341   SurfaceInspector_registerCommands();
1342
1343   GlobalPreferenceSystem().registerPreference("SurfaceWnd", getSurfaceInspector().m_importPosition, getSurfaceInspector().m_exportPosition);
1344   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale1", FloatImportStringCaller(g_si_globals.scale[0]), FloatExportStringCaller(g_si_globals.scale[0]));      
1345   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale2", FloatImportStringCaller(g_si_globals.scale[1]), FloatExportStringCaller(g_si_globals.scale[1]));
1346   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift1", FloatImportStringCaller(g_si_globals.shift[0]), FloatExportStringCaller(g_si_globals.shift[0]));
1347   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift2", FloatImportStringCaller(g_si_globals.shift[1]), FloatExportStringCaller(g_si_globals.shift[1]));
1348   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Rotate", FloatImportStringCaller(g_si_globals.rotate), FloatExportStringCaller(g_si_globals.rotate));
1349   GlobalPreferenceSystem().registerPreference("SnapTToGrid", BoolImportStringCaller(g_si_globals.m_bSnapTToGrid), BoolExportStringCaller(g_si_globals.m_bSnapTToGrid));
1350
1351   GlobalSelectionSystem().addSelectionChangeCallback(FreeCaller1<const Selectable&, SurfaceInspector_SelectionChanged>());
1352   Brush_addTextureChangedCallback(FreeCaller<SurfaceInspector_updateSelection>());
1353   Patch_addTextureChangedCallback(FreeCaller<SurfaceInspector_updateSelection>());
1354
1355   SurfaceInspector_registerPreferencesPage();
1356 }
1357 void SurfaceInspector_Destroy()
1358 {
1359   delete g_SurfaceInspector;
1360 }
1361
1362 #if TEXTOOL_ENABLED
1363
1364 namespace TexTool { // namespace hides these symbols from other object-files
1365 //
1366 //Shamus: Textool functions, including GTK+ callbacks
1367 //
1368
1369 //NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!!
1370 //      But... You can see down below that it *is* initialized! WTF?
1371 struct Extent
1372 {
1373         float minX, minY, maxX, maxY;
1374         float width(void) { return fabs(maxX - minX); }
1375         float height(void) { return fabs(maxY - minY); }
1376 };
1377
1378 //This seems to control the texture scale... (Yep! ;-)
1379 Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f };
1380 brushprimit_texdef_t tm;                                                // Texture transform matrix
1381 Vector2 pts[c_brush_maxFaces];
1382 Vector2 center;
1383 int numPts;
1384 int textureNum;
1385 Vector2 textureSize;
1386 Vector2 windowSize;
1387 #define VP_PADDING      1.2
1388 #define PI                      3.14159265358979
1389 bool lButtonDown = false;
1390 bool rButtonDown = false;
1391 //int dragPoint;
1392 //int anchorPoint;
1393 bool haveAnchor = false;
1394 brushprimit_texdef_t currentBP;
1395 brushprimit_texdef_t origBP;                                    // Original brush primitive (before we muck it up)
1396 float controlRadius = 5.0f;
1397 float rotationAngle = 0.0f;
1398 float rotationAngle2 = 0.0f;
1399 float oldRotationAngle;
1400 Vector2 rotationPoint;
1401 bool translatingX = false;                                              // Widget state variables
1402 bool translatingY = false;
1403 bool scalingX = false;
1404 bool scalingY = false;
1405 bool rotating = false;
1406 bool resizingX = false;                                                 // Not sure what this means... :-/
1407 bool resizingY = false;
1408 float origAngle, origScaleX, origScaleY;
1409 Vector2 oldCenter;
1410
1411
1412 // Function prototypes (move up to top later...)
1413
1414 void DrawCircularArc(Vector2 ctr, float startAngle, float endAngle, float radius);
1415
1416
1417 void CopyPointsFromSelectedFace(void)
1418 {
1419         // Make sure that there's a face and winding to get!
1420
1421         if (g_SelectedFaceInstances.empty())
1422         {
1423                 numPts = 0;
1424                 return;
1425         }
1426
1427         Face & face = g_SelectedFaceInstances.last().getFace();
1428         textureNum = face.getShader().m_state->getTexture().texture_number;
1429         textureSize.x() = face.getShader().m_state->getTexture().width;
1430         textureSize.y() = face.getShader().m_state->getTexture().height;
1431 //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n";
1432
1433         currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef;
1434
1435   face.EmitTextureCoordinates();
1436         Winding & w = face.getWinding();
1437         int count = 0;
1438
1439         for(Winding::const_iterator i=w.begin(); i!=w.end(); i++)
1440         {
1441     //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", ";
1442                 pts[count].x() = (*i).texcoord.x();
1443                 pts[count].y() = (*i).texcoord.y();
1444                 count++;
1445         }
1446
1447         numPts = count;
1448
1449   //globalOutputStream() << " ..copied points\n";
1450 }
1451
1452         brushprimit_texdef_t bp;
1453 //This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE]
1454 void CommitChanges(void)
1455 {
1456         texdef_t t;                                                                     // Throwaway, since this is BP only
1457
1458         bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0];
1459         bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1];
1460         bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2];
1461 //Ok, this works for translation...
1462 //      bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2] * textureSize.x();
1463         bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0];
1464         bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1];
1465         bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2];
1466 //      bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2] * textureSize.y();
1467
1468 //This doesn't work:    g_brush_texture_changed();
1469 // Let's try this:
1470 //Note: We should only set an undo *after* the button has been released... !!! FIX !!!
1471 //Definitely *should* have an undo, though!
1472 //  UndoableCommand undo("textureProjectionSetSelected");
1473         Select_SetTexdef(TextureProjection(t, bp, Vector3(0, 0, 0), Vector3(0, 0, 0)));
1474 //This is working, but for some reason the translate is causing the rest of the SI
1475 //widgets to yield bad readings... !!! FIX !!!
1476 //I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why.
1477 //It's because there were some uncommented out add/removeScale functions in brush.h and a
1478 //removeScale in brushmanip.cpp... :-/
1479 //Translate isn't working at all now... :-(
1480 //It's because we need to multiply in some scaling factor (prolly the texture width/height)
1481 //Yep. :-P
1482 }
1483
1484 void UpdateControlPoints(void)
1485 {
1486         CommitChanges();
1487
1488   // Init texture transform matrix
1489
1490         tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f;
1491         tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f;
1492 }
1493
1494
1495 /*
1496 For shifting we have:
1497 */
1498 /*
1499 The code that should provide reasonable defaults, but doesn't for some reason:
1500 It's scaling the BP by 128 for some reason, between the time it's created and the
1501 time we get back to the SI widgets:
1502
1503 static void OnBtnAxial(GtkWidget *widget, gpointer data)
1504 {
1505   UndoableCommand undo("textureDefault");
1506   TextureProjection projection;
1507   TexDef_Construct_Default(projection);
1508   Select_SetTexdef(projection);
1509 }
1510
1511 Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection)
1512 which is in brushmanip.h: This eventually calls
1513 Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face...
1514 which just copies from brushpr to m_brushpr...
1515 */
1516
1517 //Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE]
1518 //Prolly should separate out the grid drawing so that we can draw it behind the polygon.
1519 const float gridWidth = 1.3f;// Let's try an absolute height... WORKS!!!
1520 // NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision
1521 //      detection easier...
1522 const float gridRadius = gridWidth * 0.5f;
1523
1524 typedef const float WidgetColor[3];
1525 const WidgetColor widgetColor[10] = {
1526         { 1.0000f, 0.2000f, 0.0000f },                  // Red
1527         { 0.9137f, 0.9765f, 0.4980f },                  // Yellow
1528         { 0.0000f, 0.6000f, 0.3216f },                  // Green
1529         { 0.6157f, 0.7726f, 0.8196f },                  // Cyan
1530         { 0.4980f, 0.5000f, 0.4716f },                  // Grey
1531
1532         // Highlight colors
1533         { 1.0000f, 0.6000f, 0.4000f },                  // Light Red
1534         { 1.0000f, 1.0000f, 0.8980f },                  // Light Yellow
1535         { 0.4000f, 1.0000f, 0.7216f },                  // Light Green
1536         { 1.0000f, 1.0000f, 1.0000f },                  // Light Cyan
1537         { 0.8980f, 0.9000f, 0.8716f }                   // Light Grey
1538 };
1539
1540 #define COLOR_RED                       0
1541 #define COLOR_YELLOW            1
1542 #define COLOR_GREEN                     2
1543 #define COLOR_CYAN                      3
1544 #define COLOR_GREY                      4
1545 #define COLOR_LT_RED            5
1546 #define COLOR_LT_YELLOW         6
1547 #define COLOR_LT_GREEN          7
1548 #define COLOR_LT_CYAN           8
1549 #define COLOR_LT_GREY           9
1550
1551 void DrawControlWidgets(void)
1552 {
1553 //Note that the grid should go *behind* the face outline... !!! FIX !!!
1554         // Grid
1555         float xStart = center.x() - (gridWidth / 2.0f);
1556         float yStart = center.y() - (gridWidth / 2.0f);
1557         float xScale = (extents.height() / extents.width()) * (textureSize.y() / textureSize.x());
1558
1559   glPushMatrix();
1560 //Small problem with this approach: Changing the center point in the TX code doesn't seem to
1561 //change anything here--prolly because we load a new identity matrix. A couple of ways to fix
1562 //this would be to get rid of that code, or change the center to a new point by taking into
1563 //account the transforms that we toss with the new identity matrix. Dunno which is better.
1564   glLoadIdentity();
1565   glScalef(xScale, 1.0, 1.0);                           // Will that square it up? Yup.
1566   glRotatef(static_cast<float>(radians_to_degrees(atan2(-currentBP.coords[0][1], currentBP.coords[0][0]))), 0.0, 0.0, -1.0);
1567   glTranslatef(-center.x(), -center.y(), 0.0);
1568
1569         // Circle
1570   glColor3fv(translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW]);
1571   glBegin(GL_LINE_LOOP);
1572         DrawCircularArc(center, 0, 2.0f * PI, gridRadius * 0.16);
1573
1574   glEnd();
1575
1576         // Axes
1577   glBegin(GL_LINES);
1578   glColor3fv(translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN]);
1579   glVertex2f(center.x(), center.y() + (gridRadius * 0.16));
1580   glVertex2f(center.x(), center.y() + (gridRadius * 1.00));
1581   glColor3fv(translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED]);
1582   glVertex2f(center.x() + (gridRadius * 0.16), center.y());
1583   glVertex2f(center.x() + (gridRadius * 1.00), center.y());
1584   glEnd();
1585
1586         // Arrowheads
1587   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1588   glBegin(GL_TRIANGLES);
1589   glColor3fv(translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN]);
1590   glVertex2f(center.x(), center.y() + (gridRadius * 1.10));
1591   glVertex2f(center.x() + (gridRadius * 0.06), center.y() + (gridRadius * 0.94));
1592   glVertex2f(center.x() - (gridRadius * 0.06), center.y() + (gridRadius * 0.94));
1593   glColor3fv(translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED]);
1594   glVertex2f(center.x() + (gridRadius * 1.10), center.y());
1595   glVertex2f(center.x() + (gridRadius * 0.94), center.y() + (gridRadius * 0.06));
1596   glVertex2f(center.x() + (gridRadius * 0.94), center.y() - (gridRadius * 0.06));
1597   glEnd();
1598
1599         // Arc
1600   glBegin(GL_LINE_STRIP);
1601   glColor3fv(rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN]);
1602         DrawCircularArc(center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90);
1603   glEnd();
1604
1605         // Boxes
1606   glColor3fv(scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN]);
1607   glBegin(GL_LINES);
1608   glVertex2f(center.x() + (gridRadius * 0.20), center.y() + (gridRadius * 1.50));
1609   glVertex2f(center.x() - (gridRadius * 0.20), center.y() + (gridRadius * 1.50));
1610   glEnd();
1611   glBegin(GL_LINE_LOOP);
1612   glVertex2f(center.x() + (gridRadius * 0.10), center.y() + (gridRadius * 1.40));
1613   glVertex2f(center.x() - (gridRadius * 0.10), center.y() + (gridRadius * 1.40));
1614   glVertex2f(center.x() - (gridRadius * 0.10), center.y() + (gridRadius * 1.20));
1615   glVertex2f(center.x() + (gridRadius * 0.10), center.y() + (gridRadius * 1.20));
1616   glEnd();
1617
1618   glColor3fv(scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED]);
1619   glBegin(GL_LINES);
1620   glVertex2f(center.x() + (gridRadius * 1.50), center.y() + (gridRadius * 0.20));
1621   glVertex2f(center.x() + (gridRadius * 1.50), center.y() - (gridRadius * 0.20));
1622   glEnd();
1623   glBegin(GL_LINE_LOOP);
1624   glVertex2f(center.x() + (gridRadius * 1.40), center.y() + (gridRadius * 0.10));
1625   glVertex2f(center.x() + (gridRadius * 1.40), center.y() - (gridRadius * 0.10));
1626   glVertex2f(center.x() + (gridRadius * 1.20), center.y() - (gridRadius * 0.10));
1627   glVertex2f(center.x() + (gridRadius * 1.20), center.y() + (gridRadius * 0.10));
1628   glEnd();
1629
1630   glColor3fv(scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN]);
1631   glBegin(GL_LINE_STRIP);
1632   glVertex2f(center.x() + (gridRadius * 1.50), center.y() + (gridRadius * 1.10));
1633   glVertex2f(center.x() + (gridRadius * 1.50), center.y() + (gridRadius * 1.50));
1634   glVertex2f(center.x() + (gridRadius * 1.10), center.y() + (gridRadius * 1.50));
1635   glEnd();
1636   glBegin(GL_LINE_LOOP);
1637   glVertex2f(center.x() + (gridRadius * 1.40), center.y() + (gridRadius * 1.40));
1638   glVertex2f(center.x() + (gridRadius * 1.40), center.y() + (gridRadius * 1.20));
1639   glVertex2f(center.x() + (gridRadius * 1.20), center.y() + (gridRadius * 1.20));
1640   glVertex2f(center.x() + (gridRadius * 1.20), center.y() + (gridRadius * 1.40));
1641   glEnd();
1642
1643   glPopMatrix();
1644 }
1645
1646 void DrawControlPoints(void)
1647 {
1648   glColor3f(1, 1, 1);
1649   glBegin(GL_LINE_LOOP);
1650         
1651         for(int i=0; i<numPts; i++)
1652           glVertex2f(pts[i].x(), pts[i].y());
1653         
1654   glEnd();
1655 }
1656
1657 // Note: Setup and all that jazz must be done by the caller!
1658
1659 void DrawCircularArc(Vector2 ctr, float startAngle, float endAngle, float radius)
1660 {
1661         float stepSize = (2.0f * PI) / 200.0f;
1662
1663         for(float angle=startAngle; angle<=endAngle; angle+=stepSize)
1664           glVertex2f(ctr.x() + radius * cos(angle), ctr.y() + radius * sin(angle));
1665 }
1666
1667
1668 void focus()
1669 {
1670         if (numPts == 0)
1671                 return;
1672
1673         // Find selected texture's extents...
1674
1675         extents.minX = extents.maxX = pts[0].x(),
1676         extents.minY = extents.maxY = pts[0].y();
1677
1678         for(int i=1; i<numPts; i++)
1679         {
1680                 if (pts[i].x() < extents.minX)
1681                         extents.minX = pts[i].x();
1682                 if (pts[i].x() > extents.maxX)
1683                         extents.maxX = pts[i].x();
1684                 if (pts[i].y() < extents.minY)
1685                         extents.minY = pts[i].y();
1686                 if (pts[i].y() > extents.maxY)
1687                         extents.maxY = pts[i].y();
1688         }
1689
1690         // Do some viewport fitting stuff...
1691
1692 //globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n";
1693 //globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", "
1694 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1695         // TTimo: Apply a ratio to get the area we'll draw.
1696         center.x() = 0.5f * (extents.minX + extents.maxX),
1697         center.y() = 0.5f * (extents.minY + extents.maxY);
1698         extents.minX = center.x() + VP_PADDING * (extents.minX - center.x()),
1699         extents.minY = center.y() + VP_PADDING * (extents.minY - center.y()),
1700         extents.maxX = center.x() + VP_PADDING * (extents.maxX - center.x()),
1701         extents.maxY = center.y() + VP_PADDING * (extents.maxY - center.y());
1702 //globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", "
1703 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1704
1705         // TTimo: We want a texture with the same X / Y ratio.
1706         // TTimo: Compute XY space / window size ratio.
1707         float SSize = extents.width(), TSize = extents.height();
1708         float ratioX = textureSize.x() * extents.width() / windowSize.x(),
1709                 ratioY = textureSize.y() * extents.height() / windowSize.y();
1710 //globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n";
1711 //globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n";
1712
1713         if (ratioX > ratioY)
1714         {
1715                 TSize = (windowSize.y() * ratioX) / textureSize.y();
1716 //              TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y());
1717         }
1718         else
1719         {
1720                 SSize = (windowSize.x() * ratioY) / textureSize.x();
1721 //              SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x());
1722         }
1723
1724         extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize,
1725         extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize;
1726 //globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", "
1727 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1728 }
1729
1730 gboolean size_allocate(GtkWidget * win, GtkAllocation * a, gpointer)
1731 {
1732         windowSize.x() = a->width;
1733         windowSize.y() = a->height;
1734         queueDraw();
1735         return false;
1736 }
1737
1738 gboolean expose(GtkWidget * win, GdkEventExpose * e, gpointer)
1739 {
1740 //      globalOutputStream() << "--> Textool Window was exposed!\n";
1741 //      globalOutputStream() << "    (window width/height: " << cc << "/" << e->area.height << ")\n";
1742
1743 //      windowSize.x() = e->area.width, windowSize.y() = e->area.height;
1744 //This needs to go elsewhere...
1745 //      InitTextool();
1746
1747         if (glwidget_make_current(win) == FALSE)
1748         {
1749                 globalOutputStream() << "    FAILED to make current! Oh, the agony! :-(\n";
1750                 return true;
1751         }
1752         
1753         CopyPointsFromSelectedFace();
1754
1755   if(!lButtonDown)
1756   {
1757     focus();
1758   }
1759
1760         // Probably should init button/anchor states here as well...
1761 //      rotationAngle = 0.0f;
1762   glClearColor(0, 0, 0, 0);
1763   glViewport(0, 0, e->area.width, e->area.height);
1764   glMatrixMode(GL_PROJECTION);
1765   glLoadIdentity();
1766
1767 //???
1768   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1769   glDisable(GL_DEPTH_TEST);
1770   glDisable(GL_BLEND);
1771
1772   glOrtho(extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1);
1773
1774   glColor3f(1, 1, 1);
1775         // draw the texture background
1776   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1777   glBindTexture(GL_TEXTURE_2D, textureNum);
1778
1779   glEnable(GL_TEXTURE_2D);
1780   glBegin(GL_QUADS);
1781   glTexCoord2f(extents.minX, extents.minY);
1782   glVertex2f(extents.minX, extents.minY);
1783   glTexCoord2f(extents.maxX, extents.minY);
1784   glVertex2f(extents.maxX, extents.minY);
1785   glTexCoord2f(extents.maxX, extents.maxY);
1786   glVertex2f(extents.maxX, extents.maxY);
1787   glTexCoord2f(extents.minX, extents.maxY);
1788   glVertex2f(extents.minX, extents.maxY);
1789   glEnd();
1790   glDisable(GL_TEXTURE_2D);
1791
1792   // draw the texture-space grid
1793   glColor3fv(widgetColor[COLOR_GREY]);
1794   glBegin(GL_LINES);
1795
1796   const int gridSubdivisions = 8;
1797   const float gridExtents = 4.0f;
1798
1799         for(int i = 0; i < gridSubdivisions + 1; ++i)
1800         {
1801     float y = i * (gridExtents / float(gridSubdivisions));
1802     float x = i * (gridExtents / float(gridSubdivisions));
1803           glVertex2f(0, y);
1804           glVertex2f(gridExtents, y);
1805           glVertex2f(x, 0);
1806           glVertex2f(x, gridExtents);
1807         }
1808
1809   glEnd();
1810
1811         DrawControlPoints();
1812         DrawControlWidgets();
1813 //???
1814         // reset the current texture
1815 //  glBindTexture(GL_TEXTURE_2D, 0);
1816 //  glFinish();
1817         glwidget_swap_buffers(win);
1818
1819         return false;
1820 }
1821
1822 /*int FindSelectedPoint(int x, int y)
1823 {
1824         for(int i=0; i<numPts; i++)
1825         {
1826                 int nx = (int)(windowSize.x() * (pts[i].x() - extents.minX) / extents.width());
1827                 int ny = (int)(windowSize.y() * (pts[i].y() - extents.minY) / extents.height());
1828
1829                 if (abs(nx - x) <= 3 && abs(ny - y) <= 3)
1830                         return i;
1831         }
1832
1833         return -1;
1834 }//*/
1835
1836 Vector2 trans;
1837 Vector2 trans2;
1838 Vector2 dragPoint;      // Defined in terms of window space (+x/-y)
1839 Vector2 oldTrans;
1840 gboolean button_press(GtkWidget * win, GdkEventButton * e, gpointer)
1841 {
1842 //      globalOutputStream() << "--> Textool button press...\n";
1843
1844         if (e->button == 1)
1845         {
1846                 lButtonDown = true;
1847     GlobalUndoSystem().start();
1848
1849     origBP = currentBP;
1850
1851   //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n";
1852   //globalOutputStream() << "                 [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n";
1853   //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f;
1854           origAngle = (origBP.coords[0][1] > 0 ? PI : -PI);     // Could also be -PI... !!! FIX !!! [DONE]
1855
1856           if (origBP.coords[0][0] != 0.0f)
1857                   origAngle = atan(origBP.coords[0][1] / origBP.coords[0][0]);
1858
1859           origScaleX = origBP.coords[0][0] / cos(origAngle);
1860           origScaleY = origBP.coords[1][1] / cos(origAngle);
1861           rotationAngle = origAngle;
1862           oldCenter[0] = oldCenter[1] = 0;
1863
1864     //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n";
1865     //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!!
1866     //Also: should reverse texture left/right up/down instead of flipping the points...
1867
1868 //disnowok
1869 //float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX);
1870 //float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY);
1871 //disdoes...
1872 //But I want it to scroll the texture window, not the points... !!! FIX !!!
1873 //Actually, should scroll the texture window only when mouse is down on no widgets...
1874                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
1875                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
1876                 trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny;
1877                 trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny;
1878
1879                 dragPoint.x() = e->x, dragPoint.y() = e->y;
1880                 trans2.x() = nx, trans2.y() = ny;
1881                 oldRotationAngle = rotationAngle;
1882 //              oldTrans.x() = tm.coords[0][2] - nx * textureSize.x();
1883 //              oldTrans.y() = tm.coords[1][2] - ny * textureSize.y();
1884                 oldTrans.x() = tm.coords[0][2];
1885                 oldTrans.y() = tm.coords[1][2];
1886                 oldCenter.x() = center.x();
1887                 oldCenter.y() = center.y();
1888
1889           queueDraw();
1890
1891                 return true;
1892         }
1893 /*      else if (e->button == 3)
1894         {
1895                 rButtonDown = true;
1896         }//*/
1897
1898 //globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n";
1899
1900         return false;
1901 }
1902
1903 gboolean button_release(GtkWidget * win, GdkEventButton * e, gpointer)
1904 {
1905 //      globalOutputStream() << "--> Textool button release...\n";
1906
1907         if (e->button == 1)
1908         {
1909 /*              float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
1910                 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
1911
1912 //This prolly should go into the mouse move code...
1913 //Doesn't work correctly anyway...
1914                 if (translatingX || translatingY)
1915                         center.x() = ptx, center.y() = pty;//*/
1916
1917                 lButtonDown = false;
1918
1919     if(translatingX || translatingY)
1920     {
1921       GlobalUndoSystem().finish("translateTexture");
1922     }
1923     else if(rotating)
1924     {
1925       GlobalUndoSystem().finish("rotateTexture");
1926     }
1927     else if(scalingX || scalingY)
1928     {
1929       GlobalUndoSystem().finish("scaleTexture");
1930     }
1931     else if(resizingX || resizingY)
1932     {
1933       GlobalUndoSystem().finish("resizeTexture");
1934     }
1935     else
1936     {
1937       GlobalUndoSystem().finish("textoolUnknown");
1938     }
1939
1940                 rotating = translatingX = translatingY = scalingX = scalingY
1941                         = resizingX = resizingY = false;
1942
1943           queueDraw();
1944         }
1945         else if (e->button == 3)
1946         {
1947                 rButtonDown = false;
1948         }
1949
1950         return true;
1951 }
1952
1953 /*
1954 void C2DView::GridForWindow( float c[2], int x, int y)
1955 {
1956   SpaceForWindow( c, x, y );
1957   if ( !m_bDoGrid )
1958     return;
1959   c[0] /= m_GridStep[0];
1960   c[1] /= m_GridStep[1];
1961   c[0] = (float)floor( c[0] + 0.5f );
1962   c[1] = (float)floor( c[1] + 0.5f );
1963   c[0] *= m_GridStep[0];
1964   c[1] *= m_GridStep[1];
1965 }
1966 void C2DView::SpaceForWindow( float c[2], int x, int y)
1967 {
1968   c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0];
1969   c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1];
1970 }
1971 */
1972 gboolean motion(GtkWidget * win, GdkEventMotion * e, gpointer)
1973 {
1974 //      globalOutputStream() << "--> Textool motion...\n";
1975
1976         if (lButtonDown)
1977         {
1978                 if (translatingX || translatingY)
1979                 {
1980                         float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
1981                         float pty = e->y / windowSize.y() * extents.height() + extents.minY;
1982
1983 //Need to fix this to take the rotation angle into account, so that it moves along
1984 //the rotated X/Y axis...
1985                         if (translatingX)
1986                         {
1987 //                              tm.coords[0][2] = (trans.x() + ptx) * textureSize.x();
1988 //This works, but only when the angle is zero. !!! FIX !!! [DONE]
1989 //                              tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x());
1990                                 tm.coords[0][2] = oldTrans.x() + (ptx - trans2.x()) * textureSize.x();
1991 //                              center.x() = oldCenter.x() + (ptx - trans2.x());
1992                         }
1993
1994                         if (translatingY)
1995                         {
1996 //                              tm.coords[1][2] = (trans.y() + pty) * textureSize.y();
1997 //                              tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y());
1998                                 tm.coords[1][2] = oldTrans.y() + (pty - trans2.y()) * textureSize.y();
1999 //                              center.y() = oldCenter.y() + (pty - trans2.y());
2000                         }
2001
2002 //Need to update center.x/y() so that the widget translates as well. Also, oldCenter
2003 //is badly named... Should be oldTrans or something like that... !!! FIX !!!
2004 //Changing center.x/y() here doesn't seem to change anything... :-/
2005                         UpdateControlPoints();
2006                 }
2007                 else if (rotating)
2008                 {
2009                         // Shamus: New rotate code
2010                         int cx = (int)(windowSize.x() * (center.x() - extents.minX) / extents.width());
2011                         int cy = (int)(windowSize.y() * (center.y() - extents.minY) / extents.height());
2012                         Vector3 v1(dragPoint.x() - cx, dragPoint.y() - cy, 0), v2(e->x - cx, e->y - cy, 0);
2013
2014                         vector3_normalise(v1);
2015                         vector3_normalise(v2);
2016                         float c = vector3_dot(v1, v2);
2017                         Vector3 cross = vector3_cross(v1, v2);
2018                         float s = vector3_length(cross);
2019
2020                         if (cross[2] > 0)
2021                                 s = -s;
2022
2023 // Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180...
2024 // Can't derive angle from that!
2025
2026 //rotationAngle = asin(s);// * 180.0f / 3.141592653589f;
2027 rotationAngle = acos(c);
2028 //rotationAngle2 = asin(s);
2029 if (cross[2] < 0)
2030         rotationAngle = -rotationAngle;
2031
2032 //NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD;
2033 //Let's try this:
2034 //No wok.
2035 /*c = cos(rotationAngle - oldRotationAngle);
2036 s = sin(rotationAngle - oldRotationAngle);
2037 rotationAngle += oldRotationAngle;
2038 //c += cos(oldRotationAngle);
2039 //s += sin(oldRotationAngle);
2040 //rotationAngle += oldRotationAngle;
2041 //c %= 2.0 * PI;
2042 //s %= 2.0 * PI;
2043 //rotationAngle %= 2.0 * PI;//*/
2044
2045 //This is wrong... Hmm...
2046 //It seems to shear the texture instead of rotating it... !!! FIX !!!
2047 // Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-)
2048
2049 // Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!!
2050
2051 // See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from...
2052
2053                         tm.coords[0][0] =  c;
2054                         tm.coords[0][1] =  s;
2055                         tm.coords[1][0] = -s;
2056                         tm.coords[1][1] =  c;
2057 //It doesn't work anymore... Dunno why...
2058 //tm.coords[0][2] = -trans.x();                 // This works!!! Yeah!!!
2059 //tm.coords[1][2] = -trans.y();
2060 //nope.
2061 //tm.coords[0][2] = rotationPoint.x();  // This works, but strangely...
2062 //tm.coords[1][2] = rotationPoint.y();
2063 //tm.coords[0][2] = 0;// center.x() / 2.0f;
2064 //tm.coords[1][2] = 0;// center.y() / 2.0f;
2065 //No.
2066 //tm.coords[0][2] = -(center.x() * textureSize.x());
2067 //tm.coords[1][2] = -(center.y() * textureSize.y());
2068 //Eh? No, but seems to be getting closer...
2069 /*float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2070 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2071 tm.coords[0][2] = -c * center.x() - s * center.y() + ptx;
2072 tm.coords[1][2] =  s * center.x() - c * center.x() + pty;//*/
2073 //Kinda works, but center drifts around on non-square textures...
2074 /*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x();
2075 tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/
2076 //Rotates correctly, but not around the actual center of the face's points...
2077 /*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y();
2078 tm.coords[1][2] =  s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/
2079 //Yes!!!
2080                         tm.coords[0][2] = (-c * center.x() * textureSize.x() - s * center.y() * textureSize.y()) + center.x() * textureSize.x();
2081                         tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y()) + center.y() * textureSize.y();//*/
2082 //This doesn't work...
2083 //And this is the wrong place for this anyway (I'm pretty sure).
2084 /*tm.coords[0][2] += oldCenter.x();
2085 tm.coords[1][2] += oldCenter.y();//*/
2086                         UpdateControlPoints(); // will cause a redraw
2087                 }
2088
2089                 return true;
2090         }
2091         else                                                                    // Check for widget mouseovers
2092         {
2093                 Vector2 tran;
2094                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2095                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2096                 // Translate nx/y to the "center" point...
2097                 nx -= center.x();
2098                 ny -= center.y();
2099                 ny = -ny;       // Flip Y-axis so that increasing numbers move up
2100
2101                 tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny;
2102                 tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny;
2103 //This doesn't seem to generate a valid distance from the center--for some reason it
2104 //calculates a fixed number every time
2105 //Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE]
2106                 float dist = sqrt((nx * nx) + (ny * ny));
2107                 // Normalize to the 2.0 = height standard (for now)
2108 //globalOutputStream() << "--> Distance before: " << dist;
2109                 dist = dist * 2.0f / extents.height();
2110 //globalOutputStream() << ". After: " << dist;
2111                 tran.x() = tran.x() * 2.0f / extents.height();
2112                 tran.y() = tran.y() * 2.0f / extents.height();
2113 //globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n";
2114
2115 //Let's try this instead...
2116 //Interesting! It seems that e->x/y are rotated
2117 //(no, they're not--the TM above is what's doing it...)
2118 nx = ((e->x / windowSize.y()) * 2.0f) - (windowSize.x() / windowSize.y());
2119 ny = ((e->y / windowSize.y()) * 2.0f) - (windowSize.y() / windowSize.y());
2120 ny = -ny;
2121 //Cool! It works! Now just need to do rotation...
2122
2123                 rotating = translatingX = translatingY = scalingX = scalingY
2124                         = resizingX = resizingY = false;
2125
2126                 if (dist < (gridRadius * 0.16f))
2127                 {
2128                         translatingX = translatingY = true;
2129                 }
2130                 else if (dist > (gridRadius * 0.16f) && dist < (gridRadius * 1.10f)
2131                         && fabs(ny) < (gridRadius * 0.05f) && nx > 0)
2132                 {
2133                         translatingX = true;
2134                 }
2135                 else if (dist > (gridRadius * 0.16f) && dist < (gridRadius * 1.10f)
2136                         && fabs(nx) < (gridRadius * 0.05f) && ny > 0)
2137                 {
2138                         translatingY = true;
2139                 }
2140                 // Should tighten up the angle on this, or put this test after the axis tests...
2141                 else if (tran.x() > 0 && tran.y() > 0
2142                         && (dist > (gridRadius * 0.82f) && dist < (gridRadius * 0.98f)))
2143                 {
2144                         rotating = true;
2145                 }
2146
2147           queueDraw();
2148
2149                 return true;
2150         }
2151
2152         return false;
2153 }
2154
2155 //It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!!
2156 //This is still wrong... Prolly need to do something with the oldScaleX/Y stuff...
2157 void flipX(GtkToggleButton *, gpointer)
2158 {
2159 //      globalOutputStream() << "--> Flip X...\n";
2160         //Shamus:
2161 //      SurfaceInspector_GetSelectedBPTexdef();         // Refresh g_selectedBrushPrimitTexdef...
2162 //      tm.coords[0][0] = -tm.coords[0][0];
2163 //      tm.coords[1][0] = -tm.coords[1][0];
2164 //      tm.coords[0][0] = -tm.coords[0][0];                     // This should be correct now...Nope.
2165 //      tm.coords[1][1] = -tm.coords[1][1];
2166         tm.coords[0][0] = -tm.coords[0][0];                     // This should be correct now...
2167         tm.coords[1][0] = -tm.coords[1][0];
2168 //      tm.coords[2][0] = -tm.coords[2][0];//wil wok? no.
2169         UpdateControlPoints();
2170 }
2171
2172 void flipY(GtkToggleButton *, gpointer)
2173 {
2174 //      globalOutputStream() << "--> Flip Y...\n";
2175 //      tm.coords[0][1] = -tm.coords[0][1];
2176 //      tm.coords[1][1] = -tm.coords[1][1];
2177 //      tm.coords[0][1] = -tm.coords[0][1];                     // This should be correct now...Nope.
2178 //      tm.coords[1][0] = -tm.coords[1][0];
2179         tm.coords[0][1] = -tm.coords[0][1];                     // This should be correct now...
2180         tm.coords[1][1] = -tm.coords[1][1];
2181 //      tm.coords[2][1] = -tm.coords[2][1];//wil wok? no.
2182         UpdateControlPoints();
2183 }
2184
2185 } // end namespace TexTool
2186
2187 #endif