]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/surfacedialog.cpp
[q3map2] Unwind script stack in case of script loading error.
[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 extern TextureBrowser g_TextureBrowser;
1408
1409 void TextureBrowser_SetSelectedShader(TextureBrowser &textureBrowser, const char *shader);
1410
1411 const char *TextureBrowser_GetSelectedShader(TextureBrowser &textureBrowser);
1412
1413 void Scene_copyClosestTexture(SelectionTest &test)
1414 {
1415     CopiedString shader;
1416     if (Scene_getClosestTexture(GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection,
1417                                 g_faceTextureClipboard.m_flags)) {
1418         TextureBrowser_SetSelectedShader(g_TextureBrowser, shader.c_str());
1419     }
1420 }
1421
1422 void Scene_applyClosestTexture(SelectionTest &test)
1423 {
1424     UndoableCommand command("facePaintTexture");
1425
1426     Scene_setClosestTexture(GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader(g_TextureBrowser),
1427                             g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags);
1428
1429     SceneChangeNotify();
1430 }
1431
1432
1433 void SelectedFaces_copyTexture()
1434 {
1435     if (!g_SelectedFaceInstances.empty()) {
1436         Face &face = g_SelectedFaceInstances.last().getFace();
1437         face.GetTexdef(g_faceTextureClipboard.m_projection);
1438         g_faceTextureClipboard.m_flags = face.getShader().m_flags;
1439
1440         TextureBrowser_SetSelectedShader(g_TextureBrowser, face.getShader().getShader());
1441     }
1442 }
1443
1444 void FaceInstance_pasteTexture(FaceInstance &faceInstance)
1445 {
1446     faceInstance.getFace().SetTexdef(g_faceTextureClipboard.m_projection);
1447     faceInstance.getFace().SetShader(TextureBrowser_GetSelectedShader(g_TextureBrowser));
1448     faceInstance.getFace().SetFlags(g_faceTextureClipboard.m_flags);
1449     SceneChangeNotify();
1450 }
1451
1452 bool SelectedFaces_empty()
1453 {
1454     return g_SelectedFaceInstances.empty();
1455 }
1456
1457 void SelectedFaces_pasteTexture()
1458 {
1459     UndoableCommand command("facePasteTexture");
1460     g_SelectedFaceInstances.foreach(FaceInstance_pasteTexture);
1461 }
1462
1463
1464 void SurfaceInspector_constructPreferences(PreferencesPage &page)
1465 {
1466     page.appendCheckBox("", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid);
1467 }
1468
1469 void SurfaceInspector_constructPage(PreferenceGroup &group)
1470 {
1471     PreferencesPage page(group.createPage("Surface Inspector", "Surface Inspector Preferences"));
1472     SurfaceInspector_constructPreferences(page);
1473 }
1474
1475 void SurfaceInspector_registerPreferencesPage()
1476 {
1477     PreferencesDialog_addSettingsPage(makeCallbackF(SurfaceInspector_constructPage));
1478 }
1479
1480 void SurfaceInspector_registerCommands()
1481 {
1482     GlobalCommands_insert("FitTexture", makeCallbackF(SurfaceInspector_FitTexture),
1483                           Accelerator('B', (GdkModifierType) GDK_SHIFT_MASK));
1484     GlobalCommands_insert("SurfaceInspector", makeCallbackF(SurfaceInspector_toggleShown), Accelerator('S'));
1485
1486     GlobalCommands_insert("FaceCopyTexture", makeCallbackF(SelectedFaces_copyTexture));
1487     GlobalCommands_insert("FacePasteTexture", makeCallbackF(SelectedFaces_pasteTexture));
1488 }
1489
1490
1491 #include "preferencesystem.h"
1492
1493
1494 void SurfaceInspector_Construct()
1495 {
1496     g_SurfaceInspector = new SurfaceInspector;
1497
1498     SurfaceInspector_registerCommands();
1499
1500     FaceTextureClipboard_setDefault();
1501
1502     GlobalPreferenceSystem().registerPreference("SurfaceWnd", make_property<WindowPositionTracker_String>(
1503             getSurfaceInspector().m_positionTracker));
1504     GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale1", make_property_string(g_si_globals.scale[0]));
1505     GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale2", make_property_string(g_si_globals.scale[1]));
1506     GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift1", make_property_string(g_si_globals.shift[0]));
1507     GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift2", make_property_string(g_si_globals.shift[1]));
1508     GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Rotate", make_property_string(g_si_globals.rotate));
1509     GlobalPreferenceSystem().registerPreference("SnapTToGrid", make_property_string(g_si_globals.m_bSnapTToGrid));
1510
1511     typedef FreeCaller<void(
1512             const Selectable &), SurfaceInspector_SelectionChanged> SurfaceInspectorSelectionChangedCaller;
1513     GlobalSelectionSystem().addSelectionChangeCallback(SurfaceInspectorSelectionChangedCaller());
1514     typedef FreeCaller<void(), SurfaceInspector_updateSelection> SurfaceInspectorUpdateSelectionCaller;
1515     Brush_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller());
1516     Patch_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller());
1517
1518     SurfaceInspector_registerPreferencesPage();
1519 }
1520
1521 void SurfaceInspector_Destroy()
1522 {
1523     delete g_SurfaceInspector;
1524 }
1525
1526
1527 #if TEXTOOL_ENABLED
1528
1529 namespace TexTool { // namespace hides these symbols from other object-files
1530 //
1531 //Shamus: Textool functions, including GTK+ callbacks
1532 //
1533
1534 //NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!!
1535 //      But... You can see down below that it *is* initialized! WTF?
1536 struct Extent
1537 {
1538     float minX, minY, maxX, maxY;
1539     float width( void ) { return fabs( maxX - minX ); }
1540     float height( void ) { return fabs( maxY - minY ); }
1541 };
1542
1543 //This seems to control the texture scale... (Yep! ;-)
1544 Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f };
1545 brushprimit_texdef_t tm;                        // Texture transform matrix
1546 Vector2 pts[c_brush_maxFaces];
1547 Vector2 center;
1548 int numPts;
1549 int textureNum;
1550 Vector2 textureSize;
1551 Vector2 windowSize;
1552 #define VP_PADDING  1.2
1553 #define PI          3.14159265358979
1554 bool lButtonDown = false;
1555 bool rButtonDown = false;
1556 //int dragPoint;
1557 //int anchorPoint;
1558 bool haveAnchor = false;
1559 brushprimit_texdef_t currentBP;
1560 brushprimit_texdef_t origBP;                    // Original brush primitive (before we muck it up)
1561 float controlRadius = 5.0f;
1562 float rotationAngle = 0.0f;
1563 float rotationAngle2 = 0.0f;
1564 float oldRotationAngle;
1565 Vector2 rotationPoint;
1566 bool translatingX = false;                      // Widget state variables
1567 bool translatingY = false;
1568 bool scalingX = false;
1569 bool scalingY = false;
1570 bool rotating = false;
1571 bool resizingX = false;                         // Not sure what this means... :-/
1572 bool resizingY = false;
1573 float origAngle, origScaleX, origScaleY;
1574 Vector2 oldCenter;
1575
1576
1577 // Function prototypes (move up to top later...)
1578
1579 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius );
1580
1581
1582 void CopyPointsFromSelectedFace( void ){
1583     // Make sure that there's a face and winding to get!
1584
1585     if ( g_SelectedFaceInstances.empty() ) {
1586         numPts = 0;
1587         return;
1588     }
1589
1590     Face & face = g_SelectedFaceInstances.last().getFace();
1591     textureNum = face.getShader().m_state->getTexture().texture_number;
1592     textureSize.x() = face.getShader().m_state->getTexture().width;
1593     textureSize.y() = face.getShader().m_state->getTexture().height;
1594 //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n";
1595
1596     currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef;
1597
1598     face.EmitTextureCoordinates();
1599     Winding & w = face.getWinding();
1600     int count = 0;
1601
1602     for ( Winding::const_iterator i = w.begin(); i != w.end(); i++ )
1603     {
1604         //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", ";
1605         pts[count].x() = ( *i ).texcoord.x();
1606         pts[count].y() = ( *i ).texcoord.y();
1607         count++;
1608     }
1609
1610     numPts = count;
1611
1612     //globalOutputStream() << " ..copied points\n";
1613 }
1614
1615 brushprimit_texdef_t bp;
1616 //This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE]
1617 void CommitChanges( void ){
1618     texdef_t t;                                 // Throwaway, since this is BP only
1619
1620     bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0];
1621     bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1];
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];
1623 //Ok, this works for translation...
1624 //      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();
1625     bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0];
1626     bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1];
1627     bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2];
1628 //      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();
1629
1630 //This doesn't work:    g_brush_texture_changed();
1631 // Let's try this:
1632 //Note: We should only set an undo *after* the button has been released... !!! FIX !!!
1633 //Definitely *should* have an undo, though!
1634 //  UndoableCommand undo("textureProjectionSetSelected");
1635     Select_SetTexdef( TextureProjection( t, bp, Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) ) );
1636 //This is working, but for some reason the translate is causing the rest of the SI
1637 //widgets to yield bad readings... !!! FIX !!!
1638 //I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why.
1639 //It's because there were some uncommented out add/removeScale functions in brush.h and a
1640 //removeScale in brushmanip.cpp... :-/
1641 //Translate isn't working at all now... :-(
1642 //It's because we need to multiply in some scaling factor (prolly the texture width/height)
1643 //Yep. :-P
1644 }
1645
1646 void UpdateControlPoints( void ){
1647     CommitChanges();
1648
1649     // Init texture transform matrix
1650
1651     tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f;
1652     tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f;
1653 }
1654
1655
1656 /*
1657    For shifting we have:
1658  */
1659 /*
1660    The code that should provide reasonable defaults, but doesn't for some reason:
1661    It's scaling the BP by 128 for some reason, between the time it's created and the
1662    time we get back to the SI widgets:
1663
1664    static void OnBtnAxial(GtkWidget *widget, gpointer data)
1665    {
1666    UndoableCommand undo("textureDefault");
1667    TextureProjection projection;
1668    TexDef_Construct_Default(projection);
1669    Select_SetTexdef(projection);
1670    }
1671
1672    Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection)
1673    which is in brushmanip.h: This eventually calls
1674    Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face...
1675    which just copies from brushpr to m_brushpr...
1676  */
1677
1678 //Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE]
1679 //Prolly should separate out the grid drawing so that we can draw it behind the polygon.
1680 const float gridWidth = 1.3f; // Let's try an absolute height... WORKS!!!
1681 // NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision
1682 //      detection easier...
1683 const float gridRadius = gridWidth * 0.5f;
1684
1685 typedef const float WidgetColor[3];
1686 const WidgetColor widgetColor[10] = {
1687     { 1.0000f, 0.2000f, 0.0000f },          // Red
1688     { 0.9137f, 0.9765f, 0.4980f },          // Yellow
1689     { 0.0000f, 0.6000f, 0.3216f },          // Green
1690     { 0.6157f, 0.7726f, 0.8196f },          // Cyan
1691     { 0.4980f, 0.5000f, 0.4716f },          // Grey
1692
1693     // Highlight colors
1694     { 1.0000f, 0.6000f, 0.4000f },          // Light Red
1695     { 1.0000f, 1.0000f, 0.8980f },          // Light Yellow
1696     { 0.4000f, 1.0000f, 0.7216f },          // Light Green
1697     { 1.0000f, 1.0000f, 1.0000f },          // Light Cyan
1698     { 0.8980f, 0.9000f, 0.8716f }           // Light Grey
1699 };
1700
1701 #define COLOR_RED           0
1702 #define COLOR_YELLOW        1
1703 #define COLOR_GREEN         2
1704 #define COLOR_CYAN          3
1705 #define COLOR_GREY          4
1706 #define COLOR_LT_RED        5
1707 #define COLOR_LT_YELLOW     6
1708 #define COLOR_LT_GREEN      7
1709 #define COLOR_LT_CYAN       8
1710 #define COLOR_LT_GREY       9
1711
1712 void DrawControlWidgets( void ){
1713 //Note that the grid should go *behind* the face outline... !!! FIX !!!
1714     // Grid
1715     float xStart = center.x() - ( gridWidth / 2.0f );
1716     float yStart = center.y() - ( gridWidth / 2.0f );
1717     float xScale = ( extents.height() / extents.width() ) * ( textureSize.y() / textureSize.x() );
1718
1719     glPushMatrix();
1720 //Small problem with this approach: Changing the center point in the TX code doesn't seem to
1721 //change anything here--prolly because we load a new identity matrix. A couple of ways to fix
1722 //this would be to get rid of that code, or change the center to a new point by taking into
1723 //account the transforms that we toss with the new identity matrix. Dunno which is better.
1724     glLoadIdentity();
1725     glScalef( xScale, 1.0, 1.0 );           // Will that square it up? Yup.
1726     glRotatef( static_cast<float>( radians_to_degrees( atan2( -currentBP.coords[0][1], currentBP.coords[0][0] ) ) ), 0.0, 0.0, -1.0 );
1727     glTranslatef( -center.x(), -center.y(), 0.0 );
1728
1729     // Circle
1730     glColor3fv( translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW] );
1731     glBegin( GL_LINE_LOOP );
1732     DrawCircularArc( center, 0, 2.0f * PI, gridRadius * 0.16 );
1733
1734     glEnd();
1735
1736     // Axes
1737     glBegin( GL_LINES );
1738     glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1739     glVertex2f( center.x(), center.y() + ( gridRadius * 0.16 ) );
1740     glVertex2f( center.x(), center.y() + ( gridRadius * 1.00 ) );
1741     glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1742     glVertex2f( center.x() + ( gridRadius * 0.16 ), center.y() );
1743     glVertex2f( center.x() + ( gridRadius * 1.00 ), center.y() );
1744     glEnd();
1745
1746     // Arrowheads
1747     glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1748     glBegin( GL_TRIANGLES );
1749     glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1750     glVertex2f( center.x(), center.y() + ( gridRadius * 1.10 ) );
1751     glVertex2f( center.x() + ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1752     glVertex2f( center.x() - ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1753     glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1754     glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() );
1755     glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() + ( gridRadius * 0.06 ) );
1756     glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() - ( gridRadius * 0.06 ) );
1757     glEnd();
1758
1759     // Arc
1760     glBegin( GL_LINE_STRIP );
1761     glColor3fv( rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1762     DrawCircularArc( center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90 );
1763     glEnd();
1764
1765     // Boxes
1766     glColor3fv( scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1767     glBegin( GL_LINES );
1768     glVertex2f( center.x() + ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1769     glVertex2f( center.x() - ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1770     glEnd();
1771     glBegin( GL_LINE_LOOP );
1772     glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1773     glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1774     glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1775     glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1776     glEnd();
1777
1778     glColor3fv( scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1779     glBegin( GL_LINES );
1780     glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 0.20 ) );
1781     glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() - ( gridRadius * 0.20 ) );
1782     glEnd();
1783     glBegin( GL_LINE_LOOP );
1784     glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 0.10 ) );
1785     glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() - ( gridRadius * 0.10 ) );
1786     glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() - ( gridRadius * 0.10 ) );
1787     glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 0.10 ) );
1788     glEnd();
1789
1790     glColor3fv( scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1791     glBegin( GL_LINE_STRIP );
1792     glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.10 ) );
1793     glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.50 ) );
1794     glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() + ( gridRadius * 1.50 ) );
1795     glEnd();
1796     glBegin( GL_LINE_LOOP );
1797     glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.40 ) );
1798     glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.20 ) );
1799     glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.20 ) );
1800     glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.40 ) );
1801     glEnd();
1802
1803     glPopMatrix();
1804 }
1805
1806 void DrawControlPoints( void ){
1807     glColor3f( 1, 1, 1 );
1808     glBegin( GL_LINE_LOOP );
1809
1810     for ( int i = 0; i < numPts; i++ )
1811         glVertex2f( pts[i].x(), pts[i].y() );
1812
1813     glEnd();
1814 }
1815
1816 // Note: Setup and all that jazz must be done by the caller!
1817
1818 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius ){
1819     float stepSize = ( 2.0f * PI ) / 200.0f;
1820
1821     for ( float angle = startAngle; angle <= endAngle; angle += stepSize )
1822         glVertex2f( ctr.x() + radius * cos( angle ), ctr.y() + radius * sin( angle ) );
1823 }
1824
1825
1826 void focus(){
1827     if ( numPts == 0 ) {
1828         return;
1829     }
1830
1831     // Find selected texture's extents...
1832
1833     extents.minX = extents.maxX = pts[0].x(),
1834     extents.minY = extents.maxY = pts[0].y();
1835
1836     for ( int i = 1; i < numPts; i++ )
1837     {
1838         if ( pts[i].x() < extents.minX ) {
1839             extents.minX = pts[i].x();
1840         }
1841         if ( pts[i].x() > extents.maxX ) {
1842             extents.maxX = pts[i].x();
1843         }
1844         if ( pts[i].y() < extents.minY ) {
1845             extents.minY = pts[i].y();
1846         }
1847         if ( pts[i].y() > extents.maxY ) {
1848             extents.maxY = pts[i].y();
1849         }
1850     }
1851
1852     // Do some viewport fitting stuff...
1853
1854 //globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n";
1855 //globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", "
1856 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1857     // TTimo: Apply a ratio to get the area we'll draw.
1858     center.x() = 0.5f * ( extents.minX + extents.maxX ),
1859         center.y() = 0.5f * ( extents.minY + extents.maxY );
1860     extents.minX = center.x() + VP_PADDING * ( extents.minX - center.x() ),
1861     extents.minY = center.y() + VP_PADDING * ( extents.minY - center.y() ),
1862     extents.maxX = center.x() + VP_PADDING * ( extents.maxX - center.x() ),
1863     extents.maxY = center.y() + VP_PADDING * ( extents.maxY - center.y() );
1864 //globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", "
1865 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1866
1867     // TTimo: We want a texture with the same X / Y ratio.
1868     // TTimo: Compute XY space / window size ratio.
1869     float SSize = extents.width(), TSize = extents.height();
1870     float ratioX = textureSize.x() * extents.width() / windowSize.x(),
1871           ratioY = textureSize.y() * extents.height() / windowSize.y();
1872 //globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n";
1873 //globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n";
1874
1875     if ( ratioX > ratioY ) {
1876         TSize = ( windowSize.y() * ratioX ) / textureSize.y();
1877 //              TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y());
1878     }
1879     else
1880     {
1881         SSize = ( windowSize.x() * ratioY ) / textureSize.x();
1882 //              SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x());
1883     }
1884
1885     extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize,
1886     extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize;
1887 //globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", "
1888 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1889 }
1890
1891 gboolean size_allocate( ui::Widget win, GtkAllocation * a, gpointer ){
1892     windowSize.x() = a->width;
1893     windowSize.y() = a->height;
1894     queueDraw();
1895     return false;
1896 }
1897
1898 gboolean expose( ui::Widget win, GdkEventExpose * e, gpointer ){
1899 //      globalOutputStream() << "--> Textool Window was exposed!\n";
1900 //      globalOutputStream() << "    (window width/height: " << cc << "/" << e->area.height << ")\n";
1901
1902 //      windowSize.x() = e->area.width, windowSize.y() = e->area.height;
1903 //This needs to go elsewhere...
1904 //      InitTextool();
1905
1906     if ( glwidget_make_current( win ) == FALSE ) {
1907         globalOutputStream() << "    FAILED to make current! Oh, the agony! :-(\n";
1908         return true;
1909     }
1910
1911     CopyPointsFromSelectedFace();
1912
1913     if ( !lButtonDown ) {
1914         focus();
1915     }
1916
1917     // Probably should init button/anchor states here as well...
1918 //      rotationAngle = 0.0f;
1919     glClearColor( 0, 0, 0, 0 );
1920     glViewport( 0, 0, e->area.width, e->area.height );
1921     glMatrixMode( GL_PROJECTION );
1922     glLoadIdentity();
1923
1924 //???
1925     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1926     glDisable( GL_DEPTH_TEST );
1927     glDisable( GL_BLEND );
1928
1929     glOrtho( extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1 );
1930
1931     glColor3f( 1, 1, 1 );
1932     // draw the texture background
1933     glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1934     glBindTexture( GL_TEXTURE_2D, textureNum );
1935
1936     glEnable( GL_TEXTURE_2D );
1937     glBegin( GL_QUADS );
1938     glTexCoord2f( extents.minX, extents.minY );
1939     glVertex2f( extents.minX, extents.minY );
1940     glTexCoord2f( extents.maxX, extents.minY );
1941     glVertex2f( extents.maxX, extents.minY );
1942     glTexCoord2f( extents.maxX, extents.maxY );
1943     glVertex2f( extents.maxX, extents.maxY );
1944     glTexCoord2f( extents.minX, extents.maxY );
1945     glVertex2f( extents.minX, extents.maxY );
1946     glEnd();
1947     glDisable( GL_TEXTURE_2D );
1948
1949     // draw the texture-space grid
1950     glColor3fv( widgetColor[COLOR_GREY] );
1951     glBegin( GL_LINES );
1952
1953     const int gridSubdivisions = 8;
1954     const float gridExtents = 4.0f;
1955
1956     for ( int i = 0; i < gridSubdivisions + 1; ++i )
1957     {
1958         float y = i * ( gridExtents / float(gridSubdivisions) );
1959         float x = i * ( gridExtents / float(gridSubdivisions) );
1960         glVertex2f( 0, y );
1961         glVertex2f( gridExtents, y );
1962         glVertex2f( x, 0 );
1963         glVertex2f( x, gridExtents );
1964     }
1965
1966     glEnd();
1967
1968     DrawControlPoints();
1969     DrawControlWidgets();
1970 //???
1971     // reset the current texture
1972 //  glBindTexture(GL_TEXTURE_2D, 0);
1973 //  glFinish();
1974     glwidget_swap_buffers( win );
1975
1976     return false;
1977 }
1978
1979 /*int FindSelectedPoint(int x, int y)
1980    {
1981     for(int i=0; i<numPts; i++)
1982     {
1983         int nx = (int)(windowSize.x() * (pts[i].x() - extents.minX) / extents.width());
1984         int ny = (int)(windowSize.y() * (pts[i].y() - extents.minY) / extents.height());
1985
1986         if (abs(nx - x) <= 3 && abs(ny - y) <= 3)
1987             return i;
1988     }
1989
1990     return -1;
1991    }//*/
1992
1993 Vector2 trans;
1994 Vector2 trans2;
1995 Vector2 dragPoint;  // Defined in terms of window space (+x/-y)
1996 Vector2 oldTrans;
1997 gboolean button_press( ui::Widget win, GdkEventButton * e, gpointer ){
1998 //      globalOutputStream() << "--> Textool button press...\n";
1999
2000     if ( e->button == 1 ) {
2001         lButtonDown = true;
2002         GlobalUndoSystem().start();
2003
2004         origBP = currentBP;
2005
2006         //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n";
2007         //globalOutputStream() << "                 [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n";
2008         //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f;
2009         origAngle = ( origBP.coords[0][1] > 0 ? PI : -PI ); // Could also be -PI... !!! FIX !!! [DONE]
2010
2011         if ( origBP.coords[0][0] != 0.0f ) {
2012             origAngle = atan( origBP.coords[0][1] / origBP.coords[0][0] );
2013         }
2014
2015         origScaleX = origBP.coords[0][0] / cos( origAngle );
2016         origScaleY = origBP.coords[1][1] / cos( origAngle );
2017         rotationAngle = origAngle;
2018         oldCenter[0] = oldCenter[1] = 0;
2019
2020         //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n";
2021         //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!!
2022         //Also: should reverse texture left/right up/down instead of flipping the points...
2023
2024 //disnowok
2025 //float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX);
2026 //float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY);
2027 //disdoes...
2028 //But I want it to scroll the texture window, not the points... !!! FIX !!!
2029 //Actually, should scroll the texture window only when mouse is down on no widgets...
2030         float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2031         float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2032         trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny;
2033         trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny;
2034
2035         dragPoint.x() = e->x, dragPoint.y() = e->y;
2036         trans2.x() = nx, trans2.y() = ny;
2037         oldRotationAngle = rotationAngle;
2038 //              oldTrans.x() = tm.coords[0][2] - nx * textureSize.x();
2039 //              oldTrans.y() = tm.coords[1][2] - ny * textureSize.y();
2040         oldTrans.x() = tm.coords[0][2];
2041         oldTrans.y() = tm.coords[1][2];
2042         oldCenter.x() = center.x();
2043         oldCenter.y() = center.y();
2044
2045         queueDraw();
2046
2047         return true;
2048     }
2049 /*      else if (e->button == 3)
2050     {
2051         rButtonDown = true;
2052     }//*/
2053
2054 //globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n";
2055
2056     return false;
2057 }
2058
2059 gboolean button_release( ui::Widget win, GdkEventButton * e, gpointer ){
2060 //      globalOutputStream() << "--> Textool button release...\n";
2061
2062     if ( e->button == 1 ) {
2063 /*              float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2064         float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2065
2066    //This prolly should go into the mouse move code...
2067    //Doesn't work correctly anyway...
2068         if (translatingX || translatingY)
2069             center.x() = ptx, center.y() = pty;//*/
2070
2071         lButtonDown = false;
2072
2073         if ( translatingX || translatingY ) {
2074             GlobalUndoSystem().finish( "translateTexture" );
2075         }
2076         else if ( rotating ) {
2077             GlobalUndoSystem().finish( "rotateTexture" );
2078         }
2079         else if ( scalingX || scalingY ) {
2080             GlobalUndoSystem().finish( "scaleTexture" );
2081         }
2082         else if ( resizingX || resizingY ) {
2083             GlobalUndoSystem().finish( "resizeTexture" );
2084         }
2085         else
2086         {
2087             GlobalUndoSystem().finish( "textoolUnknown" );
2088         }
2089
2090         rotating = translatingX = translatingY = scalingX = scalingY
2091                                                                 = resizingX = resizingY = false;
2092
2093         queueDraw();
2094     }
2095     else if ( e->button == 3 ) {
2096         rButtonDown = false;
2097     }
2098
2099     return true;
2100 }
2101
2102 /*
2103    void C2DView::GridForWindow( float c[2], int x, int y)
2104    {
2105    SpaceForWindow( c, x, y );
2106    if ( !m_bDoGrid )
2107     return;
2108    c[0] /= m_GridStep[0];
2109    c[1] /= m_GridStep[1];
2110    c[0] = (float)floor( c[0] + 0.5f );
2111    c[1] = (float)floor( c[1] + 0.5f );
2112    c[0] *= m_GridStep[0];
2113    c[1] *= m_GridStep[1];
2114    }
2115    void C2DView::SpaceForWindow( float c[2], int x, int y)
2116    {
2117    c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0];
2118    c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1];
2119    }
2120  */
2121 gboolean motion( ui::Widget win, GdkEventMotion * e, gpointer ){
2122 //      globalOutputStream() << "--> Textool motion...\n";
2123
2124     if ( lButtonDown ) {
2125         if ( translatingX || translatingY ) {
2126             float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2127             float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2128
2129 //Need to fix this to take the rotation angle into account, so that it moves along
2130 //the rotated X/Y axis...
2131             if ( translatingX ) {
2132 //                              tm.coords[0][2] = (trans.x() + ptx) * textureSize.x();
2133 //This works, but only when the angle is zero. !!! FIX !!! [DONE]
2134 //                              tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x());
2135                 tm.coords[0][2] = oldTrans.x() + ( ptx - trans2.x() ) * textureSize.x();
2136 //                              center.x() = oldCenter.x() + (ptx - trans2.x());
2137             }
2138
2139             if ( translatingY ) {
2140 //                              tm.coords[1][2] = (trans.y() + pty) * textureSize.y();
2141 //                              tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y());
2142                 tm.coords[1][2] = oldTrans.y() + ( pty - trans2.y() ) * textureSize.y();
2143 //                              center.y() = oldCenter.y() + (pty - trans2.y());
2144             }
2145
2146 //Need to update center.x/y() so that the widget translates as well. Also, oldCenter
2147 //is badly named... Should be oldTrans or something like that... !!! FIX !!!
2148 //Changing center.x/y() here doesn't seem to change anything... :-/
2149             UpdateControlPoints();
2150         }
2151         else if ( rotating ) {
2152             // Shamus: New rotate code
2153             int cx = (int)( windowSize.x() * ( center.x() - extents.minX ) / extents.width() );
2154             int cy = (int)( windowSize.y() * ( center.y() - extents.minY ) / extents.height() );
2155             Vector3 v1( dragPoint.x() - cx, dragPoint.y() - cy, 0 ), v2( e->x - cx, e->y - cy, 0 );
2156
2157             vector3_normalise( v1 );
2158             vector3_normalise( v2 );
2159             float c = vector3_dot( v1, v2 );
2160             Vector3 cross = vector3_cross( v1, v2 );
2161             float s = vector3_length( cross );
2162
2163             if ( cross[2] > 0 ) {
2164                 s = -s;
2165             }
2166
2167 // Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180...
2168 // Can't derive angle from that!
2169
2170 //rotationAngle = asin(s);// * 180.0f / 3.141592653589f;
2171             rotationAngle = acos( c );
2172 //rotationAngle2 = asin(s);
2173             if ( cross[2] < 0 ) {
2174                 rotationAngle = -rotationAngle;
2175             }
2176
2177 //NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD;
2178 //Let's try this:
2179 //No wok.
2180 /*c = cos(rotationAngle - oldRotationAngle);
2181    s = sin(rotationAngle - oldRotationAngle);
2182    rotationAngle += oldRotationAngle;
2183    //c += cos(oldRotationAngle);
2184    //s += sin(oldRotationAngle);
2185    //rotationAngle += oldRotationAngle;
2186    //c %= 2.0 * PI;
2187    //s %= 2.0 * PI;
2188    //rotationAngle %= 2.0 * PI;//*/
2189
2190 //This is wrong... Hmm...
2191 //It seems to shear the texture instead of rotating it... !!! FIX !!!
2192 // Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-)
2193
2194 // Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!!
2195
2196 // See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from...
2197
2198             tm.coords[0][0] =  c;
2199             tm.coords[0][1] =  s;
2200             tm.coords[1][0] = -s;
2201             tm.coords[1][1] =  c;
2202 //It doesn't work anymore... Dunno why...
2203 //tm.coords[0][2] = -trans.x();                 // This works!!! Yeah!!!
2204 //tm.coords[1][2] = -trans.y();
2205 //nope.
2206 //tm.coords[0][2] = rotationPoint.x();  // This works, but strangely...
2207 //tm.coords[1][2] = rotationPoint.y();
2208 //tm.coords[0][2] = 0;// center.x() / 2.0f;
2209 //tm.coords[1][2] = 0;// center.y() / 2.0f;
2210 //No.
2211 //tm.coords[0][2] = -(center.x() * textureSize.x());
2212 //tm.coords[1][2] = -(center.y() * textureSize.y());
2213 //Eh? No, but seems to be getting closer...
2214 /*float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2215    float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2216    tm.coords[0][2] = -c * center.x() - s * center.y() + ptx;
2217    tm.coords[1][2] =  s * center.x() - c * center.x() + pty;//*/
2218 //Kinda works, but center drifts around on non-square textures...
2219 /*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x();
2220    tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/
2221 //Rotates correctly, but not around the actual center of the face's points...
2222 /*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y();
2223    tm.coords[1][2] =  s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/
2224 //Yes!!!
2225             tm.coords[0][2] = ( -c * center.x() * textureSize.x() - s * center.y() * textureSize.y() ) + center.x() * textureSize.x();
2226             tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y() ) + center.y() * textureSize.y(); //*/
2227 //This doesn't work...
2228 //And this is the wrong place for this anyway (I'm pretty sure).
2229 /*tm.coords[0][2] += oldCenter.x();
2230    tm.coords[1][2] += oldCenter.y();//*/
2231             UpdateControlPoints(); // will cause a redraw
2232         }
2233
2234         return true;
2235     }
2236     else                                    // Check for widget mouseovers
2237     {
2238         Vector2 tran;
2239         float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2240         float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2241         // Translate nx/y to the "center" point...
2242         nx -= center.x();
2243         ny -= center.y();
2244         ny = -ny;   // Flip Y-axis so that increasing numbers move up
2245
2246         tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny;
2247         tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny;
2248 //This doesn't seem to generate a valid distance from the center--for some reason it
2249 //calculates a fixed number every time
2250 //Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE]
2251         float dist = sqrt( ( nx * nx ) + ( ny * ny ) );
2252         // Normalize to the 2.0 = height standard (for now)
2253 //globalOutputStream() << "--> Distance before: " << dist;
2254         dist = dist * 2.0f / extents.height();
2255 //globalOutputStream() << ". After: " << dist;
2256         tran.x() = tran.x() * 2.0f / extents.height();
2257         tran.y() = tran.y() * 2.0f / extents.height();
2258 //globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n";
2259
2260 //Let's try this instead...
2261 //Interesting! It seems that e->x/y are rotated
2262 //(no, they're not--the TM above is what's doing it...)
2263         nx = ( ( e->x / windowSize.y() ) * 2.0f ) - ( windowSize.x() / windowSize.y() );
2264         ny = ( ( e->y / windowSize.y() ) * 2.0f ) - ( windowSize.y() / windowSize.y() );
2265         ny = -ny;
2266 //Cool! It works! Now just need to do rotation...
2267
2268         rotating = translatingX = translatingY = scalingX = scalingY
2269                                                                 = resizingX = resizingY = false;
2270
2271         if ( dist < ( gridRadius * 0.16f ) ) {
2272             translatingX = translatingY = true;
2273         }
2274         else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2275                   && fabs( ny ) < ( gridRadius * 0.05f ) && nx > 0 ) {
2276             translatingX = true;
2277         }
2278         else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2279                   && fabs( nx ) < ( gridRadius * 0.05f ) && ny > 0 ) {
2280             translatingY = true;
2281         }
2282         // Should tighten up the angle on this, or put this test after the axis tests...
2283         else if ( tran.x() > 0 && tran.y() > 0
2284                   && ( dist > ( gridRadius * 0.82f ) && dist < ( gridRadius * 0.98f ) ) ) {
2285             rotating = true;
2286         }
2287
2288         queueDraw();
2289
2290         return true;
2291     }
2292
2293     return false;
2294 }
2295
2296 //It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!!
2297 //This is still wrong... Prolly need to do something with the oldScaleX/Y stuff...
2298 void flipX( ui::ToggleButton, gpointer ){
2299 //      globalOutputStream() << "--> Flip X...\n";
2300     //Shamus:
2301 //      SurfaceInspector_GetSelectedBPTexdef();         // Refresh g_selectedBrushPrimitTexdef...
2302 //      tm.coords[0][0] = -tm.coords[0][0];
2303 //      tm.coords[1][0] = -tm.coords[1][0];
2304 //      tm.coords[0][0] = -tm.coords[0][0];                     // This should be correct now...Nope.
2305 //      tm.coords[1][1] = -tm.coords[1][1];
2306     tm.coords[0][0] = -tm.coords[0][0];         // This should be correct now...
2307     tm.coords[1][0] = -tm.coords[1][0];
2308 //      tm.coords[2][0] = -tm.coords[2][0];//wil wok? no.
2309     UpdateControlPoints();
2310 }
2311
2312 void flipY( ui::ToggleButton, gpointer ){
2313 //      globalOutputStream() << "--> Flip Y...\n";
2314 //      tm.coords[0][1] = -tm.coords[0][1];
2315 //      tm.coords[1][1] = -tm.coords[1][1];
2316 //      tm.coords[0][1] = -tm.coords[0][1];                     // This should be correct now...Nope.
2317 //      tm.coords[1][0] = -tm.coords[1][0];
2318     tm.coords[0][1] = -tm.coords[0][1];         // This should be correct now...
2319     tm.coords[1][1] = -tm.coords[1][1];
2320 //      tm.coords[2][1] = -tm.coords[2][1];//wil wok? no.
2321     UpdateControlPoints();
2322 }
2323
2324 } // end namespace TexTool
2325
2326 #endif