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 "FlagsEditor.h"
21 
22 #include "View/FlagChangedCommand.h"
23 #include "View/ViewConstants.h"
24 
25 #include <cassert>
26 #include <wx/checkbox.h>
27 #include <wx/sizer.h>
28 
29 namespace TrenchBroom {
30     namespace View {
FlagsEditor(wxWindow * parent,const size_t numCols)31         FlagsEditor::FlagsEditor(wxWindow* parent, const size_t numCols) :
32         wxPanel(parent),
33         m_numCols(numCols) {
34             assert(m_numCols > 0);
35         }
36 
setFlags(const wxArrayString & labels,const wxArrayString & tooltips)37         void FlagsEditor::setFlags(const wxArrayString& labels, const wxArrayString& tooltips) {
38             wxArrayInt values(labels.size());
39             for (size_t i = 0; i < labels.size(); ++i)
40                 values[i] = (1 << i);
41             setFlags(values, labels, tooltips);
42         }
43 
setFlags(const wxArrayInt & values,const wxArrayString & labels,const wxArrayString & tooltips)44         void FlagsEditor::setFlags(const wxArrayInt& values, const wxArrayString& labels, const wxArrayString& tooltips) {
45             const size_t count = values.size();
46             setCheckBoxCount(count);
47 
48             const size_t numRows = count / m_numCols;
49             wxFlexGridSizer* sizer = new wxFlexGridSizer(static_cast<int>(numRows),
50                                                          static_cast<int>(m_numCols),
51                                                          0, LayoutConstants::WideHMargin);
52 
53             for (size_t row = 0; row < numRows; ++row) {
54                 for (size_t col = 0; col < m_numCols; ++col) {
55                     const size_t index = col * numRows + row;
56                     if (index < count) {
57                         m_checkBoxes[index]->SetLabel(index < labels.size() ? labels[index] : wxString() << (1 << index));
58                         m_checkBoxes[index]->SetToolTip(index < tooltips.size() ? tooltips[index] : "");
59                         m_values[index] = values[index];
60                         sizer->Add(m_checkBoxes[index]);
61                     }
62                 }
63             }
64 
65             SetSizerAndFit(sizer);
66         }
67 
setFlagValue(const int on,const int mixed)68         void FlagsEditor::setFlagValue(const int on, const int mixed) {
69             for (size_t i = 0; i < m_checkBoxes.size(); ++i) {
70                 wxCheckBox* checkBox = m_checkBoxes[i];
71                 const int value = m_values[i];
72                 const bool isMixed = (mixed & value) != 0;
73                 const bool isChecked = (on & value) != 0;
74                 if (isMixed)
75                     checkBox->Set3StateValue(wxCHK_UNDETERMINED);
76                 else if (isChecked)
77                     checkBox->Set3StateValue(wxCHK_CHECKED);
78                 else
79                     checkBox->Set3StateValue(wxCHK_UNCHECKED);
80             }
81         }
82 
getNumFlags() const83         size_t FlagsEditor::getNumFlags() const {
84             return m_checkBoxes.size();
85         }
86 
isFlagSet(const size_t index) const87         bool FlagsEditor::isFlagSet(const size_t index) const {
88             assert(index < m_checkBoxes.size());
89             return m_checkBoxes[index]->Get3StateValue() == wxCHK_CHECKED;
90         }
91 
isFlagMixed(const size_t index) const92         bool FlagsEditor::isFlagMixed(const size_t index) const {
93             assert(index < m_checkBoxes.size());
94             return m_checkBoxes[index]->Get3StateValue() == wxCHK_UNDETERMINED;
95         }
96 
getSetFlagValue() const97         int FlagsEditor::getSetFlagValue() const {
98             int value = 0;
99             for (size_t i = 0; i < m_checkBoxes.size(); ++i) {
100                 if (isFlagSet(i))
101                     value |= m_values[i];
102             }
103             return value;
104         }
105 
getMixedFlagValue() const106         int FlagsEditor::getMixedFlagValue() const {
107             int value = 0;
108             for (size_t i = 0; i < m_checkBoxes.size(); ++i) {
109                 if (isFlagMixed(i))
110                     value |= m_values[i];
111             }
112             return value;
113         }
114 
getFlagLabel(const size_t index) const115         wxString FlagsEditor::getFlagLabel(const size_t index) const {
116             assert(index < m_checkBoxes.size());
117             return m_checkBoxes[index]->GetLabel();
118         }
119 
lineHeight() const120         int FlagsEditor::lineHeight() const {
121             assert(!m_checkBoxes.empty());
122             return m_checkBoxes.front()->GetSize().y;
123         }
124 
OnCheckBoxClicked(wxCommandEvent & event)125         void FlagsEditor::OnCheckBoxClicked(wxCommandEvent& event) {
126             if (IsBeingDeleted()) return;
127 
128             const size_t index = getIndexFromEvent(event);
129             assert(index < m_checkBoxes.size());
130 
131             FlagChangedCommand command;
132             command.setValues(index, getSetFlagValue(), getMixedFlagValue());
133             command.SetEventObject(this);
134             command.SetId(GetId());
135             ProcessEvent(command);
136         }
137 
setCheckBoxCount(const size_t count)138         void FlagsEditor::setCheckBoxCount(const size_t count) {
139             while (count > m_checkBoxes.size()) {
140                 wxCheckBox* checkBox = new wxCheckBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxCHK_3STATE);
141                 checkBox->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &FlagsEditor::OnCheckBoxClicked, this);
142                 m_checkBoxes.push_back(checkBox);
143             }
144             while (count < m_checkBoxes.size())
145                 delete m_checkBoxes.back(), m_checkBoxes.pop_back();
146             m_values.resize(count);
147         }
148 
getIndexFromEvent(const wxCommandEvent & event) const149         size_t FlagsEditor::getIndexFromEvent(const wxCommandEvent& event) const {
150             for (size_t i = 0; i < m_checkBoxes.size(); ++i)
151                 if (event.GetEventObject() == m_checkBoxes[i])
152                     return i;
153             return m_checkBoxes.size();
154         }
155     }
156 }
157