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 "PreferenceDialog.h"
21 
22 #include "PreferenceManager.h"
23 #include "Preferences.h"
24 #include "IO/Path.h"
25 #include "IO/ResourceUtils.h"
26 #include "View/BorderLine.h"
27 #include "View/GamesPreferencePane.h"
28 #include "View/KeyboardPreferencePane.h"
29 #include "View/MousePreferencePane.h"
30 #include "View/ViewPreferencePane.h"
31 #include "View/ViewConstants.h"
32 #include "View/PreferencePane.h"
33 #include "View/wxUtils.h"
34 
35 #include <wx/button.h>
36 #include <wx/panel.h>
37 #include <wx/sizer.h>
38 #include <wx/simplebook.h>
39 #include <wx/stdpaths.h>
40 #include <wx/toolbar.h>
41 
42 namespace TrenchBroom {
43     namespace View {
IMPLEMENT_DYNAMIC_CLASS(PreferenceDialog,wxDialog)44         IMPLEMENT_DYNAMIC_CLASS(PreferenceDialog, wxDialog)
45 
46         PreferenceDialog::PreferenceDialog() :
47         m_toolBar(NULL),
48         m_book(NULL) {
49             Create();
50         }
51 
Create()52         bool PreferenceDialog::Create() {
53             if (!wxDialog::Create(NULL, wxID_ANY, "Preferences"))
54                 return false;
55 
56             createGui();
57             bindEvents();
58             switchToPane(PrefPane_First);
59             currentPane()->updateControls();
60             SetClientSize(currentPane()->GetMinSize());
61 
62             return true;
63         }
64 
OnToolClicked(wxCommandEvent & event)65         void PreferenceDialog::OnToolClicked(wxCommandEvent& event) {
66             if (IsBeingDeleted()) return;
67 
68             const PrefPane newPane = static_cast<PrefPane>(event.GetId());
69             switchToPane(newPane);
70         }
71 
OnOKClicked(wxCommandEvent & event)72         void PreferenceDialog::OnOKClicked(wxCommandEvent& event) {
73             if (IsBeingDeleted()) return;
74 
75             if (!currentPane()->validate())
76                 return;
77 
78             PreferenceManager& prefs = PreferenceManager::instance();
79             if (!prefs.saveInstantly())
80                 prefs.saveChanges();
81             EndModal(wxID_OK);
82         }
83 
OnApplyClicked(wxCommandEvent & event)84         void PreferenceDialog::OnApplyClicked(wxCommandEvent& event) {
85             if (IsBeingDeleted()) return;
86 
87             if (!currentPane()->validate())
88                 return;
89 
90             PreferenceManager& prefs = PreferenceManager::instance();
91             if (!prefs.saveInstantly())
92                 prefs.saveChanges();
93         }
94 
OnCancelClicked(wxCommandEvent & event)95         void PreferenceDialog::OnCancelClicked(wxCommandEvent& event) {
96             if (IsBeingDeleted()) return;
97 
98             PreferenceManager& prefs = PreferenceManager::instance();
99             if (!prefs.saveInstantly())
100                 prefs.discardChanges();
101             EndModal(wxID_CANCEL);
102         }
103 
OnClose(wxCloseEvent & event)104         void PreferenceDialog::OnClose(wxCloseEvent& event) {
105             if (IsBeingDeleted()) return;
106 
107             if (!currentPane()->validate() && event.CanVeto()) {
108                 event.Veto();
109                 return;
110             }
111 
112             PreferenceManager& prefs = PreferenceManager::instance();
113             if (!prefs.saveInstantly()) {
114                 switch (event.GetId()) {
115                     case wxID_OK:
116                         prefs.saveChanges();
117                         break;
118                     default:
119                         prefs.discardChanges();
120                         break;
121 
122                 }
123             }
124         }
125 
OnFileClose(wxCommandEvent & event)126         void PreferenceDialog::OnFileClose(wxCommandEvent& event) {
127             if (IsBeingDeleted()) return;
128 
129             if (!currentPane()->validate()) {
130                 event.Skip();
131                 return;
132             }
133 
134             PreferenceManager& prefs = PreferenceManager::instance();
135             prefs.discardChanges(); // does nothing if the preferences save changes instantly
136             Close();
137         }
138 
OnResetClicked(wxCommandEvent & event)139         void PreferenceDialog::OnResetClicked(wxCommandEvent& event) {
140             if (IsBeingDeleted()) return;
141 
142             assert(currentPane()->canResetToDefaults());
143             currentPane()->resetToDefaults();
144         }
145 
OnUpdateReset(wxUpdateUIEvent & event)146         void PreferenceDialog::OnUpdateReset(wxUpdateUIEvent& event) {
147             if (IsBeingDeleted()) return;
148 
149             event.Enable(currentPane()->canResetToDefaults());
150         }
151 
createGui()152         void PreferenceDialog::createGui() {
153             SetIcon(wxIcon(wxStandardPaths::Get().GetInstallPrefix() + "/share/icons/hicolor/128x128/apps/trenchbroom.png", wxBITMAP_TYPE_PNG));
154 
155             const wxBitmap gamesImage = IO::loadImageResource("GeneralPreferences.png");
156             const wxBitmap generalImage = IO::loadImageResource("GeneralPreferences.png");
157             const wxBitmap mouseImage = IO::loadImageResource("MousePreferences.png");
158             const wxBitmap keyboardImage = IO::loadImageResource("KeyboardPreferences.png");
159 
160             m_toolBar = new wxToolBar(this, wxID_ANY);
161             m_toolBar->AddCheckTool(PrefPane_Games, "Games", gamesImage, wxNullBitmap);
162             m_toolBar->AddCheckTool(PrefPane_View, "View", generalImage, wxNullBitmap);
163             m_toolBar->AddCheckTool(PrefPane_Mouse, "Mouse", mouseImage, wxNullBitmap);
164             m_toolBar->AddCheckTool(PrefPane_Keyboard, "Keyboard", keyboardImage, wxNullBitmap);
165             m_toolBar->Realize();
166 
167             m_book = new wxSimplebook(this);
168             m_book->AddPage(new GamesPreferencePane(m_book), "Games");
169             m_book->AddPage(new ViewPreferencePane(m_book), "View");
170             m_book->AddPage(new MousePreferencePane(m_book), "Mouse");
171             m_book->AddPage(new KeyboardPreferencePane(m_book), "Keyboard");
172 
173             wxButton* resetButton = new wxButton(this, wxID_ANY, "Reset to defaults");
174             resetButton->Bind(wxEVT_BUTTON, &PreferenceDialog::OnResetClicked, this);
175             resetButton->Bind(wxEVT_UPDATE_UI, &PreferenceDialog::OnUpdateReset, this);
176 
177             wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
178             sizer->Add(m_toolBar, 0, wxEXPAND);
179 #if !defined __APPLE__
180             wxWindow* line = new BorderLine(this, BorderLine::Direction_Horizontal);
181             sizer->Add(line, 0, wxEXPAND);
182             sizer->SetItemMinSize(line, wxSize(wxDefaultCoord, 1));
183 #endif
184             sizer->Add(m_book, 1, wxEXPAND);
185 
186             wxSizer* bottomSizer = new wxBoxSizer(wxHORIZONTAL);
187 #if !defined __APPLE__
188 			bottomSizer->Add(resetButton, 0, wxALIGN_CENTER_VERTICAL);
189 			bottomSizer->AddStretchSpacer();
190             bottomSizer->Add(CreateButtonSizer(wxOK | wxAPPLY | wxCANCEL));
191 #else
192 			bottomSizer->Add(resetButton, 0, wxALL, LayoutConstants::DialogOuterMargin);
193 			bottomSizer->AddStretchSpacer();
194 #endif
195 
196             sizer->Add(wrapDialogButtonSizer(bottomSizer, this), 0, wxEXPAND);
197 
198             SetSizer(sizer);
199         }
200 
bindEvents()201         void PreferenceDialog::bindEvents() {
202             Bind(wxEVT_MENU, &PreferenceDialog::OnFileClose, this, wxID_CLOSE);
203             Bind(wxEVT_BUTTON, &PreferenceDialog::OnOKClicked, this, wxID_OK);
204             Bind(wxEVT_BUTTON, &PreferenceDialog::OnApplyClicked, this, wxID_APPLY);
205             Bind(wxEVT_BUTTON, &PreferenceDialog::OnCancelClicked, this, wxID_CANCEL);
206             Bind(wxEVT_TOOL, &PreferenceDialog::OnToolClicked, this, PrefPane_First, PrefPane_Last);
207         }
208 
switchToPane(const PrefPane pane)209         void PreferenceDialog::switchToPane(const PrefPane pane) {
210             if (currentPaneId() == pane && currentPane() != NULL) {
211                 toggleTools(currentPaneId());
212                 return;
213             }
214 
215             if (currentPane() != NULL && !currentPane()->validate()) {
216                 toggleTools(currentPaneId());
217                 return;
218             }
219 
220             toggleTools(pane);
221             m_book->SetSelection(static_cast<size_t>(pane));
222             currentPane()->updateControls();
223 
224             GetSizer()->SetItemMinSize(m_book, currentPane()->GetMinSize());
225             Fit();
226 #if defined __APPLE__
227             updateAcceleratorTable(pane);
228 #endif
229         }
230 
toggleTools(const PrefPane pane)231         void PreferenceDialog::toggleTools(const PrefPane pane) {
232             for (int i = PrefPane_First; i <= PrefPane_Last; ++i)
233                 m_toolBar->ToggleTool(i, pane == i);
234         }
235 
currentPane() const236         PreferencePane* PreferenceDialog::currentPane() const {
237             return static_cast<PreferencePane*>(m_book->GetCurrentPage());
238         }
239 
currentPaneId() const240         PreferenceDialog::PrefPane PreferenceDialog::currentPaneId() const {
241             return static_cast<PrefPane>(m_book->GetSelection());
242         }
243 
updateAcceleratorTable(const PrefPane pane)244         void PreferenceDialog::updateAcceleratorTable(const PrefPane pane) {
245             // allow the dialog to be closed using CMD+W
246             // but only if the keyboard preference pane is not active
247             if (pane != PrefPane_Keyboard) {
248                 wxAcceleratorEntry acceleratorEntries[1];
249                 acceleratorEntries[0].Set(wxACCEL_CMD, static_cast<int>('W'), wxID_CLOSE);
250                 wxAcceleratorTable accceleratorTable(1, acceleratorEntries);
251                 SetAcceleratorTable(accceleratorTable);
252             } else {
253                 wxAcceleratorTable accceleratorTable(0, NULL);
254                 SetAcceleratorTable(accceleratorTable);
255             }
256         }
257     }
258 }
259