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