disabled 'detail' checkbox in q2 surface inspector
[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             // not allowed to modify detail flag using Surface Inspector
1068             gtk_widget_set_sensitive(GTK_WIDGET(m_contentFlags[27]), FALSE);
1069           }
1070         }
1071       }
1072       {
1073         GtkFrame* frame = GTK_FRAME(gtk_frame_new("Value"));
1074         gtk_widget_show(GTK_WIDGET(frame));
1075         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), TRUE, TRUE, 0);
1076         {
1077           GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 4));
1078           gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
1079           gtk_widget_show(GTK_WIDGET(vbox3));
1080           gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3));
1081
1082           {
1083             GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
1084             gtk_widget_show(GTK_WIDGET(entry));
1085             gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(entry), TRUE, TRUE, 0);
1086             m_valueEntryWidget = entry;
1087             m_valueEntry.connect(entry);
1088           }
1089         }
1090       }
1091     }
1092
1093 #if TEXTOOL_ENABLED
1094     if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1095 // Shamus: Textool goodies...
1096     {
1097       GtkWidget * frame = gtk_frame_new("Textool");
1098       gtk_widget_show(frame);
1099       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(frame), FALSE, FALSE, 0);
1100       {
1101         //Prolly should make this a member or global var, so the SI can draw on it...
1102         TexTool::g_textoolWin = glwidget_new(FALSE);
1103         // --> Dunno, but this stuff may be necessary... (Looks like it!)
1104         gtk_widget_ref(TexTool::g_textoolWin);
1105         gtk_widget_set_events(TexTool::g_textoolWin, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
1106         GTK_WIDGET_SET_FLAGS(TexTool::g_textoolWin, GTK_CAN_FOCUS);
1107         // <-- end stuff...
1108         gtk_widget_show(TexTool::g_textoolWin);
1109         gtk_widget_set_usize(TexTool::g_textoolWin, -1, 240);   //Yeah!
1110         gtk_container_add(GTK_CONTAINER(frame), TexTool::g_textoolWin);
1111
1112         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "size_allocate", G_CALLBACK(TexTool::size_allocate), NULL);
1113         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "expose_event", G_CALLBACK(TexTool::expose), NULL);
1114         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "button_press_event", G_CALLBACK(TexTool::button_press), NULL);
1115         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "button_release_event", G_CALLBACK(TexTool::button_release), NULL);
1116         g_signal_connect(G_OBJECT(TexTool::g_textoolWin), "motion_notify_event", G_CALLBACK(TexTool::motion), NULL);
1117       }
1118       {
1119         GtkWidget * hbox = gtk_hbox_new(FALSE, 5);
1120         gtk_widget_show(hbox);
1121         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 0);
1122         // Checkboxes go here... (Flip X/Y)
1123         GtkWidget * flipX = gtk_check_button_new_with_label("Flip X axis");
1124         GtkWidget * flipY = gtk_check_button_new_with_label("Flip Y axis");
1125         gtk_widget_show(flipX);
1126         gtk_widget_show(flipY);
1127         gtk_box_pack_start(GTK_BOX(hbox), flipX, FALSE, FALSE, 0);
1128         gtk_box_pack_start(GTK_BOX(hbox), flipY, FALSE, FALSE, 0);
1129         
1130 //Instead of this, we probably need to create a vbox to put into the frame, then the
1131 //window, then the hbox. !!! FIX !!!
1132 //        gtk_container_add(GTK_CONTAINER(frame), hbox);
1133
1134 //Hmm. Do we really need g_object_set_data? Mebbe not... And we don't! :-)
1135 //        g_object_set_data(G_OBJECT(flipX), "handler", gint_to_pointer(g_signal_connect(G_OBJECT(flipX), "toggled", G_CALLBACK(TexTool::flipX), 0)));
1136 //        g_object_set_data(G_OBJECT(flipY), "handler", gint_to_pointer(g_signal_connect(G_OBJECT(flipY), "toggled", G_CALLBACK(TexTool::flipY), 0)));
1137 //Instead, just do:
1138         g_signal_connect(G_OBJECT(flipX), "toggled", G_CALLBACK(TexTool::flipX), NULL);
1139         g_signal_connect(G_OBJECT(flipY), "toggled", G_CALLBACK(TexTool::flipY), NULL);
1140       }
1141     }
1142 #endif
1143   }
1144
1145   return window;
1146 }
1147
1148 /*
1149 ==============
1150 Update
1151
1152 Set the fields to the current texdef (i.e. map/texdef -> dialog widgets)
1153 if faces selected (instead of brushes) -> will read this face texdef, else current texdef
1154 if only patches selected, will read the patch texdef
1155 ===============
1156 */
1157
1158 void spin_button_set_value_no_signal(GtkSpinButton* spin, gdouble value)
1159 {
1160   guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(spin), "handler"));
1161   g_signal_handler_block(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id);
1162   gtk_spin_button_set_value(spin, value);
1163   g_signal_handler_unblock(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id);
1164 }
1165
1166 void spin_button_set_step_increment(GtkSpinButton* spin, gdouble value)
1167 {
1168   GtkAdjustment* adjust = gtk_spin_button_get_adjustment(spin);
1169   adjust->step_increment = value;
1170 }
1171
1172 void SurfaceInspector::Update()
1173 {
1174   const char * name = SurfaceInspector_GetSelectedShader();
1175   
1176   if(shader_is_texture(name))
1177   {
1178     gtk_entry_set_text(m_texture, shader_get_textureName(name));
1179   }
1180   else
1181   {
1182     gtk_entry_set_text(m_texture, "");
1183   }
1184
1185   texdef_t shiftScaleRotate;
1186 //Shamus: This is where we get into trouble--the BP code tries to convert to a "faked"
1187 //shift, rotate & scale values from the brush face, which seems to screw up for some reason.
1188 //!!! FIX !!!
1189 /*globalOutputStream() << "--> SI::Update. About to do ShiftScaleRotate_fromFace()...\n";
1190 SurfaceInspector_GetSelectedBPTexdef();
1191 globalOutputStream() << "BP: (" << g_selectedBrushPrimitTexdef.coords[0][0] << ", " << g_selectedBrushPrimitTexdef.coords[0][1] << ")("
1192         << g_selectedBrushPrimitTexdef.coords[1][0] << ", " << g_selectedBrushPrimitTexdef.coords[1][1] << ")("
1193         << g_selectedBrushPrimitTexdef.coords[0][2] << ", " << g_selectedBrushPrimitTexdef.coords[1][2] << ") SurfaceInspector::Update\n";//*/
1194 //Ok, it's screwed up *before* we get here...
1195   ShiftScaleRotate_fromFace(shiftScaleRotate, SurfaceInspector_GetSelectedTexdef());
1196
1197   {
1198     spin_button_set_value_no_signal(m_hshiftIncrement.m_spin, shiftScaleRotate.shift[0]);
1199     spin_button_set_step_increment(m_hshiftIncrement.m_spin, g_si_globals.shift[0]);
1200     entry_set_float(m_hshiftIncrement.m_entry, g_si_globals.shift[0]);
1201   }
1202
1203   {
1204     spin_button_set_value_no_signal(m_vshiftIncrement.m_spin, shiftScaleRotate.shift[1]);
1205     spin_button_set_step_increment(m_vshiftIncrement.m_spin, g_si_globals.shift[1]);
1206     entry_set_float(m_vshiftIncrement.m_entry, g_si_globals.shift[1]);
1207   }
1208
1209   {
1210     spin_button_set_value_no_signal(m_hscaleIncrement.m_spin, shiftScaleRotate.scale[0]);
1211     spin_button_set_step_increment(m_hscaleIncrement.m_spin, g_si_globals.scale[0]);
1212     entry_set_float(m_hscaleIncrement.m_entry, g_si_globals.scale[0]);
1213   }
1214
1215   {
1216     spin_button_set_value_no_signal(m_vscaleIncrement.m_spin, shiftScaleRotate.scale[1]);
1217     spin_button_set_step_increment(m_vscaleIncrement.m_spin, g_si_globals.scale[1]);
1218     entry_set_float(m_vscaleIncrement.m_entry, g_si_globals.scale[1]);
1219   }
1220
1221   {
1222     spin_button_set_value_no_signal(m_rotateIncrement.m_spin, shiftScaleRotate.rotate);
1223     spin_button_set_step_increment(m_rotateIncrement.m_spin, g_si_globals.rotate);
1224     entry_set_float(m_rotateIncrement.m_entry, g_si_globals.rotate);
1225   }
1226
1227   if(!string_empty(g_pGameDescription->getKeyValue("si_flags")))
1228   {
1229     ContentsFlagsValue flags(SurfaceInspector_GetSelectedFlags());
1230
1231     entry_set_int(m_valueEntryWidget, flags.m_value);
1232
1233     for(GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p)
1234     {
1235       toggle_button_set_active_no_signal(GTK_TOGGLE_BUTTON(*p), flags.m_surfaceFlags & (1 << (p - m_surfaceFlags)));
1236     }
1237
1238     for(GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p)
1239     {
1240       toggle_button_set_active_no_signal(GTK_TOGGLE_BUTTON(*p), flags.m_contentFlags & (1 << (p - m_contentFlags)));
1241     }
1242   }
1243 }
1244
1245 /*
1246 ==============
1247 Apply
1248
1249 Reads the fields to get the current texdef (i.e. widgets -> MAP)
1250 in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix
1251 ===============
1252 */
1253 void SurfaceInspector::ApplyShader()
1254 {
1255   StringOutputStream name(256);
1256   name << GlobalTexturePrefix_get() << gtk_entry_get_text(m_texture);
1257
1258   // TTimo: detect and refuse invalid texture names (at least the ones with spaces)
1259   if(!texdef_name_valid(name.c_str()))
1260   {
1261     globalErrorStream() << "invalid texture name '" << name.c_str() << "'\n";
1262     SurfaceInspector_queueDraw();
1263     return;
1264   }
1265
1266   UndoableCommand undo("textureNameSetSelected");
1267   Select_SetShader(name.c_str());
1268 }
1269
1270 void SurfaceInspector::ApplyTexdef()
1271 {
1272   texdef_t shiftScaleRotate;
1273
1274   shiftScaleRotate.shift[0] = static_cast<float>(gtk_spin_button_get_value_as_float(m_hshiftIncrement.m_spin));
1275   shiftScaleRotate.shift[1] = static_cast<float>(gtk_spin_button_get_value_as_float(m_vshiftIncrement.m_spin));
1276   shiftScaleRotate.scale[0] = static_cast<float>(gtk_spin_button_get_value_as_float(m_hscaleIncrement.m_spin));
1277   shiftScaleRotate.scale[1] = static_cast<float>(gtk_spin_button_get_value_as_float(m_vscaleIncrement.m_spin));
1278   shiftScaleRotate.rotate = static_cast<float>(gtk_spin_button_get_value_as_float(m_rotateIncrement.m_spin));
1279
1280   TextureProjection projection;
1281 //Shamus: This is the other place that screws up, it seems, since it doesn't seem to do the
1282 //conversion from the face (I think) and so bogus values end up in the thing... !!! FIX !!!
1283 //This is actually OK. :-P
1284   ShiftScaleRotate_toFace(shiftScaleRotate, projection);
1285
1286   UndoableCommand undo("textureProjectionSetSelected");
1287   Select_SetTexdef(projection);
1288 }
1289
1290 void SurfaceInspector::ApplyFlags()
1291 {
1292   unsigned int surfaceflags = 0;
1293   for(GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p)
1294   {
1295     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p)))
1296     {
1297       surfaceflags |= (1 << (p - m_surfaceFlags));
1298     }
1299   }
1300
1301   unsigned int contentflags = 0;
1302   for(GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p)
1303   {
1304     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p)))
1305     {
1306       contentflags |= (1 << (p - m_contentFlags));
1307     }
1308   }
1309
1310   int value = entry_get_int(m_valueEntryWidget);
1311
1312   UndoableCommand undo("flagsSetSelected");
1313   Select_SetFlags(ContentsFlagsValue(surfaceflags, contentflags, value, true));
1314 }
1315
1316
1317 void Face_getTexture(Face& face, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags)
1318 {
1319   shader = face.GetShader();
1320   face.GetTexdef(projection);
1321   flags = face.getShader().m_flags;
1322 }
1323 typedef Function4<Face&, CopiedString&, TextureProjection&, ContentsFlagsValue&, void, Face_getTexture> FaceGetTexture;
1324
1325 void Face_setTexture(Face& face, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags)
1326 {
1327   face.SetShader(shader);
1328   face.SetTexdef(projection);
1329   face.SetFlags(flags);
1330 }
1331 typedef Function4<Face&, const char*, const TextureProjection&, const ContentsFlagsValue&, void, Face_setTexture> FaceSetTexture;
1332
1333
1334 void Patch_getTexture(Patch& patch, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags)
1335 {
1336   shader = patch.GetShader();
1337   projection = TextureProjection(texdef_t(), brushprimit_texdef_t(), Vector3(0, 0, 0), Vector3(0, 0, 0));
1338   flags = ContentsFlagsValue(0, 0, 0, false);
1339 }
1340 typedef Function4<Patch&, CopiedString&, TextureProjection&, ContentsFlagsValue&, void, Patch_getTexture> PatchGetTexture;
1341
1342 void Patch_setTexture(Patch& patch, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags)
1343 {
1344   patch.SetShader(shader);
1345 }
1346 typedef Function4<Patch&, const char*, const TextureProjection&, const ContentsFlagsValue&, void, Patch_setTexture> PatchSetTexture;
1347
1348
1349 typedef Callback3<CopiedString&, TextureProjection&, ContentsFlagsValue&> GetTextureCallback;
1350 typedef Callback3<const char*, const TextureProjection&, const ContentsFlagsValue&> SetTextureCallback;
1351
1352 struct Texturable
1353 {
1354   GetTextureCallback getTexture;
1355   SetTextureCallback setTexture;
1356 };
1357
1358
1359 void Face_getClosest(Face& face, SelectionTest& test, SelectionIntersection& bestIntersection, Texturable& texturable)
1360 {
1361   SelectionIntersection intersection;
1362   face.testSelect(test, intersection);
1363   if(intersection.valid()
1364     && SelectionIntersection_closer(intersection, bestIntersection))
1365   {
1366     bestIntersection = intersection;
1367     texturable.setTexture = makeCallback3(FaceSetTexture(), face);
1368     texturable.getTexture = makeCallback3(FaceGetTexture(), face);
1369   }
1370 }
1371
1372
1373 class OccludeSelector : public Selector
1374 {
1375   SelectionIntersection& m_bestIntersection;
1376   bool& m_occluded;
1377 public:
1378   OccludeSelector(SelectionIntersection& bestIntersection, bool& occluded) : m_bestIntersection(bestIntersection), m_occluded(occluded)
1379   {
1380     m_occluded = false;
1381   }
1382   void pushSelectable(Selectable& selectable)
1383   {
1384   }
1385   void popSelectable()
1386   {
1387   }
1388   void addIntersection(const SelectionIntersection& intersection)
1389   {
1390     if(SelectionIntersection_closer(intersection, m_bestIntersection))
1391     {
1392       m_bestIntersection = intersection;
1393       m_occluded = true;
1394     }
1395   }
1396 };
1397
1398 class BrushGetClosestFaceVisibleWalker : public scene::Graph::Walker
1399 {
1400   SelectionTest& m_test;
1401   Texturable& m_texturable;
1402   mutable SelectionIntersection m_bestIntersection;
1403 public:
1404   BrushGetClosestFaceVisibleWalker(SelectionTest& test, Texturable& texturable) : m_test(test), m_texturable(texturable)
1405   {
1406   }
1407   bool pre(const scene::Path& path, scene::Instance& instance) const
1408   {
1409     if(path.top().get().visible())
1410     {
1411       BrushInstance* brush = Instance_getBrush(instance);
1412       if(brush != 0)
1413       {
1414         m_test.BeginMesh(brush->localToWorld());
1415
1416         for(Brush::const_iterator i = brush->getBrush().begin(); i != brush->getBrush().end(); ++i)
1417         {
1418           Face_getClosest(*(*i), m_test, m_bestIntersection, m_texturable);
1419         }
1420       }
1421       else
1422       {
1423         SelectionTestable* selectionTestable = Instance_getSelectionTestable(instance);
1424         if(selectionTestable)
1425         {
1426           bool occluded;
1427           OccludeSelector selector(m_bestIntersection, occluded);
1428           selectionTestable->testSelect(selector, m_test);
1429           if(occluded)
1430           {
1431             Patch* patch = Node_getPatch(path.top());
1432             if(patch != 0)
1433             {
1434               m_texturable.setTexture = makeCallback3(PatchSetTexture(), *patch);
1435               m_texturable.getTexture = makeCallback3(PatchGetTexture(), *patch);
1436             }
1437             else
1438             {
1439               m_texturable = Texturable();
1440             }
1441           }
1442         }
1443       }
1444     }
1445     return true;
1446   }
1447 };
1448
1449 Texturable Scene_getClosestTexturable(scene::Graph& graph, SelectionTest& test)
1450 {
1451   Texturable texturable;
1452   graph.traverse(BrushGetClosestFaceVisibleWalker(test, texturable));
1453   return texturable;
1454 }
1455
1456 bool Scene_getClosestTexture(scene::Graph& graph, SelectionTest& test, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags)
1457 {
1458   Texturable texturable = Scene_getClosestTexturable(graph, test);
1459   if(texturable.getTexture != GetTextureCallback())
1460   {
1461     texturable.getTexture(shader, projection, flags);
1462     return true;
1463   }
1464   return false;
1465 }
1466
1467 void Scene_setClosestTexture(scene::Graph& graph, SelectionTest& test, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags)
1468 {
1469   Texturable texturable = Scene_getClosestTexturable(graph, test);
1470   if(texturable.setTexture != SetTextureCallback())
1471   {
1472     texturable.setTexture(shader, projection, flags);
1473   }
1474 }
1475
1476
1477 class FaceTexture
1478 {
1479 public:
1480   TextureProjection m_projection;
1481   ContentsFlagsValue m_flags;
1482 };
1483
1484 FaceTexture g_faceTextureClipboard;
1485
1486 void FaceTextureClipboard_setDefault()
1487 {
1488   g_faceTextureClipboard.m_flags = ContentsFlagsValue(0, 0, 0, false);
1489   TexDef_Construct_Default(g_faceTextureClipboard.m_projection);
1490 }
1491
1492 void TextureClipboard_textureSelected(const char* shader)
1493 {
1494   FaceTextureClipboard_setDefault();
1495 }
1496
1497 class TextureBrowser;
1498 extern TextureBrowser g_TextureBrowser;
1499 void TextureBrowser_SetSelectedShader(TextureBrowser& textureBrowser, const char* shader);
1500 const char* TextureBrowser_GetSelectedShader(TextureBrowser& textureBrowser);
1501
1502 void Scene_copyClosestTexture(SelectionTest& test)
1503 {
1504   CopiedString shader;
1505   if(Scene_getClosestTexture(GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags))
1506   {
1507     TextureBrowser_SetSelectedShader(g_TextureBrowser, shader.c_str());
1508   }
1509 }
1510
1511 void Scene_applyClosestTexture(SelectionTest& test)
1512 {
1513   UndoableCommand command("facePaintTexture");
1514
1515   Scene_setClosestTexture(GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader(g_TextureBrowser), g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags);
1516
1517   SceneChangeNotify();
1518 }
1519
1520
1521
1522
1523
1524 void SelectedFaces_copyTexture()
1525 {
1526   if(!g_SelectedFaceInstances.empty())
1527   {
1528     Face& face = g_SelectedFaceInstances.last().getFace();
1529     face.GetTexdef(g_faceTextureClipboard.m_projection);
1530     g_faceTextureClipboard.m_flags = face.getShader().m_flags;
1531
1532     TextureBrowser_SetSelectedShader(g_TextureBrowser, face.getShader().getShader());
1533   }
1534 }
1535
1536 void FaceInstance_pasteTexture(FaceInstance& faceInstance)
1537 {
1538   faceInstance.getFace().SetTexdef(g_faceTextureClipboard.m_projection);
1539   faceInstance.getFace().SetShader(TextureBrowser_GetSelectedShader(g_TextureBrowser));
1540   faceInstance.getFace().SetFlags(g_faceTextureClipboard.m_flags);
1541   SceneChangeNotify();
1542 }
1543
1544 bool SelectedFaces_empty()
1545 {
1546   return g_SelectedFaceInstances.empty();
1547 }
1548
1549 void SelectedFaces_pasteTexture()
1550 {
1551   UndoableCommand command("facePasteTexture");
1552   g_SelectedFaceInstances.foreach(FaceInstance_pasteTexture);
1553 }
1554
1555
1556
1557 void SurfaceInspector_constructPreferences(PreferencesPage& page)
1558 {
1559   page.appendCheckBox("", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid);
1560 }
1561 void SurfaceInspector_constructPage(PreferenceGroup& group)
1562 {
1563   PreferencesPage page(group.createPage("Surface Inspector", "Surface Inspector Preferences"));
1564   SurfaceInspector_constructPreferences(page);
1565 }
1566 void SurfaceInspector_registerPreferencesPage()
1567 {
1568   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, SurfaceInspector_constructPage>());
1569 }
1570
1571 void SurfaceInspector_registerCommands()
1572 {
1573   GlobalCommands_insert("FitTexture", FreeCaller<SurfaceInspector_FitTexture>(), Accelerator('B', (GdkModifierType)GDK_SHIFT_MASK));
1574   GlobalCommands_insert("SurfaceInspector", FreeCaller<SurfaceInspector_toggleShown>(), Accelerator('S'));
1575
1576   GlobalCommands_insert("FaceCopyTexture", FreeCaller<SelectedFaces_copyTexture>());
1577   GlobalCommands_insert("FacePasteTexture", FreeCaller<SelectedFaces_pasteTexture>());
1578 }
1579
1580
1581 #include "preferencesystem.h"
1582
1583
1584 void SurfaceInspector_Construct()
1585 {
1586   g_SurfaceInspector = new SurfaceInspector;
1587
1588   SurfaceInspector_registerCommands();
1589
1590   FaceTextureClipboard_setDefault();
1591
1592   GlobalPreferenceSystem().registerPreference("SurfaceWnd", getSurfaceInspector().m_importPosition, getSurfaceInspector().m_exportPosition);
1593   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale1", FloatImportStringCaller(g_si_globals.scale[0]), FloatExportStringCaller(g_si_globals.scale[0]));      
1594   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale2", FloatImportStringCaller(g_si_globals.scale[1]), FloatExportStringCaller(g_si_globals.scale[1]));
1595   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift1", FloatImportStringCaller(g_si_globals.shift[0]), FloatExportStringCaller(g_si_globals.shift[0]));
1596   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift2", FloatImportStringCaller(g_si_globals.shift[1]), FloatExportStringCaller(g_si_globals.shift[1]));
1597   GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Rotate", FloatImportStringCaller(g_si_globals.rotate), FloatExportStringCaller(g_si_globals.rotate));
1598   GlobalPreferenceSystem().registerPreference("SnapTToGrid", BoolImportStringCaller(g_si_globals.m_bSnapTToGrid), BoolExportStringCaller(g_si_globals.m_bSnapTToGrid));
1599
1600   typedef FreeCaller1<const Selectable&, SurfaceInspector_SelectionChanged> SurfaceInspectorSelectionChangedCaller;
1601   GlobalSelectionSystem().addSelectionChangeCallback(SurfaceInspectorSelectionChangedCaller());
1602   typedef FreeCaller<SurfaceInspector_updateSelection> SurfaceInspectorUpdateSelectionCaller;
1603   Brush_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller());
1604   Patch_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller());
1605
1606   SurfaceInspector_registerPreferencesPage();
1607 }
1608 void SurfaceInspector_Destroy()
1609 {
1610   delete g_SurfaceInspector;
1611 }
1612
1613
1614
1615 #if TEXTOOL_ENABLED
1616
1617 namespace TexTool { // namespace hides these symbols from other object-files
1618 //
1619 //Shamus: Textool functions, including GTK+ callbacks
1620 //
1621
1622 //NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!!
1623 //      But... You can see down below that it *is* initialized! WTF?
1624 struct Extent
1625 {
1626         float minX, minY, maxX, maxY;
1627         float width(void) { return fabs(maxX - minX); }
1628         float height(void) { return fabs(maxY - minY); }
1629 };
1630
1631 //This seems to control the texture scale... (Yep! ;-)
1632 Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f };
1633 brushprimit_texdef_t tm;                                                // Texture transform matrix
1634 Vector2 pts[c_brush_maxFaces];
1635 Vector2 center;
1636 int numPts;
1637 int textureNum;
1638 Vector2 textureSize;
1639 Vector2 windowSize;
1640 #define VP_PADDING      1.2
1641 #define PI                      3.14159265358979
1642 bool lButtonDown = false;
1643 bool rButtonDown = false;
1644 //int dragPoint;
1645 //int anchorPoint;
1646 bool haveAnchor = false;
1647 brushprimit_texdef_t currentBP;
1648 brushprimit_texdef_t origBP;                                    // Original brush primitive (before we muck it up)
1649 float controlRadius = 5.0f;
1650 float rotationAngle = 0.0f;
1651 float rotationAngle2 = 0.0f;
1652 float oldRotationAngle;
1653 Vector2 rotationPoint;
1654 bool translatingX = false;                                              // Widget state variables
1655 bool translatingY = false;
1656 bool scalingX = false;
1657 bool scalingY = false;
1658 bool rotating = false;
1659 bool resizingX = false;                                                 // Not sure what this means... :-/
1660 bool resizingY = false;
1661 float origAngle, origScaleX, origScaleY;
1662 Vector2 oldCenter;
1663
1664
1665 // Function prototypes (move up to top later...)
1666
1667 void DrawCircularArc(Vector2 ctr, float startAngle, float endAngle, float radius);
1668
1669
1670 void CopyPointsFromSelectedFace(void)
1671 {
1672         // Make sure that there's a face and winding to get!
1673
1674         if (g_SelectedFaceInstances.empty())
1675         {
1676                 numPts = 0;
1677                 return;
1678         }
1679
1680         Face & face = g_SelectedFaceInstances.last().getFace();
1681         textureNum = face.getShader().m_state->getTexture().texture_number;
1682         textureSize.x() = face.getShader().m_state->getTexture().width;
1683         textureSize.y() = face.getShader().m_state->getTexture().height;
1684 //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n";
1685
1686         currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef;
1687
1688   face.EmitTextureCoordinates();
1689         Winding & w = face.getWinding();
1690         int count = 0;
1691
1692         for(Winding::const_iterator i=w.begin(); i!=w.end(); i++)
1693         {
1694     //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", ";
1695                 pts[count].x() = (*i).texcoord.x();
1696                 pts[count].y() = (*i).texcoord.y();
1697                 count++;
1698         }
1699
1700         numPts = count;
1701
1702   //globalOutputStream() << " ..copied points\n";
1703 }
1704
1705         brushprimit_texdef_t bp;
1706 //This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE]
1707 void CommitChanges(void)
1708 {
1709         texdef_t t;                                                                     // Throwaway, since this is BP only
1710
1711         bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0];
1712         bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1];
1713         bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2];
1714 //Ok, this works for translation...
1715 //      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();
1716         bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0];
1717         bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1];
1718         bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2];
1719 //      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();
1720
1721 //This doesn't work:    g_brush_texture_changed();
1722 // Let's try this:
1723 //Note: We should only set an undo *after* the button has been released... !!! FIX !!!
1724 //Definitely *should* have an undo, though!
1725 //  UndoableCommand undo("textureProjectionSetSelected");
1726         Select_SetTexdef(TextureProjection(t, bp, Vector3(0, 0, 0), Vector3(0, 0, 0)));
1727 //This is working, but for some reason the translate is causing the rest of the SI
1728 //widgets to yield bad readings... !!! FIX !!!
1729 //I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why.
1730 //It's because there were some uncommented out add/removeScale functions in brush.h and a
1731 //removeScale in brushmanip.cpp... :-/
1732 //Translate isn't working at all now... :-(
1733 //It's because we need to multiply in some scaling factor (prolly the texture width/height)
1734 //Yep. :-P
1735 }
1736
1737 void UpdateControlPoints(void)
1738 {
1739         CommitChanges();
1740
1741   // Init texture transform matrix
1742
1743         tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f;
1744         tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f;
1745 }
1746
1747
1748 /*
1749 For shifting we have:
1750 */
1751 /*
1752 The code that should provide reasonable defaults, but doesn't for some reason:
1753 It's scaling the BP by 128 for some reason, between the time it's created and the
1754 time we get back to the SI widgets:
1755
1756 static void OnBtnAxial(GtkWidget *widget, gpointer data)
1757 {
1758   UndoableCommand undo("textureDefault");
1759   TextureProjection projection;
1760   TexDef_Construct_Default(projection);
1761   Select_SetTexdef(projection);
1762 }
1763
1764 Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection)
1765 which is in brushmanip.h: This eventually calls
1766 Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face...
1767 which just copies from brushpr to m_brushpr...
1768 */
1769
1770 //Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE]
1771 //Prolly should separate out the grid drawing so that we can draw it behind the polygon.
1772 const float gridWidth = 1.3f;// Let's try an absolute height... WORKS!!!
1773 // NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision
1774 //      detection easier...
1775 const float gridRadius = gridWidth * 0.5f;
1776
1777 typedef const float WidgetColor[3];
1778 const WidgetColor widgetColor[10] = {
1779         { 1.0000f, 0.2000f, 0.0000f },                  // Red
1780         { 0.9137f, 0.9765f, 0.4980f },                  // Yellow
1781         { 0.0000f, 0.6000f, 0.3216f },                  // Green
1782         { 0.6157f, 0.7726f, 0.8196f },                  // Cyan
1783         { 0.4980f, 0.5000f, 0.4716f },                  // Grey
1784
1785         // Highlight colors
1786         { 1.0000f, 0.6000f, 0.4000f },                  // Light Red
1787         { 1.0000f, 1.0000f, 0.8980f },                  // Light Yellow
1788         { 0.4000f, 1.0000f, 0.7216f },                  // Light Green
1789         { 1.0000f, 1.0000f, 1.0000f },                  // Light Cyan
1790         { 0.8980f, 0.9000f, 0.8716f }                   // Light Grey
1791 };
1792
1793 #define COLOR_RED                       0
1794 #define COLOR_YELLOW            1
1795 #define COLOR_GREEN                     2
1796 #define COLOR_CYAN                      3
1797 #define COLOR_GREY                      4
1798 #define COLOR_LT_RED            5
1799 #define COLOR_LT_YELLOW         6
1800 #define COLOR_LT_GREEN          7
1801 #define COLOR_LT_CYAN           8
1802 #define COLOR_LT_GREY           9
1803
1804 void DrawControlWidgets(void)
1805 {
1806 //Note that the grid should go *behind* the face outline... !!! FIX !!!
1807         // Grid
1808         float xStart = center.x() - (gridWidth / 2.0f);
1809         float yStart = center.y() - (gridWidth / 2.0f);
1810         float xScale = (extents.height() / extents.width()) * (textureSize.y() / textureSize.x());
1811
1812   glPushMatrix();
1813 //Small problem with this approach: Changing the center point in the TX code doesn't seem to
1814 //change anything here--prolly because we load a new identity matrix. A couple of ways to fix
1815 //this would be to get rid of that code, or change the center to a new point by taking into
1816 //account the transforms that we toss with the new identity matrix. Dunno which is better.
1817   glLoadIdentity();
1818   glScalef(xScale, 1.0, 1.0);                           // Will that square it up? Yup.
1819   glRotatef(static_cast<float>(radians_to_degrees(atan2(-currentBP.coords[0][1], currentBP.coords[0][0]))), 0.0, 0.0, -1.0);
1820   glTranslatef(-center.x(), -center.y(), 0.0);
1821
1822         // Circle
1823   glColor3fv(translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW]);
1824   glBegin(GL_LINE_LOOP);
1825         DrawCircularArc(center, 0, 2.0f * PI, gridRadius * 0.16);
1826
1827   glEnd();
1828
1829         // Axes
1830   glBegin(GL_LINES);
1831   glColor3fv(translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN]);
1832   glVertex2f(center.x(), center.y() + (gridRadius * 0.16));
1833   glVertex2f(center.x(), center.y() + (gridRadius * 1.00));
1834   glColor3fv(translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED]);
1835   glVertex2f(center.x() + (gridRadius * 0.16), center.y());
1836   glVertex2f(center.x() + (gridRadius * 1.00), center.y());
1837   glEnd();
1838
1839         // Arrowheads
1840   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1841   glBegin(GL_TRIANGLES);
1842   glColor3fv(translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN]);
1843   glVertex2f(center.x(), center.y() + (gridRadius * 1.10));
1844   glVertex2f(center.x() + (gridRadius * 0.06), center.y() + (gridRadius * 0.94));
1845   glVertex2f(center.x() - (gridRadius * 0.06), center.y() + (gridRadius * 0.94));
1846   glColor3fv(translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED]);
1847   glVertex2f(center.x() + (gridRadius * 1.10), center.y());
1848   glVertex2f(center.x() + (gridRadius * 0.94), center.y() + (gridRadius * 0.06));
1849   glVertex2f(center.x() + (gridRadius * 0.94), center.y() - (gridRadius * 0.06));
1850   glEnd();
1851
1852         // Arc
1853   glBegin(GL_LINE_STRIP);
1854   glColor3fv(rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN]);
1855         DrawCircularArc(center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90);
1856   glEnd();
1857
1858         // Boxes
1859   glColor3fv(scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN]);
1860   glBegin(GL_LINES);
1861   glVertex2f(center.x() + (gridRadius * 0.20), center.y() + (gridRadius * 1.50));
1862   glVertex2f(center.x() - (gridRadius * 0.20), center.y() + (gridRadius * 1.50));
1863   glEnd();
1864   glBegin(GL_LINE_LOOP);
1865   glVertex2f(center.x() + (gridRadius * 0.10), center.y() + (gridRadius * 1.40));
1866   glVertex2f(center.x() - (gridRadius * 0.10), center.y() + (gridRadius * 1.40));
1867   glVertex2f(center.x() - (gridRadius * 0.10), center.y() + (gridRadius * 1.20));
1868   glVertex2f(center.x() + (gridRadius * 0.10), center.y() + (gridRadius * 1.20));
1869   glEnd();
1870
1871   glColor3fv(scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED]);
1872   glBegin(GL_LINES);
1873   glVertex2f(center.x() + (gridRadius * 1.50), center.y() + (gridRadius * 0.20));
1874   glVertex2f(center.x() + (gridRadius * 1.50), center.y() - (gridRadius * 0.20));
1875   glEnd();
1876   glBegin(GL_LINE_LOOP);
1877   glVertex2f(center.x() + (gridRadius * 1.40), center.y() + (gridRadius * 0.10));
1878   glVertex2f(center.x() + (gridRadius * 1.40), center.y() - (gridRadius * 0.10));
1879   glVertex2f(center.x() + (gridRadius * 1.20), center.y() - (gridRadius * 0.10));
1880   glVertex2f(center.x() + (gridRadius * 1.20), center.y() + (gridRadius * 0.10));
1881   glEnd();
1882
1883   glColor3fv(scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN]);
1884   glBegin(GL_LINE_STRIP);
1885   glVertex2f(center.x() + (gridRadius * 1.50), center.y() + (gridRadius * 1.10));
1886   glVertex2f(center.x() + (gridRadius * 1.50), center.y() + (gridRadius * 1.50));
1887   glVertex2f(center.x() + (gridRadius * 1.10), center.y() + (gridRadius * 1.50));
1888   glEnd();
1889   glBegin(GL_LINE_LOOP);
1890   glVertex2f(center.x() + (gridRadius * 1.40), center.y() + (gridRadius * 1.40));
1891   glVertex2f(center.x() + (gridRadius * 1.40), center.y() + (gridRadius * 1.20));
1892   glVertex2f(center.x() + (gridRadius * 1.20), center.y() + (gridRadius * 1.20));
1893   glVertex2f(center.x() + (gridRadius * 1.20), center.y() + (gridRadius * 1.40));
1894   glEnd();
1895
1896   glPopMatrix();
1897 }
1898
1899 void DrawControlPoints(void)
1900 {
1901   glColor3f(1, 1, 1);
1902   glBegin(GL_LINE_LOOP);
1903         
1904         for(int i=0; i<numPts; i++)
1905           glVertex2f(pts[i].x(), pts[i].y());
1906         
1907   glEnd();
1908 }
1909
1910 // Note: Setup and all that jazz must be done by the caller!
1911
1912 void DrawCircularArc(Vector2 ctr, float startAngle, float endAngle, float radius)
1913 {
1914         float stepSize = (2.0f * PI) / 200.0f;
1915
1916         for(float angle=startAngle; angle<=endAngle; angle+=stepSize)
1917           glVertex2f(ctr.x() + radius * cos(angle), ctr.y() + radius * sin(angle));
1918 }
1919
1920
1921 void focus()
1922 {
1923         if (numPts == 0)
1924                 return;
1925
1926         // Find selected texture's extents...
1927
1928         extents.minX = extents.maxX = pts[0].x(),
1929         extents.minY = extents.maxY = pts[0].y();
1930
1931         for(int i=1; i<numPts; i++)
1932         {
1933                 if (pts[i].x() < extents.minX)
1934                         extents.minX = pts[i].x();
1935                 if (pts[i].x() > extents.maxX)
1936                         extents.maxX = pts[i].x();
1937                 if (pts[i].y() < extents.minY)
1938                         extents.minY = pts[i].y();
1939                 if (pts[i].y() > extents.maxY)
1940                         extents.maxY = pts[i].y();
1941         }
1942
1943         // Do some viewport fitting stuff...
1944
1945 //globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n";
1946 //globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", "
1947 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1948         // TTimo: Apply a ratio to get the area we'll draw.
1949         center.x() = 0.5f * (extents.minX + extents.maxX),
1950         center.y() = 0.5f * (extents.minY + extents.maxY);
1951         extents.minX = center.x() + VP_PADDING * (extents.minX - center.x()),
1952         extents.minY = center.y() + VP_PADDING * (extents.minY - center.y()),
1953         extents.maxX = center.x() + VP_PADDING * (extents.maxX - center.x()),
1954         extents.maxY = center.y() + VP_PADDING * (extents.maxY - center.y());
1955 //globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", "
1956 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1957
1958         // TTimo: We want a texture with the same X / Y ratio.
1959         // TTimo: Compute XY space / window size ratio.
1960         float SSize = extents.width(), TSize = extents.height();
1961         float ratioX = textureSize.x() * extents.width() / windowSize.x(),
1962                 ratioY = textureSize.y() * extents.height() / windowSize.y();
1963 //globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n";
1964 //globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n";
1965
1966         if (ratioX > ratioY)
1967         {
1968                 TSize = (windowSize.y() * ratioX) / textureSize.y();
1969 //              TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y());
1970         }
1971         else
1972         {
1973                 SSize = (windowSize.x() * ratioY) / textureSize.x();
1974 //              SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x());
1975         }
1976
1977         extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize,
1978         extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize;
1979 //globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", "
1980 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1981 }
1982
1983 gboolean size_allocate(GtkWidget * win, GtkAllocation * a, gpointer)
1984 {
1985         windowSize.x() = a->width;
1986         windowSize.y() = a->height;
1987         queueDraw();
1988         return false;
1989 }
1990
1991 gboolean expose(GtkWidget * win, GdkEventExpose * e, gpointer)
1992 {
1993 //      globalOutputStream() << "--> Textool Window was exposed!\n";
1994 //      globalOutputStream() << "    (window width/height: " << cc << "/" << e->area.height << ")\n";
1995
1996 //      windowSize.x() = e->area.width, windowSize.y() = e->area.height;
1997 //This needs to go elsewhere...
1998 //      InitTextool();
1999
2000         if (glwidget_make_current(win) == FALSE)
2001         {
2002                 globalOutputStream() << "    FAILED to make current! Oh, the agony! :-(\n";
2003                 return true;
2004         }
2005         
2006         CopyPointsFromSelectedFace();
2007
2008   if(!lButtonDown)
2009   {
2010     focus();
2011   }
2012
2013         // Probably should init button/anchor states here as well...
2014 //      rotationAngle = 0.0f;
2015   glClearColor(0, 0, 0, 0);
2016   glViewport(0, 0, e->area.width, e->area.height);
2017   glMatrixMode(GL_PROJECTION);
2018   glLoadIdentity();
2019
2020 //???
2021   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2022   glDisable(GL_DEPTH_TEST);
2023   glDisable(GL_BLEND);
2024
2025   glOrtho(extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1);
2026
2027   glColor3f(1, 1, 1);
2028         // draw the texture background
2029   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2030   glBindTexture(GL_TEXTURE_2D, textureNum);
2031
2032   glEnable(GL_TEXTURE_2D);
2033   glBegin(GL_QUADS);
2034   glTexCoord2f(extents.minX, extents.minY);
2035   glVertex2f(extents.minX, extents.minY);
2036   glTexCoord2f(extents.maxX, extents.minY);
2037   glVertex2f(extents.maxX, extents.minY);
2038   glTexCoord2f(extents.maxX, extents.maxY);
2039   glVertex2f(extents.maxX, extents.maxY);
2040   glTexCoord2f(extents.minX, extents.maxY);
2041   glVertex2f(extents.minX, extents.maxY);
2042   glEnd();
2043   glDisable(GL_TEXTURE_2D);
2044
2045   // draw the texture-space grid
2046   glColor3fv(widgetColor[COLOR_GREY]);
2047   glBegin(GL_LINES);
2048
2049   const int gridSubdivisions = 8;
2050   const float gridExtents = 4.0f;
2051
2052         for(int i = 0; i < gridSubdivisions + 1; ++i)
2053         {
2054     float y = i * (gridExtents / float(gridSubdivisions));
2055     float x = i * (gridExtents / float(gridSubdivisions));
2056           glVertex2f(0, y);
2057           glVertex2f(gridExtents, y);
2058           glVertex2f(x, 0);
2059           glVertex2f(x, gridExtents);
2060         }
2061
2062   glEnd();
2063
2064         DrawControlPoints();
2065         DrawControlWidgets();
2066 //???
2067         // reset the current texture
2068 //  glBindTexture(GL_TEXTURE_2D, 0);
2069 //  glFinish();
2070         glwidget_swap_buffers(win);
2071
2072         return false;
2073 }
2074
2075 /*int FindSelectedPoint(int x, int y)
2076 {
2077         for(int i=0; i<numPts; i++)
2078         {
2079                 int nx = (int)(windowSize.x() * (pts[i].x() - extents.minX) / extents.width());
2080                 int ny = (int)(windowSize.y() * (pts[i].y() - extents.minY) / extents.height());
2081
2082                 if (abs(nx - x) <= 3 && abs(ny - y) <= 3)
2083                         return i;
2084         }
2085
2086         return -1;
2087 }//*/
2088
2089 Vector2 trans;
2090 Vector2 trans2;
2091 Vector2 dragPoint;      // Defined in terms of window space (+x/-y)
2092 Vector2 oldTrans;
2093 gboolean button_press(GtkWidget * win, GdkEventButton * e, gpointer)
2094 {
2095 //      globalOutputStream() << "--> Textool button press...\n";
2096
2097         if (e->button == 1)
2098         {
2099                 lButtonDown = true;
2100     GlobalUndoSystem().start();
2101
2102     origBP = currentBP;
2103
2104   //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n";
2105   //globalOutputStream() << "                 [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n";
2106   //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f;
2107           origAngle = (origBP.coords[0][1] > 0 ? PI : -PI);     // Could also be -PI... !!! FIX !!! [DONE]
2108
2109           if (origBP.coords[0][0] != 0.0f)
2110                   origAngle = atan(origBP.coords[0][1] / origBP.coords[0][0]);
2111
2112           origScaleX = origBP.coords[0][0] / cos(origAngle);
2113           origScaleY = origBP.coords[1][1] / cos(origAngle);
2114           rotationAngle = origAngle;
2115           oldCenter[0] = oldCenter[1] = 0;
2116
2117     //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n";
2118     //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!!
2119     //Also: should reverse texture left/right up/down instead of flipping the points...
2120
2121 //disnowok
2122 //float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX);
2123 //float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY);
2124 //disdoes...
2125 //But I want it to scroll the texture window, not the points... !!! FIX !!!
2126 //Actually, should scroll the texture window only when mouse is down on no widgets...
2127                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2128                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2129                 trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny;
2130                 trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny;
2131
2132                 dragPoint.x() = e->x, dragPoint.y() = e->y;
2133                 trans2.x() = nx, trans2.y() = ny;
2134                 oldRotationAngle = rotationAngle;
2135 //              oldTrans.x() = tm.coords[0][2] - nx * textureSize.x();
2136 //              oldTrans.y() = tm.coords[1][2] - ny * textureSize.y();
2137                 oldTrans.x() = tm.coords[0][2];
2138                 oldTrans.y() = tm.coords[1][2];
2139                 oldCenter.x() = center.x();
2140                 oldCenter.y() = center.y();
2141
2142           queueDraw();
2143
2144                 return true;
2145         }
2146 /*      else if (e->button == 3)
2147         {
2148                 rButtonDown = true;
2149         }//*/
2150
2151 //globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n";
2152
2153         return false;
2154 }
2155
2156 gboolean button_release(GtkWidget * win, GdkEventButton * e, gpointer)
2157 {
2158 //      globalOutputStream() << "--> Textool button release...\n";
2159
2160         if (e->button == 1)
2161         {
2162 /*              float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2163                 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2164
2165 //This prolly should go into the mouse move code...
2166 //Doesn't work correctly anyway...
2167                 if (translatingX || translatingY)
2168                         center.x() = ptx, center.y() = pty;//*/
2169
2170                 lButtonDown = false;
2171
2172     if(translatingX || translatingY)
2173     {
2174       GlobalUndoSystem().finish("translateTexture");
2175     }
2176     else if(rotating)
2177     {
2178       GlobalUndoSystem().finish("rotateTexture");
2179     }
2180     else if(scalingX || scalingY)
2181     {
2182       GlobalUndoSystem().finish("scaleTexture");
2183     }
2184     else if(resizingX || resizingY)
2185     {
2186       GlobalUndoSystem().finish("resizeTexture");
2187     }
2188     else
2189     {
2190       GlobalUndoSystem().finish("textoolUnknown");
2191     }
2192
2193                 rotating = translatingX = translatingY = scalingX = scalingY
2194                         = resizingX = resizingY = false;
2195
2196           queueDraw();
2197         }
2198         else if (e->button == 3)
2199         {
2200                 rButtonDown = false;
2201         }
2202
2203         return true;
2204 }
2205
2206 /*
2207 void C2DView::GridForWindow( float c[2], int x, int y)
2208 {
2209   SpaceForWindow( c, x, y );
2210   if ( !m_bDoGrid )
2211     return;
2212   c[0] /= m_GridStep[0];
2213   c[1] /= m_GridStep[1];
2214   c[0] = (float)floor( c[0] + 0.5f );
2215   c[1] = (float)floor( c[1] + 0.5f );
2216   c[0] *= m_GridStep[0];
2217   c[1] *= m_GridStep[1];
2218 }
2219 void C2DView::SpaceForWindow( float c[2], int x, int y)
2220 {
2221   c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0];
2222   c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1];
2223 }
2224 */
2225 gboolean motion(GtkWidget * win, GdkEventMotion * e, gpointer)
2226 {
2227 //      globalOutputStream() << "--> Textool motion...\n";
2228
2229         if (lButtonDown)
2230         {
2231                 if (translatingX || translatingY)
2232                 {
2233                         float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2234                         float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2235
2236 //Need to fix this to take the rotation angle into account, so that it moves along
2237 //the rotated X/Y axis...
2238                         if (translatingX)
2239                         {
2240 //                              tm.coords[0][2] = (trans.x() + ptx) * textureSize.x();
2241 //This works, but only when the angle is zero. !!! FIX !!! [DONE]
2242 //                              tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x());
2243                                 tm.coords[0][2] = oldTrans.x() + (ptx - trans2.x()) * textureSize.x();
2244 //                              center.x() = oldCenter.x() + (ptx - trans2.x());
2245                         }
2246
2247                         if (translatingY)
2248                         {
2249 //                              tm.coords[1][2] = (trans.y() + pty) * textureSize.y();
2250 //                              tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y());
2251                                 tm.coords[1][2] = oldTrans.y() + (pty - trans2.y()) * textureSize.y();
2252 //                              center.y() = oldCenter.y() + (pty - trans2.y());
2253                         }
2254
2255 //Need to update center.x/y() so that the widget translates as well. Also, oldCenter
2256 //is badly named... Should be oldTrans or something like that... !!! FIX !!!
2257 //Changing center.x/y() here doesn't seem to change anything... :-/
2258                         UpdateControlPoints();
2259                 }
2260                 else if (rotating)
2261                 {
2262                         // Shamus: New rotate code
2263                         int cx = (int)(windowSize.x() * (center.x() - extents.minX) / extents.width());
2264                         int cy = (int)(windowSize.y() * (center.y() - extents.minY) / extents.height());
2265                         Vector3 v1(dragPoint.x() - cx, dragPoint.y() - cy, 0), v2(e->x - cx, e->y - cy, 0);
2266
2267                         vector3_normalise(v1);
2268                         vector3_normalise(v2);
2269                         float c = vector3_dot(v1, v2);
2270                         Vector3 cross = vector3_cross(v1, v2);
2271                         float s = vector3_length(cross);
2272
2273                         if (cross[2] > 0)
2274                                 s = -s;
2275
2276 // Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180...
2277 // Can't derive angle from that!
2278
2279 //rotationAngle = asin(s);// * 180.0f / 3.141592653589f;
2280 rotationAngle = acos(c);
2281 //rotationAngle2 = asin(s);
2282 if (cross[2] < 0)
2283         rotationAngle = -rotationAngle;
2284
2285 //NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD;
2286 //Let's try this:
2287 //No wok.
2288 /*c = cos(rotationAngle - oldRotationAngle);
2289 s = sin(rotationAngle - oldRotationAngle);
2290 rotationAngle += oldRotationAngle;
2291 //c += cos(oldRotationAngle);
2292 //s += sin(oldRotationAngle);
2293 //rotationAngle += oldRotationAngle;
2294 //c %= 2.0 * PI;
2295 //s %= 2.0 * PI;
2296 //rotationAngle %= 2.0 * PI;//*/
2297
2298 //This is wrong... Hmm...
2299 //It seems to shear the texture instead of rotating it... !!! FIX !!!
2300 // Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-)
2301
2302 // Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!!
2303
2304 // See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from...
2305
2306                         tm.coords[0][0] =  c;
2307                         tm.coords[0][1] =  s;
2308                         tm.coords[1][0] = -s;
2309                         tm.coords[1][1] =  c;
2310 //It doesn't work anymore... Dunno why...
2311 //tm.coords[0][2] = -trans.x();                 // This works!!! Yeah!!!
2312 //tm.coords[1][2] = -trans.y();
2313 //nope.
2314 //tm.coords[0][2] = rotationPoint.x();  // This works, but strangely...
2315 //tm.coords[1][2] = rotationPoint.y();
2316 //tm.coords[0][2] = 0;// center.x() / 2.0f;
2317 //tm.coords[1][2] = 0;// center.y() / 2.0f;
2318 //No.
2319 //tm.coords[0][2] = -(center.x() * textureSize.x());
2320 //tm.coords[1][2] = -(center.y() * textureSize.y());
2321 //Eh? No, but seems to be getting closer...
2322 /*float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2323 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2324 tm.coords[0][2] = -c * center.x() - s * center.y() + ptx;
2325 tm.coords[1][2] =  s * center.x() - c * center.x() + pty;//*/
2326 //Kinda works, but center drifts around on non-square textures...
2327 /*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x();
2328 tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/
2329 //Rotates correctly, but not around the actual center of the face's points...
2330 /*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y();
2331 tm.coords[1][2] =  s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/
2332 //Yes!!!
2333                         tm.coords[0][2] = (-c * center.x() * textureSize.x() - s * center.y() * textureSize.y()) + center.x() * textureSize.x();
2334                         tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y()) + center.y() * textureSize.y();//*/
2335 //This doesn't work...
2336 //And this is the wrong place for this anyway (I'm pretty sure).
2337 /*tm.coords[0][2] += oldCenter.x();
2338 tm.coords[1][2] += oldCenter.y();//*/
2339                         UpdateControlPoints(); // will cause a redraw
2340                 }
2341
2342                 return true;
2343         }
2344         else                                                                    // Check for widget mouseovers
2345         {
2346                 Vector2 tran;
2347                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2348                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2349                 // Translate nx/y to the "center" point...
2350                 nx -= center.x();
2351                 ny -= center.y();
2352                 ny = -ny;       // Flip Y-axis so that increasing numbers move up
2353
2354                 tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny;
2355                 tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny;
2356 //This doesn't seem to generate a valid distance from the center--for some reason it
2357 //calculates a fixed number every time
2358 //Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE]
2359                 float dist = sqrt((nx * nx) + (ny * ny));
2360                 // Normalize to the 2.0 = height standard (for now)
2361 //globalOutputStream() << "--> Distance before: " << dist;
2362                 dist = dist * 2.0f / extents.height();
2363 //globalOutputStream() << ". After: " << dist;
2364                 tran.x() = tran.x() * 2.0f / extents.height();
2365                 tran.y() = tran.y() * 2.0f / extents.height();
2366 //globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n";
2367
2368 //Let's try this instead...
2369 //Interesting! It seems that e->x/y are rotated
2370 //(no, they're not--the TM above is what's doing it...)
2371 nx = ((e->x / windowSize.y()) * 2.0f) - (windowSize.x() / windowSize.y());
2372 ny = ((e->y / windowSize.y()) * 2.0f) - (windowSize.y() / windowSize.y());
2373 ny = -ny;
2374 //Cool! It works! Now just need to do rotation...
2375
2376                 rotating = translatingX = translatingY = scalingX = scalingY
2377                         = resizingX = resizingY = false;
2378
2379                 if (dist < (gridRadius * 0.16f))
2380                 {
2381                         translatingX = translatingY = true;
2382                 }
2383                 else if (dist > (gridRadius * 0.16f) && dist < (gridRadius * 1.10f)
2384                         && fabs(ny) < (gridRadius * 0.05f) && nx > 0)
2385                 {
2386                         translatingX = true;
2387                 }
2388                 else if (dist > (gridRadius * 0.16f) && dist < (gridRadius * 1.10f)
2389                         && fabs(nx) < (gridRadius * 0.05f) && ny > 0)
2390                 {
2391                         translatingY = true;
2392                 }
2393                 // Should tighten up the angle on this, or put this test after the axis tests...
2394                 else if (tran.x() > 0 && tran.y() > 0
2395                         && (dist > (gridRadius * 0.82f) && dist < (gridRadius * 0.98f)))
2396                 {
2397                         rotating = true;
2398                 }
2399
2400           queueDraw();
2401
2402                 return true;
2403         }
2404
2405         return false;
2406 }
2407
2408 //It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!!
2409 //This is still wrong... Prolly need to do something with the oldScaleX/Y stuff...
2410 void flipX(GtkToggleButton *, gpointer)
2411 {
2412 //      globalOutputStream() << "--> Flip X...\n";
2413         //Shamus:
2414 //      SurfaceInspector_GetSelectedBPTexdef();         // Refresh g_selectedBrushPrimitTexdef...
2415 //      tm.coords[0][0] = -tm.coords[0][0];
2416 //      tm.coords[1][0] = -tm.coords[1][0];
2417 //      tm.coords[0][0] = -tm.coords[0][0];                     // This should be correct now...Nope.
2418 //      tm.coords[1][1] = -tm.coords[1][1];
2419         tm.coords[0][0] = -tm.coords[0][0];                     // This should be correct now...
2420         tm.coords[1][0] = -tm.coords[1][0];
2421 //      tm.coords[2][0] = -tm.coords[2][0];//wil wok? no.
2422         UpdateControlPoints();
2423 }
2424
2425 void flipY(GtkToggleButton *, gpointer)
2426 {
2427 //      globalOutputStream() << "--> Flip Y...\n";
2428 //      tm.coords[0][1] = -tm.coords[0][1];
2429 //      tm.coords[1][1] = -tm.coords[1][1];
2430 //      tm.coords[0][1] = -tm.coords[0][1];                     // This should be correct now...Nope.
2431 //      tm.coords[1][0] = -tm.coords[1][0];
2432         tm.coords[0][1] = -tm.coords[0][1];                     // This should be correct now...
2433         tm.coords[1][1] = -tm.coords[1][1];
2434 //      tm.coords[2][1] = -tm.coords[2][1];//wil wok? no.
2435         UpdateControlPoints();
2436 }
2437
2438 } // end namespace TexTool
2439
2440 #endif