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