2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
25 // Leonardo Zide (leo@lokigames.com)
28 #include "patchdialog.h"
32 #include "debugging/debugging.h"
34 #include <gtk/gtkvbox.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkframe.h>
37 #include <gtk/gtklabel.h>
38 #include <gtk/gtktable.h>
39 #include <gtk/gtkcombobox.h>
40 #include <gtk/gtkbutton.h>
41 #include <gtk/gtkspinbutton.h>
42 #include <gtk/gtkcheckbutton.h>
44 #include "gtkutil/idledraw.h"
45 #include "gtkutil/entry.h"
46 #include "gtkutil/button.h"
47 #include "gtkutil/nonmodal.h"
50 #include "mainframe.h"
51 #include "patchmanip.h"
54 #include "preferences.h"
55 #include "signal/isignal.h"
58 #include <gdk/gdkkeysyms.h>
60 // the increment we are using for the patch inspector (this is saved in the prefs)
77 pi_globals_t g_pi_globals;
79 class PatchFixedSubdivisions
82 PatchFixedSubdivisions() : m_enabled(false), m_x(0), m_y(0)
85 PatchFixedSubdivisions(bool enabled, std::size_t x, std::size_t y) : m_enabled(enabled), m_x(x), m_y(y)
93 void Patch_getFixedSubdivisions(const Patch& patch, PatchFixedSubdivisions& subdivisions)
95 subdivisions.m_enabled = patch.m_patchDef3;
96 subdivisions.m_x = patch.m_subdivisions_x;
97 subdivisions.m_y = patch.m_subdivisions_y;
100 const std::size_t MAX_PATCH_SUBDIVISIONS = 32;
102 void Patch_setFixedSubdivisions(Patch& patch, const PatchFixedSubdivisions& subdivisions)
106 patch.m_patchDef3 = subdivisions.m_enabled;
107 patch.m_subdivisions_x = subdivisions.m_x;
108 patch.m_subdivisions_y = subdivisions.m_y;
110 if(patch.m_subdivisions_x == 0)
112 patch.m_subdivisions_x = 4;
114 else if(patch.m_subdivisions_x > MAX_PATCH_SUBDIVISIONS)
116 patch.m_subdivisions_x = MAX_PATCH_SUBDIVISIONS;
118 if(patch.m_subdivisions_y == 0)
120 patch.m_subdivisions_y = 4;
122 else if(patch.m_subdivisions_y > MAX_PATCH_SUBDIVISIONS)
124 patch.m_subdivisions_y = MAX_PATCH_SUBDIVISIONS;
128 Patch_textureChanged();
129 patch.controlPointsChanged();
132 class PatchGetFixedSubdivisions
134 PatchFixedSubdivisions& m_subdivisions;
136 PatchGetFixedSubdivisions(PatchFixedSubdivisions& subdivisions) : m_subdivisions(subdivisions)
139 void operator()(Patch& patch)
141 Patch_getFixedSubdivisions(patch, m_subdivisions);
146 void Scene_PatchGetFixedSubdivisions(PatchFixedSubdivisions& subdivisions)
149 if(GlobalSelectionSystem().countSelected() != 0)
151 Patch* patch = Node_getPatch(GlobalSelectionSystem().ultimateSelected().path().top());
154 Patch_getFixedSubdivisions(*patch, subdivisions);
158 Scene_forEachVisibleSelectedPatch(PatchGetFixedSubdivisions(subdivisions));
162 class PatchSetFixedSubdivisions
164 const PatchFixedSubdivisions& m_subdivisions;
166 PatchSetFixedSubdivisions(const PatchFixedSubdivisions& subdivisions) : m_subdivisions(subdivisions)
169 void operator()(Patch& patch) const
171 Patch_setFixedSubdivisions(patch, m_subdivisions);
175 void Scene_PatchSetFixedSubdivisions(const PatchFixedSubdivisions& subdivisions)
177 UndoableCommand command("patchSetFixedSubdivisions");
178 Scene_forEachVisibleSelectedPatch(PatchSetFixedSubdivisions(subdivisions));
181 typedef struct _GtkCheckButton GtkCheckButton;
186 GtkCheckButton* m_enabled;
187 GtkEntry* m_horizontal;
188 GtkEntry* m_vertical;
189 Subdivisions() : m_enabled(0), m_horizontal(0), m_vertical(0)
194 PatchFixedSubdivisions subdivisions;
195 Scene_PatchGetFixedSubdivisions(subdivisions);
197 toggle_button_set_active_no_signal(GTK_TOGGLE_BUTTON(m_enabled), subdivisions.m_enabled);
199 if(subdivisions.m_enabled)
201 entry_set_int(m_horizontal, static_cast<int>(subdivisions.m_x));
202 entry_set_int(m_vertical, static_cast<int>(subdivisions.m_y));
203 gtk_widget_set_sensitive(GTK_WIDGET(m_horizontal), TRUE);
204 gtk_widget_set_sensitive(GTK_WIDGET(m_vertical), TRUE);
208 gtk_entry_set_text(m_horizontal, "");
209 gtk_entry_set_text(m_vertical, "");
210 gtk_widget_set_sensitive(GTK_WIDGET(m_horizontal), FALSE);
211 gtk_widget_set_sensitive(GTK_WIDGET(m_vertical), FALSE);
218 typedef MemberCaller<Subdivisions, &Subdivisions::cancel> CancelCaller;
221 Scene_PatchSetFixedSubdivisions(
222 PatchFixedSubdivisions(
223 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_enabled)) != FALSE,
224 static_cast<std::size_t>(entry_get_int(m_horizontal)),
225 static_cast<std::size_t>(entry_get_int(m_vertical))
229 typedef MemberCaller<Subdivisions, &Subdivisions::apply> ApplyCaller;
230 static void applyGtk(GtkToggleButton* toggle, Subdivisions* self)
236 class PatchInspector : public Dialog
238 GtkWindow* BuildDialog();
239 Subdivisions m_subdivisions;
240 NonModalEntry m_horizontalSubdivisionsEntry;
241 NonModalEntry m_verticalSubdivisionsEntry;
244 WindowPositionTracker m_position_tracker;
248 CopiedString m_strName;
261 GtkComboBox *m_pRowCombo;
262 GtkComboBox *m_pColCombo;
263 std::size_t m_countRows;
264 std::size_t m_countCols;
266 // turn on/off processing of the "changed" "value_changed" messages
267 // (need to turn off when we are feeding data in)
268 // NOTE: much more simple than blocking signals
269 bool m_bListenChanged;
272 m_horizontalSubdivisionsEntry(Subdivisions::ApplyCaller(m_subdivisions), Subdivisions::CancelCaller(m_subdivisions)),
273 m_verticalSubdivisionsEntry(Subdivisions::ApplyCaller(m_subdivisions), Subdivisions::CancelCaller(m_subdivisions)),
274 m_idleDraw(MemberCaller<PatchInspector, &PatchInspector::GetPatchInfo>(*this))
286 m_bListenChanged = true;
288 m_position_tracker.setPosition(c_default_window_pos);
293 return GTK_WIDGET_VISIBLE(GetWidget());
296 // void UpdateInfo();
297 // void SetPatchInfo();
299 void UpdateSpinners(bool bUp, int nID);
300 // read the current patch on map and initialize m_fX m_fY accordingly
301 void UpdateRowColInfo();
302 // sync the dialog our internal data structures
303 // depending on the flag it will read or write
304 // we use m_nCol m_nRow m_fX m_fY m_fZ m_fS m_fT m_strName
305 // (NOTE: this doesn't actually commit stuff to the map or read from it)
310 PatchInspector g_PatchInspector;
312 void PatchInspector_constructWindow(GtkWindow* main_window)
314 g_PatchInspector.m_parent = main_window;
315 g_PatchInspector.Create();
317 void PatchInspector_destroyWindow()
319 g_PatchInspector.Destroy();
322 void PatchInspector_queueDraw()
324 if(g_PatchInspector.visible())
326 g_PatchInspector.m_idleDraw.queueDraw();
330 void DoPatchInspector()
332 g_PatchInspector.GetPatchInfo();
333 if (!g_PatchInspector.visible())
334 g_PatchInspector.ShowDlg();
337 void PatchInspector_toggleShown()
339 if (g_PatchInspector.visible())
341 g_PatchInspector.m_Patch = 0;
342 g_PatchInspector.HideDlg();
349 // =============================================================================
352 // memorize the current state (that is don't try to undo our do before changing something else)
353 static void OnApply (GtkWidget *widget, gpointer data)
355 g_PatchInspector.exportData();
356 if (g_PatchInspector.m_Patch != 0)
358 UndoableCommand command("patchSetTexture");
359 g_PatchInspector.m_Patch->undoSave();
361 if (!texdef_name_valid(g_PatchInspector.m_strName.c_str()))
363 globalErrorStream() << "invalid texture name '" << g_PatchInspector.m_strName.c_str() << "'\n";
364 g_PatchInspector.m_strName = texdef_name_default();
366 g_PatchInspector.m_Patch->SetShader(g_PatchInspector.m_strName.c_str());
368 std::size_t r = g_PatchInspector.m_nRow;
369 std::size_t c = g_PatchInspector.m_nCol;
370 if(r < g_PatchInspector.m_Patch->getHeight()
371 && c < g_PatchInspector.m_Patch->getWidth())
373 PatchControl& p = g_PatchInspector.m_Patch->ctrlAt(r,c);
374 p.m_vertex[0] = g_PatchInspector.m_fX;
375 p.m_vertex[1] = g_PatchInspector.m_fY;
376 p.m_vertex[2] = g_PatchInspector.m_fZ;
377 p.m_texcoord[0] = g_PatchInspector.m_fS;
378 p.m_texcoord[1] = g_PatchInspector.m_fT;
379 g_PatchInspector.m_Patch->controlPointsChanged();
384 static void OnSelchangeComboColRow (GtkWidget *widget, gpointer data)
386 if (!g_PatchInspector.m_bListenChanged)
388 // retrieve the current m_nRow and m_nCol, other params are not relevant
389 g_PatchInspector.exportData();
390 // read the changed values ourselves
391 g_PatchInspector.UpdateRowColInfo();
392 // now reflect our changes
393 g_PatchInspector.importData();
396 class PatchSetTextureRepeat
400 PatchSetTextureRepeat(float s, float t) : m_s(s), m_t(t)
403 void operator()(Patch& patch) const
405 patch.SetTextureRepeat(m_s, m_t);
409 void Scene_PatchTileTexture_Selected(scene::Graph& graph, float s, float t)
411 Scene_forEachVisibleSelectedPatch(PatchSetTextureRepeat(s, t));
415 static void OnBtnPatchdetails (GtkWidget *widget, gpointer data)
417 UndoableCommand command("patchCapTexture");
419 Scene_PatchCapTexture_Selected(GlobalSceneGraph());
422 static void OnBtnPatchfit (GtkWidget *widget, gpointer data)
424 UndoableCommand command("patchFitTexture");
426 Scene_PatchTileTexture_Selected(GlobalSceneGraph(), 1, 1);
429 static void OnBtnPatchnatural (GtkWidget *widget, gpointer data)
431 UndoableCommand command("patchNaturalTexture");
433 Scene_PatchNaturalTexture_Selected(GlobalSceneGraph());
436 static void OnBtnPatchreset (GtkWidget *widget, gpointer data)
439 if (DoTextureLayout (&fx, &fy) == eIDOK)
441 UndoableCommand command("patchTileTexture");
442 Scene_PatchTileTexture_Selected(GlobalSceneGraph(), fx, fy);
446 struct PatchRotateTexture
450 PatchRotateTexture(float angle) : m_angle(angle)
453 void operator()(Patch& patch) const
455 patch.RotateTexture(m_angle);
459 void Scene_PatchRotateTexture_Selected(scene::Graph& graph, float angle)
461 Scene_forEachVisibleSelectedPatch(PatchRotateTexture(angle));
464 class PatchScaleTexture
468 PatchScaleTexture(float s, float t) : m_s(s), m_t(t)
471 void operator()(Patch& patch) const
473 patch.ScaleTexture(m_s, m_t);
477 float Patch_convertScale(float scale)
490 void Scene_PatchScaleTexture_Selected(scene::Graph& graph, float s, float t)
492 Scene_forEachVisibleSelectedPatch(PatchScaleTexture(Patch_convertScale(s), Patch_convertScale(t)));
495 class PatchTranslateTexture
499 PatchTranslateTexture(float s, float t)
503 void operator()(Patch& patch) const
505 patch.TranslateTexture(m_s, m_t);
509 void Scene_PatchTranslateTexture_Selected(scene::Graph& graph, float s, float t)
511 Scene_forEachVisibleSelectedPatch(PatchTranslateTexture(s, t));
514 static void OnSpinChanged (GtkAdjustment *adj, gpointer data)
519 td.scale[0] = td.scale[1] = 0;
520 td.shift[0] = td.shift[1] = 0;
525 if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "hshift_adj"))
527 g_pi_globals.shift[0] = static_cast<float>(atof (gtk_entry_get_text (GTK_ENTRY (data))));
530 td.shift[0] = g_pi_globals.shift[0];
532 td.shift[0] = -g_pi_globals.shift[0];
534 else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "vshift_adj"))
536 g_pi_globals.shift[1] = static_cast<float>(atof (gtk_entry_get_text (GTK_ENTRY (data))));
539 td.shift[1] = g_pi_globals.shift[1];
541 td.shift[1] = -g_pi_globals.shift[1];
543 else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "hscale_adj"))
545 g_pi_globals.scale[0] = static_cast<float>(atof (gtk_entry_get_text (GTK_ENTRY (data))));
546 if (g_pi_globals.scale[0] == 0.0f)
549 td.scale[0] = g_pi_globals.scale[0];
551 td.scale[0] = -g_pi_globals.scale[0];
553 else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "vscale_adj"))
555 g_pi_globals.scale[1] = static_cast<float>(atof (gtk_entry_get_text (GTK_ENTRY (data))));
556 if (g_pi_globals.scale[1] == 0.0f)
559 td.scale[1] = g_pi_globals.scale[1];
561 td.scale[1] = -g_pi_globals.scale[1];
563 else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "rotate_adj"))
565 g_pi_globals.rotate = static_cast<float>(atof (gtk_entry_get_text (GTK_ENTRY (data))));
568 td.rotate = g_pi_globals.rotate;
570 td.rotate = -g_pi_globals.rotate;
575 // will scale shift rotate the patch accordingly
578 if (td.shift[0] || td.shift[1])
580 UndoableCommand command("patchTranslateTexture");
581 Scene_PatchTranslateTexture_Selected (GlobalSceneGraph(), td.shift[0], td.shift[1]);
583 else if (td.scale[0] || td.scale[1])
585 UndoableCommand command("patchScaleTexture");
586 Scene_PatchScaleTexture_Selected (GlobalSceneGraph(), td.scale[0], td.scale[1]);
590 UndoableCommand command("patchRotateTexture");
591 Scene_PatchRotateTexture_Selected (GlobalSceneGraph(), td.rotate);
594 // update the point-by-point view
595 OnSelchangeComboColRow(0,0);
598 static gint OnDialogKey (GtkWidget* widget, GdkEventKey* event, gpointer data)
600 if (event->keyval == GDK_Return)
605 else if (event->keyval == GDK_Escape)
607 g_PatchInspector.GetPatchInfo();
613 // =============================================================================
614 // PatchInspector class
616 GtkWindow* PatchInspector::BuildDialog()
618 GtkWindow* window = create_floating_window("Patch Properties", m_parent);
620 m_position_tracker.connect(window);
622 global_accel_connect_window(window);
624 window_connect_focus_in_clear_focus_widget(window);
628 GtkVBox* vbox = GTK_VBOX(gtk_vbox_new(FALSE, 5));
629 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
630 gtk_widget_show(GTK_WIDGET(vbox));
631 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
633 GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(FALSE, 5));
634 gtk_widget_show(GTK_WIDGET(hbox));
635 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), TRUE, TRUE, 0);
637 GtkVBox* vbox2 = GTK_VBOX(gtk_vbox_new(FALSE, 0));
638 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
639 gtk_widget_show(GTK_WIDGET(vbox2));
640 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), TRUE, TRUE, 0);
642 GtkFrame* frame = GTK_FRAME(gtk_frame_new("Details"));
643 gtk_widget_show(GTK_WIDGET(frame));
644 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(frame), TRUE, TRUE, 0);
646 GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 5));
647 gtk_container_set_border_width(GTK_CONTAINER(vbox3), 5);
648 gtk_widget_show(GTK_WIDGET(vbox3));
649 gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3));
651 GtkTable* table = GTK_TABLE(gtk_table_new(2, 2, FALSE));
652 gtk_widget_show(GTK_WIDGET(table));
653 gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0);
654 gtk_table_set_row_spacings(table, 5);
655 gtk_table_set_col_spacings(table, 5);
657 GtkLabel* label = GTK_LABEL(gtk_label_new("Row:"));
658 gtk_widget_show(GTK_WIDGET(label));
659 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1,
660 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
661 (GtkAttachOptions)(0), 0, 0);
664 GtkLabel* label = GTK_LABEL(gtk_label_new("Column:"));
665 gtk_widget_show(GTK_WIDGET(label));
666 gtk_table_attach(table, GTK_WIDGET(label), 1, 2, 0, 1,
667 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
668 (GtkAttachOptions)(0), 0, 0);
671 GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
672 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(OnSelchangeComboColRow), this);
673 AddDialogData(*combo, m_nRow);
675 gtk_widget_show(GTK_WIDGET(combo));
676 gtk_table_attach(table, GTK_WIDGET(combo), 0, 1, 1, 2,
677 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
678 (GtkAttachOptions)(0), 0, 0);
679 gtk_widget_set_usize(GTK_WIDGET(combo), 60, -1);
684 GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
685 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(OnSelchangeComboColRow), this);
686 AddDialogData(*combo, m_nCol);
688 gtk_widget_show(GTK_WIDGET(combo));
689 gtk_table_attach(table, GTK_WIDGET(combo), 1, 2, 1, 2,
690 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
691 (GtkAttachOptions)(0), 0, 0);
692 gtk_widget_set_usize(GTK_WIDGET(combo), 60, -1);
696 GtkTable* table = GTK_TABLE(gtk_table_new(5, 2, FALSE));
697 gtk_widget_show(GTK_WIDGET(table));
698 gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0);
699 gtk_table_set_row_spacings(table, 5);
700 gtk_table_set_col_spacings(table, 5);
702 GtkLabel* label = GTK_LABEL(gtk_label_new("X:"));
703 gtk_widget_show(GTK_WIDGET(label));
704 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1,
705 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
706 (GtkAttachOptions)(0), 0, 0);
709 GtkLabel* label = GTK_LABEL(gtk_label_new("Y:"));
710 gtk_widget_show(GTK_WIDGET(label));
711 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2,
712 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
713 (GtkAttachOptions)(0), 0, 0);
716 GtkLabel* label = GTK_LABEL(gtk_label_new("Z:"));
717 gtk_widget_show(GTK_WIDGET(label));
718 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 2, 3,
719 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
720 (GtkAttachOptions)(0), 0, 0);
723 GtkLabel* label = GTK_LABEL(gtk_label_new("S:"));
724 gtk_widget_show(GTK_WIDGET(label));
725 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 3, 4,
726 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
727 (GtkAttachOptions)(0), 0, 0);
730 GtkLabel* label = GTK_LABEL(gtk_label_new("T:"));
731 gtk_widget_show(GTK_WIDGET(label));
732 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 4, 5,
733 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
734 (GtkAttachOptions)(0), 0, 0);
737 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
738 gtk_widget_show(GTK_WIDGET(entry));
739 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
740 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
741 (GtkAttachOptions)(0), 0, 0);
742 AddDialogData(*entry, m_fX);
744 g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0);
747 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
748 gtk_widget_show(GTK_WIDGET(entry));
749 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
750 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
751 (GtkAttachOptions)(0), 0, 0);
752 AddDialogData(*entry, m_fY);
754 g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0);
757 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
758 gtk_widget_show(GTK_WIDGET(entry));
759 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 2, 3,
760 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
761 (GtkAttachOptions)(0), 0, 0);
762 AddDialogData(*entry, m_fZ);
764 g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0);
767 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
768 gtk_widget_show(GTK_WIDGET(entry));
769 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 3, 4,
770 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
771 (GtkAttachOptions)(0), 0, 0);
772 AddDialogData(*entry, m_fS);
774 g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0);
777 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
778 gtk_widget_show(GTK_WIDGET(entry));
779 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 4, 5,
780 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
781 (GtkAttachOptions)(0), 0, 0);
782 AddDialogData(*entry, m_fT);
784 g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0);
788 if(g_pGameDescription->mGameType == "doom3")
790 GtkFrame* frame = GTK_FRAME(gtk_frame_new("Tesselation"));
791 gtk_widget_show(GTK_WIDGET(frame));
792 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(frame), TRUE, TRUE, 0);
794 GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 5));
795 gtk_container_set_border_width(GTK_CONTAINER(vbox3), 5);
796 gtk_widget_show(GTK_WIDGET(vbox3));
797 gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3));
799 GtkTable* table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
800 gtk_widget_show(GTK_WIDGET(table));
801 gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0);
802 gtk_table_set_row_spacings(table, 5);
803 gtk_table_set_col_spacings(table, 5);
805 GtkLabel* label = GTK_LABEL(gtk_label_new("Fixed"));
806 gtk_widget_show(GTK_WIDGET(label));
807 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1,
808 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
809 (GtkAttachOptions)(0), 0, 0);
812 GtkCheckButton* check = GTK_CHECK_BUTTON(gtk_check_button_new());
813 gtk_widget_show(GTK_WIDGET(check));
814 gtk_table_attach(table, GTK_WIDGET(check), 1, 2, 0, 1,
815 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
816 (GtkAttachOptions)(0), 0, 0);
817 m_subdivisions.m_enabled = check;
818 guint handler_id = g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(&Subdivisions::applyGtk), &m_subdivisions);
819 g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id));
822 GtkLabel* label = GTK_LABEL(gtk_label_new("Horizontal"));
823 gtk_widget_show(GTK_WIDGET(label));
824 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2,
825 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
826 (GtkAttachOptions)(0), 0, 0);
829 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
830 gtk_widget_show(GTK_WIDGET(entry));
831 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
832 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
833 (GtkAttachOptions)(0), 0, 0);
834 m_subdivisions.m_horizontal = entry;
835 m_horizontalSubdivisionsEntry.connect(entry);
838 GtkLabel* label = GTK_LABEL(gtk_label_new("Vertical"));
839 gtk_widget_show(GTK_WIDGET(label));
840 gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 2, 3,
841 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
842 (GtkAttachOptions)(0), 0, 0);
845 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
846 gtk_widget_show(GTK_WIDGET(entry));
847 gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 2, 3,
848 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
849 (GtkAttachOptions)(0), 0, 0);
850 m_subdivisions.m_vertical = entry;
851 m_verticalSubdivisionsEntry.connect(entry);
858 GtkFrame* frame = GTK_FRAME(gtk_frame_new("Texturing"));
859 gtk_widget_show(GTK_WIDGET(frame));
860 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(frame), TRUE, TRUE, 0);
862 GtkVBox* vbox2 = GTK_VBOX(gtk_vbox_new(FALSE, 5));
863 gtk_widget_show(GTK_WIDGET(vbox2));
864 gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox2));
865 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 5);
867 GtkLabel* label = GTK_LABEL(gtk_label_new("Name:"));
868 gtk_widget_show(GTK_WIDGET(label));
869 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(label), TRUE, TRUE, 0);
870 gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
871 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
874 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
875 // gtk_entry_set_editable (GTK_ENTRY (entry), false);
876 gtk_widget_show(GTK_WIDGET(entry));
877 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(entry), TRUE, TRUE, 0);
878 AddDialogData(*entry, m_strName);
880 g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0);
883 GtkTable* table = GTK_TABLE(gtk_table_new(5, 3, FALSE));
884 gtk_widget_show(GTK_WIDGET(table));
885 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(table), TRUE, TRUE, 0);
886 gtk_table_set_row_spacings(table, 5);
887 gtk_table_set_col_spacings(table, 5);
889 GtkLabel* label = GTK_LABEL(gtk_label_new("Horizontal Shift Step"));
890 gtk_widget_show(GTK_WIDGET(label));
891 gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 0, 1,
892 (GtkAttachOptions)(GTK_FILL),
893 (GtkAttachOptions)(0), 0, 0);
894 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
897 GtkLabel* label = GTK_LABEL(gtk_label_new("Vertical Shift Step"));
898 gtk_widget_show(GTK_WIDGET(label));
899 gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 1, 2,
900 (GtkAttachOptions)(GTK_FILL),
901 (GtkAttachOptions)(0), 0, 0);
902 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
905 GtkLabel* label = GTK_LABEL(gtk_label_new("Horizontal Stretch Step"));
906 gtk_widget_show(GTK_WIDGET(label));
907 gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 2, 3,
908 (GtkAttachOptions)(GTK_FILL),
909 (GtkAttachOptions)(0), 0, 0);
910 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
913 GtkLabel* label = GTK_LABEL(gtk_label_new("Vertical Stretch Step"));
914 gtk_widget_show(GTK_WIDGET(label));
915 gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 3, 4,
916 (GtkAttachOptions)(GTK_FILL),
917 (GtkAttachOptions)(0), 0, 0);
918 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
921 GtkLabel* label = GTK_LABEL(gtk_label_new("Rotate Step"));
922 gtk_widget_show(GTK_WIDGET(label));
923 gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 4, 5,
924 (GtkAttachOptions)(GTK_FILL),
925 (GtkAttachOptions)(0), 0, 0);
926 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
929 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
930 gtk_widget_show(GTK_WIDGET(entry));
931 gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 0, 1,
932 (GtkAttachOptions)(GTK_FILL),
933 (GtkAttachOptions)(0), 0, 0);
934 gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
935 g_object_set_data(G_OBJECT(window), "hshift_entry", entry);
936 // we fill in this data, if no patch is selected the widgets are unmodified when the inspector is raised
937 // so we need to have at least one initialisation somewhere
938 entry_set_float(entry, g_pi_globals.shift[0]);
940 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 1, 1, 1));
941 g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry);
942 g_object_set_data(G_OBJECT(window), "hshift_adj", adj);
944 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
945 gtk_widget_show(GTK_WIDGET(spin));
946 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 0, 1,
947 (GtkAttachOptions)(0),
948 (GtkAttachOptions)(0), 0, 0);
949 gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2);
950 GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS);
953 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
954 gtk_widget_show(GTK_WIDGET(entry));
955 gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 1, 2,
956 (GtkAttachOptions)(GTK_FILL),
957 (GtkAttachOptions)(0), 0, 0);
958 gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
959 entry_set_float(entry, g_pi_globals.shift[1]);
961 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 1, 1, 1));
962 g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry);
963 g_object_set_data(G_OBJECT(window), "vshift_adj", adj);
965 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
966 gtk_widget_show(GTK_WIDGET(spin));
967 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 1, 2,
968 (GtkAttachOptions)(0),
969 (GtkAttachOptions)(0), 0, 0);
970 gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2);
971 GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS);
974 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
975 gtk_widget_show(GTK_WIDGET(entry));
976 gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 2, 3,
977 (GtkAttachOptions)(GTK_FILL),
978 (GtkAttachOptions)(0), 0, 0);
979 gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
980 entry_set_float(entry, g_pi_globals.scale[0]);
982 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -1000, 1000, 1, 1, 1));
983 g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry);
984 g_object_set_data(G_OBJECT(window), "hscale_adj", adj);
986 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
987 gtk_widget_show(GTK_WIDGET(spin));
988 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 2, 3,
989 (GtkAttachOptions)(0),
990 (GtkAttachOptions)(0), 0, 0);
991 gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2);
992 GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS);
995 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
996 gtk_widget_show(GTK_WIDGET(entry));
997 gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 3, 4,
998 (GtkAttachOptions)(GTK_FILL),
999 (GtkAttachOptions)(0), 0, 0);
1000 gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
1001 entry_set_float(entry, g_pi_globals.scale[1]);
1003 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -1000, 1000, 1, 1, 1));
1004 g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry);
1005 g_object_set_data(G_OBJECT(window), "vscale_adj", adj);
1007 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
1008 gtk_widget_show(GTK_WIDGET(spin));
1009 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 3, 4,
1010 (GtkAttachOptions)(0),
1011 (GtkAttachOptions)(0), 0, 0);
1012 gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2);
1013 GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS);
1016 GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
1017 gtk_widget_show(GTK_WIDGET(entry));
1018 gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 4, 5,
1019 (GtkAttachOptions)(GTK_FILL),
1020 (GtkAttachOptions)(0), 0, 0);
1021 gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2);
1022 entry_set_float(entry, g_pi_globals.rotate);
1024 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -1000, 1000, 1, 1, 1)); // NOTE: Arnout - this really should be 360 but can't change it anymore as it could break existing maps
1025 g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry);
1026 g_object_set_data(G_OBJECT(window), "rotate_adj", adj);
1028 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
1029 gtk_widget_show(GTK_WIDGET(spin));
1030 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 4, 5,
1031 (GtkAttachOptions)(0),
1032 (GtkAttachOptions)(0), 0, 0);
1033 gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2);
1034 GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS);
1037 GtkHBox* hbox2 = GTK_HBOX(gtk_hbox_new(TRUE, 5));
1038 gtk_widget_show(GTK_WIDGET(hbox2));
1039 gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(hbox2), TRUE, FALSE, 0);
1041 GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("CAP"));
1042 gtk_widget_show(GTK_WIDGET(button));
1043 gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0);
1044 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchdetails), 0);
1045 gtk_widget_set_usize(GTK_WIDGET(button), 60, -1);
1048 GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("Set..."));
1049 gtk_widget_show(GTK_WIDGET(button));
1050 gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0);
1051 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchreset), 0);
1052 gtk_widget_set_usize(GTK_WIDGET(button), 60, -1);
1055 GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("Natural"));
1056 gtk_widget_show(GTK_WIDGET(button));
1057 gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0);
1058 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchnatural), 0);
1059 gtk_widget_set_usize(GTK_WIDGET(button), 60, -1);
1062 GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("Fit"));
1063 gtk_widget_show(GTK_WIDGET(button));
1064 gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0);
1065 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchfit), 0);
1066 gtk_widget_set_usize(GTK_WIDGET(button), 60, -1);
1076 // sync the dialog our internal data structures
1077 void PatchInspector::exportData()
1079 m_bListenChanged = false;
1080 Dialog::exportData();
1081 m_bListenChanged = true;
1083 void PatchInspector::importData()
1085 m_bListenChanged = false;
1086 Dialog::importData();
1087 m_bListenChanged = true;
1090 // read the map and feed in the stuff to the dialog box
1091 void PatchInspector::GetPatchInfo()
1093 if(g_pGameDescription->mGameType == "doom3")
1095 m_subdivisions.update();
1098 if(GlobalSelectionSystem().countSelected() == 0)
1104 m_Patch = Node_getPatch(GlobalSelectionSystem().ultimateSelected().path().top());
1109 m_strName = m_Patch->GetShader();
1111 // fill in the numbers for Row / Col selection
1112 m_bListenChanged = false;
1115 gtk_combo_box_set_active(m_pRowCombo, 0);
1117 for (std::size_t i = 0; i < m_countRows; ++i)
1119 gtk_combo_box_remove_text(m_pRowCombo, gint(m_countRows - i - 1));
1122 m_countRows = m_Patch->getHeight();
1123 for (std::size_t i = 0; i < m_countRows; ++i)
1126 sprintf(buffer, "%u", Unsigned(i));
1127 gtk_combo_box_append_text(m_pRowCombo, buffer);
1130 gtk_combo_box_set_active(m_pRowCombo, 0);
1134 gtk_combo_box_set_active(m_pColCombo, 0);
1136 for (std::size_t i = 0; i < m_countCols; ++i)
1138 gtk_combo_box_remove_text(m_pColCombo, gint(m_countCols - i - 1));
1141 m_countCols = m_Patch->getWidth();
1142 for (std::size_t i = 0; i < m_countCols; ++i)
1145 sprintf(buffer, "%u", Unsigned(i));
1146 gtk_combo_box_append_text(m_pColCombo, buffer);
1149 gtk_combo_box_set_active(m_pColCombo, 0);
1152 m_bListenChanged = true;
1157 //globalOutputStream() << "WARNING: no patch\n";
1159 // fill in our internal structs
1160 m_nRow = 0; m_nCol = 0;
1162 // now update the dialog box
1166 // read the current patch on map and initialize m_fX m_fY accordingly
1167 // NOTE: don't call UpdateData in there, it's not meant for
1168 void PatchInspector::UpdateRowColInfo()
1170 m_fX = m_fY = m_fZ = m_fS = m_fT = 0.0;
1174 // we rely on whatever active row/column has been set before we get called
1175 std::size_t r = m_nRow;
1176 std::size_t c = m_nCol;
1177 if(r < m_Patch->getHeight()
1178 && c < m_Patch->getWidth())
1180 const PatchControl& p = m_Patch->ctrlAt(r,c);
1181 m_fX = p.m_vertex[0];
1182 m_fY = p.m_vertex[1];
1183 m_fZ = p.m_vertex[2];
1184 m_fS = p.m_texcoord[0];
1185 m_fT = p.m_texcoord[1];
1191 void PatchInspector_SelectionChanged(const Selectable& selectable)
1193 PatchInspector_queueDraw();
1197 #include "preferencesystem.h"
1200 void PatchInspector_Construct()
1202 GlobalCommands_insert("PatchInspector", FreeCaller<PatchInspector_toggleShown>(), Accelerator('S', (GdkModifierType)GDK_SHIFT_MASK));
1204 GlobalPreferenceSystem().registerPreference("PatchWnd", WindowPositionTrackerImportStringCaller(g_PatchInspector.m_position_tracker), WindowPositionTrackerExportStringCaller(g_PatchInspector.m_position_tracker));
1205 GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Scale1", FloatImportStringCaller(g_pi_globals.scale[0]), FloatExportStringCaller(g_pi_globals.scale[0]));
1206 GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Scale2", FloatImportStringCaller(g_pi_globals.scale[1]), FloatExportStringCaller(g_pi_globals.scale[1]));
1207 GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Shift1", FloatImportStringCaller(g_pi_globals.shift[0]), FloatExportStringCaller(g_pi_globals.shift[0]));
1208 GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Shift2", FloatImportStringCaller(g_pi_globals.shift[1]), FloatExportStringCaller(g_pi_globals.shift[1]));
1209 GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Rotate", FloatImportStringCaller(g_pi_globals.rotate), FloatExportStringCaller(g_pi_globals.rotate));
1211 typedef FreeCaller1<const Selectable&, PatchInspector_SelectionChanged> PatchInspectorSelectionChangedCaller;
1212 GlobalSelectionSystem().addSelectionChangeCallback(PatchInspectorSelectionChangedCaller());
1213 typedef FreeCaller<PatchInspector_queueDraw> PatchInspectorQueueDrawCaller;
1214 Patch_addTextureChangedCallback(PatchInspectorQueueDrawCaller());
1216 void PatchInspector_Destroy()