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
24 #include "debugging/debugging.h"
27 #include "iselection.h"
32 #include "stream/stringstream.h"
33 #include "signal/isignal.h"
34 #include "shaderlib.h"
37 #include "gtkutil/idledraw.h"
38 #include "gtkutil/dialog.h"
39 #include "gtkutil/widget.h"
40 #include "brushmanip.h"
42 #include "patchmanip.h"
43 #include "patchdialog.h"
44 #include "selection.h"
45 #include "texwindow.h"
47 #include "mainframe.h"
50 #include "entityinspector.h"
54 select_workzone_t g_select_workzone;
58 Loops over all selected brushes and stores their
59 world AABBs in the specified array.
61 class CollectSelectedBrushesBounds : public SelectionSystem::Visitor
63 AABB* m_bounds; // array of AABBs
64 Unsigned m_max; // max AABB-elements in array
65 Unsigned& m_count;// count of valid AABBs stored in array
68 CollectSelectedBrushesBounds(AABB* bounds, Unsigned max, Unsigned& count)
76 void visit(scene::Instance& instance) const
78 ASSERT_MESSAGE(m_count <= m_max, "Invalid m_count in CollectSelectedBrushesBounds");
80 // stop if the array is already full
84 Selectable* selectable = Instance_getSelectable(instance);
86 && instance.isSelected())
89 if(Instance_getBrush(instance) != 0)
91 m_bounds[m_count] = instance.worldAABB();
99 Selects all objects that intersect one of the bounding AABBs.
100 The exact intersection-method is specified through TSelectionPolicy
102 template<class TSelectionPolicy>
103 class SelectByBounds : public scene::Graph::Walker
105 AABB* m_aabbs; // selection aabbs
106 Unsigned m_count; // number of aabbs in m_aabbs
107 TSelectionPolicy policy; // type that contains a custom intersection method aabb<->aabb
110 SelectByBounds(AABB* aabbs, Unsigned count)
116 bool pre(const scene::Path& path, scene::Instance& instance) const
118 Selectable* selectable = Instance_getSelectable(instance);
121 Entity* entity = Node_getEntity(path.top());
124 if(string_equal(entity->getKeyValue("classname"), "worldspawn"))
128 if( (path.size() > 1) &&
129 (!path.top().get().isRoot()) &&
133 for(Unsigned i = 0; i < m_count; ++i)
135 if(policy.Evaluate(m_aabbs[i], instance))
137 selectable->setSelected(true);
146 Performs selection operation on the global scenegraph.
147 If delete_bounds_src is true, then the objects which were
148 used as source for the selection aabbs will be deleted.
150 static void DoSelection(bool delete_bounds_src = true)
152 if(GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
154 // we may not need all AABBs since not all selected objects have to be brushes
155 const Unsigned max = (Unsigned)GlobalSelectionSystem().countSelected();
156 AABB* aabbs = new AABB[max];
159 CollectSelectedBrushesBounds collector(aabbs, max, count);
160 GlobalSelectionSystem().foreachSelected(collector);
162 // nothing usable in selection
169 // delete selected objects
170 if(delete_bounds_src)// see deleteSelection
172 UndoableCommand undo("deleteSelected");
176 // select objects with bounds
177 GlobalSceneGraph().traverse(SelectByBounds<TSelectionPolicy>(aabbs, count));
186 SelectionPolicy for SelectByBounds
187 Returns true if box and the AABB of instance intersect
189 class SelectionPolicy_Touching
192 bool Evaluate(const AABB& box, scene::Instance& instance) const
194 const AABB& other(instance.worldAABB());
195 for(Unsigned i = 0; i < 3; ++i)
197 if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] + other.extents[i]))
205 SelectionPolicy for SelectByBounds
206 Returns true if the AABB of instance is inside box
208 class SelectionPolicy_Inside
211 bool Evaluate(const AABB& box, scene::Instance& instance) const
213 const AABB& other(instance.worldAABB());
214 for(Unsigned i = 0; i < 3; ++i)
216 if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] - other.extents[i]))
223 class DeleteSelected : public scene::Graph::Walker
225 mutable bool m_remove;
226 mutable bool m_removedChild;
229 : m_remove(false), m_removedChild(false)
232 bool pre(const scene::Path& path, scene::Instance& instance) const
234 m_removedChild = false;
236 Selectable* selectable = Instance_getSelectable(instance);
238 && selectable->isSelected()
240 && !path.top().get().isRoot())
244 return false;// dont traverse into child elements
248 void post(const scene::Path& path, scene::Instance& instance) const
253 m_removedChild = false;
255 // delete empty entities
256 Entity* entity = Node_getEntity(path.top());
258 && path.top().get_pointer() != Map_FindWorldspawn(g_map)
259 && Node_getTraversable(path.top())->empty())
261 Path_deleteTop(path);
265 // node should be removed
268 if(Node_isEntity(path.parent()) != 0)
270 m_removedChild = true;
274 Path_deleteTop(path);
279 void Scene_DeleteSelected(scene::Graph& graph)
281 graph.traverse(DeleteSelected());
285 void Select_Delete (void)
287 Scene_DeleteSelected(GlobalSceneGraph());
290 class InvertSelectionWalker : public scene::Graph::Walker
292 SelectionSystem::EMode m_mode;
293 mutable Selectable* m_selectable;
295 InvertSelectionWalker(SelectionSystem::EMode mode)
296 : m_mode(mode), m_selectable(0)
299 bool pre(const scene::Path& path, scene::Instance& instance) const
301 Selectable* selectable = Instance_getSelectable(instance);
306 case SelectionSystem::eEntity:
307 if(Node_isEntity(path.top()) != 0)
309 m_selectable = path.top().get().visible() ? selectable : 0;
312 case SelectionSystem::ePrimitive:
313 m_selectable = path.top().get().visible() ? selectable : 0;
315 case SelectionSystem::eComponent:
321 void post(const scene::Path& path, scene::Instance& instance) const
323 if(m_selectable != 0)
325 m_selectable->setSelected(!m_selectable->isSelected());
331 void Scene_Invert_Selection(scene::Graph& graph)
333 graph.traverse(InvertSelectionWalker(GlobalSelectionSystem().Mode()));
338 Scene_Invert_Selection(GlobalSceneGraph());
341 class ExpandSelectionToEntitiesWalker : public scene::Graph::Walker
343 mutable std::size_t m_depth;
344 NodeSmartReference worldspawn;
346 ExpandSelectionToEntitiesWalker() : m_depth(0), worldspawn(Map_FindOrInsertWorldspawn(g_map))
349 bool pre(const scene::Path& path, scene::Instance& instance) const
354 NodeSmartReference me(path.top().get());
358 if(m_depth == 2) // entity depth
360 // traverse and select children if any one is selected
361 if(instance.childSelected())
362 Instance_setSelected(instance, true);
363 return Node_getEntity(path.top())->isContainer() && instance.isSelected();
365 else if(m_depth == 3) // primitive depth
367 Instance_setSelected(instance, true);
372 void post(const scene::Path& path, scene::Instance& instance) const
378 void Scene_ExpandSelectionToEntities()
380 GlobalSceneGraph().traverse(ExpandSelectionToEntitiesWalker());
386 void Selection_UpdateWorkzone()
388 if(GlobalSelectionSystem().countSelected() != 0)
390 Select_GetBounds(g_select_workzone.d_work_min, g_select_workzone.d_work_max);
393 typedef FreeCaller<Selection_UpdateWorkzone> SelectionUpdateWorkzoneCaller;
395 IdleDraw g_idleWorkzone = IdleDraw(SelectionUpdateWorkzoneCaller());
398 const select_workzone_t& Select_getWorkZone()
400 g_idleWorkzone.flush();
401 return g_select_workzone;
404 void UpdateWorkzone_ForSelection()
406 g_idleWorkzone.queueDraw();
409 // update the workzone to the current selection
410 void UpdateWorkzone_ForSelectionChanged(const Selectable& selectable)
412 if(selectable.isSelected())
414 UpdateWorkzone_ForSelection();
418 void Select_SetShader(const char* shader)
420 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
422 Scene_BrushSetShader_Selected(GlobalSceneGraph(), shader);
423 Scene_PatchSetShader_Selected(GlobalSceneGraph(), shader);
425 Scene_BrushSetShader_Component_Selected(GlobalSceneGraph(), shader);
428 void Select_SetTexdef(const TextureProjection& projection)
430 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
432 Scene_BrushSetTexdef_Selected(GlobalSceneGraph(), projection);
434 Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection);
437 void Select_SetFlags(const ContentsFlagsValue& flags)
439 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
441 Scene_BrushSetFlags_Selected(GlobalSceneGraph(), flags);
443 Scene_BrushSetFlags_Component_Selected(GlobalSceneGraph(), flags);
446 void Select_GetBounds (Vector3& mins, Vector3& maxs)
449 Scene_BoundsSelected(GlobalSceneGraph(), bounds);
450 maxs = vector3_added(bounds.origin, bounds.extents);
451 mins = vector3_subtracted(bounds.origin, bounds.extents);
454 void Select_GetMid (Vector3& mid)
457 Scene_BoundsSelected(GlobalSceneGraph(), bounds);
458 mid = vector3_snapped(bounds.origin);
462 void Select_FlipAxis (int axis)
464 Vector3 flip(1, 1, 1);
466 GlobalSelectionSystem().scaleSelected(flip);
470 void Select_Scale(float x, float y, float z)
472 GlobalSelectionSystem().scaleSelected(Vector3(x, y, z));
488 inline Matrix4 matrix4_rotation_for_axis90(axis_t axis, sign_t sign)
493 if(sign == eSignPositive)
495 return matrix4_rotation_for_sincos_x(1, 0);
499 return matrix4_rotation_for_sincos_x(-1, 0);
502 if(sign == eSignPositive)
504 return matrix4_rotation_for_sincos_y(1, 0);
508 return matrix4_rotation_for_sincos_y(-1, 0);
510 default://case eAxisZ:
511 if(sign == eSignPositive)
513 return matrix4_rotation_for_sincos_z(1, 0);
517 return matrix4_rotation_for_sincos_z(-1, 0);
522 inline void matrix4_rotate_by_axis90(Matrix4& matrix, axis_t axis, sign_t sign)
524 matrix4_multiply_by_matrix4(matrix, matrix4_rotation_for_axis90(axis, sign));
527 inline void matrix4_pivoted_rotate_by_axis90(Matrix4& matrix, axis_t axis, sign_t sign, const Vector3& pivotpoint)
529 matrix4_translate_by_vec3(matrix, pivotpoint);
530 matrix4_rotate_by_axis90(matrix, axis, sign);
531 matrix4_translate_by_vec3(matrix, vector3_negated(pivotpoint));
534 inline Quaternion quaternion_for_axis90(axis_t axis, sign_t sign)
540 if(sign == eSignPositive)
542 return Quaternion(c_half_sqrt2f, 0, 0, c_half_sqrt2f);
546 return Quaternion(-c_half_sqrt2f, 0, 0, -c_half_sqrt2f);
549 if(sign == eSignPositive)
551 return Quaternion(0, c_half_sqrt2f, 0, c_half_sqrt2f);
555 return Quaternion(0, -c_half_sqrt2f, 0, -c_half_sqrt2f);
557 default://case eAxisZ:
558 if(sign == eSignPositive)
560 return Quaternion(0, 0, c_half_sqrt2f, c_half_sqrt2f);
564 return Quaternion(0, 0, -c_half_sqrt2f, -c_half_sqrt2f);
568 quaternion_for_matrix4_rotation(matrix4_rotation_for_axis90((axis_t)axis, (deg > 0) ? eSignPositive : eSignNegative));
572 void Select_RotateAxis (int axis, float deg)
574 if(fabs(deg) == 90.f)
576 GlobalSelectionSystem().rotateSelected(quaternion_for_axis90((axis_t)axis, (deg > 0) ? eSignPositive : eSignNegative));
583 GlobalSelectionSystem().rotateSelected(quaternion_for_matrix4_rotation(matrix4_rotation_for_x_degrees(deg)));
586 GlobalSelectionSystem().rotateSelected(quaternion_for_matrix4_rotation(matrix4_rotation_for_y_degrees(deg)));
589 GlobalSelectionSystem().rotateSelected(quaternion_for_matrix4_rotation(matrix4_rotation_for_z_degrees(deg)));
596 void Select_ShiftTexture(float x, float y)
598 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
600 Scene_BrushShiftTexdef_Selected(GlobalSceneGraph(), x, y);
601 Scene_PatchTranslateTexture_Selected(GlobalSceneGraph(), x, y);
603 //globalOutputStream() << "shift selected face textures: s=" << x << " t=" << y << '\n';
604 Scene_BrushShiftTexdef_Component_Selected(GlobalSceneGraph(), x, y);
607 void Select_ScaleTexture(float x, float y)
609 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
611 Scene_BrushScaleTexdef_Selected(GlobalSceneGraph(), x, y);
612 Scene_PatchScaleTexture_Selected(GlobalSceneGraph(), x, y);
614 Scene_BrushScaleTexdef_Component_Selected(GlobalSceneGraph(), x, y);
617 void Select_RotateTexture(float amt)
619 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
621 Scene_BrushRotateTexdef_Selected(GlobalSceneGraph(), amt);
622 Scene_PatchRotateTexture_Selected(GlobalSceneGraph(), amt);
624 Scene_BrushRotateTexdef_Component_Selected(GlobalSceneGraph(), amt);
627 // TTimo modified to handle shader architecture:
628 // expects shader names at input, comparison relies on shader names .. texture names no longer relevant
629 void FindReplaceTextures(const char* pFind, const char* pReplace, bool bSelected)
631 if(!texdef_name_valid(pFind))
633 globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pFind << "', aborted\n";
636 if(!texdef_name_valid(pReplace))
638 globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pReplace << "', aborted\n";
642 StringOutputStream command;
643 command << "textureFindReplace -find " << pFind << " -replace " << pReplace;
644 UndoableCommand undo(command.c_str());
648 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
650 Scene_BrushFindReplaceShader_Selected(GlobalSceneGraph(), pFind, pReplace);
651 Scene_PatchFindReplaceShader_Selected(GlobalSceneGraph(), pFind, pReplace);
653 Scene_BrushFindReplaceShader_Component_Selected(GlobalSceneGraph(), pFind, pReplace);
657 Scene_BrushFindReplaceShader(GlobalSceneGraph(), pFind, pReplace);
658 Scene_PatchFindReplaceShader(GlobalSceneGraph(), pFind, pReplace);
662 typedef std::vector<const char*> PropertyValues;
664 bool propertyvalues_contain(const PropertyValues& propertyvalues, const char *str)
666 for(PropertyValues::const_iterator i = propertyvalues.begin(); i != propertyvalues.end(); ++i)
668 if(string_equal(str, *i))
676 class EntityFindByPropertyValueWalker : public scene::Graph::Walker
678 const PropertyValues& m_propertyvalues;
681 EntityFindByPropertyValueWalker(const char *prop, const PropertyValues& propertyvalues)
682 : m_propertyvalues(propertyvalues), m_prop(prop)
685 bool pre(const scene::Path& path, scene::Instance& instance) const
687 Entity* entity = Node_getEntity(path.top());
689 && propertyvalues_contain(m_propertyvalues, entity->getKeyValue(m_prop)))
691 Instance_getSelectable(instance)->setSelected(true);
697 void Scene_EntitySelectByPropertyValues(scene::Graph& graph, const char *prop, const PropertyValues& propertyvalues)
699 graph.traverse(EntityFindByPropertyValueWalker(prop, propertyvalues));
702 class EntityGetSelectedPropertyValuesWalker : public scene::Graph::Walker
704 PropertyValues& m_propertyvalues;
707 EntityGetSelectedPropertyValuesWalker(const char *prop, PropertyValues& propertyvalues)
708 : m_propertyvalues(propertyvalues), m_prop(prop)
711 bool pre(const scene::Path& path, scene::Instance& instance) const
713 Selectable* selectable = Instance_getSelectable(instance);
715 && selectable->isSelected())
717 Entity* entity = Node_getEntity(path.top());
720 if(!propertyvalues_contain(m_propertyvalues, entity->getKeyValue(m_prop)))
721 m_propertyvalues.push_back(entity->getKeyValue(m_prop));
728 void Scene_EntityGetPropertyValues(scene::Graph& graph, const char *prop, PropertyValues& propertyvalues)
730 graph.traverse(EntityGetSelectedPropertyValuesWalker(prop, propertyvalues));
733 void Select_AllOfType()
735 if(GlobalSelectionSystem().Mode() == SelectionSystem::eComponent)
737 if(GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace)
739 GlobalSelectionSystem().setSelectedAllComponents(false);
740 Scene_BrushSelectByShader_Component(GlobalSceneGraph(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
745 PropertyValues propertyvalues;
746 const char *prop = EntityInspector_getCurrentKey();
749 Scene_EntityGetPropertyValues(GlobalSceneGraph(), prop, propertyvalues);
750 GlobalSelectionSystem().setSelectedAll(false);
751 if(!propertyvalues.empty())
753 Scene_EntitySelectByPropertyValues(GlobalSceneGraph(), prop, propertyvalues);
757 Scene_BrushSelectByShader(GlobalSceneGraph(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
758 Scene_PatchSelectByShader(GlobalSceneGraph(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
763 void Select_Inside(void)
765 SelectByBounds<SelectionPolicy_Inside>::DoSelection();
768 void Select_Touching(void)
770 SelectByBounds<SelectionPolicy_Touching>::DoSelection(false);
773 void Select_FitTexture(float horizontal, float vertical)
775 if(GlobalSelectionSystem().Mode() != SelectionSystem::eComponent)
777 Scene_BrushFitTexture_Selected(GlobalSceneGraph(), horizontal, vertical);
779 Scene_BrushFitTexture_Component_Selected(GlobalSceneGraph(), horizontal, vertical);
784 inline void hide_node(scene::Node& node, bool hide)
787 ? node.enable(scene::Node::eHidden)
788 : node.disable(scene::Node::eHidden);
791 class HideSelectedWalker : public scene::Graph::Walker
795 HideSelectedWalker(bool hide)
799 bool pre(const scene::Path& path, scene::Instance& instance) const
801 Selectable* selectable = Instance_getSelectable(instance);
803 && selectable->isSelected())
805 hide_node(path.top(), m_hide);
811 void Scene_Hide_Selected(bool hide)
813 GlobalSceneGraph().traverse(HideSelectedWalker(hide));
818 Scene_Hide_Selected(true);
825 GlobalSelectionSystem().setSelectedAll(false);
829 class HideAllWalker : public scene::Graph::Walker
833 HideAllWalker(bool hide)
837 bool pre(const scene::Path& path, scene::Instance& instance) const
839 hide_node(path.top(), m_hide);
844 void Scene_Hide_All(bool hide)
846 GlobalSceneGraph().traverse(HideAllWalker(hide));
849 void Select_ShowAllHidden()
851 Scene_Hide_All(false);
857 void Selection_Flipx()
859 UndoableCommand undo("mirrorSelected -axis x");
863 void Selection_Flipy()
865 UndoableCommand undo("mirrorSelected -axis y");
869 void Selection_Flipz()
871 UndoableCommand undo("mirrorSelected -axis z");
875 void Selection_Rotatex()
877 UndoableCommand undo("rotateSelected -axis x -angle -90");
878 Select_RotateAxis(0,-90);
881 void Selection_Rotatey()
883 UndoableCommand undo("rotateSelected -axis y -angle 90");
884 Select_RotateAxis(1, 90);
887 void Selection_Rotatez()
889 UndoableCommand undo("rotateSelected -axis z -angle -90");
890 Select_RotateAxis(2,-90);
895 void Nudge(int nDim, float fNudge)
897 Vector3 translate(0, 0, 0);
898 translate[nDim] = fNudge;
900 GlobalSelectionSystem().translateSelected(translate);
903 void Selection_NudgeZ(float amount)
905 StringOutputStream command;
906 command << "nudgeSelected -axis z -amount " << amount;
907 UndoableCommand undo(command.c_str());
912 void Selection_MoveDown()
914 Selection_NudgeZ(-GetGridSize());
917 void Selection_MoveUp()
919 Selection_NudgeZ(GetGridSize());
922 void SceneSelectionChange(const Selectable& selectable)
927 SignalHandlerId Selection_boundsChanged;
929 void Selection_construct()
931 typedef FreeCaller1<const Selectable&, SceneSelectionChange> SceneSelectionChangeCaller;
932 GlobalSelectionSystem().addSelectionChangeCallback(SceneSelectionChangeCaller());
933 typedef FreeCaller1<const Selectable&, UpdateWorkzone_ForSelectionChanged> UpdateWorkzoneForSelectionChangedCaller;
934 GlobalSelectionSystem().addSelectionChangeCallback(UpdateWorkzoneForSelectionChangedCaller());
935 typedef FreeCaller<UpdateWorkzone_ForSelection> UpdateWorkzoneForSelectionCaller;
936 Selection_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback(UpdateWorkzoneForSelectionCaller());
939 void Selection_destroy()
941 GlobalSceneGraph().removeBoundsChangedCallback(Selection_boundsChanged);
946 #include <gtk/gtkbox.h>
947 #include <gtk/gtkspinbutton.h>
948 #include <gtk/gtktable.h>
949 #include <gtk/gtklabel.h>
950 #include <gdk/gdkkeysyms.h>
953 inline Quaternion quaternion_for_euler_xyz_degrees(const Vector3& eulerXYZ)
956 return quaternion_for_matrix4_rotation(matrix4_rotation_for_euler_xyz_degrees(eulerXYZ));
958 return quaternion_multiplied_by_quaternion(
959 quaternion_multiplied_by_quaternion(
960 quaternion_for_z(degrees_to_radians(eulerXYZ[2])),
961 quaternion_for_y(degrees_to_radians(eulerXYZ[1]))
963 quaternion_for_x(degrees_to_radians(eulerXYZ[0]))
966 double cx = cos(degrees_to_radians(eulerXYZ[0] * 0.5));
967 double sx = sin(degrees_to_radians(eulerXYZ[0] * 0.5));
968 double cy = cos(degrees_to_radians(eulerXYZ[1] * 0.5));
969 double sy = sin(degrees_to_radians(eulerXYZ[1] * 0.5));
970 double cz = cos(degrees_to_radians(eulerXYZ[2] * 0.5));
971 double sz = sin(degrees_to_radians(eulerXYZ[2] * 0.5));
974 cz * cy * sx - sz * sy * cx,
975 cz * sy * cx + sz * cy * sx,
976 sz * cy * cx - cz * sy * sx,
977 cz * cy * cx + sz * sy * sx
990 static gboolean rotatedlg_apply (GtkWidget *widget, RotateDialog* rotateDialog)
994 eulerXYZ[0] = static_cast<float>(gtk_spin_button_get_value(rotateDialog->x));
995 eulerXYZ[1] = static_cast<float>(gtk_spin_button_get_value(rotateDialog->y));
996 eulerXYZ[2] = static_cast<float>(gtk_spin_button_get_value(rotateDialog->z));
998 StringOutputStream command;
999 command << "rotateSelectedEulerXYZ -x " << eulerXYZ[0] << " -y " << eulerXYZ[1] << " -z " << eulerXYZ[2];
1000 UndoableCommand undo(command.c_str());
1002 GlobalSelectionSystem().rotateSelected(quaternion_for_euler_xyz_degrees(eulerXYZ));
1006 static gboolean rotatedlg_cancel (GtkWidget *widget, RotateDialog* rotateDialog)
1008 gtk_widget_hide(GTK_WIDGET(rotateDialog->window));
1010 gtk_spin_button_set_value(rotateDialog->x, 0.0f); // reset to 0 on close
1011 gtk_spin_button_set_value(rotateDialog->y, 0.0f);
1012 gtk_spin_button_set_value(rotateDialog->z, 0.0f);
1017 static gboolean rotatedlg_ok (GtkWidget *widget, RotateDialog* rotateDialog)
1019 rotatedlg_apply(widget, rotateDialog);
1020 rotatedlg_cancel(widget, rotateDialog);
1024 static gboolean rotatedlg_delete (GtkWidget *widget, GdkEventAny *event, RotateDialog* rotateDialog)
1026 rotatedlg_cancel(widget, rotateDialog);
1030 RotateDialog g_rotate_dialog;
1033 if(g_rotate_dialog.window == NULL)
1035 g_rotate_dialog.window = create_dialog_window(MainFrame_getWindow(), "Arbitrary rotation", G_CALLBACK(rotatedlg_delete), &g_rotate_dialog);
1037 GtkAccelGroup* accel = gtk_accel_group_new();
1038 gtk_window_add_accel_group(g_rotate_dialog.window, accel);
1041 GtkHBox* hbox = create_dialog_hbox(4, 4);
1042 gtk_container_add(GTK_CONTAINER(g_rotate_dialog.window), GTK_WIDGET(hbox));
1044 GtkTable* table = create_dialog_table(3, 2, 4, 4);
1045 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0);
1047 GtkWidget* label = gtk_label_new (" X ");
1048 gtk_widget_show (label);
1049 gtk_table_attach(table, label, 0, 1, 0, 1,
1050 (GtkAttachOptions) (0),
1051 (GtkAttachOptions) (0), 0, 0);
1054 GtkWidget* label = gtk_label_new (" Y ");
1055 gtk_widget_show (label);
1056 gtk_table_attach(table, label, 0, 1, 1, 2,
1057 (GtkAttachOptions) (0),
1058 (GtkAttachOptions) (0), 0, 0);
1061 GtkWidget* label = gtk_label_new (" Z ");
1062 gtk_widget_show (label);
1063 gtk_table_attach(table, label, 0, 1, 2, 3,
1064 (GtkAttachOptions) (0),
1065 (GtkAttachOptions) (0), 0, 0);
1068 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -359, 359, 1, 10, 0));
1069 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
1070 gtk_widget_show(GTK_WIDGET(spin));
1071 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 0, 1,
1072 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1073 (GtkAttachOptions) (0), 0, 0);
1074 gtk_widget_set_size_request(GTK_WIDGET(spin), 64, -1);
1075 gtk_spin_button_set_wrap(spin, TRUE);
1077 gtk_widget_grab_focus(GTK_WIDGET(spin));
1079 g_rotate_dialog.x = spin;
1082 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -359, 359, 1, 10, 0));
1083 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
1084 gtk_widget_show(GTK_WIDGET(spin));
1085 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 1, 2,
1086 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1087 (GtkAttachOptions) (0), 0, 0);
1088 gtk_widget_set_size_request(GTK_WIDGET(spin), 64, -1);
1089 gtk_spin_button_set_wrap(spin, TRUE);
1091 g_rotate_dialog.y = spin;
1094 GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -359, 359, 1, 10, 0));
1095 GtkSpinButton* spin = GTK_SPIN_BUTTON(gtk_spin_button_new(adj, 1, 0));
1096 gtk_widget_show(GTK_WIDGET(spin));
1097 gtk_table_attach(table, GTK_WIDGET(spin), 1, 2, 2, 3,
1098 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1099 (GtkAttachOptions) (0), 0, 0);
1100 gtk_widget_set_size_request(GTK_WIDGET(spin), 64, -1);
1101 gtk_spin_button_set_wrap(spin, TRUE);
1103 g_rotate_dialog.z = spin;
1107 GtkVBox* vbox = create_dialog_vbox(4);
1108 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0);
1110 GtkButton* button = create_dialog_button("OK", G_CALLBACK(rotatedlg_ok), &g_rotate_dialog);
1111 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1112 widget_make_default(GTK_WIDGET(button));
1113 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
1116 GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(rotatedlg_cancel), &g_rotate_dialog);
1117 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1118 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
1121 GtkButton* button = create_dialog_button("Apply", G_CALLBACK(rotatedlg_apply), &g_rotate_dialog);
1122 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1128 gtk_widget_show(GTK_WIDGET(g_rotate_dialog.window));
1147 static gboolean scaledlg_apply (GtkWidget *widget, ScaleDialog* scaleDialog)
1151 sx = static_cast<float>(atof(gtk_entry_get_text (GTK_ENTRY (scaleDialog->x))));
1152 sy = static_cast<float>(atof(gtk_entry_get_text (GTK_ENTRY (scaleDialog->y))));
1153 sz = static_cast<float>(atof(gtk_entry_get_text (GTK_ENTRY (scaleDialog->z))));
1155 StringOutputStream command;
1156 command << "scaleSelected -x " << sx << " -y " << sy << " -z " << sz;
1157 UndoableCommand undo(command.c_str());
1159 Select_Scale(sx, sy, sz);
1164 static gboolean scaledlg_cancel (GtkWidget *widget, ScaleDialog* scaleDialog)
1166 gtk_widget_hide(GTK_WIDGET(scaleDialog->window));
1168 gtk_entry_set_text (GTK_ENTRY(scaleDialog->x), "1.0");
1169 gtk_entry_set_text (GTK_ENTRY(scaleDialog->y), "1.0");
1170 gtk_entry_set_text (GTK_ENTRY(scaleDialog->z), "1.0");
1175 static gboolean scaledlg_ok (GtkWidget *widget, ScaleDialog* scaleDialog)
1177 scaledlg_apply(widget, scaleDialog);
1178 scaledlg_cancel(widget, scaleDialog);
1182 static gboolean scaledlg_delete (GtkWidget *widget, GdkEventAny *event, ScaleDialog* scaleDialog)
1184 scaledlg_cancel(widget, scaleDialog);
1188 ScaleDialog g_scale_dialog;
1192 if(g_scale_dialog.window == NULL)
1194 g_scale_dialog.window = create_dialog_window(MainFrame_getWindow(), "Arbitrary scale", G_CALLBACK(scaledlg_delete), &g_scale_dialog);
1196 GtkAccelGroup* accel = gtk_accel_group_new();
1197 gtk_window_add_accel_group(g_scale_dialog.window, accel);
1200 GtkHBox* hbox = create_dialog_hbox(4, 4);
1201 gtk_container_add(GTK_CONTAINER(g_scale_dialog.window), GTK_WIDGET(hbox));
1203 GtkTable* table = create_dialog_table(3, 2, 4, 4);
1204 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0);
1206 GtkWidget* label = gtk_label_new (" X ");
1207 gtk_widget_show (label);
1208 gtk_table_attach(table, label, 0, 1, 0, 1,
1209 (GtkAttachOptions) (0),
1210 (GtkAttachOptions) (0), 0, 0);
1213 GtkWidget* label = gtk_label_new (" Y ");
1214 gtk_widget_show (label);
1215 gtk_table_attach(table, label, 0, 1, 1, 2,
1216 (GtkAttachOptions) (0),
1217 (GtkAttachOptions) (0), 0, 0);
1220 GtkWidget* label = gtk_label_new (" Z ");
1221 gtk_widget_show (label);
1222 gtk_table_attach(table, label, 0, 1, 2, 3,
1223 (GtkAttachOptions) (0),
1224 (GtkAttachOptions) (0), 0, 0);
1227 GtkWidget* entry = gtk_entry_new();
1228 gtk_entry_set_text (GTK_ENTRY(entry), "1.0");
1229 gtk_widget_show (entry);
1230 gtk_table_attach(table, entry, 1, 2, 0, 1,
1231 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1232 (GtkAttachOptions) (0), 0, 0);
1234 g_scale_dialog.x = entry;
1237 GtkWidget* entry = gtk_entry_new();
1238 gtk_entry_set_text (GTK_ENTRY(entry), "1.0");
1239 gtk_widget_show (entry);
1240 gtk_table_attach(table, entry, 1, 2, 1, 2,
1241 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1242 (GtkAttachOptions) (0), 0, 0);
1244 g_scale_dialog.y = entry;
1247 GtkWidget* entry = gtk_entry_new();
1248 gtk_entry_set_text (GTK_ENTRY(entry), "1.0");
1249 gtk_widget_show (entry);
1250 gtk_table_attach(table, entry, 1, 2, 2, 3,
1251 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1252 (GtkAttachOptions) (0), 0, 0);
1254 g_scale_dialog.z = entry;
1258 GtkVBox* vbox = create_dialog_vbox(4);
1259 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0);
1261 GtkButton* button = create_dialog_button("OK", G_CALLBACK(scaledlg_ok), &g_scale_dialog);
1262 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1263 widget_make_default(GTK_WIDGET(button));
1264 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
1267 GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(scaledlg_cancel), &g_scale_dialog);
1268 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1269 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
1272 GtkButton* button = create_dialog_button("Apply", G_CALLBACK(scaledlg_apply), &g_scale_dialog);
1273 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1279 gtk_widget_show(GTK_WIDGET(g_scale_dialog.window));