MVC Item Models

A common data model involves lists, tables and trees of more or less uniform items. Several standard widgets provided by the library use such a model. The abstract WAbstractItemModel base class provides the interface which is used by these view classes.

Not only Table Views and Tree Views display (and interact with) data from an item model, but so do also the list-oriented widgets such as Combo box, Selection box, Auto complete, and charting widgets such as Cartesian Chart, Scatter Plot and Pie Chart.

The model

An item model is essentially a table of items, where each item can be a parent to a nested table of items.

Since recursive statements like the one above can confuse even a seasoned programmer, let's start from a simple model, and extend it to more complex instances.

Lists

In its most simple and perhaps most common form, an item model simply stores a list of items. Such a model has only one column.

Each item may hold different facets of the data, stored as different Item Data Roles. If a table has two dimensions (rows and columns), then data roles could be considered as a third dimension.

The common use-case for item data roles is that for a single item, there may be a textual representation, but also an icon, a customized style class, a link, etc...

The built-in views will thus interpret subsets of this data to render a single item. In particular, the following data roles are commonly supported:

DisplayRole
Rendered as text
DecorationRole
Rendered as icons
CheckStateRole
Rendered as check box state
LinkRole
Rendered as links
ToolTipRole
Rendered as tool tips
StyleClassRole
Rendered as style classes
See ItemDataRole for a more complete list of standard roles, whose use may depend on the View.

Tables

As a straight forward generalization to lists, tables may include more than one more column, and each item can be identified by a (row, column) pair.

Trees

A tree model is - like a list - a model with a single column. In addition, each item may be the parent of another list.

At this point it becomes necessary to introduce the concept of a WModelIndex to uniquely identify an item. A model index is a data structure containing:

row
The row number
column
The column number
parent
The parent model index

The recursion is thus achieved by associating a parent index with each item index. By convention, top-level items have an Invalid index (which is a default constructed WModelIndex). To make the recursive definition consistent, one can also imagine an invisible root item (corresponding to the "invalid" index) which is the parent of the top-level items.

Tree Tables

Finally, a tree table extends the list model by allow additional columns of data to be associated with each item row.

None of the standard Views render hierarchical data that is not present in the first column ! While such data structures can indeed be defined by item models, this will effectively be ignored by the standard View classes.

Top

Standard model implementations

To get you going, and more than sufficient for simple needs, the library provides a number of standard and generic models, which store the data in memory.

  • WStandardItemModel can implement any kind of item model, being composed of a table of WStandardItem's, which each can contain itself a nested table of WStandardItem items.
  • WStringListModel only supports lists with a single column, and is thus optimized for list-oriented widgets (combo-boxes and the like).

Custom models

Separating models from views would be not very useful, if it were not of the ability to implement customized models. These could be models that compute some or all data on the fly, or fetch this information from an underlying database or file system, or simply display information from an existing data structure in a tabular/tree-like way.

Table models

As a minimum, a custom table model should reimplement the following methods from WAbstractTableModel:

int rowCount()
Returns the table row count (only for the invalid parent!)
int columnCount()
Returns the table column count (only for the invalid parent!)
boost.any data()
Returns the data for an item, and a particular role
boost.any headerData()
Returns the header data for a column

As an example of a custom table model, consider the following (nonsensical) model that simply displays row/column information for each item.

Example
Column 0
Column 1
Column 2
Column 3
Column 4
Column 5
Column 6
Column 7
Column 8
Column 9
Column 10
Column 11
Column 12
Column 13
Column 14
Column 15
Column 16
Column 17
Column 18
Column 19
Column 20
Column 21
Column 22
Column 23
Column 24
Column 25
Column 26
Column 27
Column 28
Column 29
Column 30
Column 31
Column 32
Column 33
Column 34
Column 35
Column 36
Column 37
Column 38
Column 39
Column 40
Column 41
Column 42
Column 43
Column 44
Column 45
Column 46
Column 47
Column 48
Column 49
Row 0
Item row 0, col 1
Item row 0, col 2
Item row 0, col 3
Item row 0, col 4
Item row 0, col 5
Item row 0, col 6
Item row 0, col 7
Item row 0, col 8
Item row 0, col 9
Item row 0, col 10
Item row 0, col 11
Item row 0, col 12
Item row 0, col 13
Item row 0, col 14
Item row 0, col 15
Item row 0, col 16
Item row 0, col 17
Item row 0, col 18
Item row 0, col 19
Item row 0, col 20
Item row 0, col 21
Item row 0, col 22
Item row 0, col 23
Item row 0, col 24
Item row 0, col 25
Item row 0, col 26
Item row 0, col 27
Item row 0, col 28
Item row 0, col 29
Item row 0, col 30
Item row 0, col 31
Item row 0, col 32
Item row 0, col 33
Item row 0, col 34
Item row 0, col 35
Item row 0, col 36
Item row 0, col 37
Item row 0, col 38
Item row 0, col 39
Item row 0, col 40
Item row 0, col 41
Item row 0, col 42
Item row 0, col 43
Item row 0, col 44
Item row 0, col 45
Item row 0, col 46
Item row 0, col 47
Item row 0, col 48
Item row 0, col 49
Row 1
Item row 1, col 1
Item row 1, col 2
Item row 1, col 3
Item row 1, col 4
Item row 1, col 5
Item row 1, col 6
Item row 1, col 7
Item row 1, col 8
Item row 1, col 9
Item row 1, col 10
Item row 1, col 11
Item row 1, col 12
Item row 1, col 13
Item row 1, col 14
Item row 1, col 15
Item row 1, col 16
Item row 1, col 17
Item row 1, col 18
Item row 1, col 19
Item row 1, col 20
Item row 1, col 21
Item row 1, col 22
Item row 1, col 23
Item row 1, col 24
Item row 1, col 25
Item row 1, col 26
Item row 1, col 27
Item row 1, col 28
Item row 1, col 29
Item row 1, col 30
Item row 1, col 31
Item row 1, col 32
Item row 1, col 33
Item row 1, col 34
Item row 1, col 35
Item row 1, col 36
Item row 1, col 37
Item row 1, col 38
Item row 1, col 39
Item row 1, col 40
Item row 1, col 41
Item row 1, col 42
Item row 1, col 43
Item row 1, col 44
Item row 1, col 45
Item row 1, col 46
Item row 1, col 47
Item row 1, col 48
Item row 1, col 49
Row 2
Item row 2, col 1
Item row 2, col 2
Item row 2, col 3
Item row 2, col 4
Item row 2, col 5
Item row 2, col 6
Item row 2, col 7
Item row 2, col 8
Item row 2, col 9
Item row 2, col 10
Item row 2, col 11
Item row 2, col 12
Item row 2, col 13
Item row 2, col 14
Item row 2, col 15
Item row 2, col 16
Item row 2, col 17
Item row 2, col 18
Item row 2, col 19
Item row 2, col 20
Item row 2, col 21
Item row 2, col 22
Item row 2, col 23
Item row 2, col 24
Item row 2, col 25
Item row 2, col 26
Item row 2, col 27
Item row 2, col 28
Item row 2, col 29
Item row 2, col 30
Item row 2, col 31
Item row 2, col 32
Item row 2, col 33
Item row 2, col 34
Item row 2, col 35
Item row 2, col 36
Item row 2, col 37
Item row 2, col 38
Item row 2, col 39
Item row 2, col 40
Item row 2, col 41
Item row 2, col 42
Item row 2, col 43
Item row 2, col 44
Item row 2, col 45
Item row 2, col 46
Item row 2, col 47
Item row 2, col 48
Item row 2, col 49
Row 3
Item row 3, col 1
Item row 3, col 2
Item row 3, col 3
Item row 3, col 4
Item row 3, col 5
Item row 3, col 6
Item row 3, col 7
Item row 3, col 8
Item row 3, col 9
Item row 3, col 10
Item row 3, col 11
Item row 3, col 12
Item row 3, col 13
Item row 3, col 14
Item row 3, col 15
Item row 3, col 16
Item row 3, col 17
Item row 3, col 18
Item row 3, col 19
Item row 3, col 20
Item row 3, col 21
Item row 3, col 22
Item row 3, col 23
Item row 3, col 24
Item row 3, col 25
Item row 3, col 26
Item row 3, col 27
Item row 3, col 28
Item row 3, col 29
Item row 3, col 30
Item row 3, col 31
Item row 3, col 32
Item row 3, col 33
Item row 3, col 34
Item row 3, col 35
Item row 3, col 36
Item row 3, col 37
Item row 3, col 38
Item row 3, col 39
Item row 3, col 40
Item row 3, col 41
Item row 3, col 42
Item row 3, col 43
Item row 3, col 44
Item row 3, col 45
Item row 3, col 46
Item row 3, col 47
Item row 3, col 48
Item row 3, col 49
Row 4
Item row 4, col 1
Item row 4, col 2
Item row 4, col 3
Item row 4, col 4
Item row 4, col 5
Item row 4, col 6
Item row 4, col 7
Item row 4, col 8
Item row 4, col 9
Item row 4, col 10
Item row 4, col 11
Item row 4, col 12
Item row 4, col 13
Item row 4, col 14
Item row 4, col 15
Item row 4, col 16
Item row 4, col 17
Item row 4, col 18
Item row 4, col 19
Item row 4, col 20
Item row 4, col 21
Item row 4, col 22
Item row 4, col 23
Item row 4, col 24
Item row 4, col 25
Item row 4, col 26
Item row 4, col 27
Item row 4, col 28
Item row 4, col 29
Item row 4, col 30
Item row 4, col 31
Item row 4, col 32
Item row 4, col 33
Item row 4, col 34
Item row 4, col 35
Item row 4, col 36
Item row 4, col 37
Item row 4, col 38
Item row 4, col 39
Item row 4, col 40
Item row 4, col 41
Item row 4, col 42
Item row 4, col 43
Item row 4, col 44
Item row 4, col 45
Item row 4, col 46
Item row 4, col 47
Item row 4, col 48
Item row 4, col 49
Row 5
Item row 5, col 1
Item row 5, col 2
Item row 5, col 3
Item row 5, col 4
Item row 5, col 5
Item row 5, col 6
Item row 5, col 7
Item row 5, col 8
Item row 5, col 9
Item row 5, col 10
Item row 5, col 11
Item row 5, col 12
Item row 5, col 13
Item row 5, col 14
Item row 5, col 15
Item row 5, col 16
Item row 5, col 17
Item row 5, col 18
Item row 5, col 19
Item row 5, col 20
Item row 5, col 21
Item row 5, col 22
Item row 5, col 23
Item row 5, col 24
Item row 5, col 25
Item row 5, col 26
Item row 5, col 27
Item row 5, col 28
Item row 5, col 29
Item row 5, col 30
Item row 5, col 31
Item row 5, col 32
Item row 5, col 33
Item row 5, col 34
Item row 5, col 35
Item row 5, col 36
Item row 5, col 37
Item row 5, col 38
Item row 5, col 39
Item row 5, col 40
Item row 5, col 41
Item row 5, col 42
Item row 5, col 43
Item row 5, col 44
Item row 5, col 45
Item row 5, col 46
Item row 5, col 47
Item row 5, col 48
Item row 5, col 49
Row 6
Item row 6, col 1
Item row 6, col 2
Item row 6, col 3
Item row 6, col 4
Item row 6, col 5
Item row 6, col 6
Item row 6, col 7
Item row 6, col 8
Item row 6, col 9
Item row 6, col 10
Item row 6, col 11
Item row 6, col 12
Item row 6, col 13
Item row 6, col 14
Item row 6, col 15
Item row 6, col 16
Item row 6, col 17
Item row 6, col 18
Item row 6, col 19
Item row 6, col 20
Item row 6, col 21
Item row 6, col 22
Item row 6, col 23
Item row 6, col 24
Item row 6, col 25
Item row 6, col 26
Item row 6, col 27
Item row 6, col 28
Item row 6, col 29
Item row 6, col 30
Item row 6, col 31
Item row 6, col 32
Item row 6, col 33
Item row 6, col 34
Item row 6, col 35
Item row 6, col 36
Item row 6, col 37
Item row 6, col 38
Item row 6, col 39
Item row 6, col 40
Item row 6, col 41
Item row 6, col 42
Item row 6, col 43
Item row 6, col 44
Item row 6, col 45
Item row 6, col 46
Item row 6, col 47
Item row 6, col 48
Item row 6, col 49
Row 7
Item row 7, col 1
Item row 7, col 2
Item row 7, col 3
Item row 7, col 4
Item row 7, col 5
Item row 7, col 6
Item row 7, col 7
Item row 7, col 8
Item row 7, col 9
Item row 7, col 10
Item row 7, col 11
Item row 7, col 12
Item row 7, col 13
Item row 7, col 14
Item row 7, col 15
Item row 7, col 16
Item row 7, col 17
Item row 7, col 18
Item row 7, col 19
Item row 7, col 20
Item row 7, col 21
Item row 7, col 22
Item row 7, col 23
Item row 7, col 24
Item row 7, col 25
Item row 7, col 26
Item row 7, col 27
Item row 7, col 28
Item row 7, col 29
Item row 7, col 30
Item row 7, col 31
Item row 7, col 32
Item row 7, col 33
Item row 7, col 34
Item row 7, col 35
Item row 7, col 36
Item row 7, col 37
Item row 7, col 38
Item row 7, col 39
Item row 7, col 40
Item row 7, col 41
Item row 7, col 42
Item row 7, col 43
Item row 7, col 44
Item row 7, col 45
Item row 7, col 46
Item row 7, col 47
Item row 7, col 48
Item row 7, col 49
Row 8
Item row 8, col 1
Item row 8, col 2
Item row 8, col 3
Item row 8, col 4
Item row 8, col 5
Item row 8, col 6
Item row 8, col 7
Item row 8, col 8
Item row 8, col 9
Item row 8, col 10
Item row 8, col 11
Item row 8, col 12
Item row 8, col 13
Item row 8, col 14
Item row 8, col 15
Item row 8, col 16
Item row 8, col 17
Item row 8, col 18
Item row 8, col 19
Item row 8, col 20
Item row 8, col 21
Item row 8, col 22
Item row 8, col 23
Item row 8, col 24
Item row 8, col 25
Item row 8, col 26
Item row 8, col 27
Item row 8, col 28
Item row 8, col 29
Item row 8, col 30
Item row 8, col 31
Item row 8, col 32
Item row 8, col 33
Item row 8, col 34
Item row 8, col 35
Item row 8, col 36
Item row 8, col 37
Item row 8, col 38
Item row 8, col 39
Item row 8, col 40
Item row 8, col 41
Item row 8, col 42
Item row 8, col 43
Item row 8, col 44
Item row 8, col 45
Item row 8, col 46
Item row 8, col 47
Item row 8, col 48
Item row 8, col 49
Row 9
Item row 9, col 1
Item row 9, col 2
Item row 9, col 3
Item row 9, col 4
Item row 9, col 5
Item row 9, col 6
Item row 9, col 7
Item row 9, col 8
Item row 9, col 9
Item row 9, col 10
Item row 9, col 11
Item row 9, col 12
Item row 9, col 13
Item row 9, col 14
Item row 9, col 15
Item row 9, col 16
Item row 9, col 17
Item row 9, col 18
Item row 9, col 19
Item row 9, col 20
Item row 9, col 21
Item row 9, col 22
Item row 9, col 23
Item row 9, col 24
Item row 9, col 25
Item row 9, col 26
Item row 9, col 27
Item row 9, col 28
Item row 9, col 29
Item row 9, col 30
Item row 9, col 31
Item row 9, col 32
Item row 9, col 33
Item row 9, col 34
Item row 9, col 35
Item row 9, col 36
Item row 9, col 37
Item row 9, col 38
Item row 9, col 39
Item row 9, col 40
Item row 9, col 41
Item row 9, col 42
Item row 9, col 43
Item row 9, col 44
Item row 9, col 45
Item row 9, col 46
Item row 9, col 47
Item row 9, col 48
Item row 9, col 49
Row 10
Item row 10, col 1
Item row 10, col 2
Item row 10, col 3
Item row 10, col 4
Item row 10, col 5
Item row 10, col 6
Item row 10, col 7
Item row 10, col 8
Item row 10, col 9
Item row 10, col 10
Item row 10, col 11
Item row 10, col 12
Item row 10, col 13
Item row 10, col 14
Item row 10, col 15
Item row 10, col 16
Item row 10, col 17
Item row 10, col 18
Item row 10, col 19
Item row 10, col 20
Item row 10, col 21
Item row 10, col 22
Item row 10, col 23
Item row 10, col 24
Item row 10, col 25
Item row 10, col 26
Item row 10, col 27
Item row 10, col 28
Item row 10, col 29
Item row 10, col 30
Item row 10, col 31
Item row 10, col 32
Item row 10, col 33
Item row 10, col 34
Item row 10, col 35
Item row 10, col 36
Item row 10, col 37
Item row 10, col 38
Item row 10, col 39
Item row 10, col 40
Item row 10, col 41
Item row 10, col 42
Item row 10, col 43
Item row 10, col 44
Item row 10, col 45
Item row 10, col 46
Item row 10, col 47
Item row 10, col 48
Item row 10, col 49
Row 11
Item row 11, col 1
Item row 11, col 2
Item row 11, col 3
Item row 11, col 4
Item row 11, col 5
Item row 11, col 6
Item row 11, col 7
Item row 11, col 8
Item row 11, col 9
Item row 11, col 10
Item row 11, col 11
Item row 11, col 12
Item row 11, col 13
Item row 11, col 14
Item row 11, col 15
Item row 11, col 16
Item row 11, col 17
Item row 11, col 18
Item row 11, col 19
Item row 11, col 20
Item row 11, col 21
Item row 11, col 22
Item row 11, col 23
Item row 11, col 24
Item row 11, col 25
Item row 11, col 26
Item row 11, col 27
Item row 11, col 28
Item row 11, col 29
Item row 11, col 30
Item row 11, col 31
Item row 11, col 32
Item row 11, col 33
Item row 11, col 34
Item row 11, col 35
Item row 11, col 36
Item row 11, col 37
Item row 11, col 38
Item row 11, col 39
Item row 11, col 40
Item row 11, col 41
Item row 11, col 42
Item row 11, col 43
Item row 11, col 44
Item row 11, col 45
Item row 11, col 46
Item row 11, col 47
Item row 11, col 48
Item row 11, col 49
1 of 834

source
  class VirtualModel extends WAbstractTableModel {
    private static Logger logger = LoggerFactory.getLogger(VirtualModel.class);

    VirtualModel(int rows, int columns) {
      super();
      this.rows_ = rows;
      this.columns_ = columns;
    }

    int getRowCount(final WModelIndex parent) {
      if (!(parent != null)) {
        return this.rows_;
      } else {
        return 0;
      }
    }

    int getColumnCount(final WModelIndex parent) {
      if (!(parent != null)) {
        return this.columns_;
      } else {
        return 0;
      }
    }

    Object getData(final WModelIndex index, ItemDataRole role) {
      if (role.equals(ItemDataRole.Display)) {
        if (index.getColumn() == 0) {
          return new WString("Row {1}").arg(index.getRow());
        } else {
          return new WString("Item row {1}, col {2}").arg(index.getRow()).arg(index.getColumn());
        }
      } else {
        return null;
      }
    }

    Object getHeaderData(int section, Orientation orientation, ItemDataRole role) {
      if (orientation == Orientation.Horizontal) {
        if (role.equals(ItemDataRole.Display)) {
          return new WString("Column {1}").arg(section);
        } else {
          return null;
        }
      } else {
        return null;
      }
    }

    private int rows_;
    private int columns_;
  }

Note The instantiation of this model with a Table View is discussed in MVC Table Views.

Tree models

A custom tree model involves considerably more work. Each internal item (in the first column) which has children, needs to be identified by a unique 64-bit value (which may thus be a long long or a void * pointer). Depending on the source data, a suitable choice must be made for this data.

The following methods must be implemented for a minimally compliant hierarchical model:

int rowCount()
Returns the item children count
int columnCount()
Returns the column count (which is usually the same for each item in the first column)
boost.any data()
Returns the data for an item, and a particular role
boost.any headerData()
Returns the header data for a column
WModelIndex child()
Creates a child index
WModelIndex parent()
Creates a parent index

As an example of a tree table model, consider the following model that loads information from a git repository (in this case, JWt's git). Only a minimum of information is kept in memory: we allocate a data structure only for folders that are being expanded, for use as internal pointer data in model indexes.

Example
Type
File
  • .classpath
    • .gitignore
      • .project
        • LICENSE
          • Markdown
            README.md
            • build.xml
              • Folder
                doc/
                • Folder
                  examples/
                  • jwt-4.11.1.pom
                    • jwt-auth-4.11.1.pom
                      • Folder
                        lib/
                        • overview.html
                          • Folder
                            src/
                            • Folder
                              test/
                              1 of 1

                              source
                                class GitModel extends WAbstractItemModel {
                                  private static Logger logger = LoggerFactory.getLogger(GitModel.class);
                              
                                  public static ItemDataRole ContentsRole = ItemDataRole.of(ItemDataRole.User.getValue() + 1);
                              
                                  GitModel(final String repository) {
                                    super();
                                    this.git_ = new Git();
                                    this.treeData_ = new ArrayList<GitModel.Tree>();
                                    this.childPointer_ = new HashMap<GitModel.ChildIndex, Integer>();
                                    this.git_.setRepositoryPath(repository);
                                    this.loadRevision("master");
                                  }
                              
                                  void loadRevision(final String revName) {
                                    Git.ObjectId treeRoot = this.git_.getCommitTree(revName);
                                    this.layoutAboutToBeChanged().trigger();
                                    this.treeData_.clear();
                                    this.childPointer_.clear();
                                    this.treeData_.add(new GitModel.Tree(-1, -1, treeRoot, this.git_.treeSize(treeRoot)));
                                    this.layoutChanged().trigger();
                                  }
                              
                                  WModelIndex getParent(final WModelIndex index) {
                                    if (!(index != null) || index.getInternalId() == 0) {
                                      return null;
                                    } else {
                                      final GitModel.Tree item = this.treeData_.get(index.getInternalId());
                                      return this.createIndex(item.getIndex(), 0, item.getParentId());
                                    }
                                  }
                              
                                  WModelIndex getIndex(int row, int column, final WModelIndex parent) {
                                    int parentId;
                                    if (!(parent != null)) {
                                      parentId = 0;
                                    } else {
                                      int grandParentId = parent.getInternalId();
                                      parentId = this.getTreeId(grandParentId, parent.getRow());
                                    }
                                    return this.createIndex(row, column, parentId);
                                  }
                              
                                  int getColumnCount(final WModelIndex parent) {
                                    return 2;
                                  }
                              
                                  int getRowCount(final WModelIndex parent) {
                                    int treeId;
                                    if ((parent != null)) {
                                      if (parent.getColumn() != 0) {
                                        return 0;
                                      }
                                      Git.Object o = this.getObject(parent);
                                      if (o.type == Git.ObjectType.Tree) {
                                        treeId = this.getTreeId(parent.getInternalId(), parent.getRow());
                                      } else {
                                        return 0;
                                      }
                                    } else {
                                      treeId = 0;
                                    }
                                    return this.treeData_.get(treeId).getRowCount();
                                  }
                              
                                  Object getData(final WModelIndex index, ItemDataRole role) {
                                    if (!(index != null)) {
                                      return null;
                                    }
                                    Git.Object object = this.getObject(index);
                                    switch (index.getColumn()) {
                                      case 0:
                                        if (role.equals(ItemDataRole.Display)) {
                                          if (object.type == Git.ObjectType.Tree) {
                                            return object.name + '/';
                                          } else {
                                            return object.name;
                                          }
                                        } else {
                                          if (role.equals(ItemDataRole.Decoration)) {
                                            if (object.type == Git.ObjectType.Blob) {
                                              return "icons/git-blob.png";
                                            } else {
                                              if (object.type == Git.ObjectType.Tree) {
                                                return "icons/git-tree.png";
                                              }
                                            }
                                          } else {
                                            if (role.equals(ContentsRole)) {
                                              if (object.type == Git.ObjectType.Blob) {
                                                return this.git_.catFile(object.id);
                                              }
                                            }
                                          }
                                        }
                                        break;
                                      case 1:
                                        if (role.equals(ItemDataRole.Display)) {
                                          if (object.type == Git.ObjectType.Tree) {
                                            return "Folder";
                                          } else {
                                            String suffix = getSuffix(object.name);
                                            if (suffix.equals("C") || suffix.equals("cpp")) {
                                              return "C++ Source";
                                            } else {
                                              if (suffix.equals("h") || suffix.equals("") && !this.topLevel(index)) {
                                                return "C++ Header";
                                              } else {
                                                if (suffix.equals("css")) {
                                                  return "CSS Stylesheet";
                                                } else {
                                                  if (suffix.equals("js")) {
                                                    return "JavaScript Source";
                                                  } else {
                                                    if (suffix.equals("md")) {
                                                      return "Markdown";
                                                    } else {
                                                      if (suffix.equals("png") || suffix.equals("gif")) {
                                                        return "Image";
                                                      } else {
                                                        if (suffix.equals("txt")) {
                                                          return "Text";
                                                        } else {
                                                          return null;
                                                        }
                                                      }
                                                    }
                                                  }
                                                }
                                              }
                                            }
                                          }
                                        }
                                    }
                                    return null;
                                  }
                              
                                  Object getHeaderData(int section, Orientation orientation, ItemDataRole role) {
                                    if (orientation == Orientation.Horizontal && role.equals(ItemDataRole.Display)) {
                                      switch (section) {
                                        case 0:
                                          return "File";
                                        case 1:
                                          return "Type";
                                        default:
                                          return null;
                                      }
                                    } else {
                                      return null;
                                    }
                                  }
                              
                                  private Git git_;
                              
                                  static class ChildIndex {
                                    private static Logger logger = LoggerFactory.getLogger(ChildIndex.class);
                              
                                    public int parentId;
                                    public int index;
                              
                                    ChildIndex(int aParent, int anIndex) {
                                      this.parentId = aParent;
                                      this.index = anIndex;
                                    }
                              
                                    boolean equals(Object o) {
                                      GitModel.ChildIndex other = ((ChildIndex) o);
                                      return this.parentId == other.parentId && this.index == other.index;
                                    }
                              
                                    int hashCode() {
                                      int hash = 1;
                                      hash = hash * 31 + this.parentId;
                                      hash = hash * 31 + this.index;
                                      return hash;
                                    }
                                  }
                              
                                  static class Tree {
                                    private static Logger logger = LoggerFactory.getLogger(Tree.class);
                              
                                    Tree(int parentId, int index, final Git.ObjectId object, int rowCount) {
                                      this.index_ = new GitModel.ChildIndex(parentId, index);
                                      this.treeObject_ = object;
                                      this.rowCount_ = rowCount;
                                    }
                              
                                    int getParentId() {
                                      return this.index_.parentId;
                                    }
                              
                                    int getIndex() {
                                      return this.index_.index;
                                    }
                              
                                    Git.ObjectId getTreeObject() {
                                      return this.treeObject_;
                                    }
                              
                                    int getRowCount() {
                                      return this.rowCount_;
                                    }
                              
                                    private GitModel.ChildIndex index_;
                                    private Git.ObjectId treeObject_;
                                    private int rowCount_;
                                  }
                              
                                  private List<GitModel.Tree> treeData_;
                                  private Map<GitModel.ChildIndex, Integer> childPointer_;
                              
                                  int getTreeId(int parentId, int childIndex) {
                                    GitModel.ChildIndex index = new GitModel.ChildIndex(parentId, childIndex);
                                    Integer i = this.childPointer_.get(index);
                                    if (i == null) {
                                      final GitModel.Tree parentItem = this.treeData_.get(parentId);
                                      Git.Object o = this.git_.treeGetObject(parentItem.getTreeObject(), childIndex);
                                      this.treeData_.add(new GitModel.Tree(parentId, childIndex, o.id, this.git_.treeSize(o.id)));
                                      int result = this.treeData_.size() - 1;
                                      this.childPointer_.put(index, result);
                                      return result;
                                    } else {
                                      return i;
                                    }
                                  }
                              
                                  Git.Object getObject(final WModelIndex index) {
                                    int parentId = index.getInternalId();
                                    final GitModel.Tree parentItem = this.treeData_.get(parentId);
                                    return this.git_.treeGetObject(parentItem.getTreeObject(), index.getRow());
                                  }
                              
                                  static String getSuffix(final String fileName) {
                                    int dot = fileName.lastIndexOf('.');
                                    if (dot == -1) {
                                      return "";
                                    } else {
                                      return fileName.substring(dot + 1);
                                    }
                                  }
                              
                                  boolean topLevel(final WModelIndex index) {
                                    return !(this.getParent(index) != null);
                                  }
                                }
                              

                              Note The instantiation of this model with a Tree View is discussed in MVC Tree Views.

                              Top

                              Sorting

                              A model may support sorting by one of its columns. This sorting can be implemented within the model itself.

                              void sort()
                              Sorts the model according to one of its columns.

                              Sorting may be bolted onto a source model using the WSortFilterProxyModel, which is one of the standard proxy models.

                              Model Changes

                              A model does not necessarily need to be a static data source, but its data can also change, and data (rows/columns) can be added or removed. A model needs to generate events to inform Views of these modifications (for the events to which a View is subscribed). When implementing a custom model which is dynamic in nature, it is therefore important to emit these signals when making the modifications.

                              Editing

                              The model API also provides a standard interface to perform editing of the data, and some Views (such as the Tree View and Table Views) can be configured to allow editing of the data.

                              If a custom wants to support this editing API, it needs to reimplement the following methods from WAbstractTableModel:

                              bool setData()
                              Updates data. Views typically use the EditRole for the data used in editing
                              bool insertColumns()
                              Inserts one or more columns
                              bool insertRows()
                              Inserts one or more rows
                              bool removeColumns()
                              Removes one or more columns
                              bool removeRows()
                              Removes one or more rows

                              Top