From 7a14385acccd599438a274d421735a7d0c4bc9fa Mon Sep 17 00:00:00 2001 From: Mateusz Piekos Date: Tue, 17 Jul 2012 16:36:22 +0200 Subject: Added handling of items deletions diff --git a/Swift/QtUI/Whiteboard/GView.cpp b/Swift/QtUI/Whiteboard/GView.cpp index 4e7e006..1e77b62 100644 --- a/Swift/QtUI/Whiteboard/GView.cpp +++ b/Swift/QtUI/Whiteboard/GView.cpp @@ -92,6 +92,14 @@ namespace Swift { return itemsMap_.value(id); } + void GView::deleteItem(QString id) { + QGraphicsItem* item = itemsMap_.value(id); + items_.removeOne(item); + itemsMap_.remove(id); + scene()->removeItem(item); + delete item; + } + QString GView::getNewID() { return idPrefix + P2QSTRING(idGenerator.generateID()); } @@ -237,8 +245,17 @@ namespace Swift { if (!list.isEmpty()) { QGraphicsItem* item = scene()->items(rect).first(); - scene()->removeItem(item); - delete item; + QString id = item->data(100).toString(); + deleteItem(id); + int i = 1; + QList::const_iterator it; + for (it = items_.begin(); it != items_.end(); ++it) { + if (*it == item) { + break; + } + i++; + } + itemDeleted(id, i); } } else if (mode == Circle) { diff --git a/Swift/QtUI/Whiteboard/GView.h b/Swift/QtUI/Whiteboard/GView.h index 9b8f60f..9a26b01 100644 --- a/Swift/QtUI/Whiteboard/GView.h +++ b/Swift/QtUI/Whiteboard/GView.h @@ -36,6 +36,7 @@ namespace Swift { void setIDPrefix(QString prefix); void clear(); QGraphicsItem* getItem(QString id); + void deleteItem(QString id); public slots: void moveUpSelectedItem(); @@ -59,5 +60,6 @@ namespace Swift { signals: void lastItemChanged(QGraphicsItem* item, int pos, GView::Type type); + void itemDeleted(QString id, int pos); }; } diff --git a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp index add9802..6df68f3 100644 --- a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp +++ b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ namespace Swift { graphicsView = new GView(scene, this); graphicsView->setMode(GView::Line); connect(graphicsView, SIGNAL(lastItemChanged(QGraphicsItem*, int, GView::Type)), this, SLOT(handleLastItemChanged(QGraphicsItem*, int, GView::Type))); + connect(graphicsView, SIGNAL(itemDeleted(QString, int)), this, SLOT(handleItemDeleted(QString, int))); widthBox = new QSpinBox(this); connect(widthBox, SIGNAL(valueChanged(int)), this, SLOT(changeLineWidth(int))); @@ -147,6 +149,12 @@ namespace Swift { updateOp->getElement()->accept(visitor); lastOpID = updateOp->getID(); } + + WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast(operation); + if (deleteOp) { + graphicsView->deleteItem(P2QSTRING(deleteOp->getID())); + lastOpID = deleteOp->getID(); + } } void QtWhiteboardWindow::changeLineWidth(int i) @@ -357,6 +365,15 @@ namespace Swift { } } + void QtWhiteboardWindow::handleItemDeleted(QString id, int pos) { + WhiteboardDeleteOperation::ref deleteOp = boost::make_shared(); + deleteOp->setID(Q2PSTRING(id)); + deleteOp->setPos(pos); + deleteOp->setParentID(lastOpID); + lastOpID = Q2PSTRING(id); + whiteboardSession_->sendOperation(deleteOp); + } + void QtWhiteboardWindow::handleSessionTerminate() { hide(); } diff --git a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h index 8adee83..ed1bfa4 100644 --- a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h +++ b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h @@ -50,6 +50,7 @@ namespace Swift { void setPolygonMode(); void setSelectMode(); void handleLastItemChanged(QGraphicsItem* item, int pos, GView::Type type); + void handleItemDeleted(QString id, int pos); private: void handleSessionTerminate(); diff --git a/Swift/QtUI/Whiteboard/WhiteboardElementDrawingVisitor.h b/Swift/QtUI/Whiteboard/WhiteboardElementDrawingVisitor.h index fc28c6f..4ed1af5 100644 --- a/Swift/QtUI/Whiteboard/WhiteboardElementDrawingVisitor.h +++ b/Swift/QtUI/Whiteboard/WhiteboardElementDrawingVisitor.h @@ -116,6 +116,7 @@ namespace Swift { item->setData(100, id); graphicsView_->addItem(item, id, pos_); } + item->setPos(0,0); QPolygonF polygon; std::vector >::const_iterator it = element.getPoints().begin(); for (; it != element.getPoints().end(); ++it) { diff --git a/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp b/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp index 7581115..ea00335 100644 --- a/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp +++ b/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,17 @@ namespace Swift { } catch (boost::bad_lexical_cast&) { } operation = updateOp; + } else if (type == "delete") { + WhiteboardDeleteOperation::ref deleteOp = boost::make_shared(); + try { + deleteOp->setID(attributes.getAttributeValue("id").get_value_or("")); + deleteOp->setParentID(attributes.getAttributeValue("parentid").get_value_or("")); + deleteOp->setPos(boost::lexical_cast(attributes.getAttributeValue("pos").get_value_or("0"))); + } catch (boost::bad_lexical_cast&) { + } + operation = deleteOp; } + } else if (level_ == 2) { if (element == "line") { int x1 = 0; diff --git a/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp b/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp index d22d4ce..c5e3d4f 100644 --- a/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Swift { void WhiteboardElementSerializingVisitor::visit(WhiteboardLineElement& line) { @@ -165,6 +166,17 @@ namespace Swift { operationNode->addNode(visitor.getResult()); } + + WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast(payload->getOperation()); + if (deleteOp) { + try { + operationNode->setAttribute("type", "delete"); + operationNode->setAttribute("pos", boost::lexical_cast(deleteOp->getPos())); + operationNode->setAttribute("id", deleteOp->getID()); + operationNode->setAttribute("parentid", deleteOp->getParentID()); + } catch (boost::bad_lexical_cast&) { + } + } element.addNode(operationNode); } element.setAttribute("type", typeToString(payload->getType())); diff --git a/Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h b/Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h new file mode 100644 index 0000000..139ef0c --- /dev/null +++ b/Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2012 Mateusz Piękos + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include + +namespace Swift { + class WhiteboardDeleteOperation : public WhiteboardOperation { + public: + typedef boost::shared_ptr ref; + public: + ~WhiteboardDeleteOperation() { + } + }; +} diff --git a/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp index 996d8a3..7526fdd 100644 --- a/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp +++ b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include using namespace Swift; @@ -24,6 +25,7 @@ class WhiteboardClientTest : public CppUnit::TestFixture { CPPUNIT_TEST(testSynchronize_clientInterruption); CPPUNIT_TEST(testSynchronize_serverInterruption); CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations); + CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations2); CPPUNIT_TEST_SUITE_END(); public: @@ -572,6 +574,97 @@ public: //what gives 0abcd on both sides. } + /*! + * /\ + * / \ + * \ / + * \/ + */ + void testSynchronize_nonInterruptedMixOperations2() { + WhiteboardClient client; + WhiteboardInsertOperation::ref serverOp; + serverOp = createInsertOperation("0", "", 0); + WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); + CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); + + serverOp = createInsertOperation("1", "0", 1); + pairResult = client.handleServerOperationReceived(serverOp); + CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); + //Client receives first local operation, because it's parented off "0" which exists + //in server history and client doesn't wait for any operation ack from server, + //so this operation could be send + WhiteboardInsertOperation::ref clientOp; + WhiteboardUpdateOperation::ref clientUpdateOp; + WhiteboardDeleteOperation::ref clientDeleteOp; + clientUpdateOp = createUpdateOperation("a", "1", 0); + WhiteboardEllipseElement::ref aElement = boost::make_shared(0,0,0,0); + clientUpdateOp->setElement(aElement); + checkOperation(client.handleLocalOperationReceived(clientUpdateOp), "a", "1", 0, aElement); + + //Client receives second local operation, client didn't receive ack about previous + //operation from the server so it can't be send. + clientDeleteOp = createDeleteOperation("b", "a", 1); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientDeleteOp)); + + //Client receives new operation from server, it should be transformed against + //"a" and "b" before adding to local operations history because it's parented off "0". + //Because client is waiting for ack of "a", there is no operation to send to server + serverOp = createInsertOperation("c", "1", 2); + WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); + serverOp->setElement(cElement); + pairResult = client.handleServerOperationReceived(serverOp); + checkOperation(pairResult.client, "c", "b", 1, cElement); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); + + //Client receives new operation from server, it should be transformed against + //results of previous transformations, returned operation should be parented off + //"c" existing in local history. + //Because client is waiting for ack of "a", there is no operation to send to server + WhiteboardUpdateOperation::ref serverUpdateOp = createUpdateOperation("d", "c", 0); + WhiteboardEllipseElement::ref dElement = boost::make_shared(0,0,0,0); + serverUpdateOp->setElement(dElement); + pairResult = client.handleServerOperationReceived(serverUpdateOp); + checkOperation(pairResult.client, "d", "c", 0, dElement); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); + + //Client receives confirmation about processing "a", it should send next operation + //to server which is "b", but it should be version parented of transformed "a" + serverUpdateOp = createUpdateOperation("a", "d", 0); + pairResult = client.handleServerOperationReceived(serverUpdateOp); + checkOperation(pairResult.server, "b", "a", 1); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); + + + //Client receives confirmation about processing "b", there aren't any operations + //waiting so it should return nothing. + WhiteboardDeleteOperation::ref serverDeleteOp = createDeleteOperation("b", "a", 0); + serverDeleteOp->setOrigin(WhiteboardOperation::Other); + pairResult = client.handleServerOperationReceived(serverDeleteOp); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); + CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); + + //Client operations: + //ID pos + //0 0 + //a 1 + //b 2 + //c 3 + //d 4 + // + //Server operations: + //ID pos + //0 0 + //c 1 + //d 2 + //a 1 + //b 2 + // + //what gives 0abcd on both sides. + } + + WhiteboardInsertOperation::ref createInsertOperation(std::string id, std::string parent, int pos) { WhiteboardInsertOperation::ref operation = boost::make_shared(); operation->setParentID(parent); @@ -588,6 +681,14 @@ public: return operation; } + WhiteboardDeleteOperation::ref createDeleteOperation(std::string id, std::string parent, int pos) { + WhiteboardDeleteOperation::ref operation = boost::make_shared(); + operation->setParentID(parent); + operation->setID(id); + operation->setPos(pos); + return operation; + } + void checkOperation(WhiteboardOperation::ref operation, std::string id, std::string parent, int pos = -1, WhiteboardElement::ref element = WhiteboardElement::ref()) { CPPUNIT_ASSERT_EQUAL(id, operation->getID()); CPPUNIT_ASSERT_EQUAL(parent, operation->getParentID()); diff --git a/Swiften/Whiteboard/WhiteboardClient.cpp b/Swiften/Whiteboard/WhiteboardClient.cpp index 2dd9e45..70271e6 100644 --- a/Swiften/Whiteboard/WhiteboardClient.cpp +++ b/Swiften/Whiteboard/WhiteboardClient.cpp @@ -21,6 +21,10 @@ namespace Swift { if (updateOp) { op = boost::make_shared(*updateOp.get()); } + WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast(operation); + if (deleteOp) { + op = boost::make_shared(*deleteOp.get()); + } if (bridge_.size() > 0) { op->setParentID(bridge_.back()->getID()); @@ -37,6 +41,11 @@ namespace Swift { if (updateOp) { op = boost::make_shared(*updateOp.get()); } + WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast(operation); + if (deleteOp) { + op = boost::make_shared(*deleteOp.get()); + } + if (serverOperations_.size() > 0) { op->setParentID(serverOperations_.back()->getID()); diff --git a/Swiften/Whiteboard/WhiteboardTransformer.cpp b/Swiften/Whiteboard/WhiteboardTransformer.cpp index 32b1183..60963fd 100644 --- a/Swiften/Whiteboard/WhiteboardTransformer.cpp +++ b/Swiften/Whiteboard/WhiteboardTransformer.cpp @@ -13,6 +13,8 @@ namespace Swift { WhiteboardInsertOperation::ref serverInsert = boost::dynamic_pointer_cast(serverOp); WhiteboardUpdateOperation::ref clientUpdate = boost::dynamic_pointer_cast(clientOp); WhiteboardUpdateOperation::ref serverUpdate = boost::dynamic_pointer_cast(serverOp); + WhiteboardDeleteOperation::ref clientDelete = boost::dynamic_pointer_cast(clientOp); + WhiteboardDeleteOperation::ref serverDelete = boost::dynamic_pointer_cast(serverOp); if (clientInsert && serverInsert) { return transform(clientInsert, serverInsert); } else if (clientUpdate && serverUpdate) { @@ -21,6 +23,16 @@ namespace Swift { return transform(clientInsert, serverUpdate); } else if (clientUpdate && serverInsert) { return transform(clientUpdate, serverInsert); + } else if (clientDelete && serverDelete) { + return transform(clientDelete, serverDelete); + } else if (clientInsert && serverDelete) { + return transform(clientInsert, serverDelete); + } else if (clientDelete && serverInsert) { + return transform(clientDelete, serverInsert); + } else if (clientUpdate && serverDelete) { + return transform(clientUpdate, serverDelete); + } else if (clientDelete && serverUpdate) { + return transform(clientDelete, serverUpdate); } else { return std::pair(); } @@ -75,4 +87,73 @@ namespace Swift { } return result; } + + std::pair WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) { + std::pair result; + result.first = boost::make_shared(*serverOp.get()); + result.first->setParentID(clientOp->getID()); + result.second = boost::make_shared(*clientOp.get()); + result.second->setParentID(serverOp->getID()); + if (clientOp->getPos() < serverOp->getPos()) { + result.first->setPos(result.first->getPos()-1); + } else if (clientOp->getPos() < serverOp->getPos()) { + result.second->setPos(result.second->getPos()-1); + } +//TODO: situation with deletion of the same item + return result; + } + + std::pair WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) { + std::pair result; + result.first = boost::make_shared(*serverOp.get()); + result.first->setParentID(clientOp->getID()); + result.second = boost::make_shared(*clientOp.get()); + result.second->setParentID(serverOp->getID()); + if (clientOp->getPos() <= serverOp->getPos()) { + result.first->setPos(result.first->getPos()+1); + } else { + result.second->setPos(result.second->getPos()-1); + } + return result; + } + + std::pair WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) { + std::pair result; + result.first = boost::make_shared(*serverOp.get()); + result.first->setParentID(clientOp->getID()); + result.second = boost::make_shared(*clientOp.get()); + result.second->setParentID(serverOp->getID()); + if (serverOp->getPos() <= clientOp->getPos()) { + result.second->setPos(result.second->getPos()+1); + } else { + result.first->setPos(result.first->getPos()-1); + } + return result; + } + + std::pair WhiteboardTransformer::transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) { + std::pair result; + result.first = boost::make_shared(*serverOp.get()); + result.first->setParentID(clientOp->getID()); + result.second = boost::make_shared(*clientOp.get()); + result.second->setParentID(serverOp->getID()); + if (clientOp->getPos() > serverOp->getPos()) { + result.second->setPos(result.second->getPos()-1); + } + return result; +//TODO: situation with the same pos + } + + std::pair WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp) { + std::pair result; + result.first = boost::make_shared(*serverOp.get()); + result.first->setParentID(clientOp->getID()); + result.second = boost::make_shared(*clientOp.get()); + result.second->setParentID(serverOp->getID()); + if (clientOp->getPos() < serverOp->getPos()) { + result.first->setPos(result.first->getPos()-1); + } + return result; +//TODO: situation with the same pos + } } diff --git a/Swiften/Whiteboard/WhiteboardTransformer.h b/Swiften/Whiteboard/WhiteboardTransformer.h index 0bab4d3..0ff5c7f 100644 --- a/Swiften/Whiteboard/WhiteboardTransformer.h +++ b/Swiften/Whiteboard/WhiteboardTransformer.h @@ -8,15 +8,21 @@ #include #include +#include #include namespace Swift { class WhiteboardTransformer { public: -static std::pair transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp); + static std::pair transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp); static std::pair transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp); static std::pair transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp); static std::pair transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp); static std::pair transform(WhiteboardInsertOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp); + static std::pair transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp); + static std::pair transform(WhiteboardInsertOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp); + static std::pair transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp); + static std::pair transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp); + static std::pair transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp); }; } -- cgit v0.10.2-6-g49f6