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 #ifndef TrenchBroom_CellView_h
21 #define TrenchBroom_CellView_h
22 
23 #include "Renderer/RenderUtils.h"
24 #include "Renderer/Transformation.h"
25 #include "Renderer/FontDescriptor.h"
26 #include "Preferences.h"
27 #include "View/CellLayout.h"
28 #include "View/DragAndDrop.h"
29 #include "View/GLAttribs.h"
30 #include "View/RenderView.h"
31 
32 #include <wx/wx.h>
33 #include <wx/dnd.h>
34 #include <wx/event.h>
35 
36 namespace TrenchBroom {
37     namespace View {
38         class GLContextManager;
39 
40         template <typename CellData, typename GroupData>
41         class CellView : public RenderView {
42         protected:
43             typedef CellLayout<CellData, GroupData> Layout;
44         private:
45             Layout m_layout;
46             typename Layout::Group::Row::Cell* m_selectedCell;
47             bool m_layoutInitialized;
48 
49             wxScrollBar* m_scrollBar;
50 
updateScrollBar()51             void updateScrollBar() {
52                 if (m_scrollBar != NULL) {
53                     int position = m_scrollBar->GetThumbPosition();
54                     int thumbSize = GetClientSize().y;
55                     int range = static_cast<int>(m_layout.height());
56                     m_scrollBar->SetScrollbar(position, thumbSize, range, thumbSize);
57                 }
58             }
59 
initLayout()60             void initLayout() {
61                 doInitLayout(m_layout);
62                 m_layoutInitialized = true;
63             }
64 
reloadLayout()65             void reloadLayout() {
66                 initLayout(); // always initialize the layout when reloading
67 
68                 m_layout.clear();
69                 doReloadLayout(m_layout);
70                 updateScrollBar();
71             }
72         public:
73             CellView(wxWindow* parent, GLContextManager& contextManager, const GLAttribs& attribs, wxScrollBar* scrollBar = NULL) :
RenderView(parent,contextManager,attribs)74             RenderView(parent, contextManager, attribs),
75             m_layoutInitialized(false),
76             m_scrollBar(scrollBar) {
77                 Bind(wxEVT_SIZE, &CellView::OnSize, this);
78                 Bind(wxEVT_LEFT_UP, &CellView::OnMouseLeftUp, this);
79                 Bind(wxEVT_MOTION, &CellView::OnMouseMove, this);
80 
81                 if (m_scrollBar != NULL) {
82                     m_scrollBar->Bind(wxEVT_SCROLL_LINEUP, &CellView::OnScrollBarLineUp, this);
83                     m_scrollBar->Bind(wxEVT_SCROLL_LINEDOWN, &CellView::OnScrollBarLineDown, this);
84                     m_scrollBar->Bind(wxEVT_SCROLL_PAGEUP, &CellView::OnScrollBarPageUp, this);
85                     m_scrollBar->Bind(wxEVT_SCROLL_PAGEDOWN, &CellView::OnScrollBarPageDown, this);
86 
87                     m_scrollBar->Bind(wxEVT_SCROLL_TOP, &CellView::OnScrollBarChange, this);
88                     m_scrollBar->Bind(wxEVT_SCROLL_BOTTOM, &CellView::OnScrollBarChange, this);
89                     m_scrollBar->Bind(wxEVT_SCROLL_THUMBTRACK, &CellView::OnScrollBarChange, this);
90 
91                     Bind(wxEVT_MOUSEWHEEL, &CellView::OnMouseWheel, this);
92                 }
93             }
94 
reload()95             void reload() {
96                 reloadLayout();
97                 Refresh();
98             }
99 
clear()100             void clear() {
101                 m_layout.clear();
102                 doClear();
103             }
104 
OnSize(wxSizeEvent & event)105             void OnSize(wxSizeEvent& event) {
106                 m_layout.setWidth(static_cast<float>(GetClientSize().x));
107                 updateScrollBar();
108                 event.Skip();
109             }
110 
OnScrollBarChange(wxScrollEvent & event)111             void OnScrollBarChange(wxScrollEvent& event) {
112                 Refresh();
113                 event.Skip();
114             }
115 
OnScrollBarLineUp(wxScrollEvent & event)116             void OnScrollBarLineUp(wxScrollEvent& event) {
117                 float top = static_cast<float>(m_scrollBar->GetThumbPosition());
118                 m_scrollBar->SetThumbPosition(static_cast<int>(m_layout.rowPosition(top, -1)));
119                 Refresh();
120                 event.Skip();
121             }
122 
OnScrollBarLineDown(wxScrollEvent & event)123             void OnScrollBarLineDown(wxScrollEvent& event) {
124                 float top = static_cast<float>(m_scrollBar->GetThumbPosition());
125                 m_scrollBar->SetThumbPosition(static_cast<int>(m_layout.rowPosition(top, 1)));
126                 Refresh();
127                 event.Skip();
128             }
129 
OnScrollBarPageUp(wxScrollEvent & event)130             void OnScrollBarPageUp(wxScrollEvent& event) {
131                 float top = static_cast<float>(m_scrollBar->GetThumbPosition());
132                 float height = static_cast<float>(GetClientSize().y);
133                 m_scrollBar->SetThumbPosition(static_cast<int>(m_layout.rowPosition(std::max(0.0f, top - height), 0)));
134                 Refresh();
135                 event.Skip();
136             }
137 
OnScrollBarPageDown(wxScrollEvent & event)138             void OnScrollBarPageDown(wxScrollEvent& event) {
139                 float top = static_cast<float>(m_scrollBar->GetThumbPosition());
140                 m_scrollBar->SetThumbPosition(static_cast<int>(m_layout.rowPosition(top, 0)));
141                 Refresh();
142                 event.Skip();
143             }
144 
145             class DndHelper {
146             private:
147                 CellView& m_cellView;
148             public:
DndHelper(CellView & cellView)149                 DndHelper(CellView& cellView) :
150                 m_cellView(cellView) {
151                     m_cellView.dndWillStart();
152                 }
153 
~DndHelper()154                 ~DndHelper() {
155                     m_cellView.dndDidEnd();
156                 }
157             };
158 
OnMouseMove(wxMouseEvent & event)159             void OnMouseMove(wxMouseEvent& event) {
160                 int top = m_scrollBar != NULL ? m_scrollBar->GetThumbPosition() : 0;
161                 float x = static_cast<float>(event.GetX());
162                 float y = static_cast<float>(event.GetY() + top);
163                 const typename Layout::Group::Row::Cell* cell = NULL;
164                 if (event.LeftIsDown() && dndEnabled()) {
165                     if (m_layout.cellAt(x, y, &cell)) {
166                         /*
167                         wxImage* feedbackImage = dndImage(*cell);
168                         int xOffset = event.GetX() - static_cast<int>(cell->itemBounds().left());
169                         int yOffset = event.GetY() - static_cast<int>(cell->itemBounds().top()) + top;
170                          */
171 
172                         const DndHelper dndHelper(*this);
173                         wxTextDataObject dropData(dndData(*cell));
174                         DropSource dropSource(dropData, this);
175                         dropSource.DoDragDrop();
176                     }
177                 } else {
178                     if (m_layout.cellAt(x, y, &cell))
179                         SetToolTip(tooltip(*cell));
180                     else
181                         SetToolTip("");
182                 }
183                 event.Skip();
184             }
185 
OnMouseLeftUp(wxMouseEvent & event)186             void OnMouseLeftUp(wxMouseEvent& event) {
187                 int top = m_scrollBar != NULL ? m_scrollBar->GetThumbPosition() : 0;
188                 float x = static_cast<float>(event.GetX());
189                 float y = static_cast<float>(event.GetY() + top);
190                 doLeftClick(m_layout, x, y);
191                 event.Skip();
192             }
193 
OnMouseWheel(wxMouseEvent & event)194             void OnMouseWheel(wxMouseEvent& event) {
195                 if (m_scrollBar != NULL) {
196                     const float top = static_cast<float>(m_scrollBar->GetThumbPosition());
197                     float newTop = event.GetWheelRotation() < 0 ? m_layout.rowPosition(top, 1) : m_layout.rowPosition(top, -1);
198                     newTop = std::max(0.0f, std::ceil(newTop - m_layout.rowMargin()));
199 
200                     m_scrollBar->SetThumbPosition(static_cast<int>(newTop));
201                     Refresh();
202                 }
203                 event.Skip();
204             }
205         private:
doRender()206             void doRender() {
207                 if (!m_layoutInitialized)
208                     initLayout();
209 
210                 const int top = m_scrollBar != NULL ? m_scrollBar->GetThumbPosition() : 0;
211                 const wxRect visibleRect = wxRect(wxPoint(0, top), GetClientSize());
212 
213                 const float y = static_cast<float>(visibleRect.GetY());
214                 const float height = static_cast<float>(visibleRect.GetHeight());
215 
216                 const GLint viewLeft      = static_cast<GLint>(GetClientRect().GetLeft());
217                 const GLint viewTop       = static_cast<GLint>(GetClientRect().GetBottom());
218                 const GLint viewRight     = static_cast<GLint>(GetClientRect().GetRight());
219                 const GLint viewBottom    = static_cast<GLint>(GetClientRect().GetTop());
220                 glViewport(viewLeft, viewBottom, viewRight - viewLeft, viewTop - viewBottom);
221 
222                 setupGL();
223                 doRender(m_layout, y, height);
224             }
225 
setupGL()226             void setupGL() {
227                 glEnable(GL_MULTISAMPLE);
228                 glEnable(GL_BLEND);
229                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
230                 glEnable(GL_CULL_FACE);
231                 glEnable(GL_DEPTH_TEST);
232                 glDepthFunc(GL_LEQUAL);
233                 glShadeModel(GL_SMOOTH);
234             }
235 
236             virtual void doInitLayout(Layout& layout) = 0;
237             virtual void doReloadLayout(Layout& layout) = 0;
doClear()238             virtual void doClear() {}
239             virtual void doRender(Layout& layout, float y, float height) = 0;
doLeftClick(Layout & layout,float x,float y)240             virtual void doLeftClick(Layout& layout, float x, float y) {}
241 
dndEnabled()242             virtual bool dndEnabled() { return false; }
dndWillStart()243             virtual void dndWillStart() {}
dndDidEnd()244             virtual void dndDidEnd() {}
dndImage(const typename Layout::Group::Row::Cell & cell)245             virtual wxImage dndImage(const typename Layout::Group::Row::Cell& cell) { assert(false); return wxImage(); }
dndData(const typename Layout::Group::Row::Cell & cell)246             virtual wxString dndData(const typename Layout::Group::Row::Cell& cell) { assert(false); return ""; }
tooltip(const typename Layout::Group::Row::Cell & cell)247             virtual wxString tooltip(const typename Layout::Group::Row::Cell& cell) { return ""; }
248         };
249     }
250 }
251 
252 #endif
253