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