Thread: [Ktutorial-commits] SF.net SVN: ktutorial:[208] trunk/ktutorial/ktutorial-editor (Page 3)
Status: Alpha
Brought to you by:
danxuliu
From: <dan...@us...> - 2010-03-29 05:56:14
|
Revision: 208 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=208&view=rev Author: danxuliu Date: 2010-03-29 05:56:07 +0000 (Mon, 29 Mar 2010) Log Message: ----------- Fix removing an step from the middle and undoing the operation (the step was readded at the end). It required being able to add steps at any position in the tutorial, and not only at the end. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.cpp trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.h trunk/ktutorial/ktutorial-editor/src/data/Tutorial.cpp trunk/ktutorial/ktutorial-editor/src/data/Tutorial.h trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.h trunk/ktutorial/ktutorial-editor/tests/unit/commands/TutorialCommandsTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/data/TutorialTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialTreeItemTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.cpp 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.cpp 2010-03-29 05:56:07 UTC (rev 208) @@ -165,21 +165,46 @@ mDeleteStepInDestructor = true; } + virtual void undo() { + mDeleteStepInDestructor = true; + mTutorial->removeStep(mStep); + } + +}; + +class AddStepAtEnd: public AddStep { +public: + + AddStepAtEnd(QUndoCommand* parent = 0): AddStep(parent) { + } + virtual void redo() { mDeleteStepInDestructor = false; mTutorial->addStep(mStep); } - virtual void undo() { - mDeleteStepInDestructor = true; - mTutorial->removeStep(mStep); +}; + +class AddStepAtIndex: public AddStep { +public: + + int mIndex; + + AddStepAtIndex(QUndoCommand* parent = 0): AddStep(parent) { } + virtual void redo() { + mDeleteStepInDestructor = false; + mTutorial->addStep(mStep, mIndex); + } + }; class RemoveStep: public BaseStepCommand { public: + int mIndex; + RemoveStep(QUndoCommand* parent = 0): BaseStepCommand(parent) { setText(i18nc("@action", "Remove step")); mDeleteStepInDestructor = false; @@ -187,12 +212,13 @@ virtual void redo() { mDeleteStepInDestructor = true; + mIndex = mTutorial->steps().indexOf(mStep); mTutorial->removeStep(mStep); } virtual void undo() { mDeleteStepInDestructor = false; - mTutorial->addStep(mStep); + mTutorial->addStep(mStep, mIndex); } }; @@ -246,12 +272,21 @@ } QUndoCommand* TutorialCommands::addStep(Step* step, QUndoCommand* parent) { - AddStep* command = new AddStep(parent); + AddStepAtEnd* command = new AddStepAtEnd(parent); command->mTutorial = mTutorial; command->mStep = step; return command; } +QUndoCommand* TutorialCommands::addStep(Step* step, int index, + QUndoCommand* parent) { + AddStepAtIndex* command = new AddStepAtIndex(parent); + command->mTutorial = mTutorial; + command->mStep = step; + command->mIndex = index; + return command; +} + QUndoCommand* TutorialCommands::removeStep(Step* step, QUndoCommand* parent) { RemoveStep* command = new RemoveStep(parent); command->mTutorial = mTutorial; Modified: trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.h 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/src/commands/TutorialCommands.h 2010-03-29 05:56:07 UTC (rev 208) @@ -107,6 +107,19 @@ QUndoCommand* addStep(Step* step, QUndoCommand* parent = 0); /** + * Creates a new undoable command to add the given step to the tutorial at + * the given index. + * If the command was not executed yet, or it was undone, the step is + * deleted when the command is deleted. + * + * @param step The step to add to the tutorial. + * @param index The index to add the step at. + * @param parent The parent QUndoCommand. + * @return The new QUndoCommand. + */ + QUndoCommand* addStep(Step* step, int index, QUndoCommand* parent = 0); + + /** * Creates a new undoable command to remove the given step from the * tutorial. * If the command was executed and not undone, the step is deleted when the Modified: trunk/ktutorial/ktutorial-editor/src/data/Tutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/Tutorial.cpp 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/src/data/Tutorial.cpp 2010-03-29 05:56:07 UTC (rev 208) @@ -86,11 +86,15 @@ } void Tutorial::addStep(Step* step) { + addStep(step, mSteps.count()); +} + +void Tutorial::addStep(Step* step, int index) { Q_ASSERT(!mSteps.contains(step)); - mSteps.append(step); + mSteps.insert(index, step); - emit stepAdded(step); + emit stepAdded(step, index); } QList<Step*> Tutorial::steps() const { Modified: trunk/ktutorial/ktutorial-editor/src/data/Tutorial.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/Tutorial.h 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/src/data/Tutorial.h 2010-03-29 05:56:07 UTC (rev 208) @@ -31,8 +31,8 @@ * generate the code to create a true KTutorial::Tutorial. * * When any attribute is modified, dataChanged(Tutorial*) signal is emitted. - * When steps are added or removed, stepAdded(Step*) and stepRemoved(Step*) are - * emitted. + * When steps are added or removed, stepAdded(Step*, int) and stepRemoved(Step*) + * are emitted. */ class Tutorial: public QObject { Q_OBJECT @@ -76,6 +76,17 @@ * @param step The step to add. */ void addStep(Step* step); + + /** + * Adds a new step to this Tutorial at the given position. + * The Tutorial gets ownership of the Step, so it is deleted when the + * Tutorial is deleted. + * + * @param step The step to add. + * @param index The index to add the step at. + */ + void addStep(Step* step, int index); + QList<Step*> steps() const; /** @@ -99,8 +110,9 @@ * Emitted when the step is added to the tutorial. * * @param step The step added. + * @param index The index where the step was added. */ - void stepAdded(Step* step); + void stepAdded(Step* step, int index); /** * Emitted when the step is removed from the tutorial. Modified: trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.cpp 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.cpp 2010-03-29 05:56:07 UTC (rev 208) @@ -44,10 +44,10 @@ this, SLOT(update(Tutorial*))); foreach(Step* step, tutorial->steps()) { - addStep(step); + addStep(step, mStepTreeItems.count()); } - connect(tutorial, SIGNAL(stepAdded(Step*)), - this, SLOT(addStep(Step*))); + connect(tutorial, SIGNAL(stepAdded(Step*, int)), + this, SLOT(addStep(Step*, int))); connect(tutorial, SIGNAL(stepRemoved(Step*)), this, SLOT(removeStep(Step*))); } @@ -131,12 +131,14 @@ childIndex++; } + + mStepTreeItemBaseIndex = childIndex; } -void TutorialTreeItem::addStep(Step* step) { +void TutorialTreeItem::addStep(Step* step, int index) { StepTreeItem* stepTreeItem = new StepTreeItem(step, this); - appendChild(stepTreeItem); - mStepTreeItems.append(stepTreeItem); + insertChild(stepTreeItem, mStepTreeItemBaseIndex + index); + mStepTreeItems.insert(index, stepTreeItem); } void TutorialTreeItem::removeStep(Step* step) { Modified: trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.h 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/src/view/TutorialTreeItem.h 2010-03-29 05:56:07 UTC (rev 208) @@ -38,9 +38,9 @@ * | -The custom setup code * |-Tear down: * | -The custom tear down code - * |-Step first step added + * |-Step first step * | ... - * |-Step second step added + * |-Step second step * | ... * ... * @@ -131,6 +131,13 @@ QList<StepTreeItem*> mStepTreeItems; /** + * The base index to add new StepTreeItems. + * It is the next index to the last optional data item (license, setup or + * tear down code). + */ + int mStepTreeItemBaseIndex; + + /** * Returns the StepTreeItem for the given Step. * * @param step The Step to get its StepTreeItem. @@ -156,8 +163,9 @@ * Adds a new StepTreeItem when a Step is added in the tutorial. * * @param step The Step added in the Tutorial. + * @param index The index where the Step was added in the Tutorial. */ - void addStep(Step* step); + void addStep(Step* step, int index); /** * Removes the StepTreeItem for the Step removed in the tutorial. Modified: trunk/ktutorial/ktutorial-editor/tests/unit/commands/TutorialCommandsTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/commands/TutorialCommandsTest.cpp 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/tests/unit/commands/TutorialCommandsTest.cpp 2010-03-29 05:56:07 UTC (rev 208) @@ -50,8 +50,12 @@ void testAddStepRedo(); void testAddStepUndo(); + void testAddStepAtIndexRedo(); + void testAddStepAtIndexUndo(); + void testRemoveStepRedo(); void testRemoveStepUndo(); + void testRemoveStepAtMiddleUndo(); }; @@ -239,6 +243,50 @@ QCOMPARE(tutorial.steps().count(), 0); } +void TutorialCommandsTest::testAddStepAtIndexRedo() { + Tutorial tutorial; + TutorialCommands commands(&tutorial); + QUndoCommand parent; + Step* step1 = new Step(); + Step* step2 = new Step(); + Step* step3 = new Step(); + + tutorial.addStep(step1); + tutorial.addStep(step3); + + QUndoCommand* command = commands.addStep(step2, 1, &parent); + command->redo(); + + QCOMPARE(command->text(), i18nc("@action", "Add step")); + QCOMPARE(parent.child(0), command); + QCOMPARE(tutorial.steps().count(), 3); + QCOMPARE(tutorial.steps()[0], step1); + QCOMPARE(tutorial.steps()[1], step2); + QCOMPARE(tutorial.steps()[2], step3); +} + +void TutorialCommandsTest::testAddStepAtIndexUndo() { + Tutorial tutorial; + TutorialCommands commands(&tutorial); + QUndoCommand parent; + Step* step1 = new Step(); + Step* step2 = new Step(); + Step* step3 = new Step(); + + tutorial.addStep(step1); + tutorial.addStep(step3); + + QUndoCommand* command = commands.addStep(step2, 1, &parent); + command->redo(); + command->undo(); + + QCOMPARE(command->text(), i18nc("@action", "Add step")); + QCOMPARE(parent.child(0), command); + QCOMPARE(tutorial.steps().count(), 2); + QCOMPARE(tutorial.steps()[0], step1); + QCOMPARE(tutorial.steps()[1], step3); +} + void TutorialCommandsTest::testRemoveStepRedo() { Tutorial tutorial; TutorialCommands commands(&tutorial); @@ -273,6 +321,30 @@ QCOMPARE(tutorial.steps()[0], step); } +void TutorialCommandsTest::testRemoveStepAtMiddleUndo() { + Tutorial tutorial; + TutorialCommands commands(&tutorial); + QUndoCommand parent; + Step* step1 = new Step(); + Step* step2 = new Step(); + Step* step3 = new Step(); + + tutorial.addStep(step1); + tutorial.addStep(step2); + tutorial.addStep(step3); + + QUndoCommand* command = commands.removeStep(step2, &parent); + command->redo(); + command->undo(); + + QCOMPARE(command->text(), i18nc("@action", "Remove step")); + QCOMPARE(parent.child(0), command); + QCOMPARE(tutorial.steps().count(), 3); + QCOMPARE(tutorial.steps()[0], step1); + QCOMPARE(tutorial.steps()[1], step2); + QCOMPARE(tutorial.steps()[2], step3); +} + QTEST_MAIN(TutorialCommandsTest) #include "TutorialCommandsTest.moc" Modified: trunk/ktutorial/ktutorial-editor/tests/unit/data/TutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/TutorialTest.cpp 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/TutorialTest.cpp 2010-03-29 05:56:07 UTC (rev 208) @@ -45,13 +45,16 @@ void testSetCustomTearDownCode(); void testAddStep(); + void testAddStepAtIndex(); void testRemoveStep(); private: int mStepStarType; - void assertStepSignal(const QSignalSpy& spy, int index, Step* step); + void assertStepAddedSignal(const QSignalSpy& spy, int index, Step* step, + int stepIndex); + void assertStepRemovedSignal(const QSignalSpy& spy, int index, Step* step); }; @@ -180,7 +183,7 @@ Step* step2 = new Step(); Step* step3 = new Step(); - QSignalSpy stepAddedSpy(&tutorial, SIGNAL(stepAdded(Step*))); + QSignalSpy stepAddedSpy(&tutorial, SIGNAL(stepAdded(Step*, int))); tutorial.addStep(step1); tutorial.addStep(step2); @@ -191,11 +194,33 @@ QCOMPARE(tutorial.steps()[1], step2); QCOMPARE(tutorial.steps()[2], step3); QCOMPARE(stepAddedSpy.count(), 3); - assertStepSignal(stepAddedSpy, 0, step1); - assertStepSignal(stepAddedSpy, 1, step2); - assertStepSignal(stepAddedSpy, 2, step3); + assertStepAddedSignal(stepAddedSpy, 0, step1, 0); + assertStepAddedSignal(stepAddedSpy, 1, step2, 1); + assertStepAddedSignal(stepAddedSpy, 2, step3, 2); } +void TutorialTest::testAddStepAtIndex() { + Tutorial tutorial; + Step* step1 = new Step(); + Step* step2 = new Step(); + Step* step3 = new Step(); + + QSignalSpy stepAddedSpy(&tutorial, SIGNAL(stepAdded(Step*, int))); + + tutorial.addStep(step2, 0); + tutorial.addStep(step1, 0); + tutorial.addStep(step3, 2); + + QCOMPARE(tutorial.steps().count(), 3); + QCOMPARE(tutorial.steps()[0], step1); + QCOMPARE(tutorial.steps()[1], step2); + QCOMPARE(tutorial.steps()[2], step3); + QCOMPARE(stepAddedSpy.count(), 3); + assertStepAddedSignal(stepAddedSpy, 0, step2, 0); + assertStepAddedSignal(stepAddedSpy, 1, step1, 0); + assertStepAddedSignal(stepAddedSpy, 2, step3, 2); +} + void TutorialTest::testRemoveStep() { Tutorial tutorial; @@ -217,21 +242,33 @@ QCOMPARE(tutorial.steps()[0], &step1); QCOMPARE(tutorial.steps()[1], &step3); QCOMPARE(stepRemovedSpy.count(), 1); - assertStepSignal(stepRemovedSpy, 0, &step2); + assertStepRemovedSignal(stepRemovedSpy, 0, &step2); tutorial.removeStep(&step1); tutorial.removeStep(&step3); QCOMPARE(tutorial.steps().count(), 0); QCOMPARE(stepRemovedSpy.count(), 3); - assertStepSignal(stepRemovedSpy, 1, &step1); - assertStepSignal(stepRemovedSpy, 2, &step3); + assertStepRemovedSignal(stepRemovedSpy, 1, &step1); + assertStepRemovedSignal(stepRemovedSpy, 2, &step3); } /////////////////////////////////// Helpers //////////////////////////////////// -void TutorialTest::assertStepSignal(const QSignalSpy& spy, int index, - Step* step) { +void TutorialTest::assertStepAddedSignal(const QSignalSpy& spy, int index, + Step* step, int stepIndex) { + QCOMPARE(spy.at(index).count(), 2); + + QVariant argument = spy.at(index).at(0); + QCOMPARE(argument.userType(), mStepStarType); + QCOMPARE(qvariant_cast<Step*>(argument), step); + argument = spy.at(index).at(1); + QCOMPARE(argument.type(), QVariant::Int); + QCOMPARE(argument.toInt(), stepIndex); +} + +void TutorialTest::assertStepRemovedSignal(const QSignalSpy& spy, int index, + Step* step) { QCOMPARE(spy.at(index).count(), 1); QVariant argument = spy.at(index).at(0); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialTreeItemTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialTreeItemTest.cpp 2010-03-29 03:59:41 UTC (rev 207) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialTreeItemTest.cpp 2010-03-29 05:56:07 UTC (rev 208) @@ -57,6 +57,7 @@ void testTutorialSetCustomTearDownCodeEmpty(); void testTutorialAddStep(); + void testTutorialAddStepAtIndex(); void testTutorialRemoveStep(); @@ -398,6 +399,30 @@ assertStep(item.child(2), "Step id"); } +void TutorialTreeItemTest::testTutorialAddStepAtIndex() { + Tutorial tutorial; + TutorialTreeItem item(&tutorial); + + Step* step2 = new Step(); + step2->setId("Step id2"); + tutorial.addStep(step2, 0); + + Step* step1 = new Step(); + step1->setId("Step id1"); + tutorial.addStep(step1, 0); + + Step* step3 = new Step(); + step3->setId("Step id3"); + tutorial.addStep(step3, 2); + + QCOMPARE(item.childCount(), 5); + assertEmptyName(item.child(0)); + assertEmptyDescription(item.child(1)); + assertStep(item.child(2), "Step id1"); + assertStep(item.child(3), "Step id2"); + assertStep(item.child(4), "Step id3"); +} + void TutorialTreeItemTest::testTutorialRemoveStep() { Tutorial tutorial; TutorialTreeItem item(&tutorial); @@ -478,17 +503,32 @@ assertStep(item.child(4), "First step"); assertStep(item.child(5), "Second step"); - tutorial.setLicenseText("The license text"); + Step* step3 = new Step(); + step3->setId("Third step"); + tutorial.addStep(step3, 1); QCOMPARE(item.text(), i18nc("@item", "Tutorial %1", "theName")); QCOMPARE(item.childCount(), 7); assertName(item.child(0), "The name"); assertDescription(item.child(1), "The description"); + assertCustomSetupCode(item.child(2), "The setup code"); + assertCustomTearDownCode(item.child(3), "The tear down code"); + assertStep(item.child(4), "First step"); + assertStep(item.child(5), "Third step"); + assertStep(item.child(6), "Second step"); + + tutorial.setLicenseText("The license text"); + + QCOMPARE(item.text(), i18nc("@item", "Tutorial %1", "theName")); + QCOMPARE(item.childCount(), 8); + assertName(item.child(0), "The name"); + assertDescription(item.child(1), "The description"); assertLicenseText(item.child(2), "The license text"); assertCustomSetupCode(item.child(3), "The setup code"); assertCustomTearDownCode(item.child(4), "The tear down code"); assertStep(item.child(5), "First step"); - assertStep(item.child(6), "Second step"); + assertStep(item.child(6), "Third step"); + assertStep(item.child(7), "Second step"); } void TutorialTreeItemTest::testChildOrderWhenUnsettingDataInTutorial() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-03-29 06:46:28
|
Revision: 209 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=209&view=rev Author: danxuliu Date: 2010-03-29 06:46:22 +0000 (Mon, 29 Mar 2010) Log Message: ----------- Fix removing a reaction from the middle and undoing the operation (the reaction was readded at the end). It required being able to add reactions at any position in the step, and not only at the end. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.cpp trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.h trunk/ktutorial/ktutorial-editor/src/data/Step.cpp trunk/ktutorial/ktutorial-editor/src/data/Step.h trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.h trunk/ktutorial/ktutorial-editor/tests/unit/commands/StepCommandsTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/data/StepTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/StepTreeItemTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.cpp 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.cpp 2010-03-29 06:46:22 UTC (rev 209) @@ -142,21 +142,46 @@ mDeleteReactionInDestructor = true; } + virtual void undo() { + mDeleteReactionInDestructor = true; + mStep->removeReaction(mReaction); + } + +}; + +class AddReactionAtEnd: public AddReaction { +public: + + AddReactionAtEnd(QUndoCommand* parent = 0): AddReaction(parent) { + } + virtual void redo() { mDeleteReactionInDestructor = false; mStep->addReaction(mReaction); } - virtual void undo() { - mDeleteReactionInDestructor = true; - mStep->removeReaction(mReaction); +}; + +class AddReactionAtIndex: public AddReaction { +public: + + int mIndex; + + AddReactionAtIndex(QUndoCommand* parent = 0): AddReaction(parent) { } + virtual void redo() { + mDeleteReactionInDestructor = false; + mStep->addReaction(mReaction, mIndex); + } + }; class RemoveReaction: public BaseReactionCommand { public: + int mIndex; + RemoveReaction(QUndoCommand* parent = 0): BaseReactionCommand(parent) { setText(i18nc("@action", "Remove reaction")); mDeleteReactionInDestructor = false; @@ -164,12 +189,13 @@ virtual void redo() { mDeleteReactionInDestructor = true; + mIndex = mStep->reactions().indexOf(mReaction); mStep->removeReaction(mReaction); } virtual void undo() { mDeleteReactionInDestructor = false; - mStep->addReaction(mReaction); + mStep->addReaction(mReaction, mIndex); } }; @@ -212,12 +238,21 @@ QUndoCommand* StepCommands::addReaction(Reaction* reaction, QUndoCommand* parent) { - AddReaction* command = new AddReaction(parent); + AddReactionAtEnd* command = new AddReactionAtEnd(parent); command->mStep = mStep; command->mReaction = reaction; return command; } +QUndoCommand* StepCommands::addReaction(Reaction* reaction, int index, + QUndoCommand* parent) { + AddReactionAtIndex* command = new AddReactionAtIndex(parent); + command->mStep = mStep; + command->mReaction = reaction; + command->mIndex = index; + return command; +} + QUndoCommand* StepCommands::removeReaction(Reaction* reaction, QUndoCommand* parent) { RemoveReaction* command = new RemoveReaction(parent); Modified: trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.h 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/src/commands/StepCommands.h 2010-03-29 06:46:22 UTC (rev 209) @@ -96,6 +96,20 @@ QUndoCommand* addReaction(Reaction* reaction, QUndoCommand* parent = 0); /** + * Creates a new undoable command to add the given reaction to the step at + * the given index. + * If the command was not executed yet, or it was undone, the reaction is + * deleted when the command is deleted. + * + * @param reaction The reaction to add to the step. + * @param index The index to add the reaction at. + * @param parent The parent QUndoCommand. + * @return The new QUndoCommand. + */ + QUndoCommand* addReaction(Reaction* reaction, int index, + QUndoCommand* parent = 0); + + /** * Creates a new undoable command to remove the given reaction from the * step. * If the command was executed and not undone, the reaction is deleted when Modified: trunk/ktutorial/ktutorial-editor/src/data/Step.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/Step.cpp 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/src/data/Step.cpp 2010-03-29 06:46:22 UTC (rev 209) @@ -69,11 +69,15 @@ } void Step::addReaction(Reaction* reaction) { + addReaction(reaction, mReactions.count()); +} + +void Step::addReaction(Reaction* reaction, int index) { Q_ASSERT(!mReactions.contains(reaction)); - mReactions.append(reaction); + mReactions.insert(index, reaction); - emit reactionAdded(reaction); + emit reactionAdded(reaction, index); } QList<Reaction*> Step::reactions() const { Modified: trunk/ktutorial/ktutorial-editor/src/data/Step.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/Step.h 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/src/data/Step.h 2010-03-29 06:46:22 UTC (rev 209) @@ -30,7 +30,7 @@ * generate the code to create a true KTutorial::Step. * * When any attribute is modified, dataChanged(Step*) signal is emitted. When - * reactions are added or removed, reactionAdded(Reaction*) and + * reactions are added or removed, reactionAdded(Reaction*, int) and * reactionRemoved(Reaction*) are emitted. */ class Step: public QObject { @@ -64,6 +64,17 @@ * @param reaction The reaction to add. */ void addReaction(Reaction* reaction); + + /** + * Adds a new reaction to this Step at the given position. + * The Step gets ownership of the Reaction, so it is deleted when the + * Step is deleted. + * + * @param reaction The reaction to add. + * @param index The index to add the step at. + */ + void addReaction(Reaction* reaction, int index); + QList<Reaction*> reactions() const; /** @@ -87,8 +98,9 @@ * Emitted when the reaction is added to this Step. * * @param reaction The reaction added. + * @param index The index where the reaction was added. */ - void reactionAdded(Reaction* reaction); + void reactionAdded(Reaction* reaction, int index); /** * Emitted when the reaction is removed from this Step. Modified: trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.cpp 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.cpp 2010-03-29 06:46:22 UTC (rev 209) @@ -42,10 +42,10 @@ this, SLOT(update(Step*))); foreach(Reaction* reaction, step->reactions()) { - addReaction(reaction); + addReaction(reaction, mReactionTreeItems.count()); } - connect(step, SIGNAL(reactionAdded(Reaction*)), - this, SLOT(addReaction(Reaction*))); + connect(step, SIGNAL(reactionAdded(Reaction*, int)), + this, SLOT(addReaction(Reaction*, int))); connect(step, SIGNAL(reactionRemoved(Reaction*)), this, SLOT(removeReaction(Reaction*))); } @@ -118,12 +118,14 @@ childIndex++; } + + mReactionTreeItemBaseIndex = childIndex; } -void StepTreeItem::addReaction(Reaction* reaction) { +void StepTreeItem::addReaction(Reaction* reaction, int index) { ReactionTreeItem* reactionTreeItem = new ReactionTreeItem(reaction, this); - appendChild(reactionTreeItem); - mReactionTreeItems.append(reactionTreeItem); + insertChild(reactionTreeItem, mReactionTreeItemBaseIndex + index); + mReactionTreeItems.insert(index, reactionTreeItem); } void StepTreeItem::removeReaction(Reaction* reaction) { Modified: trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.h 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/src/view/StepTreeItem.h 2010-03-29 06:46:22 UTC (rev 209) @@ -127,6 +127,13 @@ QList<ReactionTreeItem*> mReactionTreeItems; /** + * The base index to add new ReactionTreeItems. + * It is the next index to the last optional data item (setup or tear down + * code). + */ + int mReactionTreeItemBaseIndex; + + /** * Returns the ReactionTreeItem for the given Reaction. * * @param reaction The Reaction to get its ReactionTreeItem. @@ -152,8 +159,9 @@ * Adds a new ReactionTreeItem when a Reaction is added in the step. * * @param step The Reaction added in the Step. + * @param index The index where the Reaction was added in the Step. */ - void addReaction(Reaction* reaction); + void addReaction(Reaction* reaction, int index); /** * Removes the ReactionTreeItem for the Reaction removed in the step. Modified: trunk/ktutorial/ktutorial-editor/tests/unit/commands/StepCommandsTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/commands/StepCommandsTest.cpp 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/tests/unit/commands/StepCommandsTest.cpp 2010-03-29 06:46:22 UTC (rev 209) @@ -47,8 +47,12 @@ void testAddReactionRedo(); void testAddReactionUndo(); + void testAddReactionAtIndexRedo(); + void testAddReactionAtIndexUndo(); + void testRemoveReactionRedo(); void testRemoveReactionUndo(); + void testRemoveReactionAtMiddleUndo(); }; @@ -204,6 +208,50 @@ QCOMPARE(step.reactions().count(), 0); } +void StepCommandsTest::testAddReactionAtIndexRedo() { + Step step; + StepCommands commands(&step); + QUndoCommand parent; + Reaction* reaction1 = new Reaction(); + Reaction* reaction2 = new Reaction(); + Reaction* reaction3 = new Reaction(); + + step.addReaction(reaction1); + step.addReaction(reaction3); + + QUndoCommand* command = commands.addReaction(reaction2, 1, &parent); + command->redo(); + + QCOMPARE(command->text(), i18nc("@action", "Add reaction")); + QCOMPARE(parent.child(0), command); + QCOMPARE(step.reactions().count(), 3); + QCOMPARE(step.reactions()[0], reaction1); + QCOMPARE(step.reactions()[1], reaction2); + QCOMPARE(step.reactions()[2], reaction3); +} + +void StepCommandsTest::testAddReactionAtIndexUndo() { + Step step; + StepCommands commands(&step); + QUndoCommand parent; + Reaction* reaction1 = new Reaction(); + Reaction* reaction2 = new Reaction(); + Reaction* reaction3 = new Reaction(); + + step.addReaction(reaction1); + step.addReaction(reaction3); + + QUndoCommand* command = commands.addReaction(reaction2, 1, &parent); + command->redo(); + command->undo(); + + QCOMPARE(command->text(), i18nc("@action", "Add reaction")); + QCOMPARE(parent.child(0), command); + QCOMPARE(step.reactions().count(), 2); + QCOMPARE(step.reactions()[0], reaction1); + QCOMPARE(step.reactions()[1], reaction3); +} + void StepCommandsTest::testRemoveReactionRedo() { Step step; StepCommands commands(&step); @@ -238,6 +286,30 @@ QCOMPARE(step.reactions()[0], reaction); } +void StepCommandsTest::testRemoveReactionAtMiddleUndo() { + Step step; + StepCommands commands(&step); + QUndoCommand parent; + Reaction* reaction1 = new Reaction(); + Reaction* reaction2 = new Reaction(); + Reaction* reaction3 = new Reaction(); + + step.addReaction(reaction1); + step.addReaction(reaction2); + step.addReaction(reaction3); + + QUndoCommand* command = commands.removeReaction(reaction2, &parent); + command->redo(); + command->undo(); + + QCOMPARE(command->text(), i18nc("@action", "Remove reaction")); + QCOMPARE(parent.child(0), command); + QCOMPARE(step.reactions().count(), 3); + QCOMPARE(step.reactions()[0], reaction1); + QCOMPARE(step.reactions()[1], reaction2); + QCOMPARE(step.reactions()[2], reaction3); +} + QTEST_MAIN(StepCommandsTest) #include "StepCommandsTest.moc" Modified: trunk/ktutorial/ktutorial-editor/tests/unit/data/StepTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/StepTest.cpp 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/StepTest.cpp 2010-03-29 06:46:22 UTC (rev 209) @@ -39,14 +39,18 @@ void testSetCustomTearDownCode(); void testAddReaction(); + void testAddReactionAtIndex(); void testRemoveReaction(); private: int mReactionStarType; - void assertReactionSignal(const QSignalSpy& spy, int index, - Reaction* reaction) const; + void assertReactionAddedSignal(const QSignalSpy& spy, int index, + Reaction* reaction, int reactionIndex) const; + void assertReactionRemovedSignal(const QSignalSpy& spy, int index, + Reaction* reaction) const; + }; void StepTest::initTestCase() { @@ -134,7 +138,7 @@ Reaction* reaction2 = new Reaction(); Reaction* reaction3 = new Reaction(); - QSignalSpy reactionAddedSpy(&step, SIGNAL(reactionAdded(Reaction*))); + QSignalSpy reactionAddedSpy(&step, SIGNAL(reactionAdded(Reaction*, int))); step.addReaction(reaction1); step.addReaction(reaction2); @@ -145,11 +149,33 @@ QCOMPARE(step.reactions()[1], reaction2); QCOMPARE(step.reactions()[2], reaction3); QCOMPARE(reactionAddedSpy.count(), 3); - assertReactionSignal(reactionAddedSpy, 0, reaction1); - assertReactionSignal(reactionAddedSpy, 1, reaction2); - assertReactionSignal(reactionAddedSpy, 2, reaction3); + assertReactionAddedSignal(reactionAddedSpy, 0, reaction1, 0); + assertReactionAddedSignal(reactionAddedSpy, 1, reaction2, 1); + assertReactionAddedSignal(reactionAddedSpy, 2, reaction3, 2); } +void StepTest::testAddReactionAtIndex() { + Step step; + Reaction* reaction1 = new Reaction(); + Reaction* reaction2 = new Reaction(); + Reaction* reaction3 = new Reaction(); + + QSignalSpy reactionAddedSpy(&step, SIGNAL(reactionAdded(Reaction*, int))); + + step.addReaction(reaction2, 0); + step.addReaction(reaction1, 0); + step.addReaction(reaction3, 2); + + QCOMPARE(step.reactions().count(), 3); + QCOMPARE(step.reactions()[0], reaction1); + QCOMPARE(step.reactions()[1], reaction2); + QCOMPARE(step.reactions()[2], reaction3); + QCOMPARE(reactionAddedSpy.count(), 3); + assertReactionAddedSignal(reactionAddedSpy, 0, reaction2, 0); + assertReactionAddedSignal(reactionAddedSpy, 1, reaction1, 0); + assertReactionAddedSignal(reactionAddedSpy, 2, reaction3, 2); +} + void StepTest::testRemoveReaction() { Step step; @@ -171,15 +197,15 @@ QCOMPARE(step.reactions()[0], &reaction1); QCOMPARE(step.reactions()[1], &reaction3); QCOMPARE(reactionRemovedSpy.count(), 1); - assertReactionSignal(reactionRemovedSpy, 0, &reaction2); + assertReactionRemovedSignal(reactionRemovedSpy, 0, &reaction2); step.removeReaction(&reaction1); step.removeReaction(&reaction3); QCOMPARE(step.reactions().count(), 0); QCOMPARE(reactionRemovedSpy.count(), 3); - assertReactionSignal(reactionRemovedSpy, 1, &reaction1); - assertReactionSignal(reactionRemovedSpy, 2, &reaction3); + assertReactionRemovedSignal(reactionRemovedSpy, 1, &reaction1); + assertReactionRemovedSignal(reactionRemovedSpy, 2, &reaction3); } /////////////////////////////////// Helpers //////////////////////////////////// @@ -187,8 +213,21 @@ //Reaction* must be declared as a metatype to be used in qvariant_cast Q_DECLARE_METATYPE(Reaction*); -void StepTest::assertReactionSignal(const QSignalSpy& spy, int index, - Reaction* reaction) const { +void StepTest::assertReactionAddedSignal(const QSignalSpy& spy, int index, + Reaction* reaction, + int reactionIndex) const { + QCOMPARE(spy.at(index).count(), 2); + + QVariant argument = spy.at(index).at(0); + QCOMPARE(argument.userType(), mReactionStarType); + QCOMPARE(qvariant_cast<Reaction*>(argument), reaction); + argument = spy.at(index).at(1); + QCOMPARE(argument.type(), QVariant::Int); + QCOMPARE(argument.toInt(), reactionIndex); +} + +void StepTest::assertReactionRemovedSignal(const QSignalSpy& spy, int index, + Reaction* reaction) const { QCOMPARE(spy.at(index).count(), 1); QVariant argument = spy.at(index).at(0); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/StepTreeItemTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/StepTreeItemTest.cpp 2010-03-29 05:56:07 UTC (rev 208) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/StepTreeItemTest.cpp 2010-03-29 06:46:22 UTC (rev 209) @@ -53,6 +53,7 @@ void testStepSetCustomTearDownCodeEmpty(); void testStepAddReaction(); + void testStepAddReactionAtIndex(); void testStepRemoveReaction(); @@ -319,6 +320,26 @@ assertReaction(item.child(1), reaction); } +void StepTreeItemTest::testStepAddReactionAtIndex() { + Step step; + StepTreeItem item(&step); + + Reaction* reaction2 = new Reaction(); + step.addReaction(reaction2, 0); + + Reaction* reaction1 = new Reaction(); + step.addReaction(reaction1, 0); + + Reaction* reaction3 = new Reaction(); + step.addReaction(reaction3, 2); + + QCOMPARE(item.childCount(), 4); + assertEmptyText(item.child(0)); + assertReaction(item.child(1), reaction1); + assertReaction(item.child(2), reaction2); + assertReaction(item.child(3), reaction3); +} + void StepTreeItemTest::testStepRemoveReaction() { Step step; StepTreeItem item(&step); @@ -380,15 +401,28 @@ assertReaction(item.child(3), reaction1); assertReaction(item.child(4), reaction2); + Reaction* reaction3 = new Reaction(); + step.addReaction(reaction3, 1); + + QCOMPARE(item.text(), i18nc("@item", "Step (id not set)")); + QCOMPARE(item.childCount(), 6); + assertText(item.child(0), "The text"); + assertCustomSetupCode(item.child(1), "The setup code"); + assertCustomTearDownCode(item.child(2), "The tear down code"); + assertReaction(item.child(3), reaction1); + assertReaction(item.child(4), reaction3); + assertReaction(item.child(5), reaction2); + step.setId("The id"); QCOMPARE(item.text(), i18nc("@item", "Step %1", "The id")); - QCOMPARE(item.childCount(), 5); + QCOMPARE(item.childCount(), 6); assertText(item.child(0), "The text"); assertCustomSetupCode(item.child(1), "The setup code"); assertCustomTearDownCode(item.child(2), "The tear down code"); assertReaction(item.child(3), reaction1); - assertReaction(item.child(4), reaction2); + assertReaction(item.child(4), reaction3); + assertReaction(item.child(5), reaction2); } void StepTreeItemTest::testChildOrderWhenUnsettingDataInStep() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-03-30 02:32:39
|
Revision: 216 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=216&view=rev Author: danxuliu Date: 2010-03-30 02:32:32 +0000 (Tue, 30 Mar 2010) Log Message: ----------- Ensure that IOExceptions are thrown when loading, saving and exporting to a directory instead of a file. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp 2010-03-29 23:58:59 UTC (rev 215) +++ trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp 2010-03-30 02:32:32 UTC (rev 216) @@ -33,6 +33,11 @@ throw (DeserializationException, IOException) { Q_ASSERT(url.isValid()); + if (url.fileName(KUrl::ObeyTrailingSlash).isEmpty()) { + throw IOException(i18n("A file was expected, but '%1' is a folder", + url.prettyUrl())); + } + QString temporaryFileName; if (!KIO::NetAccess::download(url, temporaryFileName, 0)) { throw IOException(KIO::NetAccess::lastErrorString()); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp 2010-03-29 23:58:59 UTC (rev 215) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp 2010-03-30 02:32:32 UTC (rev 216) @@ -50,12 +50,14 @@ void testSaveAndLoad(); void testLoadFromUnreadableUrl(); + void testLoadFromDirectory(); void testLoadNotAnXmlFile(); void testLoadXmlNotWellFormed(); void testLoadXmlWithoutRootTutorialElement(); void testSaveToExistingUrl(); void testSaveToUnwritableUrl(); + void testSaveToDirectory(); void testAvailableExporterTypes(); @@ -63,6 +65,7 @@ void testExportToExistingUrl(); void testExportToUnwritableUrl(); + void testExportToDirectory(); private: @@ -109,6 +112,12 @@ EXPECT_EXCEPTION(Serialization::loadTutorial(url), IOException); } +void SerializationTest::testLoadFromDirectory() { + KUrl url = KGlobal::dirs()->saveLocation("tmp"); + + EXPECT_EXCEPTION(Serialization::loadTutorial(url), IOException); +} + void SerializationTest::testLoadNotAnXmlFile() { KUrl url = KGlobal::dirs()->saveLocation("tmp") + "/SerializationTest.txt"; mFile = new QFile(url.toLocalFile()); @@ -178,6 +187,16 @@ EXPECT_EXCEPTION(Serialization::saveTutorial(&tutorial, url), IOException); } +void SerializationTest::testSaveToDirectory() { + Tutorial tutorial; + tutorial.setName("The name"); + tutorial.setDescription("The description"); + + KUrl url = KGlobal::dirs()->saveLocation("tmp"); + + EXPECT_EXCEPTION(Serialization::saveTutorial(&tutorial, url), IOException); +} + void SerializationTest::testAvailableExporterTypes() { QString types = Serialization::availableExporterTypes(); @@ -252,6 +271,17 @@ IOException); } +void SerializationTest::testExportToDirectory() { + Tutorial tutorial; + tutorial.setName("The name"); + tutorial.setDescription("The description"); + + KUrl url = KGlobal::dirs()->saveLocation("tmp"); + + EXPECT_EXCEPTION(Serialization::exportTutorial(&tutorial, "*.js", url), + IOException); +} + /////////////////////////////////// Helpers //////////////////////////////////// QString SerializationTest::readFile(QFile* file) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-03-30 03:16:13
|
Revision: 217 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=217&view=rev Author: danxuliu Date: 2010-03-30 03:16:07 +0000 (Tue, 30 Mar 2010) Log Message: ----------- Provide the main window to Serialization to be used in KIO::NetAccess operations. Serialization methods were changed from class methods to instance methods. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.h trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp 2010-03-30 02:32:32 UTC (rev 216) +++ trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp 2010-03-30 03:16:07 UTC (rev 217) @@ -76,7 +76,7 @@ void KTutorialEditor::loadTutorialFromUrl(const KUrl& url) { Tutorial* tutorial; try { - tutorial = Serialization::loadTutorial(url); + tutorial = Serialization(this).loadTutorial(url); } catch (IOException e) { QString text = i18nc("@label", "There was a problem when trying to " "open the file:<nl/>%1", e.message()); @@ -452,7 +452,7 @@ } try { - Serialization::saveTutorial(mTutorial, mTutorialUrl); + Serialization(this).saveTutorial(mTutorial, mTutorialUrl); } catch (IOException e) { QString text = i18nc("@label", "There was a problem when trying to " "save the tutorial:<nl/>%1", e.message()); @@ -483,7 +483,7 @@ } try { - Serialization::saveTutorial(mTutorial, dialog->selectedUrl()); + Serialization(this).saveTutorial(mTutorial, dialog->selectedUrl()); } catch (IOException e) { QString text = i18nc("@label", "There was a problem when trying to " "save the tutorial:<nl/>%1", e.message()); @@ -509,7 +509,7 @@ dialog->setCaption(i18nc("@title", "Export Tutorial")); dialog->setOperationMode(KFileDialog::Saving); dialog->setConfirmOverwrite(true); - dialog->setFilter(Serialization::availableExporterTypes()); + dialog->setFilter(Serialization(this).availableExporterTypes()); dialog->filterWidget()->setEditable(false); if (dialog->exec() == QDialog::Rejected) { @@ -517,8 +517,8 @@ } try { - Serialization::exportTutorial(mTutorial, dialog->currentFilter(), - dialog->selectedUrl()); + Serialization(this).exportTutorial(mTutorial, dialog->currentFilter(), + dialog->selectedUrl()); } catch (IOException e) { QString text = i18nc("@label", "There was a problem when trying to " "save the exported tutorial:<nl/>%1", e.message()); Modified: trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp 2010-03-30 02:32:32 UTC (rev 216) +++ trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.cpp 2010-03-30 03:16:07 UTC (rev 217) @@ -29,6 +29,10 @@ //public: +Serialization::Serialization(QWidget* window): + mWindow(window) { +} + Tutorial* Serialization::loadTutorial(const KUrl& url) throw (DeserializationException, IOException) { Q_ASSERT(url.isValid()); @@ -39,7 +43,7 @@ } QString temporaryFileName; - if (!KIO::NetAccess::download(url, temporaryFileName, 0)) { + if (!KIO::NetAccess::download(url, temporaryFileName, mWindow)) { throw IOException(KIO::NetAccess::lastErrorString()); } @@ -111,7 +115,7 @@ out.flush(); temporaryFile.close(); - if (!KIO::NetAccess::upload(temporaryFile.fileName(), url, 0)) { + if (!KIO::NetAccess::upload(temporaryFile.fileName(), url, mWindow)) { throw IOException(KIO::NetAccess::lastErrorString()); } } Modified: trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.h 2010-03-30 02:32:32 UTC (rev 216) +++ trunk/ktutorial/ktutorial-editor/src/serialization/Serialization.h 2010-03-30 03:16:07 UTC (rev 217) @@ -25,6 +25,7 @@ #include "IOException.h" class KUrl; +class QWidget; class Tutorial; /** @@ -37,6 +38,17 @@ public: /** + * Creates a new Serialization. + * The given window will be used as the parent window if any dialog is shown + * to request some information (for example, the user password when saving a + * file in a remote computer). It will be also used to automatically cache + * and discard that information. + * + * @param window The window associated with the input/ouput jobs, if any. + */ + explicit Serialization(QWidget* window = 0); + + /** * Loads a tutorial from the file specified by the given url. * The file must be a XML file that validates against the W3C Schema in * Tutorial.xsd. The url can be local or remote. @@ -49,8 +61,8 @@ * file. * @see TutorialReader */ - static Tutorial* loadTutorial(const KUrl& url) - throw (DeserializationException, IOException); + Tutorial* loadTutorial(const KUrl& url) throw (DeserializationException, + IOException); /** * Saves the tutorial to the file specified by the given url. @@ -63,8 +75,7 @@ * file. * @see TutorialWriter */ - static void saveTutorial(Tutorial* tutorial, const KUrl& url) - throw (IOException); + void saveTutorial(Tutorial* tutorial, const KUrl& url) throw (IOException); /** * Returns the available exporter types. @@ -75,7 +86,7 @@ * * @return The available exporter types. */ - static QString availableExporterTypes(); + QString availableExporterTypes(); /** * Exports the tutorial to the file specified by the url using the given @@ -91,17 +102,19 @@ * @throw IOException If there was a problem writing the contents to the * file. */ - static void exportTutorial(const Tutorial* tutorial, const QString& type, - const KUrl& url) throw (IOException); + void exportTutorial(const Tutorial* tutorial, const QString& type, + const KUrl& url) throw (IOException); private: + QWidget* mWindow; + /** * Returns a list of strings with the available exporter types. * * @return A list with the available exporter types. */ - static QStringList availableExporterTypeList(); + QStringList availableExporterTypeList(); /** * Writes the data to the file specified by the given url. @@ -112,8 +125,7 @@ * @throw IOException If there was a problem writing the contents to the * file. */ - static void writeFile(const QString& data, const KUrl& url) - throw (IOException); + void writeFile(const QString& data, const KUrl& url) throw (IOException); }; Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp 2010-03-30 02:32:32 UTC (rev 216) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/SerializationTest.cpp 2010-03-30 03:16:07 UTC (rev 217) @@ -94,8 +94,8 @@ KUrl url = KGlobal::dirs()->saveLocation("tmp") + "/SerializationTest.xml"; - Serialization::saveTutorial(&tutorial, url); - QScopedPointer<Tutorial> loadedTutorial(Serialization::loadTutorial(url)); + Serialization().saveTutorial(&tutorial, url); + QScopedPointer<Tutorial> loadedTutorial(Serialization().loadTutorial(url)); QVERIFY(loadedTutorial); QCOMPARE(loadedTutorial->name(), tutorial.name()); @@ -109,13 +109,13 @@ writeFile(mFile, "<tutorial name=\"The name\"></tutorial>"); QVERIFY(QFile::setPermissions(mFile->fileName(), 0)); - EXPECT_EXCEPTION(Serialization::loadTutorial(url), IOException); + EXPECT_EXCEPTION(Serialization().loadTutorial(url), IOException); } void SerializationTest::testLoadFromDirectory() { KUrl url = KGlobal::dirs()->saveLocation("tmp"); - EXPECT_EXCEPTION(Serialization::loadTutorial(url), IOException); + EXPECT_EXCEPTION(Serialization().loadTutorial(url), IOException); } void SerializationTest::testLoadNotAnXmlFile() { @@ -124,7 +124,7 @@ mFile->open(QIODevice::WriteOnly | QIODevice::Text); writeFile(mFile, "Not an XML file"); - EXPECT_EXCEPTION(Serialization::loadTutorial(url), + EXPECT_EXCEPTION(Serialization().loadTutorial(url), DeserializationException); } @@ -134,7 +134,7 @@ mFile->open(QIODevice::WriteOnly | QIODevice::Text); writeFile(mFile, "<tutorial><step></invalidEndElement></tutorial>"); - EXPECT_EXCEPTION(Serialization::loadTutorial(url), + EXPECT_EXCEPTION(Serialization().loadTutorial(url), DeserializationException); } @@ -144,7 +144,7 @@ mFile->open(QIODevice::WriteOnly | QIODevice::Text); writeFile(mFile, "<unknownRootElement></unknownRootElement>"); - EXPECT_EXCEPTION(Serialization::loadTutorial(url), + EXPECT_EXCEPTION(Serialization().loadTutorial(url), DeserializationException); } @@ -158,7 +158,7 @@ mFile->open(QIODevice::WriteOnly | QIODevice::Text); writeFile(mFile, "Hello world!"); - Serialization::saveTutorial(&tutorial, url); + Serialization().saveTutorial(&tutorial, url); QVERIFY(mFile->exists()); QVERIFY(mFile->open(QIODevice::ReadOnly | QIODevice::Text)); @@ -184,7 +184,7 @@ writeFile(mFile, "Hello world!"); QVERIFY(QFile::setPermissions(mFile->fileName(), 0)); - EXPECT_EXCEPTION(Serialization::saveTutorial(&tutorial, url), IOException); + EXPECT_EXCEPTION(Serialization().saveTutorial(&tutorial, url), IOException); } void SerializationTest::testSaveToDirectory() { @@ -194,11 +194,11 @@ KUrl url = KGlobal::dirs()->saveLocation("tmp"); - EXPECT_EXCEPTION(Serialization::saveTutorial(&tutorial, url), IOException); + EXPECT_EXCEPTION(Serialization().saveTutorial(&tutorial, url), IOException); } void SerializationTest::testAvailableExporterTypes() { - QString types = Serialization::availableExporterTypes(); + QString types = Serialization().availableExporterTypes(); QCOMPARE(types, i18nc("@item:combobox A KFileDialog filter", "*.js|Javascript file")); @@ -211,7 +211,7 @@ KUrl url = KGlobal::dirs()->saveLocation("tmp") + "/SerializationTest.js"; - Serialization::exportTutorial(&tutorial, "*.js", url); + Serialization().exportTutorial(&tutorial, "*.js", url); mFile = new QFile(url.toLocalFile()); QVERIFY(mFile->exists()); @@ -239,7 +239,7 @@ mFile->open(QIODevice::WriteOnly | QIODevice::Text); writeFile(mFile, "Hello world!"); - Serialization::exportTutorial(&tutorial, "*.js", url); + Serialization().exportTutorial(&tutorial, "*.js", url); QVERIFY(mFile->exists()); QVERIFY(mFile->open(QIODevice::ReadOnly | QIODevice::Text)); @@ -267,7 +267,7 @@ writeFile(mFile, "Hello world!"); QVERIFY(QFile::setPermissions(mFile->fileName(), 0)); - EXPECT_EXCEPTION(Serialization::exportTutorial(&tutorial, "*.js", url), + EXPECT_EXCEPTION(Serialization().exportTutorial(&tutorial, "*.js", url), IOException); } @@ -278,7 +278,7 @@ KUrl url = KGlobal::dirs()->saveLocation("tmp"); - EXPECT_EXCEPTION(Serialization::exportTutorial(&tutorial, "*.js", url), + EXPECT_EXCEPTION(Serialization().exportTutorial(&tutorial, "*.js", url), IOException); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-03-30 19:50:30
|
Revision: 221 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=221&view=rev Author: danxuliu Date: 2010-03-30 19:50:23 +0000 (Tue, 30 Mar 2010) Log Message: ----------- Add AutoExpandableTreeView that expands the items when they are added or modified in the model Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.ui trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.cpp trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.h trunk/ktutorial/ktutorial-editor/tests/unit/view/AutoExpandableTreeViewTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp 2010-03-30 07:25:48 UTC (rev 220) +++ trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp 2010-03-30 19:50:23 UTC (rev 221) @@ -39,6 +39,7 @@ #include "data/Tutorial.h" #include "serialization/Serialization.h" #include "view/ActionListWidget.h" +#include "view/AutoExpandableTreeView.h" #include "view/EditionDialog.h" #include "view/LicenseWidget.h" #include "view/ReactionWidget.h" @@ -57,7 +58,7 @@ mCurrentStep(0), mCurrentReaction(0) { - mTreeView = new QTreeView(); + mTreeView = new AutoExpandableTreeView(); mTreeView->setObjectName("centralTreeView"); setCentralWidget(mTreeView); Added: trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.cpp 2010-03-30 19:50:23 UTC (rev 221) @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "AutoExpandableTreeView.h" + +//public: + +AutoExpandableTreeView::AutoExpandableTreeView(QWidget* parent): + QTreeView(parent) { +} + +//protected: + +void AutoExpandableTreeView::dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) { + QTreeView::dataChanged(topLeft, bottomRight); + + expandParents(topLeft); +} + +void AutoExpandableTreeView::rowsInserted(const QModelIndex& parent, int start, + int end) { + QTreeView::rowsInserted(parent, start, end); + + expand(parent); + expandParents(parent); + + for (int i=start; i<=end; ++i) { + expandRecursive(model()->index(i, 0, parent)); + } +} + +//private: + +void AutoExpandableTreeView::expandParents(const QModelIndex& index) { + QModelIndex parent = index.parent(); + while (parent.isValid()) { + expand(parent); + parent = parent.parent(); + } +} + +void AutoExpandableTreeView::expandRecursive(const QModelIndex& index) { + expand(index); + + for (int i=0; i<model()->rowCount(index); ++i) { + expandRecursive(model()->index(i, 0, index)); + } +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.h 2010-03-30 19:50:23 UTC (rev 221) @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef AUTOEXPANDABLETREEVIEW_H +#define AUTOEXPANDABLETREEVIEW_H + +#include <QTreeView> + +/** + * Special QTreeView that expands its item when are inserted or changed. + * When an item is inserted, that item, all its children and all its parents are + * expanded. + * When an item is changed, the parents to that item are expanded. Note that + * neither the item nor its children are expanded. + * + * Siblings of parent items are not expanded in any case. Only the parent + * leading to the item to be expanded are expanded. + */ +class AutoExpandableTreeView: public QTreeView { +Q_OBJECT +public: + + /** + * Creates a new AutoExpandableTreeView with the given parent. + * + * @param parent The parent widget. + */ + AutoExpandableTreeView(QWidget* parent = 0); + +protected: + + /** + * This slot is called when items are changed in the model. + * After executing QTreeView default behavior, all the parents of the + * changed items are expanded. All the items should belong to the same + * parent. + * + * Reimplemented from QTreeView::dataChanged(QModelIndex, QModelIndex). + * + * @param topLeft The top left item changed, inclusive. + * @param bottomRight The bottom right item changed, inclusiev. + */ + virtual void dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + + /** + * This slot is called when rows are inserted. + * After executing QTreeView default behavior, all the items inserted, all + * their children and their parent are expanded. + * + * Reimplemented from QTreeView::rowsInserted(QModelIndex, int, int). + * + * @param parent The parent index where the children were added. + * @param start The first row inserted, inclusive. + * @param end The last row inserted, inclusive. + */ + virtual void rowsInserted(const QModelIndex& parent, int start, int end); + +private: + + /** + * Expands all the parents of the given index. + * + * @param index The index to expand its parents. + */ + void expandParents(const QModelIndex& index); + + /** + * Expands an index and all its children. + * + * @param index The index to expand. + */ + void expandRecursive(const QModelIndex& index); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/AutoExpandableTreeView.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-03-30 07:25:48 UTC (rev 220) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-03-30 19:50:23 UTC (rev 221) @@ -1,5 +1,6 @@ set(ktutorial_editor_view_SRCS ActionListWidget.cpp + AutoExpandableTreeView.cpp CommandWidget.cpp EditionDialog.cpp EditionWidget.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.ui 2010-03-30 07:25:48 UTC (rev 220) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.ui 2010-03-30 19:50:23 UTC (rev 221) @@ -19,7 +19,7 @@ </property> <layout class="QVBoxLayout" name="waitForWidgetVerticalLayout"> <item> - <widget class="QTreeView" name="waitForTreeView"/> + <widget class="AutoExpandableTreeView" name="waitForTreeView"/> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout"> @@ -74,6 +74,11 @@ <extends>QPushButton</extends> <header>kpushbutton.h</header> </customwidget> + <customwidget> + <class>AutoExpandableTreeView</class> + <extends>QTreeView</extends> + <header>AutoExpandableTreeView.h</header> + </customwidget> </customwidgets> <resources/> <connections/> Added: trunk/ktutorial/ktutorial-editor/tests/unit/view/AutoExpandableTreeViewTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/AutoExpandableTreeViewTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/AutoExpandableTreeViewTest.cpp 2010-03-30 19:50:23 UTC (rev 221) @@ -0,0 +1,223 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "AutoExpandableTreeView.h" + +#include "TextTreeItem.h" +#include "TreeModel.h" + +class AutoExpandableTreeViewTest: public QObject { +Q_OBJECT + +private slots: + + void testConstructor(); + + void testInsertRootItemChild(); + void testInsertDeepNestedChildParentExpanded(); + void testInsertDeepNestedChildParentNotExpanded(); + + void testChangeDeepNestedChild(); + +}; + +void AutoExpandableTreeViewTest::testConstructor() { + QWidget parent; + AutoExpandableTreeView* view = new AutoExpandableTreeView(&parent); + + QCOMPARE(view->parentWidget(), &parent); +} + +void AutoExpandableTreeViewTest::testInsertRootItemChild() { + AutoExpandableTreeView view; + TextTreeItem* rootItem = new TextTreeItem(); + TreeModel model(rootItem); + view.setModel(&model); + + TextTreeItem* item1 = new TextTreeItem(rootItem); + TextTreeItem* item1_1 = new TextTreeItem(item1); + TextTreeItem* item1_1_1 = new TextTreeItem(item1_1); + item1_1->appendChild(item1_1_1); + item1->appendChild(item1_1); + TextTreeItem* item1_2 = new TextTreeItem(item1); + TextTreeItem* item1_2_1 = new TextTreeItem(item1_2); + item1_2->appendChild(item1_2_1); + TextTreeItem* item1_2_2 = new TextTreeItem(item1_2); + TextTreeItem* item1_2_2_1 = new TextTreeItem(item1_2_2); + item1_2_2->appendChild(item1_2_2_1); + TextTreeItem* item1_2_2_2 = new TextTreeItem(item1_2_2); + item1_2_2->appendChild(item1_2_2_2); + item1_2->appendChild(item1_2_2); + item1->appendChild(item1_2); + + rootItem->appendChild(item1); + + QModelIndex index1 = model.index(0, 0); + QVERIFY(view.isExpanded(index1)); + QModelIndex index1_1 = model.index(0, 0, index1); + QVERIFY(view.isExpanded(index1_1)); + QModelIndex index1_2 = model.index(1, 0, index1); + QVERIFY(view.isExpanded(index1_2)); + QModelIndex index1_2_2 = model.index(1, 0, index1_2); + QVERIFY(view.isExpanded(index1_2_2)); +} + +void AutoExpandableTreeViewTest::testInsertDeepNestedChildParentExpanded() { + AutoExpandableTreeView view; + TextTreeItem* rootItem = new TextTreeItem(); + TreeModel model(rootItem); + view.setModel(&model); + + TextTreeItem* item1 = new TextTreeItem(rootItem); + TextTreeItem* item1_1 = new TextTreeItem(item1); + TextTreeItem* item1_1_1 = new TextTreeItem(item1_1); + item1_1->appendChild(item1_1_1); + item1->appendChild(item1_1); + TextTreeItem* item1_2 = new TextTreeItem(item1); + TextTreeItem* item1_2_1 = new TextTreeItem(item1_2); + item1_2->appendChild(item1_2_1); + item1->appendChild(item1_2); + rootItem->appendChild(item1); + + QModelIndex index1 = model.index(0, 0); + view.setExpanded(index1, true); + //Collapse sibling of parent to check that it is not expanded again + QModelIndex index1_1 = model.index(0, 0, index1); + view.setExpanded(index1_1, false); + QModelIndex index1_2 = model.index(1, 0, index1); + view.setExpanded(index1_2, true); + + TextTreeItem* item1_2_2 = new TextTreeItem(item1_2); + TextTreeItem* item1_2_2_1 = new TextTreeItem(item1_2_2); + TextTreeItem* item1_2_2_1_1 = new TextTreeItem(item1_2_2_1); + item1_2_2_1->appendChild(item1_2_2_1_1); + TextTreeItem* item1_2_2_1_2 = new TextTreeItem(item1_2_2_1); + item1_2_2_1->appendChild(item1_2_2_1_2); + item1_2_2->appendChild(item1_2_2_1); + TextTreeItem* item1_2_2_2 = new TextTreeItem(item1_2_2); + item1_2_2->appendChild(item1_2_2_2); + item1_2->appendChild(item1_2_2); + + QVERIFY(view.isExpanded(index1)); + QVERIFY(!view.isExpanded(index1_1)); + QVERIFY(view.isExpanded(index1_2)); + QModelIndex index1_2_2 = model.index(1, 0, index1_2); + QVERIFY(view.isExpanded(index1_2_2)); + QModelIndex index1_2_2_1 = model.index(0, 0, index1_2_2); + QVERIFY(view.isExpanded(index1_2_2_1)); +} + +void AutoExpandableTreeViewTest::testInsertDeepNestedChildParentNotExpanded() { + AutoExpandableTreeView view; + TextTreeItem* rootItem = new TextTreeItem(); + TreeModel model(rootItem); + view.setModel(&model); + + TextTreeItem* item1 = new TextTreeItem(rootItem); + TextTreeItem* item1_1 = new TextTreeItem(item1); + TextTreeItem* item1_1_1 = new TextTreeItem(item1_1); + item1_1->appendChild(item1_1_1); + item1->appendChild(item1_1); + TextTreeItem* item1_2 = new TextTreeItem(item1); + TextTreeItem* item1_2_1 = new TextTreeItem(item1_2); + item1_2->appendChild(item1_2_1); + item1->appendChild(item1_2); + rootItem->appendChild(item1); + + QModelIndex index1 = model.index(0, 0); + view.setExpanded(index1, false); + //Collapse sibling of parent to check that it is not expanded again + QModelIndex index1_1 = model.index(0, 0, index1); + view.setExpanded(index1_1, false); + QModelIndex index1_2 = model.index(1, 0, index1); + view.setExpanded(index1_2, false); + + TextTreeItem* item1_2_2 = new TextTreeItem(item1_2); + TextTreeItem* item1_2_2_1 = new TextTreeItem(item1_2_2); + TextTreeItem* item1_2_2_1_1 = new TextTreeItem(item1_2_2_1); + item1_2_2_1->appendChild(item1_2_2_1_1); + TextTreeItem* item1_2_2_1_2 = new TextTreeItem(item1_2_2_1); + item1_2_2_1->appendChild(item1_2_2_1_2); + item1_2_2->appendChild(item1_2_2_1); + TextTreeItem* item1_2_2_2 = new TextTreeItem(item1_2_2); + item1_2_2->appendChild(item1_2_2_2); + + item1_2->appendChild(item1_2_2); + + QVERIFY(view.isExpanded(index1)); + QVERIFY(!view.isExpanded(index1_1)); + QVERIFY(view.isExpanded(index1_2)); + QModelIndex index1_2_2 = model.index(1, 0, index1_2); + QVERIFY(view.isExpanded(index1_2_2)); + QModelIndex index1_2_2_1 = model.index(0, 0, index1_2_2); + QVERIFY(view.isExpanded(index1_2_2_1)); +} + +void AutoExpandableTreeViewTest::testChangeDeepNestedChild() { + AutoExpandableTreeView view; + TextTreeItem* rootItem = new TextTreeItem(); + TreeModel model(rootItem); + view.setModel(&model); + + TextTreeItem* item1 = new TextTreeItem(rootItem); + TextTreeItem* item1_1 = new TextTreeItem(item1); + TextTreeItem* item1_1_1 = new TextTreeItem(item1_1); + item1_1->appendChild(item1_1_1); + item1->appendChild(item1_1); + TextTreeItem* item1_2 = new TextTreeItem(item1); + TextTreeItem* item1_2_1 = new TextTreeItem(item1_2); + item1_2->appendChild(item1_2_1); + TextTreeItem* item1_2_2 = new TextTreeItem(item1_2); + TextTreeItem* item1_2_2_1 = new TextTreeItem(item1_2_2); + TextTreeItem* item1_2_2_1_1 = new TextTreeItem(item1_2_2_1); + item1_2_2_1->appendChild(item1_2_2_1_1); + TextTreeItem* item1_2_2_1_2 = new TextTreeItem(item1_2_2_1); + item1_2_2_1->appendChild(item1_2_2_1_2); + item1_2_2->appendChild(item1_2_2_1); + TextTreeItem* item1_2_2_2 = new TextTreeItem(item1_2_2); + item1_2_2->appendChild(item1_2_2_2); + item1_2->appendChild(item1_2_2); + item1->appendChild(item1_2); + rootItem->appendChild(item1); + + QModelIndex index1 = model.index(0, 0); + view.setExpanded(index1, false); + //Collapse sibling of parent to check that it is not expanded again + QModelIndex index1_1 = model.index(0, 0, index1); + view.setExpanded(index1_1, false); + QModelIndex index1_2 = model.index(1, 0, index1); + view.setExpanded(index1_2, false); + QModelIndex index1_2_2 = model.index(1, 0, index1_2); + view.setExpanded(index1_2_2, false); + QModelIndex index1_2_2_1 = model.index(0, 0, index1_2_2); + view.setExpanded(index1_2_2_1, false); + + item1_2_2_1->setText("New text"); + + QVERIFY(view.isExpanded(index1)); + QVERIFY(!view.isExpanded(index1_1)); + QVERIFY(view.isExpanded(index1_2)); + QVERIFY(view.isExpanded(index1_2_2)); + QVERIFY(!view.isExpanded(index1_2_2_1)); +} + +QTEST_MAIN(AutoExpandableTreeViewTest) + +#include "AutoExpandableTreeViewTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/view/AutoExpandableTreeViewTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2010-03-30 07:25:48 UTC (rev 220) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2010-03-30 19:50:23 UTC (rev 221) @@ -18,6 +18,7 @@ unit_tests( ActionListWidget + AutoExpandableTreeView CommandWidget EditionDialog LicenseWidget @@ -53,6 +54,7 @@ mem_tests( ActionListWidget + AutoExpandableTreeView CommandWidget EditionDialog LicenseWidget This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-03-31 16:28:01
|
Revision: 226 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=226&view=rev Author: danxuliu Date: 2010-03-31 16:27:52 +0000 (Wed, 31 Mar 2010) Log Message: ----------- Fix compiler warnings. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/data/Reaction.cpp trunk/ktutorial/ktutorial-editor/tests/unit/data/ReactionTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/data/Reaction.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/Reaction.cpp 2010-03-31 16:21:39 UTC (rev 225) +++ trunk/ktutorial/ktutorial-editor/src/data/Reaction.cpp 2010-03-31 16:27:52 UTC (rev 226) @@ -23,8 +23,8 @@ Reaction::Reaction(QObject* parent): QObject(parent), + mTriggerType(OptionSelected), mWaitFor(0), - mTriggerType(OptionSelected), mResponseType(NextStep) { } Modified: trunk/ktutorial/ktutorial-editor/tests/unit/data/ReactionTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/ReactionTest.cpp 2010-03-31 16:21:39 UTC (rev 225) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/ReactionTest.cpp 2010-03-31 16:27:52 UTC (rev 226) @@ -60,6 +60,7 @@ } bool equals(const WaitFor& waitFor) const { + Q_UNUSED(waitFor); return false; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-04-13 20:11:13
|
Revision: 230 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=230&view=rev Author: danxuliu Date: 2010-04-13 20:11:06 +0000 (Tue, 13 Apr 2010) Log Message: ----------- Add initial version of target application module to interact with the KTutorial editor support module in applications using KTutorial. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/targetapplication/ trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.h trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DBusExceptionTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DummyApplication.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEventSpyTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationStub.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt 2010-04-13 18:49:12 UTC (rev 229) +++ trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt 2010-04-13 20:11:06 UTC (rev 230) @@ -9,6 +9,11 @@ add_subdirectory(serialization) add_subdirectory(view) +if (QT_QTDBUS_FOUND) + add_definitions(-DQT_QTDBUS_FOUND) + add_subdirectory(targetapplication) +endif (QT_QTDBUS_FOUND) + set(ktutorial_editor_SRCS EditActions.cpp Exception.cpp @@ -28,6 +33,11 @@ ktutorial_editor_view ) +if (QT_QTDBUS_FOUND) + target_link_libraries(ktutorial_editor ktutorial_editor_targetapplication) +endif (QT_QTDBUS_FOUND) + + kde4_add_executable(ktutorial-editor main.cpp) target_link_libraries(ktutorial-editor ktutorial_editor) Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,12 @@ +set(ktutorial_editor_targetapplication_SRCS + DBusException.cpp + RemoteEditorSupport.cpp + RemoteEventSpy.cpp + RemoteObject.cpp + RemoteObjectMapper.cpp + TargetApplication.cpp +) + +kde4_add_library(ktutorial_editor_targetapplication ${ktutorial_editor_targetapplication_SRCS}) + +target_link_libraries(ktutorial_editor_targetapplication ktutorial_editor ${QT_QTDBUS_LIBRARY} ${KDE4_KDECORE_LIBS}) Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "DBusException.h" + +//public: + +DBusException::DBusException(const QString& message): Exception(message) { +} + +DBusException::~DBusException() throw() { +} Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef DBUSEXCEPTION_H +#define DBUSEXCEPTION_H + +#include "../Exception.h" + +/** + * Thrown when a DBus error happened. + */ +class DBusException: public Exception { +public: + + explicit DBusException(const QString& message = QString()); + virtual ~DBusException() throw(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteEditorSupport.h" + +#include <QDBusReply> + +#include "RemoteEventSpy.h" +#include "RemoteObject.h" +#include "RemoteObjectMapper.h" + +//public: + +RemoteEditorSupport::RemoteEditorSupport(const QString& service, + RemoteObjectMapper* mapper): + QDBusAbstractInterface(service, "/ktutorial", + "org.kde.ktutorial.EditorSupport", + QDBusConnection::sessionBus(), 0), + mMapper(mapper), + mRemoteEventSpy(0) { +} + +RemoteEditorSupport::~RemoteEditorSupport() { + delete mRemoteEventSpy; +} + +RemoteObject* RemoteEditorSupport::mainWindow() throw (DBusException) { + QDBusReply<int> reply = call("mainWindowObjectId"); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + return mMapper->remoteObject(reply.value()); +} + +void RemoteEditorSupport::highlight(RemoteObject* remoteWidget) + throw (DBusException) { + QDBusReply<void> reply = call("highlight", remoteWidget->objectId()); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } +} + +void RemoteEditorSupport::stopHighlighting(RemoteObject* remoteWidget) + throw (DBusException) { + QDBusReply<void> reply = call("stopHighlighting", remoteWidget->objectId()); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } +} + +RemoteEventSpy* RemoteEditorSupport::enableEventSpy() throw (DBusException) { + if (mRemoteEventSpy) { + return mRemoteEventSpy; + } + + QDBusReply<void> reply = call("enableEventSpy"); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + mRemoteEventSpy = new RemoteEventSpy(service(), mMapper); + return mRemoteEventSpy; +} + +void RemoteEditorSupport::disableEventSpy() throw (DBusException) { + if (!mRemoteEventSpy) { + return; + } + + QDBusReply<void> reply = call("disableEventSpy"); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + delete mRemoteEventSpy; + mRemoteEventSpy = 0; +} Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEEDITORSUPPORT_H +#define REMOTEEDITORSUPPORT_H + +#include <QDBusAbstractInterface> + +#include "DBusException.h" + +class RemoteEventSpy; +class RemoteObject; +class RemoteObjectMapper; + +/** + * Proxy for the remote EditorSupport exposed by KTutorial editor support module + * in KTutorial library. + * RemoteEditorSupport represents a remote EditorSupport exposed through DBus. + * Its purpose is provide an API to use the remote EditorSupport like any other + * local object, hiding the DBus complexity behind it. + * + * RemoteEditorSupport makes DBus calls to "org.kde.ktutorial.EditorSupport" + * interface in the DBus service specified in the constructor. + * + * Although the idea is let other objects use it like a local object, it has to + * communicate with the remote EditorSupport through DBus anyway, so the methods + * may throw a DBusException if something goes wrong. + */ +class RemoteEditorSupport: public QDBusAbstractInterface { +Q_OBJECT +public: + + /** + * Creates a new RemoteEditorSupport to represent a remote EditorSupport in + * the given DBus service. + * + * @param service The DBus service name. + * @param mapper The RemoteObjectMapper to get RemoteObjects from. + */ + RemoteEditorSupport(const QString& service, RemoteObjectMapper* mapper); + + /** + * Destroys this RemoteEditorSupport. + * The RemoteEventSpy is also destroyed, if any. + */ + virtual ~RemoteEditorSupport(); + + /** + * Returns the RemoteObject that represents the main window of the + * application. + * + * @return The RemoteObject for the main window. + * @throws DBusException If a DBus error happens. + */ + RemoteObject* mainWindow() throw (DBusException); + + /** + * Highlights the widget represented by the given remote object. + * If the object does not represent a widget nothing is highlighted. + * + * @param remoteWidget The RemoteObject for the widget to highlight. + * @throws DBusException If a DBus error happens. + */ + void highlight(RemoteObject* remoteWidget) throw (DBusException); + + /** + * Stops highlighting the widget represented by the given remote object. + * If the object does not represent a widget no highlighting is stopped. + * + * @param remoteWidget The RemoteObject for the widget to stop highlighting. + * @throws DBusException If a DBus error happens. + */ + void stopHighlighting(RemoteObject* remoteWidget) throw (DBusException); + + /** + * Enables the EventSpy in the remote EditorSupport and returns a proxy for + * it. + * If the EventSpy was already enabled, the already created proxy is + * returned again. + * The RemoteEventSpy is destroyed when the EventSpy is disabled or this + * RemoteEditorSupport destroyed, so consider using QPointer to store it. + * + * @return A proxy for the remote EventSpy. + * @throws DBusException If a DBus error happens. + */ + RemoteEventSpy* enableEventSpy() throw (DBusException); + + /** + * Disables the EventSpy in the remote EditorSupport and destroys the proxy + * for it. + * + * @throws DBusException If a DBus error happens. + */ + void disableEventSpy() throw (DBusException); + +private: + + /** + * The mapper that associates a RemoteObject with its object id. + */ + RemoteObjectMapper* mMapper; + + /** + * The RemoteEventSpy, if enabled. + */ + RemoteEventSpy* mRemoteEventSpy; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteEventSpy.h" + +#include <QDBusInterface> + +#include "RemoteObjectMapper.h" + +//public: + +RemoteEventSpy::RemoteEventSpy(const QString& service, + RemoteObjectMapper* mapper): QObject(), + mMapper(mapper) { + + //RemoteEventSpy class can not inherit from QDBusInterface as that breaks + //the "magic" done by QDbusInterface (it redefines qt_metacall and things + //like that) and signals can not be connected so easily + QDBusInterface* interface = new QDBusInterface( + service, "/ktutorial/EventSpy", "org.kde.ktutorial.EventSpy", + QDBusConnection::sessionBus(), this); + connect(interface, SIGNAL(eventReceived(int, QString)), + this, SLOT(handleEventReceived(int, QString))); +} + +//private: + +void RemoteEventSpy::handleEventReceived(int objectId, + const QString& eventType) { + emit eventReceived(mMapper->remoteObject(objectId), eventType); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.h 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEEVENTSPY_H +#define REMOTEEVENTSPY_H + +#include <QObject> + +class RemoteObject; +class RemoteObjectMapper; + +/** + * Proxy for the remote EventSpy exposed by KTutorial editor support module in + * KTutorial library. + * RemoteEventSpy represents a remote EventSpy exposed through DBus. Its purpose + * is provide an API to use the remote EventSpy like any other local object, + * hiding the DBus complexity behind it. + * + * RemoteEventSpy handles the eventReceived signal emitted by + * "org.kde.ktutorial.EventSpy" interface in the DBus service specified in the + * constructor and emits an equivalent signal replacing the object id with a + * RemoteObject proxy. + */ +class RemoteEventSpy: public QObject { +Q_OBJECT +public: + + /** + * Creates a new RemoteEventSpy to represent a remote EventSpy in the given + * DBus service. + * + * @param service The DBus service name. + * @param mapper The RemoteObjectMapper to get RemoteObjects from. + */ + RemoteEventSpy(const QString& service, RemoteObjectMapper* mapper); + +Q_SIGNALS: + + /** + * Emitted when the remote object receives an event. + * + * @param remoteObject The proxy for the real remote object. + * @param eventType The type of the event received. + */ + void eventReceived(RemoteObject* remoteObject, const QString& eventType); + +private: + + /** + * The mapper that associates a RemoteObject with its object id. + */ + RemoteObjectMapper* mMapper; + +private Q_SLOTS: + + /** + * Handles an event reception notified by the EventSpy. + * + * @param objectId The id of the remote object that received the event. + * @param eventType The type of the event received. + */ + void handleEventReceived(int objectId, const QString& eventType); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEventSpy.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObject.h" + +#include <QtDBus/QtDBus> + +#include "RemoteObjectMapper.h" + +//public: + +RemoteObject::RemoteObject(const QString& service, RemoteObjectMapper* mapper, + int objectId): + QDBusAbstractInterface(service, "/ktutorial/ObjectRegister", + "org.kde.ktutorial.ObjectRegister", + QDBusConnection::sessionBus(), 0), + mMapper(mapper), + mObjectId(objectId) { +} + +int RemoteObject::objectId() const { + return mObjectId; +} + +QString RemoteObject::name() throw (DBusException) { + QDBusReply<QString> reply = call("objectName", mObjectId); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + return reply.value(); +} + +QString RemoteObject::className() throw (DBusException) { + QDBusReply<QString> reply = call("className", mObjectId); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + return reply.value(); +} + +Q_DECLARE_METATYPE(QList<int>) + +QList<RemoteObject*> RemoteObject::children() throw (DBusException) { + qDBusRegisterMetaType< QList<int> >(); + + QDBusReply< QList<int> > reply = call("childObjectIds", mObjectId); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + QList<RemoteObject*> children; + foreach (int childObjectId, reply.value()) { + children.append(mMapper->remoteObject(childObjectId)); + } + + return children; +} Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,105 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECT_H +#define REMOTEOBJECT_H + +#include <QDBusAbstractInterface> + +#include "DBusException.h" + +class RemoteObjectMapper; + +/** + * Proxy for remote objects exposed by KTutorial editor support module in + * KTutorial library. + * RemoteObject represents a remote object exposed by ObjectRegister in + * KTutorial library through DBus. Its purpose is provide an API to use the + * remote object like any other local object, hiding the DBus complexity behind + * it. + * + * To get the data, RemoteObject makes DBus calls to the + * "org.kde.ktutorial.ObjectRegistry" interface in the DBus service specified in + * the constructor. + * + * Although the idea is let other objects use it like a local object, it has to + * communicate with the remote ObjectRegistry through DBus anyway, so the + * methods may throw a DBusException if something goes wrong. + */ +class RemoteObject: public QDBusAbstractInterface { +Q_OBJECT +public: + + /** + * Creates a new RemoteObject to represent the remote object with the given + * id in the given DBus service name. + * + * @param service The DBus service name. + * @param mapper The RemoteObjectMapper to get RemoteObjects from. + * @param objectId The id of the remote object. + */ + RemoteObject(const QString& service, RemoteObjectMapper* mapper, + int objectId); + + /** + * Returns the id of the remote object. + * + * @return The id of the remote object. + */ + int objectId() const; + + /** + * Returns the object name. + * + * @return The object name. + * @throws DBusException If a DBus error happens. + */ + QString name() throw (DBusException); + + /** + * Returns the class name. + * + * @return The class name. + * @throws DBusException If a DBus error happens. + */ + QString className() throw (DBusException); + + /** + * Returns a list with the RemoteObjects that represent the children of this + * remote object. + * + * @return The child remote objects. + * @throws DBusException If a DBus error happens. + */ + QList<RemoteObject*> children() throw (DBusException); + +private: + + /** + * The mapper that associates a RemoteObject with its object id. + */ + RemoteObjectMapper* mMapper; + + /** + * The id of the remote object. + */ + int mObjectId; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectMapper.h" + +#include <QHash> + +#include "RemoteObject.h" + +//public: + +RemoteObjectMapper::RemoteObjectMapper(const QString& service): + mService(service) { +} + +RemoteObjectMapper::~RemoteObjectMapper() { + qDeleteAll(mRemoteObjects); +} + +RemoteObject* RemoteObjectMapper::remoteObject(int objectId) { + if (mRemoteObjects.contains(objectId)) { + return mRemoteObjects.value(objectId); + } + + RemoteObject* remoteObject = new RemoteObject(mService, this, objectId); + mRemoteObjects.insert(objectId, remoteObject); + + return remoteObject; +} + +void RemoteObjectMapper::clear() { + qDeleteAll(mRemoteObjects); + mRemoteObjects.clear(); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTMAPPER_H +#define REMOTEOBJECTMAPPER_H + +#include <QHash> + +class RemoteObject; + +/** + * Map to get a RemoteObject from their objectId. + * The RemoteObjectMapper should be used to get all the RemoteObjects for its + * DBus service. It creates a new RemoteObject when there is no RemoteObject + * for the given id, or returns the previosly created one, depending on the + * case. + * + * The RemoteObjectMapper also has ownership of the RemoteObjects, so they are + * deleted when the mapper is cleared or destroyed. + */ +class RemoteObjectMapper { +public: + + /** + * Creates a new RemoteObjectMapper for the given DBus service name. + * + * @param service The DBus service name of the remote objects. + */ + RemoteObjectMapper(const QString& service); + + /** + * Destroys this RemoteObjectMapper. + * All the mapped RemoteObjects are also destroyed. + */ + ~RemoteObjectMapper(); + + /** + * Returns the RemoteObject associated with the given object id. + * The RemoteObject is destroyed when this RemoteObjectMapper is cleared or + * destroyed, so consider using QPointer to store it. + * + * @param objectId The id of the remote object. + * @return The RemoteObject. + */ + RemoteObject* remoteObject(int objectId); + + /** + * Destroys all the mapped RemoteObject. + */ + void clear(); + +private: + + /** + * The DBus service name of the remote objects. + */ + QString mService; + + /** + * All the RemoteObjects already requested since the last time this + * RemoteObjectMapper was cleared. + */ + QHash<int, RemoteObject*> mRemoteObjects; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,174 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "TargetApplication.h" + +#include <QtDBus/QtDBus> + +#include <KProcess> +#include <KUrl> + +#include "RemoteEditorSupport.h" +#include "RemoteObjectMapper.h" + +//public: + +TargetApplication* TargetApplication::self() { + return sSelf; +} + +TargetApplication::~TargetApplication() { + if (mProcess) { + mProcess->kill(); + mProcess->waitForFinished(100); + } +} + +QString TargetApplication::targetApplicationFilePath() const { + return mTargetApplicationFilePath; +} + +void TargetApplication::setTargetApplicationFilePath( + const QString& targetApplicationFilePath) { + KUrl url(targetApplicationFilePath); + url.cleanPath(); + + if (url.isRelative()) { + mTargetApplicationFilePath = ""; + } else { + mTargetApplicationFilePath = url.toLocalFile(); + } +} + +RemoteEditorSupport* TargetApplication::remoteEditorSupport() { + return mRemoteEditorSupport; +} + +void TargetApplication::start() { + if (mProcess && mProcess->program()[0] == mTargetApplicationFilePath) { + return; + } + + if (!QDBusConnection::sessionBus().isConnected()) { + emit startFailed(NoDBusConnection); + return; + } + + if (mProcess) { + mProcess->kill(); + //Don't start the new target application until the old one is dead. + //It may freeze the editor GUI, but it is unlikely that a process gets + //enough time being killed to be noticed + mProcess->waitForFinished(-1); + } + + mProcess = new KProcess(this); + mProcess->setProgram(mTargetApplicationFilePath); + + mServiceName.clear(); + + QDBusConnectionInterface* interface = + QDBusConnection::sessionBus().interface(); + connect(interface, SIGNAL(serviceRegistered(QString)), + this, SLOT(checkNewService(QString))); + connect(interface, SIGNAL(serviceOwnerChanged(QString, QString, QString)), + this, SLOT(checkNewService(QString))); + + connect(mProcess, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(handleProcessError(QProcess::ProcessError))); + + connect(mProcess, SIGNAL(finished(int)), + this, SLOT(clean())); + + mServiceDiscoveryTimer.setInterval(3000); + mServiceDiscoveryTimer.setSingleShot(true); + connect(mProcess, SIGNAL(started()), + &mServiceDiscoveryTimer, SLOT(start())); + connect(&mServiceDiscoveryTimer, SIGNAL(timeout()), + this, SLOT(handleTargetApplicationDoesNotSupportKTutorial())); + + mProcess->start(); +} + +//private: + +TargetApplication* TargetApplication::sSelf = new TargetApplication(); + +TargetApplication::TargetApplication(): + mProcess(0), + mMapper(0), + mRemoteEditorSupport(0) { +} + +//private slots: + +void TargetApplication::checkNewService(const QString& service) { + QDBusInterface interface(service, "/ktutorial", + "org.kde.ktutorial.EditorSupport"); + if (!interface.isValid()) { + return; + } + + QDBusReply<QString> reply = interface.call("applicationFilePath"); + if (!reply.isValid()) { + return; + } + + if (reply.value() != mTargetApplicationFilePath) { + return; + } + + mServiceDiscoveryTimer.stop(); + + mServiceName = service; + mMapper = new RemoteObjectMapper(mServiceName); + mRemoteEditorSupport = new RemoteEditorSupport(mServiceName, mMapper); + + emit started(); + + disconnect(QDBusConnection::sessionBus().interface(), 0, this, 0); +} + +void TargetApplication::handleProcessError(QProcess::ProcessError error) { + if (error != QProcess::FailedToStart) { + return; + } + + mServiceDiscoveryTimer.stop(); + + emit startFailed(InvalidPath); +} + +void TargetApplication::handleTargetApplicationDoesNotSupportKTutorial() { + mProcess->kill(); + + emit startFailed(InvalidApplication); +} + +void TargetApplication::clean() { + //Don't delete it directly, as waitForFinished crashes somewhere internally + //due to an event problem + mProcess->deleteLater(); + mProcess = 0; + delete mRemoteEditorSupport; + mRemoteEditorSupport = 0; + delete mMapper; + mMapper = 0; + + emit finished(); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,238 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef TARGETAPPLICATION_H +#define TARGETAPPLICATION_H + +#include <QObject> +#include <QProcess> +#include <QTimer> + +class KProcess; +class RemoteEditorSupport; +class RemoteObjectMapper; + +/** + * Class to execute a target application and communicate with it through D-Bus. + * The target application is the application that the tutorial will be part of. + * Using TargetApplication class a new instance of a target application can be + * executed and, through D-Bus, KTutorial editor can communicate with it to + * introspect its structure. The target application must have KTutorial enabled + * in order to use the KTutorial editor support module. Applications that do not + * use KTutorial can not be introspected. + * + * Knowing the structure of the target application, KTutorial editor can provide + * valuable information to the tutorial author like the name of an object or its + * class, so the tutorial author can design the tutorial more easily. + * + * The TargetApplication can be started using method start(). The method returns + * immediately, and when the application is really running the started() signal + * is emitted. If the application could not be executed, or it was executed but + * other problem happened (for example, if the target application does not + * support KTutorial), startFailed(Error) signal is emitted instead. + * + * Once the target application has been started, remoteEditorSupport + * returns a RemoteEditorSupport connected to the remote + * "org.kde.ktutorial.EditorSupport" interface exposed by the target + * application. + * + * The target application will be killed when the TargetApplication is + * destroyed, or when the start method is called again after setting a different + * application file path. In any of those cases, or if the target application + * was closed externally (by the user), finished() signal is emitted. + */ +class TargetApplication: public QObject { +Q_OBJECT +public: + + /** + * The type of error that made starting the target application fail. + */ + enum Error { + + /** + * The application was started, but it does not support KTutorial. + */ + InvalidApplication, + + /** + * The application was not even started. + */ + InvalidPath, + + /** + * The application was started, but there is no D-Bus connection to + * communicate with it. + */ + NoDBusConnection + }; + + /** + * Returns the only instance of TargetApplication class. + * + * @return The TargetApplication instance. + */ + static TargetApplication* self(); + + /** + * Destroys this TargetApplication. + * If the target application is running, it is killed. + */ + virtual ~TargetApplication(); + + /** + * Returns the executable for the target application. + * + * @return The target application file path. + */ + QString targetApplicationFilePath() const; + + /** + * Sets the executable for the target application. + * The executable must be an absolute path. A relative path will be set as + * an empty string. If the path is absolute but contains "." and ".." + * components they are resolved and the clean path is set. + * + * @param applicationFilePath The target application file path. + */ + void setTargetApplicationFilePath(const QString& targetApplicationFilePath); + + /** + * Returns the RemoteEditorSupport for the target application. + * If the application is not running a null pointer is returned. + * + * @return The RemoteEditorSupport for the target application. + */ + RemoteEditorSupport* remoteEditorSupport(); + + /** + * Starts a new TargetApplication. + * When the target application is running, started() signal is emitted. If + * there is no application file path, the application file path is not valid + * or the application does not support KTutorial editor, startFailed(Error) + * signal is emitted instead. + * + * If the target application was already started nothing is done (even + * started() signal is not emitted). + */ + void start(); + +Q_SIGNALS: + + /** + * Emitted when the target application was started and there is a D-Bus + * connection to it. + */ + void started(); + + /** + * Emitted when the target application could not be started successfully. + * + * @param error The type of error that happened. + */ + void startFailed(TargetApplication::Error error); + + /** + * Emitted when the target application was finished for any reason. + */ + void finished(); + +private: + + /** + * The only instance of TargetApplication. + */ + static TargetApplication* sSelf; + + /** + * The executable for the target application. + */ + QString mTargetApplicationFilePath; + + /** + * The process executing the target application, if any. + */ + KProcess* mProcess; + + /** + * Timer to give time to "/ktutorial" object to appear in some new service + * of the bus. + * If the timer ends and the object was not found, the target application + * seems to not to support KTutorial editor. + */ + QTimer mServiceDiscoveryTimer; + + /** + * The D-Bus service name provided by the target application, if any. + */ + QString mServiceName; + + /** + * The mapper that associates RemoteObjects with their object id. + */ + RemoteObjectMapper* mMapper; + + /** + * The RemoteEditorSupport for the target application, if any. + */ + RemoteEditorSupport* mRemoteEditorSupport; + + /** + * Creats a new TargetApplication. + * Hidden to avoid classes other than TargetApplication itself to create + * objects of it. + */ + TargetApplication(); + +private Q_SLOTS: + + /** + * Checks if the new service found in the session bus is the one from the + * started target application. + * When the service is found, the target application has started from + * KTutorial editor point of view. Signal started() is emitted in that case. + * + * @param service The D-Bus service to check. + */ + void checkNewService(const QString& service); + + /** + * Checks if the process could not be started. + * Signal startFailed(Error), with InvalidPath, is emitted in that case. + * + * @param error The process error. + */ + void handleProcessError(QProcess::ProcessError error); + + /** + * Called when the time out to find the D-Bus service expired. + * Signal startFailed(Error), with InvalidApplication, is emitted in that + * case. + */ + void handleTargetApplicationDoesNotSupportKTutorial(); + + /** + * Called when the process has finished. + * The process, mapper and remote editor support are deleted, and the + * finished() signal is emitted. + */ + void clean(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt 2010-04-13 18:49:12 UTC (rev 229) +++ trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt 2010-04-13 20:11:06 UTC (rev 230) @@ -1,6 +1,9 @@ add_subdirectory(commands) add_subdirectory(data) add_subdirectory(serialization) +if (QT_QTDBUS_FOUND) + add_subdirectory(targetapplication) +endif (QT_QTDBUS_FOUND) add_subdirectory(view) # Used by kde4_add_unit_test to set the full path to test executables Added: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,47 @@ +# Used by kde4_add_unit_test to set the full path to test executables +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${ktutorial-editor_SOURCE_DIR}/src/targetapplication ${KDE4_INCLUDES}) + +# Remote class stubs are QObjects, so moc files have to be generated for them +qt4_wrap_cpp(dbus_interface_stubs_MOC RemoteClassStubs.h) + +# Dummy application to be executed in TargetApplication test +add_executable(DummyApplication DummyApplication.cpp) +target_link_libraries(DummyApplication ${KDE4_KDEUI_LIBS} ${QT_QTDBUS_LIBRARY}) + +# Target application stub to be executed in TargetApplication test +add_executable(TargetApplicationStub TargetApplicationStub.cpp ${dbus_interface_stubs_MOC}) +target_link_libraries(TargetApplicationStub ${KDE4_KDEUI_LIBS} ${QT_QTDBUS_LIBRARY}) + +MACRO(UNIT_TESTS) + FOREACH(_className ${ARGN}) + set(_testName ${_className}Test) + kde4_add_unit_test(${_testName} TESTNAME ktutorial-editor-unit-${_testName} ${_testName}.cpp ${dbus_interface_stubs_MOC}) + target_link_libraries(${_testName} ktutorial_editor_targetapplication ${QT_QTTEST_LIBRARY}) + ENDFOREACH(_className) +ENDMACRO(UNIT_TESTS) + +unit_tests( + DBusException + RemoteEditorSupport + RemoteEventSpy + RemoteObject + RemoteObjectMapper + TargetApplication +) + +MACRO(MEM_TESTS) + FOREACH(_testname ${ARGN}) + add_test(ktutorial-editor-unit-mem-${_testname} ${CMAKE_CURRENT_SOURCE_DIR}/../runMemcheck.py ${CMAKE_CURRENT_BINARY_DIR}/${_testname}Test ${CMAKE_CURRENT_BINARY_DIR}) + ENDFOREACH(_testname) +ENDMACRO(MEM_TESTS) + +mem_tests( + DBusException + RemoteEditorSupport + RemoteEventSpy + RemoteObject + RemoteObjectMapper + TargetApplication +) Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DBusExceptionTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DBusExceptionTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DBusExceptionTest.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "DBusException.h" + +class DBusExceptionTest: public QObject { +Q_OBJECT + +private slots: + + void testConstructor(); + void testConstructorEmpty(); + +}; + +void DBusExceptionTest::testConstructor() { + DBusException exception(QString("The message")); + + QCOMPARE(exception.what(), "The message"); + QCOMPARE(exception.message(), QString("The message")); +} + +void DBusExceptionTest::testConstructorEmpty() { + DBusException exception; + + QCOMPARE(exception.what(), ""); + QCOMPARE(exception.message(), QString("")); +} + +QTEST_MAIN(DBusExceptionTest) + +#include "DBusExceptionTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DBusExceptionTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DummyApplication.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DummyApplication.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/DummyApplication.cpp 2010-04-13 20:11:06 UTC (rev 230) @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License a... [truncated message content] |
From: <dan...@us...> - 2010-04-13 20:30:23
|
Revision: 231 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=231&view=rev Author: danxuliu Date: 2010-04-13 20:30:16 +0000 (Tue, 13 Apr 2010) Log Message: ----------- -Add RemoteObjectChooser (and related classes) to select a remote object from a running application. Use it in WaitForEvent and WaitForWidget. -Add a helper class to execute dialogs that do not finish when they are hidden (required by RemoteObjectChooser to safely hide all the windows and dialogs except itself). Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/EditActions.cpp trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.h trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.ui trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.ui trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.cpp trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h trunk/ktutorial/ktutorial-editor/src/view/TargetApplicationView.cpp trunk/ktutorial/ktutorial-editor/src/view/TargetApplicationView.h trunk/ktutorial/ktutorial-editor/tests/unit/view/DialogRunnerTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectTreeItemTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectTreeItemUpdaterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectTreeSelectionManagerTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/EditActions.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/EditActions.cpp 2010-04-13 20:11:06 UTC (rev 230) +++ trunk/ktutorial/ktutorial-editor/src/EditActions.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -28,6 +28,7 @@ #include "commands/TutorialCommands.h" #include "data/Reaction.h" #include "data/Step.h" +#include "view/DialogRunner.h" #include "view/EditionDialog.h" #include "view/LicenseWidget.h" #include "view/ReactionWidget.h" @@ -213,10 +214,7 @@ EditionDialog* dialog = new EditionDialog(commandWidget, mTutorialEditor); dialog->setObjectName("editionDialog"); - int dialogCode = dialog->exec(); - dialog->deleteLater(); - - return dialogCode; + return DialogRunner(dialog).exec(); } //private slots: Modified: trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp 2010-04-13 20:11:06 UTC (rev 230) +++ trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -35,6 +35,10 @@ #include "view/TutorialTreeItem.h" #include "view/TutorialTreeSelectionManager.h" +#ifdef QT_QTDBUS_FOUND +#include "targetapplication/TargetApplication.h" +#endif + //public: KTutorialEditor::KTutorialEditor(): KXmlGuiWindow(0), @@ -63,6 +67,12 @@ setupGUI(); } +KTutorialEditor::~KTutorialEditor() { +#ifdef QT_QTDBUS_FOUND + delete TargetApplication::self(); +#endif +} + Tutorial* KTutorialEditor::tutorial() { return mTutorial; } @@ -136,6 +146,10 @@ delete mTutorial; mTutorial = tutorial; + +#ifdef QT_QTDBUS_FOUND + TargetApplication::self()->setTargetApplicationFilePath(""); +#endif } void KTutorialEditor::setupDocks() { Modified: trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.h 2010-04-13 20:11:06 UTC (rev 230) +++ trunk/ktutorial/ktutorial-editor/src/KTutorialEditor.h 2010-04-13 20:30:16 UTC (rev 231) @@ -41,6 +41,12 @@ KTutorialEditor(); /** + * Destroys this KTutorialEditor. + * The target application is also killed if it is running. + */ + virtual ~KTutorialEditor(); + + /** * Returns the tutorial being edited. * * @return The tutorial being edited. Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-04-13 20:11:06 UTC (rev 230) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-04-13 20:30:16 UTC (rev 231) @@ -2,6 +2,7 @@ ActionListWidget.cpp AutoExpandableTreeView.cpp CommandWidget.cpp + DialogRunner.cpp EditionDialog.cpp EditionWidget.cpp LicenseWidget.cpp @@ -29,6 +30,18 @@ WaitForWidget.cpp ) +if (QT_QTDBUS_FOUND) + add_definitions(-DQT_QTDBUS_FOUND) + set(ktutorial_editor_view_SRCS + ${ktutorial_editor_view_SRCS} + RemoteObjectChooser.cpp + RemoteObjectTreeItem.cpp + RemoteObjectTreeItemUpdater.cpp + RemoteObjectTreeSelectionManager.cpp + TargetApplicationView.cpp + ) +endif (QT_QTDBUS_FOUND) + kde4_add_ui_files(ktutorial_editor_view_SRCS CustomCodeWidget.ui LicenseWidget.ui @@ -41,10 +54,23 @@ WaitForWidget.ui ) +if (QT_QTDBUS_FOUND) + kde4_add_ui_files(ktutorial_editor_view_SRCS + RemoteObjectChooser.ui + ) +endif (QT_QTDBUS_FOUND) + kde4_add_library(ktutorial_editor_view ${ktutorial_editor_view_SRCS}) target_link_libraries(ktutorial_editor_view ktutorial_editor_commands ktutorial_editor_data ${KDE4_KDEUI_LIBS} + ${KDE4_KIO_LIBS} ) + +if (QT_QTDBUS_FOUND) + target_link_libraries(ktutorial_editor_view + ktutorial_editor_targetapplication + ) +endif (QT_QTDBUS_FOUND) Added: trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "DialogRunner.h" + +#include <QDialog> +#include <QEventLoop> + +//public: + +DialogRunner::DialogRunner(QDialog* dialog, QObject* parent): + QObject(parent), + mDialog(dialog) { + Q_ASSERT(dialog); +} + +int DialogRunner::exec() { + Q_ASSERT(mDialog); + + mDialog->setModal(true); + mDialog->show(); + + connect(mDialog, SIGNAL(finished(int)), this, SLOT(exitEventLoop())); + connect(mDialog, SIGNAL(destroyed(QObject*)), this, SLOT(exitEventLoop())); + + mEventLoop = new QEventLoop(); + mEventLoop->exec(QEventLoop::DialogExec); + delete mEventLoop; + + if (!mDialog) { + return QDialog::Rejected; + } + + int result = mDialog->result(); + mDialog->deleteLater(); + + return result; +} + +//private slots: + +void DialogRunner::exitEventLoop() { + mEventLoop->exit(); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.h 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef DIALOGRUNNER_H +#define DIALOGRUNNER_H + +#include <QObject> +#include <QPointer> + +class QDialog; +class QEventLoop; + +/** + * Convenience class to execute modal dialogs that can be hidden without ending + * their execution. + * DialogRunner just shows the given dialog as a modal dialog and executes an + * event loop to block in the exec() method until the user closes the dialog or + * the dialog is destroyed. The dialog can be hidden and shown again without + * causing the exec method to return (in contrast with QDialog::exec() that + * returns when the dialog is hidden). + * + * Once the event loop ends, the dialog is scheduled for deletion. + */ +class DialogRunner: public QObject { +Q_OBJECT +public: + + /** + * Creates a new DialogRunner for the given dialog and with the given + * parent. + * + * @param dialog The dialog to execute. + * @param parent The parent object of this DialogRunner. + */ + explicit DialogRunner(QDialog* dialog, QObject* parent = 0); + + /** + * Shows the dialog as a modal dialog, blocking until the user closes it. + */ + int exec(); + +private: + + /** + * The dialog to execute. + */ + QPointer<QDialog> mDialog; + + /** + * The event loop to block exec method in. + */ + QEventLoop* mEventLoop; + +private Q_SLOTS: + + /** + * Exits the event loop. + * When the event loop exits, exec method is no longer blocked and returns. + */ + void exitEventLoop(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/DialogRunner.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,204 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectChooser.h" +#include "ui_RemoteObjectChooser.h" + +#include <QPushButton> + +#include <KDebug> +#include <KDialogButtonBox> +#include <KMessageBox> + +#include "RemoteObjectTreeItem.h" +#include "RemoteObjectTreeItemUpdater.h" +#include "RemoteObjectTreeSelectionManager.h" +#include "TreeModel.h" +#include "TargetApplicationView.h" +#include "../targetapplication/RemoteEditorSupport.h" +#include "../targetapplication/RemoteEventSpy.h" +#include "../targetapplication/RemoteObject.h" +#include "../targetapplication/TargetApplication.h" + +//public: + +RemoteObjectChooser::RemoteObjectChooser(QWidget* parent): + QWidget(parent), + mCurrentRemoteObject(0), + mSuccessfullyStarted(false) { + setWindowFlags(Qt::Window); + Q_ASSERT(parent); + + ui = new Ui::RemoteObjectChooser(); + ui->setupUi(this); + + QPushButton* button = ui->dialogButtonBox->button(QDialogButtonBox::Ok); + button->setObjectName("okButton"); + button->setEnabled(false); + connect(button, SIGNAL(clicked(bool)), this, SLOT(accept())); + + button = ui->dialogButtonBox->button(QDialogButtonBox::Cancel); + button->setObjectName("cancelButton"); + connect(button, SIGNAL(clicked(bool)), this, SLOT(cancel())); + + connect(TargetApplication::self(), SIGNAL(started()), + this, SLOT(handleTargetApplicationStarted())); + connect(TargetApplication::self(), + SIGNAL(startFailed(TargetApplication::Error)), + this, SLOT(cancel())); + connect(TargetApplication::self(), SIGNAL(finished()), + this, SLOT(handleTargetApplicationFinished())); + + if (TargetApplication::self()->remoteEditorSupport()) { + handleTargetApplicationStarted(); + } else { + TargetApplicationView* targetApplicationView = + new TargetApplicationView(TargetApplication::self(), this); + targetApplicationView->start(); + } +} + +RemoteObjectChooser::~RemoteObjectChooser() { + if (mCurrentRemoteObject && + TargetApplication::self()->remoteEditorSupport()) { + TargetApplication::self()->remoteEditorSupport()-> + stopHighlighting(mCurrentRemoteObject); + } + + delete ui; +} + +//protected: + +void RemoteObjectChooser::closeEvent(QCloseEvent* event) { + QWidget::closeEvent(event); + showParentWindows(this); + deleteLater(); +} + +//private: + +void RemoteObjectChooser::hideParentWindows(QWidget* widget) { + if (!widget || !widget->parentWidget()) { + return; + } + + if (widget->parentWidget()->windowFlags() & (Qt::Window | Qt::Dialog)) { + widget->parentWidget()->hide(); + } + + hideParentWindows(widget->parentWidget()); +} + +void RemoteObjectChooser::showParentWindows(QWidget* widget) { + if (!widget || !widget->parentWidget()) { + return; + } + + //Show them in inverse order, because showing first a dialog and then its + //parent window would create an entry for the dialog and another entry for + //the window in the task bar + showParentWindows(widget->parentWidget()); + + if (widget->parentWidget()->windowFlags() & (Qt::Window | Qt::Dialog)) { + widget->parentWidget()->show(); + } +} + +//private slots: + +void RemoteObjectChooser::handleTargetApplicationStarted() { + hideParentWindows(this); + + RemoteEditorSupport* remoteEditorSupport = + TargetApplication::self()->remoteEditorSupport(); + + RemoteObject* mainWindow = remoteEditorSupport->mainWindow(); + RemoteObjectTreeItem* rootItem = new RemoteObjectTreeItem(mainWindow); + TreeModel* treeModel = new TreeModel(rootItem, this); + ui->remoteObjectsTreeView->setModel(treeModel); + + RemoteObjectTreeItemUpdater* updater = + new RemoteObjectTreeItemUpdater(this); + rootItem->setUpdater(updater); + + try { + updater->setRemoteEventSpy(remoteEditorSupport->enableEventSpy()); + } catch (DBusException e) { + kWarning() << "Remote event spy could not be enabled in the target " + << "application. Children objects will not be added and " + << "removed automatically in the list to reflect the " + << "changes in the remote objects (" << e.message() << ")."; + } + + //Parent object is set to the selection model, so the manager is also + //deleted when the selection model it watches is deleted + QItemSelectionModel* selectionModel = + ui->remoteObjectsTreeView->selectionModel(); + RemoteObjectTreeSelectionManager* selectionManager = + new RemoteObjectTreeSelectionManager(selectionModel, selectionModel); + connect(selectionManager, SIGNAL(remoteObjectSelected(RemoteObject*)), + this, SLOT(setCurrentRemoteObject(RemoteObject*))); + + mSuccessfullyStarted = true; +} + +void RemoteObjectChooser::handleTargetApplicationFinished() { + //If the application is running but it was not successfully started (the + //application does not have the KTutorial editor support module) + //this RemoteObjectChooser is just closed. The TargetApplicationView takes + //care of notifying the user about the problem. + if (!mSuccessfullyStarted) { + return; + } + + QString text = i18nc("@label", "The target application has been closed, " +"but it must be running to be able to choose the objects."); + QString caption = i18nc("@title:window", "Target application closed"); + KMessageBox::sorry(this, text, caption); + + close(); +} + +void RemoteObjectChooser::setCurrentRemoteObject(RemoteObject* remoteObject) { + if (mCurrentRemoteObject && mCurrentRemoteObject != remoteObject) { + TargetApplication::self()->remoteEditorSupport()-> + stopHighlighting(mCurrentRemoteObject); + } + + mCurrentRemoteObject = remoteObject; + + if (mCurrentRemoteObject) { + TargetApplication::self()->remoteEditorSupport()-> + highlight(mCurrentRemoteObject); + ui->dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + } else { + ui->dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + } +} + +void RemoteObjectChooser::accept() { + Q_ASSERT(mCurrentRemoteObject); + + emit remoteObjectChosen(mCurrentRemoteObject); + close(); +} + +void RemoteObjectChooser::cancel() { + close(); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,171 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTCHOOSER_H +#define REMOTEOBJECTCHOOSER_H + +#include <QWidget> + +class RemoteObject; + +namespace Ui { +class RemoteObjectChooser; +} + +/** + * Widget to choose a remote object from all the available remote objects in the + * target application. + * The RemoteObjectChooser shows a window with a list containing all the objects + * that can be accesed by KTutorial in the target application. The target + * application must be running, so it is started when the RemoteObjectChooser is + * created. If the target application is closed, the RemoteObjectChooser is also + * closed (after notifying the user). + * + * When the target application is successfully started, the RemoteObjectChooser + * hides all its parent dialogs and windows. Only the RemoteObjectChooser itself + * is kept shown to avoid the rest of windows of KTutorial editor to get in the + * way of the user when he interacts with the target application. + * + * When the user selects a remote object in the list and that object represents + * a widget, the widget is highlighted in the target application. + * + * The window contains an "Ok" and a "Cancel" button, like the ones found in + * dialogs. A window is used instead of a dialog to be shown in the task bar (as + * after hidding the rest of KTutorial editor windows no entry would be shown if + * this were a dialog). When there is a remote object selected and the "Ok" + * button is clicked, signal remoteObjectChosen(RemoteObject*) is emitted. + * + * The RemoteObjectChooser deletes itself once it has been closed, no matter the + * reason why. + */ +class RemoteObjectChooser: public QWidget { +Q_OBJECT +public: + + /** + * Creates a new RemoteObjectChooser with the given parent. + * The target application is started. + * + * @param parent The parent QWidget. + */ + explicit RemoteObjectChooser(QWidget* parent = 0); + + /** + * Destroys this RemoteObjectChooser. + * The highlighting in the current remote object is stopped, if any. + */ + virtual ~RemoteObjectChooser(); + +Q_SIGNALS: + + /** + * Emitted when the user chooses a RemoteObject. + * + * @param remoteObject The chosen RemoteObject. + */ + void remoteObjectChosen(RemoteObject* remoteObject); + +protected: + + /** + * Executes KDialog handler, shows again the parent windows and schedules + * this RemoteObjectChooser for deletion. + * close() can't be redefined to restore the parent windows, as it doesn't + * get a close triggered by the window manager (like using ALT+F4) (and it + * is not even a virtual method). Moreover, close() itself sends a + * QCloseEvent, which is handled by this method. + * Likely, Qt::WA_DeleteOnClose isn't used as it will delete this + * RemoteObjectChooser only through close(), but not through a QCloseEvent. + * Instead, this RemoteObjectChooser is scheduled for deletion after + * receiving a QCloseEvent. + * + * @param event The QCloseEvent. + */ + virtual void closeEvent(QCloseEvent* event); + +private: + + /** + * The Ui Designer generated class. + */ + Ui::RemoteObjectChooser* ui; + + /** + * The RemoteObject currently selected in the list. + */ + RemoteObject* mCurrentRemoteObject; + + /** + * True if the target application has been successfully started, false + * otherwise. + */ + bool mSuccessfullyStarted; + + /** + * Hide all the parent widgets of the given widget that are windows or + * dialogs. + * + * @param widget The widget to hide its parents. + */ + void hideParentWindows(QWidget* widget); + + /** + * Shows all the parent widgets of the given widget that are windows or + * dialogs. + * + * @param widget The widget to show its parents. + */ + void showParentWindows(QWidget* widget); + +private Q_SLOTS: + + /** + * Hides the parent windows and dialogs and sets up the models for the tree + * view. + */ + void handleTargetApplicationStarted(); + + /** + * If the target application was finished after starting successfully, a + * warning is shown to the user and this RemoteObjectChooser is closed. + */ + void handleTargetApplicationFinished(); + + /** + * Sets the current remote object. + * The current remote object is highlighted, and the highlighting in the + * previous one is stopped. + * + * @param remoteObject The current remote object to set. + */ + void setCurrentRemoteObject(RemoteObject* remoteObject); + + /** + * Emits remoteObjectChosen(RemoteObject*) and closes this + * RemoteObjectChooser. + */ + void accept(); + + /** + * Closes this RemoteObjectChooser. + */ + void cancel(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>RemoteObjectChooser</class> + <widget class="QWidget" name="RemoteObjectChooser"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string comment="@title:window">Target application objects</string> + </property> + <property name="whatsThis"> + <string comment="@info:whatsthis"><p>Chooser for objects in the target application.</p> +<p>The objects shown in the list are all the objects accessible through KTutorial. They are shown in a tree structure to represent the parent and child relationships the objects have.</p> +<p>Note, however, that not all the objects have a name set in the target application. KTutorial needs to know the name of the object to find it, so if an object does not have a name KTutorial will not be able to use it.</p></string> + </property> + <layout class="QVBoxLayout" name="remoteObjectChooserVerticalLayout"> + <item> + <widget class="AutoExpandableTreeView" name="remoteObjectsTreeView"/> + </item> + <item> + <widget class="KDialogButtonBox" name="dialogButtonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KDialogButtonBox</class> + <extends>QDialogButtonBox</extends> + <header>kdialogbuttonbox.h</header> + </customwidget> + <customwidget> + <class>AutoExpandableTreeView</class> + <extends>QTreeView</extends> + <header>AutoExpandableTreeView.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectTreeItem.h" + +#include <KDebug> +#include <KLocalizedString> + +#include "RemoteObjectTreeItemUpdater.h" +#include "../targetapplication/RemoteObject.h" + +//public: + +RemoteObjectTreeItem::RemoteObjectTreeItem(RemoteObject* remoteObject, + TreeItem* parent): + TreeItem(parent), + mRemoteObject(remoteObject), + mUpdater(0) { + Q_ASSERT(remoteObject); + + try { + mName = remoteObject->name(); + mClassName = remoteObject->className(); + } catch (DBusException e) { + mName = i18nc("@item", "D-Bus Error!"); + mClassName = i18nc("@item", "D-Bus Error!"); + } + + if (mName.isEmpty()) { + mName = i18nc("@item", "Object without name!"); + } + + if (mClassName.isEmpty()) { + mClassName = i18nc("@item", "No class name!"); + } + + updateChildren(); +} + +QString RemoteObjectTreeItem::text() const { + return i18nc("@item Object name (Class name)", "%1 (%2)", mName, + mClassName); +} + +RemoteObject* RemoteObjectTreeItem::remoteObject() const { + return mRemoteObject; +} + +void RemoteObjectTreeItem::setUpdater(RemoteObjectTreeItemUpdater* updater) { + Q_ASSERT(updater); + + mUpdater = updater; + + updater->registerRemoteObjectTreeItem(this); + + foreach (RemoteObjectTreeItem* child, mChildRemoteObjectTreeItems) { + child->setUpdater(updater); + } +} + +void RemoteObjectTreeItem::updateChildren() { + QList<RemoteObject*> children; + try { + children = mRemoteObject->children(); + } catch (DBusException e) { + kWarning() << "The children for the remote object with id " + << mRemoteObject->objectId() << " could not be updated (" + << e.message() << ")."; + return; + } + + int i=0; + while (i < mChildRemoteObjectTreeItems.count()) { + if (i >= children.count() || + mChildRemoteObjectTreeItems[i]->remoteObject() != children[i]) { + removeChildRemoteObject( + mChildRemoteObjectTreeItems[i]->remoteObject()); + } else { + i++; + } + } + + while (i < children.count()) { + addChildRemoteObject(children[i]); + i++; + } +} + +//private: + +RemoteObjectTreeItem* RemoteObjectTreeItem::remoteObjectTreeItemForRemoteObject( + RemoteObject* child) const { + foreach (RemoteObjectTreeItem* remoteObjectTreeItem, + mChildRemoteObjectTreeItems) { + if (remoteObjectTreeItem->remoteObject() == child) { + return remoteObjectTreeItem; + } + } + + return 0; +} + +//private slots: + +void RemoteObjectTreeItem::addChildRemoteObject(RemoteObject* child) { + RemoteObjectTreeItem* remoteObjectTreeItem = + new RemoteObjectTreeItem(child, this); + appendChild(remoteObjectTreeItem); + mChildRemoteObjectTreeItems.append(remoteObjectTreeItem); + + if (mUpdater) { + remoteObjectTreeItem->setUpdater(mUpdater); + } +} + +void RemoteObjectTreeItem::removeChildRemoteObject(RemoteObject* child) { + RemoteObjectTreeItem* remoteObjectTreeItem = + remoteObjectTreeItemForRemoteObject(child); + + removeChild(remoteObjectTreeItem); + mChildRemoteObjectTreeItems.removeOne(remoteObjectTreeItem); + delete remoteObjectTreeItem; +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.h 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTTREEITEM_H +#define REMOTEOBJECTTREEITEM_H + +#include "TreeItem.h" + +class RemoteObject; +class RemoteObjectTreeItemUpdater; + +/** + * A TreeItem that represents a RemoteObject. + * The tree representation of a RemoteObject is: + * The object name (The class name) + * |-The child 1 name (The child 1 class name) + * | ... + * |-The child 2 name (The child 2 class name) + * | ... + * ... + * + * If the RemoteObject returns an empty object name or an empty class name + * (for example, because the id is wrong), a placeholder is used instead. Name + * placeholder is "Object without name!", and the class name placeholder is + * "No class name!". + * + * If a D-Bus error happens, the placeholder for each element is "D-Bus Error!". + * No children are shown if a D-Bus error happened. + * + * RemoteObjects does not provide information about changes in their children. A + * helper class, RemoteObjectTreeItemUpdater, is used for this. + * RemoteObjectTreeItem can work without an updater, but changes in the children + * will not be shown if updateChildren() is not called explicitly. + * + * @see RemoteObjectTreeItemUpdater + */ +class RemoteObjectTreeItem: public TreeItem { +Q_OBJECT +public: + + /** + * Creates a new RemoteObjectTreeItem for the given RemoteObject and with + * the given parent. + * + * @param remoteObject The RemoteObject to represent. + * @param parent The parent TreeItem. + */ + explicit RemoteObjectTreeItem(RemoteObject* remoteObject, + TreeItem* parent = 0); + + /** + * Returns "The class name (The class name)", or a placeholder if the names + * are empty or can not be got. + * + * @return The text for this TreeItem. + */ + virtual QString text() const; + + /** + * Returns the RemoteObject. + * + * @return The RemoteObject. + */ + RemoteObject* remoteObject() const; + + /** + * Sets the updater to register the children tree items in. + * + * @param updater The updater to register the children tree items in. + */ + void setUpdater(RemoteObjectTreeItemUpdater* updater); + + /** + * Updates the children tree items based on the current children of the + * remote object, adding or removing them as necessary. + */ + void updateChildren(); + +private: + + /** + * The RemoteObject. + */ + RemoteObject* mRemoteObject; + + /** + * The updater for this RemoteObjectTreeItem and all its children. + */ + RemoteObjectTreeItemUpdater* mUpdater; + + /** + * The name of the RemoteObject. + */ + QString mName; + + /** + * The class name of the RemoteObject. + */ + QString mClassName; + + /** + * The RemoteObjectTreeItems for each child RemoteObject in the + * RemoteObject. + */ + QList<RemoteObjectTreeItem*> mChildRemoteObjectTreeItems; + + /** + * Returns the RemoteObjectTreeItem for the given child RemoteObject. + * + * @param child The child RemoteObject to get its RemoteObjectTreeItem. + * @return The RemoteObjectTreeItem. + */ + RemoteObjectTreeItem* remoteObjectTreeItemForRemoteObject( + RemoteObject* child) const; + + /** + * Adds a new RemoteObjectTreeItem when a child RemoteObject is added in the + * RemoteObject. + * + * @param child The child RemoteObject added in the RemoteObject. + */ + void addChildRemoteObject(RemoteObject* child); + + /** + * Removes the RemoteObjectTreeItem for the child RemoteObject removed in + * the RemoteObject. + * + * @param child The child RemoteObject removed in the RemoteObject. + */ + void removeChildRemoteObject(RemoteObject* child); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectTreeItemUpdater.h" +#include "RemoteObjectTreeItem.h" +#include "../targetapplication/RemoteEventSpy.h" + +//public: + +RemoteObjectTreeItemUpdater::RemoteObjectTreeItemUpdater(QObject* parent): + QObject(parent) { +} + +void RemoteObjectTreeItemUpdater::setRemoteEventSpy( + RemoteEventSpy* remoteEventSpy) { + connect(remoteEventSpy, SIGNAL(eventReceived(RemoteObject*, QString)), + this, SLOT(handleEventReceived(RemoteObject*, QString))); +} + +void RemoteObjectTreeItemUpdater::registerRemoteObjectTreeItem( + RemoteObjectTreeItem* item) { + mRemoteObjectTreeItems.insert(item->remoteObject(), item); +} + +//private slots: + +void RemoteObjectTreeItemUpdater::handleEventReceived( + RemoteObject* remoteObject, + const QString& eventType) { + if (!mRemoteObjectTreeItems.contains(remoteObject)) { + return; + } + + if (!mRemoteObjectTreeItems.value(remoteObject)) { + //The tree item was deleted, so an entry is no longer needed for it + mRemoteObjectTreeItems.remove(remoteObject); + return; + } + + if (eventType != "ChildAdded" && eventType != "ChildRemoved") { + return; + } + + mRemoteObjectTreeItems.value(remoteObject)->updateChildren(); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.h 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTTREEITEMUPDATER_H +#define REMOTEOBJECTTREEITEMUPDATER_H + +#include <QHash> +#include <QObject> +#include <QPointer> + +class RemoteEventSpy; +class RemoteObject; +class RemoteObjectTreeItem; + +/** + * Helper class for RemoteObjectTreeItem to notify when it should update its + * children tree items. + * RemoteObjectTreeItems can not update their list of children tree items by + * themselves, as RemoteObject does not provide that information. The + * RemoteEventSpy has to be used to watch events in all the RemoteObjects and + * update the tree items when a ChildAdded or a ChildRemoved event is received. + * + * RemoteObjectTreeItems should not be registered directly with the updater. The + * updater must be set in the root RemoteObjectTreeItem to update. The tree item + * will take care of registering its children with the updater when necessary. + * Thus, the whole tree is updated, instead of only the registered item. + * + * @see RemoteObjectTreeItem + */ +class RemoteObjectTreeItemUpdater: public QObject { +Q_OBJECT +public: + + /** + * Creates a new RemoteObjectTreeItemUpdater. + * + * @param parent The parent object. + */ + explicit RemoteObjectTreeItemUpdater(QObject* parent = 0); + + /** + * Sets the RemoteEventSpy to check its events. + * + * @param remoteEventSpy The RemoteEventSpy. + */ + void setRemoteEventSpy(RemoteEventSpy* remoteEventSpy); + + /** + * Registers a new RemoteObjectTreeItem to be updated. + * + * @param remoteObjectTreeItem The RemoteObjectTreeItem to register. + */ + void registerRemoteObjectTreeItem( + RemoteObjectTreeItem* remoteObjectTreeItem); + +private: + + /** + * The RemoteObjectTreeItems to update, identified by their RemoteObject. + */ + QHash<RemoteObject*, QPointer<RemoteObjectTreeItem> > + mRemoteObjectTreeItems; + +private Q_SLOTS: + + /** + * If the event is a ChildAdded or a ChildRemoved, the RemoteObjectTreeItem + * that represents the remoteObject is told to update its children. + * + * @param remoteObject The RemoteObject to update. + * @param eventType The type of the event received. + */ + void handleEventReceived(RemoteObject* remoteObject, + const QString& eventType); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItemUpdater.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectTreeSelectionManager.h" + +#include "RemoteObjectTreeItem.h" + +//public: + +RemoteObjectTreeSelectionManager::RemoteObjectTreeSelectionManager( + QItemSelectionModel* itemSelectionModel, + QObject* parent): QObject(parent) { + connect(itemSelectionModel, + SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, + SLOT(handleSelectionChanged(QItemSelection, QItemSelection))); +} + +//private: + +void RemoteObjectTreeSelectionManager::updateRemoteObjectSelection( + TreeItem* selected, + TreeItem* deselected) { + RemoteObject* selectedRemoteObject = getRemoteObjectForTreeItem(selected); + RemoteObject* deselectedRemoteObject = getRemoteObjectForTreeItem( + deselected); + + if (selectedRemoteObject && + selectedRemoteObject != deselectedRemoteObject) { + emit remoteObjectSelected(selectedRemoteObject); + return; + } + + if (!selectedRemoteObject && deselectedRemoteObject) { + emit remoteObjectSelected(0); + return; + } +} + +RemoteObject* RemoteObjectTreeSelectionManager::getRemoteObjectForTreeItem( + TreeItem* item) { + if (qobject_cast<RemoteObjectTreeItem*>(item)) { + return static_cast<RemoteObjectTreeItem*>(item)->remoteObject(); + } + + if (item == 0 || item->parent() == 0) { + return 0; + } + + return getRemoteObjectForTreeItem(item->parent()); +} + +//private slots: + +void RemoteObjectTreeSelectionManager::handleSelectionChanged( + const QItemSelection& selected, + const QItemSelection& deselected) { + //Only single selections are supported + Q_ASSERT(selected.count() <= 1); + Q_ASSERT(deselected.count() <= 1); + + TreeItem* selectedItem = 0; + TreeItem* deselectedItem = 0; + + if (selected.count() == 1) { + Q_ASSERT(selected.at(0).indexes().count() == 1); + + QModelIndex index = selected.at(0).indexes().at(0); + selectedItem = static_cast<TreeItem*>(index.internalPointer()); + } + + if (deselected.count() == 1) { + Q_ASSERT(deselected.at(0).indexes().count() == 1); + + QModelIndex index = deselected.at(0).indexes().at(0); + deselectedItem = static_cast<TreeItem*>(index.internalPointer()); + } + + updateRemoteObjectSelection(selectedItem, deselectedItem); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTTREESELECTIONMANAGER_H +#define REMOTEOBJECTTREESELECTIONMANAGER_H + +#include <QItemSelectionModel> +#include <QObject> + +class RemoteObject; +class TreeItem; + +/** + * Watches the QItemSelectionModel of a TreeModel composed by + * RemoteObjectTreeItems for changes in the selection. + * When an item is selected in the TreeModel, + * remoteObjectSelected(RemoteObject*) signal is emitted with the RemoteObject + * represented by the selected item. + * + * Only single item selections are supported. + */ +class RemoteObjectTreeSelectionManager: public QObject { +Q_OBJECT +public: + + /** + * Creates a new RemoteObjectTreeSelectionManager that watchs the given + * selection model. + * + * @param itemSelectionModel The selection model to watch for changes in the + * selection. + * @param parent The parent object. + */ + explicit RemoteObjectTreeSelectionManager( + QItemSelectionModel* itemSelectionModel, QObject* parent = 0); + +Q_SIGNALS: + + /** + * Emitted when a RemoteObject is selected. + * If the RemoteObject is deselected and the new selected item isn't a + * RemoteObject, the signal is emitted with a null pointer. + * + * @param remoteObject The selected RemoteObject, or null if it was + * deselected. + */ + void remoteObjectSelected(RemoteObject* remoteObject); + +private: + + /** + * Emits remoteObjectSelected(RemoteObject*) signal based on the selected + * and deselected items. + * + * @param selected The selected item, if any. + * @param deselected The deselected item, if any. + */ + void updateRemoteObjectSelection(TreeItem* selected, TreeItem* deselected); + + /** + * Returns the RemoteObject represented by the given item. + * If the item doesn't represent a RemoteObject or there is no item, a null + * pointer is returned. + * + * @param item The item to get its represented RemoteObject. + * @return The RemoteObject. + */ + RemoteObject* getRemoteObjectForTreeItem(TreeItem* item); + +private Q_SLOTS: + + /** + * Handles a change in the selection in the watched selection model. + * Signals are emitted as needed based on the selected and deselected items. + * + * @param selected The item selection of selected items. + * @param selected The item selection of deselected items. + */ + void handleSelectionChanged(const QItemSelection& selected, + const QItemSelection& deselected); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/TargetApplicationView.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/TargetApplicationView.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/TargetApplicationView.cpp 2010-04-13 20:30:16 UTC (rev 231) @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "TargetApplicationView.h" + +#include <QApplication> +#include <QScopedPointer> + +#include <KFileDialog> +#include <KFileFilterCombo> +#include <KLocalizedString> +#include <KMessageBox> + +//public: + +TargetApplicationView::TargetApplicationView( + TargetApplication* targetApplication, QWidget* parent): + QObject(parent), + mTargetApplication(targetApplication), + mParent(parent) { +} + +void TargetApplicationView::start() { + if (mTargetApplication->remoteEditorSupport()) { + return; + } + + if (mTargetApplication->targetApplicationFilePath().isEmpty()) { + QString path = askApplicationFilePath(); + + if (path.isEmpty()) { + return; + } + + ... [truncated message content] |
From: <dan...@us...> - 2010-04-14 01:14:33
|
Revision: 232 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=232&view=rev Author: danxuliu Date: 2010-04-14 01:14:27 +0000 (Wed, 14 Apr 2010) Log Message: ----------- Move Exception from root to "util" package to avoid serialization and targetapplication packages linking against the root library. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h trunk/ktutorial/ktutorial-editor/src/serialization/IOException.h trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/util/ trunk/ktutorial/ktutorial-editor/src/util/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/util/Exception.cpp trunk/ktutorial/ktutorial-editor/src/util/Exception.h trunk/ktutorial/ktutorial-editor/tests/unit/util/ trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/util/ExceptionTest.cpp Removed Paths: ------------- trunk/ktutorial/ktutorial-editor/src/Exception.cpp trunk/ktutorial/ktutorial-editor/src/Exception.h trunk/ktutorial/ktutorial-editor/tests/unit/ExceptionTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) @@ -7,6 +7,7 @@ add_subdirectory(commands) add_subdirectory(data) add_subdirectory(serialization) +add_subdirectory(util) add_subdirectory(view) if (QT_QTDBUS_FOUND) @@ -16,7 +17,6 @@ set(ktutorial_editor_SRCS EditActions.cpp - Exception.cpp FileActions.cpp KTutorialEditor.cpp ) @@ -30,6 +30,7 @@ target_link_libraries(ktutorial_editor ktutorial_editor_data ktutorial_editor_serialization + ktutorial_editor_util ktutorial_editor_view ) Deleted: trunk/ktutorial/ktutorial-editor/src/Exception.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/Exception.cpp 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/Exception.cpp 2010-04-14 01:14:27 UTC (rev 232) @@ -1,36 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * - * dan...@gm... * - * * - * 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; either version 3 of the License, or * - * (at your option) any later version. * - * * - * 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, see <http://www.gnu.org/licenses/>. * - ***************************************************************************/ - -#include "Exception.h" - -//public: - -Exception::Exception(const QString& message): std::exception(), - mMessage(message) { -} - -Exception::~Exception() throw() { -} - -const char* Exception::what() const throw() { - return mMessage.toUtf8(); -} - -QString Exception::message() const throw() { - return mMessage; -} Deleted: trunk/ktutorial/ktutorial-editor/src/Exception.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/Exception.h 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/Exception.h 2010-04-14 01:14:27 UTC (rev 232) @@ -1,54 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * - * dan...@gm... * - * * - * 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; either version 3 of the License, or * - * (at your option) any later version. * - * * - * 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, see <http://www.gnu.org/licenses/>. * - ***************************************************************************/ - -#ifndef EXCEPTION_H -#define EXCEPTION_H - -#include <exception> -#include <QString> - -/** - * Base class for exceptions. - */ -class Exception: public std::exception { -public: - - explicit Exception(const QString& message = QString()); - virtual ~Exception() throw(); - - /** - * Returns the exception message. - * - * @return The exception message. - */ - virtual const char* what() const throw(); - - /** - * Returns the exception message. - * - * @return The exception message. - */ - QString message() const throw(); - -private: - - QString mMessage; - -}; - -#endif Modified: trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/serialization/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) @@ -9,4 +9,8 @@ kde4_add_library(ktutorial_editor_serialization ${ktutorial_editor_serialization_SRCS}) -target_link_libraries(ktutorial_editor_serialization ktutorial_editor ${KDE4_KIO_LIBS}) +target_link_libraries(ktutorial_editor_serialization + ktutorial_editor_data + ktutorial_editor_util + ${KDE4_KIO_LIBS} +) Modified: trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/serialization/DeserializationException.h 2010-04-14 01:14:27 UTC (rev 232) @@ -19,7 +19,7 @@ #ifndef DESERIALIZATIONEXCEPTION_H #define DESERIALIZATIONEXCEPTION_H -#include "../Exception.h" +#include "../util/Exception.h" /** * Thrown when the XML can't be deserialized (for example, when it isn't well Modified: trunk/ktutorial/ktutorial-editor/src/serialization/IOException.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/IOException.h 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/serialization/IOException.h 2010-04-14 01:14:27 UTC (rev 232) @@ -19,7 +19,7 @@ #ifndef IOEXCEPTION_H #define IOEXCEPTION_H -#include "../Exception.h" +#include "../util/Exception.h" /** * Thrown when an input/ouput operation fails (for example, writing to an Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) @@ -9,4 +9,8 @@ kde4_add_library(ktutorial_editor_targetapplication ${ktutorial_editor_targetapplication_SRCS}) -target_link_libraries(ktutorial_editor_targetapplication ktutorial_editor ${QT_QTDBUS_LIBRARY} ${KDE4_KDECORE_LIBS}) +target_link_libraries(ktutorial_editor_targetapplication + ktutorial_editor_util + ${QT_QTDBUS_LIBRARY} + ${KDE4_KDECORE_LIBS} +) Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/DBusException.h 2010-04-14 01:14:27 UTC (rev 232) @@ -19,7 +19,7 @@ #ifndef DBUSEXCEPTION_H #define DBUSEXCEPTION_H -#include "../Exception.h" +#include "../util/Exception.h" /** * Thrown when a DBus error happened. Added: trunk/ktutorial/ktutorial-editor/src/util/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/util/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/util/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) @@ -0,0 +1,5 @@ +set(ktutorial_editor_util_SRCS + Exception.cpp +) + +kde4_add_library(ktutorial_editor_util ${ktutorial_editor_util_SRCS}) Property changes on: trunk/ktutorial/ktutorial-editor/src/util/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Copied: trunk/ktutorial/ktutorial-editor/src/util/Exception.cpp (from rev 198, trunk/ktutorial/ktutorial-editor/src/Exception.cpp) =================================================================== --- trunk/ktutorial/ktutorial-editor/src/util/Exception.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/util/Exception.cpp 2010-04-14 01:14:27 UTC (rev 232) @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "Exception.h" + +//public: + +Exception::Exception(const QString& message): std::exception(), + mMessage(message) { +} + +Exception::~Exception() throw() { +} + +const char* Exception::what() const throw() { + return mMessage.toUtf8(); +} + +QString Exception::message() const throw() { + return mMessage; +} Copied: trunk/ktutorial/ktutorial-editor/src/util/Exception.h (from rev 198, trunk/ktutorial/ktutorial-editor/src/Exception.h) =================================================================== --- trunk/ktutorial/ktutorial-editor/src/util/Exception.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/util/Exception.h 2010-04-14 01:14:27 UTC (rev 232) @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include <exception> +#include <QString> + +/** + * Base class for exceptions. + */ +class Exception: public std::exception { +public: + + explicit Exception(const QString& message = QString()); + virtual ~Exception() throw(); + + /** + * Returns the exception message. + * + * @return The exception message. + */ + virtual const char* what() const throw(); + + /** + * Returns the exception message. + * + * @return The exception message. + */ + QString message() const throw(); + +private: + + QString mMessage; + +}; + +#endif Modified: trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/tests/unit/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) @@ -4,31 +4,5 @@ if (QT_QTDBUS_FOUND) add_subdirectory(targetapplication) endif (QT_QTDBUS_FOUND) +add_subdirectory(util) add_subdirectory(view) - -# Used by kde4_add_unit_test to set the full path to test executables -set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) - -include_directories(${ktutorial-editor_SOURCE_DIR}/src/ ${KDE4_INCLUDES}) - -MACRO(UNIT_TESTS) - FOREACH(_className ${ARGN}) - set(_testName ${_className}Test) - kde4_add_unit_test(${_testName} TESTNAME ktutorial-editor-unit-${_testName} ${_testName}.cpp) - target_link_libraries(${_testName} ktutorial_editor ${QT_QTTEST_LIBRARY}) - ENDFOREACH(_className) -ENDMACRO(UNIT_TESTS) - -unit_tests( - Exception -) - -MACRO(MEM_TESTS) - FOREACH(_testname ${ARGN}) - add_test(ktutorial-editor-unit-mem-${_testname} ${CMAKE_CURRENT_SOURCE_DIR}/runMemcheck.py ${CMAKE_CURRENT_BINARY_DIR}/${_testname}Test ${CMAKE_CURRENT_BINARY_DIR}) - ENDFOREACH(_testname) -ENDMACRO(MEM_TESTS) - -mem_tests( - Exception -) Deleted: trunk/ktutorial/ktutorial-editor/tests/unit/ExceptionTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/ExceptionTest.cpp 2010-04-13 20:30:16 UTC (rev 231) +++ trunk/ktutorial/ktutorial-editor/tests/unit/ExceptionTest.cpp 2010-04-14 01:14:27 UTC (rev 232) @@ -1,49 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * - * dan...@gm... * - * * - * 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; either version 3 of the License, or * - * (at your option) any later version. * - * * - * 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, see <http://www.gnu.org/licenses/>. * - ***************************************************************************/ - -#include <QtTest> - -#include "Exception.h" - -class ExceptionTest: public QObject { -Q_OBJECT - -private slots: - - void testConstructor(); - void testConstructorEmpty(); - -}; - -void ExceptionTest::testConstructor() { - Exception exception(QString("The message")); - - QCOMPARE(exception.what(), "The message"); - QCOMPARE(exception.message(), QString("The message")); -} - -void ExceptionTest::testConstructorEmpty() { - Exception exception; - - QCOMPARE(exception.what(), ""); - QCOMPARE(exception.message(), QString("")); -} - -QTEST_MAIN(ExceptionTest) - -#include "ExceptionTest.moc" Added: trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) @@ -0,0 +1,26 @@ +# Used by kde4_add_unit_test to set the full path to test executables +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${ktutorial-editor_SOURCE_DIR}/src/util ${KDE4_INCLUDES}) + +MACRO(UNIT_TESTS) + FOREACH(_className ${ARGN}) + set(_testName ${_className}Test) + kde4_add_unit_test(${_testName} TESTNAME ktutorial-editor-unit-${_testName} ${_testName}.cpp) + target_link_libraries(${_testName} ktutorial_editor_util ${QT_QTTEST_LIBRARY}) + ENDFOREACH(_className) +ENDMACRO(UNIT_TESTS) + +unit_tests( + Exception +) + +MACRO(MEM_TESTS) + FOREACH(_testname ${ARGN}) + add_test(ktutorial-editor-unit-mem-${_testname} ${CMAKE_CURRENT_SOURCE_DIR}/../runMemcheck.py ${CMAKE_CURRENT_BINARY_DIR}/${_testname}Test ${CMAKE_CURRENT_BINARY_DIR}) + ENDFOREACH(_testname) +ENDMACRO(MEM_TESTS) + +mem_tests( + Exception +) Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/util/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Copied: trunk/ktutorial/ktutorial-editor/tests/unit/util/ExceptionTest.cpp (from rev 198, trunk/ktutorial/ktutorial-editor/tests/unit/ExceptionTest.cpp) =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/util/ExceptionTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/util/ExceptionTest.cpp 2010-04-14 01:14:27 UTC (rev 232) @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "Exception.h" + +class ExceptionTest: public QObject { +Q_OBJECT + +private slots: + + void testConstructor(); + void testConstructorEmpty(); + +}; + +void ExceptionTest::testConstructor() { + Exception exception(QString("The message")); + + QCOMPARE(exception.what(), "The message"); + QCOMPARE(exception.message(), QString("The message")); +} + +void ExceptionTest::testConstructorEmpty() { + Exception exception; + + QCOMPARE(exception.what(), ""); + QCOMPARE(exception.message(), QString("")); +} + +QTEST_MAIN(ExceptionTest) + +#include "ExceptionTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-04-25 23:10:51
|
Revision: 234 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=234&view=rev Author: danxuliu Date: 2010-04-25 23:10:44 +0000 (Sun, 25 Apr 2010) Log Message: ----------- -Add RemoteClass to act as a proxy for class information provided by the target application. -Provide completion in the signal line edit in WaitForSignalWidget with the signals of the chosen RemoteObject. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEventSpyTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationStub.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/CMakeLists.txt 2010-04-25 23:10:44 UTC (rev 234) @@ -1,5 +1,6 @@ set(ktutorial_editor_targetapplication_SRCS DBusException.cpp + RemoteClass.cpp RemoteEditorSupport.cpp RemoteEventSpy.cpp RemoteObject.cpp Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteClass.h" + +#include <QtDBus/QtDBus> + +#include "RemoteObjectMapper.h" + +//public: + +RemoteClass::RemoteClass(const QString& service, RemoteObjectMapper* mapper, + const QString& className): + QDBusAbstractInterface(service, "/ktutorial/ObjectRegister", + "org.kde.ktutorial.ClassRegister", + QDBusConnection::sessionBus(), 0), + mMapper(mapper), + mClassName(className) { +} + +QString RemoteClass::className() const { + return mClassName; +} + +RemoteClass* RemoteClass::superClass() throw (DBusException) { + QDBusReply<QString> reply = call("superClass", mClassName); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + if (reply.value().isEmpty()) { + return 0; + } + + return mMapper->remoteClass(reply.value()); +} + +QStringList RemoteClass::signalList() throw (DBusException) { + QDBusReply<QStringList> reply = call("signalList", mClassName); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } + + return reply.value(); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.h 2010-04-25 23:10:44 UTC (rev 234) @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTECLASS_H +#define REMOTECLASS_H + +#include <QDBusAbstractInterface> + +#include "DBusException.h" + +class RemoteObjectMapper; + +/** + * Proxy for remote classes exposed by KTutorial editor support module in + * KTutorial library. + * RemoteClass represents a remote class exposed by ObjectRegister in KTutorial + * library through DBus. Its purpose is provide an API to use the remote + * class/QMetaObject like any other local object, hiding the DBus complexity + * behind it. + * + * To get the data, RemoteClass makes DBus calls to the + * "org.kde.ktutorial.ClassRegistry" interface in the DBus service specified in + * the constructor. + * + * Although the idea is let other objects use it like a local object, it has to + * communicate with the remote ObjectRegistry through DBus anyway, so the + * methods may throw a DBusException if something goes wrong. + */ +class RemoteClass: public QDBusAbstractInterface { +Q_OBJECT +public: + + /** + * Creates a new RemoteClass to represent the remote class with the given + * class name in the given DBus service name. + * + * @param service The DBus service name. + * @param mapper The RemoteObjectMapper to get RemoteClasses from. + * @param className The name of the remote class. + */ + RemoteClass(const QString& service, RemoteObjectMapper* mapper, + const QString& className); + + /** + * Returns the class name. + * + * @return The class name. + */ + QString className() const; + + /** + * Returns the remote super class. + * If it has no super class, a null pointer is returned. + * + * @return The remote super class. + * @throws DBusException If a DBus error happens. + */ + RemoteClass* superClass() throw (DBusException); + + /** + * Returns a list with the signals defined in the remote class. + * The list only includes the signals from the class itself, but not its + * super classes. + * + * @return The signals. + * @throws DBusException If a DBus error happens. + */ + QStringList signalList() throw (DBusException); + +private: + + /** + * The mapper that associates a RemoteClass with its name. + */ + RemoteObjectMapper* mMapper; + + /** + * The name of the remote class. + */ + QString mClassName; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteClass.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -46,13 +46,13 @@ return reply.value(); } -QString RemoteObject::className() throw (DBusException) { +RemoteClass* RemoteObject::remoteClass() throw (DBusException) { QDBusReply<QString> reply = call("className", mObjectId); if (!reply.isValid()) { throw DBusException(reply.error().message()); } - return reply.value(); + return mMapper->remoteClass(reply.value()); } Q_DECLARE_METATYPE(QList<int>) Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObject.h 2010-04-25 23:10:44 UTC (rev 234) @@ -23,6 +23,7 @@ #include "DBusException.h" +class RemoteClass; class RemoteObjectMapper; /** @@ -72,12 +73,12 @@ QString name() throw (DBusException); /** - * Returns the class name. + * Returns the remote class. * - * @return The class name. + * @return The remote class. * @throws DBusException If a DBus error happens. */ - QString className() throw (DBusException); + RemoteClass* remoteClass() throw (DBusException); /** * Returns a list with the RemoteObjects that represent the children of this Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -20,6 +20,7 @@ #include <QHash> +#include "RemoteClass.h" #include "RemoteObject.h" //public: @@ -30,6 +31,7 @@ RemoteObjectMapper::~RemoteObjectMapper() { qDeleteAll(mRemoteObjects); + qDeleteAll(mRemoteClasses); } RemoteObject* RemoteObjectMapper::remoteObject(int objectId) { @@ -43,7 +45,21 @@ return remoteObject; } +RemoteClass* RemoteObjectMapper::remoteClass(const QString& className) { + if (mRemoteClasses.contains(className)) { + return mRemoteClasses.value(className); + } + + RemoteClass* remoteClass = new RemoteClass(mService, this, className); + mRemoteClasses.insert(className, remoteClass); + + return remoteClass; +} + void RemoteObjectMapper::clear() { qDeleteAll(mRemoteObjects); mRemoteObjects.clear(); + + qDeleteAll(mRemoteClasses); + mRemoteClasses.clear(); } Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteObjectMapper.h 2010-04-25 23:10:44 UTC (rev 234) @@ -21,17 +21,18 @@ #include <QHash> +class RemoteClass; class RemoteObject; /** - * Map to get a RemoteObject from their objectId. - * The RemoteObjectMapper should be used to get all the RemoteObjects for its - * DBus service. It creates a new RemoteObject when there is no RemoteObject - * for the given id, or returns the previosly created one, depending on the - * case. + * Map to get RemoteObjects and RemoteClasses from their objectId or class name. + * The RemoteObjectMapper should be used to get all the RemoteObjects and + * RemoteClasses for its DBus service. It creates a new RemoteObject when there + * is no RemoteObject for the given id, or returns the previosly created one, + * depending on the case. The same behavior is shown for RemoteClasses. * - * The RemoteObjectMapper also has ownership of the RemoteObjects, so they are - * deleted when the mapper is cleared or destroyed. + * The RemoteObjectMapper also has ownership of the RemoteObjects and + * RemoteClasses, so they are deleted when the mapper is cleared or destroyed. */ class RemoteObjectMapper { public: @@ -39,13 +40,13 @@ /** * Creates a new RemoteObjectMapper for the given DBus service name. * - * @param service The DBus service name of the remote objects. + * @param service The DBus service name of the remote objects and classes. */ RemoteObjectMapper(const QString& service); /** * Destroys this RemoteObjectMapper. - * All the mapped RemoteObjects are also destroyed. + * All the mapped RemoteObjects and RemoteClasses are also destroyed. */ ~RemoteObjectMapper(); @@ -60,8 +61,18 @@ RemoteObject* remoteObject(int objectId); /** - * Destroys all the mapped RemoteObject. + * Returns the RemoteClass associated with the given class name. + * The RemoteClass is destroyed when this RemoteObjectMapper is cleared or + * destroyed, so consider using QPointer to store it. + * + * @param className The name of the remote class. + * @return The RemoteClass. */ + RemoteClass* remoteClass(const QString& className); + + /** + * Destroys all the mapped RemoteObjects and RemoteClasses. + */ void clear(); private: @@ -77,6 +88,12 @@ */ QHash<int, RemoteObject*> mRemoteObjects; + /** + * All the RemoteClasses already requested since the last time this + * RemoteObjectMapper was cleared. + */ + QHash<QString, RemoteClass*> mRemoteClasses; + }; #endif Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeItem.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -22,6 +22,7 @@ #include <KLocalizedString> #include "RemoteObjectTreeItemUpdater.h" +#include "../targetapplication/RemoteClass.h" #include "../targetapplication/RemoteObject.h" //public: @@ -35,7 +36,7 @@ try { mName = remoteObject->name(); - mClassName = remoteObject->className(); + mClassName = remoteObject->remoteClass()->className(); } catch (DBusException e) { mName = i18nc("@item", "D-Bus Error!"); mClassName = i18nc("@item", "D-Bus Error!"); Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -25,6 +25,7 @@ #include <KMessageBox> #include "RemoteObjectChooser.h" +#include "../targetapplication/RemoteClass.h" #include "../targetapplication/RemoteObject.h" #endif @@ -67,6 +68,24 @@ } } +//private: + +#ifdef QT_QTDBUS_FOUND +void WaitForSignalWidget::setSignalCompletion(RemoteClass* remoteClass) { + KCompletion* completion = ui->signalNameLineEdit->completionObject(); + completion->clear(); + completion->setOrder(KCompletion::Sorted); + + while (remoteClass) { + foreach (QString signal, remoteClass->signalList()) { + completion->addItem(signal); + } + + remoteClass = remoteClass->superClass(); + } +} +#endif + //private slots: #ifdef QT_QTDBUS_FOUND @@ -81,6 +100,7 @@ void WaitForSignalWidget::setChosenRemoteObject(RemoteObject* remoteObject) { try { ui->emitterNameLineEdit->setText(remoteObject->name()); + setSignalCompletion(remoteObject->remoteClass()); } catch (DBusException e) { QString text = i18nc("@label", "The emitter name can not be set, there " "was a problem getting the name from the target application: %1", e.message()); Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h 2010-04-25 23:10:44 UTC (rev 234) @@ -21,6 +21,7 @@ #include "EditionWidget.h" +class RemoteClass; class RemoteObject; class WaitForSignal; @@ -66,6 +67,16 @@ */ Ui::WaitForSignalWidget* ui; +#ifdef QT_QTDBUS_FOUND + /** + * Sets the completion of the signal line edit to the signals emitted by the + * given remote class and its super classes. + * + * @param remoteClass The class to get its signal list. + */ + void setSignalCompletion(RemoteClass* remoteClass); +#endif + private Q_SLOTS: #ifdef QT_QTDBUS_FOUND Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/CMakeLists.txt 2010-04-25 23:10:44 UTC (rev 234) @@ -24,6 +24,7 @@ unit_tests( DBusException + RemoteClass RemoteEditorSupport RemoteEventSpy RemoteObject @@ -39,6 +40,7 @@ mem_tests( DBusException + RemoteClass RemoteEditorSupport RemoteEventSpy RemoteObject Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2010-04-25 23:10:44 UTC (rev 234) @@ -41,16 +41,45 @@ }; -class StubObjectRegister: public QObject { +class StubClassRegisterAdaptor: public QDBusAbstractAdaptor { Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.kde.ktutorial.ClassRegister") +public: + + StubClassRegisterAdaptor(QObject* parent): QDBusAbstractAdaptor(parent) { + } + +public slots: + + QString superClass(const QString& className) { + if (className.startsWith("Child")) { + return className.mid(QString("Child").count()); + } + + return ""; + } + + QStringList signalList(const QString& className) { + QStringList signalList; + for (int i=0; i<3; ++i) { + signalList.append(className + "Signal" + QString::number(i) + "()"); + } + + return signalList; + } +}; + +class StubObjectRegisterAdaptor: public QDBusAbstractAdaptor { +Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.ktutorial.ObjectRegister") public: - StubObjectRegister(QObject* parent = 0): QObject(parent) { + StubObjectRegisterAdaptor(QObject* parent): QDBusAbstractAdaptor(parent) { } public slots: + QString objectName(int objectId) { if (objectId > 100) { return ""; @@ -78,7 +107,19 @@ } return ids; } +}; +//Only one Q_CLASSINFO("D-Bus Interface", "whatever") is supported in +//each class, so adaptors have to be used to represent several interfaces +class StubObjectRegister: public QObject { +Q_OBJECT +public: + + StubObjectRegister(QObject* parent = 0): QObject(parent) { + new StubClassRegisterAdaptor(this); + new StubObjectRegisterAdaptor(this); + } + }; class StubEditorSupport: public QObject { Added: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassTest.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "RemoteClass.h" + +#include <QtDBus/QtDBus> + +#include "RemoteClassStubs.h" +#include "RemoteObjectMapper.h" + +#define EXPECT_EXCEPTION(statement, exception) \ +do {\ + try {\ + statement;\ + QFAIL("Expected " #exception " not thrown");\ + } catch (exception e) {\ + } catch (Exception e) {\ + QFAIL("Expected " #exception " not thrown");\ + }\ +} while (0) + +class RemoteClassTest: public QObject { +Q_OBJECT + +private slots: + + void init(); + void cleanup(); + + void testClassName(); + + void testSuperClass(); + void testSuperClassWhenRemoteClassIsNotAvailable(); + + void testSignalList(); + void testSignalListWhenRemoteClassIsNotAvailable(); + +private: + + StubObjectRegister* mObjectRegister; + + QString mService; + RemoteObjectMapper* mMapper; + +}; + +void RemoteClassTest::init() { + QVERIFY(QDBusConnection::sessionBus().isConnected()); + + mObjectRegister = new StubObjectRegister(); + QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", + mObjectRegister, QDBusConnection::ExportAdaptors); + + mService = QDBusConnection::sessionBus().baseService(); + mMapper = new RemoteObjectMapper(mService); +} + +void RemoteClassTest::cleanup() { + delete mMapper; + + QDBusConnection::sessionBus().unregisterObject("/ktutorial/ObjectRegister"); + delete mObjectRegister; +} + +void RemoteClassTest::testClassName() { + RemoteClass remoteClass(mService, mMapper, "The class name"); + + QCOMPARE(remoteClass.className(), QString("The class name")); +} + +void RemoteClassTest::testSuperClass() { + RemoteClass remoteClass(mService, mMapper, "ChildClass"); + + QCOMPARE(remoteClass.superClass()->className(), QString("Class")); + QCOMPARE(remoteClass.superClass()->superClass(), (RemoteClass*)0); +} + +void RemoteClassTest::testSuperClassWhenRemoteClassIsNotAvailable() { + RemoteClass remoteClass(mService, mMapper, "Class"); + + QDBusConnection::sessionBus().unregisterObject("/ktutorial/ObjectRegister"); + + EXPECT_EXCEPTION(remoteClass.superClass(), DBusException); +} + +void RemoteClassTest::testSignalList() { + RemoteClass remoteClass(mService, mMapper, "Class"); + + QStringList signalList = remoteClass.signalList(); + QCOMPARE(signalList.count(), 3); + QCOMPARE(signalList[0], QString("ClassSignal0()")); + QCOMPARE(signalList[1], QString("ClassSignal1()")); + QCOMPARE(signalList[2], QString("ClassSignal2()")); +} + +void RemoteClassTest::testSignalListWhenRemoteClassIsNotAvailable() { + RemoteClass remoteClass(mService, mMapper, "Class"); + + QDBusConnection::sessionBus().unregisterObject("/ktutorial/ObjectRegister"); + + EXPECT_EXCEPTION(remoteClass.signalList(), DBusException); +} + +QTEST_MAIN(RemoteClassTest) + +#include "RemoteClassTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -80,7 +80,7 @@ mObjectRegister = new StubObjectRegister(); QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", - mObjectRegister, QDBusConnection::ExportAllSlots); + mObjectRegister, QDBusConnection::ExportAdaptors); } void RemoteEditorSupportTest::cleanup() { Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEventSpyTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEventSpyTest.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEventSpyTest.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -52,7 +52,7 @@ mObjectRegister = new StubObjectRegister(); QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", - mObjectRegister, QDBusConnection::ExportAllSlots); + mObjectRegister, QDBusConnection::ExportAdaptors); } void RemoteEventSpyTest::cleanup() { Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectMapperTest.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -22,6 +22,7 @@ #include <QtDBus/QtDBus> +#include "RemoteClass.h" #include "RemoteClassStubs.h" #include "RemoteObject.h" @@ -37,6 +38,10 @@ void testRemoteObjectSeveralIds(); void testRemoteObjectTwice(); + void testRemoteClass(); + void testRemoteClassSeveralIds(); + void testRemoteClassTwice(); + void testClear(); private: @@ -49,7 +54,7 @@ mObjectRegister = new StubObjectRegister(); QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", - mObjectRegister, QDBusConnection::ExportAllSlots); + mObjectRegister, QDBusConnection::ExportAdaptors); } void RemoteObjectMapperTest::cleanup() { @@ -96,20 +101,60 @@ QCOMPARE(remoteObject1->name(), QString("The object name 42")); } +void RemoteObjectMapperTest::testRemoteClass() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + + RemoteClass* remoteClass = mapper.remoteClass("Class"); + + QVERIFY(remoteClass); + QCOMPARE(remoteClass->className(), QString("Class")); +} + +void RemoteObjectMapperTest::testRemoteClassSeveralIds() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + + RemoteClass* remoteClass1 = mapper.remoteClass("Class1"); + RemoteClass* remoteClass2 = mapper.remoteClass("Class2"); + RemoteClass* remoteClass3 = mapper.remoteClass("Class3"); + + QVERIFY(remoteClass1); + QVERIFY(remoteClass2); + QVERIFY(remoteClass3); + QVERIFY(remoteClass1 != remoteClass2); + QVERIFY(remoteClass1 != remoteClass3); + QVERIFY(remoteClass2 != remoteClass3); + QCOMPARE(remoteClass1->className(), QString("Class1")); + QCOMPARE(remoteClass2->className(), QString("Class2")); + QCOMPARE(remoteClass3->className(), QString("Class3")); +} + +void RemoteObjectMapperTest::testRemoteClassTwice() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + + RemoteClass* remoteClass1 = mapper.remoteClass("Class"); + RemoteClass* remoteClass2 = mapper.remoteClass("Class"); + + QVERIFY(remoteClass1); + QVERIFY(remoteClass2); + QVERIFY(remoteClass1 == remoteClass2); + QCOMPARE(remoteClass1->className(), QString("Class")); +} + void RemoteObjectMapperTest::testClear() { RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); - RemoteObject* remoteObject1 = mapper.remoteObject(42); + QPointer<RemoteObject> remoteObject1 = mapper.remoteObject(42); + QPointer<RemoteClass> remoteClass1 = mapper.remoteClass("Class"); mapper.clear(); RemoteObject* remoteObject2 = mapper.remoteObject(42); + RemoteClass* remoteClass2 = mapper.remoteClass("Class"); - QVERIFY(remoteObject1); + QVERIFY(!remoteObject1); QVERIFY(remoteObject2); - QVERIFY(remoteObject1 != remoteObject2); - //remoteObject1 values can not be checked, as it was destroyed when the - //mapper was cleared + QVERIFY(!remoteClass1); + QVERIFY(remoteClass2); } QTEST_MAIN(RemoteObjectMapperTest) Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -22,6 +22,7 @@ #include <QtDBus/QtDBus> +#include "RemoteClass.h" #include "RemoteClassStubs.h" #include "RemoteObjectMapper.h" @@ -49,8 +50,8 @@ void testName(); void testNameWhenRemoteObjectIsNotAvailable(); - void testClassName(); - void testClassNameWhenRemoteObjectIsNotAvailable(); + void testRemoteClass(); + void testRemoteClassWhenRemoteObjectIsNotAvailable(); void testChildren(); void testChildrenWhenRemoteObjectIsNotAvailable(); @@ -69,7 +70,7 @@ mObjectRegister = new StubObjectRegister(); QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", - mObjectRegister, QDBusConnection::ExportAllSlots); + mObjectRegister, QDBusConnection::ExportAdaptors); mService = QDBusConnection::sessionBus().baseService(); mMapper = new RemoteObjectMapper(mService); @@ -102,18 +103,19 @@ EXPECT_EXCEPTION(remoteObject.name(), DBusException); } -void RemoteObjectTest::testClassName() { +void RemoteObjectTest::testRemoteClass() { RemoteObject remoteObject(mService, mMapper, 42); - QCOMPARE(remoteObject.className(), QString("The class name 42")); + QCOMPARE(remoteObject.remoteClass()->className(), + QString("The class name 42")); } -void RemoteObjectTest::testClassNameWhenRemoteObjectIsNotAvailable() { +void RemoteObjectTest::testRemoteClassWhenRemoteObjectIsNotAvailable() { RemoteObject remoteObject(mService, mMapper, 42); QDBusConnection::sessionBus().unregisterObject("/ktutorial/ObjectRegister"); - EXPECT_EXCEPTION(remoteObject.className(), DBusException); + EXPECT_EXCEPTION(remoteObject.remoteClass(), DBusException); } void RemoteObjectTest::testChildren() { Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationStub.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationStub.cpp 2010-04-25 23:01:54 UTC (rev 233) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationStub.cpp 2010-04-25 23:10:44 UTC (rev 234) @@ -36,7 +36,7 @@ StubObjectRegister* objectRegister = new StubObjectRegister(); QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", - objectRegister, QDBusConnection::ExportAllSlots); + objectRegister, QDBusConnection::ExportAdaptors); return app.exec(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-04-26 07:08:06
|
Revision: 236 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=236&view=rev Author: danxuliu Date: 2010-04-26 05:24:17 +0000 (Mon, 26 Apr 2010) Log Message: ----------- Add completion for event names in WaitForEventWidget. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp 2010-04-25 23:15:13 UTC (rev 235) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp 2010-04-26 05:24:17 UTC (rev 236) @@ -18,6 +18,8 @@ #include "WaitForEventWidget.h" +#include <QMetaEnum> + #include "ui_WaitForEventWidget.h" #include "../data/WaitForEvent.h" @@ -47,6 +49,8 @@ ui->receiverNameLineEdit->setText(waitForEvent->receiverName()); ui->eventNameLineEdit->setText(waitForEvent->eventName()); + + addItemsToEventNameCompletion(); } WaitForEventWidget::~WaitForEventWidget() { @@ -65,6 +69,18 @@ } } +//private: + +void WaitForEventWidget::addItemsToEventNameCompletion() { + int index = QEvent::staticMetaObject.indexOfEnumerator("Type"); + QMetaEnum eventTypeEnumerator = QEvent::staticMetaObject.enumerator(index); + + KCompletion* completion = ui->eventNameLineEdit->completionObject(); + for (int i=0; i<eventTypeEnumerator.keyCount(); ++i) { + completion->addItem(eventTypeEnumerator.key(i)); + } +} + //private slots: #ifdef QT_QTDBUS_FOUND Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h 2010-04-25 23:15:13 UTC (rev 235) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h 2010-04-26 05:24:17 UTC (rev 236) @@ -66,6 +66,12 @@ */ Ui::WaitForEventWidget* ui; + /** + * Adds all the event names to the completion object of the event name line + * edit. + */ + void addItemsToEventNameCompletion(); + private Q_SLOTS: #ifdef QT_QTDBUS_FOUND Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp 2010-04-25 23:15:13 UTC (rev 235) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp 2010-04-26 05:24:17 UTC (rev 236) @@ -33,6 +33,8 @@ void testSaveChanges(); + void testEventNameCompletion(); + private: KLineEdit* receiverNameLineEdit(WaitForEventWidget* widget) const; @@ -69,6 +71,16 @@ QCOMPARE(waitFor.eventName(), QString("The new event name")); } +void WaitForEventWidgetTest::testEventNameCompletion() { + WaitForEvent waitFor; + + WaitForEventWidget widget(&waitFor); + + KCompletion* completion = eventNameLineEdit(&widget)->completionObject(); + QCOMPARE(completion->allMatches("Dest").count(), 1); + QCOMPARE(completion->allMatches("Dest")[0], QString("Destroy")); +} + /////////////////////////////////// Helpers //////////////////////////////////// KLineEdit* WaitForEventWidgetTest::receiverNameLineEdit( This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-05-21 23:36:39
|
Revision: 244 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=244&view=rev Author: danxuliu Date: 2010-05-21 23:36:29 +0000 (Fri, 21 May 2010) Log Message: ----------- Add RemoteObjectNameWidget to select the name of a remote object. It encapsulates the current KLineEdit and RemoteObjectChooser button in WaitForSignalWidget and WaitForEventWidget, also providing remote object name completion. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.ui trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.ui trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForSignalWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.ui trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -32,7 +32,8 @@ "org.kde.ktutorial.EditorSupport", QDBusConnection::sessionBus(), 0), mMapper(mapper), - mRemoteEventSpy(0) { + mRemoteEventSpy(0), + mNumberOfPendingEnableEventSpyCalls(0) { } RemoteEditorSupport::~RemoteEditorSupport() { @@ -66,6 +67,7 @@ RemoteEventSpy* RemoteEditorSupport::enableEventSpy() throw (DBusException) { if (mRemoteEventSpy) { + mNumberOfPendingEnableEventSpyCalls++; return mRemoteEventSpy; } @@ -75,6 +77,7 @@ } mRemoteEventSpy = new RemoteEventSpy(service(), mMapper); + mNumberOfPendingEnableEventSpyCalls = 1; return mRemoteEventSpy; } @@ -83,6 +86,11 @@ return; } + mNumberOfPendingEnableEventSpyCalls--; + if (mNumberOfPendingEnableEventSpyCalls > 0) { + return; + } + QDBusReply<void> reply = call("disableEventSpy"); if (!reply.isValid()) { throw DBusException(reply.error().message()); Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h 2010-05-21 23:36:29 UTC (rev 244) @@ -103,6 +103,12 @@ /** * Disables the EventSpy in the remote EditorSupport and destroys the proxy * for it. + * The EventSpy is not disabled until the same number of calls to the + * enableEventSpy() method are made to this method. It makes possible to + * share the same RemoteEventSpy between objects that know nothing one of + * each other: each object enable and disable the RemoteEventSpy as they + * need, but it is not actually disabled until it is disabled by the last + * object using it. * * @throws DBusException If a DBus error happens. */ @@ -120,6 +126,12 @@ */ RemoteEventSpy* mRemoteEventSpy; + /** + * The number of enableEventSpy calls that didn't get a disableEventSpy + * counterpart yet. + */ + int mNumberOfPendingEnableEventSpyCalls; + }; #endif Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-05-21 23:36:29 UTC (rev 244) @@ -35,6 +35,7 @@ set(ktutorial_editor_view_SRCS ${ktutorial_editor_view_SRCS} RemoteObjectChooser.cpp + RemoteObjectNameWidget.cpp RemoteObjectTreeItem.cpp RemoteObjectTreeItemUpdater.cpp RemoteObjectTreeSelectionManager.cpp @@ -47,6 +48,7 @@ LicenseWidget.ui NewWaitForWidget.ui ReactionWidget.ui + RemoteObjectNameWidget.ui StepDataWidget.ui TutorialInformationWidget.ui WaitForEventWidget.ui Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -0,0 +1,195 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectNameWidget.h" +#include "ui_RemoteObjectNameWidget.h" + +#include <KDebug> +#include <KMessageBox> + +#include "RemoteObjectChooser.h" +#include "../targetapplication/RemoteEditorSupport.h" +#include "../targetapplication/RemoteEventSpy.h" +#include "../targetapplication/RemoteObject.h" +#include "../targetapplication/TargetApplication.h" + +//public: + +RemoteObjectNameWidget::RemoteObjectNameWidget(QWidget* parent): + QWidget(parent) { + + ui = new Ui::RemoteObjectNameWidget(); + ui->setupUi(this); + + ui->objectNameLineEdit->completionObject()->setOrder(KCompletion::Sorted); + connect(ui->objectNameLineEdit, SIGNAL(textChanged(QString)), + this, SLOT(handleNameChanged(QString))); + + connect(ui->objectNamePushButton, SIGNAL(clicked(bool)), + this, SLOT(showRemoteObjectChooser())); + + if (TargetApplication::self()->remoteEditorSupport()) { + registerRemoteObjects(); + } + + connect(TargetApplication::self(), SIGNAL(started()), + this, SLOT(registerRemoteObjects())); + connect(TargetApplication::self(), SIGNAL(finished()), + this, SLOT(deregisterRemoteObjects())); +} + +RemoteObjectNameWidget::~RemoteObjectNameWidget() { + if (TargetApplication::self()->remoteEditorSupport()) { + TargetApplication::self()->remoteEditorSupport()->disableEventSpy(); + } + + delete ui; +} + +QString RemoteObjectNameWidget::name() const { + return ui->objectNameLineEdit->text(); +} + +void RemoteObjectNameWidget::setName(const QString& name) { + ui->objectNameLineEdit->setText(name); +} + +//private: + +void RemoteObjectNameWidget::registerRemoteObject(RemoteObject* remoteObject, + RemoteObject* parent) +throw (DBusException) { + Q_ASSERT(remoteObject); + + if (!remoteObject->name().isEmpty()) { + KCompletion* completion = ui->objectNameLineEdit->completionObject(); + completion->addItem(remoteObject->name()); + + mRemoteObjectForName.insert(remoteObject->name(), remoteObject); + mRemoteObjectForParent.insert(parent, remoteObject); + } + + foreach (RemoteObject* child, remoteObject->children()) { + registerRemoteObject(child, remoteObject); + } +} + +void RemoteObjectNameWidget::deregisterRemoteObject(RemoteObject* remoteObject, + RemoteObject* parent) +throw (DBusException) { + Q_ASSERT(remoteObject); + + //The remote object is no longer accesible, so name() can't be called + QString name = mRemoteObjectForName.key(remoteObject); + + KCompletion* completion = ui->objectNameLineEdit->completionObject(); + completion->removeItem(name); + + mRemoteObjectForName.remove(name); + mRemoteObjectForParent.remove(parent, remoteObject); + + foreach (RemoteObject* child, remoteObject->children()) { + deregisterRemoteObject(child, remoteObject); + } +} + +//private slots: + +void RemoteObjectNameWidget::showRemoteObjectChooser() { + RemoteObjectChooser* chooser = new RemoteObjectChooser(this); + connect(chooser, SIGNAL(remoteObjectChosen(RemoteObject*)), + this, SLOT(setChosenRemoteObject(RemoteObject*))); + + chooser->show(); +} + +void RemoteObjectNameWidget::setChosenRemoteObject(RemoteObject* remoteObject) { + try { + ui->objectNameLineEdit->setText(remoteObject->name()); + } catch (DBusException e) { + QString text = i18nc("@label", "The object name can not be set, there " +"was a problem getting the name from the target application: %1", e.message()); + QString title = i18nc("@title", "Can't communicate with the target " +"application"); + KMessageBox::sorry(this, text, title); + } +} + +void RemoteObjectNameWidget::registerRemoteObjects() { + try { + registerRemoteObject(TargetApplication::self()-> + remoteEditorSupport()->mainWindow(), 0); + } catch (DBusException e) { + kWarning() << "The remote objects could not be registered to provide " + << "name completion (" << e.message() << ")."; + } + + try { + RemoteEventSpy* remoteEventSpy = + TargetApplication::self()->remoteEditorSupport()->enableEventSpy(); + connect(remoteEventSpy, SIGNAL(eventReceived(RemoteObject*,QString)), + this, SLOT(updateRemoteObjects(RemoteObject*,QString))); + } catch (DBusException e) { + kWarning() << "The remote event spy could not be connected to provide " + << "name completion updates (" << e.message() << ")."; + } +} + +void RemoteObjectNameWidget::deregisterRemoteObjects() { + ui->objectNameLineEdit->completionObject()->clear(); + mRemoteObjectForName.clear(); + mRemoteObjectForParent.clear(); +} + +void RemoteObjectNameWidget::updateRemoteObjects(RemoteObject* remoteObject, + const QString& eventType) { + if (eventType != "ChildAdded" && eventType != "ChildRemoved") { + return; + } + + try { + QList<RemoteObject*> children = remoteObject->children(); + + QList<RemoteObject*> knownChildren = + mRemoteObjectForParent.values(remoteObject); + + if (eventType == "ChildAdded") { + foreach (RemoteObject* child, children) { + if (!knownChildren.contains(child)) { + registerRemoteObject(child, remoteObject); + } + } + } + + if (eventType == "ChildRemoved") { + foreach (RemoteObject* child, knownChildren) { + if (!children.contains(child)) { + deregisterRemoteObject(child, remoteObject); + } + } + } + } catch (DBusException e) { + kWarning() << "There was a problem querying the remote objects, the " + << "name completion could not be updated (" + << e.message() << ")."; + } +} + +void RemoteObjectNameWidget::handleNameChanged(const QString& name) { + emit remoteObjectChosen(mRemoteObjectForName.value(name)); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2010-05-21 23:36:29 UTC (rev 244) @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTNAMEWIDGET_H +#define REMOTEOBJECTNAMEWIDGET_H + +#include <QHash> +#include <QWidget> + +#include "../targetapplication/DBusException.h" + +class RemoteObject; + +namespace Ui { +class RemoteObjectNameWidget; +} + +/** + * Widget to choose the name of a RemoteObject. + * The widget is composed by a line edit and a button to show a + * RemoteObjectChooser. The name can be set directly in the line edit or with + * the aid of the RemoteObjectChooser. + * + * The line edit provides text completion for the names of the remote objects + * in the target application. If there is no target application running, no + * completion is provided. As soon as it starts, the completion is enabled. + * + * When a name is set in the line edit the signal + * remoteObjectChosen(RemoteObject*) is emitted. + */ +class RemoteObjectNameWidget: public QWidget { +Q_OBJECT +public: + + /** + * Creates a new RemoteObjectNameWidget. + * + * @param parent The parent QWidget. + */ + explicit RemoteObjectNameWidget(QWidget* parent = 0); + + /** + * Destroys this widget. + */ + virtual ~RemoteObjectNameWidget(); + + /** + * Returns the chosen remote object name. + * + * @return The chosen remote object name. + */ + QString name() const; + + /** + * Sets the chosen remote object name. + * + * @param name The chosen remote object name. + */ + void setName(const QString& name); + +Q_SIGNALS: + + /** + * Emitted when the name of a remote object is set. + * It does not matter how the name was set (using setName(QString), written + * by the user or chosen from the RemoteObjectChooser). + * If there is no remote object with the name set or the target application + * is not running, the parameter of this signal is a null pointer. + * + * @param remoteObject The RemoteObject with the name set. + */ + void remoteObjectChosen(RemoteObject* remoteObject); + +private: + + /** + * The RemoteObjects with a name indexed by their name. + */ + QHash<QString, RemoteObject*> mRemoteObjectForName; + + /** + * The known RemoteObjects with a name indexed by their parent + */ + QMultiHash<RemoteObject*, RemoteObject*> mRemoteObjectForParent; + + /** + * The Ui Designer generated class. + */ + Ui::RemoteObjectNameWidget* ui; + + /** + * Registers the given RemoteObject and all its children. + * The objects are only registered if they have a name. In that case, it is + * added to the completion of the name line edit. + * + * @param remoteObject The RemoteObject to register. + * @param parent The parent of the RemoteObject to register. + * @throws DBusException If a DBus error happens. + */ + void registerRemoteObject(RemoteObject* remoteObject, RemoteObject* parent) + throw (DBusException); + + /** + * Deregisters the given RemoteObject and all its children. + * + * @param remoteObject The RemoteObject to deregister. + * @param parent The parent of the RemoteObject to deregister. + * @throws DBusException If a DBus error happens. + */ + void deregisterRemoteObject(RemoteObject* remoteObject, + RemoteObject* parent) + throw (DBusException); + +private Q_SLOTS: + + /** + * Creates and shows a new RemoteObjectChooser. + */ + void showRemoteObjectChooser(); + + /** + * Sets the object name based in the chosen remote object. + * + * @param remoteObject The chosen RemoteObject. + */ + void setChosenRemoteObject(RemoteObject* remoteObject); + + /** + * Registers the remote objects and add their names to the line edit + * completion object. + */ + void registerRemoteObjects(); + + /** + * Deregisters the remote objects and removes their names from the line edit + * completion object. + */ + void deregisterRemoteObjects(); + + /** + * Updates the registered remote objects if they received a ChildAdded or + * ChildRemoved event. + * + * @param remoteObject The RemoteObject that received the event. + * @param eventType The type of the event received. + */ + void updateRemoteObjects(RemoteObject* remoteObject, + const QString& eventType); + + /** + * Emits remoteObjectChosen(RemoteObject*) with the remote object with the + * given name. + * + * @param name The name set. + */ + void handleNameChanged(const QString& name); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.ui (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.ui 2010-05-21 23:36:29 UTC (rev 244) @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>RemoteObjectNameWidget</class> + <widget class="QWidget" name="RemoteObjectNameWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="RemoteObjectNameWidgetHorizontalLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="KLineEdit" name="objectNameLineEdit"/> + </item> + <item> + <widget class="KPushButton" name="objectNamePushButton"> + <property name="toolTip"> + <string comment="@info:tooltip">Choose the object name from a list with all the objects in the target application</string> + </property> + <property name="whatsThis"> + <string comment="@info:whatsthis"><p>Opens the list with all the objects in the target application to select the object from them.</p> +<p>The object list must be gotten from a running application. The first time that you try to choose the object name you will be asked for the target application of the tutorial. A new instance of the application will then be started to know the available objects.</p> +<p>The next time that you try to choose the object name the already running application will be used. If it is quitted, a new instance will be started again.</p> +<p>When the target application is started all the KTutorial editor windows, except the one with the list, will be hidden. When the list is closed, the windows will be shown again.</p></string> + </property> + <property name="text"> + <string comment="@action:button">Choose...</string> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KPushButton</class> + <extends>QPushButton</extends> + <header>kpushbutton.h</header> + </customwidget> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -24,9 +24,7 @@ #include "../data/WaitForEvent.h" #ifdef QT_QTDBUS_FOUND -#include <KMessageBox> - -#include "RemoteObjectChooser.h" +#include "RemoteObjectNameWidget.h" #include "../targetapplication/RemoteObject.h" #endif @@ -38,16 +36,26 @@ mWaitForEvent(waitForEvent) { ui = new Ui::WaitForEventWidget(); +#ifdef QT_QTDBUS_FOUND + //Hack: RemoteObjectNameWidget is created before the other widgets to be + //the first widget in the tab order. I feel dumb, but I tried using + //setTabOrder and got nothing... + mRemoteObjectNameWidget = new RemoteObjectNameWidget(this); +#endif ui->setupUi(this); #ifdef QT_QTDBUS_FOUND - connect(ui->receiverNamePushButton, SIGNAL(clicked(bool)), - this, SLOT(showRemoteObjectChooser())); + //Replace ui->receiverNameLineEdit with mRemoteObjectNameWidget + ui->valueVerticalLayout->removeWidget(ui->receiverNameLineEdit); + delete ui->receiverNameLineEdit; + + ui->valueVerticalLayout->insertWidget(0, mRemoteObjectNameWidget); + + mRemoteObjectNameWidget->setName(waitForEvent->receiverName()); #else - ui->receiverNamePushButton->hide(); + ui->receiverNameLineEdit->setText(waitForEvent->receiverName()); #endif - ui->receiverNameLineEdit->setText(waitForEvent->receiverName()); ui->eventNameLineEdit->setText(waitForEvent->eventName()); addItemsToEventNameCompletion(); @@ -58,7 +66,11 @@ } void WaitForEventWidget::saveChanges() { +#ifdef QT_QTDBUS_FOUND + QString receiverName = mRemoteObjectNameWidget->name(); +#else QString receiverName = ui->receiverNameLineEdit->text(); +#endif if (mWaitForEvent->receiverName() != receiverName) { mWaitForEvent->setReceiverName(receiverName); } @@ -80,28 +92,3 @@ completion->addItem(eventTypeEnumerator.key(i)); } } - -//private slots: - -#ifdef QT_QTDBUS_FOUND -void WaitForEventWidget::showRemoteObjectChooser() { - RemoteObjectChooser* chooser = new RemoteObjectChooser(this); - connect(chooser, SIGNAL(remoteObjectChosen(RemoteObject*)), - this, SLOT(setChosenRemoteObject(RemoteObject*))); - - chooser->show(); -} - -void WaitForEventWidget::setChosenRemoteObject(RemoteObject* remoteObject) { - try { - ui->receiverNameLineEdit->setText(remoteObject->name()); - } catch (DBusException e) { - QString text = i18nc("@label", "The receiver name can not be set, " -"there was a problem getting the name from the target application: %1", -e.message()); - QString title = i18nc("@title", "Can't communicate with the target " -"application"); - KMessageBox::sorry(this, text, title); - } -} -#endif Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.h 2010-05-21 23:36:29 UTC (rev 244) @@ -21,7 +21,11 @@ #include "EditionWidget.h" +#ifdef QT_QTDBUS_FOUND class RemoteObject; +class RemoteObjectNameWidget; +#endif + class WaitForEvent; namespace Ui { @@ -66,27 +70,18 @@ */ Ui::WaitForEventWidget* ui; - /** - * Adds all the event names to the completion object of the event name line - * edit. - */ - void addItemsToEventNameCompletion(); - -private Q_SLOTS: - #ifdef QT_QTDBUS_FOUND /** - * Creates and shows a new RemoteObjectChooser. + * The widget to get the name of a remote object. */ - void showRemoteObjectChooser(); + RemoteObjectNameWidget* mRemoteObjectNameWidget; +#endif /** - * Sets the receiver name based in the chosen remote object. - * - * @param remoteObject The chosen RemoteObject. + * Adds all the event names to the completion object of the event name line + * edit. */ - void setChosenRemoteObject(RemoteObject* remoteObject); -#endif + void addItemsToEventNameCompletion(); }; Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.ui 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.ui 2010-05-21 23:36:29 UTC (rev 244) @@ -33,9 +33,6 @@ <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> - <property name="buddy"> - <cstring>receiverNameLineEdit</cstring> - </property> </widget> </item> <item> @@ -56,32 +53,12 @@ <item> <layout class="QVBoxLayout" name="valueVerticalLayout"> <item> - <layout class="QHBoxLayout" name="receiverNameValueHorizontalLayout"> - <item> - <widget class="KLineEdit" name="receiverNameLineEdit"> - <property name="whatsThis"> - <string comment="@info:whatsthis"><para>The name of the QObject that receives the event.</para> + <widget class="KLineEdit" name="receiverNameLineEdit"> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>The name of the QObject that receives the event.</para> <para>Note that the name is not the class of the object, but the string returned by its objectName() method.</para></string> - </property> - </widget> - </item> - <item> - <widget class="KPushButton" name="receiverNamePushButton"> - <property name="toolTip"> - <string comment="@info:tooltip">Choose the emitter name from a list with all the objects in the target application</string> - </property> - <property name="whatsThis"> - <string comment="@info:whatsthis"><p>Opens the list with all the objects in the target application to select the receiver from them.</p> -<p>The object list must be gotten from a running application. The first time that you try to choose the receiver name you will be asked for the target application of the tutorial. A new instance of the application will then be started to know the available objects.</p> -<p>The next time that you try to choose the receiver name the already running application will be used. If it is quitted, a new instance will be started again.</p> -<p>When the target application is started all the KTutorial editor windows, except the one with the list, will be hidden. When the list is closed, the windows will be shown again.</p></string> - </property> - <property name="text"> - <string comment="@action:button">Choose...</string> - </property> - </widget> - </item> - </layout> + </property> + </widget> </item> <item> <widget class="KLineEdit" name="eventNameLineEdit"> @@ -113,11 +90,6 @@ </widget> <customwidgets> <customwidget> - <class>KPushButton</class> - <extends>QPushButton</extends> - <header>kpushbutton.h</header> - </customwidget> - <customwidget> <class>KLineEdit</class> <extends>QLineEdit</extends> <header>klineedit.h</header> Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -22,9 +22,9 @@ #include "../data/WaitForSignal.h" #ifdef QT_QTDBUS_FOUND -#include <KMessageBox> +#include <KDebug> -#include "RemoteObjectChooser.h" +#include "RemoteObjectNameWidget.h" #include "../targetapplication/RemoteClass.h" #include "../targetapplication/RemoteObject.h" #endif @@ -39,16 +39,29 @@ mWaitForSignal(waitForSignal) { ui = new Ui::WaitForSignalWidget(); +#ifdef QT_QTDBUS_FOUND + //Hack: RemoteObjectNameWidget is created before the other widgets to be + //the first widget in the tab order. I feel dumb, but I tried using + //setTabOrder and got nothing... + mRemoteObjectNameWidget = new RemoteObjectNameWidget(this); +#endif ui->setupUi(this); #ifdef QT_QTDBUS_FOUND - connect(ui->emitterNamePushButton, SIGNAL(clicked(bool)), - this, SLOT(showRemoteObjectChooser())); + //Replace ui->emitterNameLineEdit with mRemoteObjectNameWidget + ui->valueVerticalLayout->removeWidget(ui->emitterNameLineEdit); + delete ui->emitterNameLineEdit; + + ui->valueVerticalLayout->insertWidget(0, mRemoteObjectNameWidget); + + connect(mRemoteObjectNameWidget, SIGNAL(remoteObjectChosen(RemoteObject*)), + this, SLOT(setChosenRemoteObject(RemoteObject*))); + + mRemoteObjectNameWidget->setName(waitForSignal->emitterName()); #else - ui->emitterNamePushButton->hide(); + ui->emitterNameLineEdit->setText(waitForSignal->emitterName()); #endif - ui->emitterNameLineEdit->setText(waitForSignal->emitterName()); ui->signalNameLineEdit->setText(waitForSignal->signalName()); } @@ -57,7 +70,11 @@ } void WaitForSignalWidget::saveChanges() { +#ifdef QT_QTDBUS_FOUND + QString emitterName = mRemoteObjectNameWidget->name(); +#else QString emitterName = ui->emitterNameLineEdit->text(); +#endif if (mWaitForSignal->emitterName() != emitterName) { mWaitForSignal->setEmitterName(emitterName); } @@ -71,7 +88,8 @@ //private: #ifdef QT_QTDBUS_FOUND -void WaitForSignalWidget::setSignalCompletion(RemoteClass* remoteClass) { +void WaitForSignalWidget::setSignalCompletion(RemoteClass* remoteClass) +throw (DBusException) { KCompletion* completion = ui->signalNameLineEdit->completionObject(); completion->clear(); completion->setOrder(KCompletion::Sorted); @@ -89,24 +107,18 @@ //private slots: #ifdef QT_QTDBUS_FOUND -void WaitForSignalWidget::showRemoteObjectChooser() { - RemoteObjectChooser* chooser = new RemoteObjectChooser(this); - connect(chooser, SIGNAL(remoteObjectChosen(RemoteObject*)), - this, SLOT(setChosenRemoteObject(RemoteObject*))); +void WaitForSignalWidget::setChosenRemoteObject(RemoteObject* remoteObject) { + if (!remoteObject) { + setSignalCompletion(0); + return; + } - chooser->show(); -} - -void WaitForSignalWidget::setChosenRemoteObject(RemoteObject* remoteObject) { try { - ui->emitterNameLineEdit->setText(remoteObject->name()); setSignalCompletion(remoteObject->remoteClass()); } catch (DBusException e) { - QString text = i18nc("@label", "The emitter name can not be set, there " -"was a problem getting the name from the target application: %1", e.message()); - QString title = i18nc("@title", "Can't communicate with the target " -"application"); - KMessageBox::sorry(this, text, title); + kWarning() << "The signal completion could not be set, there was a " + << "problem getting the class of the remote object (" + << e.message() << ")."; } } #endif Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.h 2010-05-21 23:36:29 UTC (rev 244) @@ -21,8 +21,14 @@ #include "EditionWidget.h" +#ifdef QT_QTDBUS_FOUND +#include "../targetapplication/DBusException.h" + class RemoteClass; class RemoteObject; +class RemoteObjectNameWidget; +#endif + class WaitForSignal; namespace Ui { @@ -69,23 +75,23 @@ #ifdef QT_QTDBUS_FOUND /** + * The widget to get the name of a remote object. + */ + RemoteObjectNameWidget* mRemoteObjectNameWidget; + + /** * Sets the completion of the signal line edit to the signals emitted by the * given remote class and its super classes. * * @param remoteClass The class to get its signal list. */ - void setSignalCompletion(RemoteClass* remoteClass); + void setSignalCompletion(RemoteClass* remoteClass) throw (DBusException); #endif private Q_SLOTS: #ifdef QT_QTDBUS_FOUND /** - * Creates and shows a new RemoteObjectChooser. - */ - void showRemoteObjectChooser(); - - /** * Sets the emitter name based in the chosen remote object. * * @param remoteObject The chosen RemoteObject. Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.ui 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForSignalWidget.ui 2010-05-21 23:36:29 UTC (rev 244) @@ -33,9 +33,6 @@ <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> - <property name="buddy"> - <cstring>emitterNameLineEdit</cstring> - </property> </widget> </item> <item> @@ -56,32 +53,12 @@ <item> <layout class="QVBoxLayout" name="valueVerticalLayout"> <item> - <layout class="QHBoxLayout" name="emitterNameValueHorizontalLayout"> - <item> - <widget class="KLineEdit" name="emitterNameLineEdit"> - <property name="whatsThis"> - <string comment="@info:whatsthis"><para>The name of the QObject that emits the signal.</para> + <widget class="KLineEdit" name="emitterNameLineEdit"> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>The name of the QObject that emits the signal.</para> <para>Note that the name is not the class of the object, but the string returned by its objectName() method.</para></string> - </property> - </widget> - </item> - <item> - <widget class="KPushButton" name="emitterNamePushButton"> - <property name="toolTip"> - <string comment="@info:tooltip">Choose the emitter name from a list with all the objects in the target application</string> - </property> - <property name="whatsThis"> - <string comment="@info:whatsthis"><p>Opens the list with all the objects in the target application to select the emitter from them.</p> -<p>The object list must be gotten from a running application. The first time that you try to choose the emitter name you will be asked for the target application of the tutorial. A new instance of the application will then be started to know the available objects.</p> -<p>The next time that you try to choose the emitter name the already running application will be used. If it is quitted, a new instance will be started again.</p> -<p>When the target application is started all the KTutorial editor windows, except the one with the list, will be hidden. When the list is closed, the windows will be shown again.</p></string> - </property> - <property name="text"> - <string comment="@action:button">Choose...</string> - </property> - </widget> - </item> - </layout> + </property> + </widget> </item> <item> <widget class="KLineEdit" name="signalNameLineEdit"> @@ -113,11 +90,6 @@ </widget> <customwidgets> <customwidget> - <class>KPushButton</class> - <extends>QPushButton</extends> - <header>kpushbutton.h</header> - </customwidget> - <customwidget> <class>KLineEdit</class> <extends>QLineEdit</extends> <header>klineedit.h</header> Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2010-05-21 23:36:29 UTC (rev 244) @@ -81,7 +81,7 @@ QString objectName(int objectId) { - if (objectId > 100) { + if (objectId > 1000) { return ""; } @@ -89,7 +89,7 @@ } QString className(int objectId) { - if (objectId > 100) { + if (objectId > 1000) { return ""; } Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -61,6 +61,7 @@ void testEnableEventSpyWhenRemoteEditorSupportIsNotAvailable(); void testDisableEventSpy(); + void testDisableEventSpyAfterSeveralEnableEventSpyCalls(); void testDisableEventSpyTwice(); void testDisableEventSpyWhenRemoteEditorSupportIsNotAvailable(); @@ -267,6 +268,33 @@ QCOMPARE(eventReceivedSpy.count(), 0); } +void RemoteEditorSupportTest:: + testDisableEventSpyAfterSeveralEnableEventSpyCalls() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + RemoteEditorSupport remoteEditorSupport( + QDBusConnection::sessionBus().baseService(), &mapper); + + RemoteEventSpy* remoteEventSpy = remoteEditorSupport.enableEventSpy(); + remoteEditorSupport.enableEventSpy(); + remoteEditorSupport.enableEventSpy(); + + QVERIFY(remoteEventSpy); + + remoteEditorSupport.disableEventSpy(); + remoteEditorSupport.disableEventSpy(); + + QCOMPARE(mEditorSupport->mDisableEventSpyCount, 0); + + remoteEditorSupport.enableEventSpy(); + remoteEditorSupport.disableEventSpy(); + + QCOMPARE(mEditorSupport->mDisableEventSpyCount, 0); + + remoteEditorSupport.disableEventSpy(); + + QCOMPARE(mEditorSupport->mDisableEventSpyCount, 1); +} + void RemoteEditorSupportTest::testDisableEventSpyTwice() { RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); RemoteEditorSupport remoteEditorSupport( @@ -277,9 +305,14 @@ QVERIFY(remoteEventSpy); remoteEditorSupport.disableEventSpy(); + + QCOMPARE(mEditorSupport->mDisableEventSpyCount, 1); + remoteEditorSupport.disableEventSpy(); QCOMPARE(mEditorSupport->mDisableEventSpyCount, 1); + + QVERIFY(remoteEditorSupport.enableEventSpy()); } void RemoteEditorSupportTest:: Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2010-05-21 23:36:29 UTC (rev 244) @@ -52,6 +52,7 @@ unit_tests( RemoteObjectChooser + RemoteObjectNameWidget RemoteObjectTreeItem RemoteObjectTreeItemUpdater RemoteObjectTreeSelectionManager Added: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -0,0 +1,245 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "RemoteObjectNameWidget.h" + +#include <QtDBus/QtDBus> + +#include <KLineEdit> +#include <KProcess> + +#include "../targetapplication/RemoteClassStubs.h" +#include "../targetapplication/RemoteEditorSupport.h" +#include "../targetapplication/RemoteObject.h" +#define protected public +#define private public +#include "../targetapplication/TargetApplication.h" +#undef private +#undef protected + +class RemoteObjectNameWidgetTest: public QObject { +Q_OBJECT + +private slots: + + void initTestCase(); + void init(); + void cleanup(); + + void testConstructor(); + + void testSetName(); + void testSetNameWithUnknownRemoteObjectName(); + + void testTargetApplicationStartedAfterWidget(); + void testTargetApplicationStopped(); + + void testAddingOrRemovingRemoteObjects(); + +private: + + QString mPath; + + int mRemoteObjectStarType; + + StubEditorSupport* mEditorSupport; + StubObjectRegister* mObjectRegister; + + KLineEdit* objectNameLineEdit(RemoteObjectNameWidget* widget) const; + + void assertRemoteObjectSignal(const QSignalSpy& spy, int index, + const RemoteObject* remoteObject) const; + +}; + +void RemoteObjectNameWidgetTest::initTestCase() { + //RemoteObject* must be registered in order to be used with QSignalSpy + mRemoteObjectStarType = qRegisterMetaType<RemoteObject*>("RemoteObject*"); +} + +void RemoteObjectNameWidgetTest::init() { + mPath = QApplication::applicationDirPath() + + "/../targetapplication/TargetApplicationStub"; + + //Avoid signals from previews tests to be delivered to the next ones + //setting a new TargetApplication + delete TargetApplication::sSelf; + TargetApplication::sSelf = new TargetApplication(); +} + +void RemoteObjectNameWidgetTest::cleanup() { + delete TargetApplication::sSelf; + TargetApplication::sSelf = 0; +} + +void RemoteObjectNameWidgetTest::testConstructor() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + QWidget parent; + RemoteObjectNameWidget* widget = new RemoteObjectNameWidget(&parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(widget->name(), QString("")); + QStringList items = objectNameLineEdit(widget)->completionObject()->items(); + QCOMPARE(items.count(), 5); + QVERIFY(items.contains("The object name 42")); + QVERIFY(items.contains("The object name 420")); + QVERIFY(items.contains("The object name 421")); + QVERIFY(items.contains("The object name 422")); + QVERIFY(items.contains("The object name 423")); +} + +void RemoteObjectNameWidgetTest::testSetName() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + widget.setName("The object name 423"); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(widget.name(), QString("The object name 423")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, + mainWindow->children()[3]); +} + +void RemoteObjectNameWidgetTest::testSetNameWithUnknownRemoteObjectName() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + widget.setName("The object name 108"); + + QCOMPARE(widget.name(), QString("The object name 108")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, 0); +} + +void RemoteObjectNameWidgetTest::testTargetApplicationStartedAfterWidget() { + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + QStringList items = objectNameLineEdit(&widget)->completionObject()-> + items(); + QCOMPARE(items.count(), 0); + + widget.setName("The object name 42"); + + QCOMPARE(widget.name(), QString("The object name 42")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, 0); + + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + items = objectNameLineEdit(&widget)->completionObject()->items(); + QCOMPARE(items.count(), 5); + QVERIFY(items.contains("The object name 42")); + QVERIFY(items.contains("The object name 420")); + QVERIFY(items.contains("The object name 421")); + QVERIFY(items.contains("The object name 422")); + QVERIFY(items.contains("The object name 423")); + + widget.setName("The object name 423"); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(widget.name(), QString("The object name 423")); + QCOMPARE(remoteObjectChosenSpy.count(), 2); + assertRemoteObjectSignal(remoteObjectChosenSpy, 1, + mainWindow->children()[3]); +} + +void RemoteObjectNameWidgetTest::testTargetApplicationStopped() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + TargetApplication::self()->mProcess->kill(); + + //Give the target application time to stop + QTest::qWait(1000); + + QStringList items = objectNameLineEdit(&widget)->completionObject()-> + items(); + QCOMPARE(items.count(), 0); + + widget.setName("The object name 42"); + + QCOMPARE(widget.name(), QString("The object name 42")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, 0); +} + +void RemoteObjectNameWidgetTest::testAddingOrRemovingRemoteObjects() { + QSKIP("Unfortunately, testing if the completion list is updated when an " + "object is added or removed in the target application is too " + "burdensome so the test must be done manually", SkipAll); +} + +/////////////////////////////////// Helpers //////////////////////////////////// + +KLineEdit* RemoteObjectNameWidgetTest::objectNameLineEdit( + RemoteObjectNameWidget* widget) const { + return widget->findChild<KLineEdit*>("objectNameLineEdit"); +} + +//RemoteObject* must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(RemoteObject*); + +void RemoteObjectNameWidgetTest::assertRemoteObjectSignal(const QSignalSpy& spy, + int index, const RemoteObject* remoteObject) const { + QVariant argument = spy.at(index).at(0); + QCOMPARE(argument.userType(), mRemoteObjectStarType); + QCOMPARE(qvariant_cast<RemoteObject*>(argument), remoteObject); +} + +QTEST_MAIN(RemoteObjectNameWidgetTest) + +#include "RemoteObjectNameWidgetTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -85,7 +85,11 @@ KLineEdit* WaitForEventWidgetTest::receiverNameLineEdit( WaitForEventWidget* widget) const { +#ifdef QT_QTDBUS_FOUND + return widget->findChild<KLineEdit*>("objectNameLineEdit"); +#else return widget->findChild<KLineEdit*>("receiverNameLineEdit"); +#endif } KLineEdit* WaitForEventWidgetTest::eventNameLineEdit( Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForSignalWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForSignalWidgetTest.cpp 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForSignalWidgetTest.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -72,7 +72,11 @@ KLineEdit* WaitForSignalWidgetTest::emitterNameLineEdit( WaitForSignalWidget* widget) const { +#ifdef QT_QTDBUS_FOUND + return widget->findChild<KLineEdit*>("objectNameLineEdit"); +#else return widget->findChild<KLineEdit*>("emitterNameLineEdit"); +#endif } KLineEdit* WaitForSignalWidgetTest::signalNameLineEdit( Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp 2010-05-18 16:42:44 UTC (rev 243) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp 2010-05-21 23:36:29 UTC (rev 244) @@ -362,7 +362,11 @@ void WaitForWidgetTest::setEventData() const { KLineEdit* receiverNameLineEdit = +#ifdef QT_QTDBUS_FOUND + mWidget->findChild<KLineEdit*>("objectNameLineEdit"); +#else mWidget->findChild<KLineEdit*>("receiverNameLineEdit"); +#endif QVERIFY(receiverNameLineEdit); receiverNameLineEdit->setText("The new receiver name"); @@ -378,7 +382,11 @@ void WaitForWidgetTest::setSignalData() const { KLineEdit* emitterNameLineEdit = +#ifdef QT_QTDBUS_FOUND + mWidget->findChild<KLineEdit*>("objectNameLineEdit"); +#else mWidget->findChild<KLineEdit*>("emitterNameLineEdit"); +#endif QVERIFY(emitterNameLineEdit); emitterNameLineEdit->setText("The new emitter name"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-08-14 17:30:56
|
Revision: 248 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=248&view=rev Author: danxuliu Date: 2010-08-14 17:30:49 +0000 (Sat, 14 Aug 2010) Log Message: ----------- Show an information message box to tell the user that KTutorial editor windows will be hidden when the remote object is going to be chosen. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp 2010-08-14 14:57:12 UTC (rev 247) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp 2010-08-14 17:30:49 UTC (rev 248) @@ -135,13 +135,20 @@ mServiceDiscoveryTimer.stop(); + //The bus must be disconnected before executing other code to ensure that + //no other services are handled, as when an application starts it can create + //more than one valid service: one like ":1.42" and another like + //"org.freedesktop.DBus". + //So this method could be called again before finishing its current + //execution if serviceRegistered(QSignal) is emitted, which could happen if + //other event loops are created (like in a message box). + disconnect(QDBusConnection::sessionBus().interface(), 0, this, 0); + mServiceName = service; mMapper = new RemoteObjectMapper(mServiceName); mRemoteEditorSupport = new RemoteEditorSupport(mServiceName, mMapper); emit started(); - - disconnect(QDBusConnection::sessionBus().interface(), 0, this, 0); } void TargetApplication::handleProcessError(QProcess::ProcessError error) { Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp 2010-08-14 14:57:12 UTC (rev 247) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp 2010-08-14 17:30:49 UTC (rev 248) @@ -120,14 +120,41 @@ } } +void RemoteObjectChooser::warnAboutFinishedTargetApplicationBeforeClosing() { + QString text = i18nc("@label", "The target application has been closed, " +"but it must be running to be able to choose the objects."); + QString caption = i18nc("@title:window", "Target application closed"); + KMessageBox::sorry(this, text, caption); + + close(); +} + //private slots: void RemoteObjectChooser::handleTargetApplicationStarted() { - hideParentWindows(this); + QString text = i18nc("@label", "<para>The " +"<application>KTutorial editor</application> windows will be hidden and only " +"the list with the objects in the target application will be shown. It is made " +"to better view when a widget is highlighted in the target application.</para>" +"<para>The <application>KTutorial editor</application> windows will be shown " +"again once the selection list is closed.</para>"); + QString caption = i18nc("@title:window", "<application>KTutorial " +"editor</application> windows will be hidden"); + KMessageBox::information(this, text, caption, + "KTutorialEditorWindowsWillBeHidden"); RemoteEditorSupport* remoteEditorSupport = TargetApplication::self()->remoteEditorSupport(); + //The target application may have been closed while the message box was + //shown. + if (!remoteEditorSupport) { + warnAboutFinishedTargetApplicationBeforeClosing(); + return; + } + + hideParentWindows(this); + RemoteObject* mainWindow = remoteEditorSupport->mainWindow(); RemoteObjectTreeItem* rootItem = new RemoteObjectTreeItem(mainWindow); TreeModel* treeModel = new TreeModel(rootItem, this); @@ -167,12 +194,7 @@ return; } - QString text = i18nc("@label", "The target application has been closed, " -"but it must be running to be able to choose the objects."); - QString caption = i18nc("@title:window", "Target application closed"); - KMessageBox::sorry(this, text, caption); - - close(); + warnAboutFinishedTargetApplicationBeforeClosing(); } void RemoteObjectChooser::setCurrentRemoteObject(RemoteObject* remoteObject) { Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h 2010-08-14 14:57:12 UTC (rev 247) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h 2010-08-14 17:30:49 UTC (rev 248) @@ -39,7 +39,8 @@ * When the target application is successfully started, the RemoteObjectChooser * hides all its parent dialogs and windows. Only the RemoteObjectChooser itself * is kept shown to avoid the rest of windows of KTutorial editor to get in the - * way of the user when he interacts with the target application. + * way of the user when he interacts with the target application. Before hiding + * the windows an information message is shown to the user explaining this. * * When the user selects a remote object in the list and that object represents * a widget, the widget is highlighted in the target application. @@ -132,11 +133,17 @@ */ void showParentWindows(QWidget* widget); + /** + * A warning is shown to the user explaining that the target application + * must be running and this RemoteObjectChooser is closed. + */ + void warnAboutFinishedTargetApplicationBeforeClosing(); + private Q_SLOTS: /** - * Hides the parent windows and dialogs and sets up the models for the tree - * view. + * Shows the information message about hiding the parent windows, hides the + * parent windows and dialogs, and sets up the models for the tree view. */ void handleTargetApplicationStarted(); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2010-08-14 14:57:12 UTC (rev 247) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2010-08-14 17:30:49 UTC (rev 248) @@ -58,6 +58,7 @@ void testTargetApplicationNotUsingKTutorial(); void testTargetApplicationStopped(); + void testTargetApplicationStoppedBeforeClosingInformationMessageBox(); void close(); //Closing with ALT+F4 can't be tested, as it depends on the window manager @@ -72,8 +73,11 @@ QString mPath; - void closeMessageBox(QWidget* widget, int timeToWait); + void closeInformationMessageBox(int timeToWait); + void closeSorryMessageBox(QWidget* widget, int timeToWait); + void killTargetApplication(int timeToWait); + QTreeView* remoteObjectsTreeView(RemoteObjectChooser* widget) const; QPushButton* okButton(RemoteObjectChooser* widget) const; @@ -120,6 +124,8 @@ QWidget* dialog = new QWidget(&window, Qt::Dialog); dialog->show(); + //Queue closing the information message box + closeInformationMessageBox(1000); RemoteObjectChooser* chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -147,6 +153,8 @@ QWidget* dialog = new QWidget(&window, Qt::Dialog); dialog->show(); + //Queue closing the information message box + closeInformationMessageBox(1000); RemoteObjectChooser* chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -170,8 +178,8 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Queue closing the message box - closeMessageBox(chooser, 1000); + //Queue closing the sorry message box + closeSorryMessageBox(chooser, 1000); //Give the target application time to fail to start QTest::qWait(1000); @@ -197,8 +205,8 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Queue closing the message box - closeMessageBox(chooser, 3500); + //Queue closing the sorry message box + closeSorryMessageBox(chooser, 3500); //Give the target application time to fail to start QTest::qWait(3500); @@ -219,14 +227,16 @@ QWidget* dialog = new QWidget(&window, Qt::Dialog); dialog->show(); + //Queue closing the information message box + closeInformationMessageBox(1000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); //Give the target application time to start QTest::qWait(1000); - //Queue closing the message box - closeMessageBox(chooser, 1000); + //Queue closing the sorry message box + closeSorryMessageBox(chooser, 1000); TargetApplication::self()->mProcess->kill(); @@ -241,6 +251,44 @@ QVERIFY(dialog->isVisible()); } +void RemoteObjectChooserTest:: +testTargetApplicationStoppedBeforeClosingInformationMessageBox() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + + QWidget window(0, Qt::Window); + window.show(); + QWidget* dialog = new QWidget(&window, Qt::Dialog); + dialog->show(); + + QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); + chooser->show(); + + //All the queuing has to be done before the qWait, because the killing must + //be done once the information message is already shown, and the sorry + //message appears just after the information message is closed (and it may + //appear before the wait ends, so queuing it after the wait could not work). + + //Queue killing the target application + killTargetApplication(1000); + + //Queue closing the information message box + closeInformationMessageBox(2000); + + //Queue closing the sorry message box + closeSorryMessageBox(chooser, 3000); + + //Give the target application time to start and be killed, and to close the + //sorry message box + QTest::qWait(3000); + + //Process deleteLater() + QCoreApplication::sendPostedEvents(chooser, QEvent::DeferredDelete); + + QVERIFY(!chooser); + QVERIFY(window.isVisible()); + QVERIFY(dialog->isVisible()); +} + void RemoteObjectChooserTest::close() { TargetApplication::self()->setTargetApplicationFilePath(mPath); @@ -249,6 +297,8 @@ QWidget* dialog = new QWidget(&window, Qt::Dialog); dialog->show(); + //Queue closing the information message box + closeInformationMessageBox(1000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -269,6 +319,8 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); QWidget window(0, Qt::Window); + //Queue closing the information message box + closeInformationMessageBox(1000); RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); @@ -315,6 +367,8 @@ QWidget* dialog = new QWidget(&window, Qt::Dialog); dialog->show(); + //Queue closing the information message box + closeInformationMessageBox(1000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -358,6 +412,8 @@ QWidget* dialog = new QWidget(&window, Qt::Dialog); dialog->show(); + //Queue closing the information message box + closeInformationMessageBox(1000); QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); @@ -387,38 +443,78 @@ /////////////////////////////////// Helpers //////////////////////////////////// -class CloseMessageBoxHelper: public QObject { +//The message boxes are modal, so they won't return to the test code until they +//are closed. Thus, the actions to be performed while the message boxes are +//being shown (like closing the message boxes themselves) must be "queued". +class QueuedActionsHelper: public QObject { Q_OBJECT public: - CloseMessageBoxHelper(QWidget* widget): + QueuedActionsHelper(QWidget* widget = 0): QObject(widget), mWidget(widget) { } public slots: - void closeMessageBox() const { + void closeInformation() const { + //The information message box is created in the constructor itself of + //RemoteObjectChooser, so the RemoteObjectChooser can not be passed as + //an argument when this helper is created. It must be got when it is + //going to be destroyed. + QWidget* widget = findRemoteObjectChooser(); + QVERIFY(widget); + + KDialog* messageBox = widget->findChild<KDialog*>("information"); + QVERIFY(messageBox); + delete messageBox; + } + + void closeSorry() const { KDialog* messageBox = mWidget->findChild<KDialog*>("sorry"); QVERIFY(messageBox); delete messageBox; } + void killTargetApplication() const { + TargetApplication::self()->mProcess->kill(); + } + private: QWidget* mWidget; + QWidget* findRemoteObjectChooser() const { + foreach (QWidget* widget, QApplication::topLevelWidgets()) { + if (qobject_cast<RemoteObjectChooser*>(widget)) { + return widget; + } + } + + return 0; + } + }; -void RemoteObjectChooserTest::closeMessageBox(QWidget* widget, int timeToWait) { - CloseMessageBoxHelper* helper = new CloseMessageBoxHelper(widget); +void RemoteObjectChooserTest::closeInformationMessageBox(int timeToWait) { + QueuedActionsHelper* helper = new QueuedActionsHelper(); + QTimer::singleShot(timeToWait, helper, SLOT(closeInformation())); + QTimer::singleShot(timeToWait, helper, SLOT(deleteLater())); +} - //The message box is modal, so it won't return to the test code until it is - //closed. Thus, the commands to close the message box must be "queued", as - //deleting the message box after the qWait won't work. - QTimer::singleShot(timeToWait, helper, SLOT(closeMessageBox())); +void RemoteObjectChooserTest::closeSorryMessageBox(QWidget* widget, + int timeToWait) { + QueuedActionsHelper* helper = new QueuedActionsHelper(widget); + QTimer::singleShot(timeToWait, helper, SLOT(closeSorry())); + QTimer::singleShot(timeToWait, helper, SLOT(deleteLater())); } +void RemoteObjectChooserTest::killTargetApplication(int timeToWait) { + QueuedActionsHelper* helper = new QueuedActionsHelper(); + QTimer::singleShot(timeToWait, helper, SLOT(killTargetApplication())); + QTimer::singleShot(timeToWait, helper, SLOT(deleteLater())); +} + QTreeView* RemoteObjectChooserTest::remoteObjectsTreeView( RemoteObjectChooser* widget) const { return widget->findChild<QTreeView*>("remoteObjectsTreeView"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-20 19:30:27
|
Revision: 250 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=250&view=rev Author: danxuliu Date: 2010-09-20 19:30:21 +0000 (Mon, 20 Sep 2010) Log Message: ----------- Fix crash when an application not supporting KTutorial was closed before TargetApplication realized that the application did not support KTutorial. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp 2010-09-19 16:40:33 UTC (rev 249) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.cpp 2010-09-20 19:30:21 UTC (rev 250) @@ -162,6 +162,12 @@ } void TargetApplication::handleTargetApplicationDoesNotSupportKTutorial() { + //The target application not using KTutorial may have been closed by the + //user before the timeout expired and this slot was called + if (!mProcess) { + return; + } + mProcess->kill(); emit startFailed(InvalidApplication); Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h 2010-09-19 16:40:33 UTC (rev 249) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/TargetApplication.h 2010-09-20 19:30:21 UTC (rev 250) @@ -46,6 +46,10 @@ * other problem happened (for example, if the target application does not * support KTutorial), startFailed(Error) signal is emitted instead. * + * Note that started() and startFailed(Error) signals may not be emitted if the + * target application is closed too soon to realize that it was successfully + * started or that it does not support KTutorial. + * * Once the target application has been started, remoteEditorSupport * returns a RemoteEditorSupport connected to the remote * "org.kde.ktutorial.EditorSupport" interface exposed by the target Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp 2010-09-19 16:40:33 UTC (rev 249) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp 2010-09-20 19:30:21 UTC (rev 250) @@ -68,6 +68,8 @@ void testStartApplicationNotUsingKTutorial(); void testApplicationStopped(); + void testApplicationStoppedBeforeBeingSuccessfullyStarted(); + void testApplicationNotUsingKTutorialStoppedBeforeCheckTimeout(); private: @@ -383,6 +385,71 @@ QVERIFY(!targetApplication.remoteEditorSupport()); } +void TargetApplicationTest:: + testApplicationStoppedBeforeBeingSuccessfullyStarted() { + TargetApplication targetApplication; + targetApplication.setTargetApplicationFilePath(mTargetApplicationStubPath); + + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); + //TargetApplication::Error must be registered in order to be used with + //QSignalSpy + qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); + SignalWait finishedWait(&targetApplication, SIGNAL(finished())); + + targetApplication.start(); + + //Give the application some time to start before killing it, but not enough + //time to start successfully + QTest::qWait(5); + + targetApplication.mProcess->kill(); + + QVERIFY(finishedWait.waitForCount(1, 1000)); + QVERIFY(!targetApplication.remoteEditorSupport()); + + //Keep waiting until the timeout expires to ensure that no other signal is + //emitted or the process is tried to be killed again + QTest::qWait(3000); + + QCOMPARE(startedSpy.count(), 0); + QCOMPARE(startFailedSpy.count(), 0); +} + +void TargetApplicationTest:: + testApplicationNotUsingKTutorialStoppedBeforeCheckTimeout() { + TargetApplication targetApplication; + targetApplication.setTargetApplicationFilePath( + QApplication::applicationDirPath() + "/DummyApplication"); + + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); + //TargetApplication::Error must be registered in order to be used with + //QSignalSpy + qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); + SignalWait finishedWait(&targetApplication, SIGNAL(finished())); + + targetApplication.start(); + + //Give the application some time to start before killing it, but not enough + //time to realize that it does not use KTutorial + QTest::qWait(1000); + + targetApplication.mProcess->kill(); + + QVERIFY(finishedWait.waitForCount(1, 1000)); + QVERIFY(!targetApplication.remoteEditorSupport()); + + //Keep waiting until the timeout expires to ensure that no other signal is + //emitted or the process is tried to be killed again + QTest::qWait(3000); + + QCOMPARE(startedSpy.count(), 0); + QCOMPARE(startFailedSpy.count(), 0); +} + QTEST_MAIN(TargetApplicationTest) #include "TargetApplicationTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-25 03:33:45
|
Revision: 254 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=254&view=rev Author: danxuliu Date: 2010-09-25 03:33:38 +0000 (Sat, 25 Sep 2010) Log Message: ----------- Add support to test in the target application the tutorial being designed in the editor. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/EditActions.cpp trunk/ktutorial/ktutorial-editor/src/EditActions.h trunk/ktutorial/ktutorial-editor/src/ktutorial-editorui.rc trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/TutorialTester.cpp trunk/ktutorial/ktutorial-editor/src/TutorialTester.h Modified: trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/src/CMakeLists.txt 2010-09-25 03:33:38 UTC (rev 254) @@ -21,6 +21,13 @@ KTutorialEditor.cpp ) +if (QT_QTDBUS_FOUND) + set(ktutorial_editor_SRCS + ${ktutorial_editor_SRCS} + TutorialTester.cpp + ) +endif (QT_QTDBUS_FOUND) + # Instead of compiling the executable directly from the sources, the sources are # compiled to a static library that is linked (and, being static, also embedded) # in the editor executable. Modified: trunk/ktutorial/ktutorial-editor/src/EditActions.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/EditActions.cpp 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/src/EditActions.cpp 2010-09-25 03:33:38 UTC (rev 254) @@ -37,6 +37,10 @@ #include "view/TutorialCustomCodeWidget.h" #include "view/TutorialInformationWidget.h" +#ifdef QT_QTDBUS_FOUND +#include "TutorialTester.h" +#endif + //public: EditActions::EditActions(KTutorialEditor* tutorialEditor): @@ -207,6 +211,18 @@ action->setEnabled(false); actionCollection->addAction("removeReaction", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(removeReaction())); + +#ifdef QT_QTDBUS_FOUND + action = new KAction(this); + action->setText(i18nc("@action", "Test tutorial")); + action->setStatusTip(i18nc("@info:status", "Starts the tutorial in the " +"target application.")); + action->setIcon(KIcon("document-preview")); + action->setEnabled(true); + actionCollection->addAction("testTutorial", action); + connect(action, SIGNAL(triggered(bool)), + new TutorialTester(mTutorialEditor), SLOT(testTutorial())); +#endif } int EditActions::showEditionDialog(CommandWidget* commandWidget) { Modified: trunk/ktutorial/ktutorial-editor/src/EditActions.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/EditActions.h 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/src/EditActions.h 2010-09-25 03:33:38 UTC (rev 254) @@ -30,7 +30,8 @@ /** * Edition related actions. * EditActions provide the actions to edit a tutorial (set the setup code, add a - * step...), and also actions to undo and redo the edition. + * step...), actions to undo and redo the edition, and also an action to test + * the current tutorial in the target application. * * KTutorialEditor notifies EditActions when a step or reaction is selected, so * it can know which step or reaction have to be edited. EditActions provides Added: trunk/ktutorial/ktutorial-editor/src/TutorialTester.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/TutorialTester.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/TutorialTester.cpp 2010-09-25 03:33:38 UTC (rev 254) @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "TutorialTester.h" + +#include <KLocalizedString> +#include <KMessageBox> +#include <KTemporaryFile> + +#include "KTutorialEditor.h" +#include "serialization/Serialization.h" +#include "targetapplication/RemoteEditorSupport.h" +#include "targetapplication/TargetApplication.h" +#include "view/TargetApplicationView.h" + +//public: + +TutorialTester::TutorialTester(KTutorialEditor* tutorialEditor): + QObject(tutorialEditor), + mTutorialEditor(tutorialEditor) { + + mTargetApplicationView = + new TargetApplicationView(TargetApplication::self(), mTutorialEditor); +} + +//public slots: + +void TutorialTester::testTutorial() { + if (TargetApplication::self()->remoteEditorSupport()) { + sendTutorialToTargetApplication(); + } else { + connect(TargetApplication::self(), SIGNAL(started()), + this, SLOT(sendTutorialToTargetApplication())); + + mTargetApplicationView->start(); + } +} + +//private slots: + +void TutorialTester::sendTutorialToTargetApplication() { + disconnect(TargetApplication::self(), SIGNAL(started()), + this, SLOT(sendTutorialToTargetApplication())); + + //As this TutorialTester is set as parent of the KTemporaryFile object, the + //file will be automatically removed when this TutorialTester is destroyed + KTemporaryFile* temporaryFile = new KTemporaryFile(); + temporaryFile->setAutoRemove(true); + temporaryFile->setParent(this); + temporaryFile->setSuffix(".js"); + temporaryFile->open(); + + const Tutorial* tutorial = mTutorialEditor->tutorial(); + try { + Serialization(mTutorialEditor). + exportTutorial(tutorial, "*.js", temporaryFile->fileName()); + } catch (IOException e) { + QString text = i18nc("@label", "There was a problem when trying to " +"save the tutorial to a temporary file (to be used by the target application " +"to test the tutorial):<nl/>%1", e.message()); + QString caption = i18nc("@title:window", "Tutorial could not be saved"); + KMessageBox::error(mTutorialEditor, text, caption); + delete temporaryFile; + return; + } + + try { + TargetApplication::self()->remoteEditorSupport()->testScriptedTutorial( + temporaryFile->fileName()); + } catch (DBusException e) { + QString text = i18nc("@label", "There was a problem when trying to " +"tell the target application to start the tutorial:<nl/>%1", e.message()); + QString caption = i18nc("@title:window", "Tutorial could not be " +"started"); + KMessageBox::error(mTutorialEditor, text, caption); + } +} Property changes on: trunk/ktutorial/ktutorial-editor/src/TutorialTester.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/TutorialTester.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/TutorialTester.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/TutorialTester.h 2010-09-25 03:33:38 UTC (rev 254) @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef TUTORIALTESTER_H +#define TUTORIALTESTER_H + +#include <QObject> + +class KTutorialEditor; +class TargetApplicationView; + +/** + * Utility class to test a tutorial in the target application, starting it if + * necessary. + */ +class TutorialTester: public QObject { +Q_OBJECT +public: + + /** + * Creates a new TutorialTester with the given KTutorialEditor. + * + * @param tutorialEditor The parent KTutorialEditor. + */ + explicit TutorialTester(KTutorialEditor* tutorialEditor); + +public Q_SLOTS: + + /** + * Tests the current tutorial in the target application. + * The tutorial is saved to a temporary file that will be removed when the + * editor is closed. + */ + void testTutorial(); + +private: + + /** + * The KTutorialEditor to work with. + */ + KTutorialEditor* mTutorialEditor; + + /** + * The TargetApplicationView used to start the target application. + */ + TargetApplicationView* mTargetApplicationView; + +private Q_SLOTS: + + /** + * Exports the current tutorial to a temporary Javascript file and sends the + * file name to the target application to test the tutorial. + */ + void sendTutorialToTargetApplication(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/TutorialTester.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/ktutorial-editorui.rc =================================================================== --- trunk/ktutorial/ktutorial-editor/src/ktutorial-editorui.rc 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/src/ktutorial-editorui.rc 2010-09-25 03:33:38 UTC (rev 254) @@ -27,6 +27,8 @@ <Action name="setReactionData"/> <Action name="removeReaction"/> </Menu> + <Separator/> + <Action name="testTutorial"/> </Menu> <Menu name="view"> <Menu name="panels"> @@ -40,5 +42,7 @@ <ToolBar name="mainToolBar"> <Text context="@title:menu">Main Toolbar</Text> <Action name="exportTutorial"/> + <Separator/> + <Action name="testTutorial"/> </ToolBar> </gui> Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.cpp 2010-09-25 03:33:38 UTC (rev 254) @@ -99,3 +99,11 @@ delete mRemoteEventSpy; mRemoteEventSpy = 0; } + +void RemoteEditorSupport::testScriptedTutorial(const QString& filename) + throw (DBusException) { + QDBusReply<void> reply = call("testScriptedTutorial", filename); + if (!reply.isValid()) { + throw DBusException(reply.error().message()); + } +} Modified: trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/src/targetapplication/RemoteEditorSupport.h 2010-09-25 03:33:38 UTC (rev 254) @@ -114,6 +114,14 @@ */ void disableEventSpy() throw (DBusException); + /** + * Tests the scripted tutorial stored in the file with the given name. + * The target application just starts the tutorial and shows it to the user. + * + * @param filename The name of the file that contains the tutorial to test. + */ + void testScriptedTutorial(const QString& filename) throw (DBusException); + private: /** Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2010-09-25 03:33:38 UTC (rev 254) @@ -132,6 +132,7 @@ QList<int> mStopHighlightingRemoteWidgetIds; int mEnableEventSpyCount; int mDisableEventSpyCount; + QList<QString> mTestScriptedTutorialFilenames; StubEditorSupport(QObject* parent = 0): QObject(parent), mEventSpy(0), @@ -171,6 +172,10 @@ QDBusConnection::sessionBus().unregisterObject("/ktutorial/EventSpy"); } + void testScriptedTutorial(const QString& filename) { + mTestScriptedTutorialFilenames.append(filename); + } + }; #endif Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2010-09-25 03:01:55 UTC (rev 253) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteEditorSupportTest.cpp 2010-09-25 03:33:38 UTC (rev 254) @@ -65,6 +65,9 @@ void testDisableEventSpyTwice(); void testDisableEventSpyWhenRemoteEditorSupportIsNotAvailable(); + void testTestScriptedTutorial(); + void testTestScriptedTutorialWhenRemoteEditorSupportIsNotAvailable(); + private: StubEditorSupport* mEditorSupport; @@ -328,6 +331,30 @@ EXPECT_EXCEPTION(remoteEditorSupport.disableEventSpy(), DBusException); } +void RemoteEditorSupportTest::testTestScriptedTutorial() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + RemoteEditorSupport remoteEditorSupport( + QDBusConnection::sessionBus().baseService(), &mapper); + + remoteEditorSupport.testScriptedTutorial("/some/file"); + + QCOMPARE(mEditorSupport->mTestScriptedTutorialFilenames.count(), 1); + QCOMPARE(mEditorSupport->mTestScriptedTutorialFilenames[0], + QString("/some/file")); +} + +void RemoteEditorSupportTest:: + testTestScriptedTutorialWhenRemoteEditorSupportIsNotAvailable() { + RemoteObjectMapper mapper(QDBusConnection::sessionBus().baseService()); + RemoteEditorSupport remoteEditorSupport( + QDBusConnection::sessionBus().baseService(), &mapper); + + QDBusConnection::sessionBus().unregisterObject("/ktutorial"); + + EXPECT_EXCEPTION(remoteEditorSupport.testScriptedTutorial("/some/file"), + DBusException); +} + QTEST_MAIN(RemoteEditorSupportTest) #include "RemoteEditorSupportTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-26 17:50:03
|
Revision: 258 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=258&view=rev Author: danxuliu Date: 2010-09-26 17:49:55 +0000 (Sun, 26 Sep 2010) Log Message: ----------- Add support for WaitForWindow to wait for a specific window to be shown. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialWriterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/view/NewWaitForWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForTreeItemTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.cpp trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.h trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.h trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.h trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.ui trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForWindowTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWindowTreeItemTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWindowWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt 2010-09-26 17:49:55 UTC (rev 258) @@ -7,6 +7,7 @@ WaitForEvent.cpp WaitForNot.cpp WaitForSignal.cpp + WaitForWindow.cpp ) kde4_add_library(ktutorial_editor_data ${ktutorial_editor_data_SRCS}) Added: trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "WaitForWindow.h" + +//public: + +WaitForWindow::WaitForWindow(QObject* parent): WaitFor(parent) { +} + +WaitFor* WaitForWindow::clone() const { + WaitForWindow* cloned = new WaitForWindow(); + cloned->setWindowObjectName(mWindowObjectName); + + return cloned; +} + +bool WaitForWindow::equals(const WaitFor& waitFor) const { + if (!qobject_cast<const WaitForWindow*>(&waitFor)) { + return false; + } + + const WaitForWindow* waitForWindow = + static_cast<const WaitForWindow*>(&waitFor); + if (waitForWindow->windowObjectName() != mWindowObjectName) { + return false; + } + + return true; +} + +QString WaitForWindow::windowObjectName() const { + return mWindowObjectName; +} + +void WaitForWindow::setWindowObjectName(const QString& windowObjectName) { + mWindowObjectName = windowObjectName; + + emit dataChanged(this); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.h 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef WAITFORWINDOW_H +#define WAITFORWINDOW_H + +#include "WaitFor.h" + +/** + * Container for conditions that wait for a window to be shown data. + * It stores the data used in KTutorial WaitForWindow, but it has nothing to do + * with it (they don't even know each other). Its purpose is store the data + * needed to generate the code to initialize a true KTutorial::WaitForWindow + * object. + * + * When any attribute is modified, dataChanged(WaitFor*) signal is emitted. + */ +class WaitForWindow: public WaitFor { +Q_OBJECT +public: + + /** + * Creates a new WaitForWindow. + * + * @param parent The parent QObject. + */ + WaitForWindow(QObject* parent = 0); + + virtual WaitFor* clone() const; + virtual bool equals(const WaitFor& waitFor) const; + + QString windowObjectName() const; + void setWindowObjectName(const QString& windowObjectName); + +private: + + QString mWindowObjectName; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/data/WaitForWindow.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -28,6 +28,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" //public: @@ -266,6 +267,9 @@ if (qobject_cast<const WaitForSignal*>(waitFor)) { return writeWaitFor(static_cast<const WaitForSignal*>(waitFor)); } + if (qobject_cast<const WaitForWindow*>(waitFor)) { + return writeWaitFor(static_cast<const WaitForWindow*>(waitFor)); + } return ""; } @@ -362,6 +366,24 @@ return variable; } +QString JavascriptExporter::writeWaitFor(const WaitForWindow* waitForWindow) { + if (waitForWindow->windowObjectName().isEmpty()) { + out() << "//Error: WaitForWindow without window object name!\n"; + return ""; + } + + QString variable = "waitFor" + + toUpperCamelCase(waitForWindow->windowObjectName()) + + "ToBeShown"; + variable = addVariable(variable); + + out() << variable << " = ktutorial.newWaitFor(\"WaitForWindow\");\n"; + out() << variable << ".setWindowObjectName(\"" + << escape(waitForWindow->windowObjectName()) << "\");\n"; + + return variable; +} + QTextStream& JavascriptExporter::out() { mOut << indentation(); return mOut; Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2010-09-26 17:49:55 UTC (rev 258) @@ -31,6 +31,7 @@ class WaitForEvent; class WaitForNot; class WaitForSignal; +class WaitForWindow; /** * Exporter of tutorials to Javascript code. @@ -214,6 +215,16 @@ QString writeWaitFor(const WaitForSignal* waitForSignal); /** + * Writes the code to create and set a WaitForWindow. + * If the window object name isn't set, an error message is written instead, + * and an empty string returned. + * + * @param waitForWindow The WaitForWindow. + * @return The name of the variable that holds the WaitFor. + */ + QString writeWaitFor(const WaitForWindow* waitForWindow); + + /** * Returns the output stream after adding the identation text. * It is used as a shortcut to "mOut << indentation()". Use it to output any * line that has no previous indentation. Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -29,6 +29,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" //public: @@ -167,6 +168,9 @@ if (element.tagName() == "waitForSignal") { return readWaitForSignal(element); } + if (element.tagName() == "waitForWindow") { + return readWaitForWindow(element); + } Q_ASSERT(false); return 0; @@ -238,11 +242,23 @@ return waitForSignal; } +WaitFor* TutorialReader::readWaitForWindow(const QDomElement& element) { + WaitForWindow* waitForWindow = new WaitForWindow(); + + if (element.hasAttribute("windowObjectName")) { + waitForWindow->setWindowObjectName( + element.attribute("windowObjectName")); + } + + return waitForWindow; +} + bool TutorialReader::isWaitForElement(const QDomElement& element) { if (element.tagName() != "waitForComposed" && element.tagName() != "waitForEvent" && element.tagName() != "waitForNot" && - element.tagName() != "waitForSignal") { + element.tagName() != "waitForSignal" && + element.tagName() != "waitForWindow") { return false; } Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h 2010-09-26 17:49:55 UTC (rev 258) @@ -32,6 +32,7 @@ class WaitForEvent; class WaitForNot; class WaitForSignal; +class WaitForWindow; /** * Deserializer for tutorials stored in XML. @@ -135,6 +136,14 @@ WaitFor* readWaitForSignal(const QDomElement& element); /** + * Reads a new WaitForWindow from the "waitForWindow" XML element. + * + * @param element The element to read the WaitForWindow from. + * @return The new WaitForWindow. + */ + WaitFor* readWaitForWindow(const QDomElement& element); + + /** * Returns whether the given element is one of the WaitFor elements or not. * * @param element The element to check. Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -25,6 +25,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" //public: @@ -152,6 +153,10 @@ write(static_cast<const WaitForSignal*>(waitFor)); return; } + if (qobject_cast<const WaitForWindow*>(waitFor)) { + write(static_cast<const WaitForWindow*>(waitFor)); + return; + } } void TutorialWriter::write(const WaitForComposed* waitForComposed) { @@ -204,3 +209,12 @@ mXmlWriter->writeAttribute("signalName", waitForSignal->signalName()); } } + +void TutorialWriter::write(const WaitForWindow* waitForWindow) { + mXmlWriter->writeEmptyElement("waitForWindow"); + + if (!waitForWindow->windowObjectName().isEmpty()) { + mXmlWriter->writeAttribute("windowObjectName", + waitForWindow->windowObjectName()); + } +} Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h 2010-09-26 17:49:55 UTC (rev 258) @@ -29,6 +29,7 @@ class WaitForEvent; class WaitForNot; class WaitForSignal; +class WaitForWindow; /** * Serializes a Tutorial in XML. @@ -121,6 +122,13 @@ */ void write(const WaitForSignal* waitForSignal); + /** + * Writes the XML serialization of the given WaitForWindow. + * + * @param waitForWindow The WaitForWindow to get its XML serialization. + */ + void write(const WaitForWindow* waitForWindow); + }; #endif Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2010-09-26 17:49:55 UTC (rev 258) @@ -28,6 +28,8 @@ WaitForSignalWidget.cpp WaitForTreeItem.cpp WaitForWidget.cpp + WaitForWindowTreeItem.cpp + WaitForWindowWidget.cpp ) if (QT_QTDBUS_FOUND) @@ -54,6 +56,7 @@ WaitForEventWidget.ui WaitForSignalWidget.ui WaitForWidget.ui + WaitForWindowWidget.ui ) if (QT_QTDBUS_FOUND) Modified: trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -23,6 +23,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" //public: @@ -52,6 +53,8 @@ return new WaitForSignal(); } else if (index == 4) { return new WaitForEvent(); + } else if (index == 5) { + return new WaitForWindow(); } return 0; Modified: trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui 2010-09-26 17:49:55 UTC (rev 258) @@ -66,6 +66,11 @@ <string comment="@item:inlistbox">The specified event is received</string> </property> </item> + <item> + <property name="text"> + <string comment="@item:inlistbox">The specified window is shown</string> + </property> + </item> </widget> </item> </layout> Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -21,10 +21,12 @@ #include "WaitForEventTreeItem.h" #include "WaitForNotTreeItem.h" #include "WaitForSignalTreeItem.h" +#include "WaitForWindowTreeItem.h" #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" //public: @@ -50,6 +52,11 @@ parent); } + if (qobject_cast<WaitForWindow*>(waitFor)) { + return new WaitForWindowTreeItem(static_cast<WaitForWindow*>(waitFor), + parent); + } + Q_ASSERT(false); return 0; } Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -26,11 +26,13 @@ #include "TreeModel.h" #include "WaitForEventWidget.h" #include "WaitForSignalWidget.h" +#include "WaitForWindowWidget.h" #include "WaitForTreeItem.h" #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" //public: @@ -137,6 +139,14 @@ return; } + + if (qobject_cast<WaitForWindow*>(selectedWaitFor)) { + ui->addButton->setEnabled(false); + ui->editButton->setEnabled(true); + ui->removeButton->setEnabled(true); + + return; + } } //private slots: @@ -222,6 +232,12 @@ editionWidget = new WaitForSignalWidget(waitForSignal, this); } + if (qobject_cast<WaitForWindow*>(mCurrentWaitFor)) { + WaitForWindow* waitForWindow = + static_cast<WaitForWindow*>(mCurrentWaitFor); + editionWidget = new WaitForWindowWidget(waitForWindow, this); + } + Q_ASSERT(editionWidget); EditionDialog* dialog = new EditionDialog(editionWidget, this); Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "WaitForWindowTreeItem.h" + +#include <KLocalizedString> + +#include "../data/WaitForWindow.h" + +//public: + +WaitForWindowTreeItem::WaitForWindowTreeItem(WaitForWindow* waitForWindow, + TreeItem* parent): + WaitForTreeItem(waitForWindow, parent) { + mWindowObjectName = waitForWindow->windowObjectName(); + + connect(waitForWindow, SIGNAL(dataChanged(WaitFor*)), + this, SLOT(update(WaitFor*))); +} + +QString WaitForWindowTreeItem::text() const { + QString windowObjectName; + if (mWindowObjectName.isEmpty()) { + windowObjectName = i18nc("@item", "(object name not set)"); + } else { + windowObjectName = "\"" + mWindowObjectName + "\""; + } + + return i18nc("@item", "When the window %1 is shown", windowObjectName); +} + +//private: + +void WaitForWindowTreeItem::update(WaitFor* waitFor) { + WaitForWindow* waitForWindow = static_cast<WaitForWindow*>(waitFor); + mWindowObjectName = waitForWindow->windowObjectName(); + + emit dataChanged(this); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.h 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef WAITFORWINDOWTREEITEM_H +#define WAITFORWINDOWTREEITEM_H + +#include "WaitForTreeItem.h" + +class WaitForWindow; + +/** + * A TreeItem that represents a WaitForWindow. + * The tree representation of a WaitForWindow is a plain text: + * When the window "object name" is shown + * + * If the window object name isn't set yet, a placeholder is put instead. The + * object name placeholder is "(object name not set)" (without quotes, but with + * parenthesis). + * + * Whenever the WaitForWindow data changes, the WaitForWindowTreeItem text is + * updated as needed. + */ +class WaitForWindowTreeItem: public WaitForTreeItem { +Q_OBJECT +public: + + /** + * Creates a new WaitForWindowTreeItem for the given WaitForWindow and with + * the given parent. + * + * @param waitForWindow The WaitForWindow to represent. + * @param parent The parent TreeItem. + */ + explicit WaitForWindowTreeItem(WaitForWindow* waitForWindow, + TreeItem* parent = 0); + + /** + * Returns the description of the WaitForWindow. + * + * @return The text for this TreeItem. + */ + virtual QString text() const; + +private: + + /** + * The window object name of the WaitForWindow. + */ + QString mWindowObjectName; + +private Q_SLOTS: + + /** + * Updates this WaitForWindowTreeItem when the data of its WaitForWindow + * changed. + * + * @param waitFor The WaitForWindow. + */ + void update(WaitFor* waitFor); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowTreeItem.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "WaitForWindowWidget.h" + +#include "ui_WaitForWindowWidget.h" +#include "../data/WaitForWindow.h" + +#ifdef QT_QTDBUS_FOUND +#include "RemoteObjectNameWidget.h" +#endif + +//public: + +WaitForWindowWidget::WaitForWindowWidget(WaitForWindow* waitForWindow, + QWidget* parent): + EditionWidget(parent), + mWaitForWindow(waitForWindow) { + + ui = new Ui::WaitForWindowWidget(); + ui->setupUi(this); + +#ifdef QT_QTDBUS_FOUND + //Replace ui->windowObjectNameLineEdit with mRemoteObjectNameWidget + ui->valueVerticalLayout->removeWidget(ui->windowObjectNameLineEdit); + delete ui->windowObjectNameLineEdit; + + mRemoteObjectNameWidget = new RemoteObjectNameWidget(this); + ui->valueVerticalLayout->insertWidget(0, mRemoteObjectNameWidget); + + mRemoteObjectNameWidget->setName(waitForWindow->windowObjectName()); +#else + ui->windowObjectNameLineEdit->setText(waitForWindow->windowObjectName()); +#endif +} + +WaitForWindowWidget::~WaitForWindowWidget() { + delete ui; +} + +void WaitForWindowWidget::saveChanges() { +#ifdef QT_QTDBUS_FOUND + QString windowObjectName = mRemoteObjectNameWidget->name(); +#else + QString windowObjectName = ui->windowObjectNameLineEdit->text(); +#endif + if (mWaitForWindow->windowObjectName() != windowObjectName) { + mWaitForWindow->setWindowObjectName(windowObjectName); + } +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.h 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef WAITFORWINDOWWIDGET_H +#define WAITFORWINDOWWIDGET_H + +#include "EditionWidget.h" + +#ifdef QT_QTDBUS_FOUND +class RemoteObjectNameWidget; +#endif + +class WaitForWindow; + +namespace Ui { +class WaitForWindowWidget; +} + +/** + * Edition widget for the condition to wait for a window to be shown. + */ +class WaitForWindowWidget: public EditionWidget { +Q_OBJECT +public: + + /** + * Creates a new WaitForWindowWidget for the given WaitForWindow. + * + * @param waitForWindow The WaitForWindow to set its data. + * @param parent The parent QWidget. + */ + explicit WaitForWindowWidget(WaitForWindow* waitForWindow, + QWidget* parent = 0); + + /** + * Destroys this widget. + */ + virtual ~WaitForWindowWidget(); + + /** + * Saves the window object name in the WaitForWindow. + */ + virtual void saveChanges(); + +private: + + /** + * The WaitForWindow to edit. + */ + WaitForWindow* mWaitForWindow; + + /** + * The Ui Designer generated class. + */ + Ui::WaitForWindowWidget* ui; + +#ifdef QT_QTDBUS_FOUND + /** + * The widget to get the name of a remote object. + */ + RemoteObjectNameWidget* mRemoteObjectNameWidget; +#endif + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.ui (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWindowWidget.ui 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>WaitForWindowWidget</class> + <widget class="QWidget" name="WaitForWindowWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string comment="@title">Edit window to wait for</string> + </property> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>Set the object name of the window to wait for.</para></string> + </property> + <layout class="QVBoxLayout" name="WaitForWindowVerticalLayout"> + <item> + <widget class="QGroupBox" name="waitForWindowGroupBox"> + <property name="title"> + <string comment="@title:group">Wait for window</string> + </property> + <layout class="QHBoxLayout" name="waitForWindowGroupBoxHorizontalLayout"> + <item> + <layout class="QVBoxLayout" name="labelVerticalLayout"> + <item> + <widget class="QLabel" name="windowObjectNameLabel"> + <property name="text"> + <string comment="@label:textbox">Window name:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="valueVerticalLayout"> + <item> + <widget class="KLineEdit" name="windowObjectNameLineEdit"> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>The name of the window to be shown.</para> +<para>Note that the name is not the title of the window or its class name, but the string returned by its objectName() method.</para> +<para>Also note that it can wait either for windows or for dialogs, not just for windows.</para></string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="waitForWindowWidgetSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> Modified: trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt 2010-09-26 17:49:55 UTC (rev 258) @@ -20,6 +20,7 @@ WaitForEvent WaitForNot WaitForSignal + WaitForWindow ) MACRO(MEM_TESTS) @@ -37,4 +38,5 @@ WaitForEvent WaitForNot WaitForSignal + WaitForWindow ) Added: trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForWindowTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForWindowTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForWindowTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "WaitForWindow.h" + +class WaitForWindowTest: public QObject { +Q_OBJECT + +private slots: + + void initTestCase(); + + void testConstructor(); + + void testClone(); + + void testEquals(); + + void testSetWindowObjectName(); + +private: + + int mWaitForStarType; + + void assertWaitForWindow(const QSignalSpy& spy, int index, + WaitFor* waitFor); + +}; + +class StubWaitFor: public WaitFor { +Q_OBJECT +public: + + int mValue; + + StubWaitFor(int value = 0): mValue(value) { + } + + virtual WaitFor* clone() const { + return new StubWaitFor(mValue); + } + + virtual bool equals(const WaitFor& waitFor) const { + if (!qobject_cast<const StubWaitFor*>(&waitFor)) { + return false; + } + + return mValue == static_cast<const StubWaitFor*>(&waitFor)->mValue; + } +}; + +void WaitForWindowTest::initTestCase() { + //WaitFor* must be registered in order to be used with QSignalSpy + mWaitForStarType = qRegisterMetaType<WaitFor*>("WaitFor*"); +} + +void WaitForWindowTest::testConstructor() { + QObject parent; + WaitForWindow* waitForWindow = new WaitForWindow(&parent); + + QCOMPARE(waitForWindow->parent(), &parent); +} + +void WaitForWindowTest::testClone() { + WaitForWindow waitForWindow; + waitForWindow.setWindowObjectName("The window object name"); + + WaitForWindow* cloned = static_cast<WaitForWindow*>(waitForWindow.clone()); + + QVERIFY(cloned != &waitForWindow); + QCOMPARE(cloned->windowObjectName(), waitForWindow.windowObjectName()); + delete cloned; +} + +void WaitForWindowTest::testEquals() { + WaitForWindow waitForWindow1; + waitForWindow1.setWindowObjectName("The window object name"); + WaitForWindow waitForWindow2; + + QCOMPARE(waitForWindow1 == waitForWindow2, false); + QCOMPARE(waitForWindow2 == waitForWindow1, false); + + waitForWindow2.setWindowObjectName("The window object name"); + + QCOMPARE(waitForWindow1 == waitForWindow2, true); + QCOMPARE(waitForWindow2 == waitForWindow1, true); + + StubWaitFor stubWaitFor; + + QCOMPARE(waitForWindow1 == stubWaitFor, false); +} + +void WaitForWindowTest::testSetWindowObjectName() { + WaitForWindow waitForWindow; + + QSignalSpy dataChangedSpy(&waitForWindow, SIGNAL(dataChanged(WaitFor*))); + + waitForWindow.setWindowObjectName("The window object name"); + + QCOMPARE(waitForWindow.windowObjectName(), + QString("The window object name")); + QCOMPARE(dataChangedSpy.count(), 1); + assertWaitForWindow(dataChangedSpy, 0, &waitForWindow); +} + +//WaitFor* must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(WaitFor*); + +/////////////////////////////////// Helpers //////////////////////////////////// + +void WaitForWindowTest::assertWaitForWindow(const QSignalSpy& spy, int index, + WaitFor* waitFor) { + QCOMPARE(spy.at(index).count(), 1); + + QVariant argument = spy.at(index).at(0); + QCOMPARE(argument.userType(), mWaitForStarType); + QCOMPARE(qvariant_cast<WaitFor*>(argument), waitFor); +} + +QTEST_MAIN(WaitForWindowTest) + +#include "WaitForWindowTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForWindowTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -27,6 +27,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" #define TUTORIAL_EMPTY_INFORMATION_CODE \ "t = Kross.module(\"kdetranslation\");\n"\ @@ -89,6 +90,9 @@ void testWaitForSignal(); void testWaitForSignalWithEscapeSequences(); void testWaitForSignalWithoutEmitterNameOrSignalName(); + void testWaitForWindow(); + void testWaitForWindowWithEscapeSequences(); + void testWaitForWindowWithoutWindowObjectName(); void testWaitForComposed(); void testWaitForComposedWithInvalidChildWaitFor(); void testWaitForNot(); @@ -1217,6 +1221,106 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testWaitForWindow() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForWindow* waitForWindow = new WaitForWindow(); + waitForWindow->setWindowObjectName("The window object name"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForWindow); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForTheWindowObjectNameToBeShown = \ +ktutorial.newWaitFor(\"WaitForWindow\");\n" +" waitForTheWindowObjectNameToBeShown.setWindowObjectName(\ +\"The window object name\");\n" +" step.addWaitFor(waitForTheWindowObjectNameToBeShown, \"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + +void JavascriptExporterTest::testWaitForWindowWithEscapeSequences() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForWindow* waitForWindow = new WaitForWindow(); + waitForWindow->setWindowObjectName("The \"window\" object name"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForWindow); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForTheWindowObjectNameToBeShown = \ +ktutorial.newWaitFor(\"WaitForWindow\");\n" +" waitForTheWindowObjectNameToBeShown.setWindowObjectName(\ +\"The \\\"window\\\" object name\");\n" +" step.addWaitFor(waitForTheWindowObjectNameToBeShown, \"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + +void JavascriptExporterTest::testWaitForWindowWithoutWindowObjectName() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForWindow* waitForWindow = new WaitForWindow(); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForWindow); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" //Error: WaitForWindow without window object name!\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + void JavascriptExporterTest::testWaitForComposed() { Tutorial tutorial; Step* step = new Step(); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -29,6 +29,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" #define HEADER_XML \ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" @@ -88,6 +89,8 @@ void testWaitForEventEmpty(); void testWaitForSignal(); void testWaitForSignalEmpty(); + void testWaitForWindow(); + void testWaitForWindowEmpty(); void testWaitForComposed(); void testWaitForComposedEmpty(); void testWaitForNot(); @@ -399,6 +402,38 @@ assertWaitForSignal(waitFor, "", ""); } +void TutorialReaderTest::testWaitForWindow() { + QString data = +WAITFOR_PARENT_START +" <waitForWindow windowObjectName=\"The "window" object \ +name\"/>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForWindow* waitForWindow = qobject_cast<WaitForWindow*>(waitFor); + QVERIFY(waitForWindow); + QCOMPARE(waitForWindow->windowObjectName(), + QString("The \"window\" object name")); +} + +void TutorialReaderTest::testWaitForWindowEmpty() { + QString data = +WAITFOR_PARENT_START +" <waitForWindow/>\n" +WAITFOR_PARENT_END; + + TutorialReader reader; + QScopedPointer<Tutorial> tutorial(reader.readTutorial(data)); + WaitFor* waitFor = tutorial->steps()[0]->reactions()[0]->waitFor(); + + WaitForWindow* waitForWindow = qobject_cast<WaitForWindow*>(waitFor); + QVERIFY(waitForWindow); + QCOMPARE(waitForWindow->windowObjectName(), QString("")); +} + void TutorialReaderTest::testWaitForComposed() { QString data = WAITFOR_PARENT_START Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialWriterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialWriterTest.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialWriterTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -27,6 +27,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" #define HEADER_XML \ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" @@ -86,6 +87,8 @@ void testWaitForEventEmpty(); void testWaitForSignal(); void testWaitForSignalEmpty(); + void testWaitForWindow(); + void testWaitForWindowEmpty(); void testWaitForComposed(); void testWaitForComposedEmpty(); void testWaitForNot(); @@ -440,6 +443,56 @@ QCOMPARE(savedTutorial, expected); } +void TutorialWriterTest::testWaitForWindow() { + Tutorial tutorial; + Step* step = new Step(); + tutorial.addStep(step); + + WaitForWindow* waitForWindow = new WaitForWindow(); + waitForWindow->setWindowObjectName("The \"window\" object name"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForWindow); + reaction->setResponseType(Reaction::NextStep); + step->addReaction(reaction); + + TutorialWriter saver; + QString savedTutorial = saver.writeTutorial(&tutorial); + + QString expected = +WAITFOR_PARENT_START +" <waitForWindow windowObjectName=\"The "window" object \ +name\"/>\n" +WAITFOR_PARENT_END; + + QCOMPARE(savedTutorial, expected); +} + +void TutorialWriterTest::testWaitForWindowEmpty() { + Tutorial tutorial; + Step* step = new Step(); + tutorial.addStep(step); + + WaitForWindow* waitForWindow = new WaitForWindow(); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForWindow); + reaction->setResponseType(Reaction::NextStep); + step->addReaction(reaction); + + TutorialWriter saver; + QString savedTutorial = saver.writeTutorial(&tutorial); + + QString expected = +WAITFOR_PARENT_START +" <waitForWindow/>\n" +WAITFOR_PARENT_END; + + QCOMPARE(savedTutorial, expected); +} + void TutorialWriterTest::testWaitForComposed() { Tutorial tutorial; Step* step = new Step(); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2010-09-26 17:49:55 UTC (rev 258) @@ -45,6 +45,8 @@ WaitForSignalWidget WaitForTreeItem WaitForWidget + WaitForWindowTreeItem + WaitForWindowWidget ) if (QT_QTDBUS_FOUND) @@ -94,6 +96,8 @@ WaitForSignalWidget WaitForTreeItem WaitForWidget + WaitForWindowTreeItem + WaitForWindowWidget ) if (QT_QTDBUS_FOUND) Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/NewWaitForWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/NewWaitForWidgetTest.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/NewWaitForWidgetTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -26,6 +26,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" class NewWaitForWidgetTest: public QObject { Q_OBJECT @@ -39,6 +40,7 @@ void testWaitForWhenNotConditionIsSelected(); void testWaitForWhenSignalConditionIsSelected(); void testWaitForWhenEventConditionIsSelected(); + void testWaitForWhenWindowConditionIsSelected(); private: @@ -105,6 +107,16 @@ delete waitFor; } +void NewWaitForWidgetTest::testWaitForWhenWindowConditionIsSelected() { + NewWaitForWidget widget; + + selectOption(&widget, 5); + + WaitForWindow* waitFor = qobject_cast<WaitForWindow*>(widget.waitFor()); + QVERIFY(waitFor); + delete waitFor; +} + /////////////////////////////////// Helpers //////////////////////////////////// void NewWaitForWidgetTest::selectOption(NewWaitForWidget* widget, Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForTreeItemTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForTreeItemTest.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForTreeItemTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -24,11 +24,13 @@ #include "WaitForEventTreeItem.h" #include "WaitForNotTreeItem.h" #include "WaitForSignalTreeItem.h" +#include "WaitForWindowTreeItem.h" #include "../data/WaitFor.h" #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" class WaitForTreeItemTest: public QObject { Q_OBJECT @@ -41,6 +43,7 @@ void testTreeItemForWaitForEvent(); void testTreeItemForWaitForNot(); void testTreeItemForWaitForSignal(); + void testTreeItemForWaitForWindow(); }; @@ -144,6 +147,20 @@ delete item; } +void WaitForTreeItemTest::testTreeItemForWaitForWindow() { + WaitForWindow waitFor; + StubTreeItem parent; + + WaitForTreeItem* item = WaitForTreeItem::treeItemForWaitFor(&waitFor, + &parent); + + QVERIFY(qobject_cast<WaitForWindowTreeItem*>(item)); + QCOMPARE(item->parent(), &parent); + QCOMPARE(item->waitFor(), &waitFor); + + delete item; +} + QTEST_MAIN(WaitForTreeItemTest) #include "WaitForTreeItemTest.moc" Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp 2010-09-25 20:16:03 UTC (rev 257) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -31,6 +31,7 @@ #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" #include "../data/WaitForSignal.h" +#include "../data/WaitForWindow.h" class WaitForWidgetTest: public QObject { Q_OBJECT @@ -41,6 +42,7 @@ void setEventData() const; void setSignalData() const; + void setWindowData() const; private slots: @@ -56,6 +58,7 @@ void testSelectWaitForNotEmpty(); void testSelectWaitForEvent(); void testSelectWaitForSignal(); + void testSelectWaitForWindow(); void testClearSelection(); void testAddWaitForWhenEmpty(); @@ -65,6 +68,7 @@ void testEditWaitForEvent(); void testEditWaitForSignal(); + void testEditWaitForWindow(); void testRemoveWaitForFromRoot(); void testRemoveWaitForFromWaitForComposed(); @@ -76,6 +80,7 @@ WaitForSignal* mWaitFor1; WaitForComposed* mWaitFor2; WaitForEvent* mWaitFor2_1; + WaitForWindow* mWaitFor2_2; WaitForNot* mWaitFor3; WaitForSignal* mWaitFor3_1; WaitForNot* mWaitFor4; @@ -114,6 +119,10 @@ mWaitFor2_1->setEventName("event"); mWaitFor2->addWaitFor(mWaitFor2_1); + mWaitFor2_2 = new WaitForWindow(); + mWaitFor2_2->setWindowObjectName("windowObject"); + mWaitFor2->addWaitFor(mWaitFor2_2); + mWaitFor3 = new WaitForNot(); mWaitFor->addWaitFor(mWaitFor3); @@ -187,6 +196,12 @@ assertButtonEnabled(false, true, true); } +void WaitForWidgetTest::testSelectWaitForWindow() { + selectItem(getIndex(1, getIndex(1, getIndex(0)))); + + assertButtonEnabled(false, true, true); +} + void WaitForWidgetTest::testClearSelection() { selectItem(getIndex(0)); selectItem(QModelIndex()); @@ -235,10 +250,11 @@ QCOMPARE(mWaitFor->waitFors()[2], mWaitFor3); QCOMPARE(mWaitFor->waitFors()[3], mWaitFor4); QVERIFY(qobject_cast<WaitForSignal*>(mWaitFor->waitFors()[4])); - QCOMPARE(mWaitFor2->waitFors().count(), 2); + QCOMPARE(mWaitFor2->waitFors().count(), 3); QCOMPARE(mWaitFor2->waitFors()[0], mWaitFor2_1); - QVERIFY(qobject_cast<WaitForSignal*>(mWaitFor2->waitFors()[1])); - QVERIFY(mWaitFor->waitFors()[4] != mWaitFor2->waitFors()[1]); + QCOMPARE(mWaitFor2->waitFors()[1], mWaitFor2_2); + QVERIFY(qobject_cast<WaitForSignal*>(mWaitFor2->waitFors()[2])); + QVERIFY(mWaitFor->waitFors()[4] != mWaitFor2->waitFors()[2]); } void WaitForWidgetTest::testAddWaitForToWaitForNot() { @@ -307,6 +323,20 @@ QCOMPARE(mWaitFor1->signalName(), QString("The new signal name")); } +void WaitForWidgetTest::testEditWaitForWindow() { + selectItem(getIndex(1, getIndex(1, getIndex(0)))); + + //The dialog is modal, so it won't return to the test code until it is + //closed. Thus, the commands to execute on the dialog must be "queued", + //as calling setWindowData after the button click won't work. + QTimer::singleShot(500, this, SLOT(setWindowData())); + + button("editButton")->click(); + + QCOMPARE(mWaitFor2_2->windowObjectName(), + QString("The new window object name")); +} + void WaitForWidgetTest::testRemoveWaitForFromRoot() { delete mWidget; WaitForSignal* waitForSignal = new WaitForSignal(); @@ -400,6 +430,21 @@ dialog->button(KDialog::Ok)->click(); } +void WaitForWidgetTest::setWindowData() const { + KLineEdit* windowObjectNameLineEdit = +#ifdef QT_QTDBUS_FOUND + mWidget->findChild<KLineEdit*>("objectNameLineEdit"); +#else + mWidget->findChild<KLineEdit*>("windowObjectNameLineEdit"); +#endif + QVERIFY(windowObjectNameLineEdit); + windowObjectNameLineEdit->setText("The new window object name"); + + EditionDialog* dialog = mWidget->findChild<EditionDialog*>("editionDialog"); + QVERIFY(dialog); + dialog->button(KDialog::Ok)->click(); +} + QModelIndex WaitForWidgetTest::getIndex(int row, const QModelIndex& parent) const { QTreeView* tree = mWidget->findChild<QTreeView*>("waitForTreeView"); Added: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWindowTreeItemTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWindowTreeItemTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWindowTreeItemTest.cpp 2010-09-26 17:49:55 UTC (rev 258) @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2010 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY... [truncated message content] |
From: <dan...@us...> - 2010-09-26 21:00:56
|
Revision: 261 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=261&view=rev Author: danxuliu Date: 2010-09-26 21:00:50 +0000 (Sun, 26 Sep 2010) Log Message: ----------- Set a different object name for each EditionDialog based on the CommandWidget it contains. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/EditActions.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/EditActions.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/EditActions.cpp 2010-09-26 18:49:53 UTC (rev 260) +++ trunk/ktutorial/ktutorial-editor/src/EditActions.cpp 2010-09-26 21:00:50 UTC (rev 261) @@ -228,8 +228,11 @@ int EditActions::showEditionDialog(CommandWidget* commandWidget) { commandWidget->setUndoStack(mUndoStack); + QString dialogName = commandWidget->objectName(); + dialogName.replace("Widget", "Dialog"); + EditionDialog* dialog = new EditionDialog(commandWidget, mTutorialEditor); - dialog->setObjectName("editionDialog"); + dialog->setObjectName(dialogName); return DialogRunner(dialog).exec(); } Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp 2010-09-26 18:49:53 UTC (rev 260) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp 2010-09-26 21:00:50 UTC (rev 261) @@ -240,8 +240,11 @@ Q_ASSERT(editionWidget); + QString dialogName = "edit" + editionWidget->objectName(); + dialogName.replace("Widget", "Dialog"); + EditionDialog* dialog = new EditionDialog(editionWidget, this); - dialog->setObjectName("editionDialog"); + dialog->setObjectName(dialogName); DialogRunner(dialog).exec(); } Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp 2010-09-26 18:49:53 UTC (rev 260) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp 2010-09-26 21:00:50 UTC (rev 261) @@ -405,7 +405,8 @@ QVERIFY(eventNameLineEdit); eventNameLineEdit->setText("The new event name"); - EditionDialog* dialog = mWidget->findChild<EditionDialog*>("editionDialog"); + EditionDialog* dialog = + mWidget->findChild<EditionDialog*>("editWaitForEventDialog"); QVERIFY(dialog); dialog->button(KDialog::Ok)->click(); } @@ -425,7 +426,8 @@ QVERIFY(signalNameLineEdit); signalNameLineEdit->setText("The new signal name"); - EditionDialog* dialog = mWidget->findChild<EditionDialog*>("editionDialog"); + EditionDialog* dialog = + mWidget->findChild<EditionDialog*>("editWaitForSignalDialog"); QVERIFY(dialog); dialog->button(KDialog::Ok)->click(); } @@ -440,7 +442,8 @@ QVERIFY(windowObjectNameLineEdit); windowObjectNameLineEdit->setText("The new window object name"); - EditionDialog* dialog = mWidget->findChild<EditionDialog*>("editionDialog"); + EditionDialog* dialog = + mWidget->findChild<EditionDialog*>("editWaitForWindowDialog"); QVERIFY(dialog); dialog->button(KDialog::Ok)->click(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-27 16:17:16
|
Revision: 263 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=263&view=rev Author: danxuliu Date: 2010-09-27 16:17:07 +0000 (Mon, 27 Sep 2010) Log Message: ----------- Fix loading rich text markup as rich text instead of as plain text in the KTextEdits: in some places like code edition widgets rich text makes no sense, and in places like step description it must be loaded as plain text to be properly edited and saved (as there is no WYSIWYG edition yet). Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/LicenseWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/ReactionWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/StepCustomCodeWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/TutorialCustomCodeWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/TutorialInformationWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/LicenseWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/ReactionWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/StepCustomCodeWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/StepDataWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialCustomCodeWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialInformationWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/LicenseWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/LicenseWidget.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/src/view/LicenseWidget.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -30,7 +30,7 @@ ui = new Ui::LicenseWidget(); ui->setupUi(this); - ui->licenseTextEdit->setText(tutorial->licenseText()); + ui->licenseTextEdit->setPlainText(tutorial->licenseText()); } LicenseWidget::~LicenseWidget() { Modified: trunk/ktutorial/ktutorial-editor/src/view/ReactionWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/ReactionWidget.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/src/view/ReactionWidget.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -66,7 +66,7 @@ } ui->triggerOptionLineEdit->setText(reaction->optionName()); - ui->responseCodeTextEdit->setText(reaction->customCode()); + ui->responseCodeTextEdit->setPlainText(reaction->customCode()); ui->responseStepLineEdit->setText(reaction->nextStepId()); } Modified: trunk/ktutorial/ktutorial-editor/src/view/StepCustomCodeWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/StepCustomCodeWidget.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/src/view/StepCustomCodeWidget.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -36,7 +36,7 @@ ui->setupUi(this); if (type == Setup) { - ui->customCodeTextEdit->setText(step->customSetupCode()); + ui->customCodeTextEdit->setPlainText(step->customSetupCode()); ui->customCodeGroupBox->setTitle(i18nc("@title", "Step setup code")); setWindowTitle(i18nc("@title", "Set the step setup code")); setWhatsThis(i18nc("@info:whatsthis", "<para>Set the code to be " @@ -46,7 +46,7 @@ "must be written in the same programming language the tutorial will be " "exported to.</para>")); } else { - ui->customCodeTextEdit->setText(step->customTearDownCode()); + ui->customCodeTextEdit->setPlainText(step->customTearDownCode()); ui->customCodeGroupBox->setTitle(i18nc("@title", "Step tear down code")); setWindowTitle(i18nc("@title", "Set the step tear down code")); Modified: trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -31,7 +31,7 @@ ui->setupUi(this); ui->idLineEdit->setText(step->id()); - ui->textTextEdit->setText(step->text()); + ui->textTextEdit->setPlainText(step->text()); } StepDataWidget::~StepDataWidget() { Modified: trunk/ktutorial/ktutorial-editor/src/view/TutorialCustomCodeWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/TutorialCustomCodeWidget.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/src/view/TutorialCustomCodeWidget.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -36,7 +36,7 @@ ui->setupUi(this); if (type == Setup) { - ui->customCodeTextEdit->setText(tutorial->customSetupCode()); + ui->customCodeTextEdit->setPlainText(tutorial->customSetupCode()); ui->customCodeGroupBox->setTitle(i18nc("@title", "Tutorial setup code")); setWindowTitle(i18nc("@title", "Set the tutorial setup code")); @@ -47,7 +47,7 @@ "must be written in the same programming language the tutorial will be " "exported to.</para>")); } else { - ui->customCodeTextEdit->setText(tutorial->customTearDownCode()); + ui->customCodeTextEdit->setPlainText(tutorial->customTearDownCode()); ui->customCodeGroupBox->setTitle(i18nc("@title", "Tutorial tear down code")); setWindowTitle(i18nc("@title", "Set the tutorial tear down code")); Modified: trunk/ktutorial/ktutorial-editor/src/view/TutorialInformationWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/TutorialInformationWidget.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/src/view/TutorialInformationWidget.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -32,7 +32,7 @@ ui->setupUi(this); ui->nameLineEdit->setText(tutorial->name()); - ui->descriptionTextEdit->setText(tutorial->description()); + ui->descriptionTextEdit->setPlainText(tutorial->description()); } TutorialInformationWidget::~TutorialInformationWidget() { Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/LicenseWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/LicenseWidgetTest.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/LicenseWidgetTest.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -31,6 +31,7 @@ private slots: void testConstructor(); + void testConstructorWithRichText(); void testSaveChanges(); @@ -51,6 +52,18 @@ QCOMPARE(licenseTextEdit(widget)->toPlainText(), QString("The license")); } +void LicenseWidgetTest::testConstructorWithRichText() { + Tutorial tutorial; + tutorial.setLicenseText("<p>The license</p>"); + + QWidget parent; + LicenseWidget* widget = new LicenseWidget(&tutorial, &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(licenseTextEdit(widget)->toPlainText(), + QString("<p>The license</p>")); +} + void LicenseWidgetTest::testSaveChanges() { Tutorial tutorial; tutorial.setLicenseText("The license"); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/ReactionWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/ReactionWidgetTest.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/ReactionWidgetTest.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -41,6 +41,7 @@ private slots: void testConstructor(); + void testConstructorWithRichText(); void testSelectTriggerTypeOption(); void testSelectTriggerTypeCondition(); @@ -101,6 +102,36 @@ QString("The custom code")); } +void ReactionWidgetTest::testConstructorWithRichText() { + WaitFor* waitFor = new WaitForSignal(); + Reaction reaction; + reaction.setTriggerType(Reaction::ConditionMet); + reaction.setWaitFor(waitFor); + reaction.setOptionName("The option name"); + reaction.setResponseType(Reaction::CustomCode); + reaction.setCustomCode("<p>The custom code</p>"); + reaction.setNextStepId("The step id"); + + QWidget parent; + ReactionWidget* widget = new ReactionWidget(&reaction, &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QVERIFY(!triggerOptionRadioButton(widget)->isChecked()); + QVERIFY(!triggerOptionLineEdit(widget)->isEnabled()); + QCOMPARE(triggerOptionLineEdit(widget)->text(), QString("The option name")); + QVERIFY(triggerConditionRadioButton(widget)->isChecked()); + QVERIFY(triggerConditionWidget(widget)->isEnabled()); + QVERIFY(triggerConditionWidget(widget)->waitFor() != waitFor); + QVERIFY(*triggerConditionWidget(widget)->waitFor() == *waitFor); + QVERIFY(!responseStepRadioButton(widget)->isChecked()); + QVERIFY(!responseStepLineEdit(widget)->isEnabled()); + QCOMPARE(responseStepLineEdit(widget)->text(), QString("The step id")); + QVERIFY(responseCodeRadioButton(widget)->isChecked()); + QVERIFY(responseCodeTextEdit(widget)->isEnabled()); + QCOMPARE(responseCodeTextEdit(widget)->toPlainText(), + QString("<p>The custom code</p>")); +} + void ReactionWidgetTest::testSelectTriggerTypeOption() { Reaction reaction; reaction.setTriggerType(Reaction::ConditionMet); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/StepCustomCodeWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/StepCustomCodeWidgetTest.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/StepCustomCodeWidgetTest.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -31,7 +31,9 @@ private slots: void testConstructorSetup(); + void testConstructorSetupWithRichText(); void testConstructorTearDown(); + void testConstructorTearDownWithRichText(); void testSaveChangesSetup(); void testSaveChangesTearDown(); @@ -57,6 +59,21 @@ QString("The setup code")); } +void StepCustomCodeWidgetTest::testConstructorSetupWithRichText() { + Step step; + step.setCustomSetupCode("<p>The setup code</p>"); + + QWidget parent; + StepCustomCodeWidget* widget = + new StepCustomCodeWidget(&step, + StepCustomCodeWidget::Setup, + &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(customCodeTextEdit(widget)->toPlainText(), + QString("<p>The setup code</p>")); +} + void StepCustomCodeWidgetTest::testConstructorTearDown() { Step step; step.setCustomTearDownCode("The tear down code"); @@ -72,6 +89,21 @@ QString("The tear down code")); } +void StepCustomCodeWidgetTest::testConstructorTearDownWithRichText() { + Step step; + step.setCustomTearDownCode("<p>The tear down code</p>"); + + QWidget parent; + StepCustomCodeWidget* widget = + new StepCustomCodeWidget(&step, + StepCustomCodeWidget::TearDown, + &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(customCodeTextEdit(widget)->toPlainText(), + QString("<p>The tear down code</p>")); +} + void StepCustomCodeWidgetTest::testSaveChangesSetup() { Step step; step.setCustomSetupCode("The setup code"); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/StepDataWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/StepDataWidgetTest.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/StepDataWidgetTest.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -31,6 +31,7 @@ private slots: void testConstructor(); + void testConstructorWithRichText(); void testSaveChanges(); @@ -54,6 +55,19 @@ QCOMPARE(textTextEdit(widget)->toPlainText(), QString("The text")); } +void StepDataWidgetTest::testConstructorWithRichText() { + Step step; + step.setId("The id"); + step.setText("<p>The text</p>"); + + QWidget parent; + StepDataWidget* widget = new StepDataWidget(&step, &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(idLineEdit(widget)->text(), QString("The id")); + QCOMPARE(textTextEdit(widget)->toPlainText(), QString("<p>The text</p>")); +} + void StepDataWidgetTest::testSaveChanges() { Step step; step.setId("The id"); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialCustomCodeWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialCustomCodeWidgetTest.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialCustomCodeWidgetTest.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -31,7 +31,9 @@ private slots: void testConstructorSetup(); + void testConstructorSetupWithRichText(); void testConstructorTearDown(); + void testConstructorTearDownWithRichText(); void testSaveChangesSetup(); void testSaveChangesTearDown(); @@ -57,6 +59,21 @@ QString("The setup code")); } +void TutorialCustomCodeWidgetTest::testConstructorSetupWithRichText() { + Tutorial tutorial; + tutorial.setCustomSetupCode("<p>The setup code</p>"); + + QWidget parent; + TutorialCustomCodeWidget* widget = + new TutorialCustomCodeWidget(&tutorial, + TutorialCustomCodeWidget::Setup, + &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(customCodeTextEdit(widget)->toPlainText(), + QString("<p>The setup code</p>")); +} + void TutorialCustomCodeWidgetTest::testConstructorTearDown() { Tutorial tutorial; tutorial.setCustomTearDownCode("The tear down code"); @@ -72,6 +89,21 @@ QString("The tear down code")); } +void TutorialCustomCodeWidgetTest::testConstructorTearDownWithRichText() { + Tutorial tutorial; + tutorial.setCustomTearDownCode("<p>The tear down code</p>"); + + QWidget parent; + TutorialCustomCodeWidget* widget = + new TutorialCustomCodeWidget(&tutorial, + TutorialCustomCodeWidget::TearDown, + &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(customCodeTextEdit(widget)->toPlainText(), + QString("<p>The tear down code</p>")); +} + void TutorialCustomCodeWidgetTest::testSaveChangesSetup() { Tutorial tutorial; tutorial.setCustomSetupCode("The setup code"); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialInformationWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialInformationWidgetTest.cpp 2010-09-26 21:07:06 UTC (rev 262) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/TutorialInformationWidgetTest.cpp 2010-09-27 16:17:07 UTC (rev 263) @@ -31,6 +31,7 @@ private slots: void testConstructor(); + void testConstructorWithRichText(); void testSaveChanges(); @@ -56,6 +57,21 @@ QString("The description")); } +void TutorialInformationWidgetTest::testConstructorWithRichText() { + Tutorial tutorial; + tutorial.setName("The name"); + tutorial.setDescription("<p>The description</p>"); + + QWidget parent; + TutorialInformationWidget* widget = new TutorialInformationWidget(&tutorial, + &parent); + + QCOMPARE(widget->parentWidget(), &parent); + QCOMPARE(nameLineEdit(widget)->text(), QString("The name")); + QCOMPARE(descriptionTextEdit(widget)->toPlainText(), + QString("<p>The description</p>")); +} + void TutorialInformationWidgetTest::testSaveChanges() { Tutorial tutorial; tutorial.setName("The name"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-03-27 16:07:29
|
Revision: 294 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=294&view=rev Author: danxuliu Date: 2011-03-27 16:07:21 +0000 (Sun, 27 Mar 2011) Log Message: ----------- -Add filters for named objects and widgets in the RemoteObjectChooser dialog. -Update RemoteObject and RemoteObjectNameWidget tests due to changes in RemoteClassStubs. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectTreeSelectionManagerTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.h trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserFilterModelTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-03-27 16:07:21 UTC (rev 294) @@ -37,6 +37,7 @@ set(ktutorial_editor_view_SRCS ${ktutorial_editor_view_SRCS} RemoteObjectChooser.cpp + RemoteObjectChooserFilterModel.cpp RemoteObjectNameWidget.cpp RemoteObjectTreeItem.cpp RemoteObjectTreeItemUpdater.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -25,6 +25,7 @@ #include <KDialogButtonBox> #include <KMessageBox> +#include "RemoteObjectChooserFilterModel.h" #include "RemoteObjectTreeItem.h" #include "RemoteObjectTreeItemUpdater.h" #include "RemoteObjectTreeSelectionManager.h" @@ -165,8 +166,17 @@ RemoteObject* mainWindow = remoteEditorSupport->mainWindow(); RemoteObjectTreeItem* rootItem = new RemoteObjectTreeItem(mainWindow); TreeModel* treeModel = new TreeModel(rootItem, this); - ui->remoteObjectsTreeView->setModel(treeModel); + RemoteObjectChooserFilterModel* filterModel = + new RemoteObjectChooserFilterModel(this); + filterModel->setSourceModel(treeModel); + ui->remoteObjectsTreeView->setModel(filterModel); + + connect(ui->showOnlyNamedObjectsCheckBox, SIGNAL(toggled(bool)), + filterModel, SLOT(setNamedObjectFilterEnabled(bool))); + connect(ui->showOnlyWidgetsCheckBox, SIGNAL(toggled(bool)), + filterModel, SLOT(setWidgetFilterEnabled(bool))); + RemoteObjectTreeItemUpdater* updater = new RemoteObjectTreeItemUpdater(this); rootItem->setUpdater(updater); Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.h 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -45,6 +45,11 @@ * When the user selects a remote object in the list and that object represents * a widget, the widget is highlighted in the target application. * + * The list shows all the available remote objects in the target application. + * Some of them may not be useful at all (for example, those without name), so + * the user can select some filters to narrow the remote objects shown in the + * list hiding those that do not pass the filters. + * * The window contains an "Ok" and a "Cancel" button, like the ones found in * dialogs. A window is used instead of a dialog to be shown in the task bar (as * after hidding the rest of KTutorial editor windows no entry would be shown if Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooser.ui 2011-03-27 16:07:21 UTC (rev 294) @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>400</width> - <height>300</height> + <height>400</height> </rect> </property> <property name="windowTitle"> @@ -20,6 +20,34 @@ </property> <layout class="QVBoxLayout" name="remoteObjectChooserVerticalLayout"> <item> + <widget class="QGroupBox" name="filtersGroupBox"> + <property name="whatsThis"> + <string comment="@info:whatsthis"><p>Filters the objects shown in the list.</p> + +<p>Note, however, that if an object has to be shown in the list its parent object is also shown even if it should have been filtered.</p></string> + </property> + <property name="title"> + <string comment="@title:group">Filters</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="showOnlyNamedObjectsCheckBox"> + <property name="text"> + <string comment="@option:check">Show only named objects</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="showOnlyWidgetsCheckBox"> + <property name="text"> + <string comment="@option:check">Show only widgets</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="AutoExpandableTreeView" name="remoteObjectsTreeView"/> </item> <item> Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -0,0 +1,120 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectChooserFilterModel.h" + +#include <QQueue> + +#include "RemoteObjectTreeItem.h" +#include "../targetapplication/RemoteClass.h" +#include "../targetapplication/RemoteObject.h" + +//public: + +RemoteObjectChooserFilterModel::RemoteObjectChooserFilterModel( + QObject* parent /* = 0*/): + QSortFilterProxyModel(parent), + mNamedObjectFilterEnabled(false), + mWidgetFilterEnabled(false) { +} + +//public slots: + +void RemoteObjectChooserFilterModel::setNamedObjectFilterEnabled( + bool namedObjectFilterEnabled) { + mNamedObjectFilterEnabled = namedObjectFilterEnabled; + invalidateFilter(); +} + +void RemoteObjectChooserFilterModel::setWidgetFilterEnabled( + bool widgetFilterEnabled) { + mWidgetFilterEnabled = widgetFilterEnabled; + invalidateFilter(); +} + +//protected: + +bool RemoteObjectChooserFilterModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const { + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + RemoteObjectTreeItem* item = static_cast<RemoteObjectTreeItem*>( + index.internalPointer()); + RemoteObject* remoteObject = item->remoteObject(); + Q_ASSERT(remoteObject); + + //Breadth-first traversal. No "scientific" reason to use it instead of + //depth-first, just the "feeling" that it will find widgets and named + //objects sooner than a depth-first approach. + QQueue<RemoteObject*> objects; + objects.append(remoteObject); + + while (!objects.isEmpty()) { + remoteObject = objects.dequeue(); + + try { + if (filterNamedObject(remoteObject) && filterWidget(remoteObject)) { + return true; + } + + objects.append(remoteObject->children()); + } catch (DBusException) { + return true; + } + } + + return false; +} + +//private: + +bool RemoteObjectChooserFilterModel::filterNamedObject( + RemoteObject* remoteObject) const +throw (DBusException) { + if (!mNamedObjectFilterEnabled || !remoteObject->name().isEmpty()) { + return true; + } + + return false; +} + +bool RemoteObjectChooserFilterModel::filterWidget( + RemoteObject* remoteObject) const +throw (DBusException) { + if (!mWidgetFilterEnabled || isWidget(remoteObject->remoteClass())) { + return true; + } + + return false; +} + +bool RemoteObjectChooserFilterModel::isWidget(RemoteClass* remoteClass) const +throw (DBusException) { + if (!remoteClass) { + return false; + } + + if (remoteClass->className() == "QWidget") { + return true; + } + + if (isWidget(remoteClass->superClass())) { + return true; + } + + return false; +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.h 2011-03-27 16:07:21 UTC (rev 294) @@ -0,0 +1,141 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTCHOOSERFILTERMODEL_H +#define REMOTEOBJECTCHOOSERFILTERMODEL_H + +#include <QSortFilterProxyModel> + +#include "../targetapplication/DBusException.h" + +class RemoteClass; +class RemoteObject; + +/** + * Proxy model to filter RemoteObjecTreeItem tree models. + * When applied on a TreeModel composed by RemoteObjectTreeItems it hides those + * items that do not pass the enabled filters. Note that the TreeModel must + * contain only RemoteObjectTreeItems; no other TreeItem types can appear in the + * source model. + * + * There are two available filters: named objects and widgets. Both filters + * check the RemoteObjects referenced by the RemoteObjectTreeItems. The first + * checks if it has a name, and the second checks if it is a widget. + * + * The filters can be enabled and disabled using + * setNamedObjectFilterEnabled(bool) and setWidgetFilterEnabled(bool). When both + * filters are disabled every item in the model is shown. + * + * When there are filters enabled, an item may be shown even if it does not pass + * them if any of its descendants does. + */ +class RemoteObjectChooserFilterModel: public QSortFilterProxyModel { +Q_OBJECT +public: + + /** + * Creates a new RemoteObjectChooserFilterModel. + * + * @param parent The parent object. + */ + RemoteObjectChooserFilterModel(QObject* parent = 0); + +public Q_SLOTS: + + /** + * Enables or disables the named object filter. + * + * @param namedObjectFilterEnabled If the filter has to be enabled or + * disabled. + */ + void setNamedObjectFilterEnabled(bool namedObjectFilterEnabled); + + /** + * Enables or disables the widget filter. + * + * @param widgetFilterEnabled If the filter has to be enabled or disabled. + */ + void setWidgetFilterEnabled(bool widgetFilterEnabled); + +protected: + + /** + * Returns true if the item indicated by the given sourceRow and + * sourceParent should be included in the model, false otherwise. + * An item is included if the item, or any of its children, passes the + * enabled filters. If no filter is enabled, every item is included in the + * model. + * Also, if there was a problem querying the remote objects the item is also + * included in the model. + * + * @param sourceRow The row of the index in the source model. + * @param sourceParent The parent of the index in the source model. + * @return True if the item passes the filter, false otherwise. + */ + virtual bool filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const; + +private: + + /** + * Whether the named object filter is enabled or not. + */ + bool mNamedObjectFilterEnabled; + + /** + * Whether the widget filter is enabled or not. + */ + bool mWidgetFilterEnabled; + + /** + * Checks if the given remote object passes the name object filter. + * The remote object passes the filter if the filter is not enabled, or if + * the filter is enabled and the remote object has a name. + * + * @param remoteObject The remote object to check. + * @return True if the remote object passes the named object filter, false + * otherwise. + * @throws DBusException If a DBus error happens. + */ + bool filterNamedObject(RemoteObject* remoteObject) const + throw (DBusException); + + /** + * Checks if the given remote object passes the widget filter. + * The remote object passes the filter if the filter is not enabled, or if + * the filter is enabled and the remote object is a widget. + * + * @param remoteObject The remote object to check. + * @return True if the remote object passes the widget filter, false + * otherwise. + * @throws DBusException If a DBus error happens. + */ + bool filterWidget(RemoteObject* remoteObject) const throw (DBusException); + + /** + * Returns true if the remote class is a widget, false otherwise. + * + * @param remoteClass The remote class to check. + * @return True if the remote class is a widget, false otherwise. + * @throws DBusException If a DBus error happens. + */ + bool isWidget(RemoteClass* remoteClass) const throw (DBusException); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectChooserFilterModel.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -18,6 +18,8 @@ #include "RemoteObjectTreeSelectionManager.h" +#include <QAbstractProxyModel> + #include "RemoteObjectTreeItem.h" //public: @@ -65,6 +67,17 @@ return getRemoteObjectForTreeItem(item->parent()); } +QModelIndex RemoteObjectTreeSelectionManager::getTreeModelIndex( + const QModelIndex& index) const { + const QAbstractProxyModel* proxyModel = + qobject_cast<const QAbstractProxyModel*>(index.model()); + if (proxyModel) { + return proxyModel->mapToSource(index); + } + + return index; +} + //private slots: void RemoteObjectTreeSelectionManager::handleSelectionChanged( @@ -81,6 +94,7 @@ Q_ASSERT(selected.at(0).indexes().count() == 1); QModelIndex index = selected.at(0).indexes().at(0); + index = getTreeModelIndex(index); selectedItem = static_cast<TreeItem*>(index.internalPointer()); } @@ -88,6 +102,7 @@ Q_ASSERT(deselected.at(0).indexes().count() == 1); QModelIndex index = deselected.at(0).indexes().at(0); + index = getTreeModelIndex(index); deselectedItem = static_cast<TreeItem*>(index.internalPointer()); } Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectTreeSelectionManager.h 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -26,9 +26,9 @@ class TreeItem; /** - * Watches the QItemSelectionModel of a TreeModel composed by - * RemoteObjectTreeItems for changes in the selection. - * When an item is selected in the TreeModel, + * Watches the QItemSelectionModel of a TreeModel (or a proxy to a TreeModel) + * composed by RemoteObjectTreeItems for changes in the selection. + * When an item is selected in the TreeModel (or its proxy), * remoteObjectSelected(RemoteObject*) signal is emitted with the RemoteObject * represented by the selected item. * @@ -82,6 +82,16 @@ */ RemoteObject* getRemoteObjectForTreeItem(TreeItem* item); + /** + * Ensures that the index comes from a tree model and not from a proxy + * model. + * + * @param index The index to "clean". + * @return The index mapped to the given one in the source model, or the + * given index if it already comes from a source model. + */ + QModelIndex getTreeModelIndex(const QModelIndex& index) const; + private Q_SLOTS: /** Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteClassStubs.h 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -69,6 +69,47 @@ } }; +/** + * ... + * |-42: "The object name 42" + * |-420: "The object name 420" + * |-421: "The object name 421" + * |-422: "The object name 422" + * |-423: "The object name 423" + * |-5: "Duplicated grandparent" + * | |-50: "The object name 50" + * | | |-500: "Duplicated object" + * | | |... + * | |... + * |-6: "Duplicated grandparent" + * | |-60: "Duplicated parent" + * | | |-600: "Duplicated object" + * | | |... + * | |... + * |-7: "The object name 7" + * | |-70: "Duplicated parent" + * | | |-700: "Duplicated object" + * | | |... + * | |... + * |-8: "The object name 8" + * |-80: "" (The class name 80) + * | |-800: "Duplicated object" + * | |-801: "The object name 801" + * | |-802: "" + * | |... + * |-81: "" (QWidgetChildChild) + * | |-810: "" + * | |-811: "" + * | |-812: "" + * | |-813: "" + * |... + * |-82: "The object name 82" (ChildChildQWidget) + * | |-820: "The object name 820" + * | |... + * |-83: "The object name 83" (The class name 83) + * |-830: "The object name 830" (ChildQWidget) + * |... + */ class StubObjectRegisterAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.ktutorial.ObjectRegister") @@ -79,8 +120,26 @@ public slots: + QString objectName(int objectId) { + if (objectId == 500 || objectId == 600 || objectId == 700 || + objectId == 800) { + return "Duplicated object"; + } - QString objectName(int objectId) { + if (objectId == 60 || objectId == 70) { + return "Duplicated parent"; + } + + if (objectId == 5 || objectId == 6) { + return "Duplicated grandparent"; + } + + if (objectId == 80 || objectId == 81 || objectId == 802 || + objectId == 810 || objectId == 811 || objectId == 812 || + objectId == 813) { + return ""; + } + if (objectId > 1000) { return ""; } @@ -89,6 +148,14 @@ } QString className(int objectId) { + if (objectId == 81 || objectId == 82) { + return "ChildChildQWidget"; + } + + if (objectId == 830) { + return "ChildQWidget"; + } + if (objectId > 1000) { return ""; } @@ -97,7 +164,7 @@ } QList<int> childObjectIds(int objectId) { - if (objectId > 42) { + if (objectId > 99) { return QList<int>(); } @@ -105,6 +172,14 @@ for (int i=0; i<4; i++) { ids.append(objectId * 10 + i); } + + if (objectId == 42) { + ids.append(5); + ids.append(6); + ids.append(7); + ids.append(8); + } + return ids; } }; Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/RemoteObjectTest.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -122,11 +122,15 @@ RemoteObject remoteObject(mService, mMapper, 42); QList<RemoteObject*> children = remoteObject.children(); - QCOMPARE(children.count(), 4); + QCOMPARE(children.count(), 8); QCOMPARE(children[0]->objectId(), 420); QCOMPARE(children[1]->objectId(), 421); QCOMPARE(children[2]->objectId(), 422); QCOMPARE(children[3]->objectId(), 423); + QCOMPARE(children[4]->objectId(), 5); + QCOMPARE(children[5]->objectId(), 6); + QCOMPARE(children[6]->objectId(), 7); + QCOMPARE(children[7]->objectId(), 8); } void RemoteObjectTest::testChildrenWhenRemoteObjectIsNotAvailable() { Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-03-27 16:07:21 UTC (rev 294) @@ -54,6 +54,7 @@ unit_tests( RemoteObjectChooser + RemoteObjectChooserFilterModel RemoteObjectNameWidget RemoteObjectTreeItem RemoteObjectTreeItemUpdater @@ -103,6 +104,7 @@ if (QT_QTDBUS_FOUND) mem_tests( RemoteObjectChooser + RemoteObjectChooserFilterModel RemoteObjectTreeItem RemoteObjectTreeItemUpdater RemoteObjectTreeSelectionManager Added: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserFilterModelTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserFilterModelTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserFilterModelTest.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -0,0 +1,338 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "RemoteObjectChooserFilterModel.h" + +#include "RemoteObjectTreeItem.h" +#include "TreeModel.h" +#include "../targetapplication/RemoteClassStubs.h" +#include "../targetapplication/RemoteObject.h" +#include "../targetapplication/RemoteObjectMapper.h" + +class RemoteObjectChooserFilterModelTest: public QObject { +Q_OBJECT + +private slots: + + void init(); + void cleanup(); + + void testNamedObjectFilter(); + void testNamedObjectFilterWhenRemoteObjectsAreNotAvailable(); + + void testWidgetFilter(); + void testWidgetFilterWhenRemoteObjectsAreNotAvailable(); + + void testNamedObjectAndWidgetFilters(); + +private: + + StubObjectRegister* mObjectRegister; + RemoteObjectMapper* mMapper; + + TreeModel* mTreeModel; + + RemoteObjectChooserFilterModel* mFilterModel; + + QModelIndex mappedFromSource(int row, int column, const QModelIndex& parent) const; + +}; + +void RemoteObjectChooserFilterModelTest::init() { + QVERIFY(QDBusConnection::sessionBus().isConnected()); + + mObjectRegister = new StubObjectRegister(); + QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", + mObjectRegister, QDBusConnection::ExportAdaptors); + + QString service = QDBusConnection::sessionBus().baseService(); + mMapper = new RemoteObjectMapper(service); + + RemoteObject* mainWindow = mMapper->remoteObject(42); + RemoteObjectTreeItem* rootItem = new RemoteObjectTreeItem(mainWindow); + mTreeModel = new TreeModel(rootItem, this); + + mFilterModel = new RemoteObjectChooserFilterModel(this); + mFilterModel->setSourceModel(mTreeModel); +} + +void RemoteObjectChooserFilterModelTest::cleanup() { + delete mFilterModel; + + delete mTreeModel; + + delete mMapper; + + QDBusConnection::sessionBus().unregisterObject("/ktutorial/ObjectRegister"); + delete mObjectRegister; +} + +void RemoteObjectChooserFilterModelTest::testNamedObjectFilter() { + mFilterModel->setNamedObjectFilterEnabled(true); + + QModelIndex baseIndex = mFilterModel->index(7, 0); + QModelIndex sourceBaseIndex = mTreeModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 3); + QCOMPARE(mappedFromSource(0, 0, sourceBaseIndex), + mFilterModel->index(0, 0, baseIndex)); + QCOMPARE(mappedFromSource(1, 0, sourceBaseIndex), + QModelIndex()); + QCOMPARE(mappedFromSource(2, 0, sourceBaseIndex), + mFilterModel->index(1, 0, baseIndex)); + QCOMPARE(mappedFromSource(3, 0, sourceBaseIndex), + mFilterModel->index(2, 0, baseIndex)); + + QModelIndex childBaseIndex = mFilterModel->index(0, 0, baseIndex); + QModelIndex childSourceBaseIndex = mTreeModel->index(0, 0, sourceBaseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 3); + QCOMPARE(mappedFromSource(0, 0, childSourceBaseIndex), + mFilterModel->index(0, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(1, 0, childSourceBaseIndex), + mFilterModel->index(1, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(2, 0, childSourceBaseIndex), + QModelIndex()); + QCOMPARE(mappedFromSource(3, 0, childSourceBaseIndex), + mFilterModel->index(2, 0, childBaseIndex)); + + childBaseIndex = mFilterModel->index(1, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(2, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + mFilterModel->setNamedObjectFilterEnabled(false); + + baseIndex = mFilterModel->index(7, 0); + sourceBaseIndex = mTreeModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 4); + QCOMPARE(mappedFromSource(0, 0, sourceBaseIndex), + mFilterModel->index(0, 0, baseIndex)); + QCOMPARE(mappedFromSource(1, 0, sourceBaseIndex), + mFilterModel->index(1, 0, baseIndex)); + QCOMPARE(mappedFromSource(2, 0, sourceBaseIndex), + mFilterModel->index(2, 0, baseIndex)); + QCOMPARE(mappedFromSource(3, 0, sourceBaseIndex), + mFilterModel->index(3, 0, baseIndex)); + + childBaseIndex = mFilterModel->index(0, 0, baseIndex); + childSourceBaseIndex = mTreeModel->index(0, 0, sourceBaseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + QCOMPARE(mappedFromSource(0, 0, childSourceBaseIndex), + mFilterModel->index(0, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(1, 0, childSourceBaseIndex), + mFilterModel->index(1, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(2, 0, childSourceBaseIndex), + mFilterModel->index(2, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(3, 0, childSourceBaseIndex), + mFilterModel->index(3, 0, childBaseIndex)); + + childBaseIndex = mFilterModel->index(1, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(2, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(3, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); +} + +void RemoteObjectChooserFilterModelTest:: + testNamedObjectFilterWhenRemoteObjectsAreNotAvailable() { + QDBusConnection::sessionBus().unregisterObject("/ktutorial/ObjectRegister"); + + mFilterModel->setNamedObjectFilterEnabled(true); + + QModelIndex baseIndex = mFilterModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 4); + + mFilterModel->setNamedObjectFilterEnabled(false); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 4); +} + +void RemoteObjectChooserFilterModelTest::testWidgetFilter() { + mFilterModel->setWidgetFilterEnabled(true); + + QModelIndex baseIndex = mFilterModel->index(0, 0); + QModelIndex sourceBaseIndex = mTreeModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 3); + QCOMPARE(mappedFromSource(0, 0, sourceBaseIndex), + QModelIndex()); + QCOMPARE(mappedFromSource(1, 0, sourceBaseIndex), + mFilterModel->index(0, 0, baseIndex)); + QCOMPARE(mappedFromSource(2, 0, sourceBaseIndex), + mFilterModel->index(1, 0, baseIndex)); + QCOMPARE(mappedFromSource(3, 0, sourceBaseIndex), + mFilterModel->index(2, 0, baseIndex)); + + QModelIndex childBaseIndex = mFilterModel->index(0, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 0); + + childBaseIndex = mFilterModel->index(1, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 0); + + childBaseIndex = mFilterModel->index(2, 0, baseIndex); + QModelIndex childSourceBaseIndex = mTreeModel->index(3, 0, sourceBaseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 1); + QCOMPARE(mappedFromSource(0, 0, childSourceBaseIndex), + mFilterModel->index(0, 0, childBaseIndex)); + + mFilterModel->setWidgetFilterEnabled(false); + + baseIndex = mFilterModel->index(7, 0); + sourceBaseIndex = mTreeModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 4); + QCOMPARE(mappedFromSource(0, 0, sourceBaseIndex), + mFilterModel->index(0, 0, baseIndex)); + QCOMPARE(mappedFromSource(1, 0, sourceBaseIndex), + mFilterModel->index(1, 0, baseIndex)); + QCOMPARE(mappedFromSource(2, 0, sourceBaseIndex), + mFilterModel->index(2, 0, baseIndex)); + QCOMPARE(mappedFromSource(3, 0, sourceBaseIndex), + mFilterModel->index(3, 0, baseIndex)); + + childBaseIndex = mFilterModel->index(0, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(1, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(2, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(3, 0, baseIndex); + childSourceBaseIndex = mTreeModel->index(3, 0, sourceBaseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + QCOMPARE(mappedFromSource(0, 0, childSourceBaseIndex), + mFilterModel->index(0, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(1, 0, childSourceBaseIndex), + mFilterModel->index(1, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(2, 0, childSourceBaseIndex), + mFilterModel->index(2, 0, childBaseIndex)); + QCOMPARE(mappedFromSource(3, 0, childSourceBaseIndex), + mFilterModel->index(3, 0, childBaseIndex)); +} + +void RemoteObjectChooserFilterModelTest:: + testWidgetFilterWhenRemoteObjectsAreNotAvailable() { + QDBusConnection::sessionBus().unregisterObject("/ktutorial/ObjectRegister"); + + mFilterModel->setWidgetFilterEnabled(true); + + QModelIndex baseIndex = mFilterModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 4); + + mFilterModel->setWidgetFilterEnabled(false); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 4); +} + +void RemoteObjectChooserFilterModelTest::testNamedObjectAndWidgetFilters() { + mFilterModel->setNamedObjectFilterEnabled(true); + mFilterModel->setWidgetFilterEnabled(true); + + QModelIndex baseIndex = mFilterModel->index(0, 0); + QModelIndex sourceBaseIndex = mTreeModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 2); + QCOMPARE(mappedFromSource(0, 0, sourceBaseIndex), + QModelIndex()); + QCOMPARE(mappedFromSource(1, 0, sourceBaseIndex), + QModelIndex()); + QCOMPARE(mappedFromSource(2, 0, sourceBaseIndex), + mFilterModel->index(0, 0, baseIndex)); + QCOMPARE(mappedFromSource(3, 0, sourceBaseIndex), + mFilterModel->index(1, 0, baseIndex)); + + QModelIndex childBaseIndex = mFilterModel->index(0, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 0); + + childBaseIndex = mFilterModel->index(1, 0, baseIndex); + QModelIndex childSourceBaseIndex = mTreeModel->index(3, 0, sourceBaseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 1); + QCOMPARE(mappedFromSource(0, 0, childSourceBaseIndex), + mFilterModel->index(0, 0, childBaseIndex)); + + mFilterModel->setNamedObjectFilterEnabled(false); + mFilterModel->setWidgetFilterEnabled(false); + + baseIndex = mFilterModel->index(7, 0); + sourceBaseIndex = mTreeModel->index(7, 0); + + QCOMPARE(mFilterModel->rowCount(baseIndex), 4); + QCOMPARE(mappedFromSource(0, 0, sourceBaseIndex), + mFilterModel->index(0, 0, baseIndex)); + QCOMPARE(mappedFromSource(1, 0, sourceBaseIndex), + mFilterModel->index(1, 0, baseIndex)); + QCOMPARE(mappedFromSource(2, 0, sourceBaseIndex), + mFilterModel->index(2, 0, baseIndex)); + QCOMPARE(mappedFromSource(3, 0, sourceBaseIndex), + mFilterModel->index(3, 0, baseIndex)); + + childBaseIndex = mFilterModel->index(0, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(1, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(2, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); + + childBaseIndex = mFilterModel->index(3, 0, baseIndex); + + QCOMPARE(mFilterModel->rowCount(childBaseIndex), 4); +} + +/////////////////////////////////// Helpers //////////////////////////////////// + +QModelIndex RemoteObjectChooserFilterModelTest::mappedFromSource(int row, + int column, const QModelIndex& parent) const { + return mFilterModel->mapFromSource(mTreeModel->index(row, column, parent)); +} + +QTEST_MAIN(RemoteObjectChooserFilterModelTest) + +#include "../targetapplication/moc_RemoteClassStubs.cxx" +#include "RemoteObjectChooserFilterModelTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserFilterModelTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -64,6 +64,10 @@ //Closing with ALT+F4 can't be tested, as it depends on the window manager //rather than the widget + void testShowOnlyNamedObjects(); + void testShowOnlyWidgets(); + void testShowOnlyNamedWidgets(); + void testSelectRemoteObject(); void testOkButton(); @@ -78,6 +82,9 @@ void killTargetApplication(int timeToWait); + QCheckBox* showOnlyNamedObjectsCheckBox(RemoteObjectChooser* widget) const; + QCheckBox* showOnlyWidgetsCheckBox(RemoteObjectChooser* widget) const; + QTreeView* remoteObjectsTreeView(RemoteObjectChooser* widget) const; QPushButton* okButton(RemoteObjectChooser* widget) const; @@ -315,6 +322,98 @@ QVERIFY(dialog->isVisible()); } +void RemoteObjectChooserTest::testShowOnlyNamedObjects() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + + QWidget window(0, Qt::Window); + //Queue closing the information message box + closeInformationMessageBox(1000); + RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); + chooser->show(); + + //Give the target application time to start + QTest::qWait(1000); + + QVERIFY(remoteObjectsTreeView(chooser)->model()); + + QModelIndex index = remoteObjectsTreeView(chooser)->model()->index(7, 0); + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 4); + + showOnlyNamedObjectsCheckBox(chooser)->click(); + + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 3); + + showOnlyNamedObjectsCheckBox(chooser)->click(); + + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 4); +} + +void RemoteObjectChooserTest::testShowOnlyWidgets() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + + QWidget window(0, Qt::Window); + //Queue closing the information message box + closeInformationMessageBox(1000); + RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); + chooser->show(); + + //Give the target application time to start + QTest::qWait(1000); + + QVERIFY(remoteObjectsTreeView(chooser)->model()); + + QModelIndex index = remoteObjectsTreeView(chooser)->model()->index(7, 0); + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 4); + + showOnlyWidgetsCheckBox(chooser)->click(); + + //The index changes because the other base items were hidden + index = remoteObjectsTreeView(chooser)->model()->index(0, 0); + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 3); + + showOnlyWidgetsCheckBox(chooser)->click(); + + index = remoteObjectsTreeView(chooser)->model()->index(7, 0); + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 4); +} + +void RemoteObjectChooserTest::testShowOnlyNamedWidgets() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + + QWidget window(0, Qt::Window); + //Queue closing the information message box + closeInformationMessageBox(1000); + RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); + chooser->show(); + + //Give the target application time to start + QTest::qWait(1000); + + QVERIFY(remoteObjectsTreeView(chooser)->model()); + + QModelIndex index = remoteObjectsTreeView(chooser)->model()->index(7, 0); + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 4); + + showOnlyWidgetsCheckBox(chooser)->click(); + + //The index changes because the other base items were hidden + index = remoteObjectsTreeView(chooser)->model()->index(0, 0); + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 3); + + showOnlyNamedObjectsCheckBox(chooser)->click(); + + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 2); + + showOnlyWidgetsCheckBox(chooser)->click(); + + index = remoteObjectsTreeView(chooser)->model()->index(7, 0); + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 3); + + showOnlyNamedObjectsCheckBox(chooser)->click(); + + QCOMPARE(remoteObjectsTreeView(chooser)->model()->rowCount(index), 4); +} + void RemoteObjectChooserTest::testSelectRemoteObject() { TargetApplication::self()->setTargetApplicationFilePath(mPath); @@ -516,6 +615,16 @@ QTimer::singleShot(timeToWait, helper, SLOT(killTargetApplication())); } +QCheckBox* RemoteObjectChooserTest::showOnlyNamedObjectsCheckBox( + RemoteObjectChooser* widget) const { + return widget->findChild<QCheckBox*>("showOnlyNamedObjectsCheckBox"); +} + +QCheckBox* RemoteObjectChooserTest::showOnlyWidgetsCheckBox( + RemoteObjectChooser* widget) const { + return widget->findChild<QCheckBox*>("showOnlyWidgetsCheckBox"); +} + QTreeView* RemoteObjectChooserTest::remoteObjectsTreeView( RemoteObjectChooser* widget) const { return widget->findChild<QTreeView*>("remoteObjectsTreeView"); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -67,6 +67,8 @@ void assertRemoteObjectSignal(const QSignalSpy& spy, int index, const RemoteObject* remoteObject) const; + void assertCompletionItems(const QStringList& items) const; + }; void RemoteObjectNameWidgetTest::initTestCase() { @@ -102,12 +104,7 @@ QCOMPARE(widget->parentWidget(), &parent); QCOMPARE(widget->name(), QString("")); QStringList items = objectNameLineEdit(widget)->completionObject()->items(); - QCOMPARE(items.count(), 5); - QVERIFY(items.contains("The object name 42")); - QVERIFY(items.contains("The object name 420")); - QVERIFY(items.contains("The object name 421")); - QVERIFY(items.contains("The object name 422")); - QVERIFY(items.contains("The object name 423")); + assertCompletionItems(items); } void RemoteObjectNameWidgetTest::testSetName() { @@ -172,12 +169,7 @@ QTest::qWait(1000); items = objectNameLineEdit(&widget)->completionObject()->items(); - QCOMPARE(items.count(), 5); - QVERIFY(items.contains("The object name 42")); - QVERIFY(items.contains("The object name 420")); - QVERIFY(items.contains("The object name 421")); - QVERIFY(items.contains("The object name 422")); - QVERIFY(items.contains("The object name 423")); + assertCompletionItems(items); widget.setName("The object name 423"); @@ -240,6 +232,22 @@ QCOMPARE(qvariant_cast<RemoteObject*>(argument), remoteObject); } +void RemoteObjectNameWidgetTest::assertCompletionItems( + const QStringList& items) const { + QCOMPARE(items.count(), 77); + QVERIFY(items.contains("The object name 42")); + QVERIFY(items.contains("The object name 420")); + QVERIFY(items.contains("The object name 421")); + QVERIFY(items.contains("The object name 422")); + QVERIFY(items.contains("The object name 423")); + QVERIFY(items.contains("The object name 7")); + QVERIFY(items.contains("The object name 62")); + QVERIFY(items.contains("The object name 833")); + QVERIFY(items.contains("Duplicated object")); + QVERIFY(items.contains("Duplicated parent")); + QVERIFY(items.contains("Duplicated grandparent")); +} + QTEST_MAIN(RemoteObjectNameWidgetTest) #include "RemoteObjectNameWidgetTest.moc" Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectTreeSelectionManagerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectTreeSelectionManagerTest.cpp 2011-03-09 16:38:59 UTC (rev 293) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectTreeSelectionManagerTest.cpp 2011-03-27 16:07:21 UTC (rev 294) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -20,6 +20,8 @@ #include "RemoteObjectTreeSelectionManager.h" +#include <QSortFilterProxyModel> + #include "RemoteObjectTreeItem.h" #include "TreeModel.h" #include "../targetapplication/RemoteObject.h" @@ -40,6 +42,8 @@ void testSelectRemoteObjectClearingTheSelection(); void testSelectRemoteObjectChangingBetweenRemoteObjects(); + void testSelectInProxyModel(); + private: int mRemoteObjectStarType; @@ -215,6 +219,32 @@ assertRemoteObjectSignal(mRemoteObjectSelectedSpy, 2, mRemoteObject3); } +void RemoteObjectTreeSelectionManagerTest::testSelectInProxyModel() { + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(mTreeModel); + + QItemSelectionModel selectionModel(&proxyModel); + RemoteObjectTreeSelectionManager selectionManager(&selectionModel); + + QSignalSpy remoteObjectSelectedSpy(&selectionManager, + SIGNAL(remoteObjectSelected(RemoteObject*))); + + QModelIndex remoteObject1Index = proxyModel.index(0, 0); + selectionModel.select(remoteObject1Index, + QItemSelectionModel::SelectCurrent); + + QCOMPARE(remoteObjectSelectedSpy.count(), 1); + assertRemoteObjectSignal(&remoteObjectSelectedSpy, 0, mRemoteObject1); + + QModelIndex remoteObject1_2Index = + proxyModel.index(1, 0, remoteObject1Index); + selectionModel.select(remoteObject1_2Index, + QItemSelectionModel::SelectCurrent); + + QCOMPARE(remoteObjectSelectedSpy.count(), 2); + assertRemoteObjectSignal(&remoteObjectSelectedSpy, 1, mRemoteObject1_2); +} + /////////////////////////////////// Helpers //////////////////////////////////// void RemoteObjectTreeSelectionManagerTest::select(const QModelIndex& index) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-04-05 02:11:14
|
Revision: 296 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=296&view=rev Author: danxuliu Date: 2011-04-05 02:11:06 +0000 (Tue, 05 Apr 2011) Log Message: ----------- Add support for object paths instead of just object names in RemoteObjectNameWidget. Now, when choosing an object, the ancestor names are included if necessary to differentiate between several remote objects with the same name. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-04-05 02:00:36 UTC (rev 295) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-04-05 02:11:06 UTC (rev 296) @@ -38,6 +38,7 @@ ${ktutorial_editor_view_SRCS} RemoteObjectChooser.cpp RemoteObjectChooserFilterModel.cpp + RemoteObjectNameRegister.cpp RemoteObjectNameWidget.cpp RemoteObjectTreeItem.cpp RemoteObjectTreeItemUpdater.cpp Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp 2011-04-05 02:11:06 UTC (rev 296) @@ -0,0 +1,322 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "RemoteObjectNameRegister.h" + +#include <KDebug> + +#include "../targetapplication/RemoteEditorSupport.h" +#include "../targetapplication/RemoteEventSpy.h" +#include "../targetapplication/RemoteObject.h" +#include "../targetapplication/TargetApplication.h" + +//public: + +RemoteObjectNameRegister::RemoteObjectNameRegister(QObject* parent /*= 0*/): + QObject(parent) { + if (TargetApplication::self()->remoteEditorSupport()) { + registerRemoteObjects(); + } + + connect(TargetApplication::self(), SIGNAL(started()), + this, SLOT(registerRemoteObjects())); + connect(TargetApplication::self(), SIGNAL(finished()), + this, SLOT(deregisterRemoteObjects())); +} + +RemoteObjectNameRegister::~RemoteObjectNameRegister() { + if (TargetApplication::self()->remoteEditorSupport()) { + TargetApplication::self()->remoteEditorSupport()->disableEventSpy(); + } +} + +QStringList RemoteObjectNameRegister::names() const { + return mRemoteObjectForName.keys(); +} + +QString RemoteObjectNameRegister::uniqueName(RemoteObject* remoteObject) const +throw (DBusException) { + QString name = remoteObject->name(); + if (name.isEmpty()) { + return ""; + } + + QList<QStringList> reversedPathsToHomonyms = + reversedPathsToHomonymsOf(remoteObject); + if (reversedPathsToHomonyms.count() == 0) { + return name; + } + + QStringList reversedFullPath = reversedPathTo(remoteObject); + + for (int i=1; i<reversedFullPath.size(); ++i) { + if (ancestorNotInPaths(reversedFullPath[i], reversedPathsToHomonyms)) { + return reversedFullPath[i] + "/" + reversedFullPath[0]; + } + } + + QStringList reversedUniquePath; + reversedUniquePath.append(reversedFullPath[0]); + + for (int i=1; i<reversedFullPath.size(); ++i) { + reversedUniquePath.append(reversedFullPath[i]); + + if (isUniquePath(reversedUniquePath, reversedPathsToHomonyms)) { + return reversedPathAsName(reversedUniquePath); + } + } + + return reversedPathAsName(reversedUniquePath); +} + +RemoteObject* RemoteObjectNameRegister::findRemoteObject(const QString& name) + const { + return findRemoteObject(name, 0); +} + +//private: + +void RemoteObjectNameRegister::registerRemoteObject(RemoteObject* remoteObject, + RemoteObject* parent) +throw (DBusException) { + Q_ASSERT(remoteObject); + + mRemoteObjectForParent.insert(parent, remoteObject); + + QString name = remoteObject->name(); + if (!name.isEmpty()) { + emit nameAdded(name); + + mRemoteObjectForName.insert(name, remoteObject); + } + + foreach (RemoteObject* child, remoteObject->children()) { + registerRemoteObject(child, remoteObject); + } +} + +void RemoteObjectNameRegister::deregisterRemoteObject( + RemoteObject* remoteObject, + RemoteObject* parent) +throw (DBusException) { + Q_ASSERT(remoteObject); + + //The remote object is no longer accessible, so name() can't be called + QString name = mRemoteObjectForName.key(remoteObject); + + emit nameRemoved(name); + + mRemoteObjectForName.remove(name, remoteObject); + mRemoteObjectForParent.remove(parent, remoteObject); + + foreach (RemoteObject* child, remoteObject->children()) { + deregisterRemoteObject(child, remoteObject); + } +} + +QStringList RemoteObjectNameRegister::reversedPathTo( + RemoteObject* remoteObject) const +throw (DBusException) { + QStringList reversedPath; + + RemoteObject* parent = remoteObject; + while (parent) { + QString name = parent->name(); + if (!name.isEmpty() && !name.startsWith("qt_")) { + reversedPath.append(name); + } + parent = mRemoteObjectForParent.key(parent); + } + + return reversedPath; +} + +QList<QStringList> RemoteObjectNameRegister::reversedPathsToHomonymsOf( + RemoteObject* remoteObject) const +throw (DBusException) { + QList<QStringList> reversedPaths; + + QList<RemoteObject*> homonymRemoteObjects = + mRemoteObjectForName.values(remoteObject->name()); + homonymRemoteObjects.removeOne(remoteObject); + + foreach (RemoteObject* homonymRemoteObject, homonymRemoteObjects) { + reversedPaths.append(reversedPathTo(homonymRemoteObject)); + } + + return reversedPaths; +} + +bool RemoteObjectNameRegister::ancestorNotInPaths(const QString& ancestor, + const QList<QStringList>& reversedPaths) const { + foreach (const QStringList& reversedPath, reversedPaths) { + if (reversedPath.contains(ancestor)) { + return false; + } + } + + return true; +} + +bool RemoteObjectNameRegister::isUniquePath( + const QStringList& reversedPathToCheck, + const QList<QStringList>& reversedPaths) const { + for (int i=0; i<reversedPaths.size(); ++i) { + if (!isUniquePath(reversedPathToCheck, reversedPaths[i])) { + return false; + } + } + + return true; +} + +bool RemoteObjectNameRegister::isUniquePath( + const QStringList& reversedPathToCheck, + const QStringList& reversedPath) const { + int commonAncestorIndex = -1; + for (int i=reversedPathToCheck.size()-1; i>0; --i) { + commonAncestorIndex = reversedPath.lastIndexOf(reversedPathToCheck[i], + commonAncestorIndex); + if (commonAncestorIndex == -1) { + return true; + } + } + + return false; +} + +QString RemoteObjectNameRegister::reversedPathAsName( + const QStringList& reversedPath) const { + QString name = reversedPath[0]; + for (int i=1; i<reversedPath.size(); ++i) { + name = reversedPath[i] + '/' + name; + } + + return name; +} + +RemoteObject* RemoteObjectNameRegister::findRemoteObject(const QString& name, + RemoteObject* ancestor) const { + if (name.indexOf('/') == -1) { + QList<RemoteObject*> remoteObjects = mRemoteObjectForName.values(name); + foreach (RemoteObject* remoteObject, remoteObjects) { + if (isDescendantOf(remoteObject, ancestor)) { + return remoteObject; + } + } + + return 0; + } + + QRegExp slashPattern("/+"); + QString ancestorName = name.left(name.indexOf(slashPattern)); + QString descendantName = name.mid(ancestorName.length() + + slashPattern.matchedLength()); + + QList<RemoteObject*> namedAncestors = + mRemoteObjectForName.values(ancestorName); + + foreach (RemoteObject* namedAncestor, namedAncestors) { + if (isDescendantOf(namedAncestor, ancestor)) { + RemoteObject* remoteObject = findRemoteObject(descendantName, + namedAncestor); + if (remoteObject) { + return remoteObject; + } + } + } + + return 0; +} + +bool RemoteObjectNameRegister::isDescendantOf(RemoteObject* remoteObject, + RemoteObject* ancestor) const { + if (!ancestor) { + return true; + } + + QList<RemoteObject*> children = mRemoteObjectForParent.values(ancestor); + foreach (RemoteObject* child, children) { + if (child == remoteObject || isDescendantOf(remoteObject, child)) { + return true; + } + } + + return false; +} + +//private slots: + +void RemoteObjectNameRegister::registerRemoteObjects() { + try { + registerRemoteObject(TargetApplication::self()-> + remoteEditorSupport()->mainWindow(), 0); + } catch (DBusException e) { + kWarning() << "The remote objects could not be registered to provide " + << "name completion (" << e.message() << ")."; + } + + try { + RemoteEventSpy* remoteEventSpy = + TargetApplication::self()->remoteEditorSupport()->enableEventSpy(); + connect(remoteEventSpy, SIGNAL(eventReceived(RemoteObject*,QString)), + this, SLOT(updateRemoteObjects(RemoteObject*,QString))); + } catch (DBusException e) { + kWarning() << "The remote event spy could not be connected to provide " + << "name completion updates (" << e.message() << ")."; + } +} + +void RemoteObjectNameRegister::deregisterRemoteObjects() { + mRemoteObjectForName.clear(); + mRemoteObjectForParent.clear(); +} + +void RemoteObjectNameRegister::updateRemoteObjects(RemoteObject* remoteObject, + const QString& eventType) { + if (eventType != "ChildAdded" && eventType != "ChildRemoved") { + return; + } + + try { + QList<RemoteObject*> children = remoteObject->children(); + + QList<RemoteObject*> knownChildren = + mRemoteObjectForParent.values(remoteObject); + + if (eventType == "ChildAdded") { + foreach (RemoteObject* child, children) { + if (!knownChildren.contains(child)) { + registerRemoteObject(child, remoteObject); + } + } + } + + if (eventType == "ChildRemoved") { + foreach (RemoteObject* child, knownChildren) { + if (!children.contains(child)) { + deregisterRemoteObject(child, remoteObject); + } + } + } + } catch (DBusException e) { + kWarning() << "There was a problem querying the remote objects, the " + << "name completion could not be updated (" + << e.message() << ")."; + } +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h 2011-04-05 02:11:06 UTC (rev 296) @@ -0,0 +1,302 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef REMOTEOBJECTNAMEREGISTER_H +#define REMOTEOBJECTNAMEREGISTER_H + +#include <QMultiHash> +#include <QObject> + +#include "../targetapplication/DBusException.h" + +class RemoteObject; + +/** + * Provides information related to the names of the remote objects in the target + * application. + * RemoteObjectNameRegister can be used to know the names of all the remote + * objects accesible by KTutorial in the target application, and it also + * notifies the names of the remote objects added or removed in the target + * application through the signals nameAdded(QString) and nameRemoved(QString). + * + * However, sometimes two or more remote objects may have the same name. In that + * case, the names of one or more ancestors are required to differentiate + * between those remote objects. The compound name that identifies a remote + * object can be got using uniqueName(RemoteObject*). Also, given a name, the + * remote object that it represents can be got using findObject(QString). + * + * Note that a name and the remote object it represents depends on the state of + * the target application. For example, if there is a dialog named + * "Configuration dialog" with a button named "Ok button", its unique name could + * be just "Ok button". However, if another dialog named "File information + * dialog" were opened while the "Configuration dialog" was still opened, and + * "File information dialog" had a also button called "Ok button", now the + * unique name of the first button would be "Configuration dialog/Ok button". In + * this situation, "Ok button" would not represent a specific remote object. + */ +class RemoteObjectNameRegister: public QObject { +Q_OBJECT +public: + + /** + * Creates a new RemoteObjectNameRegister. + * + * @param parent The parent QObject. + */ + explicit RemoteObjectNameRegister(QObject* parent = 0); + + /** + * Destroys this RemoteObjectNameRegister. + * If necessary, disables the EventSpy used to update the remote object + * names. + */ + virtual ~RemoteObjectNameRegister(); + + /** + * Returns a list with all the names. + * + * @return A list with all the names. + */ + QStringList names() const; + + /** + * Returns a unique name for the given remote object. + * When the remote object name is unique by itself, its name is returned. + * When there are other remote objects with the same name as the given one, + * but it has an ancestor with a unique name, the returned name includes + * both names. The ancestor with a unique name used is the nearest one to + * the remote object (parent preferred to grandparent, grandparent preferred + * to great-grandparent and so on). + * When there are other remote objects with the same name as the given one, + * and no ancestor has a unique name, the returned name includes as many + * ancestor names as necessary to create a name unique to the given remote + * object. + * + * @param remoteObject The remote object to get its unique name. + * @return A unique name for the given remote object. + * @throws DBusException If a DBus error happens. + */ + QString uniqueName(RemoteObject* remoteObject) const throw (DBusException); + + /** + * Returns the remote object with the given name, if any. + * When the name of the desired remote object is not unique the name of some + * ancestor must be included. Ancestor names are separated using a "/". That + * is, "Ancestor name/The remote object with a repeated name". As many + * ancestor names as desired can be included, not just one. + * The name of the ancestors does not need to be unique either; what has to + * be unique is the full path to the remote object to find. For example, + * "Ancestor name not unique/The remote object to find" is valid if, among + * all the remote objects called "Ancestor name not unique", there is only + * one that has a descendant called "The remote object to find". + * + * @param name The name of the remote object to find. + * @return The remote object with the specified name, or a null pointer if + * there is none. + */ + RemoteObject* findRemoteObject(const QString& name) const; + +Q_SIGNALS: + + /** + * Emitted when the name of a remote object is added to the register. + * + * @param name The name added. + */ + void nameAdded(const QString& name); + + /** + * Emitted when the name of a remote object is removed from the register. + * + * @param name The name removed. + */ + void nameRemoved(const QString& name); + +private: + + /** + * The RemoteObjects with a name indexed by their name. + */ + QMultiHash<QString, RemoteObject*> mRemoteObjectForName; + + /** + * The known RemoteObjects (with and without name) indexed by their parent. + */ + QMultiHash<RemoteObject*, RemoteObject*> mRemoteObjectForParent; + + /** + * Registers the given RemoteObject and all its children. + * The objects are only registered if they have a name. + * + * @param remoteObject The RemoteObject to register. + * @param parent The parent of the RemoteObject to register. + * @throws DBusException If a DBus error happens. + */ + void registerRemoteObject(RemoteObject* remoteObject, RemoteObject* parent) + throw (DBusException); + + /** + * Deregisters the given RemoteObject and all its children. + * + * @param remoteObject The RemoteObject to deregister. + * @param parent The parent of the RemoteObject to deregister. + * @throws DBusException If a DBus error happens. + */ + void deregisterRemoteObject(RemoteObject* remoteObject, + RemoteObject* parent) + throw (DBusException); + + /** + * Returns the reversed path to the given remote object. + * The path contains the name of the remote object itself and all its named + * ancestors. The order of the items is reversed from the natural path, that + * is, the first item in the returned path is the remote object name, the + * second item is the name of its parent, the third is the name of its + * grandparent... + * Empty names and Qt default names (like "qt_scrollarea_viewport") are not + * included in the path. + * + * @param remoteObject The remote object to get its path. + * @return The reversed path to the given remote object. + * @throws DBusException If a DBus error happens. + */ + QStringList reversedPathTo(RemoteObject* remoteObject) const + throw (DBusException); + + /** + * Returns the reversed paths to the remote objects with the same name as + * the given one. + * + * @param remoteObject The remote object to get the paths to its homonyms. + * @return The reversed paths to the homonyms of the given remote object. + * @throws DBusException If a DBus error happens. + * @see reversedPathTo(RemoteObject*) + */ + QList<QStringList> reversedPathsToHomonymsOf(RemoteObject* remoteObject) + const + throw (DBusException); + + /** + * Checks if the ancestor is unique, that is, if the paths do not contain + * the given ancestor. + * + * @param ancestor The ancestor to check. + * @param reversedPaths The list with the paths to check. + * @return True if the paths do not contain the given ancestor, false + * otherwise. + */ + bool ancestorNotInPaths(const QString& ancestor, + const QList<QStringList>& reversedPaths) const; + + /** + * Checks if the path represents a remote object that is unique regarding + * the reference paths. + * + * @param reversedPathToCheck The path to check. + * @param reversedPaths The other paths to check against. + * @return True if the path is unique, false otherwise. + * @see isUniquePath(const QStringList&, const QStringList&) + */ + bool isUniquePath(const QStringList& reversedPathToCheck, + const QList<QStringList>& reversedPaths) const; + + /** + * Checks if the path represents a remote object that is unique regarding + * the reference path. + * Note that the argument order matters. The path to check can be unique + * regarding the reference path and, at the same time, the reference path + * can be used to find the same remote object as the path to check. Take, + * for example, "Common ancestor/Another ancestor/Duplicated object name" as + * the path to check and "Common ancestor/Duplicated object name" as the + * reference path. The reference path may represent a remote object named + * "Duplicated object name" that is child of the remote object named + * "Common ancestor", but when using that path to find the object it + * represents it would represent both that object and the one represented by + * the path to check (as ANY remote object named "Duplicated object name" + * that is descendant of the remote object named "Common ancestor" would + * match). The path to check, on the other hand, would represent only its + * own object thanks to its extra ancestor. + * + * @param reversedPathToCheck The path to check. + * @param reversedPath The reference path to check against. + * @return True if the path is unique, false otherwise. + */ + bool isUniquePath(const QStringList& reversedPathToCheck, + const QStringList& reversedPath) const; + + /** + * Returns the name equivalent to the given reversed path. + * The name contains all the items in the path separated by "/". The order + * of the items in the name is their natural order, that is, the last item + * is the first one, the penultimate is the second one... + * + * @param path The reversed path to get its equivalent name. + * @return The name equivalent to the given reversed path. + */ + QString reversedPathAsName(const QStringList& reversedPath) const; + + /** + * Returns the remote object with the given name, if any. + * The name can contain also the ancestor names, separated by "/". The + * first object that matches the whole path is returned (note that ancestor + * are not necessarily direct parents). + * + * @param name The name of the remote object to find. + * @param ancestor The ancestor to look the remote object in. + * @return The remote object with the specified name, or a null pointer if + * there is none. + */ + RemoteObject* findRemoteObject(const QString& name, RemoteObject* ancestor) + const; + + /** + * Returns true if remoteObject is descendant of ancestor, false otherwise. + * When ancestor is null, it always returns true. + * + * @param remoteObject The remote object to check. + * @param ancestor The ancestor to check. + * @return True if remoteObject is descendant of ancestor, false otherwise. + */ + bool isDescendantOf(RemoteObject* remoteObject, + RemoteObject* ancestor) const; + +private Q_SLOTS: + + /** + * Registers all the remote objects. + */ + void registerRemoteObjects(); + + /** + * Deregisters all the remote objects. + */ + void deregisterRemoteObjects(); + + /** + * Updates the registered remote objects if they received a ChildAdded or + * ChildRemoved event. + * + * @param remoteObject The RemoteObject that received the event. + * @param eventType The type of the event received. + */ + void updateRemoteObjects(RemoteObject* remoteObject, + const QString& eventType); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2011-04-05 02:00:36 UTC (rev 295) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2011-04-05 02:11:06 UTC (rev 296) @@ -19,13 +19,10 @@ #include "RemoteObjectNameWidget.h" #include "ui_RemoteObjectNameWidget.h" -#include <KDebug> #include <KMessageBox> #include "RemoteObjectChooser.h" -#include "../targetapplication/RemoteEditorSupport.h" -#include "../targetapplication/RemoteEventSpy.h" -#include "../targetapplication/RemoteObject.h" +#include "RemoteObjectNameRegister.h" #include "../targetapplication/TargetApplication.h" //public: @@ -33,31 +30,33 @@ RemoteObjectNameWidget::RemoteObjectNameWidget(QWidget* parent): QWidget(parent) { + mRemoteObjectNameRegister = new RemoteObjectNameRegister(this); + ui = new Ui::RemoteObjectNameWidget(); ui->setupUi(this); - ui->objectNameLineEdit->completionObject()->setOrder(KCompletion::Sorted); connect(ui->objectNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(handleNameChanged(QString))); connect(ui->objectNamePushButton, SIGNAL(clicked(bool)), this, SLOT(showRemoteObjectChooser())); - if (TargetApplication::self()->remoteEditorSupport()) { - registerRemoteObjects(); + KCompletion* completion = ui->objectNameLineEdit->completionObject(); + completion->setOrder(KCompletion::Sorted); + + foreach (const QString& name, mRemoteObjectNameRegister->names()) { + completion->addItem(name); } - connect(TargetApplication::self(), SIGNAL(started()), - this, SLOT(registerRemoteObjects())); + connect(mRemoteObjectNameRegister, SIGNAL(nameAdded(QString)), + completion, SLOT(addItem(QString))); + connect(mRemoteObjectNameRegister, SIGNAL(nameRemoved(QString)), + completion, SLOT(removeItem(QString))); connect(TargetApplication::self(), SIGNAL(finished()), - this, SLOT(deregisterRemoteObjects())); + completion, SLOT(clear())); } RemoteObjectNameWidget::~RemoteObjectNameWidget() { - if (TargetApplication::self()->remoteEditorSupport()) { - TargetApplication::self()->remoteEditorSupport()->disableEventSpy(); - } - delete ui; } @@ -69,46 +68,6 @@ ui->objectNameLineEdit->setText(name); } -//private: - -void RemoteObjectNameWidget::registerRemoteObject(RemoteObject* remoteObject, - RemoteObject* parent) -throw (DBusException) { - Q_ASSERT(remoteObject); - - mRemoteObjectForParent.insert(parent, remoteObject); - - if (!remoteObject->name().isEmpty()) { - KCompletion* completion = ui->objectNameLineEdit->completionObject(); - completion->addItem(remoteObject->name()); - - mRemoteObjectForName.insert(remoteObject->name(), remoteObject); - } - - foreach (RemoteObject* child, remoteObject->children()) { - registerRemoteObject(child, remoteObject); - } -} - -void RemoteObjectNameWidget::deregisterRemoteObject(RemoteObject* remoteObject, - RemoteObject* parent) -throw (DBusException) { - Q_ASSERT(remoteObject); - - //The remote object is no longer accessible, so name() can't be called - QString name = mRemoteObjectForName.key(remoteObject); - - KCompletion* completion = ui->objectNameLineEdit->completionObject(); - completion->removeItem(name); - - mRemoteObjectForName.remove(name); - mRemoteObjectForParent.remove(parent, remoteObject); - - foreach (RemoteObject* child, remoteObject->children()) { - deregisterRemoteObject(child, remoteObject); - } -} - //private slots: void RemoteObjectNameWidget::showRemoteObjectChooser() { @@ -121,7 +80,8 @@ void RemoteObjectNameWidget::setChosenRemoteObject(RemoteObject* remoteObject) { try { - ui->objectNameLineEdit->setText(remoteObject->name()); + ui->objectNameLineEdit->setText( + mRemoteObjectNameRegister->uniqueName(remoteObject)); } catch (DBusException e) { QString text = i18nc("@label", "The object name can not be set, there " "was a problem getting the name from the target application: %1", e.message()); @@ -131,66 +91,6 @@ } } -void RemoteObjectNameWidget::registerRemoteObjects() { - try { - registerRemoteObject(TargetApplication::self()-> - remoteEditorSupport()->mainWindow(), 0); - } catch (DBusException e) { - kWarning() << "The remote objects could not be registered to provide " - << "name completion (" << e.message() << ")."; - } - - try { - RemoteEventSpy* remoteEventSpy = - TargetApplication::self()->remoteEditorSupport()->enableEventSpy(); - connect(remoteEventSpy, SIGNAL(eventReceived(RemoteObject*,QString)), - this, SLOT(updateRemoteObjects(RemoteObject*,QString))); - } catch (DBusException e) { - kWarning() << "The remote event spy could not be connected to provide " - << "name completion updates (" << e.message() << ")."; - } -} - -void RemoteObjectNameWidget::deregisterRemoteObjects() { - ui->objectNameLineEdit->completionObject()->clear(); - mRemoteObjectForName.clear(); - mRemoteObjectForParent.clear(); -} - -void RemoteObjectNameWidget::updateRemoteObjects(RemoteObject* remoteObject, - const QString& eventType) { - if (eventType != "ChildAdded" && eventType != "ChildRemoved") { - return; - } - - try { - QList<RemoteObject*> children = remoteObject->children(); - - QList<RemoteObject*> knownChildren = - mRemoteObjectForParent.values(remoteObject); - - if (eventType == "ChildAdded") { - foreach (RemoteObject* child, children) { - if (!knownChildren.contains(child)) { - registerRemoteObject(child, remoteObject); - } - } - } - - if (eventType == "ChildRemoved") { - foreach (RemoteObject* child, knownChildren) { - if (!children.contains(child)) { - deregisterRemoteObject(child, remoteObject); - } - } - } - } catch (DBusException e) { - kWarning() << "There was a problem querying the remote objects, the " - << "name completion could not be updated (" - << e.message() << ")."; - } -} - void RemoteObjectNameWidget::handleNameChanged(const QString& name) { - emit remoteObjectChosen(mRemoteObjectForName.value(name)); + emit remoteObjectChosen(mRemoteObjectNameRegister->findRemoteObject(name)); } Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2011-04-05 02:00:36 UTC (rev 295) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2011-04-05 02:11:06 UTC (rev 296) @@ -19,12 +19,10 @@ #ifndef REMOTEOBJECTNAMEWIDGET_H #define REMOTEOBJECTNAMEWIDGET_H -#include <QHash> #include <QWidget> -#include "../targetapplication/DBusException.h" - class RemoteObject; +class RemoteObjectNameRegister; namespace Ui { class RemoteObjectNameWidget; @@ -89,43 +87,16 @@ private: /** - * The RemoteObjects with a name indexed by their name. + * The register that provides the names of the remote objects and finds them + * based on their name. */ - QHash<QString, RemoteObject*> mRemoteObjectForName; + RemoteObjectNameRegister* mRemoteObjectNameRegister; /** - * The known RemoteObjects (with and without name) indexed by their parent. - */ - QMultiHash<RemoteObject*, RemoteObject*> mRemoteObjectForParent; - - /** * The Ui Designer generated class. */ Ui::RemoteObjectNameWidget* ui; - /** - * Registers the given RemoteObject and all its children. - * The objects are only registered if they have a name. In that case, it is - * added to the completion of the name line edit. - * - * @param remoteObject The RemoteObject to register. - * @param parent The parent of the RemoteObject to register. - * @throws DBusException If a DBus error happens. - */ - void registerRemoteObject(RemoteObject* remoteObject, RemoteObject* parent) - throw (DBusException); - - /** - * Deregisters the given RemoteObject and all its children. - * - * @param remoteObject The RemoteObject to deregister. - * @param parent The parent of the RemoteObject to deregister. - * @throws DBusException If a DBus error happens. - */ - void deregisterRemoteObject(RemoteObject* remoteObject, - RemoteObject* parent) - throw (DBusException); - private Q_SLOTS: /** @@ -141,28 +112,6 @@ void setChosenRemoteObject(RemoteObject* remoteObject); /** - * Registers the remote objects and add their names to the line edit - * completion object. - */ - void registerRemoteObjects(); - - /** - * Deregisters the remote objects and removes their names from the line edit - * completion object. - */ - void deregisterRemoteObjects(); - - /** - * Updates the registered remote objects if they received a ChildAdded or - * ChildRemoved event. - * - * @param remoteObject The RemoteObject that received the event. - * @param eventType The type of the event received. - */ - void updateRemoteObjects(RemoteObject* remoteObject, - const QString& eventType); - - /** * Emits remoteObjectChosen(RemoteObject*) with the remote object with the * given name. * Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-04-05 02:00:36 UTC (rev 295) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-04-05 02:11:06 UTC (rev 296) @@ -55,6 +55,7 @@ unit_tests( RemoteObjectChooser RemoteObjectChooserFilterModel + RemoteObjectNameRegister RemoteObjectNameWidget RemoteObjectTreeItem RemoteObjectTreeItemUpdater Added: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2011-04-05 02:11:06 UTC (rev 296) @@ -0,0 +1,380 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "RemoteObjectNameRegister.h" + +#include <KProcess> + +#include "../targetapplication/RemoteEditorSupport.h" +#include "../targetapplication/RemoteObject.h" +#define protected public +#define private public +#include "../targetapplication/TargetApplication.h" +#undef private +#undef protected + +class RemoteObjectNameRegisterTest: public QObject { +Q_OBJECT + +private slots: + + void init(); + void cleanup(); + + void testConstructor(); + + void testTargetApplicationStartedAfterRegister(); + void testTargetApplicationStopped(); + + void testAddingOrRemovingRemoteObjects(); + + void testFindRemoteObject(); + void testFindRemoteObjectWithParentName(); + void testFindRemoteObjectWithGrandparentName(); + void testFindRemoteObjectWithParentAndGrandparentNames(); + void testFindRemoteObjectWithUnknownName(); + + void testUniqueName(); + void testUniqueNameWhenUniqueParent(); + void testUniqueNameWhenUniqueGrandparentAndEmptyParent(); + void testUniqueNameWhenUniqueGrandparentAndDuplicatedParent(); + void testUniqueNameWhenUniqueUnionOfGrandparentAndParent(); + void testUniqueNameWhenEmptyName(); + +private: + + QString mPath; + + void assertNames(const QStringList& names) const; + +}; + +void RemoteObjectNameRegisterTest::init() { + mPath = QApplication::applicationDirPath() + + "/../targetapplication/TargetApplicationStub"; + + //Avoid signals from previews tests to be delivered to the next ones + //setting a new TargetApplication + delete TargetApplication::sSelf; + TargetApplication::sSelf = new TargetApplication(); +} + +void RemoteObjectNameRegisterTest::cleanup() { + delete TargetApplication::sSelf; + TargetApplication::sSelf = 0; +} + +void RemoteObjectNameRegisterTest::testConstructor() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + assertNames(remoteObjectNameRegister.names()); +} + +void RemoteObjectNameRegisterTest::testTargetApplicationStartedAfterRegister() { + RemoteObjectNameRegister remoteObjectNameRegister; + QSignalSpy nameAddedSpy(&remoteObjectNameRegister, + SIGNAL(nameAdded(QString))); + QSignalSpy nameRemovedSpy(&remoteObjectNameRegister, + SIGNAL(nameRemoved(QString))); + + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + assertNames(remoteObjectNameRegister.names()); + QCOMPARE(nameAddedSpy.count(), 82); + QCOMPARE(nameAddedSpy.at(0).at(0).toString(), + QString("The object name 42")); + QCOMPARE(nameAddedSpy.at(1).at(0).toString(), + QString("The object name 420")); + QCOMPARE(nameAddedSpy.at(5).at(0).toString(), + QString("Duplicated grandparent")); + QCOMPARE(nameAddedSpy.at(6).at(0).toString(), + QString("The object name 50")); + QCOMPARE(nameAddedSpy.at(7).at(0).toString(), + QString("Duplicated object")); + QCOMPARE(nameAddedSpy.at(11).at(0).toString(), + QString("The object name 51")); + QCOMPARE(nameAddedSpy.at(26).at(0).toString(), + QString("Duplicated grandparent")); + QCOMPARE(nameAddedSpy.at(27).at(0).toString(), + QString("Duplicated parent")); + QCOMPARE(nameAddedSpy.at(28).at(0).toString(), + QString("Duplicated object")); + QCOMPARE(nameAddedSpy.at(68).at(0).toString(), + QString("The object name 8")); + QCOMPARE(nameAddedSpy.at(69).at(0).toString(), + QString("Duplicated object")); + QCOMPARE(nameAddedSpy.at(70).at(0).toString(), + QString("The object name 801")); + QCOMPARE(nameAddedSpy.at(71).at(0).toString(), + QString("The object name 803")); + QCOMPARE(nameRemovedSpy.count(), 0); +} + +void RemoteObjectNameRegisterTest::testTargetApplicationStopped() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QSignalSpy nameAddedSpy(&remoteObjectNameRegister, + SIGNAL(nameAdded(QString))); + QSignalSpy nameRemovedSpy(&remoteObjectNameRegister, + SIGNAL(nameAdded(QString))); + + TargetApplication::self()->mProcess->kill(); + + //Give the target application time to stop + QTest::qWait(1000); + + QVERIFY(remoteObjectNameRegister.names().isEmpty()); + QCOMPARE(nameAddedSpy.count(), 0); + QCOMPARE(nameRemovedSpy.count(), 0); +} + +void RemoteObjectNameRegisterTest::testAddingOrRemovingRemoteObjects() { + QSKIP("Unfortunately, testing if the signals are emitted when an object is " + "added or removed in the target application is too burdensome so the " + "test must be done manually", SkipAll); +} + +void RemoteObjectNameRegisterTest::testFindRemoteObject() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(remoteObjectNameRegister.findRemoteObject("The object name 423"), + mainWindow->children()[3]); +} + +void RemoteObjectNameRegisterTest::testFindRemoteObjectWithParentName() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(remoteObjectNameRegister.findRemoteObject( + "The object name 50/Duplicated object"), + mainWindow->children()[4]->children()[0]->children()[0]); +} + +void RemoteObjectNameRegisterTest::testFindRemoteObjectWithGrandparentName() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(remoteObjectNameRegister.findRemoteObject( + "The object name 7/Duplicated object"), + mainWindow->children()[6]->children()[0]->children()[0]); +} + +void RemoteObjectNameRegisterTest:: + testFindRemoteObjectWithParentAndGrandparentNames() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(remoteObjectNameRegister.findRemoteObject( + "Duplicated grandparent/Duplicated parent/Duplicated object"), + mainWindow->children()[5]->children()[0]->children()[0]); +} + +void RemoteObjectNameRegisterTest::testFindRemoteObjectWithUnknownName() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QCOMPARE(remoteObjectNameRegister.findRemoteObject("The object name 108"), + (RemoteObject*)0); +} + +void RemoteObjectNameRegisterTest::testUniqueName() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[4]->children()[0]->children()[1]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("The object name 501")); +} + +void RemoteObjectNameRegisterTest::testUniqueNameWhenUniqueParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[4]->children()[0]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("The object name 50/Duplicated object")); +} + +void RemoteObjectNameRegisterTest:: + testUniqueNameWhenUniqueGrandparentAndEmptyParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[7]->children()[0]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("The object name 8/Duplicated object")); +} + +void RemoteObjectNameRegisterTest:: + testUniqueNameWhenUniqueGrandparentAndDuplicatedParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[6]->children()[0]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("The object name 7/Duplicated object")); +} + +void RemoteObjectNameRegisterTest:: + testUniqueNameWhenUniqueUnionOfGrandparentAndParent() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[5]->children()[0]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), + QString("Duplicated grandparent/Duplicated parent/" + "Duplicated object")); +} + +void RemoteObjectNameRegisterTest::testUniqueNameWhenEmptyName() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = mainWindow->children()[7]->children()[0]; + + QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), QString("")); +} + +/////////////////////////////////// Helpers //////////////////////////////////// + +void RemoteObjectNameRegisterTest::assertNames(const QStringList& names) const { + QCOMPARE(names.count(), 82); + QVERIFY(names.contains("The object name 42")); + QVERIFY(names.contains("The object name 420")); + QVERIFY(names.contains("The object name 421")); + QVERIFY(names.contains("The object name 422")); + QVERIFY(names.contains("The object name 423")); + QVERIFY(names.contains("The object name 7")); + QVERIFY(names.contains("The object name 62")); + QVERIFY(names.contains("The object name 833")); + QVERIFY(names.contains("Duplicated object")); + QVERIFY(names.contains("Duplicated parent")); + QVERIFY(names.contains("Duplicated grandparent")); +} + +QTEST_MAIN(RemoteObjectNameRegisterTest) + +#include "RemoteObjectNameRegisterTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2011-04-05 02:00:36 UTC (rev 295) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2011-04-05 02:11:06 UTC (rev 296) @@ -18,7 +18,11 @@ #include <QtTest> +#define protected public +#define private public #include "RemoteObjectNameWidget.h" +#undef private +#undef protected #include <QtDBus/QtDBus> @@ -46,8 +50,12 @@ void testConstructor(); void testSetName(); + void testSetNameWithPath(); void testSetNameWithUnknownRemoteObjectName(); + void testSetChosenRemoteObject(); + void testSetChosenRemoteObjectWithNameNotUnique(); + void testTargetApplicationStartedAfterWidget(); void testTargetApplicationStopped(); @@ -129,6 +137,28 @@ mainWindow->children()[3]); } +void RemoteObjectNameWidgetTest::testSetNameWithPath() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + widget.setName("The object name 7/Duplicated object"); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + QCOMPARE(widget.name(), QString("The object name 7/Duplicated object")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, + mainWindow->children()[6]->children()[0]->children()[0]); +} + void RemoteObjectNameWidgetTest::testSetNameWithUnknownRemoteObjectName() { TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); @@ -147,6 +177,67 @@ assertRemoteObjectSignal(remoteObjectChosenSpy, 0, 0); } +void RemoteObjectNameWidgetTest::testSetChosenRemoteObject() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + RemoteObject* remoteObject = + mainWindow->children()[4]->children()[0]->children()[1]; + + widget.setChosenRemoteObject(remoteObject); + + QCOMPARE(widget.name(), QString("The object name 501")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, remoteObject); +} + +void RemoteObjectNameWidgetTest::testSetChosenRemoteObjectWithNameNotUnique() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameWidget widget; + QSignalSpy remoteObjectChosenSpy(&widget, + SIGNAL(remoteObjectChosen(RemoteObject*))); + + RemoteObject* mainWindow = + TargetApplication::self()->remoteEditorSupport()->mainWindow(); + + RemoteObject* remoteObject = + mainWindow->children()[4]->children()[0]->children()[0]; + widget.setChosenRemoteObject(remoteObject); + + QCOMPARE(widget.name(), QString("The object name 50/Duplicated object")); + QCOMPARE(remoteObjectChosenSpy.count(), 1); + assertRemoteObjectSignal(remoteObjectChosenSpy, 0, remoteObject); + + remoteObject = mainWindow->children()[5]->children()[0]->children()[0]; + widget.setChosenRemoteObject(remoteObject); + + QCOMPARE(widget.name(), QString("Duplicated grandparent/Duplicated parent/" + "Duplicated object")); + QCOMPARE(remoteObjectChosenSpy.count(), 2); + assertRemoteObjectSignal(remoteObjectChosenSpy, 1, remoteObject); + + remoteObject = mainWindow->children()[6]->children()[0]->children()[0]; + widget.setChosenRemoteObject(remoteObject); + + QCOMPARE(widget.name(), QString("The object name 7/Duplicated object")); + QCOMPARE(remoteObjectChosenSpy.count(), 3); + assertRemoteObjectSignal(remoteObjectChosenSpy, 2, remoteObject); +} + void RemoteObjectNameWidgetTest::testTargetApplicationStartedAfterWidget() { RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-04-08 15:14:25
|
Revision: 297 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=297&view=rev Author: danxuliu Date: 2011-04-08 15:14:19 +0000 (Fri, 08 Apr 2011) Log Message: ----------- When a duplicated name is matched in the text completion, the name in the match list is now replaced by the unique names of the remote objects it represents. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp 2011-04-05 02:11:06 UTC (rev 296) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.cpp 2011-04-08 15:14:19 UTC (rev 297) @@ -84,6 +84,24 @@ return reversedPathAsName(reversedUniquePath); } +QStringList RemoteObjectNameRegister::uniqueNames(const QString& name) const +throw (DBusException) { + Q_ASSERT(mRemoteObjectForName.values(name).count() > 0); + + QStringList uniqueNames; + + if (mRemoteObjectForName.values(name).count() <= 1) { + uniqueNames.append(name); + return uniqueNames; + } + + foreach (RemoteObject* remoteObject, mRemoteObjectForName.values(name)) { + uniqueNames.append(uniqueName(remoteObject)); + } + + return uniqueNames; +} + RemoteObject* RemoteObjectNameRegister::findRemoteObject(const QString& name) const { return findRemoteObject(name, 0); Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h 2011-04-05 02:11:06 UTC (rev 296) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameRegister.h 2011-04-08 15:14:19 UTC (rev 297) @@ -38,7 +38,10 @@ * case, the names of one or more ancestors are required to differentiate * between those remote objects. The compound name that identifies a remote * object can be got using uniqueName(RemoteObject*). Also, given a name, the - * remote object that it represents can be got using findObject(QString). + * remote object that it represents can be got using findObject(QString). An + * unqualified name (without any ancestor names, just the name of the remote + * object itself) can be used to get the unique names of all the remote objects + * represented by that unqualified name. * * Note that a name and the remote object it represents depends on the state of * the target application. For example, if there is a dialog named @@ -94,6 +97,17 @@ QString uniqueName(RemoteObject* remoteObject) const throw (DBusException); /** + * Returns the unique names of the remote objects that have the given name. + * The given name can not contain any ancestor names, and it must be a + * registered name. + * + * @param name The name of the remote objects to get their unique names. + * @return The unique names of the remote objects that have the given name. + * @throws DBusException If a DBus error happens. + */ + QStringList uniqueNames(const QString& name) const throw (DBusException); + + /** * Returns the remote object with the given name, if any. * When the name of the desired remote object is not unique the name of some * ancestor must be included. Ancestor names are separated using a "/". That Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2011-04-05 02:11:06 UTC (rev 296) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.cpp 2011-04-08 15:14:19 UTC (rev 297) @@ -19,12 +19,88 @@ #include "RemoteObjectNameWidget.h" #include "ui_RemoteObjectNameWidget.h" +#include <KDebug> #include <KMessageBox> #include "RemoteObjectChooser.h" #include "RemoteObjectNameRegister.h" #include "../targetapplication/TargetApplication.h" +/** + * Completion for remote object names. + * The items added for completion should be the unqualified remote object names, + * that is, just the name of the remote objects, without any ancestor. When a + * match is found, and it represents more than one remote object, the match is + * replaced by the unique names of its remote objects. + */ +class RemoteObjectNameCompletion: public KCompletion { +public: + + /** + * Creates a new RemoteObjectNameCompletion. + * + * @param nameRegister The register to query about the remote object names. + */ + RemoteObjectNameCompletion(RemoteObjectNameRegister* nameRegister): + KCompletion() { + mNameRegister = nameRegister; + } + +protected: + + /** + * Default parent implementation. + * It is implemented just to avoid a compilation warning that complains that + * the method in the parent class is hidden by + * postProcessMatches(QStringList*). + * + * @param matches The matches to process. + */ + virtual void postProcessMatches(KCompletionMatches* matches) const { + KCompletion::postProcessMatches(matches); + } + + /** + * Processes the matches to provide unique names. + * When a match is a name that represents several remote objects that match + * is replaced by all the unique names of the remote objects it represents. + * + * @param matches The matches to process. + */ + virtual void postProcessMatches(QStringList* matches) const { + QMutableListIterator<QString> it(*matches); + while (it.hasNext()) { + QStringList uniqueNames; + + try { + uniqueNames = mNameRegister->uniqueNames(it.next()); + } catch (DBusException e) { + kWarning() << "The unique names for the remote objects named " + << it.value() << "could not be added to the " + << "completion matches (" << e.message() << ")"; + } + + if (uniqueNames.count() > 1) { + it.remove(); + + qSort(uniqueNames); + + foreach (const QString& uniqueName, uniqueNames) { + it.insert(uniqueName); + } + } + } + } + +private: + + /** + * The RemoteObjectNameRegister to query about the remote object names. + */ + RemoteObjectNameRegister* mNameRegister; + +}; + //public: RemoteObjectNameWidget::RemoteObjectNameWidget(QWidget* parent): @@ -41,8 +117,10 @@ connect(ui->objectNamePushButton, SIGNAL(clicked(bool)), this, SLOT(showRemoteObjectChooser())); - KCompletion* completion = ui->objectNameLineEdit->completionObject(); + KCompletion* completion = + new RemoteObjectNameCompletion(mRemoteObjectNameRegister); completion->setOrder(KCompletion::Sorted); + ui->objectNameLineEdit->setCompletionObject(completion); foreach (const QString& name, mRemoteObjectNameRegister->names()) { completion->addItem(name); Modified: trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2011-04-05 02:11:06 UTC (rev 296) +++ trunk/ktutorial/ktutorial-editor/src/view/RemoteObjectNameWidget.h 2011-04-08 15:14:19 UTC (rev 297) @@ -37,6 +37,8 @@ * The line edit provides text completion for the names of the remote objects * in the target application. If there is no target application running, no * completion is provided. As soon as it starts, the completion is enabled. + * When the completion matches contain a name that represents several remote + * objects that match is replaced by the unique name of each remote object. * * When a name is set in the line edit the signal * remoteObjectChosen(RemoteObject*) is emitted. Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2011-04-05 02:11:06 UTC (rev 296) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2011-04-08 15:14:19 UTC (rev 297) @@ -58,6 +58,9 @@ void testUniqueNameWhenUniqueUnionOfGrandparentAndParent(); void testUniqueNameWhenEmptyName(); + void testUniqueNames(); + void testUniqueNamesWithSingleRemoteObject(); + private: QString mPath; @@ -358,6 +361,42 @@ QCOMPARE(remoteObjectNameRegister.uniqueName(remoteObject), QString("")); } +void RemoteObjectNameRegisterTest::testUniqueNames() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QStringList uniqueNames = + remoteObjectNameRegister.uniqueNames("Duplicated object"); + + QCOMPARE(uniqueNames.count(), 4); + QVERIFY(uniqueNames.contains("The object name 50/Duplicated object")); + QVERIFY(uniqueNames.contains("Duplicated grandparent/Duplicated parent/" + "Duplicated object")); + QVERIFY(uniqueNames.contains("The object name 7/Duplicated object")); + QVERIFY(uniqueNames.contains("The object name 8/Duplicated object")); +} + +void RemoteObjectNameRegisterTest::testUniqueNamesWithSingleRemoteObject() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameRegister remoteObjectNameRegister; + + QStringList uniqueNames = + remoteObjectNameRegister.uniqueNames("The object name 423"); + + QCOMPARE(uniqueNames.count(), 1); + QVERIFY(uniqueNames.contains("The object name 423")); +} + /////////////////////////////////// Helpers //////////////////////////////////// void RemoteObjectNameRegisterTest::assertNames(const QStringList& names) const { Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2011-04-05 02:11:06 UTC (rev 296) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2011-04-08 15:14:19 UTC (rev 297) @@ -17,6 +17,7 @@ ***************************************************************************/ #include <QtTest> +#include <QtTest/QTestKeyClicksEvent> #define protected public #define private public @@ -26,6 +27,7 @@ #include <QtDBus/QtDBus> +#include <KCompletionBox> #include <KLineEdit> #include <KProcess> @@ -56,6 +58,8 @@ void testSetChosenRemoteObject(); void testSetChosenRemoteObjectWithNameNotUnique(); + void testDuplicatedNameCompletion(); + void testTargetApplicationStartedAfterWidget(); void testTargetApplicationStopped(); @@ -238,6 +242,34 @@ assertRemoteObjectSignal(remoteObjectChosenSpy, 2, remoteObject); } +void RemoteObjectNameWidgetTest::testDuplicatedNameCompletion() { + TargetApplication::self()->setTargetApplicationFilePath(mPath); + TargetApplication::self()->start(); + + //Give the target application time to start + QTest::qWait(1000); + + RemoteObjectNameWidget widget; + KLineEdit* lineEdit = objectNameLineEdit(&widget); + lineEdit->setText("Duplicated "); + + QTest::keyClick(lineEdit, Qt::Key_O, Qt::NoModifier, 500); + + KCompletionBox* completionBox = lineEdit->completionBox(); + QStringList completionItems = completionBox->items(); + + QCOMPARE(completionItems.count(), 4); + QCOMPARE(completionItems[0], + QString("Duplicated grandparent/Duplicated parent/" + "Duplicated object")); + QCOMPARE(completionItems[1], + QString("The object name 50/Duplicated object")); + QCOMPARE(completionItems[2], + QString("The object name 7/Duplicated object")); + QCOMPARE(completionItems[3], + QString("The object name 8/Duplicated object")); +} + void RemoteObjectNameWidgetTest::testTargetApplicationStartedAfterWidget() { RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-04-15 16:29:34
|
Revision: 298 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=298&view=rev Author: danxuliu Date: 2011-04-15 16:29:27 +0000 (Fri, 15 Apr 2011) Log Message: ----------- Fix always exporting step text as rich text, even when there were no rich text or semantic markup tags. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2011-04-08 15:14:19 UTC (rev 297) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2011-04-15 16:29:27 UTC (rev 298) @@ -19,7 +19,9 @@ #include "JavascriptExporter.h" #include <QRegExp> +#include <QSet> #include <QStringList> +#include <QTextDocument> #include "../data/Reaction.h" #include "../data/Step.h" @@ -137,9 +139,13 @@ if (step->text().isEmpty()) { out() << "//Error: Step without text!\n"; - } else { + } else if (Qt::mightBeRichText(step->text()) || + mightContainSemanticMarkup(step->text())) { out() << stepVariable << ".setText(t.i18nc(\"@info\", \"" << escape(step->text()) << "\"));\n"; + } else { + out() << stepVariable << ".setText(t.i18nc(\"@info/plain\", \"" + << escape(step->text()) << "\"));\n"; } mOut << '\n'; @@ -485,3 +491,26 @@ return upperCamelCase; } + +bool JavascriptExporter::mightContainSemanticMarkup(const QString& text) const { + QSet<QString> semanticMarkupTags; + semanticMarkupTags << "application" << "bcode" << "command" << "email" + << "emphasis" << "envar" << "filename" << "icode" + << "interface" << "link" << "message" << "nl" + << "numid" << "placeholder" << "resource" << "shortcut"; + semanticMarkupTags << "para" << "title" << "subtitle" << "list" << "item"; + semanticMarkupTags << "note" << "warning"; + + QRegExp tagRegExp(QString::fromLatin1("<\\s*(\\w+)[^>]*>")); + + int index = text.indexOf(tagRegExp); + while (index >= 0) { + QString tagName = tagRegExp.capturedTexts().at(1).toLower(); + if (semanticMarkupTags.contains(tagName)) { + return true; + } + index = text.indexOf(tagRegExp, index + tagRegExp.matchedLength()); + } + + return false; +} Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2011-04-08 15:14:19 UTC (rev 297) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2011-04-15 16:29:27 UTC (rev 298) @@ -130,7 +130,9 @@ /** * Writes the full code (step creation, setup, tear down and adding to the * tutorial) of a step. - * If the step id isn't set, an error message is written instead. + * If the step id or name aren't set, an error message is written instead. + * When the text seems to contain rich text or semantic markup, it is + * formatted as rich text. Otherwise, it is formatted as plain text. * * @param step The step to write its code. */ @@ -312,6 +314,18 @@ */ QString toUpperCamelCase(QString text) const; + /** + * Returns true if the string text is likely to contain semantic markup. + * It just checks whether there is something that looks like a KUIT tag in + * the text. Although the result may be correct for common cases, there is + * no guarantee. + * + * @param text The string to check. + * @return True if the string text is likely to contain semantic markup, + * false otherwise. + */ + bool mightContainSemanticMarkup(const QString& text) const; + }; #endif Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2011-04-08 15:14:19 UTC (rev 297) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2011-04-15 16:29:27 UTC (rev 298) @@ -65,6 +65,8 @@ void testStep(); void testStepWithEscapeSequences(); + void testStepWithRichText(); + void testStepWithSemanticMarkup(); void testStepSetupCode(); void testStepSetupCodeWithReactions(); void testStepTearDownCode(); @@ -239,7 +241,7 @@ TUTORIAL_EMPTY_INFORMATION_CODE "//Step The id1\n" "theId1Step = ktutorial.newStep(\"The id1\");\n" -"theId1Step.setText(t.i18nc(\"@info\", \"The text1\"));\n" +"theId1Step.setText(t.i18nc(\"@info/plain\", \"The text1\"));\n" "\n" "function theId1StepSetup(step) {\n" " waitForTheSignalNameByTheEmitterName = \ @@ -262,7 +264,7 @@ "\n" "//Step The id2\n" "theId2Step = ktutorial.newStep(\"The id2\");\n" -"theId2Step.setText(t.i18nc(\"@info\", \"The text2\"));\n" +"theId2Step.setText(t.i18nc(\"@info/plain\", \"The text2\"));\n" "\n" "function theId2StepSetup(step) {\n" " waitForTheSignalNameByTheEmitterName = \ @@ -301,7 +303,7 @@ TUTORIAL_EMPTY_INFORMATION_CODE "//Step The id\n" "theIdStep = ktutorial.newStep(\"The id\");\n" -"theIdStep.setText(t.i18nc(\"@info\", \"The text\"));\n" +"theIdStep.setText(t.i18nc(\"@info/plain\", \"The text\"));\n" "\n" "tutorial.addStep(theIdStep);\n" "\n"; @@ -323,7 +325,7 @@ TUTORIAL_EMPTY_INFORMATION_CODE "//Step The \"id\"\n" "theIdStep = ktutorial.newStep(\"The \\\"id\\\"\");\n" -"theIdStep.setText(t.i18nc(\"@info\", \ +"theIdStep.setText(t.i18nc(\"@info/plain\", \ \"The\\ttext\\nwith \\\"escape\\\" sequences\"));\n" "\n" "tutorial.addStep(theIdStep);\n" @@ -332,6 +334,51 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testStepWithRichText() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + step->setText("The <i>rich</i> text"); + tutorial.addStep(step); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +"//Step The id\n" +"theIdStep = ktutorial.newStep(\"The id\");\n" +"theIdStep.setText(t.i18nc(\"@info\", \"The <i>rich</i> text\"));\n" +"\n" +"tutorial.addStep(theIdStep);\n" +"\n"; + + QCOMPARE(exportedTutorial, expected); +} + +void JavascriptExporterTest::testStepWithSemanticMarkup() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + step->setText("The text with <emphasis>semantic markup</emphasis>"); + tutorial.addStep(step); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +"//Step The id\n" +"theIdStep = ktutorial.newStep(\"The id\");\n" +"theIdStep.setText(t.i18nc(\"@info\", \ +\"The text with <emphasis>semantic markup</emphasis>\"));\n" +"\n" +"tutorial.addStep(theIdStep);\n" +"\n"; + + QCOMPARE(exportedTutorial, expected); +} + void JavascriptExporterTest::testStepSetupCode() { Tutorial tutorial; Step* step = new Step(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-05-05 14:39:24
|
Revision: 301 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=301&view=rev Author: danxuliu Date: 2011-05-05 14:39:15 +0000 (Thu, 05 May 2011) Log Message: ----------- Add a basic helper to edit KUIT semantic markup in the text of a step. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupLinkWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupParserTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-04-30 17:31:13 UTC (rev 300) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-05-05 14:39:15 UTC (rev 301) @@ -9,6 +9,9 @@ NewWaitForWidget.cpp ReactionTreeItem.cpp ReactionWidget.cpp + SemanticMarkupEdition.cpp + SemanticMarkupLinkWidget.cpp + SemanticMarkupParser.cpp StepCustomCodeWidget.cpp StepDataWidget.cpp StepTreeItem.cpp @@ -53,6 +56,7 @@ NewWaitForWidget.ui ReactionWidget.ui RemoteObjectNameWidget.ui + SemanticMarkupLinkWidget.ui StepDataWidget.ui TutorialInformationWidget.ui WaitForEventWidget.ui Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,418 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "SemanticMarkupEdition.h" + +#include <QRegExp> +#include <QTextEdit> + +#include <KAction> +#include <KActionCollection> +#include <KDialog> +#include <KLocalizedString> +#include <KPushButton> + +#include "DialogRunner.h" +#include "SemanticMarkupLinkWidget.h" +#include "SemanticMarkupParser.h" + +//public: + +SemanticMarkupEdition::SemanticMarkupEdition(QTextEdit* textEdit): + QObject(textEdit), + mTextEdit(textEdit) { + Q_ASSERT(textEdit); + + connect(mTextEdit, SIGNAL(cursorPositionChanged()), + this, SLOT(updateActionStates())); + //TODO Needed to update the action states when the user deletes a character; + //look how to do that without updating twice in any other text change. + connect(mTextEdit, SIGNAL(textChanged()), + this, SLOT(updateActionStates())); +} + +void SemanticMarkupEdition::createActions(KActionCollection* actionCollection) { + Q_ASSERT(actionCollection); + + mEmphasisAction = new KAction("emphasis", actionCollection); + mEmphasisAction->setCheckable(true); + mEmphasisAction->setToolTip(i18nc("@info:tooltip", "Emphasized text")); + mEmphasisAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag to emphasize a word or phrase in the text.<nl/>" + "Example: <emphasis>emphasized text</emphasis>")); + actionCollection->addAction("kuit-edition-phrase-emphasis", + mEmphasisAction); + connect(mEmphasisAction, SIGNAL(triggered()), this, SLOT(emphasis())); + mActions.append(mEmphasisAction); + + mEmphasisStrongAction = new KAction("emphasis (strong)", actionCollection); + mEmphasisStrongAction->setCheckable(true); + mEmphasisStrongAction->setToolTip(i18nc("@info:tooltip", + "Strongly emphasized text")); + mEmphasisStrongAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag to strongly emphasize a word or phrase in the text.<nl/>" + "Example: <emphasis strong=\"1\">strongly emphasized text</emphasis>")); + actionCollection->addAction("kuit-edition-phrase-emphasis-strong", + mEmphasisStrongAction); + connect(mEmphasisStrongAction, SIGNAL(triggered()), + this, SLOT(emphasisStrong())); + mActions.append(mEmphasisStrongAction); + + mFilenameAction = new KAction("filename", actionCollection); + mFilenameAction->setCheckable(true); + mFilenameAction->setToolTip(i18nc("@info:tooltip", "Filename or path")); + mFilenameAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for file or folder name or path.<nl/>" + "The path separators will be transformed into what is native to the " + "platform.<nl/>" + "Example: <filename>/home/user/Music/song.ogg</filename>")); + actionCollection->addAction("kuit-edition-phrase-filename", + mFilenameAction); + connect(mFilenameAction, SIGNAL(triggered()), this, SLOT(filename())); + mActions.append(mFilenameAction); + + mInterfaceAction = new KAction("interface", actionCollection); + mInterfaceAction->setCheckable(true); + mInterfaceAction->setToolTip(i18nc("@info:tooltip", + "GUI interface element")); + mInterfaceAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for paths to GUI interface elements.<nl/>" + "If there is more than one element in the path, use \"|\" to delimit " + "elements, which will be converted into canonical delimiter.<nl/>" + "Example: <interface>File|Open</interface>")); + actionCollection->addAction("kuit-edition-phrase-interface", + mInterfaceAction); + connect(mInterfaceAction, SIGNAL(triggered()), this, SLOT(interface())); + mActions.append(mInterfaceAction); + + mLinkAction = new KAction("link", actionCollection); + mLinkAction->setCheckable(true); + mLinkAction->setToolTip(i18nc("@info:tooltip", "Link to URL or widget")); + mLinkAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag to link to a URL-addressable resource.<nl/>" + "Widgets in the target application interface can be linked using " + "<emphasis>widget:theObjectNameOfTheWidget</emphasis><nl/>" + "Example: <link url=\"http://www.kde.org\">a link</link>")); + actionCollection->addAction("kuit-edition-phrase-link", mLinkAction); + connect(mLinkAction, SIGNAL(triggered()), this, SLOT(link())); + mActions.append(mLinkAction); + + mNlAction = new KAction("nl", actionCollection); + mNlAction->setToolTip(i18nc("@info:tooltip", "Line break")); + mNlAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for line breaks.<nl/>" + "Example: line<nl/>break")); + actionCollection->addAction("kuit-edition-phrase-nl", mNlAction); + connect(mNlAction, SIGNAL(triggered()), this, SLOT(nl())); + mActions.append(mNlAction); + + mShortcutAction = new KAction("shortcut", actionCollection); + mShortcutAction->setCheckable(true); + mShortcutAction->setToolTip(i18nc("@info:tooltip", + "Combination of keys to press")); + mShortcutAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for combinations of keys to press.<nl/>" + "Separate the keys by \"+\" or \"-\", and the shortcut will be " + "converted into canonical form.<nl/>" + "Example: <shortcut>Ctrl+N</shortcut>")); + actionCollection->addAction("kuit-edition-phrase-shortcut", + mShortcutAction); + connect(mShortcutAction, SIGNAL(triggered()), this, SLOT(shortcut())); + mActions.append(mShortcutAction); + + mParaAction = new KAction("para", actionCollection); + mParaAction->setCheckable(true); + mParaAction->setToolTip(i18nc("@info:tooltip", "Paragraph")); + mParaAction->setWhatsThis(i18nc("@info:whatsthis", + "<para>Structure tag for text paragraphs.<nl/>" + "Example: one paragraph</para><para>Other paragraph</para>")); + actionCollection->addAction("kuit-edition-structure-para", mParaAction); + connect(mParaAction, SIGNAL(triggered()), this, SLOT(para())); + mActions.append(mParaAction); + + mListAction = new KAction("list", actionCollection); + mListAction->setCheckable(true); + mListAction->setToolTip(i18nc("@info:tooltip", "List of items")); + mListAction->setWhatsThis(i18nc("@info:whatsthis", + "<para>Structure tag for lists of items.<nl/>" + "Can contain only <item> as subtags. List is considered an " + "element of the paragraph, so the <list> must be found inside " + "<para>.<nl/>" + "Example: <list>" + " <item>One item</item>" + " <item>Other item</item>" + " </list></para>")); + actionCollection->addAction("kuit-edition-structure-list", mListAction); + connect(mListAction, SIGNAL(triggered()), this, SLOT(list())); + mActions.append(mListAction); + + mItemAction = new KAction("item", actionCollection); + mItemAction->setCheckable(true); + mItemAction->setToolTip(i18nc("@info:tooltip", "List items")); + mItemAction->setWhatsThis(i18nc("@info:whatsthis", + "<para>Structure tag for list items.<nl/>" + "Example: <list>" + " <item>One item</item>" + " <item>Other item</item>" + " </list></para>")); + actionCollection->addAction("kuit-edition-structure-item", mItemAction); + connect(mItemAction, SIGNAL(triggered()), this, SLOT(item())); + mActions.append(mItemAction); + + updateActionStates(); +} + +//private: + +QAction* SemanticMarkupEdition::actionForElement(const StartTag& startTag) + const { + QAction* action = 0; + + QRegExp emphasisStrongRegExp("strong=\"yes|true|1\""); + if (startTag.mName == "emphasis" && + startTag.mAttributes.indexOf(emphasisStrongRegExp) < 0) { + action = mEmphasisAction; + } else if (startTag.mName == "emphasis" && + startTag.mAttributes.indexOf(emphasisStrongRegExp) >= 0) { + action = mEmphasisStrongAction; + } else if (startTag.mName == "filename") { + action = mFilenameAction; + } else if (startTag.mName == "interface") { + action = mInterfaceAction; + } else if (startTag.mName == "link") { + action = mLinkAction; + } else if (startTag.mName == "nl") { + action = mNlAction; + } else if (startTag.mName == "shortcut") { + action = mShortcutAction; + } else if (startTag.mName == "para") { + action = mParaAction; + } else if (startTag.mName == "list") { + action = mListAction; + } else if (startTag.mName == "item") { + action = mItemAction; + } + + return action; +} + +void SemanticMarkupEdition::writeSimpleTagFor(const QAction* action) { + if (action->isChecked()) { + mTextEdit->insertPlainText('<' + action->text() + '>'); + } else { + mTextEdit->insertPlainText("</" + action->text() + '>'); + } +} + +QDialog* SemanticMarkupEdition::newLinkDialog(const QString& url) const { + KDialog* dialog = new KDialog(mTextEdit); + dialog->setModal(true); + + dialog->setButtons(KDialog::Ok | KDialog::Cancel); + + dialog->button(KDialog::Ok)->setObjectName("okButton"); + dialog->button(KDialog::Cancel)->setObjectName("cancelButton"); + + SemanticMarkupLinkWidget* linkWidget = new SemanticMarkupLinkWidget(dialog); + linkWidget->setUrl(url); + dialog->setMainWidget(linkWidget); + dialog->setWindowTitle(linkWidget->windowTitle()); + + return dialog; +} + +//private slots: + +void SemanticMarkupEdition::updateActionStates() { + Q_ASSERT(!mActions.isEmpty()); + + foreach (QAction* action, mActions) { + action->setEnabled(true); + action->setChecked(false); + } + + mLinkElementClosed = false; + + SemanticMarkupParser parser; + parser.setCursorIndex(mTextEdit->textCursor().position()); + parser.parse(mTextEdit->toPlainText()); + + if (parser.isCursorInsideTag()) { + foreach (QAction* action, mActions) { + action->setEnabled(false); + } + + return; + } + + QList<StartTag> openElementsAtCursor = parser.openElementsAtCursor(); + if (openElementsAtCursor.isEmpty()) { + mListAction->setEnabled(false); + mItemAction->setEnabled(false); + + return; + } + + QList<QAction*> phraseActions; + phraseActions << mEmphasisAction << mEmphasisStrongAction << mFilenameAction + << mInterfaceAction << mLinkAction << mNlAction + << mShortcutAction; + + foreach (const StartTag& startTag, openElementsAtCursor) { + QAction* action = actionForElement(startTag); + if (!action) { + continue; + } + + action->setChecked(true); + + bool elementClosed = parser.isElementClosed(startTag); + if (elementClosed && action != mLinkAction) { + action->setEnabled(false); + } else if (elementClosed && action == mLinkAction) { + action->setEnabled(true); + mLinkElementClosed = true; + } + + if (phraseActions.contains(action)) { + foreach (QAction* actionToDisable, mActions) { + if (actionToDisable != action) { + actionToDisable->setEnabled(false); + } + } + } else if (action == mParaAction) { + if (!mListAction->isChecked()) { + mItemAction->setEnabled(false); + } + } else if (action == mListAction) { + mParaAction->setEnabled(false); + if (!mItemAction->isChecked()) { + foreach (QAction* phraseAction, phraseActions) { + phraseAction->setEnabled(false); + } + } + } else if (action == mItemAction) { + mParaAction->setEnabled(false); + mListAction->setEnabled(false); + } + } +} + +void SemanticMarkupEdition::emphasis() { + writeSimpleTagFor(mEmphasisAction); +} + +void SemanticMarkupEdition::emphasisStrong() { + if (mEmphasisStrongAction->isChecked()) { + mTextEdit->insertPlainText("<emphasis strong=\"yes\">"); + } else { + mTextEdit->insertPlainText("</emphasis>"); + } +} + +void SemanticMarkupEdition::filename() { + writeSimpleTagFor(mFilenameAction); +} + +void SemanticMarkupEdition::interface() { + writeSimpleTagFor(mInterfaceAction); +} + +void SemanticMarkupEdition::link() { + if (!mLinkAction->isChecked() && !mLinkElementClosed) { + mTextEdit->insertPlainText("</link>"); + return; + } + + if (!mLinkElementClosed) { + QScopedPointer<QDialog> dialog(newLinkDialog("")); + if (DialogRunner(dialog.data()).exec() == QDialog::Rejected) { + mLinkAction->setChecked(false); + return; + } + + QString newUrl = dialog->findChild<SemanticMarkupLinkWidget*>()->url(); + mTextEdit->insertPlainText(QString("<link url=\"%1\">").arg(newUrl)); + + return; + } + + mLinkAction->setChecked(true); + + QString text = mTextEdit->toPlainText(); + int cursorIndex = mTextEdit->textCursor().position(); + QRegExp linkRegExp("<\\s*link\\s*(\\w+=\"[^\"]*\")*\\s*>"); + int linkElementIndex = text.lastIndexOf(linkRegExp, cursorIndex); + + QString linkText = linkRegExp.capturedTexts().at(0); + QRegExp urlRegExp(" url=\"([^\"]*)\""); + int urlAttributeIndex = linkText.indexOf(urlRegExp); + + if (urlAttributeIndex == -1) { + urlAttributeIndex = QString("<link").length(); + } + + int absoluteUrlAttributeIndex = linkElementIndex + urlAttributeIndex; + QString oldUrlAttribute = urlRegExp.capturedTexts().at(0); + QString oldUrl = urlRegExp.capturedTexts().at(1); + + QScopedPointer<QDialog> dialog(newLinkDialog(oldUrl)); + if (DialogRunner(dialog.data()).exec() == QDialog::Rejected) { + return; + } + + QString newUrl = dialog->findChild<SemanticMarkupLinkWidget*>()->url(); + + QTextCursor cursor = mTextEdit->textCursor(); + cursor.setPosition(absoluteUrlAttributeIndex); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, + oldUrlAttribute.length()); + cursor.insertText(QString(" url=\"%1\"").arg(newUrl)); +} + +void SemanticMarkupEdition::nl() { + mTextEdit->insertPlainText("<nl/>\n"); +} + +void SemanticMarkupEdition::shortcut() { + writeSimpleTagFor(mShortcutAction); +} + +void SemanticMarkupEdition::para() { + if (mParaAction->isChecked()) { + mTextEdit->insertPlainText("<para>"); + } else { + mTextEdit->insertPlainText("</para>\n\n"); + } +} + +void SemanticMarkupEdition::list() { + if (mListAction->isChecked()) { + mTextEdit->insertPlainText("<list>\n"); + } else { + mTextEdit->insertPlainText("</list>\n"); + } +} + +void SemanticMarkupEdition::item() { + if (mItemAction->isChecked()) { + mTextEdit->insertPlainText("<item>"); + } else { + mTextEdit->insertPlainText("</item>\n"); + } +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,248 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef SEMANTICMARKUPEDITION_H +#define SEMANTICMARKUPEDITION_H + +#include <QObject> + +class QAction; +class QDialog; +class QTextEdit; + +class KActionCollection; + +class StartTag; + +/** + * Helper to edit KUIT semantic markup in a QTextEdit widget. + * SemanticMarkupEdition provides several actions that ease the edition of the + * most common KUIT semantic markup tags. The actions are added to a + * KActionCollection using createActions(KActionCollection*). + * + * Triggering an action will write its associated start tag in the current + * position of the text edit cursor. Triggering the action again will write its + * associated end tag. When a tag needs extra information from the user to be + * written (for example, the URL of a link element), a dialog will be shown. + * Also, triggering the action when the cursor is between two paired start and + * end tags will show the dialog and update the attribute. + * + * The action states will be updated based on the cursor of the QTextEdit. + * Depending on its position or the current selection some actions may be + * disabled or checked. + * + * When the cursor is inside any tag all the actions will be disabled. If it is + * between two paired start and end tags they will also be disabled (except the + * ones that show a dialog), although the action for that tag (and its paired + * parent elements) will be checked. If the cursor is after a start tag, the + * action for that tag will be enabled and checked (and triggering it will write + * the end tag). + * + * The actions may also be disabled when their element can not be written at the + * current position of the cursor. For example, the list action will be disabled + * unless the cursor is after a para tag. It will be disabled too if after the + * para tag there is a phrase element tag, as list elements can be child only of + * para elements. + * + * Only the cursor position is used to update the state of the actions. + * Selections are not taken into account. + */ +class SemanticMarkupEdition: public QObject { +Q_OBJECT +public: + + /** + * Creates a new SemanticMarkupEdition. + * The QTextEdit will also act as parent of the SemanticMarkupEdition. + * + * @param textEdit The QTextEdit used for the edition of the text. + */ + SemanticMarkupEdition(QTextEdit* textEdit); + + /** + * Creates the actions for markup edition and adds them to the given + * collection. + * The collection is made parent of the actions. + * + * @param actionCollection The collection to add the actions to. + */ + void createActions(KActionCollection* actionCollection); + +private: + + /** + * All the edition actions. + */ + QList<QAction*> mActions; + + /** + * Action for "emphasis" phrase tag. + */ + QAction* mEmphasisAction; + + /** + * Action for "emphasis" phrase tag with "strong" attribute. + */ + QAction* mEmphasisStrongAction; + + /** + * Action for "filename" phrase tag. + */ + QAction* mFilenameAction; + + /** + * Action for "interface" phrase tag. + */ + QAction* mInterfaceAction; + + /** + * Action for "link" phrase tag. + */ + QAction* mLinkAction; + + /** + * Action for "nl" phrase tag. + */ + QAction* mNlAction; + + /** + * Action for "shortcut" phrase tag. + */ + QAction* mShortcutAction; + + /** + * Action for "para" structure tag. + */ + QAction* mParaAction; + + /** + * Action for "list" structure tag. + */ + QAction* mListAction; + + /** + * Action for "item" structure tag. + */ + QAction* mItemAction; + + /** + * The QTextEdit used for the edition of the text. + */ + QTextEdit* mTextEdit; + + /** + * Whether the cursor is between two paired start and end link elements or + * not. + */ + bool mLinkElementClosed; + + /** + * Returns the action that represents the given start tag. + * + * @param startTag The start tag to get its action. + * @return The action for the tag. + */ + QAction* actionForElement(const StartTag& startTag) const; + + /** + * Writes a simple tag for the given action. + * If the action is checked, a start tag for the element of the action is + * inserted in the text edit. If the action is not checked, an end tag is + * inserted. + * + * @param action The action to write its tag. + */ + void writeSimpleTagFor(const QAction* action); + + /** + * Creates a new dialog for link elements. + * The dialog contains a SemanticMarkupLinkWidget and two dialog buttons: Ok + * and Cancel. + * + * @param url The URL to show in the link widget of the dialog. + * @return The new dialog. + */ + QDialog* newLinkDialog(const QString& url) const; + +private Q_SLOTS: + + /** + * Updates the state of the edition actions based on the cursor position. + */ + void updateActionStates(); + + /** + * Behavior for "emphasis" phrase tag edition. + */ + void emphasis(); + + /** + * Behavior for "emphasis" phrase tag with "strong" attribute edition. + */ + void emphasisStrong(); + + /** + * Behavior for "filename" phrase tag edition. + */ + void filename(); + + /** + * Behavior for "interface" phrase tag edition. + */ + void interface(); + + /** + * Behavior for "link" phrase tag edition. + * Unlike other tags, to write the start link" element the user has to + * provide some information. A dialog is shown to the user to set the URL of + * the link. + * + * Moreover, the URL can be modified even on a closed "link" element. The + * "link" action can be triggered even on a closed element, and when the + * action is triggered, the dialog is shown to modify the URL. + */ + void link(); + + /** + * Behavior for "nl" phrase tag edition. + */ + void nl(); + + /** + * Behavior for "shortcut" phrase tag edition. + */ + void shortcut(); + + /** + * Behavior for "para" structure tag edition. + */ + void para(); + + /** + * Behavior for "list" structure tag edition. + */ + void list(); + + /** + * Behavior for "item" structure tag edition. + */ + void item(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "SemanticMarkupLinkWidget.h" + +#include "ui_SemanticMarkupLinkWidget.h" + +#ifdef QT_QTDBUS_FOUND +#include "RemoteObjectNameWidget.h" +#endif + +//public: + +SemanticMarkupLinkWidget::SemanticMarkupLinkWidget(QWidget* parent): + QWidget(parent) { + + ui = new Ui::SemanticMarkupLinkWidget(); + ui->setupUi(this); + +#ifdef QT_QTDBUS_FOUND + mRemoteObjectNameWidget = new RemoteObjectNameWidget(this); + + //Replace ui->widgetLinkLineEdit with mRemoteObjectNameWidget + ui->valueVerticalLayout->removeWidget(ui->widgetLinkLineEdit); + delete ui->widgetLinkLineEdit; + + ui->valueVerticalLayout->insertWidget(1, mRemoteObjectNameWidget); +#endif + + ui->genericLinkRadioButton->setChecked(true); + + connect(ui->genericLinkRadioButton, SIGNAL(toggled(bool)), + ui->genericLinkLineEdit, SLOT(setEnabled(bool))); + +#ifdef QT_QTDBUS_FOUND + mRemoteObjectNameWidget->setEnabled(false); + + connect(ui->widgetLinkRadioButton, SIGNAL(toggled(bool)), + mRemoteObjectNameWidget, SLOT(setEnabled(bool))); +#else + ui->widgetLinkLineEdit->setEnabled(false); + + connect(ui->widgetLinkRadioButton, SIGNAL(toggled(bool)), + ui->widgetLinkLineEdit, SLOT(setEnabled(bool))); +#endif +} + +SemanticMarkupLinkWidget::~SemanticMarkupLinkWidget() { + delete ui; +} + +QString SemanticMarkupLinkWidget::url() const { + if (ui->genericLinkRadioButton->isChecked()) { + return ui->genericLinkLineEdit->text(); + } + +#ifdef QT_QTDBUS_FOUND + return "widget:" + mRemoteObjectNameWidget->name(); +#else + return "widget:" + ui->widgetLinkLineEdit->text(); +#endif +} + +void SemanticMarkupLinkWidget::setUrl(const QString& url) { + if (!url.startsWith(QLatin1String("widget:"))) { + ui->genericLinkRadioButton->setChecked(true); + ui->genericLinkLineEdit->setText(url); + return; + } + + ui->widgetLinkRadioButton->setChecked(true); + QString widgetName = url.mid(QString("widget:").length()); +#ifdef QT_QTDBUS_FOUND + mRemoteObjectNameWidget->setName(widgetName); +#else + ui->widgetLinkLineEdit->setText(widgetName); +#endif +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef SEMANTICMARKUPLINKWIDGET_H +#define SEMANTICMARKUPLINKWIDGET_H + +#include <QWidget> + +#ifdef QT_QTDBUS_FOUND +class RemoteObjectNameWidget; +#endif + +namespace Ui { +class SemanticMarkupLinkWidget; +} + +/** + * Helper widget for the semantic markup edition to input the URL of a link. + * The widget contains a line edit where any URL can be written. However, it + * also offers support for a specific type of URLs: widget URLs. There are two + * radio buttons to select between a generic URL and a widget URL. + * + * Widget URLs represent a widget in the target application. When DBus module is + * enabled, the widget to link to can be selected using a RemoteObjectChooser. + * + * When the URL is got, the URL returned depends on the current type of URL + * selected. Widget URLs also contain the "widget:" protocol. When the URL is + * set, the line edit filled and radio button checked depend on the type of URL + * set. + */ +class SemanticMarkupLinkWidget: public QWidget { +Q_OBJECT +public: + + /** + * Creates a new SemanticMarkupLinkWidget. + * + * @param parent The parent QWidget. + */ + explicit SemanticMarkupLinkWidget(QWidget* parent = 0); + + /** + * Destroys this widget. + */ + virtual ~SemanticMarkupLinkWidget(); + + /** + * Returns the URL. + * If the URL is a widget URL, the returned URL contains the "widget:" + * protocol. + * + * @return The URL. + */ + QString url() const; + + /** + * Sets the URL. + * If the URL is a widget URL, the "widget:" protocol is not shown in its + * line edit. + * + * @param url The URL to set. + */ + void setUrl(const QString& url); + +private: + + /** + * The Ui Designer generated class. + */ + Ui::SemanticMarkupLinkWidget* ui; + +#ifdef QT_QTDBUS_FOUND + /** + * The widget to get the name of a remote object. + */ + RemoteObjectNameWidget* mRemoteObjectNameWidget; +#endif + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SemanticMarkupLinkWidget</class> + <widget class="QWidget" name="SemanticMarkupLinkWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string comment="@title">URL for <emphasis>link</emphasis> element</string> + </property> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>Set the <emphasis>url</emphasis> attribute for the <emphasis>link</emphasis> semantic element.</para> + +<para>Any URL can be set in the <interface>Generic</interface> field. For example, the URL of a webpage or the URL to link to a widget.</para> + +<para>However, to link to a widget it is better to just write the object name of the desired widget in the <interface>Widget</interface> field. Moreover, that field provides also text completion for the names and a dialog to choose the widget from a running target application.</para> + +<para>These advanced features, though, are not available in every system; if <application>KTutorial editor</application> was not built with <application>QtDBus</application> support only a plain text line will be shown. Again, only the name has to be written in the text line for the widget; <emphasis>widget:</emphasis> is automatically added to the URL when the dialog is accepted.</para></string> + </property> + <layout class="QVBoxLayout" name="SemanticMarkupLinkWidgetLayout"> + <item> + <layout class="QHBoxLayout" name="semanticMarkupLinkWidgetHorizontalLayout"> + <item> + <layout class="QVBoxLayout" name="radioButtonVerticalLayout"> + <item> + <widget class="QRadioButton" name="genericLinkRadioButton"> + <property name="text"> + <string comment="@option:radio Generic link URL">Generic</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="widgetLinkRadioButton"> + <property name="text"> + <string comment="@option:radio Widget link URL">Widget</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="valueVerticalLayout"> + <item> + <widget class="KLineEdit" name="genericLinkLineEdit"/> + </item> + <item> + <widget class="KLineEdit" name="widgetLinkLineEdit"/> + </item> + </layout> + </item> + </layout> + </item> + <item> + <spacer name="semanticMarkupLinkWidgetSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "SemanticMarkupParser.h" + +#include <QRegExp> +#include <QStringList> + +//public: + +SemanticMarkupParser::SemanticMarkupParser(): + mCursorIndex(-1), + mCursorInsideTag(false) { +} + +void SemanticMarkupParser::setCursorIndex(int cursorIndex) { + mCursorIndex = cursorIndex; +} + +void SemanticMarkupParser::parse(const QString& text) { + mCursorInsideTag = false; + mOpenElements.clear(); + mOpenElementsAtCursor.clear(); + mNotClosedChildElements.clear(); + + QRegExp startTagRegExp("<\\s*(\\w+)\\s*((\\w+=\"[^\"]*\"\\s*)*)>"); + QRegExp endTagRegExp("</\\s*(\\w+)\\s*>"); + QRegExp emptyTagRegExp("<\\s*(\\w+)\\s*((\\w+=\"[^\"]*\"\\s*)*)/>"); + QRegExp tagRegExp(startTagRegExp.pattern() + '|' + + endTagRegExp.pattern() + '|' + + emptyTagRegExp.pattern()); + + bool parsingReachedCursor = false; + + int parsingIndex = tagRegExp.indexIn(text); + while (parsingIndex >= 0) { + QString tag = tagRegExp.capturedTexts().at(0); + + if (mCursorIndex > parsingIndex && + mCursorIndex < parsingIndex + tagRegExp.matchedLength()) { + mCursorInsideTag = true; + mOpenElementsAtCursor.clear(); + return; + } + + if (!parsingReachedCursor && parsingIndex >= mCursorIndex) { + parsingReachedCursor = true; + mOpenElementsAtCursor = mOpenElements; + } + + if (startTagRegExp.exactMatch(tag)) { + StartTag startTag; + startTag.mName = startTagRegExp.capturedTexts().at(1); + startTag.mAttributes = startTagRegExp.capturedTexts().at(2); + startTag.mIndex = parsingIndex; + mOpenElements.insert(0, startTag); + } else if (endTagRegExp.exactMatch(tag)) { + QString endTagName = endTagRegExp.capturedTexts().at(1); + + int endElementIndex = indexOf(mOpenElements, endTagName); + if (endElementIndex >= 0) { + int index = 0; + while (index < endElementIndex) { + mNotClosedChildElements.append(mOpenElements.first()); + mOpenElements.removeFirst(); + index++; + } + mOpenElements.removeFirst(); + } + } + + parsingIndex = parsingIndex + tagRegExp.matchedLength(); + parsingIndex = tagRegExp.indexIn(text, parsingIndex); + } + + if (!parsingReachedCursor) { + mOpenElementsAtCursor = mOpenElements; + } +} + +bool SemanticMarkupParser::isElementClosed(const StartTag& startTag) const { + if (indexOf(mOpenElements, startTag) > -1 || + indexOf(mNotClosedChildElements, startTag) > -1) { + return false; + } + + return true; +} + +bool SemanticMarkupParser::isCursorInsideTag() const { + return mCursorInsideTag; +} + +QList<StartTag> SemanticMarkupParser::openElementsAtCursor() const { + return mOpenElementsAtCursor; +} + +//private: + +int SemanticMarkupParser::indexOf(const QList<StartTag>& startTags, + const QString& tagName) const { + for (int i=0; i<startTags.count(); ++i) { + if (startTags[i].mName == tagName) { + return i; + } + } + + return -1; +} + +int SemanticMarkupParser::indexOf(const QList<StartTag>& startTags, + const StartTag& startTag) const { + for (int i=0; i<startTags.count(); ++i) { + if (startTags[i].mName == startTag.mName && + startTags[i].mAttributes == startTag.mAttributes && + startTags[i].mIndex == startTag.mIndex) { + return i; + } + } + + return -1; +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,182 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef SEMANTICMARKUPPARSER_H +#define SEMANTICMARKUPPARSER_H + +#include <QList> +#include <QString> + +/** + * Structure to store the data of a XML start tag. + */ +struct StartTag { + + /** + * The tag name. + */ + QString mName; + + /** + * The attributes, if any. + */ + QString mAttributes; + + /** + * The index of the tag in the text (the position of the '<' character). + */ + int mIndex; + + /** + * Creates a new StartTag. + */ + StartTag(): + mName(""), + mAttributes(""), + mIndex(-1) { + } + +}; + +/** + * Simple parser for texts containing KUIT semantic markup. + * The parser is meant to provide SemanticMarkupEdition with information about + * the markup in the text. Thus, it is designed just for that purpose; it is not + * a general purpose parser. + * + * The parser supports XML where not all the elements have been closed. For + * example, it can parse a text with just a start tag, or a text where there + * is an unpaired start tag inside a valid element (one with paired start and + * end tags). + * + * The parser stores which elements have been opened, and when it reaches an end + * tag it regardes as closed the newest element that matches the end tag. For + * example, in <a><a><b></a></b>, when </a> + * is reached the second "a" element is regarded as closed. The </b> will + * be ignored, as the "b" element is no longer taken into account since its + * parent element was closed. Note, however, that the "b" element is not closed, + * and isElementClosed(StartTag) with that "b" element would return false. + * Passing the first "a" element would also return false; true would be returned + * only for the second "a" element. + * + * Before parsing a text, the cursor index has to be set using + * setCursorIndex(int). Then the text can be parsed using parse(QString). + * + * Once parsed, the SemanticMarkupParser object provides information about the + * parsed text regarding the cursor position. To update the information provided + * the text must be parsed again; that is, even if the only change is in the + * cursor index, parse(QString) has to be called again. + */ +class SemanticMarkupParser { +public: + + /** + * Creates a new SemanticMarkupParser. + */ + SemanticMarkupParser(); + + /** + * Sets the index of the cursor. + * + * @param cursorIndex The index to set. + */ + void setCursorIndex(int cursorIndex); + + /** + * Parses the given text, updating the state of this parser. + * + * @param text The text to parse. + */ + void parse(const QString& text); + + /** + * Returns whether the cursor is inside a tag or not. + * + * @return True if the cursor is inside a tag, false otherwise. + */ + bool isCursorInsideTag() const; + + /** + * Returns whether the given start tag was closed or not. + * + * @param startTag The tag to check. + * @return True if the start tag was closed, false otherwise. + */ + bool isElementClosed(const StartTag& startTag) const; + + /** + * Returns a list with all the start tags that were open at the cursor + * position. + * The order of the tags goes from the deepest one to the more general one. + * That is, for <a><b>, the first tag is "b" and the second one + * is "a". + * + * @return The start tags open at the cursor. + */ + QList<StartTag> openElementsAtCursor() const; + +private: + + /** + * The position of the cursor in the text. + */ + int mCursorIndex; + + /** + * Whether the cursor was inside a tag or not in the last parsed text. + */ + bool mCursorInsideTag; + + /** + * A list with all the open elements at the end of the last parsed text. + */ + QList<StartTag> mOpenElements; + + /** + * A list with all the open elements at the cursor of the last parsed text. + */ + QList<StartTag> mOpenElementsAtCursor; + + /** + * A list with all the elements that were not closed but some ancestor + * element was at the end of the last parsed text. + */ + QList<StartTag> mNotClosedChildElements; + + /** + * The position in the given list of a tag with the given name. + * + * @param startTags The list to check. + * @param tagName The name of the tag to find. + * @return The position of the tag name, or -1 if it is not found. + */ + int indexOf(const QList<StartTag>& startTags, const QString& tagName) const; + + /** + * The position in the given list of the given tag. + * + * @param startTags The list to check. + * @param startTag The tag to find. + * @return The position of the tag, or -1 if it is not found. + */ + int indexOf(const QList<StartTag>& startTags, + const StartTag& startTag) const; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp 2011-04-30 17:31:13 UTC (rev 300) +++ trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -18,7 +18,11 @@ #include "StepDataWidget.h" +#include <KActionCollection> +#include <KToolBar> + #include "ui_StepDataWidget.h" +#include "SemanticMarkupEdition.h" #include "../commands/StepCommands.h" #include "../data/Step.h" @@ -30,6 +34,17 @@ ui = new Ui::StepDataWidget(); ui->setupUi(this); + KToolBar* toolBar = new KToolBar(this); + int textEditIndex = ui->textLayout->indexOf(ui->textTextEdit); + ui->textLayout->insertWidget(textEditIndex, toolBar); + + KActionCollection* editionActions = new KActionCollection(this); + editionActions->addAssociatedWidget(toolBar); + + SemanticMarkupEdition* semanticMarkupEdition = + new SemanticMarkupEdition(ui->textTextEdit); + semanticMarkupEdition->createActions(editionActions); + ui->idLineEdit->setText(step->id()); ui->textTextEdit->setPlainText(step->text()); } Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-04-30 17:31:13 UTC (rev 300) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-05-05 14:39:15 UTC (rev 301) @@ -26,6 +26,9 @@ NewWaitForWidget ReactionTreeItem ReactionWidget + SemanticMarkupEdition + SemanticMarkupLinkWidget + SemanticMarkupParser StepCustomCodeWidget StepDataWidget StepTreeItem @@ -79,6 +82,9 @@ NewWaitForWidget ReactionTreeItem ReactionWidget + SemanticMarkupEdition + SemanticMarkupLinkWidget + SemanticMarkupParser StepCustomCodeWidget StepDataWidget StepTreeItem Added: trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,842 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "SemanticMarkupEdition.h" + +#include <QAction> +#include <QTextEdit> + +#include <KActionCollection> +#include <KPushButton> + +#include "SemanticMarkupLinkWidget.h" + +class SemanticMarkupEditionTest: public QObject { +Q_OBJECT + +private slots: + + void init(); + void cleanup(); + + void testConstructor(); + + void testCreateActions(); + + void testEmphasis(); + void testEmphasisStrong(); + void testFilename(); + void testInterface(); + void testLink(); + void testLinkUpdate(); + void testLinkUpdateWithoutUrlAttribute(); + void testNl(); + void testShortcut(); + void testPara(); + void testList(); + void testItem(); + + void testCursorInStartTag(); + void testCursorInEndTag(); + void testCursorInEmptyElementTag(); + + void testCursorBetweenPhraseTags(); + void testCursorBetweenLinkTags(); + void testCursorBetweenLinkTagsWithChildElement(); + void testCursorBetweenParaTags(); + void testCursorBetweenParaTagsWithPhraseTagNotClosed(); + void testCursorBetweenListTags(); + void testCursorBetweenListTagsWithItemTagNotClosed(); + void testCursorBetweenItemTags(); + void testCursorBetweenItemTagsWithPhraseTagNotClosed(); + + void testCursorBetweenEndAndStartTags(); + void testCursorBetweenUnpairedTags(); + void testCursorBetweenUnpairedTagsClosedLater(); + void testCursorBetweenTwoStartTags(); + void testCursorBetweenTwoEndTags(); + + void testChangeTextWithoutChangingCursorPosition(); + +private: + + SemanticMarkupEdition* mSemanticMarkupEdition; + KActionCollection* mActionCollection; + QTextEdit* mTextEdit; + + QAction* mEmphasisAction; + QAction* mEmphasisStrongAction; + QAction* mFilenameAction; + QAction* mInterfaceAction; + QAction* mLinkAction; + QAction* mNlAction; + QAction* mShortcutAction; + QAction* mParaAction; + QAction* mListAction; + QAction* mItemAction; + + void queueAssertLinkUrl(const QString& url, int timeToWait); + void queueSetLinkUrl(const QString& url, int timeToWait); + void queueCancelSetLinkUrl(const QString& url, int timeToWait); + + void moveCursorTo(int position) const; + + void assertOnlyEnabled(const QList<QAction*>& enabledActions) const; + void assertOnlyDisabled(const QList<QAction*>& disabledActions) const; + void assertOnlyChecked(const QList<QAction*>& checkedActions) const; + + void assertTextStartTriggerTextEndTriggerText(QAction* action, + const QString& tagName) const; + +}; + +void SemanticMarkupEditionTest::init() { + mTextEdit = new QTextEdit(); + mSemanticMarkupEdition = new SemanticMarkupEdition(mTextEdit); + mActionCollection = new KActionCollection(this); + + mSemanticMarkupEdition->createActions(mActionCollection); + + mEmphasisAction = mActionCollection->action("kuit-edition-phrase-emphasis"); + mEmphasisStrongAction = + mActionCollection->action("kuit-edition-phrase-emphasis-strong"); + mFilenameAction = mActionCollection->action("kuit-edition-phrase-filename"); + mInterfaceAction = + mActionCollection->action("kuit-edition-phrase-interface"); + mLinkAction = mActionCollection->action("kuit-edition-phrase-link"); + mNlAction = mActionCollection->action("kuit-edition-phrase-nl"); + mShortcutAction = mActionCollection->action("kuit-edition-phrase-shortcut"); + mParaAction = mActionCollection->action("kuit-edition-structure-para"); + mListAction = mActionCollection->action("kuit-edition-structure-list"); + mItemAction = mActionCollection->action("kuit-edition-structure-item"); +} + +void SemanticMarkupEditionTest::cleanup() { + delete mActionCollection; + delete mTextEdit; +} + +void SemanticMarkupEditionTest::testConstructor() { + QTextEdit textEdit; + SemanticMarkupEdition* semanticMarkupEdition = + new SemanticMarkupEdition(&textEdit); + + QCOMPARE(semanticMarkupEdition->parent(), &textEdit); +} + +void SemanticMarkupEditionTest::testCreateActions() { + QTextEdit textEdit; + SemanticMarkupEdition* semanticMarkupEdition = + new SemanticMarkupEdition(&textEdit); + KActionCollection actionCollection(this); + + semanticMarkupEdition->createActions(&actionCollection); + + QCOMPARE(actionCollection.count(), 10); + QVERIFY(actionCollection.action("kuit-edition-phrase-emphasis")); + QVERIFY(actionCollectio... [truncated message content] |
From: <dan...@us...> - 2011-05-06 12:49:57
|
Revision: 303 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=303&view=rev Author: danxuliu Date: 2011-05-06 12:49:50 +0000 (Fri, 06 May 2011) Log Message: ----------- Set the event name completion as sorted and not case sensitive. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp 2011-05-06 12:44:36 UTC (rev 302) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForEventWidget.cpp 2011-05-06 12:49:50 UTC (rev 303) @@ -58,6 +58,8 @@ ui->eventNameLineEdit->setText(waitForEvent->eventName()); + ui->eventNameLineEdit->completionObject()->setOrder(KCompletion::Sorted); + ui->eventNameLineEdit->completionObject()->setIgnoreCase(true); addItemsToEventNameCompletion(); } Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp 2011-05-06 12:44:36 UTC (rev 302) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForEventWidgetTest.cpp 2011-05-06 12:49:50 UTC (rev 303) @@ -77,8 +77,9 @@ WaitForEventWidget widget(&waitFor); KCompletion* completion = eventNameLineEdit(&widget)->completionObject(); - QCOMPARE(completion->allMatches("Dest").count(), 1); - QCOMPARE(completion->allMatches("Dest")[0], QString("Destroy")); + QCOMPARE(completion->order(), KCompletion::Sorted); + QCOMPARE(completion->allMatches("dest").count(), 1); + QCOMPARE(completion->allMatches("dest")[0], QString("Destroy")); } /////////////////////////////////// Helpers //////////////////////////////////// This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-05-16 16:47:00
|
Revision: 306 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=306&view=rev Author: danxuliu Date: 2011-05-16 16:46:51 +0000 (Mon, 16 May 2011) Log Message: ----------- Add support for WaitForProperty to wait for a property to change its value to the expected one. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h trunk/ktutorial/ktutorial-editor/src/serialization/Tutorial.xsd trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialReaderTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/serialization/TutorialWriterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/tests/unit/view/NewWaitForWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForTreeItemTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForWidgetTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.cpp trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.h trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.h trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.h trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.ui trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForPropertyTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForPropertyTreeItemTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForPropertyWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/data/CMakeLists.txt 2011-05-16 16:46:51 UTC (rev 306) @@ -6,6 +6,7 @@ WaitForComposed.cpp WaitForEvent.cpp WaitForNot.cpp + WaitForProperty.cpp WaitForSignal.cpp WaitForWindow.cpp ) Added: trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "WaitForProperty.h" + +//public: + +WaitForProperty::WaitForProperty(QObject* parent): WaitFor(parent) { +} + +WaitFor* WaitForProperty::clone() const { + WaitForProperty* cloned = new WaitForProperty(); + cloned->setObjectName(mObjectName); + cloned->setPropertyName(mPropertyName); + cloned->setValue(mValue); + + return cloned; +} + +bool WaitForProperty::equals(const WaitFor& waitFor) const { + if (!qobject_cast<const WaitForProperty*>(&waitFor)) { + return false; + } + + const WaitForProperty* waitForProperty = + static_cast<const WaitForProperty*>(&waitFor); + if (waitForProperty->objectName() != mObjectName) { + return false; + } + + if (waitForProperty->propertyName() != mPropertyName) { + return false; + } + + if (waitForProperty->value() != mValue) { + return false; + } + + return true; +} + +QString WaitForProperty::objectName() const { + return mObjectName; +} + +void WaitForProperty::setObjectName(const QString& objectName) { + mObjectName = objectName; + + emit dataChanged(this); +} + +QString WaitForProperty::propertyName() const { + return mPropertyName; +} + +void WaitForProperty::setPropertyName(const QString& propertyName) { + mPropertyName = propertyName; + + emit dataChanged(this); +} + +QString WaitForProperty::value() const { + return mValue; +} + +void WaitForProperty::setValue(const QString& value) { + mValue = value; + + emit dataChanged(this); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.h 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef WAITFORPROPERTY_H +#define WAITFORPROPERTY_H + +#include "WaitFor.h" + +/** + * Container for conditions that wait for a property to have some value data. + * It stores the data used in KTutorial WaitForProperty, but it has nothing to + * do with it (they don't even know each other). Its purpose is store the data + * needed to generate the code to initialize a true KTutorial::WaitForProperty + * object. + * + * When any attribute is modified, dataChanged(WaitFor*) signal is emitted. + */ +class WaitForProperty: public WaitFor { +Q_OBJECT +public: + + /** + * Creates a new WaitForProperty. + * + * @param parent The parent QObject. + */ + WaitForProperty(QObject* parent = 0); + + virtual WaitFor* clone() const; + virtual bool equals(const WaitFor& waitFor) const; + + QString objectName() const; + void setObjectName(const QString& objectName); + + QString propertyName() const; + void setPropertyName(const QString& propertyName); + + QString value() const; + void setValue(const QString& value); + +private: + + QString mObjectName; + QString mPropertyName; + QString mValue; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/data/WaitForProperty.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -29,6 +29,7 @@ #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" +#include "../data/WaitForProperty.h" #include "../data/WaitForSignal.h" #include "../data/WaitForWindow.h" @@ -270,6 +271,9 @@ if (qobject_cast<const WaitForNot*>(waitFor)) { return writeWaitFor(static_cast<const WaitForNot*>(waitFor)); } + if (qobject_cast<const WaitForProperty*>(waitFor)) { + return writeWaitFor(static_cast<const WaitForProperty*>(waitFor)); + } if (qobject_cast<const WaitForSignal*>(waitFor)) { return writeWaitFor(static_cast<const WaitForSignal*>(waitFor)); } @@ -347,6 +351,38 @@ return variable; } +QString JavascriptExporter::writeWaitFor( + const WaitForProperty* waitForProperty) { + if (waitForProperty->objectName().isEmpty()) { + out() << "//Error: WaitForProperty without object name!\n"; + return ""; + } + + if (waitForProperty->propertyName().isEmpty()) { + out() << "//Error: WaitForProperty without property name!\n"; + return ""; + } + + if (waitForProperty->value().isEmpty()) { + out() << "//Error: WaitForProperty without value!\n"; + return ""; + } + + QString propertyName = waitForProperty->propertyName(); + propertyName.remove(QRegExp("\\(.*")); + QString variable = "waitFor" + toUpperCamelCase(propertyName) + + "In" + toUpperCamelCase(waitForProperty->objectName()); + variable = addVariable(variable); + + out() << variable << " = ktutorial.newWaitFor(\"WaitForProperty\");\n"; + out() << variable << ".setProperty(ktutorial.findObject(\"" + << escape(waitForProperty->objectName()) << "\"), \"" + << escape(waitForProperty->propertyName()) << "\", " + << waitForProperty->value() << ");\n"; + + return variable; +} + QString JavascriptExporter::writeWaitFor(const WaitForSignal* waitForSignal) { if (waitForSignal->emitterName().isEmpty()) { out() << "//Error: WaitForSignal without emitter name!\n"; Modified: trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/serialization/JavascriptExporter.h 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -30,6 +30,7 @@ class WaitForComposed; class WaitForEvent; class WaitForNot; +class WaitForProperty; class WaitForSignal; class WaitForWindow; @@ -207,6 +208,18 @@ QString writeWaitFor(const WaitForNot* waitForNot); /** + * Writes the code to create and set a WaitForProperty. + * If the object name, the property name or the value aren't set, an error + * message is written instead, and an empty string returned. + * + * The value is written as is. It is neither escaped nor wrapped in quotes. + * + * @param waitForProperty The WaitForProperty. + * @return The name of the variable that holds the WaitFor. + */ + QString writeWaitFor(const WaitForProperty* waitForProperty); + + /** * Writes the code to create and set a WaitForSignal. * If the emitter name or the signal name aren't set, an error message is * written instead, and an empty string returned. Modified: trunk/ktutorial/ktutorial-editor/src/serialization/Tutorial.xsd =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/Tutorial.xsd 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/serialization/Tutorial.xsd 2011-05-16 16:46:51 UTC (rev 306) @@ -44,6 +44,7 @@ <xsd:element name="waitForComposed" type="WaitForComposedType"/> <xsd:element name="waitForEvent" type="WaitForEventType"/> <xsd:element name="waitForNot" type="WaitForNotType"/> + <xsd:element name="waitForProperty" type="WaitForPropertyType"/> <xsd:element name="waitForSignal" type="WaitForSignalType"/> <xsd:element name="waitForWindow" type="WaitForWindowType"/> </xsd:choice> @@ -92,6 +93,12 @@ </xsd:sequence> </xsd:complexType> + <xsd:complexType name="WaitForPropertyType"> + <xsd:attribute name="objectName" type="xsd:string" use="optional"/> + <xsd:attribute name="propertyName" type="xsd:string" use="optional"/> + <xsd:attribute name="value" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="WaitForSignalType"> <xsd:attribute name="emitterName" type="xsd:string" use="optional"/> <xsd:attribute name="signalName" type="xsd:string" use="optional"/> Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -28,6 +28,7 @@ #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" +#include "../data/WaitForProperty.h" #include "../data/WaitForSignal.h" #include "../data/WaitForWindow.h" @@ -165,6 +166,9 @@ if (element.tagName() == "waitForNot") { return readWaitForNot(element); } + if (element.tagName() == "waitForProperty") { + return readWaitForProperty(element); + } if (element.tagName() == "waitForSignal") { return readWaitForSignal(element); } @@ -229,6 +233,22 @@ return waitForNot; } +WaitFor* TutorialReader::readWaitForProperty(const QDomElement& element) { + WaitForProperty* waitForProperty = new WaitForProperty(); + + if (element.hasAttribute("objectName")) { + waitForProperty->setObjectName(element.attribute("objectName")); + } + if (element.hasAttribute("propertyName")) { + waitForProperty->setPropertyName(element.attribute("propertyName")); + } + if (element.hasAttribute("value")) { + waitForProperty->setValue(element.attribute("value")); + } + + return waitForProperty; +} + WaitFor* TutorialReader::readWaitForSignal(const QDomElement& element) { WaitForSignal* waitForSignal = new WaitForSignal(); @@ -257,6 +277,7 @@ if (element.tagName() != "waitForComposed" && element.tagName() != "waitForEvent" && element.tagName() != "waitForNot" && + element.tagName() != "waitForProperty" && element.tagName() != "waitForSignal" && element.tagName() != "waitForWindow") { return false; Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialReader.h 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -31,6 +31,7 @@ class WaitForComposed; class WaitForEvent; class WaitForNot; +class WaitForProperty; class WaitForSignal; class WaitForWindow; @@ -128,6 +129,14 @@ WaitFor* readWaitForNot(const QDomElement& element); /** + * Reads a new WaitForProperty from the "waitForProperty" XML element. + * + * @param element The element to read the WaitForProperty from. + * @return The new WaitForProperty. + */ + WaitFor* readWaitForProperty(const QDomElement& element); + + /** * Reads a new WaitForSignal from the "waitForSignal" XML element. * * @param element The element to read the WaitForSignal from. Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -24,6 +24,7 @@ #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" +#include "../data/WaitForProperty.h" #include "../data/WaitForSignal.h" #include "../data/WaitForWindow.h" @@ -149,6 +150,10 @@ write(static_cast<const WaitForNot*>(waitFor)); return; } + if (qobject_cast<const WaitForProperty*>(waitFor)) { + write(static_cast<const WaitForProperty*>(waitFor)); + return; + } if (qobject_cast<const WaitForSignal*>(waitFor)) { write(static_cast<const WaitForSignal*>(waitFor)); return; @@ -199,6 +204,21 @@ mXmlWriter->writeEndElement(); } +void TutorialWriter::write(const WaitForProperty* waitForProperty) { + mXmlWriter->writeEmptyElement("waitForProperty"); + + if (!waitForProperty->objectName().isEmpty()) { + mXmlWriter->writeAttribute("objectName", waitForProperty->objectName()); + } + if (!waitForProperty->propertyName().isEmpty()) { + mXmlWriter->writeAttribute("propertyName", + waitForProperty->propertyName()); + } + if (!waitForProperty->value().isEmpty()) { + mXmlWriter->writeAttribute("value", waitForProperty->value()); + } +} + void TutorialWriter::write(const WaitForSignal* waitForSignal) { mXmlWriter->writeEmptyElement("waitForSignal"); Modified: trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/serialization/TutorialWriter.h 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -28,6 +28,7 @@ class WaitForComposed; class WaitForEvent; class WaitForNot; +class WaitForProperty; class WaitForSignal; class WaitForWindow; @@ -116,6 +117,13 @@ void write(const WaitForNot* waitForNot); /** + * Writes the XML serialization of the given WaitForProperty. + * + * @param waitForProperty The WaitForProperty to get its XML serialization. + */ + void write(const WaitForProperty* waitForProperty); + + /** * Writes the XML serialization of the given WaitForSignal. * * @param waitForSignal The WaitForSignal to get its XML serialization. Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-05-16 16:46:51 UTC (rev 306) @@ -27,6 +27,8 @@ WaitForEventTreeItem.cpp WaitForEventWidget.cpp WaitForNotTreeItem.cpp + WaitForPropertyTreeItem.cpp + WaitForPropertyWidget.cpp WaitForSignalTreeItem.cpp WaitForSignalWidget.cpp WaitForTreeItem.cpp @@ -60,6 +62,7 @@ StepDataWidget.ui TutorialInformationWidget.ui WaitForEventWidget.ui + WaitForPropertyWidget.ui WaitForSignalWidget.ui WaitForWidget.ui WaitForWindowWidget.ui Modified: trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -22,6 +22,7 @@ #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" +#include "../data/WaitForProperty.h" #include "../data/WaitForSignal.h" #include "../data/WaitForWindow.h" @@ -55,6 +56,8 @@ return new WaitForEvent(); } else if (index == 5) { return new WaitForWindow(); + } else if (index == 6) { + return new WaitForProperty(); } return 0; Modified: trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/view/NewWaitForWidget.ui 2011-05-16 16:46:51 UTC (rev 306) @@ -71,6 +71,11 @@ <string comment="@item:inlistbox">The specified window is shown</string> </property> </item> + <item> + <property name="text"> + <string comment="@item:inlistbox">The specified property has certain value</string> + </property> + </item> </widget> </item> </layout> Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "WaitForPropertyTreeItem.h" + +#include <KLocalizedString> + +#include "../data/WaitForProperty.h" + +//public: + +WaitForPropertyTreeItem::WaitForPropertyTreeItem( + WaitForProperty* waitForProperty, + TreeItem* parent): + WaitForTreeItem(waitForProperty, parent) { + mObjectName = waitForProperty->objectName(); + mPropertyName = waitForProperty->propertyName(); + mValue = waitForProperty->value(); + + connect(waitForProperty, SIGNAL(dataChanged(WaitFor*)), + this, SLOT(update(WaitFor*))); +} + +QString WaitForPropertyTreeItem::text() const { + QString objectName; + if (mObjectName.isEmpty()) { + objectName = i18nc("@item", "(object name not set)"); + } else { + objectName = "\"" + mObjectName + "\""; + } + + QString propertyName; + if (mPropertyName.isEmpty()) { + propertyName = i18nc("@item", "(property not set)"); + } else { + propertyName = "\"" + mPropertyName + "\""; + } + + QString value; + if (mValue.isEmpty()) { + value = i18nc("@item", "(value not set)"); + } else { + value = mValue; + } + + return i18nc("@item", "When the property %1 in the object %2 changes to " + "the value %3", propertyName, objectName, value); +} + +//private: + +void WaitForPropertyTreeItem::update(WaitFor* waitFor) { + WaitForProperty* waitForProperty = static_cast<WaitForProperty*>(waitFor); + mObjectName = waitForProperty->objectName(); + mPropertyName = waitForProperty->propertyName(); + mValue = waitForProperty->value(); + + emit dataChanged(this); +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.h 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef WAITFORPROPERTYTREEITEM_H +#define WAITFORPROPERTYTREEITEM_H + +#include "WaitForTreeItem.h" + +class WaitForProperty; + +/** + * A TreeItem that represents a WaitForProperty. + * The tree representation of a WaitForProperty is a plain text: + * When the property "property name" in the object "object name" changes to the + * value value + * + * If the property, the object name or the value aren't set yet, a placeholder + * is put instead. Property placeholder is "(property not set)", the object name + * placeholder is "(object name not set)" , and the value placeholder is "(value + * not set)" (without quotes, but with parenthesis). + * + * Whenever the WaitForProperty data changes, the WaitForPropertyTreeItem text + * is updated as needed. + */ +class WaitForPropertyTreeItem: public WaitForTreeItem { +Q_OBJECT +public: + + /** + * Creates a new WaitForPropertyTreeItem for the given WaitForProperty and + * with the given parent. + * + * @param waitForProperty The WaitForProperty to represent. + * @param parent The parent TreeItem. + */ + explicit WaitForPropertyTreeItem(WaitForProperty* waitForProperty, + TreeItem* parent = 0); + + /** + * Returns the description of the WaitForProperty. + * + * @return The text for this TreeItem. + */ + virtual QString text() const; + +private: + + /** + * The object name of the WaitForProperty. + */ + QString mObjectName; + + /** + * The property name of the WaitForProperty. + */ + QString mPropertyName; + + /** + * The value of the WaitForProperty. + */ + QString mValue; + +private Q_SLOTS: + + /** + * Updates this WaitForPropertyTreeItem when the data of its WaitForProperty + * changed. + * + * @param waitFor The WaitForProperty. + */ + void update(WaitFor* waitFor); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyTreeItem.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "WaitForPropertyWidget.h" + +#include "ui_WaitForPropertyWidget.h" +#include "../data/WaitForProperty.h" + +#ifdef QT_QTDBUS_FOUND +#include "RemoteObjectNameWidget.h" +#endif + +//public: + +WaitForPropertyWidget::WaitForPropertyWidget(WaitForProperty* waitForProperty, + QWidget* parent): + EditionWidget(parent), + mWaitForProperty(waitForProperty) { + + ui = new Ui::WaitForPropertyWidget(); +#ifdef QT_QTDBUS_FOUND + //Hack: RemoteObjectNameWidget is created before the other widgets to be + //the first widget in the tab order. I feel dumb, but I tried using + //setTabOrder and got nothing... + mRemoteObjectNameWidget = new RemoteObjectNameWidget(this); +#endif + ui->setupUi(this); + +#ifdef QT_QTDBUS_FOUND + //Replace ui->objectNameLineEdit with mRemoteObjectNameWidget + ui->valueVerticalLayout->removeWidget(ui->objectNameLineEdit); + delete ui->objectNameLineEdit; + + ui->valueVerticalLayout->insertWidget(0, mRemoteObjectNameWidget); + + mRemoteObjectNameWidget->setName(waitForProperty->objectName()); +#else + ui->objectNameLineEdit->setText(waitForProperty->objectName()); +#endif + + ui->propertyNameLineEdit->setText(waitForProperty->propertyName()); + + ui->valueLineEdit->setText(waitForProperty->value()); +} + +WaitForPropertyWidget::~WaitForPropertyWidget() { + delete ui; +} + +void WaitForPropertyWidget::saveChanges() { +#ifdef QT_QTDBUS_FOUND + QString objectName = mRemoteObjectNameWidget->name(); +#else + QString objectName = ui->objectNameLineEdit->text(); +#endif + if (mWaitForProperty->objectName() != objectName) { + mWaitForProperty->setObjectName(objectName); + } + + QString propertyName = ui->propertyNameLineEdit->text(); + if (mWaitForProperty->propertyName() != propertyName) { + mWaitForProperty->setPropertyName(propertyName); + } + + QString value = ui->valueLineEdit->text(); + if (mWaitForProperty->value() != value) { + mWaitForProperty->setValue(value); + } +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.h 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef WAITFORPROPERTYWIDGET_H +#define WAITFORPROPERTYWIDGET_H + +#include "EditionWidget.h" + +#ifdef QT_QTDBUS_FOUND +class RemoteObjectNameWidget; +#endif + +class WaitForProperty; + +namespace Ui { +class WaitForPropertyWidget; +} + +/** + * Edition widget for the condition to wait for a property. + */ +class WaitForPropertyWidget: public EditionWidget { +Q_OBJECT +public: + + /** + * Creates a new WaitForPropertyWidget for the given WaitForProperty. + * + * @param waitForProperty The WaitForProperty to set its data. + * @param parent The parent QWidget. + */ + explicit WaitForPropertyWidget(WaitForProperty* waitForProperty, + QWidget* parent = 0); + + /** + * Destroys this widget. + */ + virtual ~WaitForPropertyWidget(); + + /** + * Saves the object name, the property name and the value in the + * WaitForProperty. + */ + virtual void saveChanges(); + +private: + + /** + * The WaitForProperty to edit. + */ + WaitForProperty* mWaitForProperty; + + /** + * The Ui Designer generated class. + */ + Ui::WaitForPropertyWidget* ui; + +#ifdef QT_QTDBUS_FOUND + /** + * The widget to get the name of a remote object. + */ + RemoteObjectNameWidget* mRemoteObjectNameWidget; +#endif + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.ui (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForPropertyWidget.ui 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>WaitForPropertyWidget</class> + <widget class="QWidget" name="WaitForPropertyWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string comment="@title">Edit property to wait for</string> + </property> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>Set the object name, the property name and the value to wait for.</para></string> + </property> + <layout class="QVBoxLayout" name="WaitForPropertyVerticalLayout"> + <item> + <widget class="QGroupBox" name="waitForPropertyGroupBox"> + <property name="title"> + <string comment="@title:group">Wait for property</string> + </property> + <layout class="QHBoxLayout" name="waitForPropertyGroupBoxHorizontalLayout"> + <item> + <layout class="QVBoxLayout" name="labelVerticalLayout"> + <item> + <widget class="QLabel" name="objectNameLabel"> + <property name="text"> + <string comment="@label:textbox">Object name:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="propertyNameLabel"> + <property name="text"> + <string comment="@label:textbox">Property name:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>propertyNameLineEdit</cstring> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="valueLabel"> + <property name="text"> + <string comment="@label:textbox">Value:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>valueLineEdit</cstring> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="valueVerticalLayout"> + <item> + <widget class="KLineEdit" name="objectNameLineEdit"> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>The name of the QObject that contains the property.</para> +<para>Note that the name is not the class of the object, but the string returned by its objectName() method.</para></string> + </property> + </widget> + </item> + <item> + <widget class="KLineEdit" name="propertyNameLineEdit"> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>The name of the property.</para> +<para>Not every property can be used to wait until its value changes to the expected one. Only properties that have a notify signal can be used with that purpose.</para> +<para>Properties that do not have a notify signal can still be used to enrich other conditions (for example, waiting for a signal to be emitted but only changing to the next step if, in addition, some property has some value), but they can not be used alone as single conditions in a reaction.</para></string> + </property> + </widget> + </item> + <item> + <widget class="KLineEdit" name="valueLineEdit"> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>The expected value of the property.</para> +<para>The value is copied as is to the script. That means that it has to be written with the syntax of the script language that the tutorial is going to be exported to. For example, if the tutorial is going to be exported to Javascript and the value to wait for is a text, the value has to be written as <em>"The expected text"</em> (wrapped in quotes).</para> +<para>The value can even contain a programming expression in the script language. For example, something like <em>t.i18nc("@item:inlistbox", "Certain item text")</em> would be used to wait for the localized text of an item in a combo box.</para></string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="waitForPropertyWidgetSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForTreeItem.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -20,11 +20,13 @@ #include "WaitForComposedTreeItem.h" #include "WaitForEventTreeItem.h" #include "WaitForNotTreeItem.h" +#include "WaitForPropertyTreeItem.h" #include "WaitForSignalTreeItem.h" #include "WaitForWindowTreeItem.h" #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" +#include "../data/WaitForProperty.h" #include "../data/WaitForSignal.h" #include "../data/WaitForWindow.h" @@ -47,6 +49,11 @@ parent); } + if (qobject_cast<WaitForProperty*>(waitFor)) { + return new WaitForPropertyTreeItem( + static_cast<WaitForProperty*>(waitFor), parent); + } + if (qobject_cast<WaitForSignal*>(waitFor)) { return new WaitForSignalTreeItem(static_cast<WaitForSignal*>(waitFor), parent); Modified: trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/src/view/WaitForWidget.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -25,12 +25,14 @@ #include "TextTreeItem.h" #include "TreeModel.h" #include "WaitForEventWidget.h" +#include "WaitForPropertyWidget.h" #include "WaitForSignalWidget.h" #include "WaitForWindowWidget.h" #include "WaitForTreeItem.h" #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" +#include "../data/WaitForProperty.h" #include "../data/WaitForSignal.h" #include "../data/WaitForWindow.h" @@ -132,6 +134,14 @@ return; } + if (qobject_cast<WaitForProperty*>(selectedWaitFor)) { + ui->addButton->setEnabled(false); + ui->editButton->setEnabled(true); + ui->removeButton->setEnabled(true); + + return; + } + if (qobject_cast<WaitForSignal*>(selectedWaitFor)) { ui->addButton->setEnabled(false); ui->editButton->setEnabled(true); @@ -228,6 +238,12 @@ editionWidget = new WaitForEventWidget(waitForEvent, this); } + if (qobject_cast<WaitForProperty*>(mCurrentWaitFor)) { + WaitForProperty* waitForProperty = + static_cast<WaitForProperty*>(mCurrentWaitFor); + editionWidget = new WaitForPropertyWidget(waitForProperty, this); + } + if (qobject_cast<WaitForSignal*>(mCurrentWaitFor)) { WaitForSignal* waitForSignal = static_cast<WaitForSignal*>(mCurrentWaitFor); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/CMakeLists.txt 2011-05-16 16:46:51 UTC (rev 306) @@ -19,6 +19,7 @@ WaitForComposed WaitForEvent WaitForNot + WaitForProperty WaitForSignal WaitForWindow ) @@ -37,6 +38,7 @@ WaitForComposed WaitForEvent WaitForNot + WaitForProperty WaitForSignal WaitForWindow ) Added: trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForPropertyTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForPropertyTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForPropertyTest.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * 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; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "WaitForProperty.h" + +class WaitForPropertyTest: public QObject { +Q_OBJECT + +private slots: + + void initTestCase(); + + void testConstructor(); + + void testClone(); + + void testEquals(); + + void testSetObjectName(); + + void testSetPropertyName(); + + void testSetValue(); + +private: + + int mWaitForStarType; + + void assertWaitForProperty(const QSignalSpy& spy, int index, + WaitFor* waitFor); + +}; + +class StubWaitFor: public WaitFor { +Q_OBJECT +public: + + int mValue; + + StubWaitFor(int value = 0): mValue(value) { + } + + virtual WaitFor* clone() const { + return new StubWaitFor(mValue); + } + + virtual bool equals(const WaitFor& waitFor) const { + if (!qobject_cast<const StubWaitFor*>(&waitFor)) { + return false; + } + + return mValue == static_cast<const StubWaitFor*>(&waitFor)->mValue; + } +}; + +void WaitForPropertyTest::initTestCase() { + //WaitFor* must be registered in order to be used with QSignalSpy + mWaitForStarType = qRegisterMetaType<WaitFor*>("WaitFor*"); +} + +void WaitForPropertyTest::testConstructor() { + QObject parent; + WaitForProperty* waitForProperty = new WaitForProperty(&parent); + + QCOMPARE(waitForProperty->parent(), &parent); +} + +void WaitForPropertyTest::testClone() { + WaitForProperty waitForProperty; + waitForProperty.setObjectName("The object name"); + waitForProperty.setPropertyName("The property name"); + waitForProperty.setValue("The value"); + + WaitForProperty* cloned = + static_cast<WaitForProperty*>(waitForProperty.clone()); + + QVERIFY(cloned != &waitForProperty); + QCOMPARE(cloned->objectName(), waitForProperty.objectName()); + QCOMPARE(cloned->propertyName(), waitForProperty.propertyName()); + QCOMPARE(cloned->value(), waitForProperty.value()); + delete cloned; +} + +void WaitForPropertyTest::testEquals() { + WaitForProperty waitForProperty1; + waitForProperty1.setObjectName("The object name"); + waitForProperty1.setPropertyName("The property name"); + waitForProperty1.setValue("The value"); + WaitForProperty waitForProperty2; + + QCOMPARE(waitForProperty1 == waitForProperty2, false); + QCOMPARE(waitForProperty2 == waitForProperty1, false); + + waitForProperty2.setObjectName("The object name"); + waitForProperty2.setPropertyName("The property name"); + waitForProperty2.setValue("The value"); + + QCOMPARE(waitForProperty1 == waitForProperty2, true); + QCOMPARE(waitForProperty2 == waitForProperty1, true); + + StubWaitFor stubWaitFor; + + QCOMPARE(waitForProperty1 == stubWaitFor, false); +} + +void WaitForPropertyTest::testSetObjectName() { + WaitForProperty waitForProperty; + + QSignalSpy dataChangedSpy(&waitForProperty, SIGNAL(dataChanged(WaitFor*))); + + waitForProperty.setObjectName("The object name"); + + QCOMPARE(waitForProperty.objectName(), QString("The object name")); + QCOMPARE(dataChangedSpy.count(), 1); + assertWaitForProperty(dataChangedSpy, 0, &waitForProperty); +} + +void WaitForPropertyTest::testSetPropertyName() { + WaitForProperty waitForProperty; + + QSignalSpy dataChangedSpy(&waitForProperty, SIGNAL(dataChanged(WaitFor*))); + + waitForProperty.setPropertyName("The property name"); + + QCOMPARE(waitForProperty.propertyName(), QString("The property name")); + QCOMPARE(dataChangedSpy.count(), 1); + assertWaitForProperty(dataChangedSpy, 0, &waitForProperty); +} + +void WaitForPropertyTest::testSetValue() { + WaitForProperty waitForProperty; + + QSignalSpy dataChangedSpy(&waitForProperty, SIGNAL(dataChanged(WaitFor*))); + + waitForProperty.setValue("The value"); + + QCOMPARE(waitForProperty.value(), QString("The value")); + QCOMPARE(dataChangedSpy.count(), 1); + assertWaitForProperty(dataChangedSpy, 0, &waitForProperty); +} + +//WaitFor* must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(WaitFor*); + +/////////////////////////////////// Helpers //////////////////////////////////// + +void WaitForPropertyTest::assertWaitForProperty(const QSignalSpy& spy, + int index, WaitFor* waitFor) { + QCOMPARE(spy.at(index).count(), 1); + + QVariant argument = spy.at(index).at(0); + QCOMPARE(argument.userType(), mWaitForStarType); + QCOMPARE(qvariant_cast<WaitFor*>(argument), waitFor); +} + +QTEST_MAIN(WaitForPropertyTest) + +#include "WaitForPropertyTest.moc" Property changes on: trunk/ktutorial/ktutorial-editor/tests/unit/data/WaitForPropertyTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2011-05-16 16:39:27 UTC (rev 305) +++ trunk/ktutorial/ktutorial-editor/tests/unit/serialization/JavascriptExporterTest.cpp 2011-05-16 16:46:51 UTC (rev 306) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -26,6 +26,7 @@ #include "../data/WaitForComposed.h" #include "../data/WaitForEvent.h" #include "../data/WaitForNot.h" +#include "../data/WaitForProperty.h" #include "../data/WaitForSignal.h" #include "../data/WaitForWindow.h" @@ -89,6 +90,9 @@ void testWaitForEvent(); void testWaitForEventWithEscapeSequences(); void testWaitForEventWithoutReceiverNameOrEventName(); + void testWaitForProperty(); + void testWaitForPropertyWithEscapeSequences(); + void testWaitForPropertyWithoutObjectNameOrPropertyNameOrValue(); void testWaitForSignal(); void testWaitForSignalWithEscapeSequences(); void testWaitForSignalWithoutEmitterNameOrSignalName(); @@ -1148,6 +1152,144 @@ QCOMPARE(exportedTutorial, expected); } +void JavascriptExporterTest::testWaitForProperty() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForProperty* waitForProperty = new WaitForProperty(); + waitForProperty->setObjectName("The object name"); + waitForProperty->setPropertyName("thePropertyName"); + waitForProperty->setValue("TheValue"); + + Reaction* reaction = new Reaction(); + reaction->setTriggerType(Reaction::ConditionMet); + reaction->setWaitFor(waitForProperty); + reaction->setResponseType(Reaction::NextStep); + reaction->setNextStepId("Another step"); + step->addReaction(reaction); + + JavascriptExporter exporter; + QString exportedTutorial = exporter.exportTutorial(&tutorial); + + QString expected = +TUTORIAL_EMPTY_INFORMATION_CODE +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_START_CODE +"function theIdStepSetup(step) {\n" +" waitForThePropertyNameInTheObjectName = \ +ktutorial.newWaitFor(\"WaitForProperty\");\n" +" waitForThePropertyNameInTheObjectName.setProperty(\ +ktutorial.findObject(\"The object name\"), \"thePropertyName\", TheValue);\n" +" step.addWaitFor(waitForThePropertyNameInTheObjectName, \ +\"Another step\");\n" +"}\n" +CONNECT_STEP_SETUP +STEP_WITH_ID_THE_ID_AND_EMPTY_TEXT_END_CODE; + + QCOMPARE(exportedTutorial, expected); +} + +void JavascriptExporterTest::testWaitForPropertyWithEscapeSequences() { + Tutorial tutorial; + Step* step = new Step(); + step->setId("The id"); + tutorial.addStep(step); + + WaitForProperty* waitForProper... [truncated message content] |