/* 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 */ #include "select.h" #include "debugging/debugging.h" #include "ientity.h" #include "iselection.h" #include "iundo.h" #include #include "stream/stringstream.h" #include "signal/isignal.h" #include "shaderlib.h" #include "scenelib.h" #include "gtkutil/idledraw.h" #include "gtkutil/dialog.h" #include "gtkutil/widget.h" #include "brushmanip.h" #include "brush.h" #include "patchmanip.h" #include "patchdialog.h" #include "selection.h" #include "texwindow.h" #include "gtkmisc.h" #include "mainframe.h" #include "grid.h" #include "map.h" select_workzone_t g_select_workzone; /** Loops over all selected brushes and stores their world AABBs in the specified array. */ class CollectSelectedBrushesBounds : public SelectionSystem::Visitor { AABB* m_bounds; // array of AABBs Unsigned m_max; // max AABB-elements in array Unsigned& m_count;// count of valid AABBs stored in array public: CollectSelectedBrushesBounds(AABB* bounds, Unsigned max, Unsigned& count) : m_bounds(bounds), m_max(max), m_count(count) { m_count = 0; } void visit(scene::Instance& instance) const { ASSERT_MESSAGE(m_count <= m_max, "Invalid m_count in CollectSelectedBrushesBounds"); // stop if the array is already full if(m_count == m_max) return; Selectable* selectable = Instance_getSelectable(instance); if((selectable != 0) && instance.isSelected()) { // brushes only if(Instance_getBrush(instance) != 0) { m_bounds[m_count] = instance.worldAABB(); ++m_count; } } } }; /** Selects all objects that intersect one of the bounding AABBs. The exact intersection-method is specified through TSelectionPolicy */ template class SelectByBounds : public scene::Graph::Walker { AABB* m_aabbs; // selection aabbs Unsigned m_count; // number of aabbs in m_aabbs TSelectionPolicy policy; // type that contains a custom intersection method aabb<->aabb public: SelectByBounds(AABB* aabbs, Unsigned count) : m_aabbs(aabbs), m_count(count) { } bool pre(const scene::Path& path, scene::Instance& instance) const { Selectable* selectable = Instance_getSelectable(instance); // ignore worldspawn Entity* entity = Node_getEntity(path.top()); if(entity) { if(string_equal(entity->getKeyValue("classname"), "worldspawn")) return true; } if( (path.size() > 1) && (!path.top().get().isRoot()) && (selectable != 0) ) { for(Unsigned i = 0; i < m_count; ++i) { if(policy.Evaluate(m_aabbs[i], instance)) { selectable->setSelected(true); } } } return true; } /** Performs selection operation on the global scenegraph. If delete_bounds_src is true, then the objects which were used as source for the selection aabbs will be deleted. */ static void DoSelection(bool delete_bounds_src = true) { if(GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive) { // we may not need all AABBs since not all selected objects have to be brushes const Unsigned max = (Unsigned)GlobalSelectionSystem().countSelected(); AABB* aabbs = new AABB[max]; Unsigned count; CollectSelectedBrushesBounds collector(aabbs, max, count); GlobalSelectionSystem().foreachSelected(collector); // nothing usable in selection if(!count) { delete[] aabbs; return; } // delete selected objects if(delete_bounds_src)// see deleteSelection { UndoableCommand undo("deleteSelected"); Select_Delete(); } // select objects with bounds GlobalSceneGraph().traverse(SelectByBounds(aabbs, count)); SceneChangeNotify(); delete[] aabbs; } } }; /** SelectionPolicy for SelectByBounds Returns true if box and the AABB of instance intersect */ class SelectionPolicy_Touching { public: bool Evaluate(const AABB& box, scene::Instance& instance) const { const AABB& other(instance.worldAABB()); for(Unsigned i = 0; i < 3; ++i) { if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] + other.extents[i])) return false; } return true; } }; /** SelectionPolicy for SelectByBounds Returns true if the AABB of instance is inside box */ class SelectionPolicy_Inside { public: bool Evaluate(const AABB& box, scene::Instance& instance) const { const AABB& other(instance.worldAABB()); for(Unsigned i = 0; i < 3; ++i) { if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] - other.extents[i])) return false; } return true; } }; class DeleteSelected : public scene::Graph::Walker { mutable bool m_remove; mutable bool m_removedChild; public: DeleteSelected() : m_remove(false), m_removedChild(false) { } bool pre(const scene::Path& path, scene::Instance& instance) const { m_removedChild = false; Selectable* selectable = Instance_getSelectable(instance); if(selectable != 0 && selectable->isSelected() && path.size() > 1 && !path.top().get().isRoot()) { m_remove = true; return false;// dont traverse into child elements } return true; } void post(const scene::Path& path, scene::Instance& instance) const { if(m_removedChild) { m_removedChild = false; // delete empty entities Entity* entity = Node_getEntity(path.top()); if(entity != 0 && path.top().get_pointer() != Map_FindWorldspawn(g_map) && Node_getTraversable(path.top())->empty()) { Path_deleteTop(path); } } // node should be removed if(m_remove) { if(Node_isEntity(path.parent()) != 0) { m_removedChild = true; } m_remove = false; Path_deleteTop(path); } } }; void Scene_DeleteSelected(scene::Graph& graph) { graph.traverse(DeleteSelected()); SceneChangeNotify(); } void Select_Delete (void) { Scene_DeleteSelected(GlobalSceneGraph()); } class InvertSelectionWalker : public scene::Graph::Walker { SelectionSystem::EMode m_mode; mutable Selectable* m_selectable; public: InvertSelectionWalker(SelectionSystem::EMode mode) : m_mode(mode), m_selectable(0) { } bool pre(const scene::Path& path, scene::Instance& instance) const { Selectable* selectable = Instance_getSelectable(instance); if(selectable) { switch(m_mode) { case SelectionSystem::eEntity: if(Node_isEntity(path.top()) != 0) { m_selectable = path.top().get().visible() ? selectable : 0; } break; case SelectionSystem::ePrimitive: m_selectable = path.top().get().visible() ? selectable : 0; break; case SelectionSystem::eComponent: break; } } return true; } void post(const scene::Path& path, scene::Instance& instance) const { if(m_selectable != 0) { m_selectable->setSelected(!m_selectable->isSelected()); m_selectable = 0; } } }; void Scene_Invert_Selection(scene::Graph& graph) { graph.traverse(InvertSelectionWalker(GlobalSelectionSystem().Mode())); } void Select_Invert() { Scene_Invert_Selection(GlobalSceneGraph()); } class ExpandSelectionToEntitiesWalker : public scene::Graph::Walker { mutable std::size_t m_depth; NodeSmartReference worldspawn; public: ExpandSelectionToEntitiesWalker() : m_depth(0), worldspawn(Map_FindOrInsertWorldspawn(g_map)) { } bool pre(const scene::Path& path, scene::Instance& instance) const { ++m_depth; // ignore worldspawn NodeSmartReference me(path.top().get()); if(me == worldspawn) return false; if(m_depth == 2) // entity depth { // traverse and select children if any one is selected if(instance.childSelected()) Instance_setSelected(instance, true); return Node_getEntity(path.top())->isContainer() && instance.isSelected(); } else if(m_depth == 3) // primitive depth { Instance_setSelected(instance, true); return false; } return true; } void post(const scene::Path& path, scene::Instance& instance) const { --m_depth; } }; void Scene_ExpandSelectionToEntities() { GlobalSceneGraph().traverse(ExpandSelectionToEntitiesWalker()); } namespace { void Selection_UpdateWorkzone() { if(GlobalSelectionSystem().countSelected() != 0) { Select_GetBounds(g_select_workzone.d_work_min, g_select_workzone.d_work_max); } } typedef FreeCaller SelectionUpdateWorkzoneCaller; IdleDraw g_idleWorkzone = IdleDraw(SelectionUpdateWorkzoneCaller()); } const select_workzone_t& Select_getWorkZone() { g_idleWorkzone.flush(); return g_select_workzone; } void UpdateWorkzone_ForSelection() { g_idleWorkzone.queueDraw(); } // update the workzone to the current selection void UpdateWorkzone_ForSelectionChanged(const Selectable& selectable) { if(selectable.isSelected()) { UpdateWorkzone_ForSelection(); } } void Select_SetShader(const char* shader) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushSetShader_Selected(GlobalSceneGraph(), shader); Scene_PatchSetShader_Selected(GlobalSceneGraph(), shader); } Scene_BrushSetShader_Component_Selected(GlobalSceneGraph(), shader); } void Select_SetTexdef(const TextureProjection& projection) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushSetTexdef_Selected(GlobalSceneGraph(), projection); } Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection); } void Select_SetFlags(const ContentsFlagsValue& flags) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushSetFlags_Selected(GlobalSceneGraph(), flags); } Scene_BrushSetFlags_Component_Selected(GlobalSceneGraph(), flags); } void Select_GetBounds (Vector3& mins, Vector3& maxs) { AABB bounds; Scene_BoundsSelected(GlobalSceneGraph(), bounds); maxs = vector3_added(bounds.origin, bounds.extents); mins = vector3_subtracted(bounds.origin, bounds.extents); } void Select_GetMid (Vector3& mid) { AABB bounds; Scene_BoundsSelected(GlobalSceneGraph(), bounds); mid = vector3_snapped(bounds.origin); } void Select_FlipAxis (int axis) { Vector3 flip(1, 1, 1); flip[axis] = -1; GlobalSelectionSystem().scaleSelected(flip); } void Select_Scale(float x, float y, float z) { GlobalSelectionSystem().scaleSelected(Vector3(x, y, z)); } enum axis_t { eAxisX = 0, eAxisY = 1, eAxisZ = 2, }; enum sign_t { eSignPositive = 1, eSignNegative = -1, }; inline Matrix4 matrix4_rotation_for_axis90(axis_t axis, sign_t sign) { switch(axis) { case eAxisX: if(sign == eSignPositive) { return matrix4_rotation_for_sincos_x(1, 0); } else { return matrix4_rotation_for_sincos_x(-1, 0); } case eAxisY: if(sign == eSignPositive) { return matrix4_rotation_for_sincos_y(1, 0); } else { return matrix4_rotation_for_sincos_y(-1, 0); } default://case eAxisZ: if(sign == eSignPositive) { return matrix4_rotation_for_sincos_z(1, 0); } else { return matrix4_rotation_for_sincos_z(-1, 0); } } } inline void matrix4_rotate_by_axis90(Matrix4& matrix, axis_t axis, sign_t sign) { matrix4_multiply_by_matrix4(matrix, matrix4_rotation_for_axis90(axis, sign)); } inline void matrix4_pivoted_rotate_by_axis90(Matrix4& matrix, axis_t axis, sign_t sign, const Vector3& pivotpoint) { matrix4_translate_by_vec3(matrix, pivotpoint); matrix4_rotate_by_axis90(matrix, axis, sign); matrix4_translate_by_vec3(matrix, vector3_negated(pivotpoint)); } inline Quaternion quaternion_for_axis90(axis_t axis, sign_t sign) { #if 1 switch(axis) { case eAxisX: if(sign == eSignPositive) { return Quaternion(c_half_sqrt2f, 0, 0, c_half_sqrt2f); } else { return Quaternion(-c_half_sqrt2f, 0, 0, -c_half_sqrt2f); } case eAxisY: if(sign == eSignPositive) { return Quaternion(0, c_half_sqrt2f, 0, c_half_sqrt2f); } else { return Quaternion(0, -c_half_sqrt2f, 0, -c_half_sqrt2f); } default://case eAxisZ: if(sign == eSignPositive) { return Quaternion(0, 0, c_half_sqrt2f, c_half_sqrt2f); } else { return Quaternion(0, 0, -c_half_sqrt2f, -c_half_sqrt2f); } } #else quaternion_for_matrix4_rotation(matrix4_rotation_for_axis90((axis_t)axis, (deg > 0) ? eSignPositive : eSignNegative)); #endif } void Select_RotateAxis (int axis, float deg) { if(fabs(deg) == 90.f) { GlobalSelectionSystem().rotateSelected(quaternion_for_axis90((axis_t)axis, (deg > 0) ? eSignPositive : eSignNegative)); } else { switch(axis) { case 0: GlobalSelectionSystem().rotateSelected(quaternion_for_matrix4_rotation(matrix4_rotation_for_x_degrees(deg))); break; case 1: GlobalSelectionSystem().rotateSelected(quaternion_for_matrix4_rotation(matrix4_rotation_for_y_degrees(deg))); break; case 2: GlobalSelectionSystem().rotateSelected(quaternion_for_matrix4_rotation(matrix4_rotation_for_z_degrees(deg))); break; } } } void Select_ShiftTexture(float x, float y) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushShiftTexdef_Selected(GlobalSceneGraph(), x, y); Scene_PatchTranslateTexture_Selected(GlobalSceneGraph(), x, y); } //globalOutputStream() << "shift selected face textures: s=" << x << " t=" << y << '\n'; Scene_BrushShiftTexdef_Component_Selected(GlobalSceneGraph(), x, y); } void Select_ScaleTexture(float x, float y) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushScaleTexdef_Selected(GlobalSceneGraph(), x, y); Scene_PatchScaleTexture_Selected(GlobalSceneGraph(), x, y); } Scene_BrushScaleTexdef_Component_Selected(GlobalSceneGraph(), x, y); } void Select_RotateTexture(float amt) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushRotateTexdef_Selected(GlobalSceneGraph(), amt); Scene_PatchRotateTexture_Selected(GlobalSceneGraph(), amt); } Scene_BrushRotateTexdef_Component_Selected(GlobalSceneGraph(), amt); } // TTimo modified to handle shader architecture: // expects shader names at input, comparison relies on shader names .. texture names no longer relevant void FindReplaceTextures(const char* pFind, const char* pReplace, bool bSelected) { if(!texdef_name_valid(pFind)) { globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pFind << "', aborted\n"; return; } if(!texdef_name_valid(pReplace)) { globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pReplace << "', aborted\n"; return; } StringOutputStream command; command << "textureFindReplace -find " << pFind << " -replace " << pReplace; UndoableCommand undo(command.c_str()); if(bSelected) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushFindReplaceShader_Selected(GlobalSceneGraph(), pFind, pReplace); Scene_PatchFindReplaceShader_Selected(GlobalSceneGraph(), pFind, pReplace); } Scene_BrushFindReplaceShader_Component_Selected(GlobalSceneGraph(), pFind, pReplace); } else { Scene_BrushFindReplaceShader(GlobalSceneGraph(), pFind, pReplace); Scene_PatchFindReplaceShader(GlobalSceneGraph(), pFind, pReplace); } } typedef std::vector Classnames; bool classnames_match_entity(const Classnames& classnames, Entity* entity) { for(Classnames::const_iterator i = classnames.begin(); i != classnames.end(); ++i) { if(string_equal(entity->getKeyValue("classname"), *i)) { return true; } } return false; } class EntityFindByClassnameWalker : public scene::Graph::Walker { const Classnames& m_classnames; public: EntityFindByClassnameWalker(const Classnames& classnames) : m_classnames(classnames) { } bool pre(const scene::Path& path, scene::Instance& instance) const { Entity* entity = Node_getEntity(path.top()); if(entity != 0 && classnames_match_entity(m_classnames, entity)) { Instance_getSelectable(instance)->setSelected(true); } return true; } }; void Scene_EntitySelectByClassnames(scene::Graph& graph, const Classnames& classnames) { graph.traverse(EntityFindByClassnameWalker(classnames)); } class EntityGetSelectedClassnamesWalker : public scene::Graph::Walker { Classnames& m_classnames; public: EntityGetSelectedClassnamesWalker(Classnames& classnames) : m_classnames(classnames) { } bool pre(const scene::Path& path, scene::Instance& instance) const { Selectable* selectable = Instance_getSelectable(instance); if(selectable != 0 && selectable->isSelected()) { Entity* entity = Node_getEntity(path.top()); if(entity != 0) { m_classnames.push_back(entity->getKeyValue("classname")); } } return true; } }; void Scene_EntityGetClassnames(scene::Graph& graph, Classnames& classnames) { graph.traverse(EntityGetSelectedClassnamesWalker(classnames)); } void Select_AllOfType() { if(GlobalSelectionSystem().Mode() == SelectionSystem::eComponent) { if(GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace) { GlobalSelectionSystem().setSelectedAllComponents(false); Scene_BrushSelectByShader_Component(GlobalSceneGraph(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser())); } } else { Classnames classnames; Scene_EntityGetClassnames(GlobalSceneGraph(), classnames); GlobalSelectionSystem().setSelectedAll(false); if(!classnames.empty()) { Scene_EntitySelectByClassnames(GlobalSceneGraph(), classnames); } else { Scene_BrushSelectByShader(GlobalSceneGraph(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser())); Scene_PatchSelectByShader(GlobalSceneGraph(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser())); } } } void Select_Inside(void) { SelectByBounds::DoSelection(); } void Select_Touching(void) { SelectByBounds::DoSelection(false); } void Select_FitTexture(float horizontal, float vertical) { if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent) { Scene_BrushFitTexture_Selected(GlobalSceneGraph(), horizontal, vertical); } Scene_BrushFitTexture_Component_Selected(GlobalSceneGraph(), horizontal, vertical); SceneChangeNotify(); } inline void hide_node(scene::Node& node, bool hide) { hide ? node.enable(scene::Node::eHidden) : node.disable(scene::Node::eHidden); } class HideSelectedWalker : public scene::Graph::Walker { bool m_hide; public: HideSelectedWalker(bool hide) : m_hide(hide) { } bool pre(const scene::Path& path, scene::Instance& instance) const { Selectable* selectable = Instance_getSelectable(instance); if(selectable != 0 && selectable->isSelected()) { hide_node(path.top(), m_hide); } return true; } }; void Scene_Hide_Selected(bool hide) { GlobalSceneGraph().traverse(HideSelectedWalker(hide)); } void Select_Hide() { Scene_Hide_Selected(true); SceneChangeNotify(); } void HideSelected() { Select_Hide(); GlobalSelectionSystem().setSelectedAll(false); } class HideAllWalker : public scene::Graph::Walker { bool m_hide; public: HideAllWalker(bool hide) : m_hide(hide) { } bool pre(const scene::Path& path, scene::Instance& instance) const { hide_node(path.top(), m_hide); return true; } }; void Scene_Hide_All(bool hide) { GlobalSceneGraph().traverse(HideAllWalker(hide)); } void Select_ShowAllHidden() { Scene_Hide_All(false); SceneChangeNotify(); } void Selection_Flipx() { UndoableCommand undo("mirrorSelected -axis x"); Select_FlipAxis(0); } void Selection_Flipy() { UndoableCommand undo("mirrorSelected -axis y"); Select_FlipAxis(1); } void Selection_Flipz() { UndoableCommand undo("mirrorSelected -axis z"); Select_FlipAxis(2); } void Selection_Rotatex() { UndoableCommand undo("rotateSelected -axis x -angle -90"); Select_RotateAxis(0,-90); } void Selection_Rotatey() { UndoableCommand undo("rotateSelected -axis y -angle 90"); Select_RotateAxis(1, 90); } void Selection_Rotatez() { UndoableCommand undo("rotateSelected -axis z -angle -90"); Select_RotateAxis(2,-90); } void Nudge(int nDim, float fNudge) { Vector3 translate(0, 0, 0); translate[nDim] = fNudge; GlobalSelectionSystem().translateSelected(translate); } void Selection_NudgeZ(float amount) { StringOutputStream command; command << "nudgeSelected -axis z -amount " << amount; UndoableCommand undo(command.c_str()); Nudge(2, amount); } void Selection_MoveDown() { Selection_NudgeZ(-GetGridSize()); } void Selection_MoveUp() { Selection_NudgeZ(GetGridSize()); } void SceneSelectionChange(const Selectable& selectable) { SceneChangeNotify(); } SignalHandlerId Selection_boundsChanged; void Selection_construct() { typedef FreeCaller1 SceneSelectionChangeCaller; GlobalSelectionSystem().addSelectionChangeCallback(SceneSelectionChangeCaller()); typedef FreeCaller1 UpdateWorkzoneForSelectionChangedCaller; GlobalSelectionSystem().addSelectionChangeCallback(UpdateWorkzoneForSelectionChangedCaller()); typedef FreeCaller UpdateWorkzoneForSelectionCaller; Selection_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback(UpdateWorkzoneForSelectionCaller()); } void Selection_destroy() { GlobalSceneGraph().removeBoundsChangedCallback(Selection_boundsChanged); } #include "gtkdlgs.h" #include #include #include #include #include inline Quaternion quaternion_for_euler_xyz_degrees(const Vector3& eulerXYZ) { #if 0 return quaternion_for_matrix4_rotation(matrix4_rotation_for_euler_xyz_degrees(eulerXYZ)); #elif 0 return quaternion_multiplied_by_quaternion( quaternion_multiplied_by_quaternion( quaternion_for_z(degrees_to_radians(eulerXYZ[2])), quaternion_for_y(degrees_to_radians(eulerXYZ[1])) ), quaternion_for_x(degrees_to_radians(eulerXYZ[0])) ); #elif 1 double cx = cos(degrees_to_radians(eulerXYZ[0] * 0.5)); double sx = sin(degrees_to_radians(eulerXYZ[0] * 0.5)); double cy = cos(degrees_to_radians(eulerXYZ[1] * 0.5)); double sy = sin(degrees_to_radians(eulerXYZ[1] * 0.5)); double cz = cos(degrees_to_radians(eulerXYZ[2] * 0.5)); double sz = sin(degrees_to_radians(eulerXYZ[2] * 0.5)); return Quaternion( cz * cy * sx - sz * sy * cx, cz * sy * cx + sz * cy * sx, sz * cy * cx - cz * sy * sx, cz * cy * cx + sz * sy * sx ); #endif } struct RotateDialog { GtkSpinButton* x; GtkSpinButton* y; GtkSpinButton* z; GtkWindow *window; }; static gboolean rotatedlg_apply (GtkWidget *widget, RotateDialog* rotateDialog) { Vector3 eulerXYZ; eulerXYZ[0] = static_cast(gtk_spin_button_get_value(rotateDialog->x)); eulerXYZ[1] = static_cast(gtk_spin_button_get_value(rotateDialog->y)); eulerXYZ[2] = static_cast(gtk_spin_button_get_value(rotateDialog->z)); StringOutputStream command; command << "rotateSelectedEulerXYZ -x " << eulerXYZ[0] << " -y " << eulerXYZ[1] << " -z " << eulerXYZ[2]; UndoableCommand undo(command.c_str()); GlobalSelectionSystem().rotateSelected(quaternion_for_euler_xyz_degrees(eulerXYZ)); return TRUE; } static gboolean rotatedlg_cancel (GtkWidget *widget, RotateDialog* rotateDialog) { gtk_widget_hide(GTK_WIDGET(rotateDialog->window)); gtk_spin_button_set_value(rotateDialog->x, 0.0f); // reset to 0 on close gtk_spin_button_set_value(rotateDialog->y, 0.0f); gtk_spin_button_set_value(rotateDialog->z, 0.0f); return TRUE; } static gboolean rotatedlg_ok (GtkWidget *widget, RotateDialog* rotateDialog) { rotatedlg_apply(widget, rotateDialog); rotatedlg_cancel(widget, rotateDialog); return TRUE; } static gboolean rotatedlg_delete (GtkWidget *widget, GdkEventAny *event, RotateDialog* rotateDialog) { rotatedlg_cancel(widget, rotateDialog); return TRUE; } RotateDialog g_rotate_dialog; void DoRotateDlg() { if(g_rotate_dialog.window == NULL) { g_rotate_dialog.window = create_dialog_window(MainFrame_getWindow(), "Arbitrary rotation", G_CALLBACK(rotatedlg_delete), &g_rotate_dialog); GtkAccelGroup* accel = gtk_accel_group_new(); gtk_window_add_accel_group(g_rotate_dialog.window, accel); { GtkHBox* hbox = create_dialog_hbox(4, 4); gtk_container_add(GTK_CONTAINER(g_rotate_dialog.window), GTK_WIDGET(hbox)); { GtkTable* table = create_dialog_table(3, 2, 4, 4); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0); { GtkWidget* label = gtk_label_new (" X "); gtk_widget_show (label); gtk_table_attach(table, label, 0, 1, 0, 1, (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0); } { GtkWidget* label = gtk_label_new (" Y "); gtk_widget_show (label); gtk_table_attach(table, label, 0, 1, 1, 2, (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0); } { GtkWidget* label = gtk_label_new (" Z "); gtk_widget_show (label); gtk_table_attach(table, label, 0, 1, 2, 3, (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0); } { GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -359, 359, 1, 10, 0)); 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) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_set_size_request(GTK_WIDGET(spin), 64, -1); gtk_spin_button_set_wrap(spin, TRUE); gtk_widget_grab_focus(GTK_WIDGET(spin)); g_rotate_dialog.x = spin; } { GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -359, 359, 1, 10, 0)); 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) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_set_size_request(GTK_WIDGET(spin), 64, -1); gtk_spin_button_set_wrap(spin, TRUE); g_rotate_dialog.y = spin; } { GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -359, 359, 1, 10, 0)); 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) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_set_size_request(GTK_WIDGET(spin), 64, -1); gtk_spin_button_set_wrap(spin, TRUE); g_rotate_dialog.z = spin; } } { GtkVBox* vbox = create_dialog_vbox(4); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0); { GtkButton* button = create_dialog_button("OK", G_CALLBACK(rotatedlg_ok), &g_rotate_dialog); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); widget_make_default(GTK_WIDGET(button)); gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0); } { GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(rotatedlg_cancel), &g_rotate_dialog); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0); } { GtkButton* button = create_dialog_button("Apply", G_CALLBACK(rotatedlg_apply), &g_rotate_dialog); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); } } } } gtk_widget_show(GTK_WIDGET(g_rotate_dialog.window)); } struct ScaleDialog { GtkWidget* x; GtkWidget* y; GtkWidget* z; GtkWindow *window; }; static gboolean scaledlg_apply (GtkWidget *widget, ScaleDialog* scaleDialog) { float sx, sy, sz; sx = static_cast(atof(gtk_entry_get_text (GTK_ENTRY (scaleDialog->x)))); sy = static_cast(atof(gtk_entry_get_text (GTK_ENTRY (scaleDialog->y)))); sz = static_cast(atof(gtk_entry_get_text (GTK_ENTRY (scaleDialog->z)))); StringOutputStream command; command << "scaleSelected -x " << sx << " -y " << sy << " -z " << sz; UndoableCommand undo(command.c_str()); Select_Scale(sx, sy, sz); return TRUE; } static gboolean scaledlg_cancel (GtkWidget *widget, ScaleDialog* scaleDialog) { gtk_widget_hide(GTK_WIDGET(scaleDialog->window)); gtk_entry_set_text (GTK_ENTRY(scaleDialog->x), "1.0"); gtk_entry_set_text (GTK_ENTRY(scaleDialog->y), "1.0"); gtk_entry_set_text (GTK_ENTRY(scaleDialog->z), "1.0"); return TRUE; } static gboolean scaledlg_ok (GtkWidget *widget, ScaleDialog* scaleDialog) { scaledlg_apply(widget, scaleDialog); scaledlg_cancel(widget, scaleDialog); return TRUE; } static gboolean scaledlg_delete (GtkWidget *widget, GdkEventAny *event, ScaleDialog* scaleDialog) { scaledlg_cancel(widget, scaleDialog); return TRUE; } ScaleDialog g_scale_dialog; void DoScaleDlg() { if(g_scale_dialog.window == NULL) { g_scale_dialog.window = create_dialog_window(MainFrame_getWindow(), "Arbitrary scale", G_CALLBACK(scaledlg_delete), &g_scale_dialog); GtkAccelGroup* accel = gtk_accel_group_new(); gtk_window_add_accel_group(g_scale_dialog.window, accel); { GtkHBox* hbox = create_dialog_hbox(4, 4); gtk_container_add(GTK_CONTAINER(g_scale_dialog.window), GTK_WIDGET(hbox)); { GtkTable* table = create_dialog_table(3, 2, 4, 4); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0); { GtkWidget* label = gtk_label_new (" X "); gtk_widget_show (label); gtk_table_attach(table, label, 0, 1, 0, 1, (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0); } { GtkWidget* label = gtk_label_new (" Y "); gtk_widget_show (label); gtk_table_attach(table, label, 0, 1, 1, 2, (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0); } { GtkWidget* label = gtk_label_new (" Z "); gtk_widget_show (label); gtk_table_attach(table, label, 0, 1, 2, 3, (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0); } { GtkWidget* entry = gtk_entry_new(); gtk_entry_set_text (GTK_ENTRY(entry), "1.0"); gtk_widget_show (entry); gtk_table_attach(table, entry, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); g_scale_dialog.x = entry; } { GtkWidget* entry = gtk_entry_new(); gtk_entry_set_text (GTK_ENTRY(entry), "1.0"); gtk_widget_show (entry); gtk_table_attach(table, entry, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); g_scale_dialog.y = entry; } { GtkWidget* entry = gtk_entry_new(); gtk_entry_set_text (GTK_ENTRY(entry), "1.0"); gtk_widget_show (entry); gtk_table_attach(table, entry, 1, 2, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); g_scale_dialog.z = entry; } } { GtkVBox* vbox = create_dialog_vbox(4); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0); { GtkButton* button = create_dialog_button("OK", G_CALLBACK(scaledlg_ok), &g_scale_dialog); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); widget_make_default(GTK_WIDGET(button)); gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0); } { GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(scaledlg_cancel), &g_scale_dialog); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0); } { GtkButton* button = create_dialog_button("Apply", G_CALLBACK(scaledlg_apply), &g_scale_dialog); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); } } } } gtk_widget_show(GTK_WIDGET(g_scale_dialog.window)); }