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