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