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