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