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