1 // This may look like C code, but it's really -*- C++ -*- 2 /* 3 * Copyright (C) 2009 Emweb bv, Herent, Belgium. 4 * 5 * See the LICENSE file for terms of use. 6 */ 7 #ifndef WT_WTABLEVIEW_H_ 8 #define WT_WTABLEVIEW_H_ 9 10 #include <Wt/WAbstractItemView.h> 11 #include <Wt/WContainerWidget.h> 12 13 namespace Wt { 14 15 class WContainerWidget; 16 class WModelIndex; 17 18 /*! \class WTableView Wt/WTableView.h Wt/WTableView.h 19 * \brief An MVC View widget for tabular data. 20 * 21 * The view displays data from a WAbstractItemModel in a table. It 22 * provides incremental rendering, without excessive use of client- or 23 * serverside resources. 24 * 25 * The rendering (and editing) of items is handled by a 26 * WAbstractItemDelegate, by default it uses WItemDelegate which 27 * renders data of all predefined roles (see also Wt::ItemDataRole), 28 * including text, icons, checkboxes, and tooltips. 29 * 30 * The view provides virtual scrolling in both horizontal and vertical 31 * directions, and can therefore be used to display large data models 32 * (with large number of columns and rows). 33 * 34 * When the view is updated, it will read the data from the model 35 * row per row, starting at the top visible row. If \c (r1,c1) and \c (r2,c2) 36 * are two model indexes of visible table cells, and \c r1 \c < \c r2 or 37 * \c r1 \c == \c r2 and \c c1 \c < \c c2, then the data for the first 38 * model index is read before the second. Keep this into account when 39 * implementing a custom WAbstractItemModel if you want to optimize 40 * performance. 41 * 42 * The view may support editing of items, if the model indicates 43 * support (see the Wt::ItemFlag::Editable flag). You can define triggers 44 * that initiate editing of an item using setEditTriggers(). The 45 * actual editing is provided by the item delegate (you can set an 46 * appropriate delegate for one column using 47 * setItemDelegateForColumn()). Using setEditOptions() you can 48 * customize if and how the view deals with multiple editors. 49 * 50 * By default, all columns are given a width of 150px. Column widths 51 * of all columns can be set through the API method setColumnWidth(), 52 * and also by the user using handles provided in the header. 53 * 54 * If the model supports sorting (WAbstractItemModel::sort()), such as 55 * the WStandardItemModel, then you can enable sorting buttons in the 56 * header, using setSortingEnabled(). 57 * 58 * You can allow selection on row or item level (using 59 * setSelectionBehavior()), and selection of single or multiple items 60 * (using setSelectionMode()), and listen for changes in the selection 61 * using the selectionChanged() signal. 62 * 63 * You may enable drag & drop support for this view, with awareness 64 * of the items in the model. When enabling dragging (see 65 * setDragEnabled()), the current selection may be dragged, but only 66 * when all items in the selection indicate support for dragging 67 * (controlled by the \link Wt::ItemFlag::DragEnabled 68 * ItemFlag::DragEnabled\endlink flag), and if the model indicates a 69 * mime-type (controlled by WAbstractItemModel::mimeType()). Likewise, 70 * by enabling support for dropping (see setDropsEnabled()), the view 71 * may receive a drop event on a particular item, at least if the item 72 * indicates support for drops (controlled by the \link 73 * Wt::ItemFlag::DropEnabled ItemFlag::DropEnabled\endlink flag). 74 * 75 * You may also react to mouse click events on any item, by connecting 76 * to one of the clicked() or doubleClicked() signals. 77 * 78 * If a WTableView is not constrained in height (either by 79 * a layout manager or by setHeight()), then it will grow according 80 * to the size of the model. 81 * 82 * \ingroup modelview 83 */ 84 class WT_API WTableView : public WAbstractItemView 85 { 86 public: 87 /*! \brief Constructor 88 */ 89 WTableView(); 90 91 virtual ~WTableView(); 92 93 virtual WWidget *itemWidget(const WModelIndex& index) const override; 94 virtual void setModel(const std::shared_ptr<WAbstractItemModel>& model) 95 override; 96 97 virtual void setColumnWidth(int column, const WLength& width) override; 98 virtual void setAlternatingRowColors(bool enable) override; 99 virtual void setRowHeight(const WLength& rowHeight) override; 100 virtual void setHeaderHeight(const WLength& height) override; 101 #ifndef WT_CNOR 102 using WAbstractItemView::setHeaderHeight; 103 #endif 104 virtual void resize(const WLength& width, const WLength& height) override; 105 virtual void setColumnHidden(int column, bool hidden) override; 106 virtual void setRowHeaderCount(int count) override; 107 108 virtual int pageCount() const override; 109 virtual int pageSize() const override; 110 virtual int currentPage() const override; 111 virtual void setCurrentPage(int page) override; 112 113 virtual void scrollTo(const WModelIndex& index, 114 ScrollHint hint = ScrollHint::EnsureVisible) override; 115 116 /*! \brief Scrolls the view x px left and y px top. 117 */ 118 void scrollTo(int x, int y); 119 120 /*! \brief set css overflow 121 */ 122 void setOverflow(Overflow overflow, 123 WFlags<Orientation> orientation 124 = (Orientation::Horizontal | Orientation::Vertical)); 125 126 /*! \brief Sets preloading margin 127 * 128 * By default the table view loads in an area equal to 3 times its height 129 * and 3 times its width. This makes it so that the user can scroll a full 130 * page in each direction without the delay caused when the table view 131 * dynamically needs to load more data. 132 * 133 * setPreloadMargin() allows to customize this margin. 134 * 135 * e.g. if the table view is H pixels high, and C is the preload margin in pixels 136 * set on the top and bottom, then enough rows are loaded to fill the area 137 * that is H + 2C pixels high. H pixels visible, C pixels above, and C pixels below. 138 * 139 * Set to 0 pixels if you don't want to load more rows or columns than are currently visible. 140 * 141 * Set to a default-constructed WLength (auto) if you want to keep default behaviour. 142 */ 143 void setPreloadMargin(const WLength &margin, WFlags<Side> side = AllSides); 144 145 /*! \brief Retrieves the preloading margin 146 * 147 * \sa setPreloadMargin 148 */ 149 WLength preloadMargin(Side side) const; 150 151 virtual void setHidden(bool hidden, 152 const WAnimation& animation = WAnimation()) override; 153 154 /*! \brief Returns the model index corresponding to a widget. 155 * 156 * This returns the model index for the item that is or contains the 157 * given widget. 158 */ 159 WModelIndex modelIndexAt(WWidget *widget) const; 160 161 virtual EventSignal<WScrollEvent>& scrolled() override; 162 163 protected: 164 virtual void render(WFlags<RenderFlag> flags) override; 165 166 /*! \brief Called when rows or columns are inserted/removed. 167 * 168 * Override this method when you want to adjust the table's size when 169 * columns or rows are inserted or removed. The method is also called when 170 * the model is reset. The default implementation does nothing. 171 */ adjustSize()172 virtual void adjustSize() {} 173 174 virtual void enableAjax() override; 175 176 private: 177 class ColumnWidget; 178 179 ColumnWidget *createColumnWidget(int column); 180 181 class ColumnWidget : public WContainerWidget 182 { 183 public: column()184 int column() const { return column_; } 185 186 private: 187 ColumnWidget(int column); 188 189 int column_; 190 191 friend ColumnWidget *WTableView::createColumnWidget(int column); 192 }; 193 194 /* For Ajax implementation */ 195 WContainerWidget *headers_, *canvas_, *table_; 196 WContainerWidget *headerContainer_, *contentsContainer_; 197 WContainerWidget *headerColumnsCanvas_, *headerColumnsTable_; 198 WContainerWidget *headerColumnsHeaderContainer_, *headerColumnsContainer_; 199 200 /* For plain HTML implementation */ 201 WTable *plainTable_; 202 203 JSignal<int, int, std::string, std::string, WMouseEvent> dropEvent_; 204 JSignal<int, int, std::string, std::string, std::string, WMouseEvent> rowDropEvent_; 205 JSignal<int, int, int, int> scrolled_; 206 JSignal<WTouchEvent> itemTouchSelectEvent_; 207 208 Signals::connection touchStartConnection_; 209 Signals::connection touchMoveConnection_; 210 Signals::connection touchEndConnection_; 211 212 WLength preloadMargin_[4]; 213 214 /* Ajax only: First and last columns rendered (this somewhat 215 * redundant with the state contained in the widgets, but because 216 * columns are variable width, we cache these values as well). The 217 * first and last rows rendered can be derived from widget 218 * properties. */ 219 int firstColumn_, lastColumn_; 220 221 /* Current size of the viewport */ 222 int viewportLeft_, viewportWidth_, viewportTop_, viewportHeight_; 223 224 /* Desired rendered area */ 225 int renderedFirstRow_, renderedLastRow_, 226 renderedFirstColumn_, renderedLastColumn_; 227 228 /* Scroll to to process after viewport height is known */ 229 int scrollToRow_; 230 ScrollHint scrollToHint_; 231 bool columnResizeConnected_; 232 233 void updateTableBackground(); 234 235 ColumnWidget *columnContainer(int renderedColumn) const; 236 237 void modelColumnsInserted(const WModelIndex& parent, int start, int end); 238 void modelColumnsAboutToBeRemoved(const WModelIndex& parent, 239 int start, int end); 240 void modelRowsInserted(const WModelIndex& parent, int start, int end); 241 void modelRowsAboutToBeRemoved(const WModelIndex& parent, int start, int end); 242 void modelRowsRemoved(const WModelIndex& parent, int start, int end); 243 virtual void modelDataChanged(const WModelIndex& topLeft, 244 const WModelIndex& bottomRight) override; 245 246 virtual void modelLayoutChanged() override; 247 248 std::unique_ptr<WWidget> renderWidget(WWidget* w, const WModelIndex& index); 249 250 int spannerCount(const Side side) const; 251 void setSpannerCount(const Side side, const int count); 252 253 void renderTable(const int firstRow, const int lastRow, 254 const int firstColumn, const int lastColumn); 255 void addSection(const Side side); 256 void removeSection(const Side side); 257 int firstRow() const; 258 int lastRow() const; 259 int firstColumn() const; 260 int lastColumn() const; 261 262 void setup(); 263 void reset(); 264 void rerenderHeader(); 265 void rerenderData(); 266 void adjustToViewport(); 267 void computeRenderedArea(); 268 headerContainer()269 virtual WContainerWidget* headerContainer() override { 270 return headerContainer_; 271 } 272 273 virtual WWidget *headerWidget(int column, bool contentsOnly = true) override; 274 275 void onViewportChange(int left, int top, int width, int height); 276 void onColumnResize(); 277 void resetGeometry(); 278 279 void handleSingleClick(bool headerColumns, const WMouseEvent& event); 280 void handleDblClick(bool headerColumns, const WMouseEvent& event); 281 void handleMouseWentDown(bool headerColumns, const WMouseEvent& event); 282 void handleMouseWentUp(bool headerColumns, const WMouseEvent& event); 283 void handleTouchSelected(const WTouchEvent& event); 284 void handleTouchStarted(const WTouchEvent& event); 285 void handleTouchMoved(const WTouchEvent& event); 286 void handleTouchEnded(const WTouchEvent& event); 287 WModelIndex translateModelIndex(bool headerColumns, const WMouseEvent& event); 288 WModelIndex translateModelIndex(const Touch& touch); 289 290 void handleRootSingleClick(int u, const WMouseEvent& event); 291 void handleRootDoubleClick(int u, const WMouseEvent& event); 292 void handleRootMouseWentDown(int u, const WMouseEvent& event); 293 void handleRootMouseWentUp(int u, const WMouseEvent& event); 294 295 void updateItem(const WModelIndex& index, 296 int renderedRow, int renderedColumn); 297 298 virtual bool internalSelect(const WModelIndex& index, SelectionFlag option) 299 override; 300 virtual void selectRange(const WModelIndex& first, const WModelIndex& last) 301 override; 302 void shiftModelIndexRows(int start, int count); 303 void shiftModelIndexColumns(int start, int count); 304 void renderSelected(bool selected, const WModelIndex& index); 305 int renderedColumnsCount() const; 306 307 void defineJavaScript(); 308 309 bool isRowRendered(const int row) const; 310 bool isColumnRendered(const int column) const; 311 void updateColumnOffsets(); 312 void updateModelIndexes(); 313 void updateModelIndex(const WModelIndex& index, 314 int renderedRow, int renderedColumn); 315 316 void onDropEvent(int renderedRow, int columnId, 317 std::string sourceId, std::string mimeType, 318 WMouseEvent event); 319 void onRowDropEvent(int renderedRow, int columnId, 320 std::string sourceId, std::string mimeType, 321 std::string side, WMouseEvent event); 322 323 void deleteItem(int row, int col, WWidget *widget); 324 ajaxMode()325 bool ajaxMode() const { return table_ != nullptr; } 326 double canvasHeight() const; 327 void setRenderedHeight(double th); 328 }; 329 330 } 331 332 #endif // WT_WTABLEVIEW_H 333