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