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