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