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