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