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