1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "RotateObjectsToolPage.h"
21 
22 #include "View/BorderLine.h"
23 #include "View/Grid.h"
24 #include "View/MapDocument.h"
25 #include "View/RotateObjectsTool.h"
26 #include "View/SpinControl.h"
27 #include "View/ViewConstants.h"
28 
29 #include <wx/button.h>
30 #include <wx/choice.h>
31 #include <wx/sizer.h>
32 #include <wx/stattext.h>
33 #include <wx/textctrl.h>
34 
35 namespace TrenchBroom {
36     namespace View {
RotateObjectsToolPage(wxWindow * parent,MapDocumentWPtr document,RotateObjectsTool * tool)37         RotateObjectsToolPage::RotateObjectsToolPage(wxWindow* parent, MapDocumentWPtr document, RotateObjectsTool* tool) :
38         wxPanel(parent),
39         m_document(document),
40         m_tool(tool) {
41             createGui();
42             m_angle->SetValue(Math::degrees(m_tool->angle()));
43         }
44 
setAxis(const Math::Axis::Type axis)45         void RotateObjectsToolPage::setAxis(const Math::Axis::Type axis) {
46             m_axis->SetSelection(static_cast<int>(axis));
47         }
48 
setCenter(const Vec3 & center)49         void RotateObjectsToolPage::setCenter(const Vec3& center) {
50             m_centerTxt->SetValue(center.asString());
51         }
52 
createGui()53         void RotateObjectsToolPage::createGui() {
54             wxStaticText* centerText = new wxStaticText(this, wxID_ANY, "Center");
55             m_centerTxt = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
56             m_centerTxt->SetToolTip("Hit Return to set the rotate handle to the entered coordinates.");
57 
58             m_resetCenterButton = new wxButton(this, wxID_ANY, "Reset", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
59             m_resetCenterButton->SetToolTip("Reset the position of the rotate handle to the center of the current selection.");
60 
61             wxStaticText* text1 = new wxStaticText(this, wxID_ANY, "Rotate objects");
62             wxStaticText* text2 = new wxStaticText(this, wxID_ANY, "degs about");
63             wxStaticText* text3 = new wxStaticText(this, wxID_ANY, "axis");
64             m_angle = new SpinControl(this);
65             m_angle->SetRange(-360.0, 360.0);
66             m_angle->SetValue(Math::degrees(m_tool->angle()));
67             m_angle->SetDigits(0, 2);
68 
69             wxString axes[] = { "X", "Y", "Z" };
70             m_axis = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 3, axes);
71             m_axis->SetSelection(2);
72 
73             m_rotateButton = new wxButton(this, wxID_ANY, "Apply", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
74 
75             Bind(wxEVT_IDLE, &RotateObjectsToolPage::OnIdle, this);
76             m_centerTxt->Bind(wxEVT_TEXT_ENTER, &RotateObjectsToolPage::OnCenterChanged, this);
77             m_resetCenterButton->Bind(wxEVT_BUTTON, &RotateObjectsToolPage::OnResetCenter, this);
78             m_angle->Bind(SPIN_CONTROL_EVENT, &RotateObjectsToolPage::OnAngleChanged, this);
79             m_rotateButton->Bind(wxEVT_UPDATE_UI, &RotateObjectsToolPage::OnUpdateRotateButton, this);
80             m_rotateButton->Bind(wxEVT_BUTTON, &RotateObjectsToolPage::OnRotate, this);
81 
82             BorderLine* separator = new BorderLine(this, BorderLine::Direction_Vertical);
83             separator->SetForegroundColour(Colors::separatorColor());
84 
85             wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
86             sizer->Add(centerText, 0, wxALIGN_CENTER_VERTICAL);
87             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
88             sizer->Add(m_centerTxt, 0, wxALIGN_CENTER_VERTICAL);
89             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
90             sizer->Add(m_resetCenterButton, 0, wxALIGN_CENTER_VERTICAL);
91             sizer->AddSpacer(LayoutConstants::MediumHMargin);
92             sizer->Add(separator, 0, wxEXPAND | wxTOP | wxBOTTOM, 2);
93             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
94             sizer->Add(text1, 0, wxALIGN_CENTER_VERTICAL);
95             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
96             sizer->Add(m_angle, 0, wxALIGN_CENTER_VERTICAL);
97             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
98             sizer->Add(text2, 0, wxALIGN_CENTER_VERTICAL);
99             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
100             sizer->Add(m_axis, 0, wxTOP, LayoutConstants::ChoiceTopMargin);
101             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
102             sizer->Add(text3, 0, wxALIGN_CENTER_VERTICAL);
103             sizer->AddSpacer(LayoutConstants::NarrowHMargin);
104             sizer->Add(m_rotateButton, 0, wxALIGN_CENTER_VERTICAL);
105             sizer->SetItemMinSize(m_angle, 80, wxDefaultCoord);
106 
107             SetSizer(sizer);
108         }
109 
OnIdle(wxIdleEvent & event)110         void RotateObjectsToolPage::OnIdle(wxIdleEvent& event) {
111             if (IsBeingDeleted()) return;
112 
113             const Grid& grid = lock(m_document)->grid();
114             m_angle->SetIncrements(Math::degrees(grid.angle()), 90.0, 1.0);
115         }
116 
OnCenterChanged(wxCommandEvent & event)117         void RotateObjectsToolPage::OnCenterChanged(wxCommandEvent& event) {
118             if (IsBeingDeleted()) return;
119 
120             const Vec3 center = Vec3::parse(m_centerTxt->GetValue().ToStdString());
121             m_tool->setRotationCenter(center);
122         }
123 
OnResetCenter(wxCommandEvent & event)124         void RotateObjectsToolPage::OnResetCenter(wxCommandEvent& event) {
125             if (IsBeingDeleted()) return;
126 
127             m_tool->resetRotationCenter();
128         }
129 
OnAngleChanged(SpinControlEvent & event)130         void RotateObjectsToolPage::OnAngleChanged(SpinControlEvent& event) {
131             if (IsBeingDeleted()) return;
132 
133             const double newAngleDegs = Math::correct(event.IsSpin() ? m_angle->GetValue() + event.GetValue() : event.GetValue());
134             m_angle->SetValue(newAngleDegs);
135             m_tool->setAngle(Math::radians(newAngleDegs));
136         }
137 
OnUpdateRotateButton(wxUpdateUIEvent & event)138         void RotateObjectsToolPage::OnUpdateRotateButton(wxUpdateUIEvent& event) {
139             if (IsBeingDeleted()) return;
140 
141             MapDocumentSPtr document = lock(m_document);
142             event.Enable(document->hasSelectedNodes());
143         }
144 
OnRotate(wxCommandEvent & event)145         void RotateObjectsToolPage::OnRotate(wxCommandEvent& event) {
146             if (IsBeingDeleted()) return;
147 
148             const Vec3 center = m_tool->rotationCenter();
149             const Vec3 axis = getAxis();
150             const FloatType angle = Math::radians(m_angle->GetValue());
151 
152             MapDocumentSPtr document = lock(m_document);
153             document->rotateObjects(center, axis, angle);
154         }
155 
getAxis() const156         Vec3 RotateObjectsToolPage::getAxis() const {
157             switch (m_axis->GetSelection()) {
158                 case 0:
159                     return Vec3::PosX;
160                 case 1:
161                     return Vec3::PosY;
162                 default:
163                     return Vec3::PosZ;
164             }
165         }
166     }
167 }
168