1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
|
/*
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of the
* License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#pragma once
#include <mforms/view.h>
#include <boost/cstdint.hpp>
/**
* Implementation of a control class for a treeview control based on node objects.
*/
namespace mforms {
class TreeNodeView;
class ContextMenu;
/** Determines what type a column should have (mainly describing the column *editor*). */
enum TreeColumnType
{
StringColumnType, //!< simple string, with text entry editor
StringLTColumnType, //!< same as StringColumnType, but truncated at the left end if it doesn't fit
IntegerColumnType, //!< numeric field, with text entry editor
LongIntegerColumnType, //!< 64bit numeric field, with text entry editor
CheckColumnType, //!< boolean field, with checkbox
TriCheckColumnType, //!< checkbox field (same as CheckColumnType) which also accepts -1 as mixed state
IconColumnType, //!< icon field, value is the icon path
IconStringColumnType = IconColumnType,
NumberWithUnitColumnType, //!< string type, representing numbers with a unit suffix (like KB, MB, ms etc)
FloatColumnType //!< a double precision floating point number
};
/** Options used to customize what to show in the tree. */
enum TreeOptions
{
TreeDefault = 0,
TreeNoColumns = 1 << 3, //!< On non-Windows platforms columns are always on, so switch them on
//! on Windows too by default and use this flag to switch them off, if really needed.
//! At least gtk has problems with arbitrary items for a tree. Treeview in gtk is
//! built on View-Source model, where source is a row/column based thing. That may
//! require some hacking to support NoColums in gtk, so really think if that is worth it.
TreeAllowReorderRows = 1 << 4, //!< Allows row reordering, sets TreeCanBeDragSource implicitly.
TreeShowColumnLines = 1 << 5, //!< show column separator lines
TreeShowRowLines = 1 << 6, //!< show row separator lines
TreeNoBorder = 1 << 7, //!< Switch off the border around the control. Default is to show the border.
TreeSidebar = 1 << 8, //!< sidebar style treeview
TreeNoHeader = 1 << 9, //!< Don't show a column header. Only meaningful if columns are used.
TreeShowHeader = 0, //!< for backwards compatibility
TreeFlatList = 1 << 10, //!< no child items expected
TreeAltRowColors = 1 << 11, //!< enable alternating row colors
TreeSizeSmall = 1 << 12, //!< small text
TreeIndexOnTag = 1 << 13, //!< keep a node index on the tags (use with node_with_tag)
TreeCanBeDragSource = 1 << 14, //!< allow the tree to be a drag source, data used depends on actual tree
};
#ifndef SWIG
inline TreeOptions operator| (TreeOptions a, TreeOptions b)
{
return (TreeOptions) ((int) a | (int) b);
}
#endif
enum TreeSelectionMode
{
TreeSelectSingle, // 0 or 1 item selected
TreeSelectMultiple // 0+ items selection
};
typedef struct TextAttributes TreeNodeTextAttributes;
// This struct represents the data comprising a node
// including a collection of child nodes.
struct MFORMS_EXPORT TreeNodeSkeleton
{
public:
TreeNodeSkeleton() {};
TreeNodeSkeleton(const std::string& caption, const std::string& icon, const std::string& tag);
std::string caption;
std::string icon;
std::string tag;
std::vector<TreeNodeSkeleton> children;
};
// This struct represents a collection of nodes sharing tag, icon and structure
struct MFORMS_EXPORT TreeNodeCollectionSkeleton
{
public:
TreeNodeCollectionSkeleton() {};
TreeNodeCollectionSkeleton(const std::string& icon);
std::string icon;
std::vector<TreeNodeSkeleton> children;
std::vector<std::string> captions;
};
class MFORMS_EXPORT TreeNodeData {
protected:
int _refcount;
public:
TreeNodeData() : _refcount(0) {}
virtual ~TreeNodeData() {}
void retain() { _refcount++; }
void release() { _refcount--; if (_refcount == 0) delete this; }
};
class TreeNode;
struct MFORMS_EXPORT TreeNodeRef
{
private:
TreeNode *node;
public:
// following methods are private, but need to be made public for the platform implementation
#ifndef SWIG
TreeNodeRef(TreeNode *anode);
TreeNode *ptr() { return node; }
#endif
public:
TreeNodeRef() : node(0) {}
TreeNodeRef(const TreeNodeRef &other);
~TreeNodeRef();
#ifndef SWIG
TreeNodeRef &operator= (const TreeNodeRef &other);
#endif
TreeNode *operator->() const;
TreeNode *operator->();
operator bool () const { return node !=0; }
bool operator == (const TreeNodeRef &other) const;
bool operator != (const TreeNodeRef &other) const;
bool is_valid();
};
class MFORMS_EXPORT TreeNode
{
friend struct TreeNodeRef;
protected:
virtual void release() = 0;
virtual void retain() = 0;
public:
virtual ~TreeNode() {};
virtual bool equals(const TreeNode &other) = 0;
#ifndef SWIG
virtual bool is_valid() const = 0;
#endif
virtual int level() const = 0; // 0 for root, 1 for top level etc.
virtual void set_icon_path(int column, const std::string &icon) = 0;
virtual void set_string(int column, const std::string &value) = 0;
virtual void set_int(int column, int value) = 0;
virtual void set_long(int column, boost::int64_t value) = 0;
virtual void set_bool(int column, bool value) = 0;
virtual void set_float(int column, double value) = 0;
virtual void set_attributes(int column, const TreeNodeTextAttributes &attrs) = 0;
virtual std::string get_string(int column) const = 0;
virtual int get_int(int column) const = 0;
virtual boost::int64_t get_long(int column) const = 0;
virtual bool get_bool(int column) const = 0;
virtual double get_float(int column) const = 0;
virtual int count() const = 0;
virtual TreeNodeRef add_child() { return insert_child(-1); }
virtual TreeNodeRef insert_child(int index) = 0;
virtual void remove_from_parent() = 0;
virtual void remove_children(); // default impl provided, subclasses may override to provide faster impl
virtual TreeNodeRef get_child(int index) const = 0;
virtual int get_child_index(TreeNodeRef child) const = 0;
virtual TreeNodeRef get_parent() const = 0;
virtual TreeNodeRef find_child_with_tag(const std::string &tag); // This will search the sub nodes sequentially.
virtual TreeNodeRef previous_sibling() const = 0;
virtual TreeNodeRef next_sibling() const = 0;
// Moves this node to a different place relative to the given node (which must be in the same tree).
virtual void move_node(TreeNodeRef node, bool before) = 0;
// This is a very special function and I'm not sure it should be here.
// It creates nodes out of the collection's captions and adds the same child nodes to each of those nodes
// as given in the children member. This works iteratively.
virtual std::vector<mforms::TreeNodeRef> add_node_collection(const TreeNodeCollectionSkeleton &nodes, int position = -1) = 0;
virtual void expand() = 0;
virtual void collapse() = 0;
virtual bool is_expanded() = 0;
// Primitive check. Use TreeNodeView::can_expand() instead for potentially more sophisticated checks.
virtual bool can_expand() { return count() > 0; };
virtual void toggle();
virtual void set_tag(const std::string &tag) = 0;
virtual std::string get_tag() const = 0;
virtual void set_data(TreeNodeData *data) = 0;
virtual TreeNodeData *get_data() const = 0;
};
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifndef SWIG
struct TreeNodeViewImplPtrs
{
bool (*create)(TreeNodeView *self, TreeOptions options);
#if defined(_WIN32)
int (*add_column)(TreeNodeView *self, TreeColumnType type, const std::string &name, int initial_width, bool editable);
#else
int (*add_column)(TreeNodeView *self, TreeColumnType type, const std::string &name, int initial_width, bool editable, bool attributed);
#endif
void (*end_columns)(TreeNodeView *self);
void (*clear)(TreeNodeView *self);
TreeNodeRef (*root_node)(TreeNodeView *self);
void (*set_row_height)(TreeNodeView *self, int height);
void (*set_allow_sorting)(TreeNodeView *self, bool);
void (*freeze_refresh)(TreeNodeView *self, bool);
void (*set_selection_mode)(TreeNodeView *self, TreeSelectionMode mode);
TreeSelectionMode (*get_selection_mode)(TreeNodeView *self);
std::list<TreeNodeRef> (*get_selection)(TreeNodeView *self);
TreeNodeRef (*get_selected_node)(TreeNodeView *self);
void (*clear_selection)(TreeNodeView *self);
void (*set_selected)(TreeNodeView *self, TreeNodeRef node, bool state);
int (*row_for_node)(TreeNodeView *self, TreeNodeRef node);
TreeNodeRef (*node_at_row)(TreeNodeView *self, int row);
TreeNodeRef(*node_at_position)(TreeNodeView *self, base::Point position);
TreeNodeRef (*node_with_tag)(TreeNodeView *self, const std::string &tag);
void (*set_column_title)(TreeNodeView *self, int column, const std::string &title);
void (*set_column_visible)(TreeNodeView *self, int column, bool flag);
bool (*get_column_visible)(TreeNodeView *self, int column);
void (*set_column_width)(TreeNodeView *self, int column, int width);
int (*get_column_width)(TreeNodeView *self, int column);
};
#endif
#endif
class ContextMenu;
/** Control to show nodes in multiple columns in the form of a tree.
Before adding items, you must first define the columns and the content types with
add_column() and end_columns()
*/
class MFORMS_EXPORT TreeNodeView : public View
{
public:
TreeNodeView(TreeOptions options);
~TreeNodeView();
/** Adds a column to be displayed in the tree.
@param type - type of value to be displayed in column
@param name - name/caption to show in header
@param initia_width - width in pixels of the column
@param editable - whether editing is allowed for rows in this column
*/
int add_column(TreeColumnType type, const std::string &name, int initial_width, bool editable = false, bool attributed = false);
/** Must be called after needed add_column() calls are finished. */
void end_columns();
/** Sets the title of the given column */
void set_column_title(int column, const std::string &title);
/** Sets a callback that's called when the mouse hovers on a row.
Callback must return path to an icon if it wants it to be displayed. When the user clicks it,
the node_activated() handler is called, with a negated, one-based index of the icon index (-1, -2 etc.).
*/
void set_row_overlay_handler(const boost::function<std::vector<std::string> (TreeNodeRef)> &overlay_icon_for_node);
int get_column_count() const { return (int)_column_types.size(); }
TreeColumnType get_column_type(int column);
/** Removes all nodes */
void clear();
TreeNodeRef root_node();
TreeNodeRef add_node();
/** Selects selection type */
void set_selection_mode(TreeSelectionMode mode);
/** Returns the current selection mode */
TreeSelectionMode get_selection_mode();
/** Returns a list of the selected nodes */
std::list<TreeNodeRef> get_selection();
/** Returns the selected node. In case multiple selected nodes exist, only the 1st is returned */
TreeNodeRef get_selected_node();
int get_selected_row();
void clear_selection();
/** Sets the selection state of the node */
void select_node(TreeNodeRef node);
void set_node_selected(TreeNodeRef node, bool flag);
int row_for_node(TreeNodeRef node);
TreeNodeRef node_at_row(int row);
TreeNodeRef node_at_position(base::Point position); // TODO: Linux
/** Requires TreeIndexOnTag */
TreeNodeRef node_with_tag(const std::string &tag);
/** Sets the height of a row in pixels */
void set_row_height(int height);
/** Toggles sorting of rows when the user clicks on a column header
Be careful with the get_selected() and set_selected() methods when sorting is enabled,
as the index of a row will change depending on the sorting. Use get_row_tag() for
an immutable identifier for rows.
\warning When sorting is enabled, you must use freeze_refresh()/thaw_refresh() when making changes to tree,
otherwise the contents will become corrupted.
*/
void set_allow_sorting(bool flag);
/** Freezes refresh of tree display. Use when updating a large number of rows or when sorting is enabled. */
void freeze_refresh();
/** Unfreezes a previously done freeze_refresh() */
void thaw_refresh();
void set_column_visible(int column, bool flag);
bool get_column_visible(int column);
#ifndef SWIG
/** Signal emitted when the selected row is changed
In Python use add_changed_callback()
*/
boost::signals2::signal<void ()>* signal_changed() {return &_signal_changed;}
/** Signal emitted when the user double-clicks a cell.
Arguments passed are row node and column index.
In Python use add_activated_callback()
*/
boost::signals2::signal<void (TreeNodeRef, int)>* signal_node_activated() {return &_signal_activated;}
boost::signals2::signal<void (TreeNodeRef, bool)>* signal_expand_toggle() {return &_signal_expand_toggle;}
boost::signals2::signal<void (int)>* signal_column_resized() {return &_signal_column_resized;}
/** Sets a callback to handle changed made by user to tree cells.
if this handler is set, it must call set() itself whenever a cell is edited
otherwise changes will not be committed.
*/
void set_cell_edit_handler(const boost::function<void (TreeNodeRef, int, std::string)> &handler);
#endif
/** Sets a context menu to be attached to the treeview, to be shown on right click
Note: Ownership of the context menu remains with the caller and it will not be freed
when this object is deleted. */
void set_context_menu(ContextMenu *menu);
/** Sets a context menu to be attached to the treeviews header, to be shown on right click
You can use the get_clicked_header_column() from the menu's will_show handler to get the column
that was right clicked.
Note: Ownership of the context menu remains with the caller and it will not be freed
when this object is deleted. */
void set_header_menu(ContextMenu *menu);
/** Returns the context menu object attached to the treeview */
ContextMenu *get_context_menu() { return _context_menu; }
/** Returns the context menu object attached to the treeview header */
ContextMenu *get_header_menu() { return _header_menu; }
int get_clicked_header_column() { return _clicked_header_column; }
/** Returns the current width of a column */
int get_column_width(int column);
/** Sets the widths of a column */
void set_column_width(int column, int width);
public:
// backwards compatibility with TreeView
int count() { return root_node()->count(); }
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifndef SWIG
public:
// Following methods are for internal use.
virtual void changed();
virtual void node_activated(TreeNodeRef row, int column);
virtual bool can_expand(TreeNodeRef row);
// to be called BEFORE a node is expanded or AFTER its collapsed
virtual void expand_toggle(TreeNodeRef row, bool expanded);
// for use when sorting NumericWithUnitColumns in platform code
static double parse_string_with_unit(const char *s);
// To be called when user edits a cell. If this returns true, the cell value should be
// updated by the caller, otherwise it should be left unchanged.
virtual bool cell_edited(TreeNodeRef row, int column, const std::string &value);
// Drag/drop support. The treeview has a different handling as dragging is initiated by the platform
// and we call back to get data and formats (pull request). The general handling, in opposition,
// is using a push request, initiating the drag in the backend.
// Return format, data and other details for a drag operation that is about to start.
// Called only if this tree can be a drag source at all (see create flags).
// If the result of this call is false then no native drag operation is started.
virtual bool get_drag_data(DragDetails &details, void **data, std::string &format);
// Called only if get_drag_data returned true (and the tree can be a drag source).
// The given operation tells what happened.
virtual void drag_finished(DragOperation operation);
virtual void column_resized(int column);
// Called when the mouse hovers on a row
std::vector<std::string> overlay_icons_for_node(TreeNodeRef row);
// Called when mouse clicks on a overlay icon
void overlay_icon_for_node_clicked(TreeNodeRef row, int index);
// Called when right clicking on a header/title of a column, so that the context menu handler can know
// what column is it being shown for
void header_clicked(int column);
#endif
#endif
private:
TreeNodeViewImplPtrs *_treeview_impl;
boost::signals2::signal<void ()> _signal_changed;
boost::signals2::signal<void (TreeNodeRef, int)> _signal_activated;
boost::signals2::signal<void (TreeNodeRef, bool)> _signal_expand_toggle;
boost::function<void (TreeNodeRef, int, std::string)> _cell_edited;
boost::signals2::signal<void (int)> _signal_column_resized;
boost::function<std::vector<std::string> (TreeNodeRef)> _overlay_icons_for_node;
ContextMenu *_context_menu;
ContextMenu *_header_menu;
std::vector<TreeColumnType> _column_types;
int _update_count;
int _clicked_header_column;
bool _index_on_tag;
bool _end_column_called;
};
}
|