]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/patchdialog.cpp
Purge GTK forward declarations
[xonotic/netradiant.git] / radiant / patchdialog.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 // Patch Dialog
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 #include "patchdialog.h"
29
30 #include "itexdef.h"
31
32 #include "debugging/debugging.h"
33
34 #include "gtkutil/idledraw.h"
35 #include "gtkutil/entry.h"
36 #include "gtkutil/button.h"
37 #include "gtkutil/nonmodal.h"
38 #include "dialog.h"
39 #include "gtkdlgs.h"
40 #include "mainframe.h"
41 #include "patchmanip.h"
42 #include "patch.h"
43 #include "commands.h"
44 #include "preferences.h"
45 #include "signal/isignal.h"
46
47
48 #include <gdk/gdkkeysyms.h>
49
50 // the increment we are using for the patch inspector (this is saved in the prefs)
51 struct pi_globals_t
52 {
53         float shift[2];
54         float scale[2];
55         float rotate;
56
57         pi_globals_t(){
58                 shift[0] = 8.0f;
59                 shift[1] = 8.0f;
60                 scale[0] = 0.5f;
61                 scale[1] = 0.5f;
62                 rotate = 45.0f;
63         }
64 };
65
66 pi_globals_t g_pi_globals;
67
68 class PatchFixedSubdivisions
69 {
70 public:
71 PatchFixedSubdivisions() : m_enabled( false ), m_x( 0 ), m_y( 0 ){
72 }
73 PatchFixedSubdivisions( bool enabled, std::size_t x, std::size_t y ) : m_enabled( enabled ), m_x( x ), m_y( y ){
74 }
75 bool m_enabled;
76 std::size_t m_x;
77 std::size_t m_y;
78 };
79
80 void Patch_getFixedSubdivisions( const Patch& patch, PatchFixedSubdivisions& subdivisions ){
81         subdivisions.m_enabled = patch.m_patchDef3;
82         subdivisions.m_x = patch.m_subdivisions_x;
83         subdivisions.m_y = patch.m_subdivisions_y;
84 }
85
86 const std::size_t MAX_PATCH_SUBDIVISIONS = 32;
87
88 void Patch_setFixedSubdivisions( Patch& patch, const PatchFixedSubdivisions& subdivisions ){
89         patch.undoSave();
90
91         patch.m_patchDef3 = subdivisions.m_enabled;
92         patch.m_subdivisions_x = subdivisions.m_x;
93         patch.m_subdivisions_y = subdivisions.m_y;
94
95         if ( patch.m_subdivisions_x == 0 ) {
96                 patch.m_subdivisions_x = 4;
97         }
98         else if ( patch.m_subdivisions_x > MAX_PATCH_SUBDIVISIONS ) {
99                 patch.m_subdivisions_x = MAX_PATCH_SUBDIVISIONS;
100         }
101         if ( patch.m_subdivisions_y == 0 ) {
102                 patch.m_subdivisions_y = 4;
103         }
104         else if ( patch.m_subdivisions_y > MAX_PATCH_SUBDIVISIONS ) {
105                 patch.m_subdivisions_y = MAX_PATCH_SUBDIVISIONS;
106         }
107
108         SceneChangeNotify();
109         Patch_textureChanged();
110         patch.controlPointsChanged();
111 }
112
113 class PatchGetFixedSubdivisions
114 {
115 PatchFixedSubdivisions& m_subdivisions;
116 public:
117 PatchGetFixedSubdivisions( PatchFixedSubdivisions& subdivisions ) : m_subdivisions( subdivisions ){
118 }
119 void operator()( Patch& patch ){
120         Patch_getFixedSubdivisions( patch, m_subdivisions );
121         SceneChangeNotify();
122 }
123 };
124
125 void Scene_PatchGetFixedSubdivisions( PatchFixedSubdivisions& subdivisions ){
126 #if 1
127         if ( GlobalSelectionSystem().countSelected() != 0 ) {
128                 Patch* patch = Node_getPatch( GlobalSelectionSystem().ultimateSelected().path().top() );
129                 if ( patch != 0 ) {
130                         Patch_getFixedSubdivisions( *patch, subdivisions );
131                 }
132         }
133 #else
134         Scene_forEachVisibleSelectedPatch( PatchGetFixedSubdivisions( subdivisions ) );
135 #endif
136 }
137
138 class PatchSetFixedSubdivisions
139 {
140 const PatchFixedSubdivisions& m_subdivisions;
141 public:
142 PatchSetFixedSubdivisions( const PatchFixedSubdivisions& subdivisions ) : m_subdivisions( subdivisions ){
143 }
144 void operator()( Patch& patch ) const {
145         Patch_setFixedSubdivisions( patch, m_subdivisions );
146 }
147 };
148
149 void Scene_PatchSetFixedSubdivisions( const PatchFixedSubdivisions& subdivisions ){
150         UndoableCommand command( "patchSetFixedSubdivisions" );
151         Scene_forEachVisibleSelectedPatch( PatchSetFixedSubdivisions( subdivisions ) );
152 }
153
154
155 class Subdivisions
156 {
157 public:
158 ui::CheckButton m_enabled;
159 ui::Entry m_horizontal;
160 ui::Entry m_vertical;
161 Subdivisions() : m_enabled( (GtkCheckButton *) 0 ), m_horizontal( nullptr ), m_vertical( nullptr ){
162 }
163 void update(){
164         PatchFixedSubdivisions subdivisions;
165         Scene_PatchGetFixedSubdivisions( subdivisions );
166
167         toggle_button_set_active_no_signal( m_enabled, subdivisions.m_enabled );
168
169         if ( subdivisions.m_enabled ) {
170                 entry_set_int( m_horizontal, static_cast<int>( subdivisions.m_x ) );
171                 entry_set_int( m_vertical, static_cast<int>( subdivisions.m_y ) );
172                 gtk_widget_set_sensitive( GTK_WIDGET( m_horizontal ), TRUE );
173                 gtk_widget_set_sensitive( GTK_WIDGET( m_vertical ), TRUE );
174         }
175         else
176         {
177                 gtk_entry_set_text( m_horizontal, "" );
178                 gtk_entry_set_text( m_vertical, "" );
179                 gtk_widget_set_sensitive( GTK_WIDGET( m_horizontal ), FALSE );
180                 gtk_widget_set_sensitive( GTK_WIDGET( m_vertical ), FALSE );
181         }
182 }
183 void cancel(){
184         update();
185 }
186 typedef MemberCaller<Subdivisions, &Subdivisions::cancel> CancelCaller;
187 void apply(){
188         Scene_PatchSetFixedSubdivisions(
189                 PatchFixedSubdivisions(
190                         gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( m_enabled ) ) != FALSE,
191                         static_cast<std::size_t>( entry_get_int( m_horizontal ) ),
192                         static_cast<std::size_t>( entry_get_int( m_vertical ) )
193                         )
194                 );
195 }
196 typedef MemberCaller<Subdivisions, &Subdivisions::apply> ApplyCaller;
197 static void applyGtk( GtkToggleButton* toggle, Subdivisions* self ){
198         self->apply();
199 }
200 };
201
202 class PatchInspector : public Dialog
203 {
204 ui::Window BuildDialog();
205 Subdivisions m_subdivisions;
206 NonModalEntry m_horizontalSubdivisionsEntry;
207 NonModalEntry m_verticalSubdivisionsEntry;
208 public:
209 IdleDraw m_idleDraw;
210 WindowPositionTracker m_position_tracker;
211
212 Patch *m_Patch;
213
214 CopiedString m_strName;
215 float m_fS;
216 float m_fT;
217 float m_fX;
218 float m_fY;
219 float m_fZ;
220 /*  float       m_fHScale;
221    float        m_fHShift;
222    float        m_fRotate;
223    float        m_fVScale;
224    float        m_fVShift; */
225 int m_nCol;
226 int m_nRow;
227 ui::ComboBoxText m_pRowCombo{nullptr};
228 ui::ComboBoxText m_pColCombo{nullptr};
229 std::size_t m_countRows;
230 std::size_t m_countCols;
231
232 // turn on/off processing of the "changed" "value_changed" messages
233 // (need to turn off when we are feeding data in)
234 // NOTE: much more simple than blocking signals
235 bool m_bListenChanged;
236
237 PatchInspector() :
238         m_horizontalSubdivisionsEntry( Subdivisions::ApplyCaller( m_subdivisions ), Subdivisions::CancelCaller( m_subdivisions ) ),
239         m_verticalSubdivisionsEntry( Subdivisions::ApplyCaller( m_subdivisions ), Subdivisions::CancelCaller( m_subdivisions ) ),
240         m_idleDraw( MemberCaller<PatchInspector, &PatchInspector::GetPatchInfo>( *this ) ){
241         m_fS = 0.0f;
242         m_fT = 0.0f;
243         m_fX = 0.0f;
244         m_fY = 0.0f;
245         m_fZ = 0.0f;
246         m_nCol = 0;
247         m_nRow = 0;
248         m_countRows = 0;
249         m_countCols = 0;
250         m_Patch = 0;
251         m_bListenChanged = true;
252
253         m_position_tracker.setPosition( c_default_window_pos );
254 }
255
256 bool visible(){
257         return gtk_widget_get_visible( GetWidget() );
258 }
259
260 //  void UpdateInfo();
261 //  void SetPatchInfo();
262 void GetPatchInfo();
263 void UpdateSpinners( bool bUp, int nID );
264 // read the current patch on map and initialize m_fX m_fY accordingly
265 void UpdateRowColInfo();
266 // sync the dialog our internal data structures
267 // depending on the flag it will read or write
268 // we use m_nCol m_nRow m_fX m_fY m_fZ m_fS m_fT m_strName
269 // (NOTE: this doesn't actually commit stuff to the map or read from it)
270 void importData();
271 void exportData();
272 };
273
274 PatchInspector g_PatchInspector;
275
276 void PatchInspector_constructWindow( ui::Window main_window ){
277         g_PatchInspector.m_parent = main_window;
278         g_PatchInspector.Create();
279 }
280 void PatchInspector_destroyWindow(){
281         g_PatchInspector.Destroy();
282 }
283
284 void PatchInspector_queueDraw(){
285         if ( g_PatchInspector.visible() ) {
286                 g_PatchInspector.m_idleDraw.queueDraw();
287         }
288 }
289
290 void DoPatchInspector(){
291         g_PatchInspector.GetPatchInfo();
292         if ( !g_PatchInspector.visible() ) {
293                 g_PatchInspector.ShowDlg();
294         }
295 }
296
297 void PatchInspector_toggleShown(){
298         if ( g_PatchInspector.visible() ) {
299                 g_PatchInspector.m_Patch = 0;
300                 g_PatchInspector.HideDlg();
301         }
302         else{
303                 DoPatchInspector();
304         }
305 }
306
307
308 // =============================================================================
309 // static functions
310
311 // memorize the current state (that is don't try to undo our do before changing something else)
312 static void OnApply( ui::Widget widget, gpointer data ){
313         g_PatchInspector.exportData();
314         if ( g_PatchInspector.m_Patch != 0 ) {
315                 UndoableCommand command( "patchSetTexture" );
316                 g_PatchInspector.m_Patch->undoSave();
317
318                 if ( !texdef_name_valid( g_PatchInspector.m_strName.c_str() ) ) {
319                         globalErrorStream() << "invalid texture name '" << g_PatchInspector.m_strName.c_str() << "'\n";
320                         g_PatchInspector.m_strName = texdef_name_default();
321                 }
322                 g_PatchInspector.m_Patch->SetShader( g_PatchInspector.m_strName.c_str() );
323
324                 std::size_t r = g_PatchInspector.m_nRow;
325                 std::size_t c = g_PatchInspector.m_nCol;
326                 if ( r < g_PatchInspector.m_Patch->getHeight()
327                          && c < g_PatchInspector.m_Patch->getWidth() ) {
328                         PatchControl& p = g_PatchInspector.m_Patch->ctrlAt( r,c );
329                         p.m_vertex[0] = g_PatchInspector.m_fX;
330                         p.m_vertex[1] = g_PatchInspector.m_fY;
331                         p.m_vertex[2] = g_PatchInspector.m_fZ;
332                         p.m_texcoord[0] = g_PatchInspector.m_fS;
333                         p.m_texcoord[1] = g_PatchInspector.m_fT;
334                         g_PatchInspector.m_Patch->controlPointsChanged();
335                 }
336         }
337 }
338
339 static void OnSelchangeComboColRow( ui::Widget widget, gpointer data ){
340         if ( !g_PatchInspector.m_bListenChanged ) {
341                 return;
342         }
343         // retrieve the current m_nRow and m_nCol, other params are not relevant
344         g_PatchInspector.exportData();
345         // read the changed values ourselves
346         g_PatchInspector.UpdateRowColInfo();
347         // now reflect our changes
348         g_PatchInspector.importData();
349 }
350
351 class PatchSetTextureRepeat
352 {
353 float m_s, m_t;
354 public:
355 PatchSetTextureRepeat( float s, float t ) : m_s( s ), m_t( t ){
356 }
357 void operator()( Patch& patch ) const {
358         patch.SetTextureRepeat( m_s, m_t );
359 }
360 };
361
362 void Scene_PatchTileTexture_Selected( scene::Graph& graph, float s, float t ){
363         Scene_forEachVisibleSelectedPatch( PatchSetTextureRepeat( s, t ) );
364         SceneChangeNotify();
365 }
366
367 static void OnBtnPatchdetails( ui::Widget widget, gpointer data ){
368         Patch_CapTexture();
369 }
370
371 static void OnBtnPatchfit( ui::Widget widget, gpointer data ){
372         Patch_FitTexture();
373 }
374
375 static void OnBtnPatchnatural( ui::Widget widget, gpointer data ){
376         Patch_NaturalTexture();
377 }
378
379 static void OnBtnPatchreset( ui::Widget widget, gpointer data ){
380         Patch_ResetTexture();
381 }
382
383 static void OnBtnPatchFlipX( ui::Widget widget, gpointer data ){
384         Patch_FlipTextureX();
385 }
386
387 static void OnBtnPatchFlipY( ui::Widget widget, gpointer data ){
388         Patch_FlipTextureY();
389 }
390
391 struct PatchRotateTexture
392 {
393         float m_angle;
394 public:
395         PatchRotateTexture( float angle ) : m_angle( angle ){
396         }
397         void operator()( Patch& patch ) const {
398                 patch.RotateTexture( m_angle );
399         }
400 };
401
402 void Scene_PatchRotateTexture_Selected( scene::Graph& graph, float angle ){
403         Scene_forEachVisibleSelectedPatch( PatchRotateTexture( angle ) );
404 }
405
406 class PatchScaleTexture
407 {
408 float m_s, m_t;
409 public:
410 PatchScaleTexture( float s, float t ) : m_s( s ), m_t( t ){
411 }
412 void operator()( Patch& patch ) const {
413         patch.ScaleTexture( m_s, m_t );
414 }
415 };
416
417 float Patch_convertScale( float scale ){
418         if ( scale > 0 ) {
419                 return scale;
420         }
421         if ( scale < 0 ) {
422                 return -1 / scale;
423         }
424         return 1;
425 }
426
427 void Scene_PatchScaleTexture_Selected( scene::Graph& graph, float s, float t ){
428         Scene_forEachVisibleSelectedPatch( PatchScaleTexture( Patch_convertScale( s ), Patch_convertScale( t ) ) );
429 }
430
431 class PatchTranslateTexture
432 {
433 float m_s, m_t;
434 public:
435 PatchTranslateTexture( float s, float t )
436         : m_s( s ), m_t( t ){
437 }
438 void operator()( Patch& patch ) const {
439         patch.TranslateTexture( m_s, m_t );
440 }
441 };
442
443 void Scene_PatchTranslateTexture_Selected( scene::Graph& graph, float s, float t ){
444         Scene_forEachVisibleSelectedPatch( PatchTranslateTexture( s, t ) );
445 }
446
447 static void OnBtnPatchAutoCap( ui::Widget widget, gpointer data ){
448         Patch_AutoCapTexture();
449 }
450
451 static void OnSpinChanged( GtkAdjustment *adj, gpointer data ){
452         texdef_t td;
453
454         td.rotate = 0;
455         td.scale[0] = td.scale[1] = 0;
456         td.shift[0] = td.shift[1] = 0;
457
458         if ( gtk_adjustment_get_value(adj) == 0 ) {
459                 return;
460         }
461
462         if ( adj == g_object_get_data( G_OBJECT( g_PatchInspector.GetWidget() ), "hshift_adj" ) ) {
463                 g_pi_globals.shift[0] = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( data ) ) ) );
464
465                 if ( gtk_adjustment_get_value(adj) > 0 ) {
466                         td.shift[0] = g_pi_globals.shift[0];
467                 }
468                 else{
469                         td.shift[0] = -g_pi_globals.shift[0];
470                 }
471         }
472         else if ( adj == g_object_get_data( G_OBJECT( g_PatchInspector.GetWidget() ), "vshift_adj" ) ) {
473                 g_pi_globals.shift[1] = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( data ) ) ) );
474
475                 if ( gtk_adjustment_get_value(adj) > 0 ) {
476                         td.shift[1] = g_pi_globals.shift[1];
477                 }
478                 else{
479                         td.shift[1] = -g_pi_globals.shift[1];
480                 }
481         }
482         else if ( adj == g_object_get_data( G_OBJECT( g_PatchInspector.GetWidget() ), "hscale_adj" ) ) {
483                 g_pi_globals.scale[0] = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( data ) ) ) );
484                 if ( g_pi_globals.scale[0] == 0.0f ) {
485                         return;
486                 }
487                 if ( gtk_adjustment_get_value(adj) > 0 ) {
488                         td.scale[0] = g_pi_globals.scale[0];
489                 }
490                 else{
491                         td.scale[0] = -g_pi_globals.scale[0];
492                 }
493         }
494         else if ( adj == g_object_get_data( G_OBJECT( g_PatchInspector.GetWidget() ), "vscale_adj" ) ) {
495                 g_pi_globals.scale[1] = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( data ) ) ) );
496                 if ( g_pi_globals.scale[1] == 0.0f ) {
497                         return;
498                 }
499                 if ( gtk_adjustment_get_value(adj) > 0 ) {
500                         td.scale[1] = g_pi_globals.scale[1];
501                 }
502                 else{
503                         td.scale[1] = -g_pi_globals.scale[1];
504                 }
505         }
506         else if ( adj == g_object_get_data( G_OBJECT( g_PatchInspector.GetWidget() ), "rotate_adj" ) ) {
507                 g_pi_globals.rotate = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( data ) ) ) );
508
509                 if ( gtk_adjustment_get_value(adj) > 0 ) {
510                         td.rotate = g_pi_globals.rotate;
511                 }
512                 else{
513                         td.rotate = -g_pi_globals.rotate;
514                 }
515         }
516
517         gtk_adjustment_set_value(adj, 0);
518
519         // will scale shift rotate the patch accordingly
520
521
522         if ( td.shift[0] || td.shift[1] ) {
523                 UndoableCommand command( "patchTranslateTexture" );
524                 Scene_PatchTranslateTexture_Selected( GlobalSceneGraph(), td.shift[0], td.shift[1] );
525         }
526         else if ( td.scale[0] || td.scale[1] ) {
527                 UndoableCommand command( "patchScaleTexture" );
528                 Scene_PatchScaleTexture_Selected( GlobalSceneGraph(), td.scale[0], td.scale[1] );
529         }
530         else if ( td.rotate ) {
531                 UndoableCommand command( "patchRotateTexture" );
532                 Scene_PatchRotateTexture_Selected( GlobalSceneGraph(), td.rotate );
533         }
534
535         // update the point-by-point view
536         OnSelchangeComboColRow( ui::root ,0 );
537 }
538
539 static gint OnDialogKey( ui::Widget widget, GdkEventKey* event, gpointer data ){
540         if ( event->keyval == GDK_Return ) {
541                 OnApply( ui::root, 0 );
542                 return TRUE;
543         }
544         else if ( event->keyval == GDK_Escape ) {
545                 g_PatchInspector.GetPatchInfo();
546                 return TRUE;
547         }
548         return FALSE;
549 }
550
551 // =============================================================================
552 // PatchInspector class
553
554 ui::Window PatchInspector::BuildDialog(){
555         ui::Window window = ui::Window(create_floating_window( "Patch Properties", m_parent ));
556
557         m_position_tracker.connect( window );
558
559         global_accel_connect_window( window );
560
561         window_connect_focus_in_clear_focus_widget( window );
562
563
564         {
565                 auto vbox = ui::VBox( FALSE, 5 );
566                 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
567                 vbox.show();
568                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
569                 {
570                         auto hbox = ui::HBox( FALSE, 5 );
571                         hbox.show();
572                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
573                         {
574                                 auto vbox2 = ui::VBox( FALSE, 0 );
575                                 gtk_container_set_border_width( GTK_CONTAINER( vbox2 ), 0 );
576                                 vbox2.show();
577                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), TRUE, TRUE, 0 );
578                                 {
579                                         auto frame = ui::Frame( "Details" );
580                                         frame.show();
581                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
582                                         {
583                                                 auto vbox3 = ui::VBox( FALSE, 5 );
584                                                 gtk_container_set_border_width( GTK_CONTAINER( vbox3 ), 5 );
585                                                 vbox3.show();
586                                                 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox3 ) );
587                                                 {
588                                                         auto table = ui::Table( 2, 2, FALSE );
589                                                         table.show();
590                                                         gtk_box_pack_start( GTK_BOX( vbox3 ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
591                                                         gtk_table_set_row_spacings( table, 5 );
592                                                         gtk_table_set_col_spacings( table, 5 );
593                                                         {
594                                                                 auto label = ui::Label( "Row:" );
595                                                                 label.show();
596                                                                 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
597                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
598                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
599                                                         }
600                                                         {
601                                                                 auto label = ui::Label( "Column:" );
602                                                                 label.show();
603                                                                 gtk_table_attach( table, GTK_WIDGET( label ), 1, 2, 0, 1,
604                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
605                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
606                                                         }
607                                                         {
608                                                                 auto combo = ui::ComboBoxText();
609                                                                 g_signal_connect( G_OBJECT( combo ), "changed", G_CALLBACK( OnSelchangeComboColRow ), this );
610                                                                 AddDialogData( *GTK_COMBO_BOX(combo), m_nRow );
611
612                                                                 combo.show();
613                                                                 gtk_table_attach( table, GTK_WIDGET( combo ), 0, 1, 1, 2,
614                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
615                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
616                                                                 gtk_widget_set_size_request( GTK_WIDGET( combo ), 60, -1 );
617                                                                 m_pRowCombo = combo;
618                                                         }
619
620                                                         {
621                                                                 auto combo = ui::ComboBoxText();
622                                                                 g_signal_connect( G_OBJECT( combo ), "changed", G_CALLBACK( OnSelchangeComboColRow ), this );
623                                                                 AddDialogData( *GTK_COMBO_BOX(combo), m_nCol );
624
625                                                                 combo.show();
626                                                                 gtk_table_attach( table, GTK_WIDGET( combo ), 1, 2, 1, 2,
627                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
628                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
629                                                                 gtk_widget_set_size_request( GTK_WIDGET( combo ), 60, -1 );
630                                                                 m_pColCombo = combo;
631                                                         }
632                                                 }
633                                                 auto table = ui::Table( 5, 2, FALSE );
634                                                 table.show();
635                                                 gtk_box_pack_start( GTK_BOX( vbox3 ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
636                                                 gtk_table_set_row_spacings( table, 5 );
637                                                 gtk_table_set_col_spacings( table, 5 );
638                                                 {
639                                                         auto label = ui::Label( "X:" );
640                                                         label.show();
641                                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
642                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
643                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
644                                                 }
645                                                 {
646                                                         auto label = ui::Label( "Y:" );
647                                                         label.show();
648                                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
649                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
650                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
651                                                 }
652                                                 {
653                                                         auto label = ui::Label( "Z:" );
654                                                         label.show();
655                                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
656                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
657                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
658                                                 }
659                                                 {
660                                                         auto label = ui::Label( "S:" );
661                                                         label.show();
662                                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 3, 4,
663                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
664                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
665                                                 }
666                                                 {
667                                                         auto label = ui::Label( "T:" );
668                                                         label.show();
669                                                         gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 4, 5,
670                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
671                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
672                                                 }
673                                                 {
674                                                         auto entry = ui::Entry();
675                                                         entry.show();
676                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
677                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
678                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
679                                                         AddDialogData( *GTK_ENTRY(entry), m_fX );
680
681                                                         g_signal_connect( G_OBJECT( entry ), "key_press_event", G_CALLBACK( OnDialogKey ), 0 );
682                                                 }
683                                                 {
684                                                         auto entry = ui::Entry();
685                                                         entry.show();
686                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
687                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
688                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
689                                                         AddDialogData( *GTK_ENTRY(entry), m_fY );
690
691                                                         g_signal_connect( G_OBJECT( entry ), "key_press_event", G_CALLBACK( OnDialogKey ), 0 );
692                                                 }
693                                                 {
694                                                         auto entry = ui::Entry();
695                                                         entry.show();
696                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 2, 3,
697                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
698                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
699                                                         AddDialogData( *GTK_ENTRY(entry), m_fZ );
700
701                                                         g_signal_connect( G_OBJECT( entry ), "key_press_event", G_CALLBACK( OnDialogKey ), 0 );
702                                                 }
703                                                 {
704                                                         auto entry = ui::Entry();
705                                                         entry.show();
706                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 3, 4,
707                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
708                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
709                                                         AddDialogData( *GTK_ENTRY(entry), m_fS );
710
711                                                         g_signal_connect( G_OBJECT( entry ), "key_press_event", G_CALLBACK( OnDialogKey ), 0 );
712                                                 }
713                                                 {
714                                                         auto entry = ui::Entry();
715                                                         entry.show();
716                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 4, 5,
717                                                                                           (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
718                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
719                                                         AddDialogData( *GTK_ENTRY(entry), m_fT );
720
721                                                         g_signal_connect( G_OBJECT( entry ), "key_press_event", G_CALLBACK( OnDialogKey ), 0 );
722                                                 }
723                                         }
724                                 }
725                                 if ( g_pGameDescription->mGameType == "doom3" ) {
726                                         auto frame = ui::Frame( "Tesselation" );
727                                         frame.show();
728                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
729                                         {
730                                                 auto vbox3 = ui::VBox( FALSE, 5 );
731                                                 gtk_container_set_border_width( GTK_CONTAINER( vbox3 ), 5 );
732                                                 vbox3.show();
733                                                 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox3 ) );
734                                                 {
735                                                         auto table = ui::Table( 3, 2, FALSE );
736                                                         table.show();
737                                                         gtk_box_pack_start( GTK_BOX( vbox3 ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
738                                                         gtk_table_set_row_spacings( table, 5 );
739                                                         gtk_table_set_col_spacings( table, 5 );
740                                                         {
741                                                                 auto label = ui::Label( "Fixed" );
742                                                                 label.show();
743                                                                 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
744                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
745                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
746                                                         }
747                                                         {
748                                                                 auto check = ui::CheckButton(GTK_CHECK_BUTTON( gtk_check_button_new() ));
749                                                                 check.show();
750                                                                 gtk_table_attach( table, GTK_WIDGET( check ), 1, 2, 0, 1,
751                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
752                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
753                                                                 m_subdivisions.m_enabled = check;
754                                                                 guint handler_id = g_signal_connect( G_OBJECT( check ), "toggled", G_CALLBACK( &Subdivisions::applyGtk ), &m_subdivisions );
755                                                                 g_object_set_data( G_OBJECT( check ), "handler", gint_to_pointer( handler_id ) );
756                                                         }
757                                                         {
758                                                                 auto label = ui::Label( "Horizontal" );
759                                                                 label.show();
760                                                                 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 1, 2,
761                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
762                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
763                                                         }
764                                                         {
765                                                                 auto entry = ui::Entry();
766                                                                 entry.show();
767                                                                 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
768                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
769                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
770                                                                 m_subdivisions.m_horizontal = entry;
771                                                                 m_horizontalSubdivisionsEntry.connect( entry );
772                                                         }
773                                                         {
774                                                                 auto label = ui::Label( "Vertical" );
775                                                                 label.show();
776                                                                 gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 2, 3,
777                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
778                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
779                                                         }
780                                                         {
781                                                                 auto entry = ui::Entry();
782                                                                 entry.show();
783                                                                 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 2, 3,
784                                                                                                   (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
785                                                                                                   (GtkAttachOptions)( 0 ), 0, 0 );
786                                                                 m_subdivisions.m_vertical = entry;
787                                                                 m_verticalSubdivisionsEntry.connect( entry );
788                                                         }
789                                                 }
790                                         }
791                                 }
792                         }
793                         {
794                                 auto frame = ui::Frame( "Texturing" );
795                                 frame.show();
796                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 );
797                                 {
798                                         auto vbox2 = ui::VBox( FALSE, 5 );
799                                         vbox2.show();
800                                         gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
801                                         gtk_container_set_border_width( GTK_CONTAINER( vbox2 ), 5 );
802                                         {
803                                                 auto label = ui::Label( "Name:" );
804                                                 label.show();
805                                                 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( label ), TRUE, TRUE, 0 );
806                                                 gtk_label_set_justify( label, GTK_JUSTIFY_LEFT );
807                                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
808                                         }
809                                         {
810                                                 auto entry = ui::Entry();
811                                                 //  gtk_editable_set_editable (GTK_ENTRY (entry), false);
812                                                 entry.show();
813                                                 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( entry ), TRUE, TRUE, 0 );
814                                                 AddDialogData( *GTK_ENTRY(entry), m_strName );
815
816                                                 g_signal_connect( G_OBJECT( entry ), "key_press_event", G_CALLBACK( OnDialogKey ), 0 );
817                                         }
818                                         {
819                                                 auto table = ui::Table( 5, 4, FALSE );
820                                                 table.show();
821                                                 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
822                                                 gtk_table_set_row_spacings( table, 5 );
823                                                 gtk_table_set_col_spacings( table, 5 );
824                                                 {
825                                                         auto label = ui::Label( "Horizontal Shift Step" );
826                                                         label.show();
827                                                         gtk_table_attach( table, GTK_WIDGET( label ), 2, 4, 0, 1,
828                                                                                           (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
829                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
830                                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
831                                                 }
832                                                 {
833                                                         auto label = ui::Label( "Vertical Shift Step" );
834                                                         label.show();
835                                                         gtk_table_attach( table, GTK_WIDGET( label ), 2, 4, 1, 2,
836                                                                                           (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
837                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
838                                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
839                                                 }
840                                                 {
841                                                         auto label = ui::Label( "Horizontal Stretch Step" );
842                                                         label.show();
843                                                         gtk_table_attach( table, GTK_WIDGET( label ), 2, 3, 2, 3,
844                                                                                           (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
845                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
846                                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
847                                                 }
848                                                 {
849                                                         auto button = ui::Button( "Flip" );
850                                                         button.show();
851                                                         gtk_table_attach( table, GTK_WIDGET( button ), 3, 4, 2, 3,
852                                                                                           (GtkAttachOptions)( GTK_FILL ),
853                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
854                                                         g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchFlipX ), 0 );
855                                                         gtk_widget_set_size_request( GTK_WIDGET( button ), 60, -1 );
856                                                 }
857                                                 {
858                                                         auto label = ui::Label( "Vertical Stretch Step" );
859                                                         label.show();
860                                                         gtk_table_attach( table, GTK_WIDGET( label ), 2, 3, 3, 4,
861                                                                                           (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
862                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
863                                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
864                                                 }
865                                                 {
866                                                         auto button = ui::Button( "Flip" );
867                                                         button.show();
868                                                         gtk_table_attach( table, GTK_WIDGET( button ), 3, 4, 3, 4,
869                                                                                           (GtkAttachOptions)( GTK_FILL ),
870                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
871                                                         g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchFlipY ), 0 );
872                                                         gtk_widget_set_size_request( GTK_WIDGET( button ), 60, -1 );
873                                                 }
874                                                 {
875                                                         auto label = ui::Label( "Rotate Step" );
876                                                         label.show();
877                                                         gtk_table_attach( table, GTK_WIDGET( label ), 2, 4, 4, 5,
878                                                                                           (GtkAttachOptions)( GTK_FILL|GTK_EXPAND ),
879                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
880                                                         gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
881                                                 }
882                                                 {
883                                                         auto entry = ui::Entry();
884                                                         entry.show();
885                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 0, 1, 0, 1,
886                                                                                           (GtkAttachOptions)( GTK_FILL ),
887                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
888                                                         gtk_widget_set_size_request( GTK_WIDGET( entry ), 50, -1 );
889                                                         g_object_set_data( G_OBJECT( window ), "hshift_entry", entry );
890                                                         // we fill in this data, if no patch is selected the widgets are unmodified when the inspector is raised
891                                                         // so we need to have at least one initialisation somewhere
892                                                         entry_set_float( entry, g_pi_globals.shift[0] );
893
894                                                         auto adj = ui::Adjustment( 0, -8192, 8192, 1, 1, 0 );
895                                                         g_signal_connect( G_OBJECT( adj ), "value_changed", G_CALLBACK( OnSpinChanged ), (gpointer) entry );
896                                                         g_object_set_data( G_OBJECT( window ), "hshift_adj", (gpointer) adj );
897
898                                                         auto spin = ui::SpinButton( adj, 1, 0 );
899                                                         spin.show();
900                                                         gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 0, 1,
901                                                                                           (GtkAttachOptions)( 0 ),
902                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
903                                                         gtk_widget_set_size_request( GTK_WIDGET( spin ), 10, -1 );
904                                                         gtk_widget_set_can_focus( spin, false );
905                                                 }
906                                                 {
907                                                         auto entry = ui::Entry();
908                                                         entry.show();
909                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 0, 1, 1, 2,
910                                                                                           (GtkAttachOptions)( GTK_FILL ),
911                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
912                                                         gtk_widget_set_size_request( GTK_WIDGET( entry ), 50, -1 );
913                                                         entry_set_float( entry, g_pi_globals.shift[1] );
914
915                                                         auto adj = ui::Adjustment( 0, -8192, 8192, 1, 1, 0 );
916                                                         g_signal_connect( G_OBJECT( adj ), "value_changed", G_CALLBACK( OnSpinChanged ), entry );
917                                                         g_object_set_data( G_OBJECT( window ), "vshift_adj", (gpointer) adj );
918
919                                                         auto spin = ui::SpinButton( adj, 1, 0 );
920                                                         spin.show();
921                                                         gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 1, 2,
922                                                                                           (GtkAttachOptions)( 0 ),
923                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
924                                                         gtk_widget_set_size_request( GTK_WIDGET( spin ), 10, -1 );
925                                                         gtk_widget_set_can_focus( spin, false );
926                                                 }
927                                                 {
928                                                         auto entry = ui::Entry();
929                                                         entry.show();
930                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 0, 1, 2, 3,
931                                                                                           (GtkAttachOptions)( GTK_FILL ),
932                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
933                                                         gtk_widget_set_size_request( GTK_WIDGET( entry ), 50, -1 );
934                                                         entry_set_float( entry, g_pi_globals.scale[0] );
935
936                                                         auto adj = ui::Adjustment( 0, -1000, 1000, 1, 1, 0 );
937                                                         g_signal_connect( G_OBJECT( adj ), "value_changed", G_CALLBACK( OnSpinChanged ), entry );
938                                                         g_object_set_data( G_OBJECT( window ), "hscale_adj", (gpointer) adj );
939
940                                                         auto spin = ui::SpinButton( adj, 1, 0 );
941                                                         spin.show();
942                                                         gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 2, 3,
943                                                                                           (GtkAttachOptions)( 0 ),
944                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
945                                                         gtk_widget_set_size_request( GTK_WIDGET( spin ), 10, -1 );
946                                                         gtk_widget_set_can_focus( spin, false );
947                                                 }
948                                                 {
949                                                         auto entry = ui::Entry();
950                                                         entry.show();
951                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 0, 1, 3, 4,
952                                                                                           (GtkAttachOptions)( GTK_FILL ),
953                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
954                                                         gtk_widget_set_size_request( GTK_WIDGET( entry ), 50, -1 );
955                                                         entry_set_float( entry, g_pi_globals.scale[1] );
956
957                                                         auto adj = ui::Adjustment( 0, -1000, 1000, 1, 1, 0 );
958                                                         g_signal_connect( G_OBJECT( adj ), "value_changed", G_CALLBACK( OnSpinChanged ), entry );
959                                                         g_object_set_data( G_OBJECT( window ), "vscale_adj", (gpointer) adj );
960
961                                                         auto spin = ui::SpinButton( adj, 1, 0 );
962                                                         spin.show();
963                                                         gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 3, 4,
964                                                                                           (GtkAttachOptions)( 0 ),
965                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
966                                                         gtk_widget_set_size_request( GTK_WIDGET( spin ), 10, -1 );
967                                                         gtk_widget_set_can_focus( spin, false );
968                                                 }
969                                                 {
970                                                         auto entry = ui::Entry();
971                                                         entry.show();
972                                                         gtk_table_attach( table, GTK_WIDGET( entry ), 0, 1, 4, 5,
973                                                                                           (GtkAttachOptions)( GTK_FILL ),
974                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
975                                                         gtk_widget_set_size_request( GTK_WIDGET( entry ), 50, -1 );
976                                                         entry_set_float( entry, g_pi_globals.rotate );
977
978                                                         auto adj = ui::Adjustment( 0, -1000, 1000, 1, 1, 0 ); // NOTE: Arnout - this really should be 360 but can't change it anymore as it could break existing maps
979                                                         g_signal_connect( G_OBJECT( adj ), "value_changed", G_CALLBACK( OnSpinChanged ), entry );
980                                                         g_object_set_data( G_OBJECT( window ), "rotate_adj", (gpointer) adj );
981
982                                                         auto spin = ui::SpinButton( adj, 1, 0 );
983                                                         spin.show();
984                                                         gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 4, 5,
985                                                                                           (GtkAttachOptions)( 0 ),
986                                                                                           (GtkAttachOptions)( 0 ), 0, 0 );
987                                                         gtk_widget_set_size_request( GTK_WIDGET( spin ), 10, -1 );
988                                                         gtk_widget_set_can_focus( spin, false );
989                                                 }
990                                         }
991                                         auto hbox2 = ui::HBox( TRUE, 5 );
992                                         hbox2.show();
993                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( hbox2 ), TRUE, FALSE, 0 );
994                                         {
995                                                 auto button = ui::Button( "Auto Cap" );
996                                                 button.show();
997                                                 gtk_box_pack_end( GTK_BOX( hbox2 ), GTK_WIDGET( button ), TRUE, FALSE, 0 );
998                                                 g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchAutoCap ), 0 );
999                                                 gtk_widget_set_size_request( GTK_WIDGET( button ), 60, -1 );
1000                                         }
1001                                         {
1002                                                 auto button = ui::Button( "CAP" );
1003                                                 button.show();
1004                                                 gtk_box_pack_end( GTK_BOX( hbox2 ), GTK_WIDGET( button ), TRUE, FALSE, 0 );
1005                                                 g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchdetails ), 0 );
1006                                                 gtk_widget_set_size_request( GTK_WIDGET( button ), 60, -1 );
1007                                         }
1008                                         {
1009                                                 auto button = ui::Button( "Set..." );
1010                                                 button.show();
1011                                                 gtk_box_pack_end( GTK_BOX( hbox2 ), GTK_WIDGET( button ), TRUE, FALSE, 0 );
1012                                                 g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchreset ), 0 );
1013                                                 gtk_widget_set_size_request( GTK_WIDGET( button ), 60, -1 );
1014                                         }
1015                                         {
1016                                                 auto button = ui::Button( "Natural" );
1017                                                 button.show();
1018                                                 gtk_box_pack_end( GTK_BOX( hbox2 ), GTK_WIDGET( button ), TRUE, FALSE, 0 );
1019                                                 g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchnatural ), 0 );
1020                                                 gtk_widget_set_size_request( GTK_WIDGET( button ), 60, -1 );
1021                                         }
1022                                         {
1023                                                 auto button = ui::Button( "Fit" );
1024                                                 button.show();
1025                                                 gtk_box_pack_end( GTK_BOX( hbox2 ), GTK_WIDGET( button ), TRUE, FALSE, 0 );
1026                                                 g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( OnBtnPatchfit ), 0 );
1027                                                 gtk_widget_set_size_request( GTK_WIDGET( button ), 60, -1 );
1028                                         }
1029                                 }
1030                         }
1031                 }
1032         }
1033
1034         return window;
1035 }
1036
1037 // sync the dialog our internal data structures
1038 void PatchInspector::exportData(){
1039         m_bListenChanged = false;
1040         Dialog::exportData();
1041         m_bListenChanged = true;
1042 }
1043 void PatchInspector::importData(){
1044         m_bListenChanged = false;
1045         Dialog::importData();
1046         m_bListenChanged = true;
1047 }
1048
1049 // read the map and feed in the stuff to the dialog box
1050 void PatchInspector::GetPatchInfo(){
1051         if ( g_pGameDescription->mGameType == "doom3" ) {
1052                 m_subdivisions.update();
1053         }
1054
1055         if ( GlobalSelectionSystem().countSelected() == 0 ) {
1056                 m_Patch = 0;
1057         }
1058         else
1059         {
1060                 m_Patch = Node_getPatch( GlobalSelectionSystem().ultimateSelected().path().top() );
1061         }
1062
1063         if ( m_Patch != 0 ) {
1064                 m_strName = m_Patch->GetShader();
1065
1066                 // fill in the numbers for Row / Col selection
1067                 m_bListenChanged = false;
1068
1069                 {
1070                         gtk_combo_box_set_active( m_pRowCombo, 0 );
1071
1072                         for ( std::size_t i = 0; i < m_countRows; ++i )
1073                         {
1074                                 gtk_combo_box_text_remove( m_pRowCombo, gint( m_countRows - i - 1 ) );
1075                         }
1076
1077                         m_countRows = m_Patch->getHeight();
1078                         for ( std::size_t i = 0; i < m_countRows; ++i )
1079                         {
1080                                 char buffer[16];
1081                                 sprintf( buffer, "%u", Unsigned( i ) );
1082                                 gtk_combo_box_text_append_text( m_pRowCombo, buffer );
1083                         }
1084
1085                         gtk_combo_box_set_active( m_pRowCombo, 0 );
1086                 }
1087
1088                 {
1089                         gtk_combo_box_set_active( m_pColCombo, 0 );
1090
1091                         for ( std::size_t i = 0; i < m_countCols; ++i )
1092                         {
1093                                 gtk_combo_box_text_remove( m_pColCombo, gint( m_countCols - i - 1 ) );
1094                         }
1095
1096                         m_countCols = m_Patch->getWidth();
1097                         for ( std::size_t i = 0; i < m_countCols; ++i )
1098                         {
1099                                 char buffer[16];
1100                                 sprintf( buffer, "%u", Unsigned( i ) );
1101                                 gtk_combo_box_text_append_text( m_pColCombo, buffer );
1102                         }
1103
1104                         gtk_combo_box_set_active( m_pColCombo, 0 );
1105                 }
1106
1107                 m_bListenChanged = true;
1108
1109         }
1110         else
1111         {
1112                 //globalOutputStream() << "WARNING: no patch\n";
1113         }
1114         // fill in our internal structs
1115         m_nRow = 0; m_nCol = 0;
1116         UpdateRowColInfo();
1117         // now update the dialog box
1118         importData();
1119 }
1120
1121 // read the current patch on map and initialize m_fX m_fY accordingly
1122 // NOTE: don't call UpdateData in there, it's not meant for
1123 void PatchInspector::UpdateRowColInfo(){
1124         m_fX = m_fY = m_fZ = m_fS = m_fT = 0.0;
1125
1126         if ( m_Patch != 0 ) {
1127                 // we rely on whatever active row/column has been set before we get called
1128                 std::size_t r = m_nRow;
1129                 std::size_t c = m_nCol;
1130                 if ( r < m_Patch->getHeight()
1131                          && c < m_Patch->getWidth() ) {
1132                         const PatchControl& p = m_Patch->ctrlAt( r,c );
1133                         m_fX = p.m_vertex[0];
1134                         m_fY = p.m_vertex[1];
1135                         m_fZ = p.m_vertex[2];
1136                         m_fS = p.m_texcoord[0];
1137                         m_fT = p.m_texcoord[1];
1138                 }
1139         }
1140 }
1141
1142
1143 void PatchInspector_SelectionChanged( const Selectable& selectable ){
1144         PatchInspector_queueDraw();
1145 }
1146
1147
1148 #include "preferencesystem.h"
1149
1150
1151 void PatchInspector_Construct(){
1152         GlobalCommands_insert( "PatchInspector", FreeCaller<PatchInspector_toggleShown>(), Accelerator( 'S', (GdkModifierType)GDK_SHIFT_MASK ) );
1153
1154         GlobalPreferenceSystem().registerPreference( "PatchWnd", WindowPositionTrackerImportStringCaller( g_PatchInspector.m_position_tracker ), WindowPositionTrackerExportStringCaller( g_PatchInspector.m_position_tracker ) );
1155         GlobalPreferenceSystem().registerPreference( "SI_PatchTexdef_Scale1", FloatImportStringCaller( g_pi_globals.scale[0] ), FloatExportStringCaller( g_pi_globals.scale[0] ) );
1156         GlobalPreferenceSystem().registerPreference( "SI_PatchTexdef_Scale2", FloatImportStringCaller( g_pi_globals.scale[1] ), FloatExportStringCaller( g_pi_globals.scale[1] ) );
1157         GlobalPreferenceSystem().registerPreference( "SI_PatchTexdef_Shift1", FloatImportStringCaller( g_pi_globals.shift[0] ), FloatExportStringCaller( g_pi_globals.shift[0] ) );
1158         GlobalPreferenceSystem().registerPreference( "SI_PatchTexdef_Shift2", FloatImportStringCaller( g_pi_globals.shift[1] ), FloatExportStringCaller( g_pi_globals.shift[1] ) );
1159         GlobalPreferenceSystem().registerPreference( "SI_PatchTexdef_Rotate", FloatImportStringCaller( g_pi_globals.rotate ), FloatExportStringCaller( g_pi_globals.rotate ) );
1160
1161         typedef FreeCaller1<const Selectable&, PatchInspector_SelectionChanged> PatchInspectorSelectionChangedCaller;
1162         GlobalSelectionSystem().addSelectionChangeCallback( PatchInspectorSelectionChangedCaller() );
1163         typedef FreeCaller<PatchInspector_queueDraw> PatchInspectorQueueDrawCaller;
1164         Patch_addTextureChangedCallback( PatchInspectorQueueDrawCaller() );
1165 }
1166 void PatchInspector_Destroy(){
1167 }