Project

General

Profile

Bug #589 ยป treeview_stale_index2.cc

Johny Mattsson, 11/04/2010 08:26 AM

 
#include <Wt/WTreeView>
#include <Wt/WApplication>
#include <Wt/WAbstractItemModel>
#include <Wt/WPushButton>
#include <Wt/WContainerWidget>
#include <Wt/WVBoxLayout>


/*
* Usage:
* 1) Build (link with -lwt -lwthttp) & start test program.
* 2) Note that there are 3 nodes visible, a parent node and a/b nodes under
* that.
* 3) Select the "a" (or "b") node by clicking it.
* 4) Press the "Remove A" button.
* 5) Observe that the server exits due to getting a stale index, either
* a or b depending on which one was selected.
* If neither "a" nor "b" was selected, there is no error.
*/

class TestModel : public Wt::WAbstractItemModel
{
public:
TestModel ()
: Wt::WAbstractItemModel (),
par (createIndex (0, 0, (void *)("parent"))),
a (createIndex (0, 0, (void *)("node a"))),
b (createIndex (1, 0, (void *)("node b"))),
b2 (createIndex (0, 0, (void *)("node b2"))),
invalid (),
a_deleted (false)
{
}

void remove_a ()
{
if (!a_deleted)
{
beginRemoveRows (par, a.row (), a.row ());
a_deleted = true;
endRemoveRows ();
// Request the index be updated, since its internal pointer
// has been changed.
dataChanged ().emit (b2, b2);
}
}

void assert_fresh (const Wt::WModelIndex &idx, const std::string &where) const
{
if (a_deleted && (idx == a || idx == b))
{
std::cout << "### Error: stale index '" <<
static_cast<const char *> (idx.internalPointer ()) <<
"' in " << where << "\n";
abort ();
}
}

int rowCount (const Wt::WModelIndex &idx) const
{
if (idx == invalid)
return 1;
if (idx == par)
return a_deleted ? 1 : 2;
return 0;
}

int columnCount (const Wt::WModelIndex &idx) const
{
if (idx == invalid || idx == par)
return 1;
return 0;
}

Wt::WModelIndex index (int row, int col, const Wt::WModelIndex &p) const
{
assert_fresh (p, "TestModel::index()");

if (p == invalid)
{
if (row == 0 && col == 0)
return par;
}
else if (p == par && col == 0)
{
if (a_deleted)
{
// The correct index at parent,0,0 is now b2, not a
if (row == 0) return b2;
}
else {
if (row == 0) return a;
if (row == 1) return b;
}
}
return invalid;
}

Wt::WModelIndex parent (const Wt::WModelIndex &idx) const
{
assert_fresh (idx, "TestModel::parent()");
if (idx == a || idx == b)
return par;
if (a_deleted && idx == b2)
return par;
return invalid;
}

boost::any data (const Wt::WModelIndex &idx, int role) const
{
assert_fresh (idx, "TestModel::data()");

if (role == Wt::DisplayRole)
return boost::any (
static_cast<const char *> (idx.internalPointer ()));
return boost::any ();
}

Wt::WFlags<Wt::ItemFlag> flags (const Wt::WModelIndex &idx) const
{
if (idx == par || idx == a || idx == b || idx == b2)
return Wt::ItemIsSelectable;
else
return 0;
}

private:
const Wt::WModelIndex par, a, b, b2, invalid;
bool a_deleted;
};

class TestApp : public Wt::WApplication
{
public:
explicit TestApp (const Wt::WEnvironment &env)
: Wt::WApplication (env),
model (new TestModel ())
{
Wt::WVBoxLayout *vbl = new Wt::WVBoxLayout ();
root ()->setLayout (vbl);
Wt::WTreeView *tv = new Wt::WTreeView ();
tv->setModel (model);
tv->expand (model->index (0, 0, Wt::WModelIndex ()));
tv->setSelectionMode (Wt::SingleSelection);
vbl->addWidget (tv, 100);
Wt::WPushButton *btn = new Wt::WPushButton ("Remove A");
btn->clicked ().connect (SLOT(model, TestModel::remove_a));
vbl->addWidget (btn);
}

private:
TestModel *model;
};




Wt::WApplication *
createApp (const Wt::WEnvironment &env)
{
return new TestApp (env);
}

int main (int argc, char *argv[])
{
return Wt::WRun (argc, argv, &createApp);
}
    (1-1/1)