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