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