/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // // Patch Dialog // // Leonardo Zide (leo@lokigames.com) // #include "patchdialog.h" #include "itexdef.h" #include "debugging/debugging.h" #include #include #include #include #include #include #include #include #include #include "gtkutil/idledraw.h" #include "gtkutil/entry.h" #include "gtkutil/button.h" #include "gtkutil/nonmodal.h" #include "dialog.h" #include "gtkdlgs.h" #include "mainframe.h" #include "patchmanip.h" #include "patch.h" #include "commands.h" #include "preferences.h" #include "signal/isignal.h" #include // the increment we are using for the patch inspector (this is saved in the prefs) struct pi_globals_t { float shift[2]; float scale[2]; float rotate; pi_globals_t() { shift[0] = 8.0f; shift[1] = 8.0f; scale[0] = 0.5f; scale[1] = 0.5f; rotate = 45.0f; } }; pi_globals_t g_pi_globals; class PatchFixedSubdivisions { public: PatchFixedSubdivisions() : m_enabled(false), m_x(0), m_y(0) { } PatchFixedSubdivisions(bool enabled, std::size_t x, std::size_t y) : m_enabled(enabled), m_x(x), m_y(y) { } bool m_enabled; std::size_t m_x; std::size_t m_y; }; void Patch_getFixedSubdivisions(const Patch& patch, PatchFixedSubdivisions& subdivisions) { subdivisions.m_enabled = patch.m_patchDef3; subdivisions.m_x = patch.m_subdivisions_x; subdivisions.m_y = patch.m_subdivisions_y; } const std::size_t MAX_PATCH_SUBDIVISIONS = 32; void Patch_setFixedSubdivisions(Patch& patch, const PatchFixedSubdivisions& subdivisions) { patch.undoSave(); patch.m_patchDef3 = subdivisions.m_enabled; patch.m_subdivisions_x = subdivisions.m_x; patch.m_subdivisions_y = subdivisions.m_y; if(patch.m_subdivisions_x == 0) { patch.m_subdivisions_x = 4; } else if(patch.m_subdivisions_x > MAX_PATCH_SUBDIVISIONS) { patch.m_subdivisions_x = MAX_PATCH_SUBDIVISIONS; } if(patch.m_subdivisions_y == 0) { patch.m_subdivisions_y = 4; } else if(patch.m_subdivisions_y > MAX_PATCH_SUBDIVISIONS) { patch.m_subdivisions_y = MAX_PATCH_SUBDIVISIONS; } SceneChangeNotify(); Patch_textureChanged(); patch.controlPointsChanged(); } class PatchGetFixedSubdivisions { PatchFixedSubdivisions& m_subdivisions; public: PatchGetFixedSubdivisions(PatchFixedSubdivisions& subdivisions) : m_subdivisions(subdivisions) { } void operator()(Patch& patch) { Patch_getFixedSubdivisions(patch, m_subdivisions); SceneChangeNotify(); } }; void Scene_PatchGetFixedSubdivisions(PatchFixedSubdivisions& subdivisions) { #if 1 if(GlobalSelectionSystem().countSelected() != 0) { Patch* patch = Node_getPatch(GlobalSelectionSystem().ultimateSelected().path().top()); if(patch != 0) { Patch_getFixedSubdivisions(*patch, subdivisions); } } #else Scene_forEachVisibleSelectedPatch(PatchGetFixedSubdivisions(subdivisions)); #endif } class PatchSetFixedSubdivisions { const PatchFixedSubdivisions& m_subdivisions; public: PatchSetFixedSubdivisions(const PatchFixedSubdivisions& subdivisions) : m_subdivisions(subdivisions) { } void operator()(Patch& patch) const { Patch_setFixedSubdivisions(patch, m_subdivisions); } }; void Scene_PatchSetFixedSubdivisions(const PatchFixedSubdivisions& subdivisions) { UndoableCommand command("patchSetFixedSubdivisions"); Scene_forEachVisibleSelectedPatch(PatchSetFixedSubdivisions(subdivisions)); } typedef struct _GtkCheckButton GtkCheckButton; class Subdivisions { public: GtkCheckButton* m_enabled; GtkEntry* m_horizontal; GtkEntry* m_vertical; Subdivisions() : m_enabled(0), m_horizontal(0), m_vertical(0) { } void update() { PatchFixedSubdivisions subdivisions; Scene_PatchGetFixedSubdivisions(subdivisions); toggle_button_set_active_no_signal(GTK_TOGGLE_BUTTON(m_enabled), subdivisions.m_enabled); if(subdivisions.m_enabled) { entry_set_int(m_horizontal, static_cast(subdivisions.m_x)); entry_set_int(m_vertical, static_cast(subdivisions.m_y)); gtk_widget_set_sensitive(GTK_WIDGET(m_horizontal), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(m_vertical), TRUE); } else { gtk_entry_set_text(m_horizontal, ""); gtk_entry_set_text(m_vertical, ""); gtk_widget_set_sensitive(GTK_WIDGET(m_horizontal), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(m_vertical), FALSE); } } void cancel() { update(); } typedef MemberCaller CancelCaller; void apply() { Scene_PatchSetFixedSubdivisions( PatchFixedSubdivisions( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_enabled)) != FALSE, static_cast(entry_get_int(m_horizontal)), static_cast(entry_get_int(m_vertical)) ) ); } typedef MemberCaller ApplyCaller; static void applyGtk(GtkToggleButton* toggle, Subdivisions* self) { self->apply(); } }; class PatchInspector : public Dialog { GtkWindow* BuildDialog(); Subdivisions m_subdivisions; NonModalEntry m_horizontalSubdivisionsEntry; NonModalEntry m_verticalSubdivisionsEntry; public: IdleDraw m_idleDraw; WindowPositionTracker m_position_tracker; Patch *m_Patch; CopiedString m_strName; float m_fS; float m_fT; float m_fX; float m_fY; float m_fZ; /* float m_fHScale; float m_fHShift; float m_fRotate; float m_fVScale; float m_fVShift; */ int m_nCol; int m_nRow; GtkComboBox *m_pRowCombo; GtkComboBox *m_pColCombo; std::size_t m_countRows; std::size_t m_countCols; // turn on/off processing of the "changed" "value_changed" messages // (need to turn off when we are feeding data in) // NOTE: much more simple than blocking signals bool m_bListenChanged; PatchInspector() : m_horizontalSubdivisionsEntry(Subdivisions::ApplyCaller(m_subdivisions), Subdivisions::CancelCaller(m_subdivisions)), m_verticalSubdivisionsEntry(Subdivisions::ApplyCaller(m_subdivisions), Subdivisions::CancelCaller(m_subdivisions)), m_idleDraw(MemberCaller(*this)) { m_fS = 0.0f; m_fT = 0.0f; m_fX = 0.0f; m_fY = 0.0f; m_fZ = 0.0f; m_nCol = 0; m_nRow = 0; m_countRows = 0; m_countCols = 0; m_Patch = 0; m_bListenChanged = true; m_position_tracker.setPosition(c_default_window_pos); } bool visible() { return GTK_WIDGET_VISIBLE(GetWidget()); } // void UpdateInfo(); // void SetPatchInfo(); void GetPatchInfo(); void UpdateSpinners(bool bUp, int nID); // read the current patch on map and initialize m_fX m_fY accordingly void UpdateRowColInfo(); // sync the dialog our internal data structures // depending on the flag it will read or write // we use m_nCol m_nRow m_fX m_fY m_fZ m_fS m_fT m_strName // (NOTE: this doesn't actually commit stuff to the map or read from it) void importData(); void exportData(); }; PatchInspector g_PatchInspector; void PatchInspector_constructWindow(GtkWindow* main_window) { g_PatchInspector.m_parent = main_window; g_PatchInspector.Create(); } void PatchInspector_destroyWindow() { g_PatchInspector.Destroy(); } void PatchInspector_queueDraw() { if(g_PatchInspector.visible()) { g_PatchInspector.m_idleDraw.queueDraw(); } } void DoPatchInspector() { g_PatchInspector.GetPatchInfo(); if (!g_PatchInspector.visible()) g_PatchInspector.ShowDlg(); } void PatchInspector_toggleShown() { if (g_PatchInspector.visible()) { g_PatchInspector.m_Patch = 0; g_PatchInspector.HideDlg(); } else DoPatchInspector(); } // ============================================================================= // static functions // memorize the current state (that is don't try to undo our do before changing something else) static void OnApply (GtkWidget *widget, gpointer data) { g_PatchInspector.exportData(); if (g_PatchInspector.m_Patch != 0) { UndoableCommand command("patchSetTexture"); g_PatchInspector.m_Patch->undoSave(); if (!texdef_name_valid(g_PatchInspector.m_strName.c_str())) { globalErrorStream() << "invalid texture name '" << g_PatchInspector.m_strName.c_str() << "'\n"; g_PatchInspector.m_strName = texdef_name_default(); } g_PatchInspector.m_Patch->SetShader(g_PatchInspector.m_strName.c_str()); std::size_t r = g_PatchInspector.m_nRow; std::size_t c = g_PatchInspector.m_nCol; if(r < g_PatchInspector.m_Patch->getHeight() && c < g_PatchInspector.m_Patch->getWidth()) { PatchControl& p = g_PatchInspector.m_Patch->ctrlAt(r,c); p.m_vertex[0] = g_PatchInspector.m_fX; p.m_vertex[1] = g_PatchInspector.m_fY; p.m_vertex[2] = g_PatchInspector.m_fZ; p.m_texcoord[0] = g_PatchInspector.m_fS; p.m_texcoord[1] = g_PatchInspector.m_fT; g_PatchInspector.m_Patch->controlPointsChanged(); } } } static void OnSelchangeComboColRow (GtkWidget *widget, gpointer data) { if (!g_PatchInspector.m_bListenChanged) return; // retrieve the current m_nRow and m_nCol, other params are not relevant g_PatchInspector.exportData(); // read the changed values ourselves g_PatchInspector.UpdateRowColInfo(); // now reflect our changes g_PatchInspector.importData(); } class PatchSetTextureRepeat { float m_s, m_t; public: PatchSetTextureRepeat(float s, float t) : m_s(s), m_t(t) { } void operator()(Patch& patch) const { patch.SetTextureRepeat(m_s, m_t); } }; void Scene_PatchTileTexture_Selected(scene::Graph& graph, float s, float t) { Scene_forEachVisibleSelectedPatch(PatchSetTextureRepeat(s, t)); SceneChangeNotify(); } static void OnBtnPatchdetails (GtkWidget *widget, gpointer data) { UndoableCommand command("patchCapTexture"); Scene_PatchCapTexture_Selected(GlobalSceneGraph()); } static void OnBtnPatchfit (GtkWidget *widget, gpointer data) { UndoableCommand command("patchFitTexture"); Scene_PatchTileTexture_Selected(GlobalSceneGraph(), 1, 1); } static void OnBtnPatchnatural (GtkWidget *widget, gpointer data) { UndoableCommand command("patchNaturalTexture"); Scene_PatchNaturalTexture_Selected(GlobalSceneGraph()); } static void OnBtnPatchreset (GtkWidget *widget, gpointer data) { float fx, fy; if (DoTextureLayout (&fx, &fy) == eIDOK) { UndoableCommand command("patchTileTexture"); Scene_PatchTileTexture_Selected(GlobalSceneGraph(), fx, fy); } } struct PatchRotateTexture { float m_angle; public: PatchRotateTexture(float angle) : m_angle(angle) { } void operator()(Patch& patch) const { patch.RotateTexture(m_angle); } }; void Scene_PatchRotateTexture_Selected(scene::Graph& graph, float angle) { Scene_forEachVisibleSelectedPatch(PatchRotateTexture(angle)); } class PatchScaleTexture { float m_s, m_t; public: PatchScaleTexture(float s, float t) : m_s(s), m_t(t) { } void operator()(Patch& patch) const { patch.ScaleTexture(m_s, m_t); } }; float Patch_convertScale(float scale) { if(scale > 0) { return scale; } if(scale < 0) { return -1 / scale; } return 1; } void Scene_PatchScaleTexture_Selected(scene::Graph& graph, float s, float t) { Scene_forEachVisibleSelectedPatch(PatchScaleTexture(Patch_convertScale(s), Patch_convertScale(t))); } class PatchTranslateTexture { float m_s, m_t; public: PatchTranslateTexture(float s, float t) : m_s(s), m_t(t) { } void operator()(Patch& patch) const { patch.TranslateTexture(m_s, m_t); } }; void Scene_PatchTranslateTexture_Selected(scene::Graph& graph, float s, float t) { Scene_forEachVisibleSelectedPatch(PatchTranslateTexture(s, t)); } static void OnSpinChanged (GtkAdjustment *adj, gpointer data) { texdef_t td; td.rotate = 0; td.scale[0] = td.scale[1] = 0; td.shift[0] = td.shift[1] = 0; if (adj->value == 0) return; if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "hshift_adj")) { g_pi_globals.shift[0] = static_cast(atof (gtk_entry_get_text (GTK_ENTRY (data)))); if (adj->value > 0) td.shift[0] = g_pi_globals.shift[0]; else td.shift[0] = -g_pi_globals.shift[0]; } else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "vshift_adj")) { g_pi_globals.shift[1] = static_cast(atof (gtk_entry_get_text (GTK_ENTRY (data)))); if (adj->value > 0) td.shift[1] = g_pi_globals.shift[1]; else td.shift[1] = -g_pi_globals.shift[1]; } else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "hscale_adj")) { g_pi_globals.scale[0] = static_cast(atof (gtk_entry_get_text (GTK_ENTRY (data)))); if (g_pi_globals.scale[0] == 0.0f) return; if (adj->value > 0) td.scale[0] = g_pi_globals.scale[0]; else td.scale[0] = -g_pi_globals.scale[0]; } else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "vscale_adj")) { g_pi_globals.scale[1] = static_cast(atof (gtk_entry_get_text (GTK_ENTRY (data)))); if (g_pi_globals.scale[1] == 0.0f) return; if (adj->value > 0) td.scale[1] = g_pi_globals.scale[1]; else td.scale[1] = -g_pi_globals.scale[1]; } else if (adj == g_object_get_data (G_OBJECT (g_PatchInspector.GetWidget()), "rotate_adj")) { g_pi_globals.rotate = static_cast(atof (gtk_entry_get_text (GTK_ENTRY (data)))); if (adj->value > 0) td.rotate = g_pi_globals.rotate; else td.rotate = -g_pi_globals.rotate; } adj->value = 0; // will scale shift rotate the patch accordingly if (td.shift[0] || td.shift[1]) { UndoableCommand command("patchTranslateTexture"); Scene_PatchTranslateTexture_Selected (GlobalSceneGraph(), td.shift[0], td.shift[1]); } else if (td.scale[0] || td.scale[1]) { UndoableCommand command("patchScaleTexture"); Scene_PatchScaleTexture_Selected (GlobalSceneGraph(), td.scale[0], td.scale[1]); } else if (td.rotate) { UndoableCommand command("patchRotateTexture"); Scene_PatchRotateTexture_Selected (GlobalSceneGraph(), td.rotate); } // update the point-by-point view OnSelchangeComboColRow(0,0); } static gint OnDialogKey (GtkWidget* widget, GdkEventKey* event, gpointer data) { if (event->keyval == GDK_Return) { OnApply (0, 0); return TRUE; } else if (event->keyval == GDK_Escape) { g_PatchInspector.GetPatchInfo(); return TRUE; } return FALSE; } // ============================================================================= // PatchInspector class GtkWindow* PatchInspector::BuildDialog() { GtkWindow* window = create_floating_window("Patch Properties", m_parent); m_position_tracker.connect(window); global_accel_connect_window(window); window_connect_focus_in_clear_focus_widget(window); { GtkVBox* vbox = GTK_VBOX(gtk_vbox_new(FALSE, 5)); gtk_container_set_border_width(GTK_CONTAINER(vbox), 5); gtk_widget_show(GTK_WIDGET(vbox)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox)); { GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(FALSE, 5)); gtk_widget_show(GTK_WIDGET(hbox)); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), TRUE, TRUE, 0); { GtkVBox* vbox2 = GTK_VBOX(gtk_vbox_new(FALSE, 0)); gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0); gtk_widget_show(GTK_WIDGET(vbox2)); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), TRUE, TRUE, 0); { GtkFrame* frame = GTK_FRAME(gtk_frame_new("Details")); gtk_widget_show(GTK_WIDGET(frame)); gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(frame), TRUE, TRUE, 0); { GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 5)); gtk_container_set_border_width(GTK_CONTAINER(vbox3), 5); gtk_widget_show(GTK_WIDGET(vbox3)); gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3)); { GtkTable* table = GTK_TABLE(gtk_table_new(2, 2, FALSE)); gtk_widget_show(GTK_WIDGET(table)); gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0); gtk_table_set_row_spacings(table, 5); gtk_table_set_col_spacings(table, 5); { GtkLabel* label = GTK_LABEL(gtk_label_new("Row:")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Column:")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 1, 2, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text()); g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(OnSelchangeComboColRow), this); AddDialogData(*combo, m_nRow); gtk_widget_show(GTK_WIDGET(combo)); gtk_table_attach(table, GTK_WIDGET(combo), 0, 1, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(combo), 60, -1); m_pRowCombo = combo; } { GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text()); g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(OnSelchangeComboColRow), this); AddDialogData(*combo, m_nCol); gtk_widget_show(GTK_WIDGET(combo)); gtk_table_attach(table, GTK_WIDGET(combo), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(combo), 60, -1); m_pColCombo = combo; } } GtkTable* table = GTK_TABLE(gtk_table_new(5, 2, FALSE)); gtk_widget_show(GTK_WIDGET(table)); gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0); gtk_table_set_row_spacings(table, 5); gtk_table_set_col_spacings(table, 5); { GtkLabel* label = GTK_LABEL(gtk_label_new("X:")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Y:")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Z:")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 2, 3, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkLabel* label = GTK_LABEL(gtk_label_new("S:")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 3, 4, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkLabel* label = GTK_LABEL(gtk_label_new("T:")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 4, 5, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); AddDialogData(*entry, m_fX); g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); AddDialogData(*entry, m_fY); g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 2, 3, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); AddDialogData(*entry, m_fZ); g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 3, 4, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); AddDialogData(*entry, m_fS); g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 4, 5, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); AddDialogData(*entry, m_fT); g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0); } } } if(g_pGameDescription->mGameType == "doom3") { GtkFrame* frame = GTK_FRAME(gtk_frame_new("Tesselation")); gtk_widget_show(GTK_WIDGET(frame)); gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(frame), TRUE, TRUE, 0); { GtkVBox* vbox3 = GTK_VBOX(gtk_vbox_new(FALSE, 5)); gtk_container_set_border_width(GTK_CONTAINER(vbox3), 5); gtk_widget_show(GTK_WIDGET(vbox3)); gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox3)); { GtkTable* table = GTK_TABLE(gtk_table_new(3, 2, FALSE)); gtk_widget_show(GTK_WIDGET(table)); gtk_box_pack_start(GTK_BOX(vbox3), GTK_WIDGET(table), TRUE, TRUE, 0); gtk_table_set_row_spacings(table, 5); gtk_table_set_col_spacings(table, 5); { GtkLabel* label = GTK_LABEL(gtk_label_new("Fixed")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkCheckButton* check = GTK_CHECK_BUTTON(gtk_check_button_new()); gtk_widget_show(GTK_WIDGET(check)); gtk_table_attach(table, GTK_WIDGET(check), 1, 2, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); m_subdivisions.m_enabled = check; guint handler_id = g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(&Subdivisions::applyGtk), &m_subdivisions); g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id)); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Horizontal")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); m_subdivisions.m_horizontal = entry; m_horizontalSubdivisionsEntry.connect(entry); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Vertical")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 2, 3, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 2, 3, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(0), 0, 0); m_subdivisions.m_vertical = entry; m_verticalSubdivisionsEntry.connect(entry); } } } } } { GtkFrame* frame = GTK_FRAME(gtk_frame_new("Texturing")); gtk_widget_show(GTK_WIDGET(frame)); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(frame), TRUE, TRUE, 0); { GtkVBox* vbox2 = GTK_VBOX(gtk_vbox_new(FALSE, 5)); gtk_widget_show(GTK_WIDGET(vbox2)); gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox2)); gtk_container_set_border_width(GTK_CONTAINER(vbox2), 5); { GtkLabel* label = GTK_LABEL(gtk_label_new("Name:")); gtk_widget_show(GTK_WIDGET(label)); gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(label), TRUE, TRUE, 0); gtk_label_set_justify(label, GTK_JUSTIFY_LEFT); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); // gtk_entry_set_editable (GTK_ENTRY (entry), false); gtk_widget_show(GTK_WIDGET(entry)); gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(entry), TRUE, TRUE, 0); AddDialogData(*entry, m_strName); g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(OnDialogKey), 0); } { GtkTable* table = GTK_TABLE(gtk_table_new(5, 3, FALSE)); gtk_widget_show(GTK_WIDGET(table)); gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(table), TRUE, TRUE, 0); gtk_table_set_row_spacings(table, 5); gtk_table_set_col_spacings(table, 5); { GtkLabel* label = GTK_LABEL(gtk_label_new("Horizontal Shift Step")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Vertical Shift Step")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Horizontal Stretch Step")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 2, 3, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Vertical Stretch Step")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 3, 4, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); } { GtkLabel* label = GTK_LABEL(gtk_label_new("Rotate Step")); gtk_widget_show(GTK_WIDGET(label)); gtk_table_attach(table, GTK_WIDGET(label), 2, 3, 4, 5, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2); g_object_set_data(G_OBJECT(window), "hshift_entry", entry); // we fill in this data, if no patch is selected the widgets are unmodified when the inspector is raised // so we need to have at least one initialisation somewhere entry_set_float(entry, g_pi_globals.shift[0]); GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 1, 1, 1)); g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry); g_object_set_data(G_OBJECT(window), "hshift_adj", adj); GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0)); gtk_widget_show(GTK_WIDGET(spin)); gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 0, 1, (GtkAttachOptions)(0), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2); GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2); entry_set_float(entry, g_pi_globals.shift[1]); GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -8192, 8192, 1, 1, 1)); g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry); g_object_set_data(G_OBJECT(window), "vshift_adj", adj); GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0)); gtk_widget_show(GTK_WIDGET(spin)); gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 1, 2, (GtkAttachOptions)(0), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2); GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 2, 3, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2); entry_set_float(entry, g_pi_globals.scale[0]); GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -1000, 1000, 1, 1, 1)); g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry); g_object_set_data(G_OBJECT(window), "hscale_adj", adj); GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0)); gtk_widget_show(GTK_WIDGET(spin)); gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 2, 3, (GtkAttachOptions)(0), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2); GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 3, 4, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2); entry_set_float(entry, g_pi_globals.scale[1]); GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -1000, 1000, 1, 1, 1)); g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry); g_object_set_data(G_OBJECT(window), "vscale_adj", adj); GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0)); gtk_widget_show(GTK_WIDGET(spin)); gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 3, 4, (GtkAttachOptions)(0), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2); GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS); } { GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_show(GTK_WIDGET(entry)); gtk_table_attach(table, GTK_WIDGET(entry), 0, 1, 4, 5, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(entry), 50, -2); entry_set_float(entry, g_pi_globals.rotate); 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 g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(OnSpinChanged), entry); g_object_set_data(G_OBJECT(window), "rotate_adj", adj); GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0)); gtk_widget_show(GTK_WIDGET(spin)); gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 4, 5, (GtkAttachOptions)(0), (GtkAttachOptions)(0), 0, 0); gtk_widget_set_usize(GTK_WIDGET(spin), 10, -2); GTK_WIDGET_UNSET_FLAGS(spin, GTK_CAN_FOCUS); } } GtkHBox* hbox2 = GTK_HBOX(gtk_hbox_new(TRUE, 5)); gtk_widget_show(GTK_WIDGET(hbox2)); gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(hbox2), TRUE, FALSE, 0); { GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("CAP")); gtk_widget_show(GTK_WIDGET(button)); gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchdetails), 0); gtk_widget_set_usize(GTK_WIDGET(button), 60, -1); } { GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("Set...")); gtk_widget_show(GTK_WIDGET(button)); gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchreset), 0); gtk_widget_set_usize(GTK_WIDGET(button), 60, -1); } { GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("Natural")); gtk_widget_show(GTK_WIDGET(button)); gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchnatural), 0); gtk_widget_set_usize(GTK_WIDGET(button), 60, -1); } { GtkButton* button = GTK_BUTTON(gtk_button_new_with_label("Fit")); gtk_widget_show(GTK_WIDGET(button)); gtk_box_pack_end(GTK_BOX(hbox2), GTK_WIDGET(button), TRUE, FALSE, 0); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(OnBtnPatchfit), 0); gtk_widget_set_usize(GTK_WIDGET(button), 60, -1); } } } } } return window; } // sync the dialog our internal data structures void PatchInspector::exportData() { m_bListenChanged = false; Dialog::exportData(); m_bListenChanged = true; } void PatchInspector::importData() { m_bListenChanged = false; Dialog::importData(); m_bListenChanged = true; } // read the map and feed in the stuff to the dialog box void PatchInspector::GetPatchInfo() { if(g_pGameDescription->mGameType == "doom3") { m_subdivisions.update(); } if(GlobalSelectionSystem().countSelected() == 0) { m_Patch = 0; } else { m_Patch = Node_getPatch(GlobalSelectionSystem().ultimateSelected().path().top()); } if (m_Patch != 0) { m_strName = m_Patch->GetShader(); // fill in the numbers for Row / Col selection m_bListenChanged = false; { gtk_combo_box_set_active(m_pRowCombo, 0); for (std::size_t i = 0; i < m_countRows; ++i) { gtk_combo_box_remove_text(m_pRowCombo, gint(m_countRows - i - 1)); } m_countRows = m_Patch->getHeight(); for (std::size_t i = 0; i < m_countRows; ++i) { char buffer[16]; sprintf(buffer, "%u", Unsigned(i)); gtk_combo_box_append_text(m_pRowCombo, buffer); } gtk_combo_box_set_active(m_pRowCombo, 0); } { gtk_combo_box_set_active(m_pColCombo, 0); for (std::size_t i = 0; i < m_countCols; ++i) { gtk_combo_box_remove_text(m_pColCombo, gint(m_countCols - i - 1)); } m_countCols = m_Patch->getWidth(); for (std::size_t i = 0; i < m_countCols; ++i) { char buffer[16]; sprintf(buffer, "%u", Unsigned(i)); gtk_combo_box_append_text(m_pColCombo, buffer); } gtk_combo_box_set_active(m_pColCombo, 0); } m_bListenChanged = true; } else { //globalOutputStream() << "WARNING: no patch\n"; } // fill in our internal structs m_nRow = 0; m_nCol = 0; UpdateRowColInfo(); // now update the dialog box importData(); } // read the current patch on map and initialize m_fX m_fY accordingly // NOTE: don't call UpdateData in there, it's not meant for void PatchInspector::UpdateRowColInfo() { m_fX = m_fY = m_fZ = m_fS = m_fT = 0.0; if (m_Patch != 0) { // we rely on whatever active row/column has been set before we get called std::size_t r = m_nRow; std::size_t c = m_nCol; if(r < m_Patch->getHeight() && c < m_Patch->getWidth()) { const PatchControl& p = m_Patch->ctrlAt(r,c); m_fX = p.m_vertex[0]; m_fY = p.m_vertex[1]; m_fZ = p.m_vertex[2]; m_fS = p.m_texcoord[0]; m_fT = p.m_texcoord[1]; } } } void PatchInspector_SelectionChanged(const Selectable& selectable) { PatchInspector_queueDraw(); } #include "preferencesystem.h" void PatchInspector_Construct() { GlobalCommands_insert("PatchInspector", FreeCaller(), Accelerator('S', (GdkModifierType)GDK_SHIFT_MASK)); GlobalPreferenceSystem().registerPreference("PatchWnd", WindowPositionTrackerImportStringCaller(g_PatchInspector.m_position_tracker), WindowPositionTrackerExportStringCaller(g_PatchInspector.m_position_tracker)); GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Scale1", FloatImportStringCaller(g_pi_globals.scale[0]), FloatExportStringCaller(g_pi_globals.scale[0])); GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Scale2", FloatImportStringCaller(g_pi_globals.scale[1]), FloatExportStringCaller(g_pi_globals.scale[1])); GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Shift1", FloatImportStringCaller(g_pi_globals.shift[0]), FloatExportStringCaller(g_pi_globals.shift[0])); GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Shift2", FloatImportStringCaller(g_pi_globals.shift[1]), FloatExportStringCaller(g_pi_globals.shift[1])); GlobalPreferenceSystem().registerPreference("SI_PatchTexdef_Rotate", FloatImportStringCaller(g_pi_globals.rotate), FloatExportStringCaller(g_pi_globals.rotate)); typedef FreeCaller1 PatchInspectorSelectionChangedCaller; GlobalSelectionSystem().addSelectionChangeCallback(PatchInspectorSelectionChangedCaller()); typedef FreeCaller PatchInspectorQueueDrawCaller; Patch_addTextureChangedCallback(PatchInspectorQueueDrawCaller()); } void PatchInspector_Destroy() { }