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