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